std/crypto: Add support for AES-GCM
Already pretty fast on platforms with AES-NI, even though GHASH reduction hasn't been optimized yet, and we don't do stitching either.
This commit is contained in:
parent
d343b75e7f
commit
06c16f44e7
@ -13,6 +13,8 @@ pub const aead = struct {
|
||||
pub const XChaCha20Poly1305 = chacha20.XChacha20Poly1305;
|
||||
pub const AEGIS128L = @import("crypto/aegis.zig").AEGIS128L;
|
||||
pub const AEGIS256 = @import("crypto/aegis.zig").AEGIS256;
|
||||
pub const AES128GCM = @import("crypto/aes_gcm.zig").AES128GCM;
|
||||
pub const AES256GCM = @import("crypto/aes_gcm.zig").AES256GCM;
|
||||
};
|
||||
|
||||
/// Authentication (MAC) functions.
|
||||
|
161
lib/std/crypto/aes_gcm.zig
Normal file
161
lib/std/crypto/aes_gcm.zig
Normal file
@ -0,0 +1,161 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const builtin = std.builtin;
|
||||
const crypto = std.crypto;
|
||||
const debug = std.debug;
|
||||
const Ghash = std.crypto.onetimeauth.Ghash;
|
||||
const mem = std.mem;
|
||||
const modes = crypto.core.modes;
|
||||
|
||||
pub const AES128GCM = AESGCM(crypto.core.aes.AES128);
|
||||
pub const AES256GCM = AESGCM(crypto.core.aes.AES256);
|
||||
|
||||
fn AESGCM(comptime AES: anytype) type {
|
||||
debug.assert(AES.block.block_size == 16);
|
||||
|
||||
return struct {
|
||||
pub const tag_length = 16;
|
||||
pub const nonce_length = 12;
|
||||
pub const key_length = AES.key_bits / 8;
|
||||
|
||||
const zeros = [_]u8{0} ** 16;
|
||||
|
||||
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, &zeros);
|
||||
|
||||
var t: [16]u8 = undefined;
|
||||
var j: [16]u8 = undefined;
|
||||
mem.copy(u8, j[0..nonce_length], npub[0..]);
|
||||
mem.writeIntBig(u32, j[nonce_length..][0..4], 1);
|
||||
aes.encrypt(&t, &j);
|
||||
|
||||
var mac = Ghash.init(&h);
|
||||
mac.update(ad);
|
||||
mac.pad();
|
||||
|
||||
mem.writeIntBig(u32, j[nonce_length..][0..4], 2);
|
||||
modes.ctr(@TypeOf(aes), aes, c, m, j, builtin.Endian.Big);
|
||||
mac.update(c[0..m.len][0..]);
|
||||
mac.pad();
|
||||
|
||||
var final_block = h;
|
||||
mem.writeIntBig(u64, final_block[0..8], ad.len * 8);
|
||||
mem.writeIntBig(u64, final_block[8..16], m.len * 8);
|
||||
mac.update(&final_block);
|
||||
mac.final(tag);
|
||||
for (t) |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) !void {
|
||||
assert(c.len == m.len);
|
||||
|
||||
const aes = AES.initEnc(key);
|
||||
var h: [16]u8 = undefined;
|
||||
aes.encrypt(&h, &zeros);
|
||||
|
||||
var t: [16]u8 = undefined;
|
||||
var j: [16]u8 = undefined;
|
||||
mem.copy(u8, j[0..nonce_length], npub[0..]);
|
||||
mem.writeIntBig(u32, j[nonce_length..][0..4], 1);
|
||||
aes.encrypt(&t, &j);
|
||||
|
||||
var mac = Ghash.init(&h);
|
||||
mac.update(ad);
|
||||
mac.pad();
|
||||
|
||||
mac.update(c);
|
||||
mac.pad();
|
||||
|
||||
var final_block = h;
|
||||
mem.writeIntBig(u64, final_block[0..8], ad.len * 8);
|
||||
mem.writeIntBig(u64, final_block[8..16], m.len * 8);
|
||||
mac.update(&final_block);
|
||||
var computed_tag: [Ghash.mac_length]u8 = undefined;
|
||||
mac.final(&computed_tag);
|
||||
for (t) |x, i| {
|
||||
computed_tag[i] ^= x;
|
||||
}
|
||||
|
||||
var acc: u8 = 0;
|
||||
for (computed_tag) |_, p| {
|
||||
acc |= (computed_tag[p] ^ tag[p]);
|
||||
}
|
||||
if (acc != 0) {
|
||||
mem.set(u8, m, 0xaa);
|
||||
return error.AuthenticationFailed;
|
||||
}
|
||||
|
||||
mem.writeIntBig(u32, j[nonce_length..][0..4], 2);
|
||||
modes.ctr(@TypeOf(aes), aes, m, c, j, builtin.Endian.Big);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const htest = @import("test.zig");
|
||||
const testing = std.testing;
|
||||
|
||||
test "AES256GCM - Empty message and no associated data" {
|
||||
const key: [AES256GCM.key_length]u8 = [_]u8{0x69} ** AES256GCM.key_length;
|
||||
const nonce: [AES256GCM.nonce_length]u8 = [_]u8{0x42} ** AES256GCM.nonce_length;
|
||||
const ad = "";
|
||||
const m = "";
|
||||
var c: [m.len]u8 = undefined;
|
||||
var m2: [m.len]u8 = undefined;
|
||||
var tag: [AES256GCM.tag_length]u8 = undefined;
|
||||
|
||||
AES256GCM.encrypt(&c, &tag, m, ad, nonce, key);
|
||||
htest.assertEqual("6b6ff610a16fa4cd59f1fb7903154e92", &tag);
|
||||
}
|
||||
|
||||
test "AES256GCM - Associated data only" {
|
||||
const key: [AES256GCM.key_length]u8 = [_]u8{0x69} ** AES256GCM.key_length;
|
||||
const nonce: [AES256GCM.nonce_length]u8 = [_]u8{0x42} ** AES256GCM.nonce_length;
|
||||
const m = "";
|
||||
const ad = "Test with associated data";
|
||||
var c: [m.len]u8 = undefined;
|
||||
var tag: [AES256GCM.tag_length]u8 = undefined;
|
||||
|
||||
AES256GCM.encrypt(&c, &tag, m, ad, nonce, key);
|
||||
htest.assertEqual("262ed164c2dfb26e080a9d108dd9dd4c", &tag);
|
||||
}
|
||||
|
||||
test "AES256GCM - Message only" {
|
||||
const key: [AES256GCM.key_length]u8 = [_]u8{0x69} ** AES256GCM.key_length;
|
||||
const nonce: [AES256GCM.nonce_length]u8 = [_]u8{0x42} ** AES256GCM.nonce_length;
|
||||
const m = "Test with message only";
|
||||
const ad = "";
|
||||
var c: [m.len]u8 = undefined;
|
||||
var m2: [m.len]u8 = undefined;
|
||||
var tag: [AES256GCM.tag_length]u8 = undefined;
|
||||
|
||||
AES256GCM.encrypt(&c, &tag, m, ad, nonce, key);
|
||||
try AES256GCM.decrypt(&m2, &c, tag, ad, nonce, key);
|
||||
testing.expectEqualSlices(u8, m[0..], m2[0..]);
|
||||
|
||||
htest.assertEqual("5ca1642d90009fea33d01f78cf6eefaf01d539472f7c", &c);
|
||||
htest.assertEqual("07cd7fc9103e2f9e9bf2dfaa319caff4", &tag);
|
||||
}
|
||||
|
||||
test "AES256GCM - Message and associated data" {
|
||||
const key: [AES256GCM.key_length]u8 = [_]u8{0x69} ** AES256GCM.key_length;
|
||||
const nonce: [AES256GCM.nonce_length]u8 = [_]u8{0x42} ** AES256GCM.nonce_length;
|
||||
const m = "Test with message";
|
||||
const ad = "Test with associated data";
|
||||
var c: [m.len]u8 = undefined;
|
||||
var m2: [m.len]u8 = undefined;
|
||||
var tag: [AES256GCM.tag_length]u8 = undefined;
|
||||
|
||||
AES256GCM.encrypt(&c, &tag, m, ad, nonce, key);
|
||||
try AES256GCM.decrypt(&m2, &c, tag, ad, nonce, key);
|
||||
testing.expectEqualSlices(u8, m[0..], m2[0..]);
|
||||
|
||||
htest.assertEqual("5ca1642d90009fea33d01f78cf6eefaf01", &c);
|
||||
htest.assertEqual("64accec679d444e2373bd9f6796c0d2c", &tag);
|
||||
}
|
@ -152,6 +152,8 @@ const aeads = [_]Crypto{
|
||||
Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" },
|
||||
Crypto{ .ty = crypto.aead.AEGIS128L, .name = "aegis-128l" },
|
||||
Crypto{ .ty = crypto.aead.AEGIS256, .name = "aegis-256" },
|
||||
Crypto{ .ty = crypto.aead.AES128GCM, .name = "aes128-gcm" },
|
||||
Crypto{ .ty = crypto.aead.AES256GCM, .name = "aes256-gcm" },
|
||||
};
|
||||
|
||||
pub fn benchmarkAead(comptime Aead: anytype, comptime bytes: comptime_int) !u64 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user