Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reinstantiates AEGIS-MAC with the final construction #22205

Merged
merged 1 commit into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions lib/std/crypto.zig
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,20 @@ pub const auth = struct {
pub const siphash = @import("crypto/siphash.zig");
pub const aegis = struct {
const variants = @import("crypto/aegis.zig");
pub const Aegis128X4Mac = variants.Aegis128X4Mac;
pub const Aegis128X2Mac = variants.Aegis128X2Mac;
pub const Aegis128LMac = variants.Aegis128LMac;

pub const Aegis256X4Mac = variants.Aegis256X4Mac;
pub const Aegis256X2Mac = variants.Aegis256X2Mac;
pub const Aegis256Mac = variants.Aegis256Mac;

pub const Aegis128X4Mac_128 = variants.Aegis128X4Mac_128;
pub const Aegis128X2Mac_128 = variants.Aegis128X2Mac_128;
pub const Aegis128LMac_128 = variants.Aegis128LMac_128;

pub const Aegis256X4Mac_128 = variants.Aegis256X4Mac_128;
pub const Aegis256X2Mac_128 = variants.Aegis256X2Mac_128;
pub const Aegis256Mac_128 = variants.Aegis256Mac_128;
};
pub const cmac = @import("crypto/cmac.zig");
Expand Down
241 changes: 220 additions & 21 deletions lib/std/crypto/aegis.zig
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ fn State128X(comptime degree: u7) type {
state.update(msg0, msg1);
}

fn mac(state: *State, comptime tag_bits: u9, adlen: usize, mlen: usize) [tag_bits / 8]u8 {
fn finalize(state: *State, comptime tag_bits: u9, adlen: usize, mlen: usize) [tag_bits / 8]u8 {
const blocks = &state.blocks;
var sizes: [aes_block_length]u8 = undefined;
mem.writeInt(u64, sizes[0..8], @as(u64, adlen) * 8, .little);
Expand Down Expand Up @@ -200,6 +200,59 @@ fn State128X(comptime degree: u7) type {
else => unreachable,
}
}

fn finalizeMac(state: *State, comptime tag_bits: u9, datalen: usize) [tag_bits / 8]u8 {
const blocks = &state.blocks;
var sizes: [aes_block_length]u8 = undefined;
mem.writeInt(u64, sizes[0..8], @as(u64, datalen) * 8, .little);
mem.writeInt(u64, sizes[8..16], tag_bits, .little);
for (1..degree) |i| {
@memcpy(sizes[i * 16 ..][0..16], sizes[0..16]);
}
var t = blocks[2].xorBlocks(AesBlockVec.fromBytes(&sizes));
for (0..7) |_| {
state.update(t, t);
}
if (degree > 1) {
var v = [_]u8{0} ** rate;
switch (tag_bits) {
128 => {
const tags = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]).xorBlocks(blocks[5]).xorBlocks(blocks[6]).toBytes();
for (0..degree / 2) |d| {
v[0..32].* = tags[d * 32 ..][0..32].*;
state.absorb(&v);
}
},
256 => {
const tags_0 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).toBytes();
const tags_1 = blocks[4].xorBlocks(blocks[5]).xorBlocks(blocks[6]).xorBlocks(blocks[7]).toBytes();
for (1..degree) |d| {
v[0..32].* = tags_0[d * 16 ..][0..16].* ++ tags_1[d * 16 ..][0..16].*;
state.absorb(&v);
}
},
else => unreachable,
}
mem.writeInt(u64, sizes[0..8], degree, .little);
mem.writeInt(u64, sizes[8..16], tag_bits, .little);
t = blocks[2].xorBlocks(AesBlockVec.fromBytes(&sizes));
for (0..7) |_| {
state.update(t, t);
}
}
switch (tag_bits) {
128 => {
const tags = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]).xorBlocks(blocks[5]).xorBlocks(blocks[6]).toBytes();
return tags[0..16].*;
},
256 => {
const tags_0 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).toBytes();
const tags_1 = blocks[4].xorBlocks(blocks[5]).xorBlocks(blocks[6]).xorBlocks(blocks[7]).toBytes();
return tags_0[0..16].* ++ tags_1[0..16].*;
},
else => unreachable,
}
}
};
}

