aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2020-10-05 22:20:29 -0400
committerGitHub <noreply@github.com>2020-10-05 22:20:29 -0400
commit41aa5edaaf79da869c6f39f0f0508b3fb5d3d34c (patch)
treeda13660df4039b808db9460ee58cf4d7419b18d9 /lib/std
parent75db8d9e2cdf0d0dff2f287cce30f1b727dc2d54 (diff)
parent06c16f44e77decd8bdafd5f1cd149b475ddf92cd (diff)
downloadzig-41aa5edaaf79da869c6f39f0f0508b3fb5d3d34c.tar.gz
zig-41aa5edaaf79da869c6f39f0f0508b3fb5d3d34c.zip
Merge pull request #6580 from jedisct1/aesgcm
std/crypto: Add support for AES-GCM
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/crypto.zig2
-rw-r--r--lib/std/crypto/aes_gcm.zig161
-rw-r--r--lib/std/crypto/benchmark.zig2
-rw-r--r--lib/std/crypto/ghash.zig23
-rw-r--r--lib/std/crypto/poly1305.zig15
5 files changed, 194 insertions, 9 deletions
diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig
index 5699f7db36..8c225aa719 100644
--- a/lib/std/crypto.zig
+++ b/lib/std/crypto.zig
@@ -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.
diff --git a/lib/std/crypto/aes_gcm.zig b/lib/std/crypto/aes_gcm.zig
new file mode 100644
index 0000000000..c7093a7593
--- /dev/null
+++ b/lib/std/crypto/aes_gcm.zig
@@ -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);
+}
diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig
index d0ff29e896..1db3a1e870 100644
--- a/lib/std/crypto/benchmark.zig
+++ b/lib/std/crypto/benchmark.zig
@@ -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 {
diff --git a/lib/std/crypto/ghash.zig b/lib/std/crypto/ghash.zig
index 6a1bf7c186..04bc6a8275 100644
--- a/lib/std/crypto/ghash.zig
+++ b/lib/std/crypto/ghash.zig
@@ -250,7 +250,7 @@ pub const Ghash = struct {
}
mb = mb[want..];
st.leftover += want;
- if (st.leftover > block_size) {
+ if (st.leftover < block_size) {
return;
}
st.blocks(&st.buf);
@@ -269,14 +269,21 @@ pub const Ghash = struct {
}
}
- pub fn final(st: *Ghash, out: *[mac_length]u8) void {
- if (st.leftover > 0) {
- var i = st.leftover;
- while (i < block_size) : (i += 1) {
- st.buf[i] = 0;
- }
- st.blocks(&st.buf);
+ /// Zero-pad to align the next input to the first byte of a block
+ pub fn pad(st: *Ghash) void {
+ if (st.leftover == 0) {
+ return;
}
+ var i = st.leftover;
+ while (i < block_size) : (i += 1) {
+ st.buf[i] = 0;
+ }
+ st.blocks(&st.buf);
+ st.leftover = 0;
+ }
+
+ pub fn final(st: *Ghash, out: *[mac_length]u8) void {
+ st.pad();
mem.writeIntBig(u64, out[0..8], st.y1);
mem.writeIntBig(u64, out[8..16], st.y0);
diff --git a/lib/std/crypto/poly1305.zig b/lib/std/crypto/poly1305.zig
index 31d1d6ba5a..c6613f64ba 100644
--- a/lib/std/crypto/poly1305.zig
+++ b/lib/std/crypto/poly1305.zig
@@ -91,7 +91,7 @@ pub const Poly1305 = struct {
}
mb = mb[want..];
st.leftover += want;
- if (st.leftover > block_size) {
+ if (st.leftover < block_size) {
return;
}
st.blocks(&st.buf, false);
@@ -114,6 +114,19 @@ pub const Poly1305 = struct {
}
}
+ /// Zero-pad to align the next input to the first byte of a block
+ pub fn pad(st: *Poly1305) void {
+ if (st.leftover == 0) {
+ return;
+ }
+ var i = st.leftover;
+ while (i < block_size) : (i += 1) {
+ st.buf[i] = 0;
+ }
+ st.blocks(&st.buf);
+ st.leftover = 0;
+ }
+
pub fn final(st: *Poly1305, out: *[mac_length]u8) void {
if (st.leftover > 0) {
var i = st.leftover;