Vigenère cipher reference

Vigenère Autokey Cipher Explained: How the Keystream Extends

A repeating-key Vigenère cipher starts with a keyword and cycles that same keyword for as long as the message lasts.

An autokey construction takes a different approach.

It begins with a short secret primer, then extends the keystream using material from the message itself.

For the plaintext-autokey convention used in this article:

Primer:
RAVEN

Plaintext:
SENDTHEPACKAGEBEFORENOON

Full keystream:
RAVENSENDTHEPACKAGEBEFOR

The first five key symbols come from the primer.

After that, the plaintext itself supplies the key symbols.

Using standard A=0 through Z=25 Vigenère arithmetic, the verified ciphertext is:

JEIHGZICDVREVEDOFUVFRTCE

Decrypting sequentially with the same primer reconstructs:

SENDTHEPACKAGEBEFORENOON

exactly.

The important word is sequentially.

During decryption, the receiver does not know the plaintext-fed part of the keystream in advance. Each recovered plaintext letter eventually becomes a future key letter, so the keystream is rebuilt as the message is deciphered.

If you simply need to encode or decode standard repeating-key Vigenère text, use the Vigenère Cipher Decoder and Encoder. This article focuses specifically on how plaintext autokey generates, consumes, and reconstructs its keystream.

“Autokey” can refer to more than one feedback rule

Classical descriptions use more than one autokey convention.

Two important forms are:

plaintext autokey
ciphertext autokey

In a plaintext autokey, the initial primer is followed by plaintext symbols.

In a ciphertext autokey, the primer is followed by ciphertext symbols.

Loyola Marymount University’s cryptology notes describe the Vigenère autokey idea as a keyphrase followed by message-derived text and explicitly distinguish plaintext and ciphertext feedback: Auto-Key Vigenère notes.

The rest of this article uses plaintext autokey unless stated otherwise.

That convention must be specified because the two variants are not interchangeable.

The primer and the keystream are not the same thing

Suppose the primer is:

RAVEN

Its length is:

r = 5

The primer is the secret starting material.

The keystream is the complete sequence of shifts actually used over the whole message.

For plaintext:

SENDTHEPACKAGEBEFORENOON

the full keystream is:

RAVENSENDTHEPACKAGEBEFOR

Only the first five characters are secret primer characters:

R A V E N

Every later keystream character is copied from plaintext that appeared five positions earlier.

That distinction is fundamental:

primer
≠
full keystream

The primer starts the process.

The message extends it.

The plaintext-autokey equation

Let:

Pᵢ = plaintext value at position i
Cᵢ = ciphertext value at position i
Kᵢ = keystream value at position i
r  = primer length

using:

A = 0, B = 1, ..., Z = 25

For the first r positions:

Kᵢ = primerᵢ

After the primer is exhausted:

Kᵢ = Pᵢ₋ᵣ

So encryption is:

Cᵢ = (Pᵢ + Kᵢ) mod 26

or, after the primer:

Cᵢ = (Pᵢ + Pᵢ₋ᵣ) mod 26

The keystream is therefore not periodic merely because the primer has length r.

After position r, it follows earlier plaintext.

How the keystream is assembled

Write the primer first:

RAVEN

Then append plaintext from the beginning until the keystream is long enough:

Plaintext:
SENDTHEPACKAGEBEFORENOON

Primer:
RAVEN

Append plaintext:
RAVEN + SENDTHEPACKAGEBEFOR...

Trim to the message length:

RAVENSENDTHEPACKAGEBEFOR

The alignment is:

Plaintext: SENDTHEPACKAGEBEFORENOON
Keystream: RAVENSENDTHEPACKAGEBEFOR

Notice the offset.

At plaintext position 6, the keystream starts using plaintext position 1.

At position 7, it uses plaintext position 2.

In general:

message position i
uses plaintext position i - r
after the primer

Complete worked encryption

For the verified example:

Plaintext:
SENDTHEPACKAGEBEFORENOON

Primer:
RAVEN

Keystream:
RAVENSENDTHEPACKAGEBEFOR

The full position table is:

