Nime

Affine cipher

The affine cipher turns letters from one alphabet into letters from the same alphabet.

There are three steps to enciphering/deciphering using the affine cipher.

  1. Turn a letter into a number
  2. Do math on that number
  3. Turn that number into a letter

The second step is where the enciphering and deciphering are different.

Values and techniques used

Keys

The affine cipher uses 2 numerical keys.

These keys are often represented by the variables a and b.

Alphabet length

Letters map to a number and the other way around. For the English alphabet that means “a” is 0, “b” is 1, …, “z” is 25.

The length of that sequence is used during both enciphering and deciphering.

The alphabet length is often represented by the variable m. For English: m = 26.

Key a and alphabet length m have to be coprime. That means they’re integers with a greatest common divisor of 1.

This requirement exists because it is used while deciphering. A cipher that can’t ever be deciphered isn’t very useful now, is it?

Modulo

The modulus operation is used both in enciphering and deciphering.

While similar, modulus and remainder are NOT the same thing!

A modulo operation can be thought of as counting on a circle.
That circle has as many steps as the modulus you are applying.

Adding 1 to the number we take the modulo of means taking one step clockwise on the circle.

Starting at 0, and incrementing by one every step.
The result of a modulo calculation starts at 0 and increments by 1 until the maximum is reached.
Instead of incrementing that maximum, it reaches the top, and starts from 0 again.

In our example, we calculate numbers with a modulus of 26 (the length of the English alphabet). That means the maximum number is 25.

The same logic is applied for subtracting 1.
A subtraction by 1 results in 1 step counterclockwise.

mod 26

0

Modular multiplicative inverse

The modular multiplicative inverse is used during deciphering.

In modular arithmetic, regular division isn’t allowed.
Instead we multiply by the modular inverse.

In regular arithmetic dividing by 77 is equivalent to multiplying by 17{1 \over 7}.

In modular arithmetic that inverse is dependent on which modulus is used.

For example, a modular multiplicative inverse of 77 in mod26\bmod 26 is 1515.

We want to find a number (which we’ll call vv) where multiplying by that number in mod26\bmod 26 works out to 11.

  • aa is 77
  • ww is a random integer
  • in mod26\bmod 26

We want to find vv such that:

av=1±26wa * v = 1 \plusmn 26 * w

written differently:

7v1(mod26)7 * v \cong 1 \pmod {26}

What we just wrote, is a rearranged special case of Bézout’s identity. It says that a multiple of 7 plus a multiple of 26 is equal to the greatest common divisor of 7 and 26.

In the affine cipher, the greatest common divisor is always 1.

Written as Bezout's identity

av+mw=17v+26w=1a * v + m * w = 1 \newline 7 * v + 26 * w = 1

Remember, the number we’re interested in is a1a^{-1}.

The extended Euclidean algorithm calculates the greatest common divisor of 2 numbers along with both factors of Bézout’s identity.

vv is one of those factors!

By plugging aa and 2626 into the algorithm, we get 3 things back.

  1. The greatest common divisor of aa and 2626, that’s 11.
  2. The first integer in Bézout’s identity, that’s vv
  3. The second integer in Bézout’s identity, which is irrelevant.
index.js
function egcd(a, b) {
if (a == 0) {
return [b, 0, 1];
}
if (b == 0) {
return [a, 1, 0];
}
let quotient = Math.floor(b / a);
let remainder = b % a;
let [g, x, y] = egcd(remainder, a);
return [g, y - quotient * x, x];
}

The function to calculate the modular multiplicative inverse checks if the greatest common divisor between key a, and alphabet length m is 1. If it is, it returns the first Bezout coefficient modm\bmod m, which is a1a^{-1}.

index.js
function mmi(a, m) {
let [gcd, v] = egcd(a, m);
if (gcd == 1) {
return ((v % m) + m) % m;
}
}

Enciphering

Plaintext letter to number

A plaintext character is mapped to a number.

Our example uses the English alphabet and zero indexes letters. It maps “a” to 0, “b” to 1, …, “z” to 25.

In code, we take advantage of the ASCII table.

In ASCII, a lowercase "a" is represented by the number 97.

As a result, a character maps to the ASCII value of that character minus 97.

index.js
let plaintextNum = plaintextChar.charCodeAt(0) - 97;

