0% found this document useful (0 votes)
348 views

Sm473 Crypto Lecture Notes

The document discusses the history and applications of cryptography. It covers various cryptographic ciphers and techniques. It also mentions several works of fiction and films that feature cryptography. Error-correcting codes are also discussed as they relate to reliable communication.

Uploaded by

Nephron
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
348 views

Sm473 Crypto Lecture Notes

The document discusses the history and applications of cryptography. It covers various cryptographic ciphers and techniques. It also mentions several works of fiction and films that feature cryptography. Error-correcting codes are also discussed as they relate to reliable communication.

Uploaded by

Nephron
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 116

Projects in Cryptography, Codes, and

Information Security
sm473 class notes
Prof David Joyner, [email protected]
January 9, 2015

Contents
1 Security terminology and concepts 7

2 The shift and affine ciphers 12


2.1 Brute force decryption . . . . . . . . . . . . . . . . . . . . . . 17
2.2 Frequency analysis . . . . . . . . . . . . . . . . . . . . . . . . 20
2.3 Index of coincidence . . . . . . . . . . . . . . . . . . . . . . . 25

3 Substitution cipher 26

4 Bifid cipher 29

5 The Vigenère cipher 33

6 Number theory background 37


6.1 Binary representation of a number . . . . . . . . . . . . . . . . 37
6.2 Modular inverses and extended Euclidean algorithm . . . . . . 39
6.3 Finite fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

7 The Hill cipher 48


7.1 Matrix inverses (mod m) . . . . . . . . . . . . . . . . . . . . 49
7.2 Enciphering and deciphering . . . . . . . . . . . . . . . . . . . 50

1
8 PK Cryptosystems 56
8.1 RSA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
8.1.1 Set up and examples . . . . . . . . . . . . . . . . . . . 57
8.2 Kid RSA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
8.3 Breaking Kid RSA . . . . . . . . . . . . . . . . . . . . . . . . 63
8.4 The discrete log problem . . . . . . . . . . . . . . . . . . . . . 64
8.5 Diffie-Hellman-Merkle key exchange . . . . . . . . . . . . . . . 64
8.6 ElGamal encryption . . . . . . . . . . . . . . . . . . . . . . . 66

9 Stream ciphers 67
9.1 Binary stream ciphers . . . . . . . . . . . . . . . . . . . . . . 67
9.2 Background on solving recurrence equations . . . . . . . . . . 69
9.3 Matrix reformulation . . . . . . . . . . . . . . . . . . . . . . . 70
9.4 Linear feedback shift registers . . . . . . . . . . . . . . . . . . 76
9.5 Computations with LFSRs . . . . . . . . . . . . . . . . . . . . 80
9.6 Blum-Blum-Shub (BBS) stream cipher . . . . . . . . . . . . . 90

10 Bent functions 91
10.1 Functions with a given least support . . . . . . . . . . . . . . 91
10.2 Cipherstreams via a filter function . . . . . . . . . . . . . . . . 92
10.3 The Walsh transform . . . . . . . . . . . . . . . . . . . . . . . 95

11 Error-correcting codes 99
11.1 The communication model . . . . . . . . . . . . . . . . . . . . 99
11.2 Basic definitions . . . . . . . . . . . . . . . . . . . . . . . . . . 100
11.3 Binary hamming codes . . . . . . . . . . . . . . . . . . . . . . 104
11.4 The covering radius . . . . . . . . . . . . . . . . . . . . . . . . 107

12 Steganography 109
12.1 Basic terminology . . . . . . . . . . . . . . . . . . . . . . . . . 110
12.2 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

Cryptography is now part of the fabric of our society. It is used in cell-


phone communication and in internet transactions. It has been featured in
lots of popular fiction, for example,

• Edgar Allan Poe’s “The Gold Bug” (1843) [Po],

2
From Wikipedia: Set on Sullivan’s Island, South Carolina, the plot follows
William Legrand, who was recently bitten by a gold-colored bug. His ser-
vant, Jupiter, fears Legrand is going insane and goes to Legrand’s friend,
an unnamed narrator, who agrees to visit his old friend. Legrand pulls the
other two into an adventure after deciphering a secret message that will lead
to a buried treasure.

• Sherlock Holmes “The Adventure of the Dancing Men” (1903) [D],


