checkbitperm.py
1 #!/usr/bin/env python 2 3 # this file verifies if the indexes of the 64 bit fixed permutation of 4 # step 2 of sbt is according to my reverse engineering as seen in 5 # bitpermbit. as this list of bits is very different from the list of 6 # bits shown on the sbt page of the cryptomuseum. 7 8 # this is using the byte/bit indexes as reverse engineered and 9 # verified by emulator and reimplementation against test vectors 10 def bitperm(cb): 11 return bytes(sum(((cb[by] >> bi) & 1) << (7-i) for i, (by, bi) in enumerate(b)) 12 for b in (((4,4),(1,5),(3,1),(7,6),(0,5),(6,0),(5,7),(2,3)), 13 ((3,2),(0,7),(7,1),(1,0),(6,3),(4,5),(5,4),(2,0)), 14 ((7,5),(3,3),(1,3),(5,1),(0,3),(2,4),(6,2),(4,1)), 15 ((5,5),(3,0),(0,1),(4,3),(1,6),(6,7),(2,2),(7,3)), 16 ((2,5),(6,4),(4,7),(0,6),(5,6),(7,7),(3,5),(1,2)), 17 ((6,5),(4,2),(3,6),(5,2),(1,7),(2,6),(7,4),(0,2)), 18 ((5,0),(7,2),(1,4),(3,4),(4,6),(6,1),(0,0),(2,1)), 19 ((6,6),(0,4),(1,1),(7,0),(3,7),(2,7),(4,0),(5,3))) 20 ) 21 22 # this is the above indexes converted into one continous 0..63 23 # left-to-right indexes of the 64 bit vector. 24 # this is the indexes that we try to verify and contrast with the 25 # indexes published by the cryptomuseum. 26 def bitpermbit(cb): 27 bits = f"{cb:064b}" 28 return sum(int(bits[x]) << (63-i) for i,x in enumerate((35, 10, 30, 57, 2, 55, 40, 20, 29 29, 0, 62, 15, 52, 34, 43, 23, 30 58, 28, 12, 46, 4, 19, 53, 38, 31 42, 31, 6, 36, 9, 48, 21, 60, 32 18, 51, 32, 1, 41, 56, 26, 13, 33 50, 37, 25, 45, 8, 17, 59, 5, 34 47, 61, 11, 27, 33, 54, 7, 22, 35 49, 3, 14, 63, 24, 16, 39, 44)) 36 ) 37 38 # test vector from emulator/reimplementation 39 cb0=0x86820280c3c181c0.to_bytes(8,byteorder="big") 40 cb1 = bitperm(cb0) 41 assert cb1 == 0x164001242c09892a.to_bytes(8,byteorder="big") 42 43 cb1b = bitpermbit(0x86820280c3c181c0) 44 assert cb1b == 0x164001242c09892a 45 46 # symbolic testing, instead of concrete values, we have a distinct 47 def split_by_n(obj, n): 48 # src https://stackoverflow.com/questions/9475241/split-string-every-nth-character 49 return [obj[i:i+n] for i in range(0, len(obj), n)] 50 51 # same as above but instead of algebraic symbolic 52 def bitperm_sym(cb): 53 return [cb[by][7-bi] for by, bi in ((4,4),(1,5),(3,1),(7,6),(0,5),(6,0),(5,7),(2,3), 54 (3,2),(0,7),(7,1),(1,0),(6,3),(4,5),(5,4),(2,0), 55 (7,5),(3,3),(1,3),(5,1),(0,3),(2,4),(6,2),(4,1), 56 (5,5),(3,0),(0,1),(4,3),(1,6),(6,7),(2,2),(7,3), 57 (2,5),(6,4),(4,7),(0,6),(5,6),(7,7),(3,5),(1,2), 58 (6,5),(4,2),(3,6),(5,2),(1,7),(2,6),(7,4),(0,2), 59 (5,0),(7,2),(1,4),(3,4),(4,6),(6,1),(0,0),(2,1), 60 (6,6),(0,4),(1,1),(7,0),(3,7),(2,7),(4,0),(5,3))] 61 62 # same as above but instead of algebraic symbolic 63 def bitpermbit_sym(bits): 64 bits=''.join(''.join(x) for x in bits) 65 return [bits[x] for x in (35, 10, 30, 57, 2, 55, 40, 20, 66 29, 0, 62, 15, 52, 34, 43, 23, 67 58, 28, 12, 46, 4, 19, 53, 38, 68 42, 31, 6, 36, 9, 48, 21, 60, 69 18, 51, 32, 1, 41, 56, 26, 13, 70 50, 37, 25, 45, 8, 17, 59, 5, 71 47, 61, 11, 27, 33, 54, 7, 22, 72 49, 3, 14, 63, 24, 16, 39, 44)] 73 74 allchars=list(chr(x) for x in range(ord('a'),ord('z')+1)) 75 allchars.extend(list(chr(x) for x in range(ord('A'),ord('Z')+1))) 76 allchars.extend(list(chr(x) for x in range(ord('0'),ord('9')+1))) 77 allchars.extend(list(chr(x) for x in range(0x1d400,0x1d467+1))) 78 79 # symbol for each bit position (a-zA-Z0-9𝐀𝐁) 80 cb0sym = split_by_n(allchars[:8*8], 8) 81 # cb0sym == abcdefgh ijklmnop qrstuvwx yzABCDEF GHIJKLMN OPQRSTUV WXYZ0123 456789𝐀𝐁 82 83 print(' '.join((''.join(x) for x in cb0sym))) 84 85 cb1sym = bitperm_sym(cb0sym) 86 print(' '.join(split_by_n(''.join(cb1sym),8))) 87 cb1bsym = bitpermbit_sym(cb0sym) 88 print(' '.join(split_by_n(''.join(cb1bsym),8))) 89 90 assert cb1sym == cb1bsym 91 92 # this confirms my fixed bit permutation indexes are correct. 93 94 # cryptomuseum published order 95 def bitpermbit_cm_sym(bits): 96 bits=''.join(''.join(x) for x in bits) 97 return [bits[x-1] for x in (10, 60, 55, 8, 33, 41, 32, 21, 98 24, 6, 52, 36, 26, 15, 64, 47, 99 11, 30, 34, 22, 49, 42, 4, 62, 100 43, 12, 25, 58, 18, 1, 35, 54, 101 19, 40, 63, 29, 50, 9, 46, 5, 102 3, 37, 53, 23, 61, 44, 14, 31, 103 38, 57, 7, 56, 13, 27, 20, 48, 104 28, 51, 39, 2, 59, 16, 17, 45)] 105 106 cb1cmsym = bitpermbit_cm_sym(cb0sym) 107 print(' '.join(split_by_n(''.join(cb1cmsym),8))) 108 109 # symbolically looks completely different