Manipulate the number

  • aa and bb are the key numbers.
  • mm is the length of the used alphabet.
  • xx is the number corresponding to the plaintext letter.
  • yy is the number corresponding to the ciphertext letter before the modulo operation.

The formula for enciphering is y=ax+by = a * x + b.

This is done in mod mmod \space m, so in our example it’s in mod 26mod \space 26.

That means the final ciphertext number is y mod 26y \space mod \space 26.

index.js
let y = a * x + b;
y = ((y % 26) + 26) % 26;

Number to enciphered letter

Where we turned a letter into a number previously, we reverse that process.

In code, we use the ASCII table again.

This time, we add 97 to the zero indexed number and look up the resulting letter in the ASCII table.

index.js
let ciphertextChar = String.fromCharCode(ciphertextNum + 97);

Demo

Input area

Output area

Plaintext char as number

0

Cipher number before modulo

7

Cipher number after modulo

7

Cipher char

h

Deciphering

Enciphered letter to number

This step is identical to the first one while enciphering.

We look up the ASCII code for a lowercase letter, then we subtract the code for a lowercase ASCII "a". This results in a number that is zero indexed.

index.js
let ciphertextNum = ciphertextChar.charCodeAt(0) - 97;

Manipulate the number

  • aa and bb are the key numbers.
  • mm is the length of the used alphabet.
  • yy is the number corresponding to the ciphertext letter.
  • xx is the number corresponding to the plaintext letter before the modulo operation.

The formula for enciphering is y=ax+by = a * x + b.

The formula for deciphering is derived by rewriting that equation. The goal is to isolate the plaintext letter in the equation.

y=ax+byb=axa1(yb)=xy = a * x + b \newline y - b = a * x \newline a^{-1} * (y - b) = x

Rewritten with the plaintext number on the left side: The formula for deiphering is x=a1(yb)x = a^{-1} * (y - b).

It is important to remember these calculation are done in modm\bmod m.

That is why we are not allowed to divide by aa. Instead, we multiply by the modular multiplicative inverse of a(modm)a \pmod m.

index.js
const inverse = mmi(a, 26);
let x = inverse * (cipherCharNum - b);
x = ((x % 26) + 26) % 26;

Number to plaintext letter

This step is identical to the last one while enciphering.

We add the number for a lowercase ASCII "a" to our zero indexed number. Then we look up the letter for that number in the ASCII table.

index.js
let plaintextChar = String.fromCharCode(plaintextNum + 97);

Demo

Input area

Output area

Cipher char as number

0

Modular inverse of a

21

Plain char number before modulo

-147

Plain char number after modulo

9

Plain char

j

Final code and demo

The demo and code to encipher/decipher entire strings instead of single characters is below.

Demo

Input area

enciphered text

Code

index.js
const LOWERCASE_ASCII_A = 97;
function mod(a, b) {
return ((a % b) + b) % b;
}
function egcd(a, b) {
if (a == 0) {
return [b, 0, 1];
}
if (b == 0) {
return [a, 1, 0];
}
let quotient = Math.floor(b / a);
let remainder = b % a;
let [g, x, y] = egcd(remainder, a);
return [g, y - quotient * x, x];
}
function mmi(a, b) {
let [gcd, v] = egcd(a, b);
if (gcd == 1) {
return mod(v, b);
} else {
throw new Error(`Key a: ${a} and alphabet length m: ${b} are not coprime`);
}
}
function encipherChar(plainchar, a, b, m) {
let plaincharNum = plainchar.charCodeAt(0) - LOWERCASE_ASCII_A;
let ciphercharNum = a * plaincharNum + b;
ciphercharNum = mod(ciphercharNum, m);
return String.fromCharCode(ciphercharNum + LOWERCASE_ASCII_A);
}
function decipherChar(cipherchar, inverse, b, m) {
let ciphercharNum = cipherchar.charCodeAt(0) - LOWERCASE_ASCII_A;
let plaincharNum = inverse * (ciphercharNum - b);
plaincharNum = mod(plaincharNum, m);
return String.fromCharCode(plaincharNum + LOWERCASE_ASCII_A);
}
function encipherString(plaintext, a, b, m) {
// check if an inverse to a exists
mmi(a, m);
return plaintext
.split("")
.map((char) => encipherChar(char, a, b, m))
.join("");
}
function decipherString(ciphertext, a, b, m) {
const inverse = mmi(a, m);
return ciphertext
.split("")
.map((char) => decipherChar(char, inverse, b, m))
.join("");
}