From Wikipedia: Mr. Hilton Cubitt of Ridling Thorpe Manor in Norfolk
visits Sherlock Holmes and gives him a piece of paper with this mysteri-
ous sequence of stick figures. The little dancing men are at the heart of
a mystery which seems to be driving his young wife Elsie to distraction.
He married her about a year ago, and until recently, everything was well.
Holmes comes to realize that it is a substitution cipher. He cracks the code
by frequency analysis. The last of the messages conveyed by the dancing
men is a particularly alarming one. Holmes discovers the sender is Elsie’s
former fianc from Chicago and has come to England to woo her back (more
precisely to scare Elsie out of Cubitt’s arms into his own.

• Neal Stephenson’s “Cryptonomicon” (1999).


From Wikipedia: This award-winning novel follows, in part, Lawrence Pritchard
Waterhouse, a young United States Navy code breaker and mathematical
genius in 1942, as well as his grandson in 1997.
Stephenson also includes a precise description of (and even Perl script for)
the Solitaire (or Pontifex) cipher, a cryptographic algorithm developed by
Bruce Schneier for use with a deck of playing cards, as part of the plot.

There are many movies involving some cryptography, such as

• Breaking the Code (1996), directed by Herbert Wise,


Breaking the Code is a 1986 play by Hugh Whitemore about British mathe-
matician Alan Turing, who was a key player in the breaking of the German
Enigma code at Bletchley Park during World War II and founder of com-
puter science.

• Enigma (2001), directed by Michael Apted,


Using a screenplay by Tom Stoppard, adapted from the novel Enigma by
Robert Harris, this is a film about a young genius frantically races against

3
time to crack an enemy WWII code and solve the mystery surrounding the
woman he loves.

• Mercury Rising (1998), directed by Harold Becker,


IMDB logine: Shadowy elements in the NSA target a nine-year old autistic
savant for death when he is able to decipher a top secret code.

• Sneakers (1992), directed by Phil Alden Robinson,


IMDB logine: Shadowy elements in the NSA target a nine-year old autistic
savant for death when he is able to decipher a top secret code.

• U-571 (2000), directed by Jonathan Mostow,


IMDB logine: A German submarine is boarded by disguised American sub-
mariners trying to capture their Enigma cipher machine.

• The Thomas Beale Cipher (2010, short film), directed by Andrew Allen
(https://vimeo.com/19115071),
IMDB logine: Professor White, cryptographer extraordinaire, is on the trail
of the notoriously uncrackable Thomas Beale cipher, a century-old riddle
hiding the location of a fortune in gold. But White is not alone-shadowy
forces are tight on his tail.

• National Treasure (2004), directed by Jon Turteltaub,


IMDB logine: A German submarine is boarded by disguised American sub-
mariners trying to capture their Enigma cipher machine.

• Pi (1998), directed by Darren Aronovsky,


IMDB logine: A paranoid mathematician searches for a key number that
will unlock the universal patterns found in nature.

• Windtalkers (2002), directed by John Woo,


IMDB logine: Two U.S. Marines in WWII are assigned to protect Navajo
Marines who use their native language as an unbreakable radio cipher.

• Zodiac (2007), directed by David Fincher.


IMDB logine: A San Francisco cartoonist becomes an amateur detective
obsessed with tracking down the Zodiac killer, who communicates via enci-
phered messages.

4
In 2010, there was even an AMC TV series Rubicon (http://en.wikipedia.
org/wiki/Rubicon_(TV_series) featuring a running theme of messages hid-
den in newpaper crossword puzzles.
Cryptography is all around us, any time secrecy is a goal.
Error-correcting codes are all around us as well. A sender uses an error-
correcting code on a message when thay want that message to arrive in a
readable form. The communication channel might have noise, but by encod-
ing the message with an error-correcting code, enough redundancy is added
so that the receiver can recover the original message from the possible cor-
rupted data they received. Reliable communication, at a low cost, is the goal
here. Cell-phones, music CDs, video DVDs, all use error-correcting codes.
There is another type of code which we won’t study here much. Codes
were developed by communication companies to send information in a more
compressed format (e.g., telegraph companies made telegraph codes, data
processing companies made ASCII codes, etc). Here is some Python code for
converting back-and-forth between ascii and letter strings:
Sage

def num2bin(x):
"""
Converts integer in range (1,255) to binary.

EXAMPLES:
sage: num2bin(129)
[1, 0, 0, 0, 0, 0, 0, 1]

"""
return [floor(x/2**(7-i))%2 for i in range(8)]

def string2ascii(m):
"""
Converts a string of characters to a sequence of
0’s and 1’s using the Python ord command.

"""
L = []
for a in m:
L.append(ord(a))
M = [num2bin(x) for x in L]
return flatten(M)

def ascii2string(M):
"""
M is a ciphertext message of 0’s and 1’s of length 8k.
This returns a string of characters representing that
list in ascii.

"""
m = len(M)

5
k = int(m/8)
S = []
for i in range(k):
s = sum([2**(7-j)*M[8*i+j] for j in range(8)])
S.append(chr(s))
sumS = ""
for s in S:
sumS = sumS + s
return sumS

For example, string2ascii("US") returns [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,


0, 1, 1], and ascii2string([0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
0, 1, 1]) returns ’US’.
This course will be an introduction to some of its basic aspects, using
Sage (http://www.sagemath.org/) and SymPy (http://www.sympy.org/,
also available at try the installer at http://www.lfd.uci.edu/~gohlke/
pythonlibs/) to illustrate some of the computations. For many of the Sage
commands, you will need to load the file classical-ciphers-examples.sage
(available from the class webpage into Sage. This is done by saving file to
your computer, starting Sage, and typing

attach "/mypath/classical-ciphers-examples.sage"

at the command line or into a cell of the notebook. (Here mypath is the
full path to whereever you saved the file to.) For the SymPy commands, the
commands are included in version 0.7.4 or later of SymPy, available from the
sympy website. For windows installation, you may wish to try

http://nipy.bic.berkeley.edu/sympy-dist/

instead of the sympy website.


Some good ideas for course projects can be found by looking through (a)
chapters of the textbook we have not covered in class, or (b) the topics in
past issues of Cryptologia:
http://ftp.math.utah.edu/pub/tex/bib/toc/cryptologia.html
http://www.tandfonline.com/loi/ucry20
If you have trouble accessing these issues from a USNA computer, please let
me know.

6
1 Security terminology and concepts
Basic setup: A cryptosystem is a collection of

• a finite set A of symbols which acts as an alphabet,

• the set P of words in A which acts as the set of plaintext messages,

• the set C of words in A which acts as the set of ciphertext messages,

• a finite set K of symbols which acts as a key space,

• for each k ∈ K an enciphering map

Ek : P → C,
a corresponding deciphering key k 0 ∈ K, and a deciphering map

Dk0 : C → P,
such that

Dk0 ◦ Ek (p) = p,
for any p ∈ P.

Alice sends message ----------> Bob receives message


|
|
|
|
Eve (evil eavesdropper)
Mallory (malicious attacker, who can modify messages)

You should know all these and be able to provide examples (when appro-
priate).

1. Basic terms

(a) plaintext - the message to be sent (often a string of letters or


numbers, possibly encoded with an error-correcting code)

7
(b) key - a parameter that determines the output of a cryptographic
algorithm. A key is used as part of the enciphering (and decipher-
ing) process.
In asymmetric cryptosystems, keys are divided into two parts -
the private key and the public key. In general, some part of the
key must be kept secret.
(c) key space - the set of all possible keys
(d) encrypt/encipher - disguising the plaintext in some way, using the
key, resulting in ciphertext
(e) ciphertext - the result of encryption (depends on the key)
(f) decrypt/decipher - the process of ”undoing” the encryption, (“usu-
ally”) resulting in the original plaintext
(g) cryptosystem - the combined systems of the encryption and de-
cryption algorithms
(h) cryptanalysis ((from the Greek “hidden, secret” and “to untie”) -
the art and science of “attacking” or “breaking” cryptosystems
(i) cryptography or cryptology - (from Greek “study of hidden or
secret writing”) the practice and study of techniques for secure
communication in the presence of third parties
(j) attack or break - an method of cryptanalysis applied to a spe-
cific cryptosystem which circumvents the decryption algorithm
and tries to recover the key or the plaintext.
(k) Brute force attack is a search through the entire keyspace to de-
termine the key used. (Other attacks are discussed later.)
(l) A “successful” attack/break occurs when the method results in
a decryption method which is significantly more efficient than a
brute force attack.
(m) symmetric-key cryptosystems (also called “secret-key cryptosys-
tems”) - a cryptosystem which uses the same key for encryption
and for decryption.
(n) public-key cryptosystems (also called “asymmetric-key cryptosystems”)-
a cryptosystem which uses one key for encryption and another for
decryption; the encryption key is made public, but the decryption
key is kept secret.

8
2. Data security terms.
(a) Secrecy - ensuring that the data is available only to those people
who are authorized to have it.
(b) Integrity - ensuring data is not manipulated during the transmis-
sion1 .
(c) Authentication - ensuring that the receiver (Bob) can verify that
the message was indeed sent by the presumed sender (Alice).
(d) Non-repudiation - ensuring that the sender (Alice) can’t deny
sending the message received by Bob.
3. Auguste Kerckhoff articulated six rules for cryptographic security in
his 1883 article, “La cryptographie militaire” (available here: http:
//www.petitcolas.net/fabien/kerckhoffs/).

Kerckhoff ’s Principles of Cryptographic Security:

(a) The system should be unbreakable in practice.


(b) The ciphertext must be able to fall into the hands of the enemy
without inconvenience. This is “Kerckhoff’s principle.”
(c) Its key must be communicable and retainable without the help
of written notes, and changeable or modifiable at the will of the
correspondents.
(d) The cryptogram should be transmissible digitally.
(e) Encryption apparatus is portable and operable by a single person.
(f) The system should be easy to use .

Kerckhoff’s principle: The security of a cryptosystem must be measured


only after assuming all aspects of the cryptosystem is known except for
the key.
Shannon’s maxim: “The enemy knows the system.” (Due to infor-
mation/communications theorist Claude Shannon, possibly indepen-
dently.)
David’s Dictum: “Security through obscurity is absurdity.”
1
Messages sent over a noisy channel can cause data loss, but this is less of s security
issue than a reliability issue solved using error-correcting codes.

9
4. Attacks:

(a) brute force - a search of the entire key space


(b) frequency analysis - compare the frequency of the usage of symbols
in the ciphertext with the frequency of usage in standard English
text.
(c) ciphertext-only - the cryptanalyst has access only to a collection
of ciphertexts (from which he/she tried to recreate the plaintext)
(d) known plaintext - the attacker has a set of ciphertexts to which
he knows the corresponding plaintext.
(e) chosen plaintext - the attacker can obtain the ciphertexts corre-
sponding to an arbitrary set of plaintexts of his own choosing
(f) adaptive chosen-plaintext - like a chosen-plaintext attack, except
the attacker can choose subsequent plaintexts based on informa-
tion learned from previous encryptions
(g) chosen ciphertext - the attacker can obtain the plaintexts corre-
sponding to an arbitrary set of ciphertexts of his own choosing
(h) differential analysis - the study of how differences in plaintexts
can affect the resultant difference in the ciphertext
(i) linear cryptanalysis - the study of finding affine approximations
(eg, the affine Hill cipher) to a cryptosystem
(j) man-in-the-middle - a form of active eavesdropping in which the
attacker makes independent connections with Alice and Bob, and
relays messages between them, making them believe that they are
talking directly to each other over a private connection, when in
fact the entire conversation is controlled by the attacker.

5. Cryptographic problems:

(a) key exchange - Alice and Bob want to communicate using a secret-
key cryptosystem For that, they must exchange keys secretly.
(b) digital signatures - a mathematical scheme for demonstrating the
authenticity of a digital message or document. A valid digital
signature gives a recipient reason to believe that the message was
created by a known sender, and that it was not altered in transit

10
(c) timestamps - authenticating the date or time at which a certain
event occurred
(d) coin-flipping - find a reliable way to use a “coin flip” to settle a
dispute between two parties if they cannot both see the coin
(e) key distribution - Consider a system with N members, all of whom
wish to communicate in secret with each other. In a secret-key
cryptosystem, each pair must have a different key (which is a lot
of keys).
(f) key generation - In a given cryptographic system, the person or
party responsible for generating the key(s) but do so in a secure
manner.

6. Types of classical ciphers:

(a) transposition cipher - a method of encryption by which the posi-


tions held by units of plaintext (which are commonly characters
or pairs of characters) are permuted, so that the ciphertext con-
stitutes a permutation of the plaintext.
Reference:
http://en.wikipedia.org/wiki/Transposition_cipher
Example: A ”route cipher” is one where the characters of the
plaintext are written on a pre-arranged route into a matrix agreed
upon by the sender and receiver. The ciphertext is then obtained
by transcribing the resulting text left-to-right, top-to-bottom.
(b) substitution cipher - a method of encryption by which units of
plaintext are substituted with ciphertext.
i. “simple” substitution cipher - substitution operates on single
letters of the plaintext
Example: shift cipher
ii. “polygraphic” substitution cipher - substitution operates on
larger groups of letters is termed.
Example: Playfair cipher, Hill cipher
iii. “mono-alphabetic” substitution cipher - uses fixed (one-to-
one) substitution over the entire plaintext
Example: shift cipher

11
Julius Caesar, pug Caesar or shift cipher diagram

Figure 1: The Caesar, or shift, cipher

iv. “polyalphabetic” substitution cipher - uses a different substi-


tutions at different times in the plaintext
Example: Vigenère cipher

2 The shift and affine ciphers


The shift cipher is also called the Caesar cipher, named after Julius Caesar,
who, according to Suetonius, used it over 2000 years ago with a shift of three
to protect messages of military significance.
If he had anything confidential to say, he wrote it in cipher,
that is, by so changing the order of the letters of the alphabet,
that not a word could be made out. If anyone wishes to decipher
these, and get at their meaning, he must substitute the fourth
letter of the alphabet, namely D, for A, and so with the others.
-Suetonius, Life of Julius Caesar
http://en.wikipedia.org/wiki/Shift_cipher
Caesar’s nephew Augustus reportedly used a similar cipher, but with a
right shift of 1.
See also pp 81-84 in Kahn’s classic book [K] for more on the history of
this cipher.
There are also several instances (incredibly enough) of modern uses of
this cipher. One by a mobster in 2006 and another by a terrorist in 2011.

ALGORITHM (shift cipher):

12
Figure 2: The alphabet, as integers, in the shift cipher.

INPUT:
k - an integer from 0 to 25 (the secret "key")
m - string of upper-case letters (the "plaintext" message)
OUTPUT:
c - string of upper-case letters (the "ciphertext" message)
Identify the alphabet A, ..., Z with the integers 0, ..., 25.
Step 1: Compute from the string m a list L1 of corresponding integers.
Step 2: Compute from the list L1 a new list L2, given by
adding k (mod 26) to each element in L1.
Step 3: Compute from the list L2 a string c of corresponding letters.

There are many alphabets possible in Sage- the usual English letter,
the binary alphabet, etc. See the reference manual of Sage’s (http://
www.sagemath.org/doc/reference/sage/crypto/classical.html Classi-
cal Cryptosystem module, for details.

13
We shall use the Sage notebook interface in the next few Sage examples,
so you will not see the sage: prompt on the input line.
Sage

AS = AlphabeticStrings()
A = AS.alphabet()
print A
print A[25]

These commands produce the following output.


Sage

(’A’, ’B’, ’C’, ’D’, ’E’, ’F’, ’G’, ’H’, ’I’, ’J’, ’K’, ’L’, ’M’,
’N’, ’O’, ’P’, ’Q’, ’R’, ’S’, ’T’, ’U’, ’V’, ’W’, ’X’, ’Y’, ’Z’)
Z

Using the SymPy version, one can use the default alphabet, or you can
make up your own:
SymPy

>>> alphabet_of_cipher()
[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z]
>>> L = [str(i) for i in range(10)]+["a","b","c"]; L
[’0’, ’1’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’, ’8’, ’9’, ’a’, ’b’, ’c’]
>>> A = "".join(L); A
’0123456789abc’
>>> alphabet_of_cipher(A)
[’0’, ’1’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’, ’8’, ’9’, ’a’, ’b’, ’c’]

Whether you use Sage or SymPy, you will need to initialize an alphabet.
Sage

s = "Go Navy! Beat Army!"


m = AS.encoding(s)
print s
print m

These commands produce the following output.

14
Figure 3: The tabula recta.

Sage

Go Navy! Beat Army!


GONAVYBEATARMY

This is to set up our plaintext message. As a sequence of numbers, the


plaintext is
6, 14, 13, 0, 21, 24, 1, 4, 0, 19, 0, 17, 12, 24.
Next we select the ShiftCryptosystem Python class in Sage and decide
which shift to use.
Sage

CS = ShiftCryptosystem(AS)

15
m = CS.encoding(s) # the plaintext message, again
c = CS.enciphering(1, m) # the ciphertext
print m
print c

These commands produce the following output.


Sage

GONAVYBEATARMY
HPOBWZCFBUBSNZ

This string “HPOBWZCFBUBSNZ” is the enciphered ciphertext.


You can do this in SymPy also:
SymPy

>>> pt = "GONAVYBEATARMY"
>>> shift_cipher_encrypt(pt, 1)
HPOBWZCFBUBSNZ
>>> shift_cipher_encrypt(pt, 0)
GONAVYBEATARMY
>>> shift_cipher_encrypt(pt, -1)
FNMZUXADZSZQLX

We turn to the very similar affine cipher.

ALGORITHM:
INPUT:
a, b - a pair integers, where gcd(a, 26) = 1 (the secret "key")
m - string of upper-case letters (the "plaintext" message)
OUTPUT:
c - string of upper-case letters (the "ciphertext" message)

Identify the alphabet A, ..., Z with the integers 0, ..., 25.


Step 1: Compute from the string m a list L1 of corresponding

16
integers.
Step 2: Compute from the list L1 a new list L2, given by
replacing x by a*x+b (k mod 26), for each element x in L1.
Step 3: Compute from the list L2 a string c of corresponding
letters.

We use a = 3 and b = 4 (for encryption) and a = 9 and b = 16 (for


decryption).
Sage

sage: AS = AlphabeticStrings()
sage: A = AS.alphabet()
sage: ACS = AffineCryptosystem(AS)
sage: ACS
Affine cryptosystem on Free alphabetic string monoid on A-Z
sage: AS = AlphabeticStrings()
sage: A = AS.alphabet()
sage: ACS = AffineCryptosystem(AS)
sage: m = "Go Navy! Beat Army!"
sage: p = ACS.encoding(m)
sage: c = ACS.enciphering(3,4,p); c
WUREPYHQEJEDOY
sage: ACS.deciphering(3,4,c)
GONAVYBEATARMY
sage: pp = ACS.encoding("WUREPYHQEJEDOY")
sage: cc = ACS.enciphering(9,16,pp); cc
GONAVYBEATARMY

2.1 Brute force decryption


Of course, deciphering this ciphertext message “HPOBWZCFBUBSNZ” can
be accomplished by either shifting right by 25 (enciphering) or to the left by
-1 (deciphering):
Sage

print CS.enciphering(25, c)
print CS.deciphering(1, c)

17
These commands produce the following output.
Sage

GONAVYBEATARMY
GONAVYBEATARMY

We could also search over the entire keyspace of 26 letters and see which shift
makes the most sense as the plaintext.

Exercise 2.1. Crack this ciphertext: “LRZZOACZZQTDZYPESLEXLVPDFD-


HTDPC.”
(Found in Prof Praeger’s course notes at [P].)

Solution: We brute force search over the entire key space. Note the 11th line
below.
Sage

ct = "LRZZOACZZQTDZYPESLEXLVPDFDHTDPC"
c = CS.encoding(ct)
for i in range(26):
print i, CS.deciphering(i, c)

These commands produce the following output.


Sage

0 LRZZOACZZQTDZYPESLEXLVPDFDHTDPC
1 KQYYNZBYYPSCYXODRKDWKUOCECGSCOB
2 JPXXMYAXXORBXWNCQJCVJTNBDBFRBNA
3 IOWWLXZWWNQAWVMBPIBUISMACAEQAMZ
4 HNVVKWYVVMPZVULAOHATHRLZBZDPZLY
5 GMUUJVXUULOYUTKZNGZSGQKYAYCOYKX
6 FLTTIUWTTKNXTSJYMFYRFPJXZXBNXJW
7 EKSSHTVSSJMWSRIXLEXQEOIWYWAMWIV
8 DJRRGSURRILVRQHWKDWPDNHVXVZLVHU
9 CIQQFRTQQHKUQPGVJCVOCMGUWUYKUGT
10 BHPPEQSPPGJTPOFUIBUNBLFTVTXJTFS
11 AGOODPROOFISONETHATMAKESUSWISER
12 ZFNNCOQNNEHRNMDSGZSLZJDRTRVHRDQ
13 YEMMBNPMMDGQMLCRFYRKYICQSQUGQCP
14 XDLLAMOLLCFPLKBQEXQJXHBPRPTFPBO

18
15 WCKKZLNKKBEOKJAPDWPIWGAOQOSEOAN
16 VBJJYKMJJADNJIZOCVOHVFZNPNRDNZM
17 UAIIXJLIIZCMIHYNBUNGUEYMOMQCMYL
18 TZHHWIKHHYBLHGXMATMFTDXLNLPBLXK
19 SYGGVHJGGXAKGFWLZSLESCWKMKOAKWJ
20 RXFFUGIFFWZJFEVKYRKDRBVJLJNZJVI
21 QWEETFHEEVYIEDUJXQJCQAUIKIMYIUH
22 PVDDSEGDDUXHDCTIWPIBPZTHJHLXHTG
23 OUCCRDFCCTWGCBSHVOHAOYSGIGKWGSF
24 NTBBQCEBBSVFBARGUNGZNXRFHFJVFRE
25 MSAAPBDAARUEAZQFTMFYMWQEGEIUEQD

Using Sympy, the code is:


SymPy

>>> ct = "LRZZOACZZQTDZYPESLEXLVPDFDHTDPC"
>>> for i in range(1,26): print encipher_shift(ct, i)

MSAAPBDAARUEAZQFTMFYMWQEGEIUEQD
NTBBQCEBBSVFBARGUNGZNXRFHFJVFRE
OUCCRDFCCTWGCBSHVOHAOYSGIGKWGSF
PVDDSEGDDUXHDCTIWPIBPZTHJHLXHTG
QWEETFHEEVYIEDUJXQJCQAUIKIMYIUH
RXFFUGIFFWZJFEVKYRKDRBVJLJNZJVI
SYGGVHJGGXAKGFWLZSLESCWKMKOAKWJ
TZHHWIKHHYBLHGXMATMFTDXLNLPBLXK
UAIIXJLIIZCMIHYNBUNGUEYMOMQCMYL
VBJJYKMJJADNJIZOCVOHVFZNPNRDNZM
WCKKZLNKKBEOKJAPDWPIWGAOQOSEOAN
XDLLAMOLLCFPLKBQEXQJXHBPRPTFPBO
YEMMBNPMMDGQMLCRFYRKYICQSQUGQCP
ZFNNCOQNNEHRNMDSGZSLZJDRTRVHRDQ
AGOODPROOFISONETHATMAKESUSWISER
BHPPEQSPPGJTPOFUIBUNBLFTVTXJTFS
CIQQFRTQQHKUQPGVJCVOCMGUWUYKUGT
DJRRGSURRILVRQHWKDWPDNHVXVZLVHU
EKSSHTVSSJMWSRIXLEXQEOIWYWAMWIV
FLTTIUWTTKNXTSJYMFYRFPJXZXBNXJW
GMUUJVXUULOYUTKZNGZSGQKYAYCOYKX
HNVVKWYVVMPZVULAOHATHRLZBZDPZLY
IOWWLXZWWNQAWVMBPIBUISMACAEQAMZ
JPXXMYAXXORBXWNCQJCVJTNBDBFRBNA

19
KQYYNZBYYPSCYXODRKDWKUOCECGSCOB

2.2 Frequency analysis


The most commonly used letters in English language text are e, t, a, o, i, n.
How often a letter is used in a selection of text is called the letter frequency.
How often a pair of letters occurs together is called the digram frequency.
The most commonly occuring digrams in English are th, he, in, er. How
often a n-tuple of letters occurs together is called the n-gram frequency.
a 8.167%
b 1.492%
c 2.782%
d 4.253%
e 12.702%
f 2.228%
g 2.015%
h 6.094%
i 6.966%
j 0.153%
k 0.772%
l 4.025%
m 2.406%
n 6.749%
o 7.507%
p 1.929%
q 0.095%
r 5.987%
s 6.327%
t 9.056%
u 2.758%
v 0.978%
w 2.360%
x 0.150%
y 1.974%
z 0.074%

20
Figure 4: The frequency histogram for English

Figure 5: The frequency histogram for Spanish and Arabic.

Other languages have different histograms. For example, Spanish and


Arabic frequencies are given in Figure 5.
(Figures 4 and 5 are courtesy of Wikipedia.)
Note that “E” is still in first place!
In the next example, let us try to use frequency analysis to break a
monoalphabetic substitution cipher. For this we need a fairly long cipher
text, such as the following selection from Edgar Allan Poe’s “The Raven”
[Po].
Sage

raven = "Once upon a midnight dreary, while I pondered, weak and weary,
Over many a quaint and curious volume of forgotten lore, While I nodded,
nearly napping, suddenly there came a tapping, As of some one gently

21
rapping, rapping at my chamber door. ’Tis some visiter,’ I muttered,
’tapping at my chamber door -- Only this, and nothing more.’"
print raven

(The string raven must be entered into Sage all on one line; it appears on 5
lines above merely for typographical reasons.) These two commands produce
the following output.
Sage

Once upon a midnight dreary, while I pondered, weak and weary, Over
many a quaint and curious volume of forgotten lore, While I nodded,
nearly napping, suddenly there came a tapping, As of some one gently
rapping, rapping at my chamber door. ’Tis some visiter,’ I muttered,
’tapping at my chamber door -- Only this, and nothing more.’

Now we have entered the plaintext as a string, we ask Sage to output a


frequency distribution, which we then sort:
Sage

m = CS.encoding(raven)
fd = m.frequency_distribution()
dict_fd = fd.function()
list_fd = [[dict_fd[x],x] for x in dict_fd.keys()]
list_fd.sort()
list_fd.reverse()
list_fd

These commands produce the following output.


Sage

[[0.108527131782946, E], [0.0930232558139535, N],


[0.0891472868217055, A], [0.0852713178294574, O],
[0.0736434108527132, R], [0.0736434108527132, I],
[0.0620155038759690, T], [0.0581395348837209, D],
[0.0465116279069767, P], [0.0465116279069767, M],
[0.0348837209302326, Y], [0.0348837209302326, G],
[0.0310077519379845, S], [0.0310077519379845, L],
[0.0310077519379845, H], [0.0271317829457364, U],
[0.0193798449612403, C], [0.0155038759689922, W],
[0.0116279069767442, V], [0.0116279069767442, F],
[0.00775193798449612, B], [0.00387596899224806, Q],
[0.00387596899224806, K]]

We ask SymPy to output a frequency distribution, which we then sort:

22
SymPy

>>> AoC = alphabet_of_cipher()


>>> n = len(raven) - raven.count(" ")
>>> n
277
>>> L = [(1.000*raven.upper().count(x)/n,x) for x in AoC]
>>> L.sort()
>>> L.reverse()
>>> L
[(0.101083032491, E), (0.086642599278, N), (0.0830324909747, A),
(0.0794223826715, O), (0.0685920577617, R), (0.0685920577617, I),
(0.057761732852, T), (0.0541516245487, D), (0.043321299639, P),
(0.043321299639, M), (0.0324909747292, Y), (0.0324909747292, G),
(0.028880866426, S), (0.028880866426, L), (0.028880866426, H),
(0.0252707581227, U), (0.0180505415162, C), (0.014440433213, W),
(0.0108303249097, V), (0.0108303249097, F), (0.0072202166065, B),
(0.00361010830325, Q), (0.00361010830325, K), (0.0, Z), (0.0, X), (0.0, J)]

In either case, we see that “E” is the most frequent and “K” and “Q” are
the least frequent (of those letters actually used). The slight differences in
the statistics are due to the fact that one of them is counting the non-alpha-
numerical characters and one is not.
How does this compare with “typical” (whatever that means) English
language use? See Figure 4.

Exercise 2.2. Decipher this: “VUJLBWVUHTPKUPNOAKYLHYFDOP-


SLPWVUKLYLKDLHR HUKDLHYFVCLYTHUFHXBHPUAHUKJBYPVBZCVS-
BTLVMM VYNVAALUSVYLDOPSLPUVKKLKULHYSFUHWWPUNZBKKL
USFAOLYLJHTLHAHWWPUNHZVMZVTLVULNLUAS FYHWW PUNY-
HWWPUNHATFJOHTILYKVVYAPZZVTLCPZPALYPTBAA LYLKAHWW-
PUNHATFJOHTILYKVVYVUSFAOPZHUKUVA.” Use frequency analysis
and not brute force.

We solve this using Sage.


Sage

ct =
"VUJLBWVUHTPKUPNOAKYLHYFDOPSLPWVUKLYLKDLHR
HUKDLHYFVCLYTHUFHXBHPUAHUKJBYPVBZCVSBTLVMM
VYNVAALUSVYLDOPSLPUVKKLKULHYSFUHWWPUNZBKKL
USFAOLYLJHTLHAHWWPUNHZVMZVTLVULNLUAS FYHWW
PUNYHWWPUNHATFJOHTILYKVVYAPZZVTLCPZPALYPTBAA
LYLKAHWWPUNHATFJOHTILYKVVYVUSFAOPZHUKUVA’’

23
c = CS.encoding(ct)
fd = c.frequency_distribution()
dict_fd = fd.function()
list_fd = [[dict_fd[x],x] for x in dict_fd.keys()]
list_fd.sort()
list_fd.reverse()
list_fd

For typographical purposes, ct was printed in 5 lines. Note that ct must


be entered as a string into Sage using no carriage returns. These commands
produce the following output.
Sage

[[0.108527131782946, L], [0.0930232558139535, U],


[0.0891472868217055, H], [0.0852713178294574, V],
[0.0736434108527132, Y], [0.0736434108527132, P],
[0.0620155038759690, A], [0.0581395348837209, K],
[0.0465116279069767, W], [0.0465116279069767, T],
[0.0348837209302326, N], [0.0348837209302326, F],
[0.0310077519379845, Z], [0.0310077519379845, S],
[0.0310077519379845, O], [0.0271317829457364, B],
[0.0193798449612403, J], [0.0155038759689922, D],
[0.0116279069767442, M], [0.0116279069767442, C],
[0.00775193798449612, I], [0.00387596899224806, X],
[0.00387596899224806, R]]

We ask SymPy to output a frequency distribution, which we then sort:


SymPy

>>> AoC = alphabet_of_cipher()


>>> ct = ’VUJLBWVUHTPKUPNOAKYLHYFDOPSLPWVUKLYLKDLHRHUKDLHYFV
CLYTHUFHXBHPUAHUKJBYPVBZCVSBTLVMMVYNVAALUSVYLDOPSLPUVKKLKULH
YSFUHWWPUNZBKKLUSFAOLYLJHTLHAHWWPUNHZVMZVTLVULNLUAS FYHWWPUN
YHWWPUNHATFJOHTILYKVVYAPZZVTLCPZPALYPTBAALYLKAHWWPUNHATFJOHT
ILYKVVYVUSFAOPZHUKUVA’
>>> n = len(ct)
>>> L = [(1.000*ct.count(x)/n,x) for x in AoC]
>>> L.sort()
>>> L.reverse()

24
>>> L
[(0.107569721116, L), (0.0916334661355, U), (0.0916334661355, H),
(0.0836653386454, V), (0.0717131474104, Y), (0.0717131474104, P),
(0.0637450199203, A), (0.0597609561753, K), (0.0478087649402, W),
(0.0438247011952, T), (0.0358565737052, F), (0.0318725099602, Z),
(0.0318725099602, S), (0.0318725099602, N), (0.0278884462151, O),
(0.0278884462151, B), (0.0199203187251, J), (0.0159362549801, D),
(0.0119521912351, M), (0.0119521912351, C), (0.00796812749004, I),
(0.00398406374502, X), (0.00398406374502, R), (0.0, Q), (0.0, G), (0.0, E)]

Comparing frequencies, we might guess E is mapped to L. Using the alphabet


circle in Figure 2, we might guess that the shift, i.e., the key, is 11 − 4 = 7.
Let’s try this using Sage.
Sage

CS.deciphering(11-4, c)

The output is as follows.


Sage

ONCEUPONAMIDNIGHTDREARYWHILEIPONDEREDWEAKANDWEARYOVERMANYAQUAINTANDC\
URIOUSVOLUMEOFFORGOTTENLOREWHILEINODDEDNEARLYNAPPINGSUDDENLYTHERECAM\
EATAPPINGASOFSOMEONEGENTLYRAPPINGRAPPINGATMYCHAMBERDOORTISSOMEVISITE\
RIMUTTEREDTAPPINGATMYCHAMBERDOORONLYTHISANDNOTHINGMORE

That’s the plain text!

2.3 Index of coincidence


Consider a cryptosystem
We can express the index of coincidence IC for a given letter-frequency
distribution as a summation:
X count(c, p)(count(c, p) − 1)
IC(p) =
c∈p
N (N − 1)
where N is the length of the text p and count(c, p) is the frequency of the
letter c in the message p of the alphabet. If p is a long English message then
IC(p) ∼ = 0.067.

25
Lemma 1. If p is any plaintext message and if c is a ciphertext encipherment
using a monoalphabetic substitution cipher then

IC(p) = IC(c).

Proof. Exercise. 

Example 2. Consider the text from Edgar Alan Poe’s “The Raven”.
Sage

sage: AS = AlphabeticStrings()
sage: CS = ShiftCryptosystem(AS)
sage: m = ’’ONCEUPONAMIDNIGHTDREARYWHILEIPONDEREDWEAKANDWEARYOVERMANYAQUAINTANDC\
URIOUSVOLUMEOFFORGOTTENLOREWHILEINODDEDNEARLYNAPPINGSUDDENLYTHERECAM\
EATAPPINGASOFSOMEONEGENTLYRAPPINGRAPPINGATMYCHAMBERDOORTISSOMEVISITE\
RIMUTTEREDTAPPINGATMYCHAMBERDOORONLYTHISANDNOTHINGMORE’’
sage: m = CS.encoding(m)
sage: m.coincidence_index()
0.0614424034024070
sage: c = CS.enciphering(1, m)
sage: c.coincidence_index()
0.0614424034024070
sage: VC = VigenereCryptosystem(AS, 3)
sage: key = AS("KEY")
sage: m = VC.encoding(raven1) # raven1 is in an example
sage: c = VC.enciphering(key, m)
sage: m.coincidence_index()
0.0614424034024070
sage: c.coincidence_index()
0.0468132597351673

Note that the index of coincidence did not change under a monoalphabetic
substitution cipher but it did change under a polyalphabetic substitution
cipher.

3 Substitution cipher
A substitution cipher is a cryptosystem by which string “units” are replaced
with other sstrings. When the “units” are single letters then it is called a
simple substitution. A monoalphabetic substitution cipher uses fixed substi-
tution over the entire message, whereas a polyalphabetic substitution cipher
uses a number of substitutions at different positions in the message.
The algorithm to encipher a message of a simple monoalphabetic sub-
stitution cipher is simply to apply a fixed permutation p of the alphabet to

26
each of the characters in the plaintext, one after the other. The algorithm to
decipher a message of a simple monoalphabetic substitution cipher is simply
to apply the inverse of this fixed permutation to each of the characters in the
ciphertext, one after the other.
In a simple substitution, the key is a permutation of the 26 letters of the
alphabet. Therefore, the key space is 26!
Sage

sage: M = AlphabeticStrings()
sage: E = SubstitutionCryptosystem(M)
sage: E
Substitution cryptosystem on Free alphabetic string monoid on A-Z
sage: K = M([ 25-i for i in range(26) ])
sage: K
ZYXWVUTSRQPONMLKJIHGFEDCBA
sage: e = E(K)
sage: m = M("THECATINTHEHAT")
sage: e(m)
GSVXZGRMGSVSZG

Here the key is the substitution sending A → Z, B → Y , . . . , Z → A.


One method of creating a key is to first write out a keyword, remove any
repeated letters in it, then writing all the remaining letters in the alphabet
in the usual order. Using this system, the keyword “zebras” gives us the
following key: “ZEBRASCDFGHIJKLMNOPQTUVWXY.” This method is
closely related the the Bifid cipher described in the next section.
Security issues: A disadvantage of this method of the substitution cipher
is that the frequency distribution remains unchanged Although the key space
is large (26! ≈ 4 × 1026 ), this cipher is not very strong, and is easily broken.
Provided the message is of reasonable length (say 50 or so), the cryptanalyst
can deduce the probable meaning of the most common symbols by analyzing
the frequency distribution of the ciphertext.
Example 3. Let us consider the example of the cipher from Poe’s “The Gold
Bug” [Po]. The cipher is
"53++!305))6*;4826)4+)4+).;806*;48!8]60))85;1+8*:+(;:+*8!83(88)5*!;
46(;88*96*?;8)*+(;485);5*!2:*+(;4956*2(5*-4)8]8*;4069285);)6!8)4++;
1(+9;48081;8:8+1;48!85;4)485!528806*81(+9;48;(88;4(+?34;48)4+;161;:
188;+?;"

27
To analyze this in Sage, we must exchange these 21 symbols (5, 3, . . . )
for the capital letters of the alphabet (5 → A, 3 → B, . . . ). This gives us a
cipher:
"ABCCDBEAFFGHIJKSGFJCFJCFLIKEGHIJKDKMGEFFKAINCKHOCPIOCHKDKBPKKFAHDI
JGPIKKHRGHQIKFHCPIJKAFIAHDSOHCPIJRAGHSPAHTJFKMKHIJEGRSKAFIFGDKFJCCI
NPCRIJKEKNIKOKCNIJKDKAIJFJKADASKKEGHKNPCRIJKIPKKIJPCQBJIJKFJCINGNIONKKICQI"

Sage

sage: AS = AlphabeticStrings()
sage: AS
Free alphabetic string monoid on A-Z
sage:
sage:goldbugcipher="ABCCDBEAFFGHIJKSGFJCLFJCFIKEGHIJKDKMGEFFK
AINCKHOCPIOCHKDKBPKKFAHDIJGPIKKHRGHQIKFHCPIJKAFIAHDSOHCP
IJRAGHSPAHTJFKMKHIJEGRSKAFIFGDKFJCCINPCRIJKEKNIKOKCNIJKDKAIJFJ
KADASKKEGHKNPCRIJKIPKKIJPCQBJIJKFJCINGNIONKKICQI"
sage: m = AS.encoding(goldbugcipher)
sage: m.frequency_distribution()
Discrete probability space defined by {A: 0.0579710144927536,
C: 0.0821256038647343, B: 0.0193236714975845, E: 0.0289855072463768,
D: 0.0386473429951691, G: 0.0531400966183575, F: 0.0772946859903381,
I: 0.125603864734299, H: 0.0676328502415459, K: 0.164251207729468,
J: 0.0917874396135265, M: 0.00966183574879227, L: 0.00483091787439614,
O: 0.0241545893719807, N: 0.0386473429951691, Q: 0.0144927536231884,
P: 0.0483091787439614, S: 0.0241545893719807, R: 0.0241545893719807,
T: 0.00483091787439614}
sage: m.character_count()
{A: 12, C: 17, B: 4, E: 6, D: 8, G: 11, F: 16, I: 26, H: 14, K: 34, J: 19, M: 2,
L: 1, O: 5, N: 8, Q: 3, P: 10, S: 5, R: 5, T: 1}
sage: fd = m.frequency_distribution()
sage: dict_fd = fd.function()
sage: list_fd = [[dict_fd[x],x] for x in dict_fd.keys()]
sage: list_fd.sort()
sage: list_fd.reverse()
sage: list_fd
[[0.164251207729468, K], [0.125603864734299, I], [0.0917874396135265, J],
[0.0821256038647343, C], [0.0772946859903381, F], [0.0676328502415459, H],
[0.0579710144927536, A], [0.0531400966183575, G], [0.0483091787439614, P],
[0.0386473429951691, N], [0.0386473429951691, D], [0.0289855072463768, E],
[0.0241545893719807, S], [0.0241545893719807, R], [0.0241545893719807, O],
[0.0193236714975845, B], [0.0144927536231884, Q], [0.00966183574879227, M],
[0.00483091787439614, T], [0.00483091787439614, L]]

Now we decipher it using a key (obtained by trial-and-error2 ):


2
I used a site http://vorpal.nebrwesleyan.edu/~mcclung/ciphers.php, but there
are many other similar sites on the web.

28
Sage

sage: SC = SubstitutionCryptosystem(AS)
sage: key = "AGODLSINTHEPVFYRUMBCJKQWXZ"
sage: key = A("AGODLSINTHEBPVFRYUMCJKQWXZ"); key
AGODLSINTHEBPVFRYUMCJKQWXZ
sage: SC.enciphering(key, AS(goldbugcipher))
AGOODGLASSINTHEBISHOPSHOSTELINTHEDEVILSSEATFORTYONEDEGREESANDTHIRTEENMINUTES
NORTHEASTANDBYNORTHMAINBRANCHSEVENTHLIMBEASTSIDESHOOTFROMTHELEFTEYEOFTHE
DEATHSHEADABEELINEFROMTHETREETHROUGHTHESHOTFIFTYFEETOUT

(I’ve written down the inverse of the key, so enciphering is really decipher-
ing.)
SymPy

>>> goldbug="ABCCDBEAFFGHIJKSGFJCFJCFLIKEGHIJKDKMGEFFKAINCKHOCPIO
CHKDKBPKKFAHDIJGPIKKHRGHQIKFHCPIJKAFIAHDSOHCPIJRAGHSPAHTJFKMKHIJE
GRSKAFIFGDKFJCCINPCRIJKEKNIKOKCNIJKDKAIJFJKADASKKEGHKNPCRIJKIPKKI
JPCQBJIJKFJCINGNIONKKICQI"
>>> key = "AGODLSINTHEPVFYRUMBCJKQWXZ"
>>> encipher_substitution(goldbug, key)
AGOODGLASSINTHEBISHOSHOSPTELINTHEDEVILSSEATFOENYORTYONEDEGREESAND
THIRTEENMINUTESNORTHEASTANDBYNORTHMAINBRANCHSEVENTHLIMBEASTSIDESHOOT
FROMTHELEFTEYEOFTHEDEATHSHEADABEELINEFROMTHETREETHROUGHTHESHOTFIFTYFEETOUT

In either case, this gives us the plaintext in “The Gold Bug”:

“A good glass in the bishop’s hostel in the devil’s seat forty-one


degrees and thirteen minutes northeast and by north main branch
seventh limb east side, shoot from the left eye of the death’s head
a bee-line from the tree through the shot fifty feet out.”

4 Bifid cipher
The Bifid cipher was invented around 1901 by Felix Delastelle. It is a “frac-
tional substitution” cipher, where letters are replaced by pairs of symbols
from a smaller alphabet. The cipher uses a 5 × 5 square filled with some
ordering of the alphabet, except that I’s and J’s are identified (this is a so-
called Polybius square). There is a 6 × 6 analog if you add back in the J’s

29
and also append onto the usual 26 letter alphabet, the digits 0, 1, . . . , 9). Ac-
cording to Helen Gaines’ book [G], this type of cipher was used in the field
by the German Army during World War I.

ALGORITHM: (6x6 case, encryption)


INPUT:
key - a string of letters for the key (no repetitions)
pt - a string of letters for the plaintext (length n)

OUTPUT:
ct - ciphertext message

Step 1:
Create the 6x6 Polybius square S associated to the k as follows:
bottom,
a) starting top left, moving left-to-right, top-to- place the letters
of the key into a 6x6 matrix,
b) when finished, add those letters of the alphabet, followed
by the digits 0, ..., 9, which are not in the key, until the 6x6
square is filled
Step 2:
Create a list P of pairs of numbers which are the coordinates
in the Polybius square of the letters in pt.
Step 3:
Let L1 be the list of all first coordinates of P (length of L1= n),
let L2 be the list of all second coordinates of P (so the length of L2
is also n)
Step 4:
Let L be the concatenation of L1 and L2 (so length L = 2n),
except that consecutive numbers are paired (L[2i], L[2i+1]).
You can regard L as a list of pairs of length n.
Step 5: Let C be the list of all letters which are of the form
S[i,j], fr all (i,j) in L. As a string, this is the ciphertext ct.

Example 4. As an example of a Polybius square for the Bifid cipher, pick


the key to be “encrypt” (as in [McA]). In that case, the Polybius square is

30
 
E N C R Y

 P T A B D 


 F G H I K .

 L M O Q S 
U V W X Z
If the key is “encrypt” and the plaintext is “meet me on monday” then the
ciphertext is “LNLLQNPPNPGADK”. We can verify this using SymPy.
SymPy

>>> pt = "meet me on monday"


>>> key = "encrypt"
>>> print bifid5_square(key)
[E, N, C, R, Y]
[P, T, A, B, D]
[F, G, H, I, K]
[L, M, O, Q, S]
[U, V, W, X, Z]
>>> encipher_bifid5(pt, key, verbose=True)
[[3, 1], [0, 0], [0, 0], [1, 1], [3, 1], [0, 0], [3, 2],
[0, 1], [3, 1], [3, 2], [0, 1], [1, 4], [1, 2], [0, 4]]
LNLLQNPPNPGADK

BTW, the 6 × 6 analog is:


 
E N C R Y P
 T A B D F G 
 
 H I J K L M 
 
 O Q S U V W 
 
 X Z 0 1 2 3 
4 5 6 7 8 9
If the key is “encrypt” and the plaintext is “meet me on monday at 8am”
then the ciphertext is “HNHOKNTA5MEPEGNQZYG”. We can verify this
using SymPy.
SymPy

>>> key = "encrypt"


>>> print bifid_square(key)
[E, N, C, R, Y, P]
[T, A, B, D, F, G]
[H, I, J, K, L, M]
[O, Q, S, U, V, W]
[X, Z, 0, 1, 2, 3]
[4, 5, 6, 7, 8, 9]

31
>>> pt = "meet me on monday at 8am"
>>> encipher_bifid6(pt, key)
HNHOKNTA5MEPEGNQZYG
>>> encipher_bifid6(pt, key, verbose = True)
[[2, 5], [0, 0], [0, 0], [1, 0], [2, 5], [0, 0], [3, 0],
[0, 1], [2, 5], [3, 0], [0, 1], [1, 3], [1, 1], [0, 4],
[1, 1], [1, 0], [5, 4], [1, 1], [2, 5]]
HNHOKNTA5MEPEGNQZYG
>>> ct = "HNHOKNTA5MEPEGNQZYG"
>>> decipher_bifid6(ct, key)
MEETMEONMONDAYAT8AM

ALGORITHM: (6x6 case, decryption)


INPUT:
key - a string of letters for the key (no repetitions)
ct - a string of letters for the ciphertext (length n)

OUTPUT:
pt - plaintext message

Step 1:
Create the 6x6 Polybius square S associated to the key as follows:
bottom,
a) starting top left, moving left-to-right, top-to- place the letters
of the key into a 6x6 matrix,
b) when finished, add those letters of the alphabet, followed
by the digits 0, ..., 9, which are not in the key, until the 6x6
square is filled
We call the list of all symbols in the square, taken top-to-bottom,
left-to-right, the "long key"
Step 2:
From the "long key", find the coordinates corresponding to each
symbol in the cipher text: the coordinate of x is (i,j) if x
is the m-th element of the long key and m = 6i+j.
Step 3: Read the i’s then the j’s off consecutively, then use
the Polybius square to find the corresponding symbols.
This is the plaintext pt.

Example 5. What about the larger bifid squares? The problem is - how are
the squares to be filled in? With integers?

32
Here is an example:
SymPy

>>> key = "gold bug"


>>> print bifid7_square(key)
[ G, O, L, D, B, U, A]
[ C, E, F, H, I, J, K]
[ M, N, P, Q, R, S, T]
[ V, W, X, Y, Z, 0, 1]
[ 2, 3, 4, 5, 6, 7, 8]
[ 9, 10, 11, 12, 13, 14, 15]
[16, 17, 18, 19, 20, 21, 22]

Let us use this to encipher “Meet me on Monday at 8am.”


SymPy

>>> pt = "meet me on monday at 8am"


>>> encipher_bifid7(pt, key)
NFNLMMDL2ME16ECE112216

You see the ambiguity in deciphering: Is the “11“ an 11 or two 1’s? You
can’t use numbers in 7 × 7 or higher bifid squares without running into these
ambiguities.

In-class team challenge: encrypt a message, then give it to another team to


decrypt.

5 The Vigenère cipher


From what I’ve read, the Vigenère cipher is named after Blaise de Vigenère,
a sixteenth century diplomat and cryptographer, by a historical accident.
Vigene‘re actually invented a different and more complicated cipher. The
so-called “Vigenère cipher ” cipher was actually invented by Giovan Batista
Belaso in 1553. In any case, it is this cipher which we shall discuss next.
This cipher has been re-invented by several authors, such as author and
mathematician Charles Lutwidge Dodgson (Lewis Carroll) who claimed his
1868 “The Alphabet Cipher” was unbreakable. Several others claimed the
so-called Vigenère cipher was unbreakable (e.g., the Scientific American mag-
azine in 1917). However, Friedrich Kasiski and Charles Babbage broke the

33
cipher in the 1800’s: once it is known that the key is, say, n characters long,
frequency analysis can be applied to every n−th letter of the ciphertext to de-
termine the plaintext. This method is called Kasiski examination, although
it was first discovered by Babbage.
This cipher was used in the 1700’s, for example, during the American Civil
War. The Confederacy used a brass cipher disk to implement the Vigenère
cipher (now on display in the NSA Museum in Fort Meade).
The so-called Vigenère cipher is a generalization of the shift cipher. Whereas
the shift cipher shifts each letter by the same amount (that amount being
the key of the shift cipher) the so-called Vigenère cipher shifts a letter by an
amount determined by the key, which is a word or phrase known only to the
sender and receiver).
For example, if the key was a single letter, such as “C”, then the so-called
Vigenère cipher is actually a shift cipher with a shift of 2 (since “C” is the
2nd letter of the alphabet, if you start counting at 0). If the key was a word
with two letters, such as “CA”, then the so-called Vigenère cipher will shift
letters in even positions by 2 and letters in odd positions are left alone (or
shifted by 0, since “A” is the 0th letter, if you start counting at 0).

