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