Expand Down Expand Up @@ -252,7 +305,7 @@ fn Aegis128XGeneric(comptime degree: u7, comptime tag_bits: u9) type {
state.enc(&dst, &src);
@memcpy(c[i..][0 .. m.len % block_length], dst[0 .. m.len % block_length]);
}
tag.* = state.mac(tag_bits, ad.len, m.len);
tag.* = state.finalize(tag_bits, ad.len, m.len);
}

/// `m`: Message
Expand Down Expand Up @@ -284,7 +337,7 @@ fn Aegis128XGeneric(comptime degree: u7, comptime tag_bits: u9) type {
if (m.len % block_length != 0) {
state.decLast(m[i..], c[i..]);
}
var computed_tag = state.mac(tag_bits, ad.len, m.len);
var computed_tag = state.finalize(tag_bits, ad.len, m.len);
const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
if (!verify) {
crypto.secureZero(u8, &computed_tag);
Expand Down Expand Up @@ -401,7 +454,7 @@ fn State256X(comptime degree: u7) type {
state.update(msg);
}

fn mac(state: *State, comptime tag_bits: u9, adlen: usize, mlen: usize) [tag_bits / 8]u8 {
fn finalize(state: *State, comptime tag_bits: u9, adlen: usize, mlen: usize) [tag_bits / 8]u8 {
const blocks = &state.blocks;
var sizes: [aes_block_length]u8 = undefined;
mem.writeInt(u64, sizes[0..8], @as(u64, adlen) * 8, .little);
Expand Down Expand Up @@ -440,6 +493,61 @@ fn State256X(comptime degree: u7) type {
else => unreachable,
}
}

fn finalizeMac(state: *State, comptime tag_bits: u9, datalen: usize) [tag_bits / 8]u8 {
const blocks = &state.blocks;
var sizes: [aes_block_length]u8 = undefined;
mem.writeInt(u64, sizes[0..8], @as(u64, datalen) * 8, .little);
mem.writeInt(u64, sizes[8..16], tag_bits, .little);
for (1..degree) |i| {
@memcpy(sizes[i * 16 ..][0..16], sizes[0..16]);
}
var t = blocks[3].xorBlocks(AesBlockVec.fromBytes(&sizes));
for (0..7) |_| {
state.update(t);
}
if (degree > 1) {
var v = [_]u8{0} ** rate;
switch (tag_bits) {
128 => {
const tags = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]).xorBlocks(blocks[5]).toBytes();
for (1..degree) |d| {
v[0..16].* = tags[d * 16 ..][0..16].*;
state.absorb(&v);
}
},
256 => {
const tags_0 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).toBytes();
const tags_1 = blocks[3].xorBlocks(blocks[4]).xorBlocks(blocks[5]).toBytes();
for (1..degree) |d| {
v[0..16].* = tags_0[d * 16 ..][0..16].*;
state.absorb(&v);
v[0..16].* = tags_1[d * 16 ..][0..16].*;
state.absorb(&v);
}
},
else => unreachable,
}
mem.writeInt(u64, sizes[0..8], degree, .little);
mem.writeInt(u64, sizes[8..16], tag_bits, .little);
t = blocks[3].xorBlocks(AesBlockVec.fromBytes(&sizes));
for (0..7) |_| {
state.update(t);
}
}
switch (tag_bits) {
128 => {
const tags = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]).xorBlocks(blocks[5]).toBytes();
return tags[0..16].*;
},
256 => {
const tags_0 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).toBytes();
const tags_1 = blocks[3].xorBlocks(blocks[4]).xorBlocks(blocks[5]).toBytes();
return tags_0[0..16].* ++ tags_1[0..16].*;
},
else => unreachable,
}
}
};
}