ALGORITHM:
INPUT:
key - a string of upper-case letters (the secret "key")
m - string of upper-case letters (the "plaintext" message)
OUTPUT:
c - string of upper-case letters (the "ciphertext" message)

Identify the alphabet A, ..., Z with the integers 0, ..., 25.


Step 1: Compute from the string key a list L1 of corresponding
integers. Let n1 = len(L1).
Step 2: Compute from the string m a list L2 of corresponding
integers. Let n2 = len(L2).
Step 3: Break L2 up sequencially into sublists of size n1, and one sublist
at the end of size <=n1.
Step 4: For each of these sublists L of L2, compute a new list C given by
C[i] = L[i]+L1[i] (mod 26) to the i-th element in the sublist,
for each i.
Step 5: Assemble these lists C by concatenation into a new list of length n2.
Step 6: Compute from the new list a string c of corresponding letters.

The Tabula Recta in Figure 3 is a useful aide to enciphering and decipher-

34
ing this cipher. To encipher using this table, simply look up each letter of the
plaintext along the top (this specifies a column), then the corresponding let-
ter of the key along the side (this specifies the row), and record as ciphertext
that letter which is in that row and column of the table. To decrypt, using
this table, simply look up each letter of the ciphertext along the top (this
specifies a column), then find the corresponding letter of the negative of the
key along the side (this specifies the row), and record as plaintext that letter
which is in that row and column of the table. For example, the key is USNA
(which corresponds to the sequence (20, 18, 13, 0)) then the negative of the
key is GINA (which corresponds to the sequence (6, 8, 13, 0) = −(20, 18, 13, 0)
(mod 26)).
Sage

sage: AS = AlphabeticStrings()
sage: A = AS.alphabet()
sage: key = AS("A")
sage: VC = VigenereCryptosystem(AS, 1)
sage: m = VC.encoding("Beat Army!"); m
BEATARMY
sage: VC.enciphering(key, m)
BEATARMY
sage: key = AS("B")
sage: VC.enciphering(key, m)
CFBUBSNZ
sage: VC.deciphering(key, c)
BEATARMY
sage: VC.enciphering(AS("Z"), c)
BEATARMY

This cipher is also implemented in SymPy:


SymPy

>>> key = "encrypt"


>>> pt = "meet me on monday"
>>> encipher_vigenere(pt, key)
QRGKKTHRZQEBPR

35
Next, we decryption:
Sage

sage: VC = VigenereCryptosystem(AS, 2)
sage: key = AS("CA")
sage: m = VC.encoding("Beat Army!"); m
BEATARMY
sage: VC.enciphering(key, m)
DECTCROY
sage: c = VC.encoding("DECTCROY")
sage: c
DECTCROY
sage: VC.deciphering(key, c)
BEATARMY
sage: c = AS("DECTCROY")
sage: VC.deciphering(key, c)
BEATARMY

To decipher “QRGKKTHRZQEBPR” (with some spaces added) in SymPy,


use the following commands.
SymPy

>>> key = "encrypt"


>>> ct = "QRGK kt HRZQE BPR"
>>> decipher_vigenere(ct, key)
MEETMEONMONDAY

The cipher Vigenère actually discovered is an “auto-key” cipher described as


follows.

ALGORITHM:
INPUT:
key - a string of upper-case letters (the secret "key")
m - string of upper-case letters (the "plaintext" message)
OUTPUT:

36
c - string of upper-case letters (the "ciphertext" message)

Identify the alphabet A, ..., Z with the integers 0, ..., 25.


Step 1: Compute from the string m a list L2 of corresponding
integers. Let n2 = len(L2).
Step 2: Let n1 be the length of the key. Concatenate the string
key with the first n2-n1 characters of the plaintext message.
Compute from this string of length n2 a list L1 of corresponding
integers. Note n2 = len(L1).
Step 3: Compute a new list C given by C[i] = L1[i]+L2[i] (mod 26), for each i.
Note n2 = len(C).
Step 5: Compute from the new list a string c of corresponding letters.

6 Number theory background


It is convenient to replace letters and other characters by numbers and have
our cipher act by permuting sequences of numbers instead of sequences of
characters. For this reason, a good background in number theory (the math-
ematics of the inetgers) is very useful in understanding the mathematical
aspects of cryptography.

6.1 Binary representation of a number


The material below can be found in any standard textbook, for example
[JKT].
Each natural number is most commonly written in decimal form (or base
10),
a = ak 10k + ...a1 10 + a0 ,
where 0 ≤ ai ≤ 9 are the digits. (Without loss of generality we may assume
that the leading digit ak is non-zero.) This representation is unique. (In spite
of the fact that the decimal representation of a real number is not unique -
1.0 = .9999.....) Similarly, each natural number can be written (uniqely) in
a binary expansion (or base 2),
a = ak 2k + ... + a1 2 + a0 ,
where 0 ≤ ai ≤ 1 are the bits. The binary representation of a is written as
a ∼ ak ...a1 a0 . Clearly, a is even if and only if a0 = 0. To find the binary
expansion of a natural number, perform the following steps.

37
(1) Find the “leading bit” ak by determining the largest power of 2 less
than or equal to a. Call this power k and let ak = 1.
(2) Subtract this power from a and replace a by this difference.
(3) If the result is non-zero, go to step 1; otherwise, stop.
This determines all the non-zero bits in the binary representation of a. The
other bits are 0.
Example 6. Find the binary representation of 130. The largest power of 2
less than or equal to 130 is 128 = 27 , so a7 = 1. The largest power of 2 less
than or equal to 2 = 130 − 128 is 2 = 21 , so a1 = 1. The other bits are zero:
a6 = a5 = a4 = a3 = a2 = a0 = 0, so
130 = 128 + 2 ∼ 10000010.
More generally, let us fix an integer m > 1. Each natural number can be
written in an m-ary expansion
a = ak mk + ... + a1 m + a0 ,
where 0 ≤ ai ≤ m − 1 are the m-ary digits. Again, the m-ary representation
of a is written as a ∼ ak ...a1 a0 . Clearly, m|a if and only if a0 = 0. To find
the m-ary expansion of a natural number, perform the following steps.
(1) Find ak by determining the largest power of m less than or equal to a.
Call this power k.
(2) Find the largest positive integer multiple of this power which is less
than or equal to a. This multiple will be the k-th digit ak .
(3) Subtract ak mk from a and replace a by this difference.
(4) If the result is non-zero, go to step 1; otherwise, stop.
Example 7. Find the 3-ary representation of 211. The largest power of 3
less than or equal to 211 is 81 = 34 , and 211 > 2 · 81 = 162 so a4 = 2.
The largest power of 3 less than or equal to 49 = 211 − 162 is 27 = 33 , and
49 < 2·27 so a3 = 1. The largest power of 3 less than or equal to 22 = 49−27
is 9 = 32 , and 22 > 2 · 9 so a2 = 2. The largest power of 3 less than or equal
to 4 = 22 − 18 is 3 = 31 , and 4 < 2 · 3 so a1 = 1. The last “bit” is 1:
a4 = 2, a3 = 1, a2 = 2, a1 = 1, a0 = 1, so
211 = 2 · 34 + 1 · 33 + 2 · 32 + 1 · 3 + 1 ∼ 21211.

38
Example 8. Convert 100101 from binary to 5-ary. In decimal (or “10-ary”),
100101 is

1 · 25 + 0 · 24 + 0 · 23 + 1 · 22 + 0 · 21 + 1 · 20 = 32 + 4 + 1 = 37.

In 5-ary,
37 = 1 · 52 + 2 · 51 + 2 · 50 ∼ 122.

6.2 Modular inverses and extended Euclidean algo-


rithm
For a positive integer m, two integers a and b are said to be congruent modulo
m, written:

a ≡ b (mod m),
if their difference a − b is an integer multiple of m. We write m|n if the
integer m divides the integer n, so m|n if and only if n ≡ 0 (mod m). The
number m is called the modulus of the congruence. This congruence symbol
≡ satisfies the same properties as = between integers, except (possibly) for
the cancellation law. All the following hold:

• if a ≡ b (mod m) and c ≡ d (mod m) then

ac ≡ bd (mod m) and a + c ≡ b + d (mod m).

• if a ≡ b (mod m) and b ≡ c (mod m) then

a ≡ c (mod m).

• a ≡ b (mod m) if and only if b ≡ a (mod m).

• For all c ∈ Z,
a ≡ a + cm (mod m).

As an example, we prove ac ≡ bd (mod m): Since a ≡ b (mod m) and


c ≡ d (mod m), we have ac ≡ bc (mod m) and bc ≡ bd (mod m). Therefore
m divides ac − bc and also divides bc − bd. It therefore must divide their
difference, ac − bd. This completes the proof. The others are left to the
student, if he or she is interested.

39
If bc ≡ 1 (mod m) then we say b is the modular inverse of c (mod m),
written

b = c−1 (mod m).

Lemma 9. The inverse of c (mod m) exists if and only if c and m are


relatively prime, i.e., they have no prime factors in common.

The greatest common divisor of c, m is denoted gcd(c, m).


We say c, m are relatively prime if and only if gcd(c, m) = 1. For example,
7 and 12 are relatively prime but 12 and 15 are not. The number of positive
integers c less than m which are relatively prime to m is called Euler’s phi
function (also called Euler’s totient function) and denoted
Y
φ(m) = |{1 ≤ c ≤ m − 1 | gcd(c, m) = 1} = m (1 − 1/p). (1)
p|m

For example, φ(90) = 90(1−1/2)(1−1/3)(1−1/5) = 24, so there are exactly