Pos. Plain Key P K (P+K) mod 26 Cipher
1 S R 18 17 9 J
2 E A 4 0 4 E
3 N V 13 21 8 I
4 D E 3 4 7 H
5 T N 19 13 6 G
6 H S 7 18 25 Z
7 E E 4 4 8 I
8 P N 15 13 2 C
9 A D 0 3 3 D
10 C T 2 19 21 V
11 K H 10 7 17 R
12 A E 0 4 4 E
13 G P 6 15 21 V
14 E A 4 0 4 E
15 B C 1 2 3 D
16 E K 4 10 14 O
17 F A 5 0 5 F
18 O G 14 6 20 U
19 R E 17 4 21 V
20 E B 4 1 5 F
21 N E 13 4 17 R
22 O F 14 5 19 T
23 O O 14 14 2 C
24 N R 13 17 4 E

The ciphertext is:

JEIHGZICDVREVEDOFUVFRTCE

The first five positions use the primer.

Position 6 is the first plaintext-fed position.

Its plaintext letter is:

H = 7

Its keystream letter is plaintext position 1:

S = 18

Therefore:

(7 + 18) mod 26
=
25
→ Z

which matches ciphertext position 6.

Why decryption cannot precompute the entire keystream

Encryption knows the plaintext.

It can therefore construct:

primer + plaintext

immediately.

Decryption begins with only:

ciphertext
primer

It does not yet know the plaintext section needed for the later keystream.

So the receiver decrypts in order.

For positions 1 through 5:

use RAVEN

Those positions reveal:

SENDT

At position 6, the required key symbol is the recovered plaintext from position 1:

S

At position 7, use recovered plaintext position 2:

E

and so on.

Decryption therefore has a feedback dependency:

recover plaintext now
→ save it
→ use it as key material r positions later

The decryption recurrence

For positions inside the primer:

Pᵢ = (Cᵢ - primerᵢ) mod 26

After the primer:

Pᵢ = (Cᵢ - Pᵢ₋ᵣ) mod 26

That second equation explains why decryption must proceed from left to right.

To recover plaintext position:

i

the receiver may need plaintext position:

i - r

which must already have been recovered.

For primer length 5:

P₆ needs P₁
P₇ needs P₂
P₈ needs P₃
...

The dependency remains deterministic because i-r is always earlier than i.

Step-by-step start of decryption

Ciphertext:

JEIHGZICDVREVEDOFUVFRTCE

Primer:

RAVEN

For position 1:

J = 9
R = 17

9 - 17
=
-8 mod 26
=
18
→ S

Position 2:

E = 4
A = 0

4 - 0
=
4
→ E

Continue through the primer and the receiver obtains:

SENDT

At position 6, the primer is finished.

The key letter is now recovered plaintext position 1:

S

Ciphertext position 6 is:

Z = 25

So:

25 - 18
=
7
→ H

The receiver has extended the plaintext to:

SENDTH

and the same process continues until the full message is recovered.

A short message may never reach the autokey feedback stage

If the plaintext is shorter than the primer, none of the plaintext is needed to extend the keystream.

For:

Primer:
RAVEN

Plaintext:
MEET

the keystream is simply the first four primer symbols:

RAVE

The verified ciphertext is:

DEZX

The autokey rule exists, but the message ends before feedback begins.

This creates a useful boundary condition:

message length ≤ primer length
→ no plaintext-derived key symbols are used

A correct implementation should handle that naturally rather than requiring the message to exceed the primer.

The primer does not repeat

For the 24-letter worked message, a repeated-key stream based on RAVEN would be:

RAVENRAVENRAVENRAVENRAVE

The plaintext-autokey stream is:

RAVENSENDTHEPACKAGEBEFOR

They match only during the initial primer:

RAVEN

After that point the autokey stream is driven by earlier plaintext rather than by another copy of the primer.

This is the defining mechanical change.

The primer length still matters because it sets the delay between a plaintext symbol and its later use as key material.

But it is not a repeating-key period in the usual sense.

Primer length controls the feedback delay

Let the primer length be:

r

Then plaintext position j reappears as key material at:

j + r

For a five-letter primer:

P₁ feeds K₆
P₂ feeds K₇
P₃ feeds K₈
...

For a ten-letter primer:

P₁ feeds K₁₁

The primer therefore determines how far apart a plaintext character and its first keystream reuse are.

It does not determine a cycle in which the same primer letter necessarily returns every r positions.

A ciphertext error can propagate through plaintext feedback

Because recovered plaintext becomes future key material, one ciphertext error can affect more than one decrypted position.

The reproducibility script changes ciphertext position 8 by one letter.

Original ciphertext:

JEIHGZICDVREVEDOFUVFRTCE

Corrupted ciphertext:

JEIHGZIDDVREVEDOFUVFRTCE

