Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

LFSR Sum

import sys
sys.path.insert(0, '../../')

from toolkit import lfsrStream, XOR

LFSR Sum

We’ve already seen that a single LFSR are vulnerable to known-plaintext attacks. However, by combining multiple LFSRs we can start to create a system which is at least a little more resistant to known-plaintext attacks.

XORing Two LFSR Streams

Suppose you have two LFSR systems, a 3-bit and a 5-bit system, each with an ideal definition that results in a maximal length bitstream from each. Therefore, the first system has definition of: b3b1+b2b_3 \leftarrow b_1' + b_2' with a period of 7, and the second system has definition of: b5b1+b3b_5 \leftarrow b_1' + b_3' with a period of 31. Since 7 and 31 share no common factors, the two bitstreams won’t repeat together until 7×31=2177 \times 31 = 217 bits. To illustrate this fact, let’s generate the corresponding bitstreams.

Choosing seed values of 101 and 11010, let’s print twice the theoretical period to verify each LSFR system does repeat as predicted.

3-bit LFSR, b3b1+b2b_3 \leftarrow b_1' + b_2', seed: 101

print( lfsrStream('101', [1,2], 14) )
10111001011100

5-bit LFSR, b5b1+b3b_5 \leftarrow b_1' + b_3', seed: 11010

print( lfsrStream('11010', [1,3], 62) )
01011001111100011011101010000100101100111110001101110101000010

Next, let’s generate 217 bits from each system and XOR them together and inspect the output to see if there’s any repetition.

print( XOR(lfsrStream('101', [1,2], 217), lfsrStream('11010', [1,3], 217)) )
1110000010000011010111110100111100100100110011010010100110110000000101010010001100100001100001011110000111010001011011010101011101111010110100000011111101100101011000101000111000100010011100111011011111111001100011110

Which, while difficult to confirm visually, has not yet begun to repeat.

Below is 2×217=4342 \times 217 = 434 bits of the same stream. You should see about half-way through the bitstream begins to repeat.

print( XOR(lfsrStream('101', [1,2], 434), lfsrStream('11010', [1,3], 434)) )
11100000100000110101111101001111001001001100110100101001101100000001010100100011001000011000010111100001110100010110110101010111011110101101000000111111011001010110001010001110001000100111001110110111111110011000111101110000010000011010111110100111100100100110011010010100110110000000101010010001100100001100001011110000111010001011011010101011101111010110100000011111101100101011000101000111000100010011100111011011111111001100011110

This is a helpful result:

If LFSR systems have period of p1p_1 and p2p_2, where p1p_1 and p2p_2 share no common factors, then the XOR of these streams will have period p1×p2p_1 \times p_2

This result provides an easy way to generate LFSR streams with long periods using several LFSR systems with shorter periods. For example, the A5/1 cipher which is used to protect GSM cell phone signals in Europe is composed of 3 LFSR streams of different sizes: 19-bit, 22-bit and 23-bit. The periods of each cipher are 2191=524,2872^19 - 1 = 524,287, 2221=4,194,3032^22 - 1 = 4,194,303, and 2231=8,388,6072^23 - 1 = 8,388,607 respectively. These numbers do not share any factors, so by XORing their streams together the cipher can create a keystream with a period of:

524,287×4,194,303×8,388,607=18,446,702,292,280,803,327524,287 \times 4,194,303 \times 8,388,607 = 18,446,702,292,280,803,327

Defining LFSRsum

We’ll formalize this combining of LFSR systems as the LFSRsum cipher, inspired by the book The Mathematics of Encryption.

LFSRsum: The LFSR system uses two LFSR systems in tandem to create a single pseudo-random stream of 1’s and 0’s.

  • The LFSRsum systems works by adding two streams together modulo 2 (XOR) to form a keystream to be used with the XOR cipher.

  • The last bit (b1b_1) of the seed values of each LFSR must be 1. This will ensure that a row will never result in all 0’s, which the LFSR would never be able to move out of.

  • If the two systems are nn-bits and mm-bits, the key for the LFSRsum cipher is n1n-1 + m1m-1 bits in length, where n1n-1 and m1m-1 refer to all but b1b_1 in the seed value in each LFSR respectively.

This method also helps security, since if an attacker were to use a known-plaintext attack to determine some of the keystream, they will still need to determine the seed values for each of the component LFSRs that generate the keystream. While this may frustrate an attacker, it’s certainly possible to do, which will be detailed below.

Example of LFSRsum

Encipher the message Leia using a 5-bit and 7-bit LFSR with key of 1000001011. The LFSRs have definitions of b5b2+b3b_5 \leftarrow b_2' + b_3' and b7b1+b2b_7 \leftarrow b_1' + b_2'.

Solution: Converting base64 plaintext Leia to binary yields: 001011 011110 100010 011010, a 24-bit message.

