|
| 1 | +# |
| 2 | +# |
| 3 | +# Nim's Runtime Library |
| 4 | +# (c) Copyright 2010 Andreas Rumpf |
| 5 | +# |
| 6 | +# See the file "copying.txt", included in this |
| 7 | +# distribution, for details about the copyright. |
| 8 | +# |
| 9 | + |
| 10 | +# `std/md5` without VM and JavaScript support, to circumvent a bug with |
| 11 | +# openarrays on Nim < 1.4. |
| 12 | + |
| 13 | +when defined(nimHasStyleChecks): |
| 14 | + {.push styleChecks: off.} |
| 15 | + |
| 16 | +type |
| 17 | + MD5State = array[0..3, uint32] |
| 18 | + MD5Block = array[0..15, uint32] |
| 19 | + MD5CBits = array[0..7, uint8] |
| 20 | + MD5Digest* = array[0..15, uint8] |
| 21 | + ## MD5 checksum of a string, obtained with the `toMD5 proc <#toMD5,string>`_. |
| 22 | + MD5Buffer = array[0..63, uint8] |
| 23 | + MD5Context* {.final.} = object |
| 24 | + state: MD5State |
| 25 | + count: array[0..1, uint32] |
| 26 | + buffer: MD5Buffer |
| 27 | + |
| 28 | +const |
| 29 | + padding: array[0..63, uint8] = [ |
| 30 | + 0x80'u8, 0, 0, 0, 0, 0, 0, 0, |
| 31 | + 0, 0, 0, 0, 0, 0, 0, 0, |
| 32 | + 0, 0, 0, 0, 0, 0, 0, 0, |
| 33 | + 0, 0, 0, 0, 0, 0, 0, 0, |
| 34 | + 0, 0, 0, 0, 0, 0, 0, 0, |
| 35 | + 0, 0, 0, 0, 0, 0, 0, 0, |
| 36 | + 0, 0, 0, 0, 0, 0, 0, 0, |
| 37 | + 0, 0, 0, 0, 0, 0, 0, 0 |
| 38 | + ] |
| 39 | + |
| 40 | +proc F(x, y, z: uint32): uint32 {.inline.} = |
| 41 | + result = (x and y) or ((not x) and z) |
| 42 | + |
| 43 | +proc G(x, y, z: uint32): uint32 {.inline.} = |
| 44 | + result = (x and z) or (y and (not z)) |
| 45 | + |
| 46 | +proc H(x, y, z: uint32): uint32 {.inline.} = |
| 47 | + result = x xor y xor z |
| 48 | + |
| 49 | +proc I(x, y, z: uint32): uint32 {.inline.} = |
| 50 | + result = y xor (x or (not z)) |
| 51 | + |
| 52 | +proc rot(x: var uint32, n: uint8) {.inline.} = |
| 53 | + x = (x shl n) or (x shr (32'u32 - n)) |
| 54 | + |
| 55 | +proc FF(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) = |
| 56 | + a = a + F(b, c, d) + x + ac |
| 57 | + rot(a, s) |
| 58 | + a = a + b |
| 59 | + |
| 60 | +proc GG(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) = |
| 61 | + a = a + G(b, c, d) + x + ac |
| 62 | + rot(a, s) |
| 63 | + a = a + b |
| 64 | + |
| 65 | +proc HH(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) = |
| 66 | + a = a + H(b, c, d) + x + ac |
| 67 | + rot(a, s) |
| 68 | + a = a + b |
| 69 | + |
| 70 | +proc II(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) = |
| 71 | + a = a + I(b, c, d) + x + ac |
| 72 | + rot(a, s) |
| 73 | + a = a + b |
| 74 | + |
| 75 | +proc encode(dest: var MD5Block, src: openArray[uint8]) = |
| 76 | + var j = 0 |
| 77 | + for i in 0..high(dest): |
| 78 | + dest[i] = uint32(ord(src[j])) or |
| 79 | + uint32(ord(src[j+1])) shl 8 or |
| 80 | + uint32(ord(src[j+2])) shl 16 or |
| 81 | + uint32(ord(src[j+3])) shl 24 |
| 82 | + inc(j, 4) |
| 83 | + |
| 84 | +proc decode(dest: var openArray[uint8], src: openArray[uint32]) = |
| 85 | + var i = 0 |
| 86 | + for j in 0..high(src): |
| 87 | + dest[i] = uint8(src[j] and 0xff'u32) |
| 88 | + dest[i+1] = uint8(src[j] shr 8 and 0xff'u32) |
| 89 | + dest[i+2] = uint8(src[j] shr 16 and 0xff'u32) |
| 90 | + dest[i+3] = uint8(src[j] shr 24 and 0xff'u32) |
| 91 | + inc(i, 4) |
| 92 | + |
| 93 | +template slice(s: string | cstring, a, b): openArray[uint8] = |
| 94 | + s.toOpenArrayByte(a, b) |
| 95 | + |
| 96 | +template slice(s: openArray[uint8], a, b): openArray[uint8] = |
| 97 | + s.toOpenArray(a, b) |
| 98 | + |
| 99 | +proc transform(buffer: openArray[uint8], state: var MD5State) = |
| 100 | + var |
| 101 | + myBlock: MD5Block |
| 102 | + encode(myBlock, buffer) |
| 103 | + var a = state[0] |
| 104 | + var b = state[1] |
| 105 | + var c = state[2] |
| 106 | + var d = state[3] |
| 107 | + FF(a, b, c, d, myBlock[0], 7'u8, 0xD76AA478'u32) |
| 108 | + FF(d, a, b, c, myBlock[1], 12'u8, 0xE8C7B756'u32) |
| 109 | + FF(c, d, a, b, myBlock[2], 17'u8, 0x242070DB'u32) |
| 110 | + FF(b, c, d, a, myBlock[3], 22'u8, 0xC1BDCEEE'u32) |
| 111 | + FF(a, b, c, d, myBlock[4], 7'u8, 0xF57C0FAF'u32) |
| 112 | + FF(d, a, b, c, myBlock[5], 12'u8, 0x4787C62A'u32) |
| 113 | + FF(c, d, a, b, myBlock[6], 17'u8, 0xA8304613'u32) |
| 114 | + FF(b, c, d, a, myBlock[7], 22'u8, 0xFD469501'u32) |
| 115 | + FF(a, b, c, d, myBlock[8], 7'u8, 0x698098D8'u32) |
| 116 | + FF(d, a, b, c, myBlock[9], 12'u8, 0x8B44F7AF'u32) |
| 117 | + FF(c, d, a, b, myBlock[10], 17'u8, 0xFFFF5BB1'u32) |
| 118 | + FF(b, c, d, a, myBlock[11], 22'u8, 0x895CD7BE'u32) |
| 119 | + FF(a, b, c, d, myBlock[12], 7'u8, 0x6B901122'u32) |
| 120 | + FF(d, a, b, c, myBlock[13], 12'u8, 0xFD987193'u32) |
| 121 | + FF(c, d, a, b, myBlock[14], 17'u8, 0xA679438E'u32) |
| 122 | + FF(b, c, d, a, myBlock[15], 22'u8, 0x49B40821'u32) |
| 123 | + GG(a, b, c, d, myBlock[1], 5'u8, 0xF61E2562'u32) |
| 124 | + GG(d, a, b, c, myBlock[6], 9'u8, 0xC040B340'u32) |
| 125 | + GG(c, d, a, b, myBlock[11], 14'u8, 0x265E5A51'u32) |
| 126 | + GG(b, c, d, a, myBlock[0], 20'u8, 0xE9B6C7AA'u32) |
| 127 | + GG(a, b, c, d, myBlock[5], 5'u8, 0xD62F105D'u32) |
| 128 | + GG(d, a, b, c, myBlock[10], 9'u8, 0x02441453'u32) |
| 129 | + GG(c, d, a, b, myBlock[15], 14'u8, 0xD8A1E681'u32) |
| 130 | + GG(b, c, d, a, myBlock[4], 20'u8, 0xE7D3FBC8'u32) |
| 131 | + GG(a, b, c, d, myBlock[9], 5'u8, 0x21E1CDE6'u32) |
| 132 | + GG(d, a, b, c, myBlock[14], 9'u8, 0xC33707D6'u32) |
| 133 | + GG(c, d, a, b, myBlock[3], 14'u8, 0xF4D50D87'u32) |
| 134 | + GG(b, c, d, a, myBlock[8], 20'u8, 0x455A14ED'u32) |
| 135 | + GG(a, b, c, d, myBlock[13], 5'u8, 0xA9E3E905'u32) |
| 136 | + GG(d, a, b, c, myBlock[2], 9'u8, 0xFCEFA3F8'u32) |
| 137 | + GG(c, d, a, b, myBlock[7], 14'u8, 0x676F02D9'u32) |
| 138 | + GG(b, c, d, a, myBlock[12], 20'u8, 0x8D2A4C8A'u32) |
| 139 | + HH(a, b, c, d, myBlock[5], 4'u8, 0xFFFA3942'u32) |
| 140 | + HH(d, a, b, c, myBlock[8], 11'u8, 0x8771F681'u32) |
| 141 | + HH(c, d, a, b, myBlock[11], 16'u8, 0x6D9D6122'u32) |
| 142 | + HH(b, c, d, a, myBlock[14], 23'u8, 0xFDE5380C'u32) |
| 143 | + HH(a, b, c, d, myBlock[1], 4'u8, 0xA4BEEA44'u32) |
| 144 | + HH(d, a, b, c, myBlock[4], 11'u8, 0x4BDECFA9'u32) |
| 145 | + HH(c, d, a, b, myBlock[7], 16'u8, 0xF6BB4B60'u32) |
| 146 | + HH(b, c, d, a, myBlock[10], 23'u8, 0xBEBFBC70'u32) |
| 147 | + HH(a, b, c, d, myBlock[13], 4'u8, 0x289B7EC6'u32) |
| 148 | + HH(d, a, b, c, myBlock[0], 11'u8, 0xEAA127FA'u32) |
| 149 | + HH(c, d, a, b, myBlock[3], 16'u8, 0xD4EF3085'u32) |
| 150 | + HH(b, c, d, a, myBlock[6], 23'u8, 0x04881D05'u32) |
| 151 | + HH(a, b, c, d, myBlock[9], 4'u8, 0xD9D4D039'u32) |
| 152 | + HH(d, a, b, c, myBlock[12], 11'u8, 0xE6DB99E5'u32) |
| 153 | + HH(c, d, a, b, myBlock[15], 16'u8, 0x1FA27CF8'u32) |
| 154 | + HH(b, c, d, a, myBlock[2], 23'u8, 0xC4AC5665'u32) |
| 155 | + II(a, b, c, d, myBlock[0], 6'u8, 0xF4292244'u32) |
| 156 | + II(d, a, b, c, myBlock[7], 10'u8, 0x432AFF97'u32) |
| 157 | + II(c, d, a, b, myBlock[14], 15'u8, 0xAB9423A7'u32) |
| 158 | + II(b, c, d, a, myBlock[5], 21'u8, 0xFC93A039'u32) |
| 159 | + II(a, b, c, d, myBlock[12], 6'u8, 0x655B59C3'u32) |
| 160 | + II(d, a, b, c, myBlock[3], 10'u8, 0x8F0CCC92'u32) |
| 161 | + II(c, d, a, b, myBlock[10], 15'u8, 0xFFEFF47D'u32) |
| 162 | + II(b, c, d, a, myBlock[1], 21'u8, 0x85845DD1'u32) |
| 163 | + II(a, b, c, d, myBlock[8], 6'u8, 0x6FA87E4F'u32) |
| 164 | + II(d, a, b, c, myBlock[15], 10'u8, 0xFE2CE6E0'u32) |
| 165 | + II(c, d, a, b, myBlock[6], 15'u8, 0xA3014314'u32) |
| 166 | + II(b, c, d, a, myBlock[13], 21'u8, 0x4E0811A1'u32) |
| 167 | + II(a, b, c, d, myBlock[4], 6'u8, 0xF7537E82'u32) |
| 168 | + II(d, a, b, c, myBlock[11], 10'u8, 0xBD3AF235'u32) |
| 169 | + II(c, d, a, b, myBlock[2], 15'u8, 0x2AD7D2BB'u32) |
| 170 | + II(b, c, d, a, myBlock[9], 21'u8, 0xEB86D391'u32) |
| 171 | + state[0] = state[0] + a |
| 172 | + state[1] = state[1] + b |
| 173 | + state[2] = state[2] + c |
| 174 | + state[3] = state[3] + d |
| 175 | + |
| 176 | +proc md5Init*(c: var MD5Context) {.raises: [], tags: [], gcsafe.} |
| 177 | +proc md5Update*(c: var MD5Context, input: openArray[uint8]) {.raises: [], |
| 178 | + tags: [], gcsafe.} |
| 179 | +proc md5Final*(c: var MD5Context, digest: var MD5Digest) {.raises: [], tags: [], gcsafe.} |
| 180 | + |
| 181 | +proc md5Update*(c: var MD5Context, input: cstring, len: int) {.raises: [], |
| 182 | + tags: [], gcsafe.} = |
| 183 | + ## Updates the `MD5Context` with the `input` data of length `len`. |
| 184 | + ## |
| 185 | + ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this |
| 186 | + ## function explicitly. |
| 187 | + md5Update(c, input.slice(0, len - 1)) |
| 188 | + |
| 189 | + |
| 190 | +proc toMD5*(s: string): MD5Digest = |
| 191 | + ## Computes the `MD5Digest` value for a string `s`. |
| 192 | + ## |
| 193 | + ## **See also:** |
| 194 | + ## * `getMD5 proc <#getMD5,string>`_ which returns a string representation |
| 195 | + ## of the `MD5Digest` |
| 196 | + ## * `$ proc <#$,MD5Digest>`_ for converting MD5Digest to string |
| 197 | + runnableExamples: |
| 198 | + assert $toMD5("abc") == "900150983cd24fb0d6963f7d28e17f72" |
| 199 | + |
| 200 | + var c: MD5Context |
| 201 | + md5Init(c) |
| 202 | + md5Update(c, s.slice(0, s.len - 1)) |
| 203 | + md5Final(c, result) |
| 204 | + |
| 205 | +proc `$`*(d: MD5Digest): string = |
| 206 | + ## Converts a `MD5Digest` value into its string representation. |
| 207 | + const digits = "0123456789abcdef" |
| 208 | + result = "" |
| 209 | + for i in 0..15: |
| 210 | + add(result, digits[(d[i].int shr 4) and 0xF]) |
| 211 | + add(result, digits[d[i].int and 0xF]) |
| 212 | + |
| 213 | +proc getMD5*(s: string): string = |
| 214 | + ## Computes an MD5 value of `s` and returns its string representation. |
| 215 | + ## |
| 216 | + ## **See also:** |
| 217 | + ## * `toMD5 proc <#toMD5,string>`_ which returns the `MD5Digest` of a string |
| 218 | + runnableExamples: |
| 219 | + assert getMD5("abc") == "900150983cd24fb0d6963f7d28e17f72" |
| 220 | + |
| 221 | + var |
| 222 | + c: MD5Context |
| 223 | + d: MD5Digest |
| 224 | + md5Init(c) |
| 225 | + md5Update(c, s.slice(0, s.len - 1)) |
| 226 | + md5Final(c, d) |
| 227 | + result = $d |
| 228 | + |
| 229 | +proc `==`*(D1, D2: MD5Digest): bool = |
| 230 | + ## Checks if two `MD5Digest` values are identical. |
| 231 | + for i in 0..15: |
| 232 | + if D1[i] != D2[i]: return false |
| 233 | + return true |
| 234 | + |
| 235 | + |
| 236 | +proc clearBuffer(c: var MD5Context) {.inline.} = |
| 237 | + zeroMem(addr(c.buffer), sizeof(MD5Buffer)) |
| 238 | + |
| 239 | +proc md5Init*(c: var MD5Context) = |
| 240 | + ## Initializes an `MD5Context`. |
| 241 | + ## |
| 242 | + ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this |
| 243 | + ## function explicitly. |
| 244 | + c.state[0] = 0x67452301'u32 |
| 245 | + c.state[1] = 0xEFCDAB89'u32 |
| 246 | + c.state[2] = 0x98BADCFE'u32 |
| 247 | + c.state[3] = 0x10325476'u32 |
| 248 | + c.count[0] = 0'u32 |
| 249 | + c.count[1] = 0'u32 |
| 250 | + clearBuffer(c) |
| 251 | + |
| 252 | +proc writeBuffer(c: var MD5Context, index: int, |
| 253 | + input: openArray[uint8], inputIndex, len: int) {.inline.} = |
| 254 | + copyMem(addr(c.buffer[index]), unsafeAddr(input[inputIndex]), len) |
| 255 | + |
| 256 | +proc md5Update*(c: var MD5Context, input: openArray[uint8]) = |
| 257 | + ## Updates the `MD5Context` with the `input` data. |
| 258 | + ## |
| 259 | + ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this |
| 260 | + ## function explicitly. |
| 261 | + var Index = int((c.count[0] shr 3) and 0x3F) |
| 262 | + c.count[0] = c.count[0] + (uint32(input.len) shl 3) |
| 263 | + if c.count[0] < (uint32(input.len) shl 3): c.count[1] = c.count[1] + 1'u32 |
| 264 | + c.count[1] = c.count[1] + (uint32(input.len) shr 29) |
| 265 | + var PartLen = 64 - Index |
| 266 | + if input.len >= PartLen: |
| 267 | + writeBuffer(c, Index, input, 0, PartLen) |
| 268 | + transform(c.buffer, c.state) |
| 269 | + var i = PartLen |
| 270 | + while i + 63 < input.len: |
| 271 | + transform(input.slice(i, i + 63), c.state) |
| 272 | + inc(i, 64) |
| 273 | + if i < input.len: |
| 274 | + writeBuffer(c, 0, input, i, input.len - i) |
| 275 | + elif input.len > 0: |
| 276 | + writeBuffer(c, Index, input, 0, input.len) |
| 277 | + |
| 278 | +proc md5Final*(c: var MD5Context, digest: var MD5Digest) = |
| 279 | + ## Finishes the `MD5Context` and stores the result in `digest`. |
| 280 | + ## |
| 281 | + ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this |
| 282 | + ## function explicitly. |
| 283 | + var |
| 284 | + Bits: MD5CBits |
| 285 | + PadLen: int |
| 286 | + decode(Bits, c.count) |
| 287 | + var Index = int((c.count[0] shr 3) and 0x3F) |
| 288 | + if Index < 56: PadLen = 56 - Index |
| 289 | + else: PadLen = 120 - Index |
| 290 | + md5Update(c, padding.slice(0, PadLen - 1)) |
| 291 | + md5Update(c, Bits) |
| 292 | + decode(digest, c.state) |
| 293 | + clearBuffer(c) |
| 294 | + |
| 295 | + |
| 296 | +when defined(nimHasStyleChecks): |
| 297 | + {.pop.} #{.push styleChecks: off.} |
0 commit comments