The original plaintext is:

SENDTHEPACKAGEBEFORENOON

The corrupted decryption is:

SENDTHEQACKAFEBEFPRENONN

The wrong plaintext positions are:

8, 13, 18, 23

With primer length 5, those positions form:

8
13
18
23

Each is five positions after the previous one.

Why?

The ciphertext error first corrupts:

P₈

That incorrect plaintext symbol becomes key material for:

P₁₃

The resulting wrong P₁₃ later becomes key material for:

P₁₈

and the chain continues.

For plaintext autokey, an isolated transmission error can therefore propagate along one feedback chain.

This behavior follows directly from:

Pᵢ = Cᵢ - Pᵢ₋ᵣ mod 26

after the primer.

The exact error path is predictable

Suppose one ciphertext character at position j is corrupted and:

j > r

Then the first plaintext error occurs at:

j

That incorrect plaintext symbol is reused at:

j + r

which can then affect:

j + 2r
j + 3r
...

until the message ends.

For the worked example:

j = 8
r = 5

so:

8
8 + 5  = 13
8 + 10 = 18
8 + 15 = 23

Those are exactly the four positions found by the independent test.

This is not random diffusion.

It is a deterministic consequence of the feedback delay.

A wrong primer has a related cascading effect

The receiver must know the correct primer.

If one primer symbol is wrong, the corresponding early plaintext position is wrong.

That wrong plaintext then enters the keystream r positions later.

So an incorrect primer letter can create a chain of errors through:

j
j + r
j + 2r
...

where j is the affected primer position.

A decoder that produces periodic-looking corruption under a suspected primer may therefore be showing a feedback error rather than an arbitrary failure.

Plaintext autokey is not a one-time pad

The full keystream may be as long as the message.

That does not make it a one-time pad.

A one-time pad requires a truly random key stream, independent of the plaintext, used only once, with length matching the message.

Plaintext autokey instead generates most of its keystream from:

the plaintext itself

Plaintext has structure.

Natural language has nonuniform letters, recurring words, grammar, and predictable sequences.

Those properties leak into the generated keystream.

The fact that the keystream does not consist of a short repeating keyword does not imply perfect secrecy.

The loss of a short period changes the cryptanalytic problem

Classical repeating-key attacks rely heavily on the fact that positions:

1, 1+r, 1+2r, ...

are repeatedly shifted by the same key letter.

Plaintext autokey does not preserve that simple periodic shift pattern after the primer.

Once feedback begins, later key symbols come from earlier plaintext.

That means a standard workflow such as:

find one repeating period
split ciphertext into fixed Caesar columns
solve one Caesar shift per column

is no longer an exact model of the later message.

The site’s key-length methods comparison is specifically about repeating-key Vigenère. Its assumptions should not be transferred blindly to autokey ciphertext.

Autokey has its own weaknesses, but the key-recovery model has changed.

Known plaintext has a special role

For positions after the primer:

Kᵢ = Pᵢ₋ᵣ

So if an analyst knows a plaintext fragment, that fragment may also reveal later keystream symbols.

Likewise, if part of the plaintext is recovered confidently, it can constrain later decryption.

This feedback structure is useful to a legitimate receiver and potentially useful to a cryptanalyst.

It is another reason the message-derived keystream should not be mistaken for independent random key material.

The site’s known-plaintext attack guide explains the broader Vigenère relationship:

K = C - P mod 26

Autokey adds the extra constraint that later K values may have to equal earlier plaintext values.

Plaintext and ciphertext autokey decrypt differently

It is worth keeping the two common feedback rules separate.

For plaintext autokey after a primer of length r:

Kᵢ = Pᵢ₋ᵣ

For ciphertext autokey:

Kᵢ = Cᵢ₋ᵣ

The ciphertext version can read its later key symbols directly from already received ciphertext.

The plaintext version has to reconstruct its later key symbols from already recovered plaintext.

They therefore have different error behavior and different implementation dependencies.

A system that merely says:

Autokey Vigenère

without identifying the feedback source is underspecified.

Normalization and key advancement still need a profile

The mathematical example in this article uses:

A–Z only
one key symbol per encrypted letter
no spaces or punctuation

A formatted-text implementation has to decide what happens to:

spaces
digits
punctuation
non-ASCII letters

If those characters are preserved and do not consume the keystream, then plaintext fed back into the key should normally be based on the same sequence of encrypted symbols.

