← ACARA Digital Technologies Years 9–10

Vigenere cipher in Python

Picks the keyword letter for position i and wraps back to the start with %, so each message letter gets its own shift. This is how a Vigenere cipher loop cycles the keyword.

Understanding Vigenere cipher in Python in ACARA Digital Technologies Years 9–10

What it does: Using j instead of i means spaces and punctuation do not advance the keyword, so the keyword stays lined up with the letters. Decrypting reverses encrypting. Subtracting the shift undoes it, and % 26 keeps the result inside the alphabet. 4 % 3 = 1, so the index wraps back to the start and picks key[1], which is A. The % operator makes the keyword cycle. Without %, key[i] would cause an IndexError once i passed the end of the keyword. The % keeps the index inside the keyword. key[0] always gives the first keyword letter, so every letter gets the same shift. That is a Caesar cipher, not a Vigenere cipher.

Common mistakes: / gives a decimal, which cannot be used as an index. Use % so i wraps back to the start of the keyword: key[i % len(key)]. The shift is the keyword letter's position in the alphabet (A=0 to Z=25), so you subtract 65: ord(s) - 65. Adding 65 gives a huge, wrong shift.

Class: _____________________  ·  Name: _____________________  ·  Date: _________

Free printable worksheet with full answer key — or add this module to a class and your students practise it interactively.

This module's key terms are also in a printable crossword (Modules 876–944) and word search (Modules 860–944).

Take this worksheet further — free with Cloodit

Each concept is reinforced from every angle — learned, then applied, then tested.

Learning
Duo
A Vigenere loop uses key[i % len(key)] with key = "CAT". Which keyword letter is used for position i = 4?
A (4 % 3 = 1, so key[1])T (the keyword just stops at the last letter)
Matches
k = key[i]
↓
There is no % len(key), so the index runs past the end of the keyword on a long message and causes an IndexError.
Applying
Meanings
Term
key[i % len(key)]
Meaning
Picks the keyword letter for position i and wraps back to the start with %, so each message letter gets its own shift. This is how a Vigenere cipher loop cycles the keyword.
Practice
message = "ATTACK AT DAWN"
Testing
Gaps
k = key[i len(key)]
Check Answer
Spots
2 c = msg[i]
3 s = key[i / len(key)]

Cloodit turns curriculum-aligned coding content into journeys students work through one biome at a time.

The 'Star Fox' badge — Frozen Master, one of Cloodit's 106 collectible badges
Students earn collectable badges

Over 100 of them, across seven natural biomes.

Study Buddy
Students build a real study resource

Turns their work into revision material ready for Flash Cards and Exam study.

Diamond Dash — one of Cloodit's live, competitive Sprint games
Students lock in with Sprints

Live, competitive practice for the start of the lesson or a teacher-led lesson finisher.

A Cloodit certificate of achievement, ready to personalise and print
Custom printable certificates

Honour your top performers, or encourage a student who's trying hard — ready to print in seconds.

Quick Questions

1. A Vigenere loop uses key[i % len(key)] with key = "CAT". Which keyword letter is used for position i = 4?
A. A (4 % 3 = 1, so key[1]) ✓
B. T (the keyword just stops at the last letter)
4 % 3 = 1, so the index wraps back to the start and picks key[1], which is A. The % operator makes the keyword cycle.
2. Why does the Vigenere loop use i % len(key) instead of just i?
A. So the index wraps back to 0 when the message is longer than the keyword ✓
B. So the keyword is made shorter before encrypting
Without %, key[i] would cause an IndexError once i passed the end of the keyword. The % keeps the index inside the keyword.
3. Which line picks a different shift for each letter by cycling through the keyword?
A. shift = ord(key[0]) - 65
B. shift = ord(key[i % len(key)]) - 65 ✓
key[0] always gives the first keyword letter, so every letter gets the same shift. That is a Caesar cipher, not a Vigenere cipher.
4. With key = "KEY" (length 3), what does i % len(key) give when i = 3?
A. 3
B. 0 ✓
3 % 3 = 0 because 3 divides evenly by 3. The keyword starts again from its first letter.
5. The message is "HI THERE". To keep the keyword in step, which letters should move the keyword position on?
A. Only the letters, so the space is skipped ✓
B. Every character, including the space
If spaces use up a keyword letter, the keyword goes out of step with the letters and decryption gives the wrong text. Count only letters, for example with isalpha() and a separate counter.

