Module:Hash

-- Utils: structs

function pack_le_u32(u32) b0 = u32 % 256; u32 = (u32 - b0) / 256 b1 = u32 % 256; u32 = (u32 - b1) / 256 b2 = u32 % 256; u32 = (u32 - b2) / 256 b3 = u32 % 256 return string.char(b0, b1, b2, b3) end

function pack_le_u64(u64) b0 = u64 % 256; u64 = (u64 - b0) / 256 b1 = u64 % 256; u64 = (u64 - b1) / 256 b2 = u64 % 256; u64 = (u64 - b2) / 256 b3 = u64 % 256; u64 = (u64 - b3) / 256 b4 = u64 % 256; u64 = (u64 - b4) / 256 b5 = u64 % 256; u64 = (u64 - b5) / 256 b6 = u64 % 256; u64 = (u64 - b6) / 256 b7 = u64 % 256 return string.char(b0, b1, b2, b3, b4, b5, b6, b7) end

-- Utils: representation

function bytes_to_hex(bytes) repr = '' for i = 1, #bytes do       repr = repr .. string.format('%02x', string.byte(bytes, i)) end return repr end

-- Utils: MW helpers

function make_message(frame) message = '' for k, v in pairs(frame.args) do       assert(type(v) == 'string') if    type(k) == 'number' then message = message .. v       elseif type(k) == 'string' then message = k .. '=' .. v       else                            assert(false) end end return message end

-- Utils: hash functions

function md5_digest(message) -- Pad (per sections 3.1 and 3.2 of RFC 1321) message = message .. '\128' .. string.rep('\0', ((56 - ((#message + 1) % 64)) + 64) % 64) .. pack_le_u64(#message * 8) assert(#message % 64 == 0)

-- Initialize MD buffer (per section 3.3 of RFC 1321) a = 0x67452301 b = 0xefcdab89 c = 0x98badcfe d = 0x10325476

-- TODO

-- Concatenate (per section 3.5 of RFC 1321) return pack_le_u32(a) .. pack_le_u32(b) .. pack_le_u32(c) .. pack_le_u32(d) end

-- Exports

local p = {}

function p.md5_hexdigest(frame) return bytes_to_hex(md5_digest(make_message(frame))) end

return p