If one implementation counts punctuation but another does not, their feedback alignment diverges permanently after the first disagreement.

The same implementation discipline discussed in the Python Vigenère guide matters even more when plaintext is fed back into future key positions.

The exact Python implementation

The following standard-library script is the code used to generate and verify every worked value in this article.

import string

ALPHABET = string.ascii_uppercase
A = ord("A")


def normalize_az(text):
    text = text.upper()
    if any(ch not in ALPHABET for ch in text):
        raise ValueError("This reproducible profile accepts A-Z only.")
    return text


def key_shifts(text):
    return [ord(ch) - A for ch in text]


def plaintext_autokey_keystream(plaintext, primer):
    plaintext = normalize_az(plaintext)
    primer = normalize_az(primer)

    if not primer:
        raise ValueError("Primer must not be empty.")

    return (primer + plaintext)[:len(plaintext)]


def encrypt_plaintext_autokey(plaintext, primer):
    plaintext = normalize_az(plaintext)
    primer = normalize_az(primer)

    if not primer:
        raise ValueError("Primer must not be empty.")

    keystream = plaintext_autokey_keystream(plaintext, primer)
    return "".join(
        chr(
            (
                (ord(p) - A)
                + (ord(k) - A)
            ) % 26
            + A
        )
        for p, k in zip(plaintext, keystream)
    )


def decrypt_plaintext_autokey(ciphertext, primer):
    ciphertext = normalize_az(ciphertext)
    primer = normalize_az(primer)

    if not primer:
        raise ValueError("Primer must not be empty.")

    primer_values = key_shifts(primer)
    recovered = []

    for i, ch in enumerate(ciphertext):
        c = ord(ch) - A

        if i < len(primer):
            k = primer_values[i]
        else:
            # Once the primer has been consumed, the key symbol is the
            # plaintext recovered len(primer) positions earlier.
            k = recovered[i - len(primer)]

        recovered.append((c - k) % 26)

    return "".join(chr(value + A) for value in recovered)


def repeat_keyword(keyword, length):
    keyword = normalize_az(keyword)
    if not keyword:
        raise ValueError("Keyword must not be empty.")
    return "".join(keyword[i % len(keyword)] for i in range(length))


if __name__ == "__main__":
    primer = "RAVEN"
    plaintext = "SENDTHEPACKAGEBEFORENOON"

    keystream = plaintext_autokey_keystream(plaintext, primer)
    ciphertext = encrypt_plaintext_autokey(plaintext, primer)
    recovered = decrypt_plaintext_autokey(ciphertext, primer)

    print("Primer:", primer)
    print("Plaintext:", plaintext)
    print("Keystream:", keystream)
    print("Ciphertext:", ciphertext)
    print("Recovered:", recovered)

    print()
    print("Primer length:", len(primer))
    print("Plaintext length:", len(plaintext))
    print("Primer positions:", f"1-{len(primer)}")
    print(
        "Plaintext-fed positions:",
        f"{len(primer)+1}-{len(plaintext)}",
    )

    # Demonstrate why a repeated keyword is not the same keystream.
    print()
    print(
        "Repeated-key stream:",
        repeat_keyword(primer, len(plaintext)),
    )

    # Corrupt ciphertext position 8 by shifting it forward one letter.
    # With a 5-letter primer, the error re-enters the key at positions
    # 13, 18, and 23 (1-indexed) in this 24-letter message.
    corrupt_index = 7  # zero-indexed position 8
    corrupt_char = chr(
        (
            (ord(ciphertext[corrupt_index]) - A + 1)
            % 26
        )
        + A
    )
    corrupted_ciphertext = (
        ciphertext[:corrupt_index]
        + corrupt_char
        + ciphertext[corrupt_index + 1:]
    )
    corrupted_plaintext = decrypt_plaintext_autokey(
        corrupted_ciphertext,
        primer,
    )

    differences = [
        i + 1
        for i, (a, b) in enumerate(
            zip(plaintext, corrupted_plaintext)
        )
        if a != b
    ]

    print()
    print("Corrupted ciphertext:", corrupted_ciphertext)
    print("Corrupted plaintext:", corrupted_plaintext)
    print("Wrong plaintext positions:", differences)

    # A message shorter than the primer never reaches plaintext feedback.
    short_plaintext = "MEET"
    print()
    print("Short plaintext:", short_plaintext)
    print(
        "Short keystream:",
        plaintext_autokey_keystream(short_plaintext, primer),
    )
    print(
        "Short ciphertext:",
        encrypt_plaintext_autokey(short_plaintext, primer),
    )

