AEGIS is a very fast authenticated encryption system built on top of the core AES function.
The 256 bits variants of AEGIS have a 256 bit key and a 256 bit nonce.
https://datatracker.ietf.org/doc/draft-irtf-cfrg-aegis-aead/
fn Aegis256XGeneric(comptime degree: u7, comptime tag_bits: u9) type
fn Aegis256XGeneric(comptime degree: u7, comptime tag_bits: u9) type {
comptime assert(degree > 0); // degree must be greater than 0
comptime assert(tag_bits == 128 or tag_bits == 256); // tag must be 128 or 256 bits
return struct {
const State = State256X(degree);
pub const tag_length = tag_bits / 8;
pub const nonce_length = 32;
pub const key_length = 32;
pub const block_length = State.rate;
const alignment = State.alignment;
/// c: ciphertext: output buffer should be of size m.len
/// tag: authentication tag: output MAC
/// m: message
/// ad: Associated Data
/// npub: public nonce
/// k: private key
pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void {
assert(c.len == m.len);
var state = State.init(key, npub);
var src: [block_length]u8 align(alignment) = undefined;
var dst: [block_length]u8 align(alignment) = undefined;
var i: usize = 0;
while (i + block_length <= ad.len) : (i += block_length) {
state.absorb(ad[i..][0..block_length]);
}
if (ad.len % block_length != 0) {
@memset(src[0..], 0);
@memcpy(src[0 .. ad.len % block_length], ad[i..][0 .. ad.len % block_length]);
state.absorb(&src);
}
i = 0;
while (i + block_length <= m.len) : (i += block_length) {
state.enc(c[i..][0..block_length], m[i..][0..block_length]);
}
if (m.len % block_length != 0) {
@memset(src[0..], 0);
@memcpy(src[0 .. m.len % block_length], m[i..][0 .. m.len % block_length]);
state.enc(&dst, &src);
@memcpy(c[i..][0 .. m.len % block_length], dst[0 .. m.len % block_length]);
}
tag.* = state.finalize(tag_bits, ad.len, m.len);
}
/// `m`: Message
/// `c`: Ciphertext
/// `tag`: Authentication tag
/// `ad`: Associated data
/// `npub`: Public nonce
/// `k`: Private key
/// Asserts `c.len == m.len`.
///
/// Contents of `m` are undefined if an error is returned.
pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void {
assert(c.len == m.len);
var state = State.init(key, npub);
var src: [block_length]u8 align(alignment) = undefined;
var i: usize = 0;
while (i + block_length <= ad.len) : (i += block_length) {
state.absorb(ad[i..][0..block_length]);
}
if (ad.len % block_length != 0) {
@memset(src[0..], 0);
@memcpy(src[0 .. ad.len % block_length], ad[i..][0 .. ad.len % block_length]);
state.absorb(&src);
}
i = 0;
while (i + block_length <= m.len) : (i += block_length) {
state.dec(m[i..][0..block_length], c[i..][0..block_length]);
}
if (m.len % block_length != 0) {
state.decLast(m[i..], c[i..]);
}
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);
@memset(m, undefined);
return error.AuthenticationFailed;
}
}
};
}