Vigenère cipher reference

Vigenère Cipher Test Vectors: Verified Plaintext, Keys, Ciphertext, and Edge Cases

A Vigenère implementation can produce readable-looking output and still be wrong.

The most common failures are small: treating A as shift 1 instead of 0, advancing the key across spaces in one part of the program but not another, mishandling wraparound at Z, repeating the key from the wrong position, or applying a different text-normalization rule during decryption.

Test vectors make those mistakes visible.

A useful test vector states the plaintext, key, expected ciphertext, and the exact convention under which the result was produced. If the convention is not stated, two correct implementations can disagree simply because they handle spaces, case, punctuation, or unsupported characters differently.

This page therefore separates two questions:

  1. Is the Vigenère arithmetic correct?
  2. Does the application handle surrounding text according to its documented policy?

The first question has exact mathematical answers. The second depends on implementation choices and must be tested under an explicit profile.

If you want to use a working tool rather than validate an implementation, use the Vigenère Cipher Decoder and Encoder. If you are still learning the manual process, see How to Encrypt and Decrypt With the Vigenère Cipher.

Core conformance profile: uppercase A–Z only

The primary vectors on this page use a deliberately strict profile so the expected results are unambiguous.

Call this profile VDH-AZ-1.

Its rules are:

Alphabet: A–Z
Letter values: A=0, B=1, ... Z=25
Key: one or more uppercase A–Z letters
Plaintext: uppercase A–Z letters only
Ciphertext: uppercase A–Z letters only
Key alignment: first key letter aligns with first plaintext letter
Key repetition: repeat from the first key letter as needed

Encryption is:

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

Decryption is:

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

where the key position is taken modulo the key length.

This profile is not a claim that every Vigenère website or program must strip formatting. It is a controlled test environment for validating the cipher itself before text-handling rules are added.

Verified core test vectors

Every vector below was recalculated using the formula above and then round-tripped through decryption to confirm that the original plaintext is recovered.

ID Plaintext Key Expected ciphertext What it tests
classic_lemon ATTACKATDAWN LEMON LXFOPVEFRNHR Standard repeating-key behavior
ucf_computer MEETMEATTHESTORE COMPUTER OSQIGXEKVVQHNHVV Independent published cross-check
identity_key_a HELLOWORLD A HELLOWORLD A=0 identity shift
single_shift_h HELLOWORLD H OLSSVDVYSK One-letter key behaves like Caesar shift 7
wraparound_b ZZZZZ BBBBB AAAAA Modulo-26 wraparound
all_a_reveals_key AAAAA ORBIT ORBIT Zero-valued plaintext reveals keystream letters
all_z_wrap ZZZZZ ORBIT NQAHS Different shifts wrapping from Z
full_alphabet_shift ABCDEFGHIJKLMNOPQRSTUVWXYZ B BCDEFGHIJKLMNOPQRSTUVWXYZA Full alphabet rotation and boundary handling
key_longer_than_text CODE ORANGE QFDR Only the needed prefix of a long key is used
pangram_round_trip THEQUICKBROWNFOXJUMPSOVERTHELAZYDOG VECTOR OLGJIZXODKCNIJQQXLHTUHJVMXJXZRUCFHU Longer multi-cycle round trip

The MEETMEATTHESTORE / COMPUTER vector also matches the worked Vigenère example in the University of Central Florida CIS 3362 cryptography notes, giving this test set one independent published reference point.

Why these vectors catch different bugs

A large collection of random examples is less useful than a small collection in which each row has a purpose.

A must act as shift zero

This vector:

Plaintext:  HELLOWORLD
Key:        A
Ciphertext: HELLOWORLD

should be boring.

That is precisely why it is useful.

If an implementation returns a changed ciphertext, its letter-to-number mapping is probably using A=1 rather than A=0, or it is applying a table convention incorrectly.

With the standard additive Vigenère model:

A = 0
P + 0 = P

so key A is the identity transformation.

A one-letter key reduces to a Caesar shift

For:

Plaintext:  HELLOWORLD
Key:        H
Ciphertext: OLSSVDVYSK

H=7, so every position receives the same shift of 7.

This checks two things at once:

  • the key repeats correctly;
  • a one-letter key produces the expected fixed-shift behavior.

If a one-letter key does not behave like a Caesar shift, the Vigenère core is not being applied consistently.