24 integers less than 90 which have no 2 or 3 or 5 in their prime factorization.
Question How do you compute the modular inverse c−1 (mod m) (if it ex-
ists)?
There are several methods, some better than others.
Method 1: Search over all b ∈ {1, 2, . . . , m − 1} and test if bc ≡ 1
(mod m) holds or not.
This is very inefficient.
Method 2: Use

Lemma 10. (Fermat’s Little Theorem/Euler’s Theorem) If gcd(c, m) = 1


then

cφ(m) ≡ 1 (mod m).

This gives us a formula for the inverse:

c−1 = cφ(m)−1 (mod m).


It turns out cφ(m)−1 (mod m) is easy to compute using repeated squaring:
To compute cn (mod m) (where n > 1)

40
1. Write n as a sum of powers of 2 (the “binary expansion” of n):

n = 2k + . . . (smaller powers of 2),

where k is about log2 (n).


k
2. To compute c2 , compute

c1 = c2 (mod m),

then

3.
c2 = c21 (mod m) = (c2 )2 = c4 (mod m),
k
etc, until you get to c2 . This requires k multiplications (mod m).

4. To compute cn (mod m), multiply those c1 , c2 , . . . , ck occuring the in


the binary expansion above.

As there are at most k multiplications in the last step, in general, we can


compute cn (mod m) in O(log n) steps3 . This is an example of an algorithm
which is “linear time” (the number of computations is linear in the number
of digits needed to write n down).

Example 11. Here is a table of inverses mod 11:

a 1 2 3 4 5 6 7 8 9 10
1
a
1 6 4 3 9 2 8 7 5 10

For example, 7−1 ≡ 8 (mod 11) because 7·8 = 56 = 1+55 ≡ 1 (mod 11).

Method 3: (Extended Euclidean algorithm) This is based on

Lemma 12. (Bezout’s Lemma) If a > 1 and b > 1 are integers with greatest
common divisor d, then there exist integers x and y such that

ax + by = d.
3
The “big O” notation is defined as follows: We say f (n) = O(log(n)) if and only if
there is a constant C > 0 such that f (n) ≤ C log(n) for all n > 1. The constant C will, in
this case, implicitly depends on m.

41
proof: Consider the set

ha, bi = {ra + sb | r ∈ Z, s ∈ Z}.


Since d divides a and b, this set ha, bi must be contained in the set

hdi = {td | t ∈ Z},


i.e., ha, bi ⊂ hdi.
Let c > 0 be the smallest integer such that

hci ⊂ ha, bi.


(Note that hdi ⊂ ha, bi so we have c ≤ d.) Suppose now hci = 6 ha, bi, so hci is
a proper subset of ha, bi. Suppose n = ax + by is the smallest positive integer
in ha, bi which is not in hci. By the integer “long division” algorithm, there is
a remainder r < c and a quotient q such that n = qc + r. But n ∈ ha, bi and
qc ∈ hci ⊂ ha, bi, so therefore r = n = qc ∈ ha, bi. Therefore, hri ⊂ ha, bi.
This is a contradiction to the assumption that c was as small as possible.
Therefore,

hci = ha, bi.


In fact, hci = ha, bi implies c|a and c|b, so c = d = gcd(a, b).
Bézout’s lemma follows immediately from hdi = ha, bi. 
Example 13. For example, gcd(12, 15) = 3. Obviously, 15 − 12 = 3, so with
a = 12 and b = 15, we have x = −1 and y = 1.
Here is the SymPy syntax:
SymPy

>>> gcdex(15,12)
(1, -1, 3)

Here is the Sage syntax:


Sage

sage: xgcd(15,12)
(3, 1, -1)

42
Additionally, d is the smallest positive integer for which there are integer
solutions x and y for the preceding equation.

• Initial table: (a < b)

i q r u v
−1 b 1 0
0 a 0 1

• As you increment i, apply the recursive equations: qi = [ri−2 /ri−1 ],


ri = ri−2 − ri−1 qi , ui = ui−2 − ui−1 qi , vi = vi−2 − vi−1 qi .

• Stop when rk = 0 and then let

x = vk−1 , y = uk−1 .

To compute the inverse of c (mod m), assume gcd(c, m) = 1 and compute


x, y such that cx + my = 1. We have x (mod m) = c−1 (mod m).
How many steps does this take in the worst-case situation? This method
is also “linear time” and very efficient.
Suppose that b > a and that b is an n-bit integer (i.e., b ≤ 2n ). The
recursive equations are repeated over and over, as long as ri (which gets
re-assigned each step of the loop) stays strictly positive.
Some notation will help us understand the steps better. Call (a0 , b0 ) the
original values of (a, b). After the first step of the while loop, the values of
a and b get re-assigned. Call these updated values (a1 , b1 ). After the second
step of the while loop, the values of a and b get re-assigned again. Call these
updated values (a2 , b2 ). Similarly, after the j-th step, denote the updated
values of (a, b), by (aj , bj ). After the first step, (a0 , b0 ) = (a, b) is replaced
by (a1 , b1 ) = (b (mod a), a). Note that a > b/2 implies b (mod a) < b/2,
therefore we must have either 0 ≤ a1 ≤ b0 /2 or 0 ≤ b1 ≤ b0 /2 (or both). If
we repeat this while loop step again, then we see that 0 ≤ a2 ≤ b0 /2 and
0 ≤ b2 ≤ b0 /2. Every 2 steps of the while loop, we decrease the value of a by
a factor of 2. Therefore, this algorithm has complexity T (n) where

T (n) = O(n) = O(log2 (b)).


Such an algorithm is called a linear time algorithm, since it complexity is
bounded by a polynomial in n of degree 1.

43
Example 14.
i q r u v
−1 2697 1 0
0 2553 0 1
1 1 144 1 −1
2 17 105 −17 18
3 1 39 18 −19
4 2 27 −53 56
5 1 12 71 −75
6 2 3 −195 206
7 4 0 −− −−
Thus k = 7, so x = v6 = −195, y = u6 = 206. Indeed, 3 = gcd(2697, 2553) =
(−195) · 2697 + (206) · 2553.

6.3 Finite fields


A finite field is, as you might expect, a finite set which has a multiplication
operation ·” and an addition operation ‘+”and, together, they satisfy all the
expected properties of a field such as the real or complexes or rationals.
Definition 15. A field is a set F which has two binary operations, denoted
+ and ·, satisfying the following properties. For all a, b, c ∈ F , we have
1. a + b = b + a, (“addition is commutative”)
2. a · b = b · a, (“multiplication is commutative”)
3. (a + b) + c = a + (b + c), (“addition is associative”)
4. (a · b)c = a(b · c), (“multiplication is associative”)
5. (a + b) · c = a · c + b · c, (“distributive”)
6. there is an element 1 ∈ F such that a·1 = a, (“1 is a multiplicative
identity”)
7. there is an element 0 ∈ F such that a + 0 = a (“0 is a additive
identity”),
8. if a 6= 0 then there is an element, denoted a−1 , such that a · a−1 = 1
(“the inverse of any non-zero element exists”).

44
Here is the simplest example, sometimes called the Boolean field, written
GF (2).
Example 16. Let F = {0, 1}, with two binary operations, + (addition
(mod 2)), and · (integer multiplication). This field is used is the mathematics
of electrical circuits, with1 being “on” and 0 being “off ”.
The addition table is
+ 0 1
0 0 1
1 1 0
The multiplication table is
· 0 1
0 0 0
1 0 1
Example 17. Let F = {0, 1, 2}, with two binary operations, + (addition),
and · (multiplication), where each is computed (mod 3). This field is some-
times denoted GF (3).
The addition table is
+ 0 1 2
0 0 1 2
1 1 2 0
2 2 0 1
The multiplication table is
· 0 1 2
0 0 0 0
1 0 1 2
2 0 2 1
If p is any prime, then the integers (mod p), written Z/pZ, is a finite
field with p elements. It will also be denoted GF (p) and is called a prime
(Galois) field.
However, if m is any composite then Z/mZ (the integers (mod m)) is
not a field. For example, Z/4Z and Z/6Z is not a field. In neither case does
the element 2 have an inverse. (If you don’t know this already, then this is a
good exercise for yourself to verify this.)

45
Example 18. Here is an example of using Sage to compute in GF (5):
Sage

sage: GF5 = GF(5)


sage: F = GF(5)
sage: a = F(2); b = F(3) # 2 and 3 in GF(5)
sage: a+b
0
sage: -a
3
sage: -b
2
sage: 1/a
3
sage: 1/b
2

Sage computed the additive inverse of 2 in GF (5) and the multiplicative in-
verse of 2 in GF (5). Both (by an unusual coincidence) are equal to 3.
Here is a similar example using SymPy:
SymPy

>>> F = FF(2)
>>> F = FF(5)
>>> a= F(1)
>>> a+a
2 mod 5
>>> a+a+a
3 mod 5
>>> a+a+a+a
4 mod 5
>>> a+a+a+a+a
0 mod 5
>>> b = a+a+a
>>> a*b
3 mod 5
>>> a/b
2 mod 5

46
Here are some SymPy commands to produce (more or less) a multiplica-
tion table for Z/5Z:
SymPy

>>> L = [["*"]+range(5)]
>>> L = L+[[i]+[(i*x)%5 for x in range(5)] for i in range(5)]
>>> L
[[*, 0, 1, 2, 3, 4],
[0, 0, 0, 0, 0, 0],
[1, 0, 1, 2, 3, 4],
[2, 0, 2, 4, 1, 3],
[3, 0, 3, 1, 4, 2],
[4, 0, 4, 3, 2, 1]]

Similarly, here are SymPy commands to produce a multiplication table for


Z/6Z:
SymPy

>>> L = [["*"]+range(6)]
>>> L = L+[[i]+[(i*x)%6 for x in range(6)] for i in range(6)]
>>> L
[[*, 0, 1, 2, 3, 4, 5],
[0, 0, 0, 0, 0, 0, 0],
[1, 0, 1, 2, 3, 4, 5],
[2, 0, 2, 4, 0, 2, 4],
[3, 0, 3, 0, 3, 0, 3],
[4, 0, 4, 2, 0, 4, 2],
[5, 0, 5, 4, 3, 2, 1]]

You see the difference? The “lower right-hand corner” of the 5 × 5 table
is a Latin square. The “lower right-hand corner” of the 6 × 6 table is not.

If E and F are fields and


(a) as sets, F ⊂ E,
(b) the field operations for F are the restrictions of the field operations
for E,

47
then F is called a subfield of E and E is called an extension field of F , written
E/F .
It is a theorem (proven in most any textbook in abstract algebra) that if
F is a finite field then F must have pk elements, where p is a prime and k is
an integer. If k = 1 then F must be isomorphic to GF (p). (Two fields are
“isomorphic” if there is a bijective map from one to the other which preserves
the addition, multiplication operations.) The fields with k > 1 are so-called
extension fields and will not arise in these lectures.

7 The Hill cipher


The Hill cipher, invented by Lester S. Hill in 1920’s, it was the first poly-
graphic cipher in which it was practical to operate on more than three sym-
bols at once. However, there are no public records of it being used in practice,
to my knowledge.
However, there is a fascinating story of how Hill and his colleague Wisner
and Hunter College filed a patent for a telegraphic device encryption and
error-correction device which was roughly based on ideas arising form the
Hill cipher. See Christensen, Joyner and Torres [CJT] for more details.
The following discussion assumes an elementary knowledge of matrices.
First, each letter is first encoded as a number. We assume here that A ↔ 0,
B ↔ 1, . . . , Z ↔ 25, as in Figure 2.
We denote the integers {0, 1, . . . , 25} by Z/26Z. This is closed under ad-
dition and multiplication and satisfies the associative and distributive prop-
erties, as the integers Z do.
Suppose your message m consists of n capital letters, with no spaces. This
may be regarded an n-tuple M of elements of Z/26Z. A key in the Hill cipher
is ak × k matrix K, all of whose entries are in Z/26Z, such that the matrix
K is invertible (ie, that the linear transformation K : (Z/26Z)k → (Z/26Z)k
is one-to-one).
A plaintext string over an alphabet of order 26 is rewritten as a vector
p ∈ (Z/26Z)k using the correspondence above. A matrix K ∈ GL(k, Z/26Z)
is chosen to be the key matrix, where GL(m, R) denotes the set of invertible
transformations from Rm → Rm . The encryption is performed by computing

c = Kp, (2)

48
and rewriting the resulting matrix as a string over the same alphabet. De-
cryption is performed similarly by computing

p = K −1 c. (3)

7.1 Matrix inverses (mod m)


Clearly, we must learn how to invert matrices (mod m). Here are a few
methods to find the matrix inverse of K (mod m).

• Method 1: Use row-reduction (mod m) applied to the block matrix


(K, In )

• Method 2: Find the order of K, call it d. Compute K d−1 (mod m),


using repeated squaring.

• Method 3:

– Compute adj(K) = cof (K)t , the adjoint matrix of K.


– Compute r = 1/det(K) (mod m).
– Compute K −1 = r · adj(K) (mod m).

Example 19. Here’s an example using SymPy:


SymPy

>>> from sympy.crypto1 import *


>>> A = Matrix(2, 2, [1, 2, 3, 4])
>>> print matrix_inverse_mod(A, 5)
[ 3, 1]
[4, 2]

 
5 2
Example 20. The example of (mod 35) is not so easy to compute
7 3
using row-reduction by hand (try it!). However, it is easy to compute using
the adjoint formula and, of course, Sage can do it.

49
Sage

sage: Z35 = IntegerModRing(35)


sage: K = matrix(Z35, [[5,2],[7,3]])
sage: det(K)
1
sage: Kˆ(-1)
[ 3 33]
[28 5]

 
−1 3 33
So. K = .
28 5

7.2 Enciphering and deciphering


The enciphering algorithm uses a key K, which is a k × k matrix of integers
which is invertible (mod 26).

ALGORITHM:
INPUT:
key - a kxk invertible matrix K, all of whose entries are in Z26
m - string of n upper-case letters (the "plaintext" message)
(Note: Sage assumes that n is a multiiple of k.)
OUTPUT:
c - string of upper-case letters (the "ciphertext" message)

Identify the alphabet A, ..., Z with the integers 0, ..., 25.


Step 1: Compute from the string m a list L of corresponding
integers. Let n = len(L).
Step 2: Break the list L up into t = ceiling(n/k) sublists
L_1, ..., L_t of size k (where the last list might be
"padded" by 0’s to ensure it is size k).
Step 3: Compute new list C_1, ..., C_t given by C[i] = K*L_i
(arithmetic is done mod 26), for each i.
Step 4: Concatenate these into a list C = C_1 + ... + C_t.
Step 5: Compute from C a string c of corresponding letters.
This has length k*t.

50
The deciphering algorithm is basically the same, except that it uses the
inverse matrix K −1 (mod 26).

Example 21. Here is an example. We will encrypt “Go Navy! Beat Army!”
using Sage’s Hill cipher implementation.
Sage

sage: AS = AlphabeticStrings()
sage: HC = HillCryptosystem(AS,3)
sage: Z26 = IntegerModRing(26)
sage: K = matrix(Z26, [[1,0,1],[0,1,1],[2,2,3]])
sage: det(K)
25
sage: m = "GONAVYBEATARMYX"
sage: m = HC.encoding(m)
sage: ct = HC.enciphering(K, m)
sage: ct
GOHWRPBEFBISGSB
sage: HC.deciphering(K, c)
GONAVYBEATARMYX
# You can always have \sage pick the key for you:
sage: K = HC.random_key(); K
[ 0 3 25]
[ 0 13 2]
[19 8 8]
sage: det(K)
23

Let’s break this down by steps, as in the algorithm presented above.


First, we see what the corresponding numbers are in the plaintext “GO-
NAVYBEATARMYX” (we added an “X” at the end to make the length of
the message divisible by 3).
Sage

sage: AS = AlphabeticStrings()
sage: HC = HillCryptosystem(AS,3)
sage: Z26 = IntegerModRing(26)
sage: K = matrix(Z26, [[1,0,1],[0,1,1],[2,2,3]])

51
sage: m = "GONAVYBEATARMYX"
sage: pt = HC.encoding("GONAVYBEATARMYX")
sage: ct = HC.enciphering(K, pt); ct
GOHWRPBEFBISGSB
sage: [A.index(x) for x in m]
[6, 14, 13, 0, 21, 24, 1, 4, 0, 19, 0, 17, 12, 24, 23]

Next, we create vectors corresponding to each block of size 3 and encrypt


them.
Sage

sage: v1 = vector(Z26, [6, 14, 13])


sage: c1 = K*v1; c1
(19, 1, 1)
sage: v2 = vector(Z26, [0, 21, 24])
sage: c2 = K*v2; c2
(24, 19, 10)
sage: v3 = vector(Z26, [1, 4, 0])
sage: c3 = K*v3; c3
(1, 4, 10)
sage: v4 = vector(Z26, [19, 0, 17])
sage: c4 = K*v4; c4
(10, 17, 11)
sage: v5 = vector(Z26, [12, 24, 23])
sage: c5 = K*v5; c5
(9, 21, 11)

Next, we put these numbers into a list and find the corresponding letters:
Sage

sage: ct0 = [19, 1, 1, 24, 19, 10, 1, 4, 10, 10, 17, 11, 9, 21, 11]
sage: [A[i] for i in ct0]
[’T’, ’B’, ’B’, ’Y’, ’T’, ’K’, ’B’, ’E’, ’K’, ’K’, ’R’, ’L’, ’J’, ’V’, ’L’]

Something is wrong - this doesn’t look like “GOHWRPBEFBISGSB” at all!

52
The problem is that the Sage implementation uses matrix multiplication
on the left, not on the right as we are used to in the United Stated (and many
other parts of the world). Therefore, we must replace K by it’s transpose,
K t.
Sage

sage: Kt = K.transpose()
sage: Kt
[1 0 2]
[0 1 2]
[1 1 3]
sage: c1t = Kt*v1; c1t
(6, 14, 7)
sage: c2t = Kt*v2; c2t
(22, 17, 15)
sage: c3t = Kt*v3; c3t
(1, 4, 5)
sage: c4t = Kt*v4; c4t
(1, 8, 18)
sage: c5t = Kt*v5; c5t
(6, 18, 1)

This does indeed agree with the above list, [6, 14, 13, 0, 21, 24, 1, 4, 0, 19, 0, 17, 12, 24, 23],
of numbers corresponding to the letters in the cipher text.

Example 22. Next, let’s try using SymPy to work through an example.
SymPy

>>> pt = "meet me on monday"


>>> key = Matrix( ((1,2), (3,5)) )
>>> encipher_hill(pt, key)
UEQDUEODOCTCWQ
>>> pt = "meet me on tuesday"
>>> encipher_hill(pt, key)
UEQDUEODHBOYDJYU

53
Using the plaintext “Meet me on Monday” (which is translated into “MEET-
MEONMONDAY”) and a block size of k = 2, SymPy gives the ciphertext
“UEQDUEODOCTCWQ.” On the other hand, using the similar plaintext
“Meet me on Tuesday” (which is translated into “MEETMEONTUESDAY”)
and a block size of k = 2, SymPy gives the similar ciphertext “UEQDUEOD-
HBOYDJYU.”
We can also decrypt using SymPy:
SymPy

>>> ct = "UEQDUEODOCTCWQ"
>>> key = Matrix( ((1,2), (3,5)) )
>>> decipher_hill(ct, key)
MEETMEONMONDAY
>>> ct = "UEQDUEODHBOYDJYU"
>>> decipher_hill(ct, key)
MEETMEONTUESDAYA

In each example, we have recovered the plaintext, except that “Meet me on


Tuesday” got padded by an extra character (“A”).
Note: When you use SymPy, you don’t need to worry about using the
transpose matrix, as we did with Sage.

Example 23. Let us try another example, this time using Sage.
Sage

sage: AS = AlphabeticStrings()
sage: HC = HillCryptosystem(AS,2)
sage: Z26 = IntegerModRing(26)
sage: K = matrix(Z26, [[2,1],[13,20]])
sage: det(K)
1

We see the block size is 2. Again, let’s encrypt “Go Navy! Beat Army!”
First, we use Sage’s implementation of Hill’s cipher.

54
Sage

sage: m = "GONAVYBEATARMY"
sage: pt = HC.encoding(m)
sage: ct = HC.enciphering(K, pt); ct
MAANQHCDNQNCYY
sage: A = AS.alphabet()
sage: [A.index(x) for x in m]
[6, 14, 13, 0, 21, 24, 1, 4, 0, 19, 0, 17, 12, 24]
sage: c = "MAANQHCDNQNCYY"
sage: [A.index(x) for x in c]
[12, 0, 0, 13, 16, 7, 2, 3, 13, 16, 13, 2, 24, 24]

We have the corresponding numbers, so let’s perform the block encryption


“by hand”:
Sage

sage: v1 = vector(Z26, [6, 14])


sage: Kt = K.transpose()
sage: c1t = Kt*v1; c1t
(12, 0)
sage: v2 = vector(Z26, [13, 0])
sage: c2t = Kt*v2; c2t
(0, 13)
sage: v3 = vector(Z26, [21, 24])
sage: c3t = Kt*v3; c3t
(16, 7)
sage: v4 = vector(Z26, [1, 4])
sage: c4t = Kt*v4; c4t
(2, 3)
sage: v5 = vector(Z26, [0, 19])
sage: c5t = Kt*v5; c5t
(13, 16)
sage: v6 = vector(Z26, [0, 17])
sage: c6t = Kt*v6; c6t
(13, 2)
sage: v7 = vector(Z26, [12, 24])
sage: c7t = Kt*v7; c7t
(24, 24)
sage: C = [12, 0, 0, 13, 16, 7, 2, 3, 13, 16, 13, 2, 24, 24]
sage: [A[i] for i in C]