The key of 1000001011 implies seeds of 10001 and 0010111 for the 5-bit and 7-bit LFSR respectively. Generating 24-bits from each LFSR yields.

print( '5-bit Stream: ' + lfsrStream('10001', [2,3], 24) )
5-bit Stream: 100010011010111100010011
print( '7-bit Stream: ' + lfsrStream('0010111', [1,2], 24) )
7-bit Stream: 111010000111000100100110

XORing these streams together to create the keystream.

print( XOR('100010011010111100010011', '111010000111000100100110') )
011000011101111000110101

Now, use this keystream, 001011011110100010011010, to encipher the plaintext message, 011000011101111000110101 with another XOR:

print( XOR('001011011110100010011010', '011000011101111000110101') )
010011000011011010101111

Converting this binary stream back to base64 characters:


ciphertext binary: 010011 000011 011010 101111
ciphertext base64: TDav

Cracking LFSRsum

Let’s attempt to deduce the seed values for both the 5-bit and 7-bit LFSR ciphers used in the previous example based on a known-plaintext attack. Again, we can assume that the definition for each LFSR is known to the attacker, b5b2+b3b_5 \leftarrow b_2' + b_3' and b7b1+b2b_7 \leftarrow b_1' + b_2'.

The attacker would know that the 5-bit LFSR would start in an initial state of:

b5b_5b4b_4b3b_3b2b_2b1b_1
k1k_1k2k_2k3k_3k4k_41
\vdots\vdots\vdots\vdots\vdots

and the 7-bit LFSR would start in an initial state of:

b7b_7b6b_6b5b_5b4b_4b3b_3b2b_2b1b_1
k5k_5k6k_6k7k_7k8k_8k9k_9k10k_{10} 1
\vdots\vdots\vdots\vdots\vdots\vdots\vdots

where {k1,k2,k3,...,k10}\{ k_1, k_2, k_3, ..., k_{10} \} represent the digits of the key for the system, k1k2k3k4k5k6k7k8k9k10k_1k_2k_3k_4k_5k_6k_7k_8k_9k_{10}. We know (but the attacker does not) that this key is 1000001011.

Using the definition of each LFSR, the attacker can fill in subsequent rows of each table to create a generalized structure of each LFSR stream. Keep in mind that when using bitwise addition, k1k1+2k_1 \equiv k_1 + 2 since 20(mod2)2 \equiv 0 \pmod{2}. Also, note that 2k10(mod2)2k_1 \equiv 0 \pmod{2} regardless of if k1k_1 is a 1 or 0. These facts will help keep the generalized LFSR streams as simple as possible.

5-bit LFSR

b5b_5b4b_4b3b_3b2b_2b1b_1
k1k_1k2k_2k3k_3k4k_41
k3+k4k_3 + k_4k1k_1k2k_2k3k_3k4k_4
k2+k3k_2 + k_3k3+k4k_3 + k_4k1k_1k2k_2k3k_3
k1+k2k_1 + k_2k2+k3k_2 + k_3k3+k4k_3 + k_4k1k_1k2k_2
k1+k3+k4k_1 + k_3 + k_4k1+k2k_1 + k_2k2+k3k_2 + k_3k3+k4k_3 + k_4k1k_1
k2+k4k_2 + k_4k1+k3+k4k_1 + k_3 + k_4k1+k2k_1 + k_2k2+k3k_2 + k_3k3+k4{k_3 + k_4}
k1+k3k_1 + k_3k2+k4k_2 + k_4k1+k3+k4k_1 + k_3 + k_4k1+k2k_1 + k_2k2+k3k_2 + k_3
k2+k3+k4k_2 + k_3 + k_4k1+k3k_1 + k_3k2+k4k_2 + k_4k1+k3+k4k_1 + k_3 + k_4k1+k2k_1 + k_2
k1+k2+k3k_1 + k_2 + k_3k2+k3+k4k_2 + k_3 + k_4k1+k3k_1 + k_3k2+k4k_2 + k_4k1+k3+k4k_1 + k_3 + k_4
k1+k2+k3+k4k_1 + k_2 + k_3 + k_4k1+k2+k3k_1 + k_2 + k_3k2+k3+k4k_2 + k_3 + k_4k1+k3k_1 + k_3k2+k4k_2 + k_4
k1+k2+k4k_1 + k_2 + k_4k1+k2+k3+k4k_1 + k_2 + k_3 + k_4k1+k2+k3k_1 + k_2 + k_3k2+k3+k4k_2 + k_3 + k_4k1+k3k_1 + k_3

7-bit LFSR

