Advertisement
me30

idea.py

Nov 23rd, 2020
567
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.28 KB | None | 0 0
  1. from binascii import hexlify, unhexlify
  2.  
  3. ROUNDS = 8
  4. KEYLEN = (6 * ROUNDS + 4)
  5.  
  6.  
  7. # For example: bytelist_to_debugstr([255, 0, 192]) -> "[FF 00 C0]".
  8. def bytelist_to_debugstr(bytelist):
  9.     assert isinstance(bytelist, (list, tuple))
  10.     return "[" + " ".join(f"{b:02X}" for b in bytelist) + "]"
  11.  
  12. # Returns x + y modulo 2^16. Inputs and output are uint16.
  13. def add(x, y):
  14.     assert 0 <= x <= 0xFFFF
  15.     assert 0 <= y <= 0xFFFF
  16.     return (x + y) & 0xFFFF
  17.  
  18.  
  19. # Returns x * y modulo (2^16 + 1), where 0x0000 is treated as 0x10000.
  20. # Inputs and output are uint16. Note that 2^16 + 1 is prime..
  21. def multiply(x, y):
  22.     assert 0 <= x <= 0xFFFF
  23.     assert 0 <= y <= 0xFFFF
  24.     if x == 0x0000:
  25.         x = 0x10000
  26.     if y == 0x0000:
  27.         y = 0x10000
  28.     z = (x * y) % 0x10001
  29.     if z == 0x10000:
  30.         z = 0x0000
  31.     assert 0 <= z <= 0xFFFF
  32.     return z
  33.  
  34. # Returns the additive inverse of x modulo 2^16.
  35. # Input and output are uint16. Only used by _invert_key_schedule().
  36. def negate(x):
  37.     assert 0 <= x <= 0xFFFF
  38.     return (-x) & 0xFFFF
  39.  
  40. # Returns the multiplicative inverse of x modulo (2^16 + 1), where 0x0000 is
  41. # treated as 0x10000. Input and output are uint16. Only used by _invert_key_schedule().
  42. def reciprocal(x):
  43.     assert 0 <= x <= 0xFFFF
  44.     if x == 0:
  45.         return 0
  46.     else:
  47.         return pow(x, 0xFFFF, 0x10001)  # By Fermat's little theorem
  48.  
  49. def make_enc_keys(key):
  50.     bigKey = 0
  51.     for byte in key:
  52.         bigKey = (bigKey << 8) | byte
  53.  
  54.     bigKey = (bigKey << 16) | (bigKey >> 112)
  55.     enc_keys = []
  56.     for i in range(KEYLEN):
  57.         offset = (i * 16 + i // 8 * 25) % 128
  58.         enc_keys.append((bigKey >> (128 - offset)) & 0xffff)
  59.     return tuple(enc_keys)
  60.  
  61. def make_dec_keys(enc_keys):
  62.     dec_keys = []
  63.     dec_keys.append(reciprocal(enc_keys[-4]))
  64.     dec_keys.append(negate(enc_keys[-3]))
  65.     dec_keys.append(negate(enc_keys[-2]))
  66.     dec_keys.append(reciprocal(enc_keys[-1]))
  67.     dec_keys.append(enc_keys[-6])
  68.     dec_keys.append(enc_keys[-5])
  69.    
  70.     for i in range(1, ROUNDS):
  71.         j = i * 6
  72.         dec_keys.append(reciprocal(enc_keys[-j - 4]))
  73.         dec_keys.append(negate(enc_keys[-j - 2]))
  74.         dec_keys.append(negate(enc_keys[-j - 3]))
  75.         dec_keys.append(reciprocal(enc_keys[-j - 1]))
  76.         dec_keys.append(enc_keys[-j - 6])
  77.         dec_keys.append(enc_keys[-j - 5])
  78.    
  79.     dec_keys.append(reciprocal(enc_keys[0]))
  80.     dec_keys.append(negate(enc_keys[1]))
  81.     dec_keys.append(negate(enc_keys[2]))
  82.     dec_keys.append(reciprocal(enc_keys[3]))
  83.     return tuple(dec_keys)
  84.  
  85. def idea_crypt(block, key, mode, printdebug = False):
  86.     # Check input arguments
  87.     assert isinstance(block, list) and len(block) == 8
  88.     assert isinstance(key, list) and len(key) == 16
  89.     assert mode in ("encrypt", "decrypt")
  90.     if printdebug: print(f"ideacipher.{mode}(block = {bytelist_to_debugstr(block)}, key = {bytelist_to_debugstr(key)})")
  91.    
  92.     # Compute and handle the key schedule
  93.     keyschedule = make_enc_keys(key)
  94.     if mode == "decrypt":
  95.         keyschedule = make_dec_keys(keyschedule)
  96.     # print(keys)
  97.     # Pack block bytes into variables as uint16 in big endian
  98.     w = block[0] << 8 | block[1]
  99.     x = block[2] << 8 | block[3]
  100.     y = block[4] << 8 | block[5]
  101.     z = block[6] << 8 | block[7]
  102.    
  103.     # Perform 8 rounds of encryption/decryption
  104.     for i in range(ROUNDS):
  105.         if printdebug: print(f"    Round {i}: block = [{w:04} {x:04} {y:04} {z:04}]")
  106.         j = i * 6
  107.         w = multiply(w, keyschedule[j + 0])
  108.         x = add(x, keyschedule[j + 1])
  109.         y = add(y, keyschedule[j + 2])
  110.         z = multiply(z, keyschedule[j + 3])
  111.         u = multiply(w ^ y, keyschedule[j + 4])
  112.         v = multiply(add(x ^ z, u), keyschedule[j + 5])
  113.         u = add(u, v)
  114.         w ^= v
  115.         x ^= u
  116.         y ^= v
  117.         z ^= u
  118.         x, y = y, x
  119.    
  120.     # Perform final half-round
  121.     if printdebug: print(f"    Round {ROUNDS}: block = [{w:04} {x:04} {y:04} {z:04}]")
  122.     x, y = y, x
  123.     w = multiply(w, keyschedule[-4])
  124.     x = add(x, keyschedule[-3])
  125.     y = add(y, keyschedule[-2])
  126.     z = multiply(z, keyschedule[-1])
  127.    
  128.     # Serialize the final block as a bytelist in big endian
  129.     return [
  130.         w >> 8, w & 0xFF,
  131.         x >> 8, x & 0xFF,
  132.         y >> 8, y & 0xFF,
  133.         z >> 8, z & 0xFF]
  134.  
  135.  
  136. #1000, 2000, 3000, 4000, 4000, 3000, 2000, 1000 = b'\x03\xe8\x07\xd0\x0b\xb8\x0f\xa0\x0f\xa0\x0b\xb8\x07\xd0\x03\xe8'
  137.  
  138. key = b'\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08'
  139. block = b'\x00\x00\x00\x01\x00\x02\x00\x03'
  140. cipher = idea_crypt(list(block), list(key), "encrypt", True)
  141. print(f"block = [{cipher[0]:04} {cipher[1]:04} {cipher[2]:04} {cipher[3]:04}]")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement