md2.odin 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. package md2
  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 MD2 hashing algorithm, as defined in RFC 1319 <https://datatracker.ietf.org/doc/html/rfc1319>
  8. */
  9. import "core:os"
  10. import "core:io"
  11. /*
  12. High level API
  13. */
  14. DIGEST_SIZE :: 16
  15. // hash_string will hash the given input and return the
  16. // computed hash
  17. hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
  18. return hash_bytes(transmute([]byte)(data))
  19. }
  20. // hash_bytes will hash the given input and return the
  21. // computed hash
  22. hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
  23. hash: [DIGEST_SIZE]byte
  24. ctx: Md2_Context
  25. // init(&ctx) No-op
  26. update(&ctx, data)
  27. final(&ctx, hash[:])
  28. return hash
  29. }
  30. // hash_string_to_buffer will hash the given input and assign the
  31. // computed hash to the second parameter.
  32. // It requires that the destination buffer is at least as big as the digest size
  33. hash_string_to_buffer :: proc(data: string, hash: []byte) {
  34. hash_bytes_to_buffer(transmute([]byte)(data), hash)
  35. }
  36. // hash_bytes_to_buffer will hash the given input and write the
  37. // computed hash into the second parameter.
  38. // It requires that the destination buffer is at least as big as the digest size
  39. hash_bytes_to_buffer :: proc(data, hash: []byte) {
  40. assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
  41. ctx: Md2_Context
  42. // init(&ctx) No-op
  43. update(&ctx, data)
  44. final(&ctx, hash)
  45. }
  46. // hash_stream will read the stream in chunks and compute a
  47. // hash from its contents
  48. hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
  49. hash: [DIGEST_SIZE]byte
  50. ctx: Md2_Context
  51. // init(&ctx) No-op
  52. buf := make([]byte, 512)
  53. defer delete(buf)
  54. read := 1
  55. for read > 0 {
  56. read, _ = s->impl_read(buf)
  57. if read > 0 {
  58. update(&ctx, buf[:read])
  59. }
  60. }
  61. final(&ctx, hash[:])
  62. return hash, true
  63. }
  64. // hash_file will read the file provided by the given handle
  65. // and compute a hash
  66. hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
  67. if !load_at_once {
  68. return hash_stream(os.stream_from_handle(hd))
  69. } else {
  70. if buf, ok := os.read_entire_file(hd); ok {
  71. return hash_bytes(buf[:]), ok
  72. }
  73. }
  74. return [DIGEST_SIZE]byte{}, false
  75. }
  76. hash :: proc {
  77. hash_stream,
  78. hash_file,
  79. hash_bytes,
  80. hash_string,
  81. hash_bytes_to_buffer,
  82. hash_string_to_buffer,
  83. }
  84. /*
  85. Low level API
  86. */
  87. @(warning="Init is a no-op for MD2")
  88. init :: proc(ctx: ^Md2_Context) {
  89. // No action needed here
  90. }
  91. update :: proc(ctx: ^Md2_Context, data: []byte) {
  92. for i := 0; i < len(data); i += 1 {
  93. ctx.data[ctx.datalen] = data[i]
  94. ctx.datalen += 1
  95. if (ctx.datalen == DIGEST_SIZE) {
  96. transform(ctx, ctx.data[:])
  97. ctx.datalen = 0
  98. }
  99. }
  100. }
  101. final :: proc(ctx: ^Md2_Context, hash: []byte) {
  102. to_pad := byte(DIGEST_SIZE - ctx.datalen)
  103. for ctx.datalen < DIGEST_SIZE {
  104. ctx.data[ctx.datalen] = to_pad
  105. ctx.datalen += 1
  106. }
  107. transform(ctx, ctx.data[:])
  108. transform(ctx, ctx.checksum[:])
  109. for i := 0; i < DIGEST_SIZE; i += 1 {
  110. hash[i] = ctx.state[i]
  111. }
  112. }
  113. /*
  114. MD2 implementation
  115. */
  116. Md2_Context :: struct {
  117. data: [DIGEST_SIZE]byte,
  118. state: [DIGEST_SIZE * 3]byte,
  119. checksum: [DIGEST_SIZE]byte,
  120. datalen: int,
  121. }
  122. PI_TABLE := [?]byte {
  123. 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
  124. 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, 76,
  125. 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, 138,
  126. 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, 245, 142,
  127. 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, 148, 194, 16,
  128. 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, 39, 53, 62,
  129. 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, 181, 209, 215,
  130. 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, 150, 164, 125, 182,
  131. 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, 112, 89, 100, 113, 135,
  132. 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, 96, 37, 173, 174, 176,
  133. 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, 85, 71, 163, 35, 221,
  134. 81, 175, 58, 195, 92, 249, 206, 186, 197, 234, 38, 44, 83, 13, 110,
  135. 133, 40, 132, 9, 211, 223, 205, 244, 65, 129, 77, 82, 106, 220, 55,
  136. 200, 108, 193, 171, 250, 36, 225, 123, 8, 12, 189, 177, 74, 120, 136,
  137. 149, 139, 227, 99, 232, 109, 233, 203, 213, 254, 59, 0, 29, 57, 242,
  138. 239, 183, 14, 102, 88, 208, 228, 166, 119, 114, 248, 235, 117, 75, 10,
  139. 49, 68, 80, 180, 143, 237, 31, 26, 219, 153, 141, 51, 159, 17, 131,
  140. 20,
  141. }
  142. transform :: proc(ctx: ^Md2_Context, data: []byte) {
  143. j,k,t: byte
  144. for j = 0; j < DIGEST_SIZE; j += 1 {
  145. ctx.state[j + DIGEST_SIZE] = data[j]
  146. ctx.state[j + DIGEST_SIZE * 2] = (ctx.state[j + DIGEST_SIZE] ~ ctx.state[j])
  147. }
  148. t = 0
  149. for j = 0; j < DIGEST_SIZE + 2; j += 1 {
  150. for k = 0; k < DIGEST_SIZE * 3; k += 1 {
  151. ctx.state[k] ~= PI_TABLE[t]
  152. t = ctx.state[k]
  153. }
  154. t = (t + j) & 0xff
  155. }
  156. t = ctx.checksum[DIGEST_SIZE - 1]
  157. for j = 0; j < DIGEST_SIZE; j += 1 {
  158. ctx.checksum[j] ~= PI_TABLE[data[j] ~ t]
  159. t = ctx.checksum[j]
  160. }
  161. }