feature. See also
. The project being documented here (as the example) is the Zig library itself.
aes_gcm.AesGcm
fn AesGcm(comptime Aes: anytype) type
File
Code
fn AesGcm(comptime Aes: anytype) type {
debug.assert(Aes.block.block_length == 16);
return struct {
pub const tag_length = 16;
pub const nonce_length = 12;
pub const key_length = Aes.key_bits / 8;
pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void {
debug.assert(c.len == m.len);
debug.assert(m.len <= 16 * ((1 << 32) - 2));
const aes = Aes.initEnc(key);
var h: [16]u8 = undefined;
aes.encrypt(&h, &@splat(0));
var t: [16]u8 = undefined;
var j: [16]u8 = undefined;
j[0..nonce_length].* = npub;
mem.writeInt(u32, j[nonce_length..][0..4], 1, .big);
aes.encrypt(&t, &j);
const block_count = @divCeil(ad.len, Ghash.block_length) + @divCeil(c.len, Ghash.block_length) + 1;
var mac = Ghash.initForBlockCount(&h, block_count);
mac.update(ad);
mac.pad();
mem.writeInt(u32, j[nonce_length..][0..4], 2, .big);
modes.ctr(@TypeOf(aes), aes, c, m, j, .big);
mac.update(c[0..m.len][0..]);
mac.pad();
var final_block = h;
mem.writeInt(u64, final_block[0..8], @as(u64, ad.len) * 8, .big);
mem.writeInt(u64, final_block[8..16], @as(u64, m.len) * 8, .big);
mac.update(&final_block);
mac.final(tag);
for (t, 0..) |x, i| {
tag[i] ^= x;
}
}
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);
const aes = Aes.initEnc(key);
var h: [16]u8 = undefined;
aes.encrypt(&h, &@splat(0));
var t: [16]u8 = undefined;
var j: [16]u8 = undefined;
j[0..nonce_length].* = npub;
mem.writeInt(u32, j[nonce_length..][0..4], 1, .big);
aes.encrypt(&t, &j);
const block_count = @divCeil(ad.len, Ghash.block_length) + @divCeil(c.len, Ghash.block_length) + 1;
var mac = Ghash.initForBlockCount(&h, block_count);
mac.update(ad);
mac.pad();
mac.update(c);
mac.pad();
var final_block = h;
mem.writeInt(u64, final_block[0..8], @as(u64, ad.len) * 8, .big);
mem.writeInt(u64, final_block[8..16], @as(u64, m.len) * 8, .big);
mac.update(&final_block);
var computed_tag: [Ghash.mac_length]u8 = undefined;
mac.final(&computed_tag);
for (t, 0..) |x, i| {
computed_tag[i] ^= x;
}
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;
}
mem.writeInt(u32, j[nonce_length..][0..4], 2, .big);
modes.ctr(@TypeOf(aes), aes, m, c, j, .big);
}
};
}