authorgravatar for 48591413+chrboesch@users.noreply.github.comChris Boesch <48591413+chrboesch@users.noreply.github.com> 2023-02-16 21:18:20+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-02-16 21:18:20+01:00
log438b71155a6e86263c61a664a1af596fe9b210bd
tree296701fb8db537f6afb296de4d937349271bad5c
parent7199d7c77715fe06606c5c89595e6852b3fa8c20
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

crypto: add AES-CMAC (RFC 4493) (#14545)

* crypto: add AES-CMAC Co-authored-by: Frank Denis <124872+jedisct1@users.noreply.github.com>

3 files changed, 159 insertions(+), 0 deletions(-)

lib/std/crypto.zig+2
...@@ -41,6 +41,7 @@ pub const auth = struct {...@@ -41,6 +41,7 @@ pub const auth = struct {
41 pub const Aegis128LMac = @import("crypto/aegis.zig").Aegis128LMac;41 pub const Aegis128LMac = @import("crypto/aegis.zig").Aegis128LMac;
42 pub const Aegis256Mac = @import("crypto/aegis.zig").Aegis256Mac;42 pub const Aegis256Mac = @import("crypto/aegis.zig").Aegis256Mac;
43 };43 };
44 pub const cmac = @import("crypto/cmac.zig");
44};45};
4546
46/// Core functions, that should rarely be used directly by applications.47/// Core functions, that should rarely be used directly by applications.
...@@ -203,6 +204,7 @@ test {...@@ -203,6 +204,7 @@ test {
203 _ = aead.salsa_poly.XSalsa20Poly1305;204 _ = aead.salsa_poly.XSalsa20Poly1305;
204205
205 _ = auth.hmac;206 _ = auth.hmac;
207 _ = auth.cmac;
206 _ = auth.siphash;208 _ = auth.siphash;
207209
208 _ = core.aes;210 _ = core.aes;
lib/std/crypto/benchmark.zig+1
...@@ -66,6 +66,7 @@ const macs = [_]Crypto{...@@ -66,6 +66,7 @@ const macs = [_]Crypto{
66 Crypto{ .ty = crypto.auth.siphash.SipHash128(1, 3), .name = "siphash128-1-3" },66 Crypto{ .ty = crypto.auth.siphash.SipHash128(1, 3), .name = "siphash128-1-3" },
67 Crypto{ .ty = crypto.auth.aegis.Aegis128LMac, .name = "aegis-128l mac" },67 Crypto{ .ty = crypto.auth.aegis.Aegis128LMac, .name = "aegis-128l mac" },
68 Crypto{ .ty = crypto.auth.aegis.Aegis256Mac, .name = "aegis-256 mac" },68 Crypto{ .ty = crypto.auth.aegis.Aegis256Mac, .name = "aegis-256 mac" },
69 Crypto{ .ty = crypto.auth.cmac.CmacAes128, .name = "aes-cmac" },
69};70};
7071
71pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {72pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {
lib/std/crypto/cmac.zig created+156
...@@ -0,0 +1,156 @@
1const std = @import("std");
2const crypto = std.crypto;
3const mem = std.mem;
4
5/// CMAC with AES-128 - RFC 4493 https://www.rfc-editor.org/rfc/rfc4493
6pub const CmacAes128 = Cmac(crypto.core.aes.Aes128);
7
8/// NIST Special Publication 800-38B - The CMAC Mode for Authentication
9/// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38b.pdf
10pub fn Cmac(comptime BlockCipher: type) type {
11 const BlockCipherCtx = @typeInfo(@TypeOf(BlockCipher.initEnc)).Fn.return_type.?;
12 const Block = [BlockCipher.block.block_length]u8;
13
14 return struct {
15 const Self = @This();
16 pub const key_length = BlockCipher.key_bits / 8;
17 pub const block_length = BlockCipher.block.block_length;
18 pub const mac_length = block_length;
19
20 cipher_ctx: BlockCipherCtx,
21 k1: Block,
22 k2: Block,
23 buf: Block = [_]u8{0} ** block_length,
24 pos: usize = 0,
25
26 pub fn create(out: *[mac_length]u8, msg: []const u8, key: *const [key_length]u8) void {
27 var ctx = Self.init(key);
28 ctx.update(msg);
29 ctx.final(out);
30 }
31
32 pub fn init(key: *const [key_length]u8) Self {
33 const cipher_ctx = BlockCipher.initEnc(key.*);
34 const zeros = [_]u8{0} ** block_length;
35 var k1: Block = undefined;
36 cipher_ctx.encrypt(&k1, &zeros);
37 k1 = double(k1);
38 return Self{
39 .cipher_ctx = cipher_ctx,
40 .k1 = k1,
41 .k2 = double(k1),
42 };
43 }
44
45 pub fn update(self: *Self, msg: []const u8) void {
46 const left = block_length - self.pos;
47 var m = msg;
48 if (m.len > left) {
49 for (self.buf[self.pos..]) |*b, i| b.* ^= m[i];
50 m = m[left..];
51 self.cipher_ctx.encrypt(&self.buf, &self.buf);
52 self.pos = 0;
53 }
54 while (m.len > block_length) {
55 for (self.buf[0..block_length]) |*b, i| b.* ^= m[i];
56 m = m[block_length..];
57 self.cipher_ctx.encrypt(&self.buf, &self.buf);
58 self.pos = 0;
59 }
60 if (m.len > 0) {
61 for (self.buf[self.pos..][0..m.len]) |*b, i| b.* ^= m[i];
62 self.pos += m.len;
63 }
64 }
65
66 pub fn final(self: *Self, out: *[mac_length]u8) void {
67 var mac = self.k1;
68 if (self.pos < block_length) {
69 mac = self.k2;
70 mac[self.pos] ^= 0x80;
71 }
72 for (mac) |*b, i| b.* ^= self.buf[i];
73 self.cipher_ctx.encrypt(out, &mac);
74 }
75
76 fn double(l: Block) Block {
77 const Int = std.meta.Int(.unsigned, block_length * 8);
78 const l_ = mem.readIntBig(Int, &l);
79 const l_2 = switch (block_length) {
80 8 => (l_ << 1) ^ (0x1b & -%(l_ >> 63)), // mod x^64 + x^4 + x^3 + x + 1
81 16 => (l_ << 1) ^ (0x87 & -%(l_ >> 127)), // mod x^128 + x^7 + x^2 + x + 1
82 32 => (l_ << 1) ^ (0x0425 & -%(l_ >> 255)), // mod x^256 + x^10 + x^5 + x^2 + 1
83 64 => (l_ << 1) ^ (0x0125 & -%(l_ >> 511)), // mod x^512 + x^8 + x^5 + x^2 + 1
84 else => @compileError("unsupported block length"),
85 };
86 var l2: Block = undefined;
87 mem.writeIntBig(Int, &l2, l_2);
88 return l2;
89 }
90 };
91}
92
93const testing = std.testing;
94
95test "CmacAes128 - Example 1: len = 0" {
96 const key = [_]u8{
97 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
98 };
99 var msg: [0]u8 = undefined;
100 const exp = [_]u8{
101 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46,
102 };
103 var out: [CmacAes128.mac_length]u8 = undefined;
104 CmacAes128.create(&out, &msg, &key);
105 try testing.expectEqualSlices(u8, &out, &exp);
106}
107
108test "CmacAes128 - Example 2: len = 16" {
109 const key = [_]u8{
110 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
111 };
112 const msg = [_]u8{
113 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
114 };
115 const exp = [_]u8{
116 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c,
117 };
118 var out: [CmacAes128.mac_length]u8 = undefined;
119 CmacAes128.create(&out, &msg, &key);
120 try testing.expectEqualSlices(u8, &out, &exp);
121}
122
123test "CmacAes128 - Example 3: len = 40" {
124 const key = [_]u8{
125 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
126 };
127 const msg = [_]u8{
128 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
129 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
130 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
131 };
132 const exp = [_]u8{
133 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27,
134 };
135 var out: [CmacAes128.mac_length]u8 = undefined;
136 CmacAes128.create(&out, &msg, &key);
137 try testing.expectEqualSlices(u8, &out, &exp);
138}
139
140test "CmacAes128 - Example 4: len = 64" {
141 const key = [_]u8{
142 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
143 };
144 const msg = [_]u8{
145 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
146 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
147 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
148 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,
149 };
150 const exp = [_]u8{
151 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe,
152 };
153 var out: [CmacAes128.mac_length]u8 = undefined;
154 CmacAes128.create(&out, &msg, &key);
155 try testing.expectEqualSlices(u8, &out, &exp);
156}