Skip to content

Instantly share code, notes, and snippets.

@ItsDrike
Created June 17, 2022 17:23
Show Gist options
  • Save ItsDrike/ecb6a062a72f724fcf1d442922a0d939 to your computer and use it in GitHub Desktop.
Save ItsDrike/ecb6a062a72f724fcf1d442922a0d939 to your computer and use it in GitHub Desktop.
Linear Feedback Shift Register (LSFR) random number generator
#!/usr/bin/python
# Linear Feedback Shift Register (LFSR) Random Number generator We should be
# able to go through all of the possible states from our initial one before we
# start repeating. This means that we can have 2^n-1 unique numbers, n being
# the number of bits in our state before we start repeating numbers.
# Warning: This algorithm is NOT cryptographically secure, because given enough
# outputted bytes, by solving a bunch of linear equations and recompute the
# LFSR generator bits. If we do need something cryptographically secure, we
# could combine 2 LFSR streams XORed together, and they don't always shift at
# the same time, we could achieve something a lot more chaotic and very hard to
# predict.
def pcb(number: int):
"""
A debug function for printing a number represented in a binary form, in a
compressed manner, this is only really useful if the number is expected to
have a lot of repeated bits (100000000001 would be 1x1+10x0+1x1)
"""
compressed_binary = []
binary_form = bin(number)[2:]
last_bit = binary_form[0] # To avoid 0x0 or 0x1 always use starting last bit
bit_amount = 0
for bit in binary_form:
if bit == last_bit:
bit_amount += 1
else:
compressed_binary.append(f"{bit_amount}x{last_bit}")
last_bit = bit
bit_amount = 1
compressed_binary.append(f"{bit_amount}x{last_bit}")
return "+".join(compressed_binary)
def lfsr(state: int, number_size: int, taps: list[int]) -> int:
"""
LFSR random number generator algorithm.
- `state` is the initial "seed" of our algorithm
- `taps` are the bit positions which will be used in the XOR
- `number_size` is the size for the number to be generated in bits
The returned number is composed of `number_size` of bits, produced by the
algorithm given the `state` and `taps`.
"""
output = 0
state_binary_length = len(bin(state)) - 2
max_nonrepeating = 2 ** state_binary_length - 1
if number_size > max_nonrepeating:
print(
f"Warning, with {state_binary_length} binary state length, you "
f"only have {max_nonrepeating} non-repeating random numbers, you "
f"requested {number_size} numbers, which means some of the bits in it "
"will follow a repeated sequence."
)
for i in range(number_size):
# Get the first bit, which will be our random output
rand = state & 1
# Print the state and bit output for debugging purposes
print(f"#{i + 1} {state=:04b}, out={rand:01b}")
# Leftshift the output to make space for a new ending bit and use OR to
# set the bit depending on rand
output = (output << 1) | rand
# Take the XOR of the tapped bits by running XOR on the state and the
# right-shifted state, and taking the last digit of that.
# For example:
# 1001 is our original state
# 0100 is the right bitshifted state
# 1101 is the XOR result for taps=[1] (works for state=0b1001)
newbit = state
for tap_bit_pos in taps:
newbit = newbit ^ (state >> tap_bit_pos)
# We can now use result & 1 to only obtain the last bit allowing us to
# only run XOR between last 2 bits of current state
newbit = newbit & 1
# Bitshift the state to the right and set the first bit of it to the
# computed newbit value
state = (state >> 1) | (newbit << (state_binary_length - 1))
return output
# Define some parameters to be used for the LFSR function
# The state used in the start, can't be only zeroes
#start_state = 0b1001
# For state of 1001, the taps (positions to get XORed) are just [1]
# taps = [1]
# Will produce 1000...0001 of 128 bits long guaranteeing 2^128 - 1
# non-repeating pseudo-random number sequence
start_state = (1 << 127) | 1
taps = [1, 2, 7]
# The desired size (in bits) of the generated random number
number_size = 24
if __name__ == "__main__":
out = lfsr(start_state, number_size, taps=taps)
binary_out = f"{out:b}".zfill(number_size)
print(f"Obtained random number: {out} (binary form: {binary_out}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment