master
  1const std = @import("../std.zig");
  2const crypto = std.crypto;
  3const debug = std.debug;
  4const mem = std.mem;
  5
  6pub const HmacMd5 = Hmac(crypto.hash.Md5);
  7pub const HmacSha1 = Hmac(crypto.hash.Sha1);
  8
  9pub const sha2 = struct {
 10    pub const HmacSha224 = Hmac(crypto.hash.sha2.Sha224);
 11    pub const HmacSha256 = Hmac(crypto.hash.sha2.Sha256);
 12    pub const HmacSha384 = Hmac(crypto.hash.sha2.Sha384);
 13    pub const HmacSha512 = Hmac(crypto.hash.sha2.Sha512);
 14};
 15
 16pub fn Hmac(comptime Hash: type) type {
 17    return struct {
 18        const Self = @This();
 19        pub const mac_length = Hash.digest_length;
 20        pub const key_length_min = 0;
 21        pub const key_length = mac_length; // recommended key length
 22
 23        o_key_pad: [Hash.block_length]u8,
 24        hash: Hash,
 25
 26        // HMAC(k, m) = H(o_key_pad || H(i_key_pad || msg)) where || is concatenation
 27        pub fn create(out: *[mac_length]u8, msg: []const u8, key: []const u8) void {
 28            var ctx = Self.init(key);
 29            ctx.update(msg);
 30            ctx.final(out);
 31        }
 32
 33        pub fn init(key: []const u8) Self {
 34            var ctx: Self = undefined;
 35            var scratch: [Hash.block_length]u8 = undefined;
 36            var i_key_pad: [Hash.block_length]u8 = undefined;
 37
 38            // Normalize key length to block size of hash
 39            if (key.len > Hash.block_length) {
 40                Hash.hash(key, scratch[0..mac_length], .{});
 41                @memset(scratch[mac_length..Hash.block_length], 0);
 42            } else if (key.len < Hash.block_length) {
 43                @memcpy(scratch[0..key.len], key);
 44                @memset(scratch[key.len..Hash.block_length], 0);
 45            } else {
 46                @memcpy(&scratch, key);
 47            }
 48
 49            for (&ctx.o_key_pad, 0..) |*b, i| {
 50                b.* = scratch[i] ^ 0x5c;
 51            }
 52
 53            for (&i_key_pad, 0..) |*b, i| {
 54                b.* = scratch[i] ^ 0x36;
 55            }
 56
 57            ctx.hash = Hash.init(.{});
 58            ctx.hash.update(&i_key_pad);
 59            return ctx;
 60        }
 61
 62        pub fn update(ctx: *Self, msg: []const u8) void {
 63            ctx.hash.update(msg);
 64        }
 65
 66        pub fn final(ctx: *Self, out: *[mac_length]u8) void {
 67            var scratch: [mac_length]u8 = undefined;
 68            ctx.hash.final(&scratch);
 69            var ohash = Hash.init(.{});
 70            ohash.update(&ctx.o_key_pad);
 71            ohash.update(&scratch);
 72            ohash.final(out);
 73        }
 74    };
 75}
 76
 77const htest = @import("test.zig");
 78
 79test "md5" {
 80    var out: [HmacMd5.mac_length]u8 = undefined;
 81    HmacMd5.create(out[0..], "", "");
 82    try htest.assertEqual("74e6f7298a9c2d168935f58c001bad88", out[0..]);
 83
 84    HmacMd5.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
 85    try htest.assertEqual("80070713463e7749b90c2dc24911e275", out[0..]);
 86}
 87
 88test "sha1" {
 89    var out: [HmacSha1.mac_length]u8 = undefined;
 90    HmacSha1.create(out[0..], "", "");
 91    try htest.assertEqual("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d", out[0..]);
 92
 93    HmacSha1.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
 94    try htest.assertEqual("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9", out[0..]);
 95}
 96
 97test "sha256" {
 98    var out: [sha2.HmacSha256.mac_length]u8 = undefined;
 99    sha2.HmacSha256.create(out[0..], "", "");
100    try htest.assertEqual("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad", out[0..]);
101
102    sha2.HmacSha256.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
103    try htest.assertEqual("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", out[0..]);
104}