authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-05 22:20:29-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-10-05 22:20:29-04:00
log41aa5edaaf79da869c6f39f0f0508b3fb5d3d34c
treeda13660df4039b808db9460ee58cf4d7419b18d9
parent75db8d9e2cdf0d0dff2f287cce30f1b727dc2d54
parent06c16f44e77decd8bdafd5f1cd149b475ddf92cd
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #6580 from jedisct1/aesgcm

std/crypto: Add support for AES-GCM

5 files changed, 194 insertions(+), 9 deletions(-)

lib/std/crypto.zig+2
......@@ -13,6 +13,8 @@ pub const aead = struct {
1313 pub const XChaCha20Poly1305 = chacha20.XChacha20Poly1305;
1414 pub const AEGIS128L = @import("crypto/aegis.zig").AEGIS128L;
1515 pub const AEGIS256 = @import("crypto/aegis.zig").AEGIS256;
16 pub const AES128GCM = @import("crypto/aes_gcm.zig").AES128GCM;
17 pub const AES256GCM = @import("crypto/aes_gcm.zig").AES256GCM;
1618};
1719
1820/// Authentication (MAC) functions.
lib/std/crypto/aes_gcm.zig created+161
......@@ -0,0 +1,161 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const builtin = std.builtin;
4const crypto = std.crypto;
5const debug = std.debug;
6const Ghash = std.crypto.onetimeauth.Ghash;
7const mem = std.mem;
8const modes = crypto.core.modes;
9
10pub const AES128GCM = AESGCM(crypto.core.aes.AES128);
11pub const AES256GCM = AESGCM(crypto.core.aes.AES256);
12
13fn AESGCM(comptime AES: anytype) type {
14 debug.assert(AES.block.block_size == 16);
15
16 return struct {
17 pub const tag_length = 16;
18 pub const nonce_length = 12;
19 pub const key_length = AES.key_bits / 8;
20
21 const zeros = [_]u8{0} ** 16;
22
23 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void {
24 debug.assert(c.len == m.len);
25 debug.assert(m.len <= 16 * ((1 << 32) - 2));
26
27 const aes = AES.initEnc(key);
28 var h: [16]u8 = undefined;
29 aes.encrypt(&h, &zeros);
30
31 var t: [16]u8 = undefined;
32 var j: [16]u8 = undefined;
33 mem.copy(u8, j[0..nonce_length], npub[0..]);
34 mem.writeIntBig(u32, j[nonce_length..][0..4], 1);
35 aes.encrypt(&t, &j);
36
37 var mac = Ghash.init(&h);
38 mac.update(ad);
39 mac.pad();
40
41 mem.writeIntBig(u32, j[nonce_length..][0..4], 2);
42 modes.ctr(@TypeOf(aes), aes, c, m, j, builtin.Endian.Big);
43 mac.update(c[0..m.len][0..]);
44 mac.pad();
45
46 var final_block = h;
47 mem.writeIntBig(u64, final_block[0..8], ad.len * 8);
48 mem.writeIntBig(u64, final_block[8..16], m.len * 8);
49 mac.update(&final_block);
50 mac.final(tag);
51 for (t) |x, i| {
52 tag[i] ^= x;
53 }
54 }
55
56 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) !void {
57 assert(c.len == m.len);
58
59 const aes = AES.initEnc(key);
60 var h: [16]u8 = undefined;
61 aes.encrypt(&h, &zeros);
62
63 var t: [16]u8 = undefined;
64 var j: [16]u8 = undefined;
65 mem.copy(u8, j[0..nonce_length], npub[0..]);
66 mem.writeIntBig(u32, j[nonce_length..][0..4], 1);
67 aes.encrypt(&t, &j);
68
69 var mac = Ghash.init(&h);
70 mac.update(ad);
71 mac.pad();
72
73 mac.update(c);
74 mac.pad();
75
76 var final_block = h;
77 mem.writeIntBig(u64, final_block[0..8], ad.len * 8);
78 mem.writeIntBig(u64, final_block[8..16], m.len * 8);
79 mac.update(&final_block);
80 var computed_tag: [Ghash.mac_length]u8 = undefined;
81 mac.final(&computed_tag);
82 for (t) |x, i| {
83 computed_tag[i] ^= x;
84 }
85
86 var acc: u8 = 0;
87 for (computed_tag) |_, p| {
88 acc |= (computed_tag[p] ^ tag[p]);
89 }
90 if (acc != 0) {
91 mem.set(u8, m, 0xaa);
92 return error.AuthenticationFailed;
93 }
94
95 mem.writeIntBig(u32, j[nonce_length..][0..4], 2);
96 modes.ctr(@TypeOf(aes), aes, m, c, j, builtin.Endian.Big);
97 }
98 };
99}
100
101const htest = @import("test.zig");
102const testing = std.testing;
103
104test "AES256GCM - Empty message and no associated data" {
105 const key: [AES256GCM.key_length]u8 = [_]u8{0x69} ** AES256GCM.key_length;
106 const nonce: [AES256GCM.nonce_length]u8 = [_]u8{0x42} ** AES256GCM.nonce_length;
107 const ad = "";
108 const m = "";
109 var c: [m.len]u8 = undefined;
110 var m2: [m.len]u8 = undefined;
111 var tag: [AES256GCM.tag_length]u8 = undefined;
112
113 AES256GCM.encrypt(&c, &tag, m, ad, nonce, key);
114 htest.assertEqual("6b6ff610a16fa4cd59f1fb7903154e92", &tag);
115}
116
117test "AES256GCM - Associated data only" {
118 const key: [AES256GCM.key_length]u8 = [_]u8{0x69} ** AES256GCM.key_length;
119 const nonce: [AES256GCM.nonce_length]u8 = [_]u8{0x42} ** AES256GCM.nonce_length;
120 const m = "";
121 const ad = "Test with associated data";
122 var c: [m.len]u8 = undefined;
123 var tag: [AES256GCM.tag_length]u8 = undefined;
124
125 AES256GCM.encrypt(&c, &tag, m, ad, nonce, key);
126 htest.assertEqual("262ed164c2dfb26e080a9d108dd9dd4c", &tag);
127}
128
129test "AES256GCM - Message only" {
130 const key: [AES256GCM.key_length]u8 = [_]u8{0x69} ** AES256GCM.key_length;
131 const nonce: [AES256GCM.nonce_length]u8 = [_]u8{0x42} ** AES256GCM.nonce_length;
132 const m = "Test with message only";
133 const ad = "";
134 var c: [m.len]u8 = undefined;
135 var m2: [m.len]u8 = undefined;
136 var tag: [AES256GCM.tag_length]u8 = undefined;
137
138 AES256GCM.encrypt(&c, &tag, m, ad, nonce, key);
139 try AES256GCM.decrypt(&m2, &c, tag, ad, nonce, key);
140 testing.expectEqualSlices(u8, m[0..], m2[0..]);
141
142 htest.assertEqual("5ca1642d90009fea33d01f78cf6eefaf01d539472f7c", &c);
143 htest.assertEqual("07cd7fc9103e2f9e9bf2dfaa319caff4", &tag);
144}
145
146test "AES256GCM - Message and associated data" {
147 const key: [AES256GCM.key_length]u8 = [_]u8{0x69} ** AES256GCM.key_length;
148 const nonce: [AES256GCM.nonce_length]u8 = [_]u8{0x42} ** AES256GCM.nonce_length;
149 const m = "Test with message";
150 const ad = "Test with associated data";
151 var c: [m.len]u8 = undefined;
152 var m2: [m.len]u8 = undefined;
153 var tag: [AES256GCM.tag_length]u8 = undefined;
154
155 AES256GCM.encrypt(&c, &tag, m, ad, nonce, key);
156 try AES256GCM.decrypt(&m2, &c, tag, ad, nonce, key);
157 testing.expectEqualSlices(u8, m[0..], m2[0..]);
158
159 htest.assertEqual("5ca1642d90009fea33d01f78cf6eefaf01", &c);
160 htest.assertEqual("64accec679d444e2373bd9f6796c0d2c", &tag);
161}
lib/std/crypto/benchmark.zig+2
......@@ -152,6 +152,8 @@ const aeads = [_]Crypto{
152152 Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" },
153153 Crypto{ .ty = crypto.aead.AEGIS128L, .name = "aegis-128l" },
154154 Crypto{ .ty = crypto.aead.AEGIS256, .name = "aegis-256" },
155 Crypto{ .ty = crypto.aead.AES128GCM, .name = "aes128-gcm" },
156 Crypto{ .ty = crypto.aead.AES256GCM, .name = "aes256-gcm" },
155157};
156158
157159pub fn benchmarkAead(comptime Aead: anytype, comptime bytes: comptime_int) !u64 {
lib/std/crypto/ghash.zig+15-8
......@@ -250,7 +250,7 @@ pub const Ghash = struct {
250250 }
251251 mb = mb[want..];
252252 st.leftover += want;
253 if (st.leftover > block_size) {
253 if (st.leftover < block_size) {
254254 return;
255255 }
256256 st.blocks(&st.buf);
......@@ -269,14 +269,21 @@ pub const Ghash = struct {
269269 }
270270 }
271271
272 pub fn final(st: *Ghash, out: *[mac_length]u8) void {
273 if (st.leftover > 0) {
274 var i = st.leftover;
275 while (i < block_size) : (i += 1) {
276 st.buf[i] = 0;
277 }
278 st.blocks(&st.buf);
272 /// Zero-pad to align the next input to the first byte of a block
273 pub fn pad(st: *Ghash) void {
274 if (st.leftover == 0) {
275 return;
279276 }
277 var i = st.leftover;
278 while (i < block_size) : (i += 1) {
279 st.buf[i] = 0;
280 }
281 st.blocks(&st.buf);
282 st.leftover = 0;
283 }
284
285 pub fn final(st: *Ghash, out: *[mac_length]u8) void {
286 st.pad();
280287 mem.writeIntBig(u64, out[0..8], st.y1);
281288 mem.writeIntBig(u64, out[8..16], st.y0);
282289
lib/std/crypto/poly1305.zig+14-1
......@@ -91,7 +91,7 @@ pub const Poly1305 = struct {
9191 }
9292 mb = mb[want..];
9393 st.leftover += want;
94 if (st.leftover > block_size) {
94 if (st.leftover < block_size) {
9595 return;
9696 }
9797 st.blocks(&st.buf, false);
......@@ -114,6 +114,19 @@ pub const Poly1305 = struct {
114114 }
115115 }
116116
117 /// Zero-pad to align the next input to the first byte of a block
118 pub fn pad(st: *Poly1305) void {
119 if (st.leftover == 0) {
120 return;
121 }
122 var i = st.leftover;
123 while (i < block_size) : (i += 1) {
124 st.buf[i] = 0;
125 }
126 st.blocks(&st.buf);
127 st.leftover = 0;
128 }
129
117130 pub fn final(st: *Poly1305, out: *[mac_length]u8) void {
118131 if (st.leftover > 0) {
119132 var i = st.leftover;