Current section
Files
Jump to
Current section
Files
src/munch/internal/md5.gleam
import gleam/bit_array
import munch/internal/bitwise.{band, bnot, bor, rol, xor}
import munch/internal/hash.{type Hash} as hash_state
const a0 = 0x67452301
const b0 = 0xefcdab89
const c0 = 0x98badcfe
const d0 = 0x10325476
const mask = 0xffffffff
fn f(a, b, c, d, k, m, s) {
let x = xor(d, band(b, xor(c, d)))
band(b + rol(band(a + x + k + m, mask), s), mask)
}
fn g(a, b, c, d, k, m, s) {
let x = xor(c, band(d, xor(b, c)))
band(b + rol(band(a + x + k + m, mask), s), mask)
}
fn h(a, b, c, d, k, m, s) {
let x = xor(xor(b, c), d)
band(b + rol(band(a + x + k + m, mask), s), mask)
}
fn i(a, b, c, d, k, m, s) {
let x = xor(c, bor(b, bnot(d)))
band(b + rol(band(a + x + k + m, mask), s), mask)
}
pub opaque type State {
State(a: Int, b: Int, c: Int, d: Int, buffer: BitArray, length: Int)
}
pub fn new() -> Hash {
let state = State(a: a0, b: b0, c: c0, d: d0, buffer: <<>>, length: 0)
hash_state.from(state, update, finish)
}
fn update(state: State, data: BitArray) -> State {
let State(a:, b:, c:, d:, buffer:, length:) = state
let length = length + bit_array.bit_size(data)
let #(a, b, c, d, buffer) = blocks(a, b, c, d, <<buffer:bits, data:bits>>)
State(a:, b:, c:, d:, buffer:, length:)
}
fn finish(state: State) -> BitArray {
let State(a:, b:, c:, d:, buffer:, length:) = state
// Pad the message: a single 1 bit, then 0 bits until the length is congruent
// to 448 modulo 512, then the original message length as a 64-bit little
// endian integer. Anything still buffered is a partial (< 512 bit) block.
let used = { bit_array.bit_size(buffer) + 1 } % 512
let zeros = { 448 + 512 - used } % 512
let padded = <<buffer:bits, 1:1, 0:size(zeros), length:64-little>>
let #(a, b, c, d, _rest) = blocks(a, b, c, d, padded)
<<a:32-little, b:32-little, c:32-little, d:32-little>>
}
fn blocks(
oa: Int,
ob: Int,
oc: Int,
od: Int,
data: BitArray,
) -> #(Int, Int, Int, Int, BitArray) {
case data {
<<
m0:32-little,
m1:32-little,
m2:32-little,
m3:32-little,
m4:32-little,
m5:32-little,
m6:32-little,
m7:32-little,
m8:32-little,
m9:32-little,
m10:32-little,
m11:32-little,
m12:32-little,
m13:32-little,
m14:32-little,
m15:32-little,
rest:bits,
>> -> {
let a = oa
let b = ob
let c = oc
let d = od
let a = f(a, b, c, d, 0xD76AA478, m0, 7)
let d = f(d, a, b, c, 0xE8C7B756, m1, 12)
let c = f(c, d, a, b, 0x242070DB, m2, 17)
let b = f(b, c, d, a, 0xC1BDCEEE, m3, 22)
let a = f(a, b, c, d, 0xF57C0FAF, m4, 7)
let d = f(d, a, b, c, 0x4787C62A, m5, 12)
let c = f(c, d, a, b, 0xA8304613, m6, 17)
let b = f(b, c, d, a, 0xFD469501, m7, 22)
let a = f(a, b, c, d, 0x698098D8, m8, 7)
let d = f(d, a, b, c, 0x8B44F7AF, m9, 12)
let c = f(c, d, a, b, 0xFFFF5BB1, m10, 17)
let b = f(b, c, d, a, 0x895CD7BE, m11, 22)
let a = f(a, b, c, d, 0x6B901122, m12, 7)
let d = f(d, a, b, c, 0xFD987193, m13, 12)
let c = f(c, d, a, b, 0xA679438E, m14, 17)
let b = f(b, c, d, a, 0x49B40821, m15, 22)
let a = g(a, b, c, d, 0xF61E2562, m1, 5)
let d = g(d, a, b, c, 0xC040B340, m6, 9)
let c = g(c, d, a, b, 0x265E5A51, m11, 14)
let b = g(b, c, d, a, 0xE9B6C7AA, m0, 20)
let a = g(a, b, c, d, 0xD62F105D, m5, 5)
let d = g(d, a, b, c, 0x02441453, m10, 9)
let c = g(c, d, a, b, 0xD8A1E681, m15, 14)
let b = g(b, c, d, a, 0xE7D3FBC8, m4, 20)
let a = g(a, b, c, d, 0x21E1CDE6, m9, 5)
let d = g(d, a, b, c, 0xC33707D6, m14, 9)
let c = g(c, d, a, b, 0xF4D50D87, m3, 14)
let b = g(b, c, d, a, 0x455A14ED, m8, 20)
let a = g(a, b, c, d, 0xA9E3E905, m13, 5)
let d = g(d, a, b, c, 0xFCEFA3F8, m2, 9)
let c = g(c, d, a, b, 0x676F02D9, m7, 14)
let b = g(b, c, d, a, 0x8D2A4C8A, m12, 20)
let a = h(a, b, c, d, 0xFFFA3942, m5, 4)
let d = h(d, a, b, c, 0x8771F681, m8, 11)
let c = h(c, d, a, b, 0x6D9D6122, m11, 16)
let b = h(b, c, d, a, 0xFDE5380C, m14, 23)
let a = h(a, b, c, d, 0xA4BEEA44, m1, 4)
let d = h(d, a, b, c, 0x4BDECFA9, m4, 11)
let c = h(c, d, a, b, 0xF6BB4B60, m7, 16)
let b = h(b, c, d, a, 0xBEBFBC70, m10, 23)
let a = h(a, b, c, d, 0x289B7EC6, m13, 4)
let d = h(d, a, b, c, 0xEAA127FA, m0, 11)
let c = h(c, d, a, b, 0xD4EF3085, m3, 16)
let b = h(b, c, d, a, 0x04881D05, m6, 23)
let a = h(a, b, c, d, 0xD9D4D039, m9, 4)
let d = h(d, a, b, c, 0xE6DB99E5, m12, 11)
let c = h(c, d, a, b, 0x1FA27CF8, m15, 16)
let b = h(b, c, d, a, 0xC4AC5665, m2, 23)
let a = i(a, b, c, d, 0xF4292244, m0, 6)
let d = i(d, a, b, c, 0x432AFF97, m7, 10)
let c = i(c, d, a, b, 0xAB9423A7, m14, 15)
let b = i(b, c, d, a, 0xFC93A039, m5, 21)
let a = i(a, b, c, d, 0x655B59C3, m12, 6)
let d = i(d, a, b, c, 0x8F0CCC92, m3, 10)
let c = i(c, d, a, b, 0xFFEFF47D, m10, 15)
let b = i(b, c, d, a, 0x85845DD1, m1, 21)
let a = i(a, b, c, d, 0x6FA87E4F, m8, 6)
let d = i(d, a, b, c, 0xFE2CE6E0, m15, 10)
let c = i(c, d, a, b, 0xA3014314, m6, 15)
let b = i(b, c, d, a, 0x4E0811A1, m13, 21)
let a = i(a, b, c, d, 0xF7537E82, m4, 6)
let d = i(d, a, b, c, 0xBD3AF235, m11, 10)
let c = i(c, d, a, b, 0x2AD7D2BB, m2, 15)
let b = i(b, c, d, a, 0xEB86D391, m9, 21)
// Feed-forward: add this block's output back into its input state.
blocks(
band(oa + a, mask),
band(ob + b, mask),
band(oc + c, mask),
band(od + d, mask),
rest,
)
}
rest -> #(oa, ob, oc, od, rest)
}
}