55
[’M’, ’A’, ’A’, ’N’, ’Q’, ’H’, ’C’, ’D’, ’N’, ’Q’, ’N’, ’C’, ’Y’, ’Y’]

We got the ciphertext returned by Sage, as expected.


A few words regarding Sage’s syntax. We see that to decipher using Sage’s
implementation, we can’t use (K t )−1 of K t with the deciphering method, but
we can use K with the deciphering method, or K −1 with the enciphering
method:
Sage

sage: HC.deciphering(Ktˆ(-1), ct)


YANANKHIQVCBUM
sage: HC.deciphering(Kt, ct)
GANABOLGKTYROW
sage: HC.deciphering(K, ct)
GONAVYBEATARMY
sage: HC.enciphering(Kˆ(-1), ct)
GONAVYBEATARMY

Security concerns: This cipher is linear, so can be broken by a known plain-


text attack. To determine the key from known plaintext, you set up the
corresponding equations c = Kp and solve for K. It is also susceptible to a
known-ciphertext attack. You set up the equations p = K −1 c and adaptively
try various keys K for which p matches some dictionary entries.

8 PK Cryptosystems
This section discusses some commonly used public key (PK) cryptosystems.

8.1 RSA
RSA stands for Ron Rivest, Adi Shamir and Leonard Adleman, who first
publicly described the algorithm in 1977. In 1997, the work of Clifford Cocks,
a mathematician at GCHQ, was declassified. He had developed an equivalent
cryptosystem in 1973.

56
8.1.1 Set up and examples
Alice wants to talk to Bob.
Bob secretly chooses two large distinct primes p, q. He computes n = pq
and sends that to Alice. The length of n in bits is the “key length.”
Next, Bob computes φ(n) = (p − 1)(q − 1), where φ is Euler’s totient
function, and chooses an integer e such that 1 < e < φ(n) and greatest
common divisor gcd(e, φ(n)) = 1. Bob sends e to Alice - this is the “public
key exponent.”
The public key is the pair (n, e).
Finally, Bob determine d, the multiplicative inverse of e (mod φ(n)). We
call d the private key exponent or private key4 .
Encryption: Alice wishes to send a message M to Bob. She first turns
M into an integer (or a sequence of such integers) m, such that 0 ≤ m <
n by using an agreed-upon protocol. She then computes the ciphertext c
corresponding to m by

c ≡ me (mod n). (4)


Decryption: Bob can recover m from c by using the private key exponent
d via computing

m ≡ cd (mod n). (5)


Here is a Sage example.

Example 24. First, we pick our primes.


Sage

sage: p = next_prime(1000) ## secret


sage: q = next_prime(1010) ## secret
sage: n = p*q ## public
sage: n; p; q
1022117
1009
1013

4
Sometimes - with some slight inaccuracy - we call (n, d) the private key.

57
Next, we compute the public key.
Sage

sage: k = euler_phi(n) ## = (p-1)(q-1),


sage: e = 123451 ## public key exponent
sage: k; xgcd(k, e)
1020096
(1, -36308, 300019)

Since p, q are unknown, we assume (with no evidence otherwise) that φ(n)


is also unknown, or at least impractical to compute. Here φ is the Euler
φ-function in (1). Now, we compute our private key.
Sage

sage: x = xgcd(k, e)[1]


sage: y = xgcd(k, e)[2]
sage: d = y%k ## private key exponent
sage: d; y*e%k; d*e%k
300019
1
1

Finally, we encrypt, then decrypt a message.


Sage

sage: m = randint(100, k); m


19861
sage: c = mˆe%n; c ## slow method
482896
sage: power_mod(m,e,n) ## faster method
482896
sage: power_mod(c,d,n)
19861

58
Here is another example.
Example 25. This time, we use some Sage code to do the computations.
Sage

sage: p,q,e = 3,5,7


sage: n, e = rsa_public_key(p,q,e)
sage: n;e
15
7
sage: rsa_private_key(p,q,e)
(15, 7)
sage: pt = 12
sage: rsa_encrypt(p,q,e,pt)
3
sage: ct = 3
sage: rsa_decrypt(p,q,e,pt)
12

This works, but it is a bit strange. We see m = 12 is not relatively prime


to n = 15 and the private key exponent is d = 7, which is the same as the
public key exponent e = 7.
Here is another Sage example.
Sage
Example 26.
sage: p,q,e = 7,11,17
sage: n,e = rsa_public_key(p,q,e); n; e
77
17
sage: rsa_private_key(p,q,e)
(77, 53)
sage: pt = 51
sage: rsa_encrypt(p,q,e,pt)
39
sage: ct = 39
sage: rsa_decrypt(p,q,e,ct)
51

59
Here is a SymPy example.
Example 27. First, we pick our primes and compute the public key.
SymPy

>>> p,q,e = 3,5,7


>>> n, e = rsa_public_key(p,q,e)
>>> n
15
>>> e
7

Now, we compute our private key.


Sage

>>> p,q,e = 3,5,7


>>> rsa_private_key(p,q,e)
(15, 7)

Finally, we encrypt, then decrypt a message.


Sage

>>> p,q,e = 3,5,7


>>> pt = 12
>>> encipher_rsa(p,q,e,pt)
3
>>> ct = 3
>>> decipher_rsa(p,q,e,ct)
12

Security issues: (1) Of course, if integers were easy to factor quickly in


general, then RSA would be insecure. However, it appears to be a strange
fact of life that large “random” integers are typically very hard to factor into

60
a product of primes.
(2) Since φ(n) = (p − 1)(q − 1) = pq − p − q + 1 = n − p − q + 1, if we could
somehow get only p + q (as opposed to p and q individually, that would be
enough to break RSA. In other words, given that an integer n is a product of
two primes, can you find the sum of the factors? This too seems to be very
hard.
(3) When e = 3, it turns out that RSA is insecure in some cases. See Boneh’s
survey [Bo].
(4) Under the “generalized Riemann hypothesis” (which is too technical to
define here, but widely believed by mathematicians to be true), it has been
shown by the computer scientist Gary Miller that the following computations
are of equal computational complexity, up to a polynomial: (1) factoring n
into p and q, (2) computing d, given n and e. For details, see Miller [Mi].

8.2 Kid RSA


Kid RSA is a version of RSA useful to teach grade school children since it
does not involve exponentiation.
Alice wants to talk to Bob. Bob generates keys as follows. Key generation:

• Select positive integers a, b, A, B at random.


• Compute M = ab − 1, e = AM + a, d = BM + b, n = (ed − 1)/M .
• The public key is (n, e). Bob sends these to Alice. The private key is
d, which Bob keeps secret.

Example 28. If a = 3, b = 4, A = 5, B = 6 then the public key is


(n, e) = (369, 58). The private key is d = 70.
Encryption: If m is the plaintext message then the ciphertext is c = me
(mod n).
Example 29. If n = 369, e = 58 and the plaintext is m = 200 then the
ciphertext is c = 200 · 58 (mod 369) = 161.
Decryption: If c is the ciphertext message then the plaintext is m = cd
(mod n).
Example 30. If n = 369, e = 58 and the ciphertext is c = 161 then the
plaintext is m = 161 · 70 (mod 369) = 200.

61
Here is Sage code to compute the public and private keys, with an exam-
ple.
Sage

sage: a, b, A, B = 3, 4, 5, 6
sage: n, e = kid_rsa_public_key(a,b,A,B)
sage: n;e
369
58
sage: d = kid_rsa_private_key(a,b,A,B); d
70

Here is Sage code for encryption and decryption, with an example.


Sage

sage: pt = 200
sage: pk = kid_rsa_public_key(a,b,A,B)
sage: kid_rsa_encrypt(pt, pk)
161
sage: d = kid_rsa_private_key(a,b,A,B)
sage: pt = 200
sage: pk = kid_rsa_public_key(a,b,A,B)
sage: ct = kid_rsa_encrypt(pt, pk)
sage: kid_rsa_decrypt(ct, pk, d)
200

Here is the (very similar) SymPy code for Kid RSA. First, the keys.
SymPy

>>> a, b, A, B = 3, 4, 5, 6
>>> n, e = kid_rsa_public_key(a,b,A,B)
>>> n, e
(369, 58)
>>> d = kid_rsa_private_key(a,b,A,B); d
70

62
Next, the encryption and decryption.
SymPy

>>> pt = 200
>>> pk = kid_rsa_public_key(a,b,A,B)
>>> kid_rsa_encrypt(pt, pk)
161
>>> d = kid_rsa_private_key(a,b,A,B)
>>> pt = 200
>>> pk = kid_rsa_public_key(a,b,A,B)
>>> ct = encipher_kid_rsa(pt, pk)
>>> decipher_kid_rsa(ct, pk, d)
200

The security issues are discussed in the next subsection.

8.3 Breaking Kid RSA


How do we break Kid RSA? If the public key is (n, e), we need to find a way
to compute the private key from the public key more efficiently than “brute
force” searching each value of d which works. It turns out, one can efficiently
compute the private key without having any ciphertext.
Recall a, b, A, B were selected at random and then we computed M =
ab − 1, e = AM + a, d = BM + b, n = (ed − 1)/M . Note

de = (AM + a)(BM + b) = (ABM + aB + bA)M + ab


= (ABM + aB + bA + 1)M + 1,

therefore n = (ed − 1)/M = ABM + aB + bA + 1, so

de = nM + 1.
This last equation allows us to compute d (mod n) by computing e−1 (mod n)
using Bezout’s Lemma 12.

Example 31. Here is a Sage example.

63
Sage

sage: n = 369; e = 58
sage: n = 369; e = 58
sage: xgcd(n,e)
(1, -11, 70)
sage: d = xgcd(n,e)[2]; d
70

We see that if (n, e) = (369, 58) then the Extended Euclidean algorithm com-
putes the private key d = 70.
Similar commands will work using SymPy’s gcdex command instead. (See
Example 13 for comparison.)

8.4 The discrete log problem


In some cases, keys must be shared in a secure manner, and to solve this
problem, key exchange methods have been developed. Before discussing one
of the most popular, the Diffie-Hellman key exchange, we discuss the math-
ematical problem that underlies it. Some of this discussion follows David
Kohel’s notes [Ko].
Suppose m > 1 and a, b are integers relatively prime to m. When

b ≡ ax (mod m),
then we say that x is the discrete log of b (mod m): x = loga (b). Finding x,
given a, b (and of course m) is called the discrete log problem (DLP). There
are no known “fast, efficient” ways to solve this problem in general.
The “brute force” algorithm for solving the discrete logarithm problem
for loga (b) is to compute 1, a, a2 , . . . until a match is found with b. There are
faster methods than this, but none that are polynomial time in the length of
m (as a binary string).

8.5 Diffie-Hellman-Merkle key exchange


Whitfield Diffie and Martin Hellman proposed the following scheme for es-
tablishing a common key. The Diffie-Hellman key exchange method allows

64
two parties that have no prior knowledge of each other to jointly establish
a shared secret key over an insecure communications channel. This key can
then be used to encrypt subsequent communications using a symmetric key
cipher.
The scheme was first published by Diffie and Hellman in 1976, although it
later emerged that it had been separately invented a few years earlier within
GCHQ, the British signals intelligence agency, by Malcolm J. Williamson but
was kept classified. In 2002, Hellman suggested the algorithm be called Diffie-
Hellman-Merkle key exchange in recognition of Ralph Merkle’s contribution
to the invention of public-key cryptography.

1. Alice and Bob decide on a large prime number p and a primitive element
a of Z/pZ, both of which can be made public.

2. Alice chooses a secret random x with gcd(x, p1) = 1 and Bob chooses
a secret random y with gcd(y, p1) = 1.

3. Alice sends Bob ax (mod p) and Bob sends Alice ay (mod p).

4. Each is able to compute a session key K = axy = (ax )y = (ay )x


(mod p).

Example 32. Here is a Sage example.


Sage

sage: p = 101; is_prime(p); a = primitive_root(p) # Alice and Bob


True
sage: print p, a # Alice and Bob
101 2
sage: x = 33 # Alice
sage: gcd(x,p-1)
1
sage: y = 51 # Bob
sage: gcd(y,p-1)
1
sage: aˆx%p # Alice and Bob
35
sage: aˆy%p # Alice and Bob
99
sage: 35ˆy%p # Bob

65
66
sage: 99ˆx%p # Alice
66

We see that ...

8.6 ElGamal encryption


Bob sends message m to Alice as follows:

• Bob obtains Alice’s public key (p, a, ak (mod p)).

• Bob represents the message m as an integer ≤ m ≤ p − 1 (by choosing


p sufficiently large, this is always possible; alternatively, the message
may be broken up into smaller pieces, each piece belonging to the range
0, 1, ..., p − 1).

• Bob selects a random integer `, 1 ≤ ` ≤ p − 2.

• Bob computes d ≡ a` (mod p)) and e ≡ m · (ak )` (mod p)), 1 ≤ d, e ≤


p − 1.

• Bob sends the “ciphertext” c = (d, e) to Alice.

Alice decrypts m as follows:

• Alice uses her private key to compute dp−1−k (mod p)) and dp−1−k ≡
(a−k` (mod p)).

• Alice computes m ≡ (dp−1−k ) · e (mod p)).

Example 33. Using the numerical position in the alphabet, “A” is the 1st
letter, which corresponds numerically to 01, “B” to 02, ..., “Z” to 26. A word
corresponds to the associated string of numbers. Note, for example, “ab” is
0102 = 102 but “ob” is 1502.
Pick p = 150001, a = 7. Pick k = 113, so that ak = 7113 ≡ 66436 (mod p).
Alice’s public key is (150001, 7, 66436). Alice’s private key is 113. Suppose
Bob is Alice’s son and wants to send the message “Hi Mom”. He converts this

66
to m = 080913 = 809 and m = 131513. Bob picks ` = 1000 and computes
7` ≡ 90429 (mod 150001)), so d = 90429. The encryption is 809 · (7113 )1000 ≡
15061 = e (mod 150001)) and 131513 · (7113 )1000 ≡ 57422 = e (mod 150001)),
so c = (90429, 15061) and c = (90429, 57422) are sent to Alice.
Alice computes dp−1−k = 90427149887 ≡ 80802 (mod 150001)). Then m ≡
80802 · e (mod 150001)) returns the original messages m = 809 and m =
131513. Since 809 has an odd number of digits, the first digit must have
been a 0. Alice can see that 809 must mean that the first letter comes from
the 8 = 08, the second letter comes from a 09. These spell out “hi”. For
m = 131513, the third letter must come from the 13, the fourth letter must
come from the 15, and the last must be a 13. These spell out “hi” and “mom”.
Adding a space and proper capitalization, this is “Hi Mom”!

Exercise 8.1. (a) Let p = 541, a = 2. Pick k = 113, ` = 101. Encrypt the
messages m = 200 and m = 201 using ElGamal.
(a) Let p = 541, a = 2, k = 101. Decrypt the ElGamal ciphers c =
(54, 300) and c = (54, 301).

9 Stream ciphers
A “stream cipher” is a cryptosystem derived using a sequence of digits as
the key, where the plaintext is another such sequence and these streams are
combined in some prescribed manner to create the ciphertext.

9.1 Binary stream ciphers


A cryptographic bit-stream is a cryptosystem derived using a sequence of
binary digits for the key, where the plaintext is also a binary sequence and
these streams are added (componentwise, (mod 2)) to create the ciphertext.
Ideally, a cryptographic bit stream would have infinite length and could
achieve complete randomness. The reality of practical application and con-
struction techniques necessitates the use of only finite sequences. Since finite
sequences can never be truly random, there are certain properties singled
out that are associated with randomness. Golomb states these properties
(roughly speaking) as:

1. The number of 1’s is approximately equal to the number of 0’s.

67
2. The runs of consecutive 1’s or 0’s frequently occur with short runs more
frequent than long runs.

3. The sequence possesses an auto-correlation function, which is peaked


in the middle and tapering of rapidly at the ends.

More precise version will be given later in Definition 41.

Example 34. Following Brock [Br], we provide an example of how a message


might be encrypted for secrecy and then decrypted by the authorized receiver so
that the information can be read. In order for Alice and Bob to send messages
to each other using computers, they must convert the English syntax of the
message into a binary form. For our purposes we will allow the following
table define our

SPACE = 0000
L = 1111
A = 0001
! = 1000
B = 0010
# = 1001
E = 0011
? = 1010
M = 0100
; = 1011
R = 0101
, = 1100
T = 0110
. = 1101
Y = 0111
Q = 1110

Suppose Alice wanted to send the message M =“BEAT ARMY!” to Bob,


but she wanted to keep it secret. By converting “BEAT ARMY!” from English
to binary, we have a string of binary bits

pt = 0010001100010110000000010101010001111000.

68
In order to generate a stream cipher that only Alice and Bob know, to be used
as the key, some method of secret key exchange is required. For example, each
person can generate a pseudo-random sequence of sufficient length. Alice will
send her sequence to Bob and Bob will send his sequence to Alice. They
will then add their sequences together bit-wise to produce a common stream
cipher. (It will be known to only to the two of them unless someone has
intercepted both of their communications.) For our purposes, suppose the
resultant sequence from the two individual sequences is

k = 1101011001000111101011001000111101011001
Alice now has a message in binary format and a cipher to encode the mes-
sage. Adding the cipher to the message, Alice gets the encrypted message or
ciphertext, ct:

0010001100010110000000010101010001111000 pt
+ 1101011001000111101011001000111101011001 k
-----------------------------------------
1111010101010001101011011101101100100001 ct

If a third party, Charlie, were to intercept the ciphertext and tried to read it,
knowing that the computers used the above table to talk to one another, he
would decrypt the ciphertext as “LRRA?..;BA”. Notice that both ‘A’s in the
original message where changed to two different letters (‘R’ the first time and
‘.’ the second time) and that ‘E’ and ‘A’ both are changed to ‘R’ and ‘T’ and
‘!’ are both changed to ‘A’. This helps prevent a cryptanalyst from breaking
the code by mapping each character to something different everytime. The
mapping appears random since the stream cipher used was a pseudo-random
sequence. When Bob receives the ciphertext, however, he is able to decrypt it
since he has the stream cipher that he and Alice created earlier. By adding
the stream cipher to the ciphertext, he will uncover the original message:

ct + k = pt. (6)
Now Bob can use the table to decode the message from binary into English
and receives “BEAT ARMY!” from Alice.

9.2 Background on solving recurrence equations


Solving linear homogeneous recurrence equations with constant coefficients

69
A linear homogeneous recurrence relation with constant coefficients is an
equation of the form:

an = c1 an−1 + c2 an−2 + · · · + cd an−d (7)


where the d coefficients ci (for all i) are constants. The integer d is called the
order and the numbers a0 , . . . , ad−1 . More precisely, this is an infinite list
of simultaneous linear equations, one for each n > d − 1. A sequence which
satisfies a relation of this form is called a linear recurrence sequence (LRS).
There are d degrees of freedom for LRS, i.e., the initial values a0 , . . . , ad−1
can be taken to be any values but then the linear recurrence determines the
sequence uniquely.
The same coefficients yield the characteristic polynomial (also auxiliary
polynomial or a connection polynomial)

p(t) = td − c1 td−1 − c2 td−2 − · · · − cd (8)


whose d roots play a crucial role in finding and understanding the sequences
satisfying the recurrence. If the roots r1 , r2 , . . . , rd are all distinct, then the
solution to the recurrence takes the form

an = k1 r1n + k2 r2n + · · · + kd rdn , (9)


where the coefficients ki are determined in order to fit the initial conditions of
the recurrence. When the same roots occur multiple times, the terms in this
formula corresponding to the second and later occurrences of the same root
are multiplied by increasing powers of n. For instance, if the characteristic
polynomial can be factored as (x − r)d , with the same root r occurring d
times, then the solution would take the form

an = k1 rn + k2 nrn + · · · + kd nd−1 rn .
Linear recursive sequences are precisely the sequences whose generating
function is a rational function: the denominator is the polynomial obtained
from the auxiliary polynomial by reversing the order of the coefficients, and
the numerator is determined by the initial values of the sequence.

9.3 Matrix reformulation


Given a linearly recursive sequence, let C be the transpose of the companion
matrix of the characteristic polynomial p(t), that is

70
 
0 1 0 ··· 0
0
 0 1 ··· 0 
 .. .
. .. .. .. 
C=. . . . .
 
0 0 0 ··· 1
cd cd−1 cd−2 ··· c1
In other words,

det(C − tId ) = p(t), (10)


where p(t) is as in (8). In particular, the roots of p(t) are the eigenvalues of
C. Furthermore, if p(t) has distinct roots λ1 , . . . , λd then the the eigenvector
of C corresponding to λi is the vector
 
1
 λi 
 ..  .
 
 . 
λid−1
Observe that
   
an−d+2 an−d+1
 ..   . 
 .  = C ·  .. 
an+1 an
and so
   
an−d+1 a0
 ..  n .. 
 . =C  .  (11)
an ad−1

Example 35. For the Fibonacci sequence,

fn = fn−1 + fn−2 , f0 = 0, f1 = 1.
We have p(t) = t2 − t − 1, and
 
0 1
C=
1 1

71
and the above identity becomes
   
fn−1 fn−2
=C·
fn fn−1
For example, taking n = 2 gives the initial condition
     
f1 0 1 f
= · 0
f2 1 1 f1
Here is a Sage example.
Sage

sage: C = matrix(QQ, [[0,1],[1,1]])


sage: f0 = vector(QQ, [0,1])
sage: C*f0
(1, 1)
sage: Cˆ2*f0
(1, 2)
sage: Cˆ3*f0
(2, 3)
sage: Cˆ4*f0
(3, 5)
sage: Cˆ5*f0
(5, 8)

The SymPy commands are similar:


Sage

>>> C = Matrix(2, 2, [0, 1, 1, 1])

>>> f0 = Matrix(2, 1, [0, 1])

>>> print C*f0


[1]
[1]

>>> print C**2*f0


[1]

72
[2]

>>> print C**3*f0


[2]
[3]

>>> print C**4*f0


[3]
[5]

>>> print C**5*f0


[5]
[8]

Here is how to compute C n in general: Determine an eigenbasis v1 , . . . , vd


corresponding to eigenvalues λ1 , . . . , λd . Then express the seed (the initial
conditions of the recurrence sequence) as a linear combination of the eigen-
basis vectors:
 
a0
 .. 
 .  = b1 v1 + · · · + bd vd
ad−1
Then it conveniently works out that:

   
an+d−1 ad−1
 ..  n .  n n n
 .  = C  ..  = C (b1 v1 +· · ·+bd vd ) = λ1 b1 v1 +· · ·+λd bd vd (12)
an a0

Example 36. The example in the case of the Fibonacci numbers has become
known as Binet’s formula:
ϕn − ψ n ϕn − ψ n
fn = = √ (13)
ϕ−ψ 5
where √
1+ 5
ϕ= ≈ 1.6180339887 . . .
2

73
is the golden ratio (sequence A001622 in the online OEIS, http: // oeis.
org/ ), and √
1− 5 1
ψ= =1−ϕ=− .
2 ϕ
To see this, note that ϕ and ψ are both solutions of the equation

x2 = x + 1.

Example 37. Compute, using Sage, the example:

xn = 4xn−1 − xn−2 − 6xn−3 , x0 = 0, x1 = 1, x2 = 2.


{xn } = {0, 1, 2, 7, 20, . . . }.
This can be written as as a matrix equation as
    
xn−2 0 1 0 xn−3
xn−1  =  0 0 1  xn−2 
xn −6 −1 4 xn−1
or as
    
xn 4 −1 −6 xn−1
xn−1  =  1 0 0  xn−2  .
xn−2 0 1 0 xn−3
 n
0 1 0
Let us use the former equation. To compute  0 0 1  , we com-
  −6 −1 4
0 1 0
pute the diagonalization of  0 0 1 . The Sage code below shows that
−6 −1 4
the eigenvalues are

λ1 = 3, λ1 = 2, λ1 = −1,
and corresponding eigenvectors are
     
1 1 1
~v1 = 3 , ~v2 = 2 ,
   ~v3 = −1 .
9 4 1

74
If
 
1 1 1
P =  3 2 −1 
9 4 1
is the matrix of eigenvectors and
 
3 0 0
D= 0 2 0 
0 0 −1
the diagonal matrix of eigenvalues then

A = P DP −1
is the diagonalization. We compute arbitrary poswers using An = P Dn P −1 .
This can be computed efficiently  (mod p) using 
repeated squares.
4 −1 −6
Incidently, the eigenvalues of  1 0 0  are
0 1 0
λ1 = 3, λ1 = 2, λ1 = −1,
and corresponding eigenvectors are
     
9 4 1
~v1 = 3 , ~v2 = 2 , ~v3 = −1 .
1 1 1
Therefore, a similar calculation works for the latter matrix reformulation
above.
Sage

sage: A = matrix(QQ, [[0,1,0],[0,0,1],[-6,-1,4]])


sage: A.eigenspaces_right()
[
(3, Vector space of degree 3 and dimension 1 over Rational Field
User basis matrix:
[1 3 9]),
(2, Vector space of degree 3 and dimension 1 over Rational Field
User basis matrix:
[1 2 4]),
(-1, Vector space of degree 3 and dimension 1 over Rational Field
User basis matrix:
[ 1 -1 1])
]

75
sage: P = matrix(QQ, [[1,1,1],[3,2,-1],[9,4,1]])
sage: D = Pˆ(-1)*A*P; D
[ 3 0 0]
[ 0 2 0]
[ 0 0 -1]

This is the diagonalization of A.


Here is the corresponding computation in SymPy:
Sage

>>> A = Matrix(3, 3, [0,1,0,0,0,1,-6,-1,4])


>>> print A
[ 0, 1, 0]
[ 0, 0, 1]
[-6, -1, 4]
>>> A.eigenvals()
{-1: 1, 2: 1, 3: 1}
>>> print A.eigenvects()
[(2, 1, [[1/4]
[1/2]
[ 1]]), (-1, 1, [[ 1]
[-1]
[ 1]]), (3, 1, [[1/9]
[1/3]
[ 1]])]
>>> A = Matrix(3, 3, [0,1,0,0,0,1,-6,-1,4]); print A
[ 0, 1, 0]
[ 0, 0, 1]
[-6, -1, 4]
>>> P = A.diagonalize()[0]
>>> print P
[1/4, 1, 1/9]
[1/2, -1, 1/3]
[ 1, 1, 1]
>>> print P**(-1)*A*P
[2, 0, 0]
[0, -1, 0]
[0, 0, 3]

(Note that SymPy returns “right” eigenvectors by default.)

9.4 Linear feedback shift registers


The initial fill is the initial values of the state cells,

s0 , s1 , s2 , . . . , sL−1

76
the initial contents of the L stages of the register. In general, the binary linear
feedback shift register (LFSR) sequence is defined by the following recursion
relation
L
X
sj = ci sj−i (mod 2),
i=1
for j ≥ L. The coefficients c1 , ..., cL are fixed and form the key to the cryp-
tographic bitstream.
The key may be represented as a vector
c = [c1 , c2 , . . . , cL ],
but is more often defined by a polynomial, known as the connection polyno-
mial

C(x) = 1 + c1 x + c2 x2 + · · · + cL xL . (14)
We say a binary sequence a0 , a1 , . . . is periodic with period P if

ai = ai+P ,
for all i. By this definition, if {an }n≥0 is periodic with period P then it also
has period 2P , 3P , . . . . Therefore, we often assume that P > 0 is choosen
as small as possible.
Example 38. If you divide a k digit positive integer by the number with k
9s, the digits in the decimal representation of the resulting rational number
is a repeating decimal. For example, 274/999 = 0.274274274274 . . . .)
We say a binary sequence a0 , a1 , . . . is eventually periodic with period P
if there is an i0 > 0 such that ai = ai+P , for all i > i0 .
Example 39. The decimal expansion of the rational number obtained by
dividing any positive integer by another is eventually repeating decimal. For
example, 1/7 = 0.142857142857143 . . . .)
Question: Why is this eventually periodic?
Example 40. If we are given the key as a vector c = [1, 0, 0, 1] and the initial
fill as a vector s = [1, 1, 0, 1] in GF (2), we can create the sequence