Expand Down Expand Up @@ -492,7 +600,7 @@ fn Aegis256XGeneric(comptime degree: u7, comptime tag_bits: u9) type {
state.enc(&dst, &src);
@memcpy(c[i..][0 .. m.len % block_length], dst[0 .. m.len % block_length]);
}
tag.* = state.mac(tag_bits, ad.len, m.len);
tag.* = state.finalize(tag_bits, ad.len, m.len);
}

/// `m`: Message
Expand Down Expand Up @@ -524,7 +632,7 @@ fn Aegis256XGeneric(comptime degree: u7, comptime tag_bits: u9) type {
if (m.len % block_length != 0) {
state.decLast(m[i..], c[i..]);
}
var computed_tag = state.mac(tag_bits, ad.len, m.len);
var computed_tag = state.finalize(tag_bits, ad.len, m.len);
const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
if (!verify) {
crypto.secureZero(u8, &computed_tag);
Expand Down Expand Up @@ -562,9 +670,31 @@ pub const Aegis128X2Mac = AegisMac(Aegis128X2_256);
/// - It has a large security margin against internal collisions.
pub const Aegis128LMac = AegisMac(Aegis128L_256);

/// The `Aegis256X4Mac` message authentication function has a 256-bit key size,
/// and outputs 256 bit tags.
/// The key size is the main practical difference with `Aegis128X4Mac`.
/// AEGIS' large state, non-linearity and non-invertibility provides the
/// following properties:
/// - 256 bit security against forgery.
/// - Recovering the secret key from the state would require ~2^256 attempts,
/// which is infeasible for any practical adversary.
/// - It has a large security margin against internal collisions.
pub const Aegis256X4Mac = AegisMac(Aegis256X4_256);

/// The `Aegis256X2Mac` message authentication function has a 256-bit key size,
/// and outputs 256 bit tags.
/// The key size is the main practical difference with `Aegis128X2Mac`.
/// AEGIS' large state, non-linearity and non-invertibility provides the
/// following properties:
/// - 256 bit security against forgery.
/// - Recovering the secret key from the state would require ~2^256 attempts,
/// which is infeasible for any practical adversary.
/// - It has a large security margin against internal collisions.
pub const Aegis256X2Mac = AegisMac(Aegis256X2_256);

/// The `Aegis256Mac` message authentication function has a 256-bit key size,
/// and outputs 256 bit tags. Unless theoretical multi-target attacks are a
/// concern, the AEGIS-128L variant should be preferred.
/// and outputs 256 bit tags.
/// The key size is the main practical difference with `Aegis128LMac`.
/// AEGIS' large state, non-linearity and non-invertibility provides the
/// following properties:
/// - 256 bit security against forgery.
Expand All @@ -573,9 +703,21 @@ pub const Aegis128LMac = AegisMac(Aegis128L_256);
/// - It has a large security margin against internal collisions.
pub const Aegis256Mac = AegisMac(Aegis256_256);

/// AEGIS-128X4 MAC with 128-bit tags
pub const Aegis128X4Mac_128 = AegisMac(Aegis128X4);

/// AEGIS-128X2 MAC with 128-bit tags
pub const Aegis128X2Mac_128 = AegisMac(Aegis128X2);

/// AEGIS-128L MAC with 128-bit tags
pub const Aegis128LMac_128 = AegisMac(Aegis128L);

/// AEGIS-256X4 MAC with 128-bit tags
pub const Aegis256X4Mac_128 = AegisMac(Aegis256X4);

/// AEGIS-256X2 MAC with 128-bit tags
pub const Aegis256X2Mac_128 = AegisMac(Aegis256X2);

/// AEGIS-256 MAC with 128-bit tags
pub const Aegis256Mac_128 = AegisMac(Aegis256);

Expand All @@ -585,18 +727,25 @@ fn AegisMac(comptime T: type) type {

pub const mac_length = T.tag_length;
pub const key_length = T.key_length;
pub const nonce_length = T.nonce_length;
pub const block_length = T.block_length;

state: T.State,
buf: [block_length]u8 = undefined,
off: usize = 0,
msg_len: usize = 0,

/// Initialize a state for the MAC function
/// Initialize a state for the MAC function, with a key and a nonce
pub fn initWithNonce(key: *const [key_length]u8, nonce: *const [nonce_length]u8) Mac {
return Mac{
.state = T.State.init(key.*, nonce.*),
};
}

/// Initialize a state for the MAC function, with a default nonce
pub fn init(key: *const [key_length]u8) Mac {
const nonce = [_]u8{0} ** T.nonce_length;
return Mac{
.state = T.State.init(key.*, nonce),
.state = T.State.init(key.*, [_]u8{0} ** nonce_length),
};
}

Expand Down Expand Up @@ -634,7 +783,14 @@ fn AegisMac(comptime T: type) type {
@memcpy(pad[0..self.off], self.buf[0..self.off]);
self.state.absorb(&pad);
}
out.* = self.state.mac(T.tag_length * 8, self.msg_len, 0);
out.* = self.state.finalizeMac(T.tag_length * 8, self.msg_len);
}

/// Return an authentication tag for a message, a key and a nonce
pub fn createWithNonce(out: *[mac_length]u8, msg: []const u8, key: *const [key_length]u8, nonce: *const [nonce_length]u8) void {
var ctx = Mac.initWithNonce(key, nonce);
ctx.update(msg);
ctx.final(out);
}

/// Return an authentication tag for a message and a key
Expand Down Expand Up @@ -820,29 +976,72 @@ test "Aegis MAC" {
st.update(msg[0..32]);
st.update(msg[32..]);
st.final(&tag);
try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag);
try htest.assertEqual("f5eb88d90b7d31c9a679eb94ed1374cd14816b19cdb77930d1a5158f8595983b", &tag);

st = st_init;
st.update(msg[0..31]);
st.update(msg[31..]);
st.final(&tag);
try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag);
try htest.assertEqual("f5eb88d90b7d31c9a679eb94ed1374cd14816b19cdb77930d1a5158f8595983b", &tag);

st = st_init;
st.update(msg[0..14]);
st.update(msg[14..30]);
st.update(msg[30..]);
st.final(&tag);
try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag);

