Source
pub fn Hmac(comptime Hash: type) type {
return struct {
const Self = @This();
pub const mac_length = Hash.digest_length;
pub const key_length_min = 0;
pub const key_length = mac_length; // recommended key length
o_key_pad: [Hash.block_length]u8,
hash: Hash,
// HMAC(k, m) = H(o_key_pad || H(i_key_pad || msg)) where || is concatenation
pub fn create(out: *[mac_length]u8, msg: []const u8, key: []const u8) void {
var ctx = Self.init(key);
ctx.update(msg);
ctx.final(out);
}
pub fn init(key: []const u8) Self {
var ctx: Self = undefined;
var scratch: [Hash.block_length]u8 = undefined;
var i_key_pad: [Hash.block_length]u8 = undefined;
// Normalize key length to block size of hash
if (key.len > Hash.block_length) {
Hash.hash(key, scratch[0..mac_length], .{});
@memset(scratch[mac_length..Hash.block_length], 0);
} else if (key.len < Hash.block_length) {
@memcpy(scratch[0..key.len], key);
@memset(scratch[key.len..Hash.block_length], 0);
} else {
@memcpy(&scratch, key);
}
for (&ctx.o_key_pad, 0..) |*b, i| {
b.* = scratch[i] ^ 0x5c;
}
for (&i_key_pad, 0..) |*b, i| {
b.* = scratch[i] ^ 0x36;
}
ctx.hash = Hash.init(.{});
ctx.hash.update(&i_key_pad);
return ctx;
}
pub fn update(ctx: *Self, msg: []const u8) void {
ctx.hash.update(msg);
}
pub fn final(ctx: *Self, out: *[mac_length]u8) void {
var scratch: [mac_length]u8 = undefined;
ctx.hash.final(&scratch);
var ohash = Hash.init(.{});
ohash.update(&ctx.o_key_pad);
ohash.update(&scratch);
ohash.final(out);
}
};
}