Its exact output is:

Primer: RAVEN
Plaintext: SENDTHEPACKAGEBEFORENOON
Keystream: RAVENSENDTHEPACKAGEBEFOR
Ciphertext: JEIHGZICDVREVEDOFUVFRTCE
Recovered: SENDTHEPACKAGEBEFORENOON

Primer length: 5
Plaintext length: 24
Primer positions: 1-5
Plaintext-fed positions: 6-24

Repeated-key stream: RAVENRAVENRAVENRAVENRAVE

Corrupted ciphertext: JEIHGZIDDVREVEDOFUVFRTCE
Corrupted plaintext: SENDTHEQACKAFEBEFPRENONN
Wrong plaintext positions: [8, 13, 18, 23]

Short plaintext: MEET
Short keystream: RAVE
Short ciphertext: DEZX

The decryption function never constructs the unknown keystream in advance.

Instead it stores recovered plaintext values and looks back exactly:

len(primer)

positions whenever the primer has been exhausted.

That mirrors the mathematical recurrence directly.

What to test in an autokey implementation

A reliable plaintext-autokey implementation should test more than one successful round trip.

Known-answer encryption

Verify:

Primer:
RAVEN

Plaintext:
SENDTHEPACKAGEBEFORENOON

Ciphertext:
JEIHGZICDVREVEDOFUVFRTCE

Known-answer decryption

Verify:

JEIHGZICDVREVEDOFUVFRTCE
→
SENDTHEPACKAGEBEFORENOON

with primer RAVEN.

Exact keystream construction

Verify:

RAVENSENDTHEPACKAGEBEFOR

not:

RAVENRAVENRAVENRAVENRAVE

Short-message boundary

Verify that:

MEET

with primer RAVEN uses:

RAVE

and produces:

DEZX

without attempting plaintext feedback.

Empty-primer rejection

There is no starting key material when the primer is empty.

Reject it explicitly.

Corruption propagation

Alter one ciphertext symbol and verify the expected feedback-chain positions.

This catches implementations that accidentally repeat the primer instead of feeding back plaintext.

Round-trip tests are necessary but not sufficient

As with standard Vigenère, this property is useful:

decrypt(
    encrypt(P, primer),
    primer
)
=
P

But two matching bugs can still satisfy a round trip.

For example, an encryptor and decryptor that both incorrectly repeat the primer could agree with each other while failing to implement autokey at all.

That is why fixed keystream and ciphertext vectors matter.

The Vigenère test-vector reference explains the same conformance-testing principle for the standard cipher.

The practical algorithm

Plaintext-autokey encryption can be summarized as:

1. Write the secret primer.

2. Append plaintext from the beginning until the keystream
   is as long as the message.

3. Encrypt each position with ordinary Vigenère addition.

Decryption is:

1. Use the primer to decrypt the first r positions.

2. Save every recovered plaintext symbol.

3. After the primer, use plaintext recovered r positions
   earlier as the current key symbol.

4. Continue until the ciphertext ends.

For the verified example:

Primer:
RAVEN

Plaintext:
SENDTHEPACKAGEBEFORENOON

Keystream:
RAVENSENDTHEPACKAGEBEFOR

Ciphertext:
JEIHGZICDVREVEDOFUVFRTCE

The full keystream is not secret independent material.

It is a deterministic combination of:

secret primer
+
earlier plaintext

That is the defining idea of the plaintext-autokey cipher.

The key point to remember

Autokey does not mean:

repeat the primer automatically

It means the cipher has a rule for generating later key material from the message.

Under the plaintext-autokey convention used here:

Kᵢ = primerᵢ

during the primer and:

Kᵢ = Pᵢ₋ᵣ

afterward.

That one change reshapes the entire keystream.

It also makes decryption stateful: recovered plaintext is not merely output—it becomes future key material.

For RAVEN and SENDTHEPACKAGEBEFORENOON, the result is exactly:

RAVENSENDTHEPACKAGEBEFOR

and:

JEIHGZICDVREVEDOFUVFRTCE

A correct decoder can recover the message only by preserving that feedback relationship from start to finish.


Written by Marcus Hale

Editorially reviewed by VigenereDecoder.com editorial team

Written by Marcus Hale

Editorially reviewed by VigenereDecoder.com editorial team

Similar Posts