Wraparound must happen modulo 26

The vector:

Plaintext:  ZZZZZ
Key:        BBBBB
Ciphertext: AAAAA

targets the upper boundary of the alphabet.

Numerically:

Z = 25
B = 1

25 + 1 = 26
26 mod 26 = 0
0 = A

An implementation that returns an invalid character, stays at Z, or moves to the wrong letter has a wraparound error.

The ZZZZZ / ORBIT vector makes the same check under several different key values:

Plaintext:  Z Z Z Z Z
Key:        O R B I T
Ciphertext: N Q A H S

All-A plaintext exposes the repeated key directly

Because A=0:

A + K = K

So:

Plaintext:  AAAAA
Key:        ORBIT
Ciphertext: ORBIT

For a longer all-A plaintext, the ciphertext should simply reproduce the repeated key stream.

This is one of the fastest ways to detect:

  • incorrect key indexing;
  • an off-by-one shift convention;
  • a failure to repeat the key correctly.

It is also a useful reminder of the algebra behind the cipher, not a security recommendation.

A key longer than the plaintext should not wrap unnecessarily

For:

Plaintext:  CODE
Key:        ORANGE
Ciphertext: QFDR

only the first four key letters are used:

Plaintext: C O D E
Key:       O R A N

The unused G and E have no effect.

A program that first truncates the key and a program that indexes the original key directly should therefore produce the same ciphertext.

Every encryption vector is also a decryption vector

The table can be read in both directions.

For example:

Ciphertext: LXFOPVEFRNHR
Key:        LEMON
Plaintext:  ATTACKATDAWN

and:

Ciphertext: OLGJIZXODKCNIJQQXLHTUHJVMXJXZRUCFHU
Key:        VECTOR
Plaintext:  THEQUICKBROWNFOXJUMPSOVERTHELAZYDOG

must decrypt exactly.

A valid implementation should satisfy:

decrypt(encrypt(P, K), K) = P

for every valid plaintext P and nonempty valid key K in the profile.

Passing one famous example is not enough. Round-trip testing across many inputs catches errors that happen only at certain letters, key boundaries, or message lengths.

Machine-readable fixture

The same verified core vectors are provided below as JSON so they can be copied into automated tests without retyping the expected ciphertexts.

{
  "profile": "VDH-AZ-1",
  "alphabet": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
  "letter_values": "A=0 ... Z=25",
  "encryption": "C_i = (P_i + K_(i mod m)) mod 26",
  "decryption": "P_i = (C_i - K_(i mod m)) mod 26",
  "vectors": [
    {
      "id": "classic_lemon",
      "plaintext": "ATTACKATDAWN",
      "key": "LEMON",
      "ciphertext": "LXFOPVEFRNHR"
    },
    {
      "id": "ucf_computer",
      "plaintext": "MEETMEATTHESTORE",
      "key": "COMPUTER",
      "ciphertext": "OSQIGXEKVVQHNHVV"
    },
    {
      "id": "identity_key_a",
      "plaintext": "HELLOWORLD",
      "key": "A",
      "ciphertext": "HELLOWORLD"
    },
    {
      "id": "single_shift_h",
      "plaintext": "HELLOWORLD",
      "key": "H",
      "ciphertext": "OLSSVDVYSK"
    },
    {
      "id": "wraparound_b",
      "plaintext": "ZZZZZ",
      "key": "BBBBB",
      "ciphertext": "AAAAA"
    },
    {
      "id": "all_a_reveals_key",
      "plaintext": "AAAAA",
      "key": "ORBIT",
      "ciphertext": "ORBIT"
    },
    {
      "id": "all_z_wrap",
      "plaintext": "ZZZZZ",
      "key": "ORBIT",
      "ciphertext": "NQAHS"
    },
    {
      "id": "full_alphabet_shift",
      "plaintext": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
      "key": "B",
      "ciphertext": "BCDEFGHIJKLMNOPQRSTUVWXYZA"
    },
    {
      "id": "key_longer_than_text",
      "plaintext": "CODE",
      "key": "ORANGE",
      "ciphertext": "QFDR"
    },
    {
      "id": "pangram_round_trip",
      "plaintext": "THEQUICKBROWNFOXJUMPSOVERTHELAZYDOG",
      "key": "VECTOR",
      "ciphertext": "OLGJIZXODKCNIJQQXLHTUHJVMXJXZRUCFHU"
    }
  ]
}

