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}