1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, . . . .

77
The auto-correlation function of a periodic sequence X = {x0 .x1 , . . . } (of
real numbers) with period P is defined by
P
X
AC(X, k) = (1/P ) xi xi+k (15)
i=1

By abuse of terminology, if S = {s0 , s1 , . . . } is a periodic binary sequence


with period P (in GF (2)) then the auto-correlation function is defined by
P
X P
X
AC(S, k) = (1/P ) (−1)si (−1)si+k = (1/P ) (−1)si +si+k
i=1 i=1

Definition 41. (Pseudo-random binary sequence) Let A = {a0 , a1 , . . . }, be


a periodic binnary sequence with period P . We say this sequence is pseudo-
random provided the following conditions hold (Golomb’s Principles):

1. Balance: | Pi=1 (−1)ai | ≤ 1


P

2. Low Autocorrelation:

1, k = 0,
AC(A, k) =
6 0,
, k =
where  is “small.”

3. Proportional Runs Property: In each period, half the runs are length 1,
one-fourth are length 2, one-eighth are length 3, etc. Moveover, there
are as many runs of 1’s as there are of 0’s.

Example 42. Consider the sequence of period 7 defined by the values of the
linear function f : GF (2)3 → GF (2) given by f1 (x0 , x1 , x2 ) = x1 + x2 ,

A = {0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, . . . },
where Ai = f (vi ) and GF (2)3 − {(0, 0, 0)} = {v1 , v2 , . . . , v7 }. The following
Sage code computes the values of the autocorrelation function:
Sage

sage: f = lambda x: (x[1]+x[2])%2


sage: F = GF(2)
sage: V = Fˆ3
sage: flist = [f(x) for x in V if x<>V(0)]

78
sage: flist2 = 2*flist
sage: flist
[0, 1, 1, 1, 1, 0, 0]
sage: flist2
[0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0]
sage: autocorr = [[j,sum([(-1)ˆ(flist2[i]+flist2[i+j]) for i in range(7)])] for j in range(7)]
sage: autocorr
[[0, 7], [1, 3], [2, -1], [3, -5], [4, -5], [5, -1], [6, 3]]

In other words,


 1, k = 0,
3/7, k = 1, 6,

AC(A, k) =

 −1/7, k = 2, 5,
−5/7, k = 3, 4.

Example 43. Consider the sequence of period 7 defined by the values of the
linear function f : GF (2)3 → GF (2) given by f1 (x0 , x1 , x2 ) = x0 + x1 + x2 ,

A = {1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, . . . },
where Ai = f (vi ) and GF (2)3 − {(0, 0, 0)} = {v1 , v2 , . . . , v7 }. The following
Sage code computes the values of the autocorrelation function:
Sage

age: f = lambda x: (x[0]+x[1]+x[2])%2


sage: flist = [f(x) for x in V if x<>V(0)]
sage: flist2 = 2*flist
sage: flist2
[1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1]
sage: autocorr = [[j,sum([(-1)ˆ(flist2[i]+flist2[i+j]) for i in range(7)])] for j in range(7)]
sage: autocorr
[[0, 7], [1, -1], [2, -1], [3, -1], [4, -1], [5, -1], [6, -1]]

In other words,

1, k = 0,
AC(A, k) =
−1/7, k =
6 0.
Example 44. Consider the sequence of period 7 defined by the values of the
non-linear function f : GF (2)3 → GF (2) given by f1 (x0 , x1 , x2 ) = x1 + x0 x2 ,

79
A = {0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, . . . },
where Ai = f (vi ) and GF (2)3 − {(0, 0, 0)} = {v1 , v2 , . . . , v7 }. The following
Sage code computes the values of the autocorrelation function:
Sage

sage: f = lambda x: (x[0]*x[2]+x[1])%2


sage: flist = [f(x) for x in V if x<>V(0)]
sage: flist2 = 2*flist
sage: flist2
[0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0]
sage: autocorr = [[j,sum([(-1)ˆ(flist2[i]+flist2[i+j]) for i in range(7)])] for j in range(7)]
sage: autocorr
[[0, 7], [1, -1], [2, -5], [3, 3], [4, 3], [5, -5], [6, -1]]

In other words,


 1, k = 0,
3/7, k = 3, 4,

AC(A, k) =
 −1/7,
 k = 1, 6,
−5/7, k = 2, 5.

9.5 Computations with LFSRs


We look at several examples:
Sage

sage: F = GF(2)
sage: o = F(0); l = F(1)
sage: key = [l,o,o,l]; fill = [l,l,o,l]; n = 20
sage: s = lfsr_sequence(key,fill,n); s
[1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0]
sage: massey(s)
xˆ4 + x + 1

Suppose {an } is a sequence in a finite field or even Q. If

an = c1 an−1 + ... + ck an−k


then the sequence a0 , ..., ak−1 is called the initial fill. The connection poly-
nomial of a sequence defined by

80
an = c1 an−1 + ... + ck an−k
is

p(x) = xk − c1 xk−1 − ... − ck−1 x − ck .


For the sequence above, it is p(x) = x4 + x3 + 1. The reciprocal or reverse
polynomial is the feedback polynomial.
A polynomial an xn + an−1 xn−1 + ... + a1 x + a0 is called monic if an = 1.

Example 45. Consider the example,

an = an−1 + an−4 ,
and a0 = 1, a1 = 1, a2 = 0, a3 = 1. This gives

a0 , a1 , · · · = 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, . . . .

Let us check this using Sage:


Sage

sage: F = GF(2)
sage: o = F(0); l = F(1)
sage: key = [l,o,o,l]; fill = [l,l,o,l]; n = 20
sage: s = lfsr_sequence(key,fill,n); s
[1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0]
sage: # Let’s do this "by hand":
sage: a1 = l
sage: a2 = o
sage: a3 = o
sage: a4 = l
sage: lfsr = lambda x: a1*x[0]+a2*x[1]+a3*x[2]+a4*x[3]
sage: s0 = fill
sage: lfsr(s0)
0
sage: s1 = [1,0,1,0]
sage: lfsr(s1)
1
sage: s2 = [0,1,0,1]
sage: lfsr(s2)
1
sage: s3 = [1,0,1,1]
sage: lfsr(s3)
0
sage: A = matrix(GF(2), [[0,1,0,0],[0,0,1,0],[0,0,0,1],[1,0,0,1]])
sage: s0 = vector(GF(2), [1, 1, 0, 1])
sage: s1 = A*s0; s1
(1, 0, 1, 0)

81
sage: s2 = A*s1; s2
(0, 1, 0, 1)

Now, suppose we know the {an }’s but not the key. Suppose we know

an = c1 an−1 + c2 an−2 + c3 an−3 + c4 an−4 ,


but not what the key (c1 , c2 , c3 , c4 ) is. Let’s try to find the key by hand, solving
these “state equations.”
Sage

sage: c1,c2,c3,c4 = var("c1,c2,c3,c4")


sage: solve([c1*1+c2*1+c3*0+c4*1 == 0,
c1*1+c2*0+c3*1+c4*0 == 1,
c1*0+c2*1+c3*0+c4*1 == 1,
c1*1+c2*0+c3*1+c4*1 == 0], [c1,c2,c3,c4])
[[c1 == -1, c2 == 2, c3 == 2, c4 == -1]]

Note that mod 2, this is [1, 0, 0, 1], the key. Another way:
Sage

sage: B = matrix(GF(2), [[1,1,0,1,0], [1,0,1,0,1], [0,1,0,1,1], [1,0,1,1,0]])


sage: B.echelon_form()
[1 0 0 0 1]
[0 1 0 0 0]
[0 0 1 0 0]
[0 0 0 1 1]

Again, the solution is [1, 0, 0, 1], the key.


This cannot be done in SymPy since, as far as I can tell, at the present
time, SymPy does not have row reduction over finite fields.
Example 46. Consider the example,

xn = 4xn−1 − xn−2 − 6xn−3 ,


and x0 = 0, x1 = 1, x2 = 2. This gives

x0 , x1 , · · · = 0, 1, 2, 7, 20, 61, 182, 547, . . . .


Let us check this using Sage (the SymPy syntax is exactly the same):

82
Sage

sage: a1 = -6
sage: a2 = -1
sage: a3 = 4
sage: rr = lambda x: a1*x[0]+a2*x[1]+a3*x[2]
sage: s0 = [0,1,2]
sage: rr(s0)
7
sage: s1 = [1,2,7]
sage: rr(s1)
20
sage: s2 = [2,7,20]
sage: rr(s2)
61
sage: s3 = [7,20,61]
sage: rr(s3)
182

We can generate the sequence using the recurrance relation. We can also use
the corresponding matrix to generate the terms.
For
xn = 4xn−1 − xn−2 − 6xn−3 ,
and x0 = 0, x1 = 1, x2 = 2, we have:
Sage

sage: A = matrix(QQ, [[0,1,0],[0,0,1],[-6,-1,4]])


sage: s0 = vector(QQ, [0,1,2])
sage: s1 = A*s0
sage: s2 = A*s1
sage: s3 = A*s2
sage: s4 = A*s3
sage: s5 = A*s4
sage: s6 = A*s5
sage: s1; s2; s3; s4; s5; s6
(1, 2, 7)
(2, 7, 20)
(7, 20, 61)
(20, 61, 182)
(61, 182, 547)
(182, 547, 1640)

83
For
xn = 3xn−1 + xn−2 − 3xn−3 ,
and x0 = 0, x1 = 1, x2 = 2, we have:
Sage

sage: A = matrix(QQ, [[0,1,0],[0,0,1],[-3,1,3]])


sage: s0 = vector(QQ, [0,1,2])
sage: s1 = A*s0
sage: s2 = A*s1
sage: s3 = A*s2
sage: s4 = A*s3
sage: s5 = A*s4
sage: s6 = A*s5
sage: s1; s2; s3; s4; s5; s6
(1, 2, 7)
(2, 7, 20)
(7, 20, 61)
(20, 61, 182)
(61, 182, 547)
(182, 547, 1640)

For
xn = 7xn−2 + 6xn−3 ,
and x0 = 0, x1 = 1, x2 = 2, we have:
Sage

sage: A = matrix(QQ, [[0,1,0],[0,0,1],[6,7,0]])


sage: s0 = vector(QQ, [0,1,2])
sage: s1 = A*s0
sage: s2 = A*s1
sage: s3 = A*s2
sage: s4 = A*s3
sage: s5 = A*s4
sage: s6 = A*s5
sage: s1; s2; s3; s4; s5; s6
(1, 2, 7)
(2, 7, 20)
(7, 20, 61)
(20, 61, 182)
(61, 182, 547)
(182, 547, 1640)

We see that several different matrices can be used to generate the terms.

84
To “solve” for xn , we want to compute the diagonalization of one of these
matrices, say
 
0 1 0
A= 0 0 1 .
−6 −1 4
To find the diagonalization, we need the eigenvalues and eigenvectors. Sage
can doe this easily.
Sage

sage: A = matrix(QQ, [[0,1,0],[0,0,1],[-6,-1,4]])


sage: A.eigenspaces_right()
[
(3, Vector space of degree 3 and dimension 1 over Rational Field
User basis matrix:
[1 3 9]),
(2, Vector space of degree 3 and dimension 1 over Rational Field
User basis matrix:
[1 2 4]),
(-1, Vector space of degree 3 and dimension 1 over Rational Field
User basis matrix:
[ 1 -1 1])
]
sage: P = matrix(QQ, [[1, 3, 9],[ 1, 2, 4], [ 1, -1, 1]]).transpose()
sage: D = matrix(QQ, [[3,0,0],[0,2,0],[0,0,-1]])
sage: P*D*Pˆ(-1)
[ 0 1 0]
[ 0 0 1]
[-6 -1 4]

Now, let’s work over the finite field GF (5). In this case, the recursion
becomes

xn = 4xn−1 + 4xn−2 + 4xn−3 ,


and the sequence becomes

x0 , x1 , · · · = 0, 1, 2, 2, 0, 1, 2, 2, . . . .
We check this using Sage:
Sage

sage: # over GF(5)


sage: A = matrix(GF(5), [[0,1,0],[0,0,1],[-6,-1,4]])
sage: s0 = vector(GF(5), [0,1,2])

85
sage: s1 = A*s0
sage: s2 = A*s1
sage: s3 = A*s2
sage: s4 = A*s3
sage: print s0.list()+[s1[2]]+[s2[2]]+[s3[2]]+[s4[2]]
[0, 1, 2, 2, 0, 1, 2]
sage: A = matrix(GF(5), [[0,1,0],[0,0,1],[-3,1,3]])
sage: s0 = vector(GF(5), [0,1,2])
sage: s1 = A*s0
sage: s2 = A*s1
sage: s3 = A*s2
sage: s4 = A*s3
sage: print s0.list()+[s1[2]]+[s2[2]]+[s3[2]]+[s4[2]]
[0, 1, 2, 2, 0, 1, 2]

Suppose we know the {xn }’s but not the key. Suppose we know

xn = c1 xn−1 + c2 xn−2 + c3 xn−3 ,


but not what the key (c1 , c2 , c3 ) is. Let’s try to find the key by hand, solving
these “state equations.” We find that

2 = 2 · c1 + 1 · c2 + 0 · c3 ,
0 = 2 · c1 + 2 · c2 + 1 · c3 ,
1 = 0 · c1 + 2 · c2 + 2 · c3 ,
2 = 1 · c1 + 0 · c2 + 2 · c3 ,
..
.,
and so on. Taking, say, the last three equations above, we obtain a system of
linear equations with augmented matrix
 
2 2 1 0
A= 0 2 2 1 
1 0 2 2
with row-reduced echelon form
 
1 0 2 2
 0 1 1 3 
0 0 0 0
Therefore,

c1 = 2 + 3c3 , c2 = 3 + 4c3 .
Let’s look at this case-by-case.

86
• c3 = 0. Then the key is (c1 , c2 , c3 ) = (2, 3, 0). The equation xn =
2xn−1 + 3xn−2 gives

x0 , x1 , · · · = 0, 1, 2, 2, 0, 1, . . . ,
agreeing with the original sequence, but with a different key than the
one we started with.

• c3 = 1. Then the key is (c1 , c2 , c3 ) = (0, 2, 1). The equation xn =


2xn−2 + xn−3 gives

x0 , x1 , · · · = 0, 1, 2, 2, 0, 1, . . . ,
agreeing with the original sequence, but with a different key than the
one we started with.

• c3 = 2. Then the key is (c1 , c2 , c3 ) = (3, 1, 2). The equation xn =


3xn−1 + xn−2 + 2xn−3 gives

x0 , x1 , · · · = 0, 1, 2, 2, 0, 1, . . . ,
agreeing with the original sequence, but with a different key than the
one we started with.

• c3 = 3. Then the key is (c1 , c2 , c3 ) = (1, 0, 3). The equation xn =


xn−1 + 3xn−3 gives

x0 , x1 , · · · = 0, 1, 2, 2, 0, 1, . . . ,
agreeing with the original sequence, but with a different key than the
one we started with.

