speck.nim 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import strformat
  2. import bitops
  3. # For rotation, the following functions can be used:
  4. # rotateLeftBits, rotateRightBits from std module
  5. #[
  6. SPECK Helper functions
  7. * words64ToBytes
  8. * bytesToWords64
  9. ]#
  10. proc bytesToWords64(bytes: seq[uint8], numbytes: int): seq[uint64] =
  11. # counter for the input bytes
  12. var j: int = 0
  13. for i in 0..<int(numbytes/8):
  14. var tmpNum: uint64 = cast[uint64](bytes[j]) or
  15. cast[uint64](bytes[j+1]).shl(8) or
  16. cast[uint64](bytes[j+2]).shl(16) or
  17. cast[uint64](bytes[j+3]).shl(24) or
  18. cast[uint64](bytes[j+4]).shl(32) or
  19. cast[uint64](bytes[j+5]).shl(40) or
  20. cast[uint64](bytes[j+6]).shl(48) or
  21. cast[uint64](bytes[j+7]).shl(56)
  22. result.add(tmpNum)
  23. j += 8
  24. proc words64ToBytes(words: seq[uint64], numwords: int): seq[uint8] =
  25. for i in 0..<numwords:
  26. var tmpCurrentWord = words[i]
  27. result.add(cast[uint8](tmpCurrentWord))
  28. result.add(cast[uint8](tmpCurrentWord.shr(8)))
  29. result.add(cast[uint8](tmpCurrentWord.shr(16)))
  30. result.add(cast[uint8](tmpCurrentWord.shr(24)))
  31. result.add(cast[uint8](tmpCurrentWord.shr(32)))
  32. result.add(cast[uint8](tmpCurrentWord.shr(40)))
  33. result.add(cast[uint8](tmpCurrentWord.shr(48)))
  34. result.add(cast[uint8](tmpCurrentWord.shr(56)))
  35. #[
  36. simpler in c:
  37. #define r(x,y,k) (x=ror64(x,8), x+=y, x^=k, y=rol64(y,3), y^=x)
  38. ]#
  39. proc R(x: uint64, y: uint64, k: uint64): (uint64, uint64) =
  40. var lx = x
  41. var ly = y
  42. var lk = k
  43. lx = rotaterightbits(lx, 8)
  44. lx += ly
  45. lx = lx xor lk
  46. ly = rotateleftbits(ly, 3)
  47. ly = ly xor lx
  48. return (lx, ly)
  49. #[
  50. simpler in c:
  51. #define RI(x,y,k) (y^=x, y=ROR64(y,3), x^=k, x-=y, x=ROL64(x,8))
  52. ]#
  53. proc RI(x: uint64, y: uint64, k: uint64): (uint64, uint64) =
  54. var lx = x
  55. var ly = y
  56. var lk = k
  57. ly = ly xor lx
  58. ly = rotaterightbits(ly, 3)
  59. lx = lx xor lk
  60. lx = lx - ly
  61. lx = rotateleftbits(lx, 8)
  62. return (lx, ly)
  63. #[
  64. Modifies the initial key in several rounds
  65. to output the round-key
  66. ]#
  67. proc speck128256KeySchedule(K: seq[uint64]): seq[uint64] =
  68. var D:uint64 = K[3]
  69. var C:uint64 = K[2]
  70. var B:uint64 = K[1]
  71. var A:uint64 = K[0]
  72. for i in countUp(0, 33, 3):
  73. result.add(A)
  74. (B, A) = R(B, A, cast[uint64](i)) # ER64(B,A,i)
  75. result.add(A)
  76. (C, A) = R(C, A, cast[uint64](i+1)) #ER64(C,A,i+1)
  77. result.add(A)
  78. (D, A) = R(D, A, cast[uint64](i+2)) # ER64(D,A,i+2)
  79. result.add(A)
  80. proc speck128256Encrypt(Pt: seq[uint64], rk: seq[uint64]): seq[uint64] =
  81. result.add(Pt[0])
  82. result.add(Pt[1])
  83. for i in 0..<34:
  84. (result[1], result[0]) = R(result[1], result[0], rk[i])
  85. proc speck128256Decrypt(Ct: seq[uint64], rk: seq[uint64]): seq[uint64] =
  86. result.add(Ct[0])
  87. result.add(Ct[1])
  88. for i in countDown(33, 0, 1):
  89. (result[1], result[0]) = RI(result[1], result[0], rk[i])
  90. proc test() =
  91. echo "[+] Starting tests"
  92. # plaintext as byte array
  93. var pt = @[uint8 0x70, 0x6f, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x20, 0x49, 0x6e, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65]
  94. var key = @[uint8 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f]
  95. # Start encryption routine
  96. let Pt = bytesToWords64(pt, 16)
  97. let K = bytesToWords64(key, 32)
  98. let rk = speck128256KeySchedule(K)
  99. let Ct = speck128256Encrypt(Pt, rk)
  100. let ct = words64ToBytes(Ct, 2)
  101. # decryption
  102. let reversed_PtB = speck128256Decrypt(Ct, rk)
  103. let reversed_pt = words64ToBytes(reversed_PtB, 2)
  104. #
  105. # check if every result is correct according to the reference values
  106. assert Pt == @[uint64 0x202e72656e6f6f70'u64, 0x65736f6874206e49'u64]
  107. assert K == @[uint64 0x0706050403020100'u64, 0x0f0e0d0c0b0a0908'u64, 0x1716151413121110'u64, 0x1f1e1d1c1b1a1918'u64]
  108. assert Ct == @[uint64 0x4eeeb48d9c188f43'u64, 0x4109010405c0f53e'u64]
  109. assert ct == @[uint8 67, 143, 24, 156, 141, 180, 238, 78, 62, 245, 192, 5, 4, 1, 9, 65]
  110. assert reversed_pt == pt
  111. echo "[+] All tests successfull"
  112. test()