feature. See also
. The project being documented here (as the example) is the Zig library itself.
aegis.State128X
fn State128X(comptime degree: u7) type
File
Code
fn State128X(comptime degree: u7) type {
return struct {
const AesBlockVec = crypto.core.aes.BlockVec(degree);
const State = @This();
blocks: [8]AesBlockVec,
const aes_block_length = AesBlockVec.block_length;
const rate = aes_block_length * 2;
const alignment = AesBlockVec.native_word_size;
fn init(key: [16]u8, nonce: [16]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_block = AesBlockVec.fromBytes(&repeat16u8(degree, key));
const nonce_block = AesBlockVec.fromBytes(&repeat16u8(degree, nonce));
const blocks = [8]AesBlockVec{
key_block.xorBlocks(nonce_block),
c1,
c2,
c1,
key_block.xorBlocks(nonce_block),
key_block.xorBlocks(c2),
key_block.xorBlocks(c1),
key_block.xorBlocks(c2),
};
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..10) |_| {
state.blocks[3] = state.blocks[3].xorBlocks(context_block);
state.blocks[7] = state.blocks[7].xorBlocks(context_block);
state.update(nonce_block, key_block);
}
} else {
for (0..10) |_| {
state.update(nonce_block, key_block);
}
}
return state;
}
fn update(state: *State, d1: AesBlockVec, d2: AesBlockVec) void {
const blocks = &state.blocks;
const tmp = blocks[7];
comptime var i: usize = 7;
inline while (i > 0) : (i -= 1) {
blocks[i] = blocks[i - 1].encrypt(blocks[i]);
}
blocks[0] = tmp.encrypt(blocks[0]);
blocks[0] = blocks[0].xorBlocks(d1);
blocks[4] = blocks[4].xorBlocks(d2);
}
fn absorb(state: *State, src: *const [rate]u8) void {
const msg0 = AesBlockVec.fromBytes(src[0..aes_block_length]);
const msg1 = AesBlockVec.fromBytes(src[aes_block_length..rate]);
state.update(msg0, msg1);
}
fn enc(state: *State, dst: *[rate]u8, src: *const [rate]u8) void {
const blocks = &state.blocks;
const msg0 = AesBlockVec.fromBytes(src[0..aes_block_length]);
const msg1 = AesBlockVec.fromBytes(src[aes_block_length..rate]);
var tmp0 = msg0.xorBlocks(blocks[6]).xorBlocks(blocks[1]);
var tmp1 = msg1.xorBlocks(blocks[2]).xorBlocks(blocks[5]);
tmp0 = tmp0.xorBlocks(blocks[2].andBlocks(blocks[3]));
tmp1 = tmp1.xorBlocks(blocks[6].andBlocks(blocks[7]));
dst[0..aes_block_length].* = tmp0.toBytes();
dst[aes_block_length..rate].* = tmp1.toBytes();
state.update(msg0, msg1);
}
fn dec(state: *State, dst: *[rate]u8, src: *const [rate]u8) void {
const blocks = &state.blocks;
var msg0 = AesBlockVec.fromBytes(src[0..aes_block_length]).xorBlocks(blocks[6]).xorBlocks(blocks[1]);
var msg1 = AesBlockVec.fromBytes(src[aes_block_length..rate]).xorBlocks(blocks[2]).xorBlocks(blocks[5]);
msg0 = msg0.xorBlocks(blocks[2].andBlocks(blocks[3]));
msg1 = msg1.xorBlocks(blocks[6].andBlocks(blocks[7]));
dst[0..aes_block_length].* = msg0.toBytes();
dst[aes_block_length..rate].* = msg1.toBytes();
state.update(msg0, msg1);
}
fn decLast(state: *State, dst: []u8, src: []const u8) void {
const blocks = &state.blocks;
const z0 = blocks[6].xorBlocks(blocks[1]).xorBlocks(blocks[2].andBlocks(blocks[3]));
const z1 = blocks[2].xorBlocks(blocks[5]).xorBlocks(blocks[6].andBlocks(blocks[7]));
var pad: [rate]u8 = @splat(0);
pad[0..aes_block_length].* = z0.toBytes();
pad[aes_block_length..].* = z1.toBytes();
for (pad[0..src.len], src) |*p, x| p.* ^= x;
@memcpy(dst, pad[0..src.len]);
@memset(pad[src.len..], 0);
const msg0 = AesBlockVec.fromBytes(pad[0..aes_block_length]);
const msg1 = AesBlockVec.fromBytes(pad[aes_block_length..rate]);
state.update(msg0, msg1);
}
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[2]);
for (0..7) |_| {
state.update(tmp, 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]).xorBlocks(blocks[6]).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]).xorBlocks(blocks[3]).toBytes();
const tag_multi_2 = blocks[4].xorBlocks(blocks[5]).xorBlocks(blocks[6]).xorBlocks(blocks[7]).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[2].xorBlocks(AesBlockVec.fromBytes(&sizes));
for (0..7) |_| {
state.update(t, 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]).xorBlocks(blocks[6]).toBytes();
for (0..degree / 2) |d| {
v[0..16].* = tags[d * 32 ..][0..16].*;
v[rate / 2 ..][0..16].* = tags[d * 32 ..][16..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..16].* = tags_0[d * 16 ..][0..16].*;
v[rate / 2 ..][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,
}
}
};
}