# (c) 04/11/2016, Rolf Rolles, Mobius Strip Reverse Engineeringimport z3impo - Pastebin.com

3 min read Original article ↗
  1. # (c) 04/11/2016, Rolf Rolles, Mobius Strip Reverse Engineering

  2. import z3

  3. import struct

  4. # XOR key for src.bin

  5. KEY_SECTOR = 0x37

  6. # Counter position, as two words

  7. CNTLO      = 0

  8. CNTHI      = 0

  9. # Symbolic names for array positions

  10. POS_CONST0 = 0

  11. POS_KEY0   = 1

  12. POS_KEY2   = 2

  13. POS_KEY4   = 3

  14. POS_KEY6   = 4

  15. POS_CONST2 = 5

  16. POS_NONCE0 = 6

  17. POS_NONCE2 = 7

  18. POS_CNTLO  = 8

  19. POS_CNTHI  = 9

  20. POS_CONST4 = 10

  21. POS_KEY8   = 11

  22. POS_KEY10  = 12

  23. POS_KEY12  = 13

  24. POS_KEY14  = 14

  25. POS_CONST6 = 15

  26. # Constant values and their positions

  27. constvals = [ 0x7865, 0x646E, 0x2D32, 0x6574 ]

  28. constpos  = [ POS_CONST0, POS_CONST2, POS_CONST4, POS_CONST6 ]

  29. # Key byte and word variables

  30. key_bytes = [ z3.BitVec('kb%d' % i,8) for i in xrange(8) ]

  31. key_words = [ None ]*len(key_bytes)

  32. # Representation of key bytes as transformed words

  33. for i in xrange(len(key_bytes)):

  34.     hi = z3.ZeroExt(8,key_bytes[i]) << 9

  35.     lo = z3.ZeroExt(8,key_bytes[i]) + ord('z')

  36.     key_words[i] = hi | lo

  37. # Position of key words within array

  38. keypos  = [ POS_KEY0, POS_KEY2, POS_KEY4, POS_KEY6, POS_KEY8, POS_KEY10, POS_KEY12, POS_KEY14 ]

  39. # Initialize the modified Salsa.16-10 array (word-level values)

  40. def init_array(nonce0,nonce2,cntlo,cnthi):

  41.     initial_array = [None]*16

  42. for i in xrange(len(constvals)):

  43.         initial_array[constpos[i]] = z3.BitVecVal(constvals[i],16)

  44. for i in xrange(len(key_words)):

  45.         initial_array[  keypos[i]] = key_words[i]

  46.     initial_array[POS_NONCE0] = z3.BitVecVal(nonce0,16)

  47.     initial_array[POS_NONCE2] = z3.BitVecVal(nonce2,16)

  48.     initial_array[POS_CNTLO]  = z3.BitVecVal(cntlo, 16)

  49.     initial_array[POS_CNTHI]  = z3.BitVecVal(cnthi, 16)

  50. return initial_array

  51. # Input values to the Salsa round primitive

  52. salsa_steps = [

  53. (4,0,12,7),

  54. (8,4,0,9),

  55. (12,8,4,13),

  56. (0,12,8,18),

  57. (9,5,1,7),

  58. (13,9,5,9),

  59. (1,13,9,13),

  60. (5,1,13,18),

  61. (14,10,6,7),

  62. (2,14,10,9),

  63. (6,2,14,13),

  64. (10,6,2,18),

  65. (3,15,11,7),

  66. (7,3,15,9),

  67. (11,7,3,13),

  68. (15,11,7,18),

  69. (1,0,3,7),

  70. (2,1,0,9),

  71. (3,2,1,13),

  72. (0,3,2,18),

  73. (6,5,4,7),

  74. (7,6,5,9),

  75. (4,7,6,13),

  76. (5,4,7,18),

  77. (11,10,9,7),

  78. (8,11,10,9),

  79. (9,8,11,13),

  80. (10,9,8,18),

  81. (12,15,14,7),

  82. (13,12,15,9),

  83. (14,13,12,13),

  84. (15,14,13,18),

  85. ]

  86. # z3 representation of the Salsa.16 round primitive

  87. def step(arr,out,inl,inr,rotamt):

  88. sum = z3.ZeroExt(16,arr[inl] + arr[inr])

  89.     rot = z3.RotateLeft(sum,rotamt)

  90.     arr[out] ^= z3.Extract(15,0,rot)

  91. # Perform one Salsa.16 round

  92. def salsarnd(arr):

  93. for rnd in salsa_steps:

  94.         step(arr,*rnd)

  95. # Perform all 10 Salsa.16 rounds

  96. def salsa10(arr):

  97. for i in xrange(10):

  98.         salsarnd(arr)

  99. # Create solver object

  100. s = z3.SolverFor('QF_BV')

  101. # Extract nonce words from the binary nonce data; create initial array

  102. with open("salsa10-nonce.bin", "rb") as f_nonce:

  103. (n0,n2,n4,n6) = struct.unpack("HHHH", f_nonce.read())

  104.     initial = init_array(n0,n4,CNTLO,CNTHI)

  105.     init_copy = [ x for x in initial ]

  106. # Extract ciphertext words from the binary sector data

  107. with open("salsa10-src.bin", "rb") as f_src:

  108.     srcbytes   = bytearray(f_src.read())

  109.     z3srcwords = [ None ] * len(initial)

  110. for i in xrange(len(initial)):

  111.         lo = (srcbytes[4*i]   ^ KEY_SECTOR)

  112.         hi = (srcbytes[4*i+1] ^ KEY_SECTOR) << 8

  113.         z3srcwords[i] = z3.BitVecVal(hi | lo,16)

  114. # Specify constraints on key bytes

  115. for k in key_bytes:

  116.     s.add(k != ord('O'))

  117.     s.add(k != ord('I'))

  118.     s.add(k != ord('l'))

  119.     s.add(z3.Or(z3.And(k >= 0x31,k <= 0x39),z3.And(k >= 0x61,k <= 0x78),z3.And(k >= 0x41,k <= 0x58)))

  120. # Perform the Salsa.16 operation

  121. salsa10(initial)

  122. # Specify constraints on outputs

  123. for i in xrange(len(initial)):

  124.     s.add((init_copy[i] + initial[i]) == z3srcwords[i])

  125. # Check SAT, print solution if so

  126. if s.check() == z3.sat:

  127.     m = s.model()

  128.     keybytes = map(lambda k: chr(m[k].as_long()), key_bytes)

  129. print 'x'.join(keybytes)

  130. else:

  131. print "UNSAT!"