Match the Pairs

key[i % len(key)] → Picks the keyword letter for position i and wraps back to the start of the keyword when i passes its length.
shift = ord(k) - 65 → Turns an uppercase keyword letter into a shift number from 0 to 25 (A is 0, B is 1, and so on).
Only letters use up the keyword, so a counter j goes up only when the character is a letter and spaces are skipped. → if ch.isalpha(): k = key[j % len(key)] j += 1
To decrypt a Vigenere message, subtract the keyword shift instead of adding it, and wrap with % 26. → chr((ord(ch) - 65 - shift) % 26 + 65)
k = key[i] → There is no % len(key), so the index runs past the end of the keyword on a long message and causes an IndexError.
k = key[j % len(key)] j += 1 # runs for every character → j goes up for spaces and punctuation as well as letters, so the keyword gets out of step with the letters being encrypted.

Key Term

key[i % len(key)]
Picks the keyword letter for position i and wraps back to the start with %, so each message letter gets its own shift. This is how a Vigenere cipher loop cycles the keyword.

Fill in the Blanks

1. k = key[i % len(key)]
Hint: Type the operator that wraps the position back to the start of the keyword when it runs out.
2. k = key[i % len(key)]
Hint: Type the built-in function that gives how many letters are in the keyword.
3. if ch.isalpha(): k = key[n % len(key)] n = n + 1
Hint: Type the counter variable that only goes up for letters, so spaces do not use up a keyword letter.
4. shift = ord(key[i % len(key)]) - 65 new = (ord(ch) - 65 - shift) % 26
Hint: Type the arithmetic operator that undoes the shift when decrypting.
5. shift = ord(key[i % len(key)]) - 65 new = (ord(ch) - 65 + shift) % 26 + 65
Hint: Type the function that turns the chosen keyword letter into its character number.

Spot the Error

Question 1
A for i in range(len(msg)):
B c = msg[i]
C s = key[i / len(key)]
D k = ord(s) - 65
Line C — / gives a decimal, which cannot be used as an index. Use % so i wraps back to the start of the keyword: key[i % len(key)].
Question 2
A for i in range(len(msg)):
B s = key[i % len(key)]
C k = ord(s) + 65
D x = ord(msg[i]) - 65 + k
E out += chr(x % 26 + 65)
Line C — The shift is the keyword letter's position in the alphabet (A=0 to Z=25), so you subtract 65: ord(s) - 65. Adding 65 gives a huge, wrong shift.
Question 3
A for i in range(len(msg)):
B s = key[i % len(key)]
C k = ord(s) - 65
D x = ord(msg[i]) - 65 + k
E plain += chr(x % 26 + 65)
Line D — Decrypting undoes the shift, so you subtract k. Adding it shifts the letter forward again and gives the wrong plaintext.
Question 4
A j = 0
B for ch in msg:
C if ch.isalpha():
D s = key[j % len(msg)]
E j += 1
F k = ord(s) - 65
Line D — The keyword must wrap using its own length. Using len(msg) does not cycle the keyword and can go out of range. It should be key[j % len(key)].
Question 5
A key = LEMON
B j = 0
C for ch in msg:
D if ch.isalpha():
E s = key[j % len(key)]
F j += 1
Line A — The keyword is a string, so it needs quotes. Without them Python looks for a variable called LEMON and raises a NameError. It should be key = "LEMON".

Student with a class code? Join your class →