sm3.odin 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. package sm3
  2. /*
  3. Copyright 2021 zhibog
  4. Made available under the BSD-3 license.
  5. List of contributors:
  6. zhibog, dotbmp: Initial implementation.
  7. Implementation of the SM3 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02>
  8. */
  9. import "core:os"
  10. import "core:io"
  11. import "../util"
  12. /*
  13. High level API
  14. */
  15. DIGEST_SIZE :: 32
  16. // hash_string will hash the given input and return the
  17. // computed hash
  18. hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
  19. return hash_bytes(transmute([]byte)(data))
  20. }
  21. // hash_bytes will hash the given input and return the
  22. // computed hash
  23. hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
  24. hash: [DIGEST_SIZE]byte
  25. ctx: Sm3_Context
  26. init(&ctx)
  27. update(&ctx, data)
  28. final(&ctx, hash[:])
  29. return hash
  30. }
  31. // hash_string_to_buffer will hash the given input and assign the
  32. // computed hash to the second parameter.
  33. // It requires that the destination buffer is at least as big as the digest size
  34. hash_string_to_buffer :: proc(data: string, hash: []byte) {
  35. hash_bytes_to_buffer(transmute([]byte)(data), hash)
  36. }
  37. // hash_bytes_to_buffer will hash the given input and write the
  38. // computed hash into the second parameter.
  39. // It requires that the destination buffer is at least as big as the digest size
  40. hash_bytes_to_buffer :: proc(data, hash: []byte) {
  41. assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
  42. ctx: Sm3_Context
  43. init(&ctx)
  44. update(&ctx, data)
  45. final(&ctx, hash)
  46. }
  47. // hash_stream will read the stream in chunks and compute a
  48. // hash from its contents
  49. hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
  50. hash: [DIGEST_SIZE]byte
  51. ctx: Sm3_Context
  52. init(&ctx)
  53. buf := make([]byte, 512)
  54. defer delete(buf)
  55. read := 1
  56. for read > 0 {
  57. read, _ = s->impl_read(buf)
  58. if read > 0 {
  59. update(&ctx, buf[:read])
  60. }
  61. }
  62. final(&ctx, hash[:])
  63. return hash, true
  64. }
  65. // hash_file will read the file provided by the given handle
  66. // and compute a hash
  67. hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
  68. if !load_at_once {
  69. return hash_stream(os.stream_from_handle(hd))
  70. } else {
  71. if buf, ok := os.read_entire_file(hd); ok {
  72. return hash_bytes(buf[:]), ok
  73. }
  74. }
  75. return [DIGEST_SIZE]byte{}, false
  76. }
  77. hash :: proc {
  78. hash_stream,
  79. hash_file,
  80. hash_bytes,
  81. hash_string,
  82. hash_bytes_to_buffer,
  83. hash_string_to_buffer,
  84. }
  85. /*
  86. Low level API
  87. */
  88. init :: proc(ctx: ^Sm3_Context) {
  89. ctx.state[0] = IV[0]
  90. ctx.state[1] = IV[1]
  91. ctx.state[2] = IV[2]
  92. ctx.state[3] = IV[3]
  93. ctx.state[4] = IV[4]
  94. ctx.state[5] = IV[5]
  95. ctx.state[6] = IV[6]
  96. ctx.state[7] = IV[7]
  97. }
  98. update :: proc(ctx: ^Sm3_Context, data: []byte) {
  99. data := data
  100. ctx.length += u64(len(data))
  101. if ctx.bitlength > 0 {
  102. n := copy(ctx.x[ctx.bitlength:], data[:])
  103. ctx.bitlength += u64(n)
  104. if ctx.bitlength == 64 {
  105. block(ctx, ctx.x[:])
  106. ctx.bitlength = 0
  107. }
  108. data = data[n:]
  109. }
  110. if len(data) >= 64 {
  111. n := len(data) &~ (64 - 1)
  112. block(ctx, data[:n])
  113. data = data[n:]
  114. }
  115. if len(data) > 0 {
  116. ctx.bitlength = u64(copy(ctx.x[:], data[:]))
  117. }
  118. }
  119. final :: proc(ctx: ^Sm3_Context, hash: []byte) {
  120. length := ctx.length
  121. pad: [64]byte
  122. pad[0] = 0x80
  123. if length % 64 < 56 {
  124. update(ctx, pad[0: 56 - length % 64])
  125. } else {
  126. update(ctx, pad[0: 64 + 56 - length % 64])
  127. }
  128. length <<= 3
  129. util.PUT_U64_BE(pad[:], length)
  130. update(ctx, pad[0: 8])
  131. assert(ctx.bitlength == 0)
  132. util.PUT_U32_BE(hash[0:], ctx.state[0])
  133. util.PUT_U32_BE(hash[4:], ctx.state[1])
  134. util.PUT_U32_BE(hash[8:], ctx.state[2])
  135. util.PUT_U32_BE(hash[12:], ctx.state[3])
  136. util.PUT_U32_BE(hash[16:], ctx.state[4])
  137. util.PUT_U32_BE(hash[20:], ctx.state[5])
  138. util.PUT_U32_BE(hash[24:], ctx.state[6])
  139. util.PUT_U32_BE(hash[28:], ctx.state[7])
  140. }
  141. /*
  142. SM3 implementation
  143. */
  144. Sm3_Context :: struct {
  145. state: [8]u32,
  146. x: [64]byte,
  147. bitlength: u64,
  148. length: u64,
  149. }
  150. IV := [8]u32 {
  151. 0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600,
  152. 0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e,
  153. }
  154. block :: proc "contextless" (ctx: ^Sm3_Context, buf: []byte) {
  155. buf := buf
  156. w: [68]u32
  157. wp: [64]u32
  158. state0, state1, state2, state3 := ctx.state[0], ctx.state[1], ctx.state[2], ctx.state[3]
  159. state4, state5, state6, state7 := ctx.state[4], ctx.state[5], ctx.state[6], ctx.state[7]
  160. for len(buf) >= 64 {
  161. for i := 0; i < 16; i += 1 {
  162. j := i * 4
  163. w[i] = u32(buf[j]) << 24 | u32(buf[j + 1]) << 16 | u32(buf[j + 2]) << 8 | u32(buf[j + 3])
  164. }
  165. for i := 16; i < 68; i += 1 {
  166. p1v := w[i - 16] ~ w[i - 9] ~ util.ROTL32(w[i - 3], 15)
  167. // @note(zh): inlined P1
  168. w[i] = p1v ~ util.ROTL32(p1v, 15) ~ util.ROTL32(p1v, 23) ~ util.ROTL32(w[i - 13], 7) ~ w[i - 6]
  169. }
  170. for i := 0; i < 64; i += 1 {
  171. wp[i] = w[i] ~ w[i + 4]
  172. }
  173. a, b, c, d := state0, state1, state2, state3
  174. e, f, g, h := state4, state5, state6, state7
  175. for i := 0; i < 16; i += 1 {
  176. v1 := util.ROTL32(u32(a), 12)
  177. ss1 := util.ROTL32(v1 + u32(e) + util.ROTL32(0x79cc4519, i), 7)
  178. ss2 := ss1 ~ v1
  179. // @note(zh): inlined FF1
  180. tt1 := u32(a ~ b ~ c) + u32(d) + ss2 + wp[i]
  181. // @note(zh): inlined GG1
  182. tt2 := u32(e ~ f ~ g) + u32(h) + ss1 + w[i]
  183. a, b, c, d = tt1, a, util.ROTL32(u32(b), 9), c
  184. // @note(zh): inlined P0
  185. e, f, g, h = (tt2 ~ util.ROTL32(tt2, 9) ~ util.ROTL32(tt2, 17)), e, util.ROTL32(u32(f), 19), g
  186. }
  187. for i := 16; i < 64; i += 1 {
  188. v := util.ROTL32(u32(a), 12)
  189. ss1 := util.ROTL32(v + u32(e) + util.ROTL32(0x7a879d8a, i % 32), 7)
  190. ss2 := ss1 ~ v
  191. // @note(zh): inlined FF2
  192. tt1 := u32(((a & b) | (a & c) | (b & c)) + d) + ss2 + wp[i]
  193. // @note(zh): inlined GG2
  194. tt2 := u32(((e & f) | ((~e) & g)) + h) + ss1 + w[i]
  195. a, b, c, d = tt1, a, util.ROTL32(u32(b), 9), c
  196. // @note(zh): inlined P0
  197. e, f, g, h = (tt2 ~ util.ROTL32(tt2, 9) ~ util.ROTL32(tt2, 17)), e, util.ROTL32(u32(f), 19), g
  198. }
  199. state0 ~= a
  200. state1 ~= b
  201. state2 ~= c
  202. state3 ~= d
  203. state4 ~= e
  204. state5 ~= f
  205. state6 ~= g
  206. state7 ~= h
  207. buf = buf[64:]
  208. }
  209. ctx.state[0], ctx.state[1], ctx.state[2], ctx.state[3] = state0, state1, state2, state3
  210. ctx.state[4], ctx.state[5], ctx.state[6], ctx.state[7] = state4, state5, state6, state7
  211. }