aboutsummaryrefslogtreecommitdiff
path: root/lib/std/crypto/hmac.zig
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std/crypto/hmac.zig')
-rw-r--r--lib/std/crypto/hmac.zig99
1 files changed, 99 insertions, 0 deletions
diff --git a/lib/std/crypto/hmac.zig b/lib/std/crypto/hmac.zig
new file mode 100644
index 0000000000..69c1b86386
--- /dev/null
+++ b/lib/std/crypto/hmac.zig
@@ -0,0 +1,99 @@
+const std = @import("../std.zig");
+const crypto = std.crypto;
+const debug = std.debug;
+const mem = std.mem;
+
+pub const HmacMd5 = Hmac(crypto.Md5);
+pub const HmacSha1 = Hmac(crypto.Sha1);
+pub const HmacSha256 = Hmac(crypto.Sha256);
+pub const HmacBlake2s256 = Hmac(crypto.Blake2s256);
+
+pub fn Hmac(comptime Hash: type) type {
+ return struct {
+ const Self = @This();
+ pub const mac_length = Hash.digest_length;
+ pub const minimum_key_length = 0;
+
+ o_key_pad: [Hash.block_length]u8,
+ i_key_pad: [Hash.block_length]u8,
+ scratch: [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: []u8, msg: []const u8, key: []const u8) void {
+ var ctx = Self.init(key);
+ ctx.update(msg);
+ ctx.final(out[0..]);
+ }
+
+ pub fn init(key: []const u8) Self {
+ var ctx: Self = undefined;
+
+ // Normalize key length to block size of hash
+ if (key.len > Hash.block_length) {
+ Hash.hash(key, ctx.scratch[0..mac_length]);
+ mem.set(u8, ctx.scratch[mac_length..Hash.block_length], 0);
+ } else if (key.len < Hash.block_length) {
+ mem.copy(u8, ctx.scratch[0..key.len], key);
+ mem.set(u8, ctx.scratch[key.len..Hash.block_length], 0);
+ } else {
+ mem.copy(u8, ctx.scratch[0..], key);
+ }
+
+ for (ctx.o_key_pad) |*b, i| {
+ b.* = ctx.scratch[i] ^ 0x5c;
+ }
+
+ for (ctx.i_key_pad) |*b, i| {
+ b.* = ctx.scratch[i] ^ 0x36;
+ }
+
+ ctx.hash = Hash.init();
+ ctx.hash.update(ctx.i_key_pad[0..]);
+ return ctx;
+ }
+
+ pub fn update(ctx: *Self, msg: []const u8) void {
+ ctx.hash.update(msg);
+ }
+
+ pub fn final(ctx: *Self, out: []u8) void {
+ debug.assert(Hash.block_length >= out.len and out.len >= mac_length);
+
+ ctx.hash.final(ctx.scratch[0..mac_length]);
+ ctx.hash.reset();
+ ctx.hash.update(ctx.o_key_pad[0..]);
+ ctx.hash.update(ctx.scratch[0..mac_length]);
+ ctx.hash.final(out[0..mac_length]);
+ }
+ };
+}
+
+const htest = @import("test.zig");
+
+test "hmac md5" {
+ var out: [HmacMd5.mac_length]u8 = undefined;
+ HmacMd5.create(out[0..], "", "");
+ htest.assertEqual("74e6f7298a9c2d168935f58c001bad88", out[0..]);
+
+ HmacMd5.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
+ htest.assertEqual("80070713463e7749b90c2dc24911e275", out[0..]);
+}
+
+test "hmac sha1" {
+ var out: [HmacSha1.mac_length]u8 = undefined;
+ HmacSha1.create(out[0..], "", "");
+ htest.assertEqual("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d", out[0..]);
+
+ HmacSha1.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
+ htest.assertEqual("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9", out[0..]);
+}
+
+test "hmac sha256" {
+ var out: [HmacSha256.mac_length]u8 = undefined;
+ HmacSha256.create(out[0..], "", "");
+ htest.assertEqual("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad", out[0..]);
+
+ HmacSha256.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
+ htest.assertEqual("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", out[0..]);
+}