b7b_7b6b_6b5b_5b4b_4b3b_3b2b_2b1b_1
k5k_5k6k_6k7k_7k8k_8k9k_9k10k_{10}1
1+k101 + k_{10}k5k_5k6k_6k7k_7k8k_8k9k_9k10k_{10}
k9+k10k_9 + k_{10}1+k101 + k_{10}k5k_5k6k_6k7k_7k8k_8k9k_9
k8+k9k_8 + k_9k9+k10k_9 + k_{10}1+k101 + k_{10}k5k_5k6k_6k7k_7k8k_8
k7+k8k_7 + k_8k8+k9k_8 + k_9k9+k10k_9 + k_{10}1+k101 + k_{10}k5k_5k6k_6k7k_7
k6+k7k_6 + k_7k7+k8k_7 + k_8k8+k9k_8 + k_9k9+k10k_9 + k_{10}1+k101 + k_{10}k5k_5k6k_6
k5+k6k_5 + k_6k6+k7k_6 + k_7k7+k8k_7 + k_8k8+k9k_8 + k_9k9+k10k_9 + k_{10}1+k101 + k_{10}k5k_5
1+k5+k101 + k_5 + k_{10}k5+k6k_5 + k_6k6+k7k_6 + k_7k7+k8k_7 + k_8k8+k9k_8 + k_9k9+k10k_9 + k_{10}1+k101 + k_{10}
1+k91 + k_91+k5+k101 + k_5 + k_{10}k5+k6k_5 + k_6k6+k7k_6 + k_7k7+k8k_7 + k_8k8+k9k_8 + k_9k9+k10k_9 + k_{10}
k8+k10k_8 + k_{10}1+k91 + k_91+k5+k101 + k_5 + k_{10}k5+k6k_5 + k_6k6+k7k_6 + k_7k7+k8k_7 + k_8k8+k9k_8 + k_9
k7+k9k_7 + k_9k8+k10k_8 + k_{10}1+k91 + k_91+k5+k101 + k_5 + k_{10}k5+k6k_5 + k_6k6+k7k_6 + k_7k7+k8k_7 + k_8

XORing these two general LFSR streams together results in the following keystream, all modulo 2, which the attacker can deduce to be equivalent to 01100001110... using a known-plaintext attack.

general LFSR keystreamkeystream from known-plaintext
00
k4+k10k_4 + k_{10}1
k3+k9k_3 + k_91
k2+k8k_2 + k_80
k1+k7k_1 + k_70
k3+k4+k6k_3 + k_4 + k_60
k2+k3+k5k_2 + k_3 + k_50
1+k1+k2+k101 + k_1 + k_2 + k_{10}1
k1+k3+k4+k9+k10k_1 + k_3 + k_4 + k_9 + k_{10}1
k2+k4+k8+k9k_2 + k_4 + k_8 + k_91
k1+k3+k7+k8k_1 + k_3 + k_7 + k_80

Which means that the attacker now has a system of 10 linear equations with 10 unknown values. This is a system that can be solved through various algebraic or computational techniques.

{k4+k101(mod2)k3+k91(mod2)k2+k80(mod2)k1+k70(mod2)k3+k4+k60(mod2)k2+k3+k50(mod2)k1+k2+k100(mod2)k1+k3+k4+k91(mod2)k2+k4+k8+k91(mod2)k1+k3+k7+k80(mod2)\begin{cases} & & & k_4 & & & & & & + k_{10} &\equiv 1 \pmod{2} \\ & & k_3 & & & & & & +k_9 & &\equiv 1 \pmod{2} \\ & k_2 & & & & & & +k_8 & & &\equiv 0 \pmod{2} \\ k_1 & & & & & & +k_7 & & & &\equiv 0 \pmod{2} \\ & & k_3 & +k_4 & & +k_6& & & & &\equiv 0 \pmod{2} \\ & k_2 & +k_3 & & +k_5 & & & & & &\equiv 0 \pmod{2} \\ k_1 &+k_2 & & & & & & & & +k_{10} &\equiv 0 \pmod{2} \\ k_1 & & +k_3 & +k_4 & & & & & +k_9& &\equiv 1 \pmod{2} \\ & k_2 & & +k_4 & & & & +k_8& +k_9& &\equiv 1 \pmod{2} \\ k_1 & & +k_3 & & & & +k_7 & +k_8& & &\equiv 0 \pmod{2} \\ \end{cases}

In this case, the attacker can utilize a variety of technology tools to implement Row Reduction, a very efficient way to solve systems of linear equations. Most mathematical software (Python, Maple, Mathematica, MATLAB, etc.) can do this with a bit of configuration to solve the system of equations quickly and determine that k1k2k3k4k5k6k7k8k9k10k_1k_2k_3k_4k_5k_6k_7k_8k_9k_{10} is equal to: 1000001011. This solution is equivalent to the seed values of 10001 and 0010111.

At this point the attacker has all the information needed to compute as much of the keystream as needed to completely decipher the message.