sha1.nim 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2015 Nim Contributors
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## **Note:** Import ``std/sha1`` to use this module
  10. ##
  11. ## SHA-1 (Secure Hash Algorithm 1) is a cryptographic hash function which
  12. ## takes an input and produces a 160-bit (20-byte) hash value known as a
  13. ## message digest.
  14. ##
  15. ## .. code-block::
  16. ## import std/sha1
  17. ##
  18. ## let accessName = secureHash("John Doe")
  19. ## assert $accessName == "AE6E4D1209F17B460503904FAD297B31E9CF6362"
  20. ##
  21. ## .. code-block::
  22. ## import std/sha1
  23. ##
  24. ## let
  25. ## a = secureHashFile("myFile.nim")
  26. ## b = parseSecureHash("10DFAEBF6BFDBC7939957068E2EFACEC4972933C")
  27. ##
  28. ## if a == b:
  29. ## echo "Files match"
  30. ##
  31. ## **See also:**
  32. ## * `base64 module<base64.html>`_ implements a base64 encoder and decoder
  33. ## * `hashes module<hashes.html>`_ for efficient computations of hash values for diverse Nim types
  34. ## * `md5 module<md5.html>`_ implements the MD5 checksum algorithm
  35. import strutils
  36. from endians import bigEndian32, bigEndian64
  37. const Sha1DigestSize = 20
  38. type
  39. Sha1Digest = array[0 .. Sha1DigestSize-1, uint8]
  40. SecureHash* = distinct Sha1Digest
  41. type
  42. Sha1State = object
  43. count: int
  44. state: array[5, uint32]
  45. buf: array[64, byte]
  46. # This implementation of the SHA1 algorithm was ported from the Chromium OS one
  47. # with minor modifications that should not affect its functionality.
  48. proc newSha1State(): Sha1State =
  49. result.count = 0
  50. result.state[0] = 0x67452301'u32
  51. result.state[1] = 0xEFCDAB89'u32
  52. result.state[2] = 0x98BADCFE'u32
  53. result.state[3] = 0x10325476'u32
  54. result.state[4] = 0xC3D2E1F0'u32
  55. template ror27(val: uint32): uint32 = (val shr 27) or (val shl 5)
  56. template ror2 (val: uint32): uint32 = (val shr 2) or (val shl 30)
  57. template ror31(val: uint32): uint32 = (val shr 31) or (val shl 1)
  58. proc transform(ctx: var Sha1State) =
  59. var W: array[80, uint32]
  60. var A, B, C, D, E: uint32
  61. var t = 0
  62. A = ctx.state[0]
  63. B = ctx.state[1]
  64. C = ctx.state[2]
  65. D = ctx.state[3]
  66. E = ctx.state[4]
  67. template SHA_F1(A, B, C, D, E, t: untyped) =
  68. bigEndian32(addr W[t], addr ctx.buf[t * 4])
  69. E += ror27(A) + W[t] + (D xor (B and (C xor D))) + 0x5A827999'u32
  70. B = ror2(B)
  71. while t < 15:
  72. SHA_F1(A, B, C, D, E, t + 0)
  73. SHA_F1(E, A, B, C, D, t + 1)
  74. SHA_F1(D, E, A, B, C, t + 2)
  75. SHA_F1(C, D, E, A, B, t + 3)
  76. SHA_F1(B, C, D, E, A, t + 4)
  77. t += 5
  78. SHA_F1(A, B, C, D, E, t + 0) # 16th one, t == 15
  79. template SHA_F11(A, B, C, D, E, t: untyped) =
  80. W[t] = ror31(W[t-3] xor W[t-8] xor W[t-14] xor W[t-16])
  81. E += ror27(A) + W[t] + (D xor (B and (C xor D))) + 0x5A827999'u32
  82. B = ror2(B)
  83. SHA_F11(E, A, B, C, D, t + 1)
  84. SHA_F11(D, E, A, B, C, t + 2)
  85. SHA_F11(C, D, E, A, B, t + 3)
  86. SHA_F11(B, C, D, E, A, t + 4)
  87. template SHA_F2(A, B, C, D, E, t: untyped) =
  88. W[t] = ror31(W[t-3] xor W[t-8] xor W[t-14] xor W[t-16])
  89. E += ror27(A) + W[t] + (B xor C xor D) + 0x6ED9EBA1'u32
  90. B = ror2(B)
  91. t = 20
  92. while t < 40:
  93. SHA_F2(A, B, C, D, E, t + 0)
  94. SHA_F2(E, A, B, C, D, t + 1)
  95. SHA_F2(D, E, A, B, C, t + 2)
  96. SHA_F2(C, D, E, A, B, t + 3)
  97. SHA_F2(B, C, D, E, A, t + 4)
  98. t += 5
  99. template SHA_F3(A, B, C, D, E, t: untyped) =
  100. W[t] = ror31(W[t-3] xor W[t-8] xor W[t-14] xor W[t-16])
  101. E += ror27(A) + W[t] + ((B and C) or (D and (B or C))) + 0x8F1BBCDC'u32
  102. B = ror2(B)
  103. while t < 60:
  104. SHA_F3(A, B, C, D, E, t + 0)
  105. SHA_F3(E, A, B, C, D, t + 1)
  106. SHA_F3(D, E, A, B, C, t + 2)
  107. SHA_F3(C, D, E, A, B, t + 3)
  108. SHA_F3(B, C, D, E, A, t + 4)
  109. t += 5
  110. template SHA_F4(A, B, C, D, E, t: untyped) =
  111. W[t] = ror31(W[t-3] xor W[t-8] xor W[t-14] xor W[t-16])
  112. E += ror27(A) + W[t] + (B xor C xor D) + 0xCA62C1D6'u32
  113. B = ror2(B)
  114. while t < 80:
  115. SHA_F4(A, B, C, D, E, t + 0)
  116. SHA_F4(E, A, B, C, D, t + 1)
  117. SHA_F4(D, E, A, B, C, t + 2)
  118. SHA_F4(C, D, E, A, B, t + 3)
  119. SHA_F4(B, C, D, E, A, t + 4)
  120. t += 5
  121. ctx.state[0] += A
  122. ctx.state[1] += B
  123. ctx.state[2] += C
  124. ctx.state[3] += D
  125. ctx.state[4] += E
  126. proc update(ctx: var Sha1State, data: openArray[char]) =
  127. var i = ctx.count mod 64
  128. var j = 0
  129. var len = data.len
  130. # Gather 64-bytes worth of data in order to perform a round with the leftover
  131. # data we had stored (but not processed yet)
  132. if len > 64 - i:
  133. copyMem(addr ctx.buf[i], unsafeAddr data[j], 64 - i)
  134. len -= 64 - i
  135. j += 64 - i
  136. transform(ctx)
  137. # Update the index since it's used in the while loop below _and_ we want to
  138. # keep its value if this code path isn't executed
  139. i = 0
  140. # Process the bulk of the payload
  141. while len >= 64:
  142. copyMem(addr ctx.buf[0], unsafeAddr data[j], 64)
  143. len -= 64
  144. j += 64
  145. transform(ctx)
  146. # Process the tail of the payload (len is < 64)
  147. while len > 0:
  148. dec len
  149. ctx.buf[i] = byte(data[j])
  150. inc i
  151. inc j
  152. if i == 64:
  153. transform(ctx)
  154. i = 0
  155. ctx.count += data.len
  156. proc finalize(ctx: var Sha1State): Sha1Digest =
  157. var cnt = uint64(ctx.count * 8)
  158. # A 1 bit
  159. update(ctx, "\x80")
  160. # Add padding until we reach a complexive size of 64 - 8 bytes
  161. while (ctx.count mod 64) != (64 - 8):
  162. update(ctx, "\x00")
  163. # The message length as a 64bit BE number completes the block
  164. var tmp: array[8, char]
  165. bigEndian64(addr tmp[0], addr cnt)
  166. update(ctx, tmp)
  167. # Turn the result into a single 160-bit number
  168. for i in 0 ..< 5:
  169. bigEndian32(addr ctx.state[i], addr ctx.state[i])
  170. copyMem(addr result[0], addr ctx.state[0], Sha1DigestSize)
  171. # Public API
  172. proc secureHash*(str: openArray[char]): SecureHash =
  173. ## Generates a ``SecureHash`` from a ``str``.
  174. ##
  175. ## **See also:**
  176. ## * `secureHashFile proc <#secureHashFile,string>`_ for generating a ``SecureHash`` from a file
  177. ## * `parseSecureHash proc <#parseSecureHash,string>`_ for converting a string ``hash`` to ``SecureHash``
  178. runnableExamples:
  179. let hash = secureHash("Hello World")
  180. assert hash == parseSecureHash("0A4D55A8D778E5022FAB701977C5D840BBC486D0")
  181. var state = newSha1State()
  182. state.update(str)
  183. SecureHash(state.finalize())
  184. proc secureHashFile*(filename: string): SecureHash =
  185. ## Generates a ``SecureHash`` from a file.
  186. ##
  187. ## **See also:**
  188. ## * `secureHash proc <#secureHash,string>`_ for generating a ``SecureHash`` from a string
  189. ## * `parseSecureHash proc <#parseSecureHash,string>`_ for converting a string ``hash`` to ``SecureHash``
  190. secureHash(readFile(filename))
  191. proc `$`*(self: SecureHash): string =
  192. ## Returns the string representation of a ``SecureHash``.
  193. ##
  194. ## **See also:**
  195. ## * `secureHash proc <#secureHash,string>`_ for generating a ``SecureHash`` from a string
  196. runnableExamples:
  197. let hash = secureHash("Hello World")
  198. assert $hash == "0A4D55A8D778E5022FAB701977C5D840BBC486D0"
  199. result = ""
  200. for v in Sha1Digest(self):
  201. result.add(toHex(int(v), 2))
  202. proc parseSecureHash*(hash: string): SecureHash =
  203. ## Converts a string ``hash`` to ``SecureHash``.
  204. ##
  205. ## **See also:**
  206. ## * `secureHash proc <#secureHash,string>`_ for generating a ``SecureHash`` from a string
  207. ## * `secureHashFile proc <#secureHashFile,string>`_ for generating a ``SecureHash`` from a file
  208. runnableExamples:
  209. let
  210. hashStr = "0A4D55A8D778E5022FAB701977C5D840BBC486D0"
  211. secureHash = secureHash("Hello World")
  212. assert secureHash == parseSecureHash(hashStr)
  213. for i in 0 ..< Sha1DigestSize:
  214. Sha1Digest(result)[i] = uint8(parseHexInt(hash[i*2] & hash[i*2 + 1]))
  215. proc `==`*(a, b: SecureHash): bool =
  216. ## Checks if two ``SecureHash`` values are identical.
  217. runnableExamples:
  218. let
  219. a = secureHash("Hello World")
  220. b = secureHash("Goodbye World")
  221. c = parseSecureHash("0A4D55A8D778E5022FAB701977C5D840BBC486D0")
  222. assert a != b
  223. assert a == c
  224. # Not a constant-time comparison, but that's acceptable in this context
  225. Sha1Digest(a) == Sha1Digest(b)
  226. when isMainModule:
  227. let hash1 = secureHash("a93tgj0p34jagp9[agjp98ajrhp9aej]")
  228. doAssert hash1 == hash1
  229. doAssert parseSecureHash($hash1) == hash1
  230. template checkVector(s, exp: string) =
  231. doAssert secureHash(s) == parseSecureHash(exp)
  232. checkVector("", "da39a3ee5e6b4b0d3255bfef95601890afd80709")
  233. checkVector("abc", "a9993e364706816aba3e25717850c26c9cd0d89d")
  234. checkVector("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
  235. "84983e441c3bd26ebaae4aa1f95129e5e54670f1")