diff options
Diffstat (limited to 'A5.1/python/A51_Tables/a51.py')
-rw-r--r-- | A5.1/python/A51_Tables/a51.py | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/A5.1/python/A51_Tables/a51.py b/A5.1/python/A51_Tables/a51.py new file mode 100644 index 0000000..b118287 --- /dev/null +++ b/A5.1/python/A51_Tables/a51.py @@ -0,0 +1,167 @@ +from BitVector import BitVector +""" +Module with implementations of: +lsfr - Linear Feedback Shift Register +a51 - algorithm A5/1 +""" + +class lsfr: + def __init__(self, length, taps, middle_bit, name=""): + """ + length - length of the register in bits + taps - feedback taps, for clocking the shift register. + These correspond to the primitive polynomial + Example polynomials from A5/1: + x**19 + x**5 + x**2 + x + 1, x**22 + x + 1, + or x**23 + x**15 + x**2 + x + 1 + middle_bit - middle bit of each of the three shift registers, for clock control + name - name of LSFR - for print() + """ + self.taps = taps + self.length = length + self.name = name + self.value = BitVector(size = length); + self.clk_bit_nr = length-middle_bit-1 + + def mix(self, liczba): + """Read value from LSB to MSB and add each bit to LSFR's feedback""" + for key_bit in reversed(liczba): + bit = key_bit + self.clock(bit) + + def clock(self, bit=False): + """Clock LSFR. Can add value of bit to feedback.""" + for tap in self.taps: + bit_nr = self.length - tap - 1 + bit = bit ^ self.value[bit_nr] + self.value << 1 + self.value[self.length-1] = bit + + def out(self): + """Clock LSFR. Can add value of bit to loopback.""" + return self.value[0] + + def clk_bit(self): + """Return clocking bit.""" + return self.value[self.clk_bit_nr] + + def set_value(self, value): + """Set internal state of LSFR.""" + self.value = BitVector(size=self.length, intVal=value) + + def __str__(self): + return "%s:%X" % (self.name, self.value.intValue()) + + +class a51: + def __init__(self): + self.key_size = 64 + self.fn_size = 22 + self.r1 = lsfr(19, [13, 16, 17, 18], 8, "R1") + self.r2 = lsfr(22, [20, 21], 10, "R2") + self.r3 = lsfr(23, [7, 20, 21, 22], 10, "R3") + self.r1_state_mask = ((1 << self.r1.length) -1 ) + self.r2_state_mask = ((1 << self.r2.length) -1 ) + self.r3_state_mask = ((1 << self.r3.length) -1 ) + + def initialize(self, key, fn): + """ + Initialize algorithm with: + + key - value of Kc - integer value of 64 bits + fn - fame number - integer value of 22 bits + """ + key_vect = BitVector(size=self.key_size, intVal=key) + fn_vect = BitVector(size=self.fn_size, intVal=fn) + + self.r1.mix(key_vect) + self.r2.mix(key_vect) + self.r3.mix(key_vect) + + self.r1.mix(fn_vect) + self.r2.mix(fn_vect) + self.r3.mix(fn_vect) + + for i in xrange(0, 100): + self._clock_regs() + + def _clock_regs(self): + """Clock all LSFR's according to majority rule.""" + maj = self._majority() + if self.r1.clk_bit() == maj: + self.r1.clock() + if self.r2.clk_bit() == maj: + self.r2.clock() + if self.r3.clk_bit() == maj: + self.r3.clock() + + def get_output(self): + """ + Generate 228 keystream bits: + + AtoB - 114 bits of keystream for the A->B direction. Store it, MSB first + BtoA - 114 bits of keystream for the B->A direction. Store it, MSB first + """ + AtoB = self.gen_block(114) + BtoA = self.gen_block(114) + return (AtoB, BtoA) + + def _out_bit(self): + out_bit = self.r1.out() ^ self.r2.out() ^ self.r3.out() + return out_bit + + def gen_block(self,size): + out = BitVector(size=0) + for i in xrange(0, size): + self._clock_regs() + out = out + BitVector(intVal = int(self._out_bit())) + return out.intValue() + + def _majority(self): + sum = self.r1.clk_bit()+self.r2.clk_bit()+self.r3.clk_bit() + if sum >= 2: + return True + else: + return False + + def get_state(self): + state = self.r3.value + self.r2.value + self.r1.value + return state.intValue() + + def set_state(self,state): + r1_value = state & self.r1_state_mask + state = state >> self.r1.length + r2_value = state & self.r2_state_mask + state = state >> self.r2.length + r3_value = state & self.r3_state_mask + state = state >> self.r3.length + self.r1.set_value(r1_value) + self.r2.set_value(r2_value) + self.r3.set_value(r3_value) + +def test(): + key = 0xEFCDAB8967452312 #== 0x48C4A2E691D5B3F7 from A5/1 pedagogical implementation in C + fn = 0x000134 + known_good_AtoB = 0x14D3AA960BFA0546ADB861569CA30# == 0x534EAA582FE8151AB6E1855A728C00 from A5/1 pedagogical implementation in C + known_good_BtoA = 0x093F4D68D757ED949B4CBE41B7C6B#== 0x24FD35A35D5FB6526D32F906DF1AC0 from A5/1 pedagogical implementation in C + alg = a51() + alg.initialize(key, fn) + alg.set_state(alg.get_state()) + (AtoB, BtoA) = alg.get_output() + failed = False + if (AtoB != known_good_AtoB) or (BtoA != known_good_BtoA): + failed = True + + print "Key: %x" % key + print "Fame number: %x" % fn + print "Known good keystream from A to B: 0x%029x" % known_good_AtoB + print "Observed keystream from A to B : 0x%029x" % AtoB + print "Known good keystream from B to A: 0x%029x" % known_good_BtoA + print "Observed keystream from B to A : 0x%029x" % BtoA + if failed != True: + print "\nResult: everything looks ok." + else: + print "\nResult: test failed!" + +if __name__ == '__main__': + test()
\ No newline at end of file |