• c3 = 4. Then the key is (c1 , c2 , c3 ) = (4, 4, 4). The equation xn =


xn−1 + 3xn−3 gives

x0 , x1 , · · · = 0, 1, 2, 2, 0, 1, . . . ,
agreeing with the original sequence, and with same key than we started
with.

87
In any case, we can “break” this cipher very easily using linear algebra.
Here are some more Sage computations regarding this example. First, we
show that, in this example, we can diagonalize the matrix over GF (5).
Sage

sage: A = matrix(GF(5), [[0,1,0],[0,0,1],[-6,-1,4]])


sage: A.eigenspaces_right()
[
(4, Vector space of degree 3 and dimension 1 over Finite Field of size 5
User basis matrix:
[1 4 1]),
(3, Vector space of degree 3 and dimension 1 over Finite Field of size 5
User basis matrix:
[1 3 4]),
(2, Vector space of degree 3 and dimension 1 over Finite Field of size 5
User basis matrix:
[1 2 4])
]
sage: P = matrix(GF(5), [[1, 4, 1],[ 1, 3, 4], [ 1, 2, 4]]).transpose()
sage: D = matrix(GF(5), [[4,0,0],[0,3,0],[0,0,2]])
sage: P*D*Pˆ(-1)
[0 1 0]
[0 0 1]
[4 4 4]
sage: F = GF(5); key = [F(-6), F(-1), F(4)]
sage: fill = [F(0), F(1), F(2)]; n = 20
sage: s = lfsr_sequence(key,fill,n); s
[0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2]

This recursive equation can be written having a “shorter” key length (as we
already saw in the above itemized list, case c3 = 0):
Sage

sage: berlekamp_massey(s)
xˆ2 + 3*x + 2
sage: A = matrix(GF(5), [[0,1,0],[0,0,1],[-6,-1,4]])
sage: A
[0 1 0]
[0 0 1]
[4 4 4]
sage: A.eigenspaces_right()
[
(4, Vector space of degree 3 and dimension 1 over Finite Field of size 5
User basis matrix:
[1 4 1]),
(3, Vector space of degree 3 and dimension 1 over Finite Field of size 5
User basis matrix:
[1 3 4]),
(2, Vector space of degree 3 and dimension 1 over Finite Field of size 5
User basis matrix:

88
[1 2 4])
]
sage: F = GF(5); key = [F(-2), F(-3)]; fill = [F(0), F(1)]; n = 20
sage: s = lfsr_sequence(key,fill,n); s
[0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2]

Fact 1: If r1 , . . . , rk are distinct roots of the connection polynomial of a LFSR


{an } then

an = α1 r1n + · · · + αk rkn
for some α1 , . . . , αk determined by the initial fill.
The generating function A(x) of a sequence {an } is

X
A(x) = a0 + a1 x + ... = an x n . (16)
n=0

Fact 2; A sequence {an } is eventually periodic if and only if A(x) is a rational


function.

Fact 3: If {an } is a LFSR then A(x) = B(x)/C(x). If we put this quotient in


least common terms and assume C(x) is monic then C(x) is the connection
polynomial of {an }.

Example 47.

(1 + x + x3 )/(x4 + x3 + 1) = 1 + x + x3 + x4 + x5 + x7 + x8 + x9 + x11
+x12 + x13 + x15 + x16 + x17 + x19 + x20
+x21 + x23 + x24 + x25 + x27 + x28 + x29 + O(x30 ).

The coefficients are 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, . . . .

Example 48. We return to the finite field GF (5) and the recursion

xn = 4xn−1 + 4xn−2 + 4xn−3 ,


associated to the sequence

89
x0 , x1 , · · · = 0, 1, 2, 2, 0, 1, 2, 2, . . . .
A key is (c1 , c2 , c3 ) = (2, 3, 0). We check Fact 2 above using Sage:
Sage

sage: LSR.<q> = LaurentSeriesRing(GF(5))


sage: f = q; g = 1+3*q+2*qˆ2; f/g
q + 2*qˆ2 + 2*qˆ3 + qˆ5 + 2*qˆ6 + 2*qˆ7 + qˆ9 + 2*qˆ10 +
2*qˆ11 + qˆ13 + 2*qˆ14 + 2*qˆ15 + qˆ17 + 2*qˆ18 + 2*qˆ19 + O(qˆ21)

9.6 Blum-Blum-Shub (BBS) stream cipher


The number theory behind the BBS cipher uses quadratic residues.
Definition 49. (Quadratic residue) Let a > 0 be an integer. We say a is a
quadratic residue (mod m) if the congruence x2 ≡ a (mod m) is solvable.
The set of all quadratic residues a, 0 < a < m, is denoted by Qm .
Let m be a prime number, m > 2. Qm is a subgroup of
(Z/mZ)× = {1, . . . , m − 1}
of index 2. In other words, |Qm | = (m − 1)/2.
Definition 50. (Special Blum Prime) Let p be a prime number. We say
p is a special Blum prime if and only if p ≡ 3 (mod 4), p = 2p1 + 1 and
p1 = 2p2 + 1 with p1 , p2 prime numbers. A number n = pq is special if and
only if p, q are distinct special Blum primes.
Definition 51. (Blum-Blum-Shub streamcipher) Let p, q be two distinct
prime numbers such that p ≡ 3 (mod 4) and q ≡ 3 (mod 4). Let n = pq
and let 0 < r < n be a random number. We define x0 , the first number of
the Blum-Blum-Shub (BBS) pseudo-random number generator, as x0 ≡ r2
(mod n). Each proceeding seed can be defined as

xi+1 = x2i (mod 2).


The streamcipher, b = b1 b2 · · · bn . . . , is created by setting
bi = x i (mod 2),
thus yielding a pseudo-random string of 0s and 1s.
Note: The BBS stream cipher is most secure when n is special.
See Hogan’s thesis [H] for more details.

90
10 Bent functions
Recall a LFSR in GF (q) of length k is defined by an order k linear homoge-
neous recurrence relation with constant coefficients of the form:

an = c1 an−1 + c2 an−2 + · · · + ck an−k


where the k coefficients ci (for 1 ≤ i ≤ k) are given (the key is (c1 , . . . , ck ))
and the initial values a0 , ..., ak−1 are given values in GF (q). We have looked
at these when q = 2; the more general case is similar. These can have good
pseudo-random properties with period P = q k − 1, but are insecure.

10.1 Functions with a given least support


This section follows Celerier’s USNA thesis closely [C].
It turns out it is rather easy to determine the algebraic normal form of
a function if you know its support. This section discusses this idea in the
Boolean case.
For each v ∈ GF (2)n , define a monotone function f = fv to be atomic
based on v if its support consists of all vectors greater than or equal to v,
i.e., if

Ωf = {x ∈ GF (2)n | v ≤ x},
where ≤ is the partial order defined above. We call f atomic if there is some
v 6= 0 such that f is atomic based on v.
Definition 52. Let f : GF (2)n → GF (2) be any monotone function. We
say that Γ ⊂ GF (2)n is the least support of f if Γ consists of all vectors in
Ωf which are smallest in the partial ordering ≤ on GF (2)n .
Theorem 53. Let f be a monotone Boolean function whose least support
vectors are given by Γ ⊂ GF (2)n . Then
Y
f (x) = 1 + (xv + 1). (17)
v∈Γ

Proof. Define a Boolean function g : GF (2)n → GF (2) such that


Y
g(x) = 1 + (xv + 1)
v∈Γ

91
where Γ is the set of least support vectors for a monotone Boolean function
f.
For x ∈ GF (2)n , define the subset Sx of least support vectors v ∈ Γ such
that v ≤ x as
Sx = {v ∈ Γ | v ≤ x}.
We will show f = g by proving f (x) = 0 ⇔ g(x) = 0.
(⇒) Let y ∈ GF (2)n satisfy f (y) = 0. Then, y 6∈ Ωf and Sy = ∅. Thus,
for every v ∈ Γ, there exists an i such that vi = 1 and yi = 0. Consequently,
from the definition of g, we have
Y
g(y) = 1 + (y v + 1) = 1 + 1 = 0.
v∈Γ

(⇐) The converse is exactly the reverse of the above argument. We


provide details for the convenience of the reader. Let y ∈ GF (2)n satisfy
v
Q
g(y) = 0. Since g(y) = 1 + v∈Γ (y + 1), this means that for each v ∈ Γ, we
have y v = 0. Thus, for every v ∈ Γ, there exists an i such that vi = 1 and
yi = 0. This means that y ≥ v is false for each v ∈ Γ. Since f is monotone,
this implies y ∈
/ Ωf , which means that f (y) = 0. 

10.2 Cipherstreams via a filter function


To make them more secure, there are a few ways to construct a stream cipher
from a “filter function.”
Method 1: Let

f : GF (q)m → GF (q)
be a given function. Label the N = q k elements of GF (q)n as follows:

v0 = (0, . . . , 0, 0), v1 = (0, . . . , 0, 1), vN −1 = (1, . . . , 1).


In other words, construct the “filtered” binary sequence

a1 = f (v1 ), ..., aN −1 = f (vN −1 ), aP = f (v1 ), aN +1 = a2 , . . . .

This has period P = N − 1 = q k − 1.


Method 2: Let

f : GF (q)m → GF (q)

92
be a given function. Let a0 , a1 , . . . be a LFSR of key length k over GF (q)
having period P = q k − 1.
let

s0 = (a0 , ..., ak−1 ), s1 = (a1 , ..., ak ), ...sP −1 = (aP −1 , a0 , ..., aP −k ),

and produce the new sequence

b0 = f (s0 ), b1 = f (s1 ), ...

If f is linear then this is another LFSR. However, if f is non-linear then this


could be more secure.
Let

GF (2)m = {v0 , v1 , . . . , vM −1 },
where M = 2m . For any Boolean function f : GF (2)m → GF (2), define

fˆ = ((−1)f (v0 ) , (−1)f (v1 ) , . . . , (−1)f (vM −1 ) ) ∈ RM .


Finally, define L to be the vector space of all linear functions GF (2)m →
GF (2). Since every linear function f (x) has the form f (x) = x · a, for
some unique a ∈ GF (2)m , there are 2m elements of L. An affine function
f : GF (2)m → GF (2) is one of the form f (x) = L(x) + , where L ∈ L and
 ∈ GF (2) is a constant.

Example 54. Let V = GF (2)2 = {(0, 0), (1, 0), (0, 1), (1, 1)}. The linear
functions on V are

f0 (x0 , x1 ) = 0, f1 (x0 , x1 ) = x0 , f2 (x0 , x1 ) = x1 , f3 (x0 , x1 ) = x0 + x1 .

These correspond to

fˆ0 = (1, 1, 1, 1), fˆ1 = (1, −1, 1, −1), fˆ2 = (1, 1, −1, −1), fˆ3 = (1, −1, −1, 1).

It is easy to check that fˆi ⊥ fˆj , for i 6= j. For a non-linear example, consider
g(x0 , x1 ) = x0 x1 , which coresponds to ĝ = (1, 1, 1, −1). Note this is not
perpendicular to any of the fˆi .

93
Figure 6: The plot of the autocorrelation function of f .

Example 55. Consider the function on GF (2)4 given by f (x0 , x1 , x2 , x3 ) =


x0 + x1 + x3 . Method 1 yields the stream cipher of period 15 given by

X = {xn } : 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, . . . .

The table of values of the autocorrelation function is


k 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
AC(k,X) 1 -1/15 -3/5 -1/15 1/5 1/5 1/5 -1/3 -1/3 1/5 1/5 1/5 -1/15 -3/5 -1/15

This is plotted as in Figure 6.


In other words, linear functions yield stream ciphers which do not have
“small” autocorrelation.
Example 56. Consider the function on GF (2)4 given by f (x0 , x1 , x2 , x3 ) =
x0 x3 + x1 x2 . Method 1 yields the stream cipher of period 15 given by

94
Figure 7: The plot of the autocorrelation function of f .

X = {xn } : 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, . . . .
The table of values of the autocorrelation function is
k 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
AC(k,X) 1 -1/15 1/5 -1/15 -1/15 -1/15 -1/3 1/5 1/5 -1/3 -1/15 -1/15 -1/15 1/5 -1/15

This is plotted as in Figure 7.


In other words, this particular nonlinear functions yields a stream ciphers
which has a “small” autocorrelation.

10.3 The Walsh transform


This section will introduce a transform which will measure “how non-linear”
a function is.

95
Lemma 57. Let

L∧ = {fˆ | f ∈ L}.
A Boolean function f : GF (2)m → GF (2) is affine if and only if fˆ is perpen-
dicular to all but one of the vectors in L∧ .

Proof. Consider the dot product


X X
fˆ · ĝ = (−1)f (x) (−1)g(x) = (−1)f (x)+g(x) .
x∈V x∈V

Suppose g ∈ L. If g(x) = x · b, for some b ∈ V , then this is


X
fˆ · ĝ = (−1)f (x)+x·b .
x∈V

(⇐): First, let us assume f is linear and show that it is perpendicular to all
but one of the vectors in L∧ . Suppose f (x) = x · a, for some a ∈ V . Then
X X
fˆ · ĝ = (−1)x·a+x·b = (−1)x·(a+b) .
x∈V x∈V

The following fact is easy to verify: given any non-zero c ∈ V , the number
of times x · c = 0 is 2m /2 and the number of times x · c = 1 is 2m /2. Based
on this, we see that
 m
ˆ 2 , a + b = 0,
f · ĝ = (18)
0, a + b 6= 0.
(⇒): Now, suppose f is any function which has the property that
X
fˆ · ĝ = (−1)f (x)+x·b
x∈V

is equal to 0 for all b ∈ V except for one (to be discussed below). In other
words, if we multiply the vector fˆ by the 2m × 2m matrix

H = ((−1)vi ·vj )0≤i,j≤M −1 ,


then we get a vector in RM in which all but one entry is 0. The matrix H is
invertible. In fact, it is not hard to check using (18) that

96
H 2 = M · IM ,
where IM is the m × M identity matrix. Therefore, H −1 = M −1 H. This
implies that

fˆ = H −1~v = M −1 H~v ,
where w~ = (w1 , . . . , wM ) ∈ RM is zero for all but one coordinate, say w` 6= 0.
Therefore, fˆ is a constant multiple of the `-th column of H. This forces fˆ to
be of the form cĝ, for some constant c and some linear function g. If c = 1
then f is linear and if c = −1 then f is affine.

This motivates the following question: What is the most non-linear func-
tion one can find?
A Boolean function

f : GF (2)m → GF (2)
is bent (also called perfectly non-linear) if, for all a ∈ GF (2)m ,

|Wf (a)| = 2m/2 ,


where W is the Walsh transform. If f is any Boolean function as above then
its Walsh transform is
X
Wf (a) = (−1)f (x)+x·a .
x∈GF (2)m

Two questions arise:

1. How do you compute the Walsh transform?

2. Why does this condition on the Walsh transform of f guarantee that f


is “very” non-linear?

Computing Wf (a):
Order GF (2)m lexicographically

v0 = 000...0 < v1 = 100...0 < v2 = 010...0 < · · · < vM = 111...1,

97
where M = 2m − 1. Using this order, define the character of f by

f ∗ = ((−1)f (v0 ) , (−1)f (v1 ) , (−1)f (v2 ) , ..., (−1)f (vM ) ).


