FIPS 113 (1985): Computer Data Authentication https://csrc.nist.gov/publications/detail/fips/113/archive/1985-05-30
WARNING: CBC-MAC is insecure for variable-length messages without additional protection. Only use when required by protocols like CCM that mitigate this.
pub fn CbcMac(comptime BlockCipher: type) type
pub fn CbcMac(comptime BlockCipher: type) type {
const BlockCipherCtx = @typeInfo(@TypeOf(BlockCipher.initEnc)).@"fn".return_type.?;
const Block = [BlockCipher.block.block_length]u8;
return struct {
const Self = @This();
pub const key_length = BlockCipher.key_bits / 8;
pub const block_length = BlockCipher.block.block_length;
pub const mac_length = block_length;
cipher_ctx: BlockCipherCtx,
buf: Block = @splat(0),
pos: usize = 0,
pub fn create(out: *[mac_length]u8, msg: []const u8, key: *const [key_length]u8) void {
var ctx = Self.init(key);
ctx.update(msg);
ctx.final(out);
}
pub fn init(key: *const [key_length]u8) Self {
return Self{
.cipher_ctx = BlockCipher.initEnc(key.*),
};
}
pub fn update(self: *Self, msg: []const u8) void {
const left = block_length - self.pos;
var m = msg;
// Partial buffer exists from previous update. Complete the block.
if (m.len > left) {
for (self.buf[self.pos..], 0..) |*b, i| b.* ^= m[i];
m = m[left..];
self.cipher_ctx.encrypt(&self.buf, &self.buf);
self.pos = 0;
}
// Full blocks.
while (m.len > block_length) {
for (self.buf[0..block_length], 0..) |*b, i| b.* ^= m[i];
m = m[block_length..];
self.cipher_ctx.encrypt(&self.buf, &self.buf);
self.pos = 0;
}
// Copy any remainder for next pass.
if (m.len > 0) {
for (self.buf[self.pos..][0..m.len], 0..) |*b, i| b.* ^= m[i];
self.pos += m.len;
}
}
pub fn final(self: *Self, out: *[mac_length]u8) void {
// CBC-MAC: encrypt the current buffer state.
// Partial blocks are implicitly zero-padded: buf[pos..] contains zeros from initialization.
self.cipher_ctx.encrypt(out, &self.buf);
}
};
}