master
  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..], 0..) |*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], 0..) |*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], 0..) |*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, 0..) |*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.readInt(Int, &l, .big);
 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.writeInt(Int, &l2, l_2, .big);
 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}