var empty: [0]u8 = undefined;
const nonce = [_]u8{0x00} ** Aegis128L_256.nonce_length;
Aegis128L_256.encrypt(&empty, &tag, &empty, &msg, nonce, key);
try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag);
try htest.assertEqual("f5eb88d90b7d31c9a679eb94ed1374cd14816b19cdb77930d1a5158f8595983b", &tag);

// An update whose size is not a multiple of the block size
st = st_init;
st.update(msg[0..33]);
st.final(&tag);
try htest.assertEqual("c7cf649a844c1a6676cf6d91b1658e0aee54a4da330b0a8d3bc7ea4067551d1b", &tag);
try htest.assertEqual("07b3ba5ad9ceee5ef1906e3396f0fa540fbcd2f33833ef97c35bdc2ae9ae0535", &tag);
}

test "AEGISMAC-128* test vectors" {
const key = [_]u8{ 0x10, 0x01 } ++ [_]u8{0x00} ** (16 - 2);
const nonce = [_]u8{ 0x10, 0x00, 0x02 } ++ [_]u8{0x00} ** (16 - 3);
var msg: [35]u8 = undefined;
for (&msg, 0..) |*byte, i| byte.* = @truncate(i);
var mac128: [16]u8 = undefined;
var mac256: [32]u8 = undefined;

Aegis128LMac.createWithNonce(&mac256, &msg, &key, &nonce);
Aegis128LMac_128.createWithNonce(&mac128, &msg, &key, &nonce);
try htest.assertEqual("d3f09b2842ad301687d6902c921d7818", &mac128);
try htest.assertEqual("9490e7c89d420c9f37417fa625eb38e8cad53c5cbec55285e8499ea48377f2a3", &mac256);

Aegis128X2Mac.createWithNonce(&mac256, &msg, &key, &nonce);
Aegis128X2Mac_128.createWithNonce(&mac128, &msg, &key, &nonce);
try htest.assertEqual("7aa41edfd57a95c1108d83c63b8d4d01", &mac128);
try htest.assertEqual("55b6449929cd2b01d04786e57698b3ddfb5cbf6e421bbd022637a33d60f40294", &mac256);

Aegis128X4Mac.createWithNonce(&mac256, &msg, &key, &nonce);
Aegis128X4Mac_128.createWithNonce(&mac128, &msg, &key, &nonce);
try htest.assertEqual("46a194ea4337bb32c2186a99e312f3a7", &mac128);
try htest.assertEqual("ea884072699569532fb68ae9fb2653c9ffef3e974333d3a17d77be02453cc12f", &mac256);
}