The fixture deliberately contains no spaces or punctuation. That keeps failures in the mathematical core separate from failures in preprocessing or presentation logic.

Property tests that should hold beyond the fixed examples

Fixed vectors catch known failure modes. Property tests check rules that should remain true over many inputs.

Round-trip property

For every valid normalized plaintext and valid nonempty key:

decrypt(encrypt(P, K), K) = P

Generate many random A–Z messages and keys and verify this property.

A round-trip test alone is not sufficient, because encryption and decryption can contain matching mistakes and still reverse each other. That is why fixed known-answer vectors are also necessary.

Output length

For the strict A–Z profile:

len(ciphertext) = len(plaintext)

Encryption substitutes each symbol. It does not insert or remove symbols.

Output alphabet

Every encrypted character must remain inside:

A–Z

If the core receives only valid A–Z input and emits punctuation, lowercase characters, or code points outside that alphabet, something is wrong.

Repeated-key equivalence

A key that is itself a repetition of a shorter key creates the same keystream.

For any plaintext:

Key ABC

and:

Key ABCABC

produce the same sequence:

ABCABCABCABC...

and therefore the same ciphertext.

This property is useful for detecting code that treats the literal stored key length as cryptographically meaningful after the shift sequence has already begun repeating.

Long-key prefix property

If the key is longer than the plaintext, key characters beyond the plaintext length cannot affect the result.

For a four-letter plaintext, these two keys must behave identically if their first four letters are the same:

ORAN
ORANGE

The cipher never reaches the extra key characters.

Text handling needs a separate test profile

Real tools rarely receive perfectly normalized A–Z strings.

Users paste:

Meet me at 9.

not:

MEETMEAT

The underlying Vigenère arithmetic does not determine what a user interface should do with the spaces, digit, period, or lowercase letters.

Different implementations can make different choices and still have a correct Vigenère core.

For reproducible edge-case testing, this article defines a second profile.

VDH-PRESERVE-1

Under this profile:

ASCII letters A–Z and a–z are encrypted.
The key contains one or more A–Z letters, case-insensitive.
Output letters preserve the input letter's case.
Nonletters are copied unchanged.
Nonletters do not consume a key position.
The key repeats only across encryptable letters.

These are application rules for this test profile, not universal Vigenère requirements.

Verified formatting and edge-case vectors

Plaintext Key Expected ciphertext Behavior under test
MEET ME AT 9. LIME XMQX XM MX 9. Spaces, digit, and period preserved
Meet me at 9. LIME Xmqx xm mx 9. Original letter case preserved
A-A BC B-C Hyphen does not advance the key
123-!? KEY 123-!? Nonletter-only input unchanged
Z z! B A a! Wraparound with preserved case
Attack at dawn. LEMON Lxfopv ef rnhr. Classic vector with formatting retained

These vectors are especially useful because they expose bugs that the normalized test set cannot see.

Why A-A is a high-value edge case

Under the stated profile:

Plaintext: A - A
Key used:  B   C
Output:    B - C

The hyphen is copied but does not consume a key letter.

If an implementation incorrectly advances its key on the hyphen, the second A will use the wrong key position. A tiny three-character input can therefore expose a key-alignment bug that would otherwise appear only after punctuation in a long message.

Empty plaintext and empty key are different cases

An empty plaintext does not create a mathematical problem when a valid key exists.

Under the profiles above:

Plaintext: ""
Key:       ORBIT
Output:    ""

There are no characters to encrypt, so the result is empty.

An empty key is different.

Vigenère needs a key value for every encrypted position. A repeating key of length zero is undefined.

So:

Plaintext: HELLO
Key:       ""

should be treated as invalid input, not as a special zero-shift key.

Returning HELLO would silently turn a missing key into an identity transformation and could hide a serious validation error.

Invalid and out-of-profile inputs should not have invented expected ciphertexts

Some cases are not questions of Vigenère arithmetic at all.

Under the strict VDH-AZ-1 profile:

Plaintext: HELLO WORLD

is not valid until a normalization policy is applied, because it contains a space.

Likewise:

Key: K3Y

is invalid because 3 is outside the defined key alphabet.

Characters such as:

