diff options
Diffstat (limited to 'lib/std/crypto/hmac.zig')
| -rw-r--r-- | lib/std/crypto/hmac.zig | 99 |
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..]); +} |