test "AEGISMAC-256* test vectors" {
const key = [_]u8{ 0x10, 0x01 } ++ [_]u8{0x00} ** (32 - 2);
const nonce = [_]u8{ 0x10, 0x00, 0x02 } ++ [_]u8{0x00} ** (32 - 3);
var msg: [35]u8 = undefined;
for (&msg, 0..) |*byte, i| byte.* = @truncate(i);
var mac128: [16]u8 = undefined;
var mac256: [32]u8 = undefined;

Aegis256Mac.createWithNonce(&mac256, &msg, &key, &nonce);
Aegis256Mac_128.createWithNonce(&mac128, &msg, &key, &nonce);
try htest.assertEqual("c08e20cfc56f27195a46c9cef5c162d4", &mac128);
try htest.assertEqual("a5c906ede3d69545c11e20afa360b221f936e946ed2dba3d7c75ad6dc2784126", &mac256);

Aegis256X2Mac.createWithNonce(&mac256, &msg, &key, &nonce);
Aegis256X2Mac_128.createWithNonce(&mac128, &msg, &key, &nonce);
try htest.assertEqual("fb319cb6dd728a764606fb14d37f2a5e", &mac128);
try htest.assertEqual("0844b20ed5147ceae89c7a160263afd4b1382d6b154ecf560ce8a342cb6a8fd1", &mac256);

Aegis256X4Mac.createWithNonce(&mac256, &msg, &key, &nonce);
Aegis256X4Mac_128.createWithNonce(&mac128, &msg, &key, &nonce);
try htest.assertEqual("a51f9bc5beae60cce77f0dbc60761edd", &mac128);
try htest.assertEqual("b36a16ef07c36d75a91f437502f24f545b8dfa88648ed116943c29fead3bf10c", &mac256);
}
4 changes: 4 additions & 0 deletions lib/std/crypto/benchmark.zig
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ const macs = [_]Crypto{
Crypto{ .ty = crypto.auth.siphash.SipHash64(1, 3), .name = "siphash-1-3" },
Crypto{ .ty = crypto.auth.siphash.SipHash128(2, 4), .name = "siphash128-2-4" },
Crypto{ .ty = crypto.auth.siphash.SipHash128(1, 3), .name = "siphash128-1-3" },
Crypto{ .ty = crypto.auth.aegis.Aegis128X4Mac, .name = "aegis-128x4 mac" },
Crypto{ .ty = crypto.auth.aegis.Aegis256X4Mac, .name = "aegis-256x4 mac" },
Crypto{ .ty = crypto.auth.aegis.Aegis128X2Mac, .name = "aegis-128x2 mac" },
Crypto{ .ty = crypto.auth.aegis.Aegis256X2Mac, .name = "aegis-256x2 mac" },
Crypto{ .ty = crypto.auth.aegis.Aegis128LMac, .name = "aegis-128l mac" },
Crypto{ .ty = crypto.auth.aegis.Aegis256Mac, .name = "aegis-256 mac" },
Crypto{ .ty = crypto.auth.cmac.CmacAes128, .name = "aes-cmac" },
Expand Down
Loading