It turns out that f is bent if and only if its character f ∗ is “as far as possible”
from the characters `∗ , where ` is taken from the collection of affine functions
`(x) = b · x + . Define the Hadamard matrix H by

H = ((−1)vi ·vj | 0 ≤ i, j ≤ M )
Then the Walsh transform can be expressed in terms of this matrix transfor-
mation:
Wf = Hf ∗ .

Definition 58. We say that a Boolean function

f : GF (2)m → GF (2)

is linear if there is a b ∈ GF (2)m such that, for all x ∈ GF (2)m ,

f (x) = x · b.

Lemma 59. Suppose f (x) = x · b. Then



0, if a 6= b,
Wf (a) = m
2 , if a = b,
Conversely, if

f : GF (2)m → GF (2)
is any Boolean function satisfying

0, if a 6= b,
Wf (a) =
2m , if a = b,
for some b ∈ GF (2)m , then f (x) = x · b.

Example 60. The function f (x1 , x2 , x3 , x4 ) = x1 x3 + x2 x4 defines a bent


function on GF (2)4 . Exercise: Verify this by compting Wf (a) for each a ∈
GF (2)4 .

98
11 Error-correcting codes
Roughly speaking a code is a system for converting a message into another
form for the purpose of communicating the message more efficiently or reli-
ably. A few examples are listed below.

• Semaphore, where a message is converted into a sequence of flag move-


ments for communication across a distance.

• Morse code, where a message is converted into a sequence of dots and


dashes for communication using telegraph,

• Marconi Telegraph Code, where a commonly used phrase is converted


into a more compact 5-letter sequence. For example, “what do you
suggest?” is encoded as “VYHIC.”

A code could be used as a cipher, but most codes are not created with se-
curity in mind. For example, during the Prohibition Era, rumrunners used
slightly modified telegraph codes to transmit shipment information and meet-
ing places for ship-loads of alchohol. Such ciphers were routinely broken by
Coast Guard cryptographers.
Some codes are designed for compression - to store digital data more com-
pactly. Some codes are designed for reliability - to communicate information
over a noisy channel, yet to correct the errors which arise.

11.1 The communication model


Consider a source sending messages through a noisy channel. The message
sent will be regarded as a vector of length n whose entries are taken from a
given finite field F (typically, F = GF (2)).
For simplicity, assume that the message being sent is a sequence of 0’s and
1’s. Assume that, due to noise, when a 0 is sent, the probability that a 1 is
(incorrectly) received is p and the probability that a 0 is (correctly) received
is 1 − p. The error rate p is a small positive number (such as 1/10000) which
represents the “noisiness” of the channel. Assume also that the error rate
(and channel noise) is not dependent on the symbol sent: when a 1 is sent,
the probability that a 1 is (correctly) received is 1 − p and the probability
that a 0 is (incorrectly) received is p.

99
11.2 Basic definitions
The theory of error-correcting codes was originated by Richard Hamming
in the late 1940’s, a mathematician who worked for Bell Telephone. Some
specific examples of his codes actually arose earlier in various isolated con-
nections - for example, statistical design theory and in soccer betting(!).
Hamming’s motivation was to program a computer to correct “bugs” which
arose in punch-card programs. The overall goal behind the theory of error-
correcting codes is to reliably enable digital communication.
Let F = GF (q) be any finite field.
A (linear error-correcting) code C of length n over F is a vector subspace
of Fn (provided with the standard basis5 ) and its elements are called code-
words. When F = GF (2) it is called a binary code. These are the most
important codes from the practical point of view. Think of the following
scenario: You are sending an n-vector of 0’s and 1’s (the codeword) across
a noisy channel to your friend. Your friend gets a corrupted version (the
received word differs from the codeword in a certain number of error posi-
tions). Depending on how the code C was constructed and the number of
errors made, it is possible that the original codeword can be recovered. This
raises the natural question: given C, how many errors can be corrected? Stay
tuned...
A code of length n and dimension k (as a vector space over F) is called
an [n, k]-code. In abstract terms, an [n, k]-code is given by a short exact
sequence6
G H
0 → Fk → Fn → Fn−k → 0. (19)
We identify C with the image of G. The function

G : Fk → C,
~ 7−→ mG,
m ~
5
It is important that the code be provided with a fixed basis which never changes.
This is because the minimum distance function is not invariant under a change of basis.
However, the minimum distance is one quantity used to measure how “good” a code is,
from the practical point of view.
6
“Short exact” is a compact way of specifying the following three conditions at once:
(1) the first map G is injective, i.e., G is a full-rank k × n matrix, (2) the second map H
is surjective, and (3) image(G) = kernel(H).

100
is called the encoder. Since the sequence (19) is exact, a vector ~v ∈ Fn is a
codeword if and only if H(~v ) = 0. If Fn is given the usual standard vector
space basis then the matrix of G is a generating matrix of C and the matrix
of H is a check matrix of C. In other words,

C = {~c | ~c = mG,
~ some m ~ ∈ Fk }
= {~c ∈ Fn | H~c = ~0}.
When G has the block matrix form

G = (Ik | A),

where Ik denotes the k × k idenity matrix and A is some k × (n − k) matrix,


then we say G is in standard form. By abuse of terminology, if this is the
case then we say C is in standard form.
The matrix G has rank k, so the row-reduced echelon form of G, call it
0
G , has no rows equal to the zero vector. In fact, the standard basis vectors
~e1 , ..., ~ek of the column space Fk occur amongst k columns of those of G0 .
The corresponding coordinates of C are called the information coordinates
(or information bits, if C is binary) of C.
Aside: For a “random” k × k matrix with real entries, the “probability” that its
rank is k is of course 1. This is because “generically” a square matrix with real entries is
invertible. In the case of finite fields, this is not the case. For example, the probability
that a “large random” k × k matrix with entries in GF (2) is invertible is

(2k − 1)(2k − 2)...(2k − 2k−1 ) Y
lim = (1 − 2−i ) = 0.288... .
k→∞ 2k2 i=1

The Hamming metric is the function

d : Fn × Fn → R,
~ ~0).
~ = |{i | vi 6= wi }| = d(~v − w,
d(~v , w)
The Hamming weight of a vector is simply its distance from the origin:

wt(~v ) = d(~v , ~0).


Question: How many vectors belong to the “shell’ of radius r about the origin
~0 ∈ GF (q)r ?

101
 
n
Answer: (q − 1)r . Think about it! (Hint: “distance r” means that there
r
are exactly r non-zero coordinates. The binomial coefficient describes the number
of ways to choose these r coordinates.)
The minimum distance of C is defined to be the number

d(C) = min d(~c, ~0).


~c6=~0

(It is not hard to see that this is equal to the closest distance between any
two distinct codewords in C.) An [n, k]-code with minimum distance d is
called an [n, k, d]-code.

Lemma 61. (Singleton bound) Every linear [n, k, d] code C satisfies

k + d ≤ n + 1.

Note: this bound does not depend on the size of F. A code C whose
parameters satisfy k + d = n + 1 is called maximum distance separable or
MDS. Such codes, when they exist, are in some sense best possible.
proof: Fix a basis of Fnq and write all the codewords in this basis. Delete
the first d − 1 coordinates in each code word. Call this new code C 0 . Since
C has minimum distance d, these codewords of C 0 are still distinct. There
are therefore q k of them. But there cannot be more than q n−d+1 = |Fn−d+1
q |
of them. This gives the inequality. 
The rate of the code is R = k/n - this measures how much information
the code can transmit. The relative minimum distance of the code is δ = d/n
- this is directly related to how many errors can be corrected.

Lemma 62. If ~v ∈ Fn is arbitrary and 0 < r ≤ [ d−1


2
] then the “ball” about ~v
with radius r,

~ ∈ Fn | d(~v , w)
Br (~v ) = {w ~ ≤ r}
contains at most one codeword in C.

This follows easily from the fact that the Hamming metric is, in fact, a
metric. Here is a picture of the idea.

102
◦ • • ◦ • • ◦

• • • • • • •

• • • •'• •$•

◦ • • ◦ • • ◦

• • • •&• •%•

• • • • • • •

◦ • • ◦ • • ◦
Lemma 63. (sphere-packing bound) For any code C ⊂ Fn , we have
t  
X n
|C| (q − 1)i ≤ q n ,
i
i=0

where t = [(d − 1)/2].


proof: For each codeword of C, construct a ball of radius t about it.
These are non-intersecting, by definition of d and the previous lemma. Each
such ball has
t  
X n
(q − 1)i
i
i=0

elements. The result follows from the fact that ∪~c∈C Bt (~c) ⊂ Fn and |Fn | = q n .

Suppose (a) you sent ~c ∈ C, (b) your friend received ~v ∈ Fn , (c) you
know (or are very confident) that the number t of errors made is less than or
equal to [ d−1
2
]. By the lemma above, the “ball” about ~v of radius t contains a
unique codeword. It must be ~c, so your friend can recover what you sent (by
searching though all the vectors in the ball and checking which one is in C)
even though she/he only knows C and ~v . This is called the nearest neighbor
decoding algorithm:
1. Input: A received vector ~v ∈ Fn .
Output: A codeword ~c ∈ C closest to ~v .
2. Enumerate the elements of the ball Bt (~v ) about the received word. Set
~c =“fail”.

103
3. For each w~ ∈ Bt (~v ), check if w
~ ∈ C. If so, put ~c = w
~ and break to the next
step; otherwise, discard w ~ and move to the next element.

4. Return ~c.

Note “fail” is not returned unless t > [ d−1


2
], by the above lemma.

Definition 64. We say that a linear C is t-error correcting if |Bt (w)∩C|


~ ≤ 1.

Note that t ≤ [ d−1


2
] if and only if d ≥ 2t + 1.
The general goal in the theory is to optimize the following properties:

• the rate, R = k/n,

• the relative minimum distance, δ = d/n,

• the speed at which a “good” encoder for the code can be implemented,

• the speed at which a “good” decoder for the code can be implemented.

There are (sometimes very technical) constraints on which these can be


achieved, as we have seen with the Singleton bound and the sphere-packing
bounds.

11.3 Binary hamming codes


This material can be found in many standard textbooks, such as [JKT].
A Hamming code is a member of a family of binary error-correcting codes
defined by Richard Hamming, a Bell telephone mathematician, in the 1940s.

Definition 65. Let r > 1. The Hamming [n, k, 3]-code C is the linear code
with
n = 2r − 1, k = 2r − r − 1,
and parity check matrix H defined to be the matrix whose columns are all the
(distinct) non-zero vectors in GF (2)r . By Lemma 66, this code has minimum
distance d = 3.

Lemma 66. Every binary Hamming code C has minimum distance 3.

104
Proof. Indeed, if C has a code wode of weight 1 then the parity check matrix
H of C would have to have a column which consists of the zero vector,
contradicting the definition of H. Likewise, if C has a code wode of weight
2 then the parity check matrix H of C would have to have two identical
columns, contradicting the definition of H. Thus d ≥ 3.
Since      
1 0 1
 0   1   1 
     
 0   0 
  ,   , and  0  ,
 
 ..   ..   .. 
 .   .   . 
0 0 0
form three columns of the parity check matrix H of C - say the 1st , 2nd , and
3rd columns - the vector (1, 1, 1, 0, ..., 0) must be a code word. Thus d ≤ 3.


Example 67.r = 2 The Hamming [3, 1]-code has parity check matrix
 
1 0 1
H=
0 1 1

The matrix G = (1, 1, 1) is a generating matrix.

r = 3 The Hamming [7, 4]-code has parity check matrix


 
1 0 0 1 0 1 1
H=  0 1 0 1 1 0 1 
0 0 1 0 1 1 1

The matrix  
1 1 0 1 0 0 0
 0 1 1 0 1 0 0 
G=
 1

0 1 0 0 1 0 
1 1 1 0 0 0 1
is a generating matrix.

Example 68. Consider the Hamming [7, 4] example above. The meaning of
the statement that G is a generator matrix is that a vector

105
'$
'$
A 1'$
4 2 B
7 5
6&%
&%
3
&%

 
x1

 x2 


 x3 

~x = 
 x4 


 x5 

 x6 
x7
is a codeword if and only if ~x is a linear combination of the rows of G. The
meaning of the statement that H is a check matrix is H~x = ~0, ie

x1 + x4 + x6 + x7 = 0, x2 + x4 + x5 + x7 = 0, x3 + x5 + x6 + x7 = 0.

This may be visualized via a Venn diagram (see Figure 68).


Decoding algorithm for the Hamming [7, 4]-code
Denote the received word by
w
~ = (w1 , w2 , w3 , w4 , w5 , w6 , w7 ).

1. Put wi in region i of the Venn diagram above, i = 1, 2, ..., 7.

2. Do parity checks on each of the circles A, B, and C.


parity failure region(s) error position
none none
A, B, and C 7
B and C 5
A and C 6
A and B 4
A 1
B 2
C 3

Here is some Sage code to illustrate this:

106
Sage

sage: C = codes.HammingCode(3,GF(2)); C
Linear code of length 7, dimension 4 over Finite Field of size 2
sage: C.minimum_distance()
3
sage: H = matrix(GF(2), 3, 7, [[1, 0, 0, 1, 0, 1, 1], [0, 1, 0, 1, 1, 0, 1], [0, 0, 1, 0, 1, 1, 1]])
sage: H
[1 0 0 1 0 1 1]
[0 1 0 1 1 0 1]
[0 0 1 0 1 1 1]
sage: C = codes.LinearCodeFromCheckMatrix(H)
sage: C.check_mat()
[1 0 0 1 0 1 1]
[0 1 0 1 1 0 1]
[0 0 1 0 1 1 1]
sage: C.minimum_distance()
3
sage: C.list()
[(0, 0, 0, 0, 0, 0, 0),
(1, 0, 0, 0, 1, 0, 1),
(0, 1, 0, 0, 0, 1, 1),
(1, 1, 0, 0, 1, 1, 0),
(0, 0, 1, 0, 1, 1, 1),
(1, 0, 1, 0, 0, 1, 0),
(0, 1, 1, 0, 1, 0, 0),
(1, 1, 1, 0, 0, 0, 1),
(0, 0, 0, 1, 1, 1, 0),
(1, 0, 0, 1, 0, 1, 1),
(0, 1, 0, 1, 1, 0, 1),
(1, 1, 0, 1, 0, 0, 0),
(0, 0, 1, 1, 0, 0, 1),
(1, 0, 1, 1, 1, 0, 0),
(0, 1, 1, 1, 0, 1, 0),
(1, 1, 1, 1, 1, 1, 1)]

11.4 The covering radius


Question: What is the smallest radius r such that the balls of radius r
centered about all the codewords,

B(c, r) = {v ∈ GF (q)n | d(c, v) ≤ r}


are disjoint?
Answer: [(d − 1)/2]. By the above proof, we see that the triangle inequal-
ity will not allow two balls centered at neighboring codewords are disjoint if
and only if they have radius ≤ [(d − 1)/2].
The union of all these disjoint balls of radius [(d − 1)/2] centered at the
codewords in C usually does not equal the entire space V = GF (q)n . (When

107
it does, C is called perfect) How much larger do we have to make the radius
so that the union of these balls does cover all of V ? In other words, we want
to increase the radius r = [(d − 1)/2] to some new radius rho so that

∪c∈C B(c, ρ) = V.
This new radius is called the covering radius. In general, it is hard to find
good upper bounds on ρ. A coset is a subset of GF (q)n of the form C + v for
some v ∈ GF (q)n . Equivalently, a coset is a pre-image of some y in GF (q)n−k
under the check matrix H : GF (q)n → GF (q)n−k . Let S be a coset of C.
Then, a coset leader of S is an element of S having smallest weight.

Theorem 69. The coset leaders of a Hamming code are those vectors of
wt ≤ 1.

Proof. Let the Hamming code be defined as a [n, k, d] code as above where
for some integer r, n = 2r − 1, k = 2r − 1 − r, and d = r. In the binary case,
the size of the ambient space is q n = 2n = |GF (q)n | and the size of the code
is q k = 2k = |C|. Thus, the size of any coset S of C is

|S| = |GF (q)n ||C| = 2n−k = 2r = n + 1.

Claim: Each coset contains a coset leader of wt ≤ 1 and no coset contains


more than one vector of wt ≤ 1. Proof of claim: Assume that v1 + C is one
such coset with two distinct vectors w1 , w2 of wt ≤ 1. Then,

w1 = v1 + c1 , w2 = v1 + c2 .

So,
w1 − w2 = c1 − c2 ∈ C.
And, since wt(w1 − w2 ) = 2 and d(C) = 3 for a Hamming code, we have a
contradiction. Also, by the Pigeonhole Principle, each coset contains exactly
one vector of wt = 1. Thus, the claim holds, and this also proves the theorem.


Theorem 70. Hamming codes are perfect.

Proof. Since d(C) = 3 for Hamming codes, we desire to show that equality
holds in
ρ = [(d − 1)/2] = 1.

108
To attain a contradiction, assume
ρ= max d(x, C) > 1;
x∈GF (q)n

then for some x ∈ GF (q)n , d(x, C) > 1. But by the previous theorem, the
coset x + C must contain a coset leader of wt ≤ 1, a contradiction to the
assumption that d(x, C) > 1. Thus, ρ = 1. 

12 Steganography
Basic idea:
Steganography, meaning “covered writing,” is the science of secret com-
munication. The medium used to carry the information is called the ”cover”
or ”stego-cover.” The term ”digital steganography” refers to secret commu-
nication where the cover is a digital media file.
Cryptography is usually devoted to messages between parties where the
ciphertext is known. In steganography, one tries to hide the existence of the
communication itself. Early examples include writing on the backing of a
wax tablet before covering it, shaving and tattooing the head of a slave, only
to let the hair re-grow, or using invisible ink.
One of the most common systems of digital steganography is the Least
Significant Bit (LSB) system. In this system, the encoder embeds one bit of
information in the least significant bit of a binary number representing the
darkness of a pixel of a given image. In this situation, a greyscale image is
regarded as an array of pixels, where each pixel is represented by a binary
vector of a certain fixed length. Care must be taken with this system to ensure
that the changes made do not betray the stego-cover, while still maximizing
the information hidden.
From a short note of Crandell [Cr] in 1998, it was realized that error-
correcting codes can give rise to “stego-schemes,” ie, methods by which a
message can be hidden in a digital file efficiently.
Usages:
Espionage: In the summer of 2010, 11 suspected Russian spies were ar-
rested in the United States. According to the FBI’s charging documents,
these individuals used a steganographic system that embedded information
into images.
Terrorism: Stegonagraphy is discussed in “Technical Mujahid, Issue #2”
from February 2007. Despite numerous newspaper reports (eg, USA Today

109
Figure 8: The plot of two “tanks” using matrix plot.

in the early 2000’s), there are no (publicly) known cases of terrorist groups
using steganography.
A really simple illustration can be made using Sage’s matrix plot com-
mand.
Here is Sage code used to create the “tank” on the left side of Figure 8.
What matrix produces the “tank” on the right?
Sage

T = [[1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1],
[1,1,1,1,0,0,1,1,1],
[1,0,0,0,0,0,0,1,1],
[1,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,1,1],
[1,0,0,1,1,0,0,1,1]]
A = matrix(T)
matrix_plot(A)

The images look very similar, since we have only changed one “pixel.” Where
that changed pixel is located can communicate (hidden) information.
The rest of our discussion will follow Tucker-Davis’ thesis [TD].

12.1 Basic terminology


Following [Mu], a steganographic system S can be formally defined as

S = {C, M, K, emb, rec},

110
where

(i) C is a set of all possible covers

(ii) M is a set of all possible messages

(iii) K is a set of all possible keys

(iv) emb : C × M × K → C is an embedding function

(v) rec : C × K → M is a recovery function

and
rec(emb(c, m, k), k) = m, (20)
for all m ∈ M, c ∈ C, K ∈ K. We will assume that a fixed key K ∈ K is
used, and therefore, the dependence on an element in the keyspace K can
be ignored. The original cover c is called the plain cover, m is called the
message, and emb(c, m, k) = emb(c, m) is called the stegocover. Let C be
GF (q)n , representing both plain and stego covers. Also, let M be GF (q)k ,
where k is a fixed integer such that 0 ≤ k ≤ n.
An (n, k, t) stegocoding function over finite field GF (q) is a vector-valued
linear transformation L(x) = (`1 (x), `2 (x), . . . , `k (x)) : GF (q)n → GF (q)k
satisfying the following condition: For any given x ∈ GF (q)n and y ∈
GF (q)k , there exists a z ∈ GF (q)n such that wt(z) ≤ t and L(x + z) = y.
We call the matrix L an (n, k, t) stego-code matrix.
In this case,

• x is the plain cover,

• y is the message,

• x + z is the stegocover (so z is the change made to the plain cover).

Of course, z is derived from the specific stegosystem used, but generally it


is desired that the weight of z is small (so the plain copver is not too easily
distinguishable from the stegocover) while still retaining the ability to embed
as much information as possible (so that the stegosystem efficiently conveys
information).

111
12.2 Examples
First, we consider and example from a linear error-correcting block code, C.

Example 71. Let C be the [n, n−k, d] binary linear code of dimension n−k
and minimum distance d having check matrix L. In other words, C is given
by C = ker(L), where L is a k × n matrix of full rank over GF (2). Choose
t such that it is the largest weight of all the coset leaders of C. For each
y ∈ GF (2)k , let v = v(y) ∈ GF (2)n be such that L−1 (y) = C + v. For each
x ∈ GF (2)n , select z to be an element of C + v + x of smallest weight, i.e. a
coset leader.

Theorem 72. Assume L is a k × n matrix as above. L is a (n, k, t) linear


stego-code matrix iff t is the covering radius of the code C whose check matrix
is L.

The proof of the theorem above can be found in [Mu].


Let us consider an example using a [7, 4, 3] Hamming code. Define the
check matrix as follows:
 
1 0 0 1 0 1 1
H =  0 1 0 1 1 0 1 .
0 0 1 0 1 1 1
Denote the coset space by Q = GF (2)7 /C, where C = ker(H) is a [7, 4, 3]
Hamming code.
Let x = (1, 1, 1, 1, 1, 1, 1) and y = (1, 0, 0) We verify that z = (0, 0, 0, 0, 1, 0, 0),
since
 
1
1
    
1 0 0 1 0 1 1  1
 0
 0 1 0 1 1 0 1  1 = 1 = y.
 
0 0 1 0 1 1 1  0
 1
1
1
Next, we consider the extremely clever “F5” stegosystem of Ron Crandall
[Cr].

112
Example 73. First, new notation must be defined. For any positive integer
m, [m]2 denotes the binary representation of m. Similarly, for any binary
representation x, [x]10 denotes the associated positive integer. Therefore, for
any binary x, [[x]10 ]2 = x, and for any positive integer m, [[m]2 ]10 = m.
In this system, the embedding map is defined as

emb : GF (2)n × GF (2)k → GF (2)n (21)


Xn
 
emb(c, m) = c + e( m + ci [i]2 10 ) . (22)
i=1

At this point, note that the message is in GF (2)q , and both the plain
text and stegocover are in GF (2)n .
The recovery map is

rec : GF (2)n → GF (2)k (23)


n
X
rec(c0 ) = c0i [i]2 . (24)
i=1

Theorem 74. With emb and rec as in (22) and (24), respectively, we have
rec(emb(c, m)) = m.

This is proven in the USNA thesis of Tucker-Davis [TD].

References
[BF] J. Bierbrauer and J. Fridrich. Constructing good covering codes for ap-
plications in steganography, preprint, 2006.
http://www.math.mtu.edu/jbierbra/.

[B1] N. Biggs, Codes: An introduction to information, communica-


tion, and cryptography, Springer-Verlag, 2008.

[B2] ——, The critical group from a cryptographic perspective, Bulletin of


the London Mathematical Society 39(2007)829-836

[Bl] S. Blackburn, Cryptanalysing the Critical Group: Efficiently Solving


Biggs’s Discrete Logarithm Problem, preprint, 2008. J. Math. Cryptol-
ogy,

113
[Bo] D. Boneh, Twenty years of attacks on the RSA cryptosystem, Notices
of the American Mathematical Society 46 (1999) 203213.
http://crypto.stanford.edu/~dabo/abstracts/
RSAattack-survey.html

[Br] Timothy Brock’s Honors Project 2005-2006, Linear Feedback Shift Reg-
isters and Cyclic Codes in Sage,
http://www.usna.edu/Users/math/wdj/_files/documents/brock/

[C] C. Celerier, Feedback with carry shift registers and bent sequences,
Honors Project 2011-2012.
http://www.usna.edu/Users/math/wdj/_files/documents/
celerier/

[CJT] C. Christensen, D. Joyner and J. Torres, Lester Hill’s Error-Detecting


Codes, Cryptologia 36 (2012)88-103.

[Cr] Crandall, Ron. Some Notes on Steganography. Posted on a Steganogra-


phy Mailing List, 1998.
http://os.inf.tu-dresden.de/westfeld/crandall.pdf

[Cry] Cryptologia, a scholarly journal dealing with the history and technol-
ogy of communications intelligence.
http://www.tandfonline.com/loi/ucry20

[D] Arthur Conan Doyle, Sherlock Holmes tale The Adventure of the Danc-
ing Men, http://en.wikipedia.org/wiki/The_Adventure_of_the_
Dancing_Men

[G] H. Gaines, Cryptanalysis: A Study of Ciphers and Their


Solution, Courier Dover Publications, 1956.
http://books.google.com/books/about/Cryptanalysis.html?id=
fKNB-7y_Hs4C

[Hi] Lester S. Hill, Cryptography in an Algebraic Alphabet, The American


Mathematical Monthly Vol.36, June-July 1929, pp.306-312.

[H] Michael Hogan’s Honors Project 2009-2010, The Blum-Goldwasser cryp-


tosystem,
http://www.usna.edu/Users/math/wdj/_files/documents/hogan/

114
[JKT] David Joyner, Richard Kreminski, JoAnn Turisco, Applied Abstract
Algebra, Johns Hopkins Univ. Press, 2004.
[K] David Kahn, The Codebreakers, 1996.
[Ko] David Kohel, Cryptography, notes 2008 (138 pages with Sage code).
http://www.sagemath.org/files/kohel-book-2008.pdf
[McA] A. McAndrew, Introduction to Cryptography with Open-
source Software, CRC Press, 2011
[MvOV] Alfred J. Menezes, Paul C. van Oorschot and Scott A. Vanstone ,
Handbook of Applied Cryptography, CRC Press, 1996.
http://www.cacr.math.uwaterloo.ca/hac/
(All chapters are free online.)
[Mi] Gary L. Miller, Riemann’s hypothesis and tests for primality, In
Proceedings of Seventh Annual ACM Symposium on Theory
of Computing, pp. 234239, May 1975.
http://www.cs.cmu.edu/~glmiller/Publications/Papers/Mi75.
pdf.
[Mu] Munuera, Carlos, Steganography from a Coding Theory Point of View,
preprint, 2010.
http://www.singacom.uva.es/oldsite/Actividad/s3cm/s3cm10/
10courses.html
[Po] Edgar Allan Poe, The Raven, http://en.wikipedia.org/wiki/The_
Raven
The Gold-Bug, http://en.wikipedia.org/wiki/The_Gold-Bug
[P] C. Praeger, Course notes at
http://school.maths.uwa.edu.au/~praeger/teaching/3CC/WWW/
chapter2.html
[St] A. Stanoyevitch, Introduction to Cryptography with Mathemat-
ical Foundation, CRC Press, 2010.
[TD] Kyle Tucker-Davis, ”An analysis of the F5 steganographic system”,
Honors Project 2010-2011
http://www.usna.edu/Users/math/wdj/_files/documents/
tucker-davis/

115
[ZL] W. Zhang and S. Li. Steganographic Codes- a New Problem of Coding
Theory, preprint, 2005.
http://arxiv.org/abs/cs/0505072.

116

You might also like