É
Ñ
ß
Ω
🙂

are outside the A–Z model.

A tool may preserve them, reject them, strip them, transliterate them, or extend the cipher to a larger alphabet. Any of those choices needs its own documented rules and corresponding test vectors.

The mistake is not choosing one policy over another. The mistake is leaving the policy implicit and then comparing outputs as though one universal answer exists.

Case normalization and case preservation are separate decisions

A tool might accept:

key = lemon

and normalize it internally to:

LEMON

That is an input convenience.

Separately, it might choose to:

  • convert all plaintext to uppercase;
  • preserve the case pattern of the plaintext;
  • lowercase all output.

Those choices do not change the core shifts, but they change the visible ciphertext.

For conformance testing, decide whether case belongs to the mathematical input or only to presentation. Then write tests for that decision explicitly.

Do not let implicit case conversion contaminate the known-answer tests for the core cipher.

How to diagnose a failed vector

The pattern of the failure often points directly to the bug.

Symptom Likely cause
Key A changes the plaintext A=1 mapping or wrong table indexing
ZZZZZ + BBBBB does not give AAAAA Incorrect modulo wraparound
First key cycle is correct, later text diverges Key repetition or modulo-key-index bug
Output matches until the first space or punctuation Different key-advancement policy
Encryption looks correct but decryption fails near A/Z Subtraction or negative-modulo handling
AAAAA + ORBIT does not return ORBIT Key alignment or letter-value mapping error
ABC and ABCABC behave differently as repeated keystreams Key-period handling bug
Core A–Z vectors pass but mixed-case vectors fail Presentation/normalization bug rather than cipher arithmetic

This separation saves debugging time. Fixing punctuation logic will not repair an A=1 arithmetic error, and rewriting the modular arithmetic will not resolve a disagreement about whether spaces consume key positions.

A wrong key is not normally an input error

Suppose the ciphertext is valid and the supplied key contains valid A–Z letters, but the key is not the one used during encryption.

A Vigenère decoder generally has no mathematical way to know that.

It will still produce letters.

The output may be meaningless, but the operation itself is defined.

That means a test such as:

Ciphertext: LXFOPVEFRNHR
Wrong key:  ORBIT

should not expect the decoder to throw an “incorrect key” error unless the application has some separate validation mechanism.

The cipher transformation can validate syntax. It cannot automatically validate the historical truth of the key.

Do not confuse a round trip with independent verification

A subtle testing mistake is to implement encryption and decryption, run:

plaintext
→ encrypt
→ decrypt
→ original plaintext

and conclude that both algorithms are correct.

They may contain complementary errors.

For example, if both functions use A=1 instead of A=0, they can still reverse each other while disagreeing with the standard additive Vigenère convention used by other tools.

A robust test suite therefore uses both:

Known-answer tests

fixed P + fixed K → independently verified C

and:

Round-trip tests

decrypt(encrypt(P, K), K) → P

The first checks interoperability with the chosen convention. The second checks that encryption and decryption remain inverses across a larger input space.

What a trustworthy Vigenère test suite should cover

A compact but serious suite should exercise four layers.

Layer Minimum checks
Arithmetic A=0, addition/subtraction modulo 26, wraparound
Key handling first-position alignment, repetition, one-letter key, long key
Reversibility known decryption vectors plus broad round-trip tests
Application policy spaces, punctuation, case, empty input, invalid key, unsupported characters

If all four layers are mixed into one test, a failure can be difficult to diagnose.

Keeping them separate makes the result much more useful.

The reference point to keep fixed

The Vigenère cipher itself is simple:

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

Most implementation disagreements occur around that core rather than inside it.

For that reason, the best test process is:

verify the normalized A–Z core first
        ↓
verify key repetition and wraparound
        ↓
verify decryption against known answers
        ↓
verify round-trip properties
        ↓
only then test spaces, punctuation, case, Unicode, and UI policy

A tool that passes the famous ATTACKATDAWN example has passed one test.

A tool that passes independent known-answer vectors, boundary cases, invariants, formatting-policy tests, and broad round trips has demonstrated something much stronger: its behavior is defined well enough to reproduce and verify.


Written by Marcus Hale

Editorially reviewed by VigenereDecoder.com editorial team

Written by Marcus Hale

Editorially reviewed by VigenereDecoder.com editorial team

Similar Posts