feature. See also
. The project being documented here (as the example) is the Zig library itself.
aegis.State256X
fn State256X(comptime degree: u7) type
File
Code
fn State256X(comptime degree: u7) type {
return struct {
const AesBlockVec = crypto.core.aes.BlockVec(degree);
const State = @This();
blocks: [6]AesBlockVec,
const aes_block_length = AesBlockVec.block_length;
const rate = aes_block_length;
const alignment = AesBlockVec.native_word_size;
fn init(key: [32]u8, nonce: [32]u8) State {
const c1 = AesBlockVec.fromBytes(&repeat16u8(degree, .{ 0xdb, 0x3d, 0x18, 0x55, 0x6d, 0xc2, 0x2f, 0xf1, 0x20, 0x11, 0x31, 0x42, 0x73, 0xb5, 0x28, 0xdd }));
const c2 = AesBlockVec.fromBytes(&repeat16u8(degree, .{ 0x0, 0x1, 0x01, 0x02, 0x03, 0x05, 0x08, 0x0d, 0x15, 0x22, 0x37, 0x59, 0x90, 0xe9, 0x79, 0x62 }));
const key_block1 = AesBlockVec.fromBytes(&repeat16u8(degree, key[0..16].*));
const key_block2 = AesBlockVec.fromBytes(&repeat16u8(degree, key[16..32].*));
const nonce_block1 = AesBlockVec.fromBytes(&repeat16u8(degree, nonce[0..16].*));
const nonce_block2 = AesBlockVec.fromBytes(&repeat16u8(degree, nonce[16..32].*));
const kxn1 = key_block1.xorBlocks(nonce_block1);
const kxn2 = key_block2.xorBlocks(nonce_block2);
const blocks = [6]AesBlockVec{
kxn1,
kxn2,
c1,
c2,
key_block1.xorBlocks(c2),
key_block2.xorBlocks(c1),
};
var state = State{ .blocks = blocks };
if (degree > 1) {
const context_block = ctx: {
var contexts_bytes: [aes_block_length]u8 = @splat(0);
for (0..degree) |i| {
contexts_bytes[i * 16] = @intCast(i);
contexts_bytes[i * 16 + 1] = @intCast(degree - 1);
}
break :ctx AesBlockVec.fromBytes(&contexts_bytes);
};
for (0..4) |_| {
state.blocks[3] = state.blocks[3].xorBlocks(context_block);
state.blocks[5] = state.blocks[5].xorBlocks(context_block);
state.update(key_block1);
state.blocks[3] = state.blocks[3].xorBlocks(context_block);
state.blocks[5] = state.blocks[5].xorBlocks(context_block);
state.update(key_block2);
state.blocks[3] = state.blocks[3].xorBlocks(context_block);
state.blocks[5] = state.blocks[5].xorBlocks(context_block);
state.update(kxn1);
state.blocks[3] = state.blocks[3].xorBlocks(context_block);
state.blocks[5] = state.blocks[5].xorBlocks(context_block);
state.update(kxn2);
}
} else {
for (0..4) |_| {
state.update(key_block1);
state.update(key_block2);
state.update(kxn1);
state.update(kxn2);
}
}
return state;
}
fn update(state: *State, d: AesBlockVec) void {
const blocks = &state.blocks;
const tmp = blocks[5].encrypt(blocks[0]);
comptime var i: usize = 5;
inline while (i > 0) : (i -= 1) {
blocks[i] = blocks[i - 1].encrypt(blocks[i]);
}
blocks[0] = tmp.xorBlocks(d);
}
fn absorb(state: *State, src: *const [rate]u8) void {
const msg = AesBlockVec.fromBytes(src);
state.update(msg);
}
fn enc(state: *State, dst: *[rate]u8, src: *const [rate]u8) void {
const blocks = &state.blocks;
const msg = AesBlockVec.fromBytes(src);
var tmp = msg.xorBlocks(blocks[5]).xorBlocks(blocks[4]).xorBlocks(blocks[1]);
tmp = tmp.xorBlocks(blocks[2].andBlocks(blocks[3]));
dst.* = tmp.toBytes();
state.update(msg);
}
fn dec(state: *State, dst: *[rate]u8, src: *const [rate]u8) void {
const blocks = &state.blocks;
var msg = AesBlockVec.fromBytes(src).xorBlocks(blocks[5]).xorBlocks(blocks[4]).xorBlocks(blocks[1]);
msg = msg.xorBlocks(blocks[2].andBlocks(blocks[3]));
dst.* = msg.toBytes();
state.update(msg);
}
fn decLast(state: *State, dst: []u8, src: []const u8) void {
const blocks = &state.blocks;
const z = blocks[5].xorBlocks(blocks[4]).xorBlocks(blocks[1]).xorBlocks(blocks[2].andBlocks(blocks[3]));
var pad = z.toBytes();
for (pad[0..src.len], src) |*p, x| p.* ^= x;
@memcpy(dst, pad[0..src.len]);
@memset(pad[src.len..], 0);
const msg = AesBlockVec.fromBytes(pad[0..]);
state.update(msg);
}
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);
mem.writeInt(u64, sizes[8..16], @as(u64, mlen) * 8, .little);
for (1..degree) |i| {
@memcpy(sizes[i * 16 ..][0..16], sizes[0..16]);
}
const tmp = AesBlockVec.fromBytes(&sizes).xorBlocks(blocks[3]);
for (0..7) |_| {
state.update(tmp);
}
switch (tag_bits) {
128 => {
var tag_multi = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]).xorBlocks(blocks[5]).toBytes();
var tag = tag_multi[0..16].*;
@memcpy(tag[0..], tag_multi[0..16]);
for (1..degree) |d| {
for (0..16) |i| {
tag[i] ^= tag_multi[d * 16 + i];
}
}
return tag;
},
256 => {
const tag_multi_1 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).toBytes();
const tag_multi_2 = blocks[3].xorBlocks(blocks[4]).xorBlocks(blocks[5]).toBytes();
var tag = tag_multi_1[0..16].* ++ tag_multi_2[0..16].*;
for (1..degree) |d| {
for (0..16) |i| {
tag[i] ^= tag_multi_1[d * 16 + i];
tag[i + 16] ^= tag_multi_2[d * 16 + i];
}
}
return tag;
},
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: [rate]u8 = @splat(0);
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,
}
}
};
}