authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2023-04-14 11:20:36+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-04-14 11:20:36+02:00
logb42562be74ee7b88e39edc757643444de63d2df5
treeaad2b4508d87ff230fc9967d611c181929da377b
parent56d800ff7e65897d8ff9ede8e8194af4d08f0df5
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

std.crypto.aegis: support 256-bit tags (#15276)


2 files changed, 243 insertions(+), 193 deletions(-)

lib/std/crypto.zig+2
...@@ -4,7 +4,9 @@ const root = @import("root");...@@ -4,7 +4,9 @@ const root = @import("root");
4pub const aead = struct {4pub const aead = struct {
5 pub const aegis = struct {5 pub const aegis = struct {
6 pub const Aegis128L = @import("crypto/aegis.zig").Aegis128L;6 pub const Aegis128L = @import("crypto/aegis.zig").Aegis128L;
7 pub const Aegis128L_256 = @import("crypto/aegis.zig").Aegis128L_256;
7 pub const Aegis256 = @import("crypto/aegis.zig").Aegis256;8 pub const Aegis256 = @import("crypto/aegis.zig").Aegis256;
9 pub const Aegis256_256 = @import("crypto/aegis.zig").Aegis256_256;
8 };10 };
911
10 pub const aes_gcm = struct {12 pub const aes_gcm = struct {
lib/std/crypto/aegis.zig+241-193
...@@ -1,9 +1,39 @@...@@ -1,9 +1,39 @@
1//! AEGIS is a very fast authenticated encryption system built on top of the core AES function.
2//!
3//! The AEGIS-128L variant has a 128 bit key, a 128 bit nonce, and processes 256 bit message blocks.
4//! The AEGIS-256 variant has a 256 bit key, a 256 bit nonce, and processes 128 bit message blocks.
5//!
6//! The AEGIS cipher family offers performance that significantly exceeds that of AES-GCM with
7//! hardware support for parallelizable AES block encryption.
8//!
9//! Unlike with AES-GCM, nonces can be safely chosen at random with no practical limit when using AEGIS-256.
10//! AEGIS-128L also allows for more messages to be safely encrypted when using random nonces.
11//!
12//! AEGIS is believed to be key-committing, making it a safer choice than most other AEADs
13//! when the key has low entropy, or can be controlled by an attacker.
14//!
15//! Finally, leaking the state does not leak the key.
16//!
17//! https://datatracker.ietf.org/doc/draft-irtf-cfrg-aegis-aead/
18
1const std = @import("std");19const std = @import("std");
2const mem = std.mem;20const mem = std.mem;
3const assert = std.debug.assert;21const assert = std.debug.assert;
4const AesBlock = std.crypto.core.aes.Block;22const AesBlock = std.crypto.core.aes.Block;
5const AuthenticationError = std.crypto.errors.AuthenticationError;23const AuthenticationError = std.crypto.errors.AuthenticationError;
624
25/// AEGIS-128L with a 128-bit authentication tag.
26pub const Aegis128L = Aegis128LGeneric(128);
27
28/// AEGIS-128L with a 256-bit authentication tag.
29pub const Aegis128L_256 = Aegis128LGeneric(256);
30
31/// AEGIS-256 with a 128-bit authentication tag.
32pub const Aegis256 = Aegis256Generic(128);
33
34/// AEGIS-256 with a 256-bit authentication tag.
35pub const Aegis256_256 = Aegis256Generic(256);
36
7const State128L = struct {37const State128L = struct {
8 blocks: [8]AesBlock,38 blocks: [8]AesBlock,
939
...@@ -72,7 +102,7 @@ const State128L = struct {...@@ -72,7 +102,7 @@ const State128L = struct {
72 state.update(msg0, msg1);102 state.update(msg0, msg1);
73 }103 }
74104
75 fn mac(state: *State128L, adlen: usize, mlen: usize) [16]u8 {105 fn mac(state: *State128L, comptime tag_bits: u9, adlen: usize, mlen: usize) [tag_bits / 8]u8 {
76 const blocks = &state.blocks;106 const blocks = &state.blocks;
77 var sizes: [16]u8 = undefined;107 var sizes: [16]u8 = undefined;
78 mem.writeIntLittle(u64, sizes[0..8], adlen * 8);108 mem.writeIntLittle(u64, sizes[0..8], adlen * 8);
...@@ -82,103 +112,109 @@ const State128L = struct {...@@ -82,103 +112,109 @@ const State128L = struct {
82 while (i < 7) : (i += 1) {112 while (i < 7) : (i += 1) {
83 state.update(tmp, tmp);113 state.update(tmp, tmp);
84 }114 }
85 return blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4])115 return switch (tag_bits) {
86 .xorBlocks(blocks[5]).xorBlocks(blocks[6]).toBytes();116 128 => blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3])
117 .xorBlocks(blocks[4]).xorBlocks(blocks[5]).xorBlocks(blocks[6]).toBytes(),
118 256 => tag: {
119 const t1 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]);
120 const t2 = blocks[4].xorBlocks(blocks[5]).xorBlocks(blocks[6]).xorBlocks(blocks[7]);
121 break :tag t1.toBytes() ++ t2.toBytes();
122 },
123 else => unreachable,
124 };
87 }125 }
88};126};
89127
90/// AEGIS is a very fast authenticated encryption system built on top of the core AES function.128fn Aegis128LGeneric(comptime tag_bits: u9) type {
91///129 comptime assert(tag_bits == 128 or tag_bits == 256); // tag must be 128 or 256 bits
92/// The 128L variant of AEGIS has a 128 bit key, a 128 bit nonce, and processes 256 bit message blocks.
93/// It was designed to fully exploit the parallelism and built-in AES support of recent Intel and ARM CPUs.
94///
95/// https://datatracker.ietf.org/doc/draft-irtf-cfrg-aegis-aead/
96pub const Aegis128L = struct {
97 pub const tag_length = 16;
98 pub const nonce_length = 16;
99 pub const key_length = 16;
100 pub const block_length = 32;
101
102 const State = State128L;
103
104 /// c: ciphertext: output buffer should be of size m.len
105 /// tag: authentication tag: output MAC
106 /// m: message
107 /// ad: Associated Data
108 /// npub: public nonce
109 /// k: private key
110 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void {
111 assert(c.len == m.len);
112 var state = State128L.init(key, npub);
113 var src: [32]u8 align(16) = undefined;
114 var dst: [32]u8 align(16) = undefined;
115 var i: usize = 0;
116 while (i + 32 <= ad.len) : (i += 32) {
117 state.absorb(ad[i..][0..32]);
118 }
119 if (ad.len % 32 != 0) {
120 mem.set(u8, src[0..], 0);
121 mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]);
122 state.absorb(&src);
123 }
124 i = 0;
125 while (i + 32 <= m.len) : (i += 32) {
126 state.enc(c[i..][0..32], m[i..][0..32]);
127 }
128 if (m.len % 32 != 0) {
129 mem.set(u8, src[0..], 0);
130 mem.copy(u8, src[0 .. m.len % 32], m[i .. i + m.len % 32]);
131 state.enc(&dst, &src);
132 mem.copy(u8, c[i .. i + m.len % 32], dst[0 .. m.len % 32]);
133 }
134 tag.* = state.mac(ad.len, m.len);
135 }
136130
137 /// m: message: output buffer should be of size c.len131 return struct {
138 /// c: ciphertext132 pub const tag_length = tag_bits / 8;
139 /// tag: authentication tag133 pub const nonce_length = 16;
140 /// ad: Associated Data134 pub const key_length = 16;
141 /// npub: public nonce135 pub const block_length = 32;
142 /// k: private key136
143 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 {137 const State = State128L;
144 assert(c.len == m.len);138
145 var state = State128L.init(key, npub);139 /// c: ciphertext: output buffer should be of size m.len
146 var src: [32]u8 align(16) = undefined;140 /// tag: authentication tag: output MAC
147 var dst: [32]u8 align(16) = undefined;141 /// m: message
148 var i: usize = 0;142 /// ad: Associated Data
149 while (i + 32 <= ad.len) : (i += 32) {143 /// npub: public nonce
150 state.absorb(ad[i..][0..32]);144 /// k: private key
151 }145 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void {
152 if (ad.len % 32 != 0) {146 assert(c.len == m.len);
153 mem.set(u8, src[0..], 0);147 var state = State128L.init(key, npub);
154 mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]);148 var src: [32]u8 align(16) = undefined;
155 state.absorb(&src);149 var dst: [32]u8 align(16) = undefined;
156 }150 var i: usize = 0;
157 i = 0;151 while (i + 32 <= ad.len) : (i += 32) {
158 while (i + 32 <= m.len) : (i += 32) {152 state.absorb(ad[i..][0..32]);
159 state.dec(m[i..][0..32], c[i..][0..32]);153 }
160 }154 if (ad.len % 32 != 0) {
161 if (m.len % 32 != 0) {155 mem.set(u8, src[0..], 0);
162 mem.set(u8, src[0..], 0);156 mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]);
163 mem.copy(u8, src[0 .. m.len % 32], c[i .. i + m.len % 32]);157 state.absorb(&src);
164 state.dec(&dst, &src);158 }
165 mem.copy(u8, m[i .. i + m.len % 32], dst[0 .. m.len % 32]);159 i = 0;
166 mem.set(u8, dst[0 .. m.len % 32], 0);160 while (i + 32 <= m.len) : (i += 32) {
167 const blocks = &state.blocks;161 state.enc(c[i..][0..32], m[i..][0..32]);
168 blocks[0] = blocks[0].xorBlocks(AesBlock.fromBytes(dst[0..16]));162 }
169 blocks[4] = blocks[4].xorBlocks(AesBlock.fromBytes(dst[16..32]));163 if (m.len % 32 != 0) {
170 }164 mem.set(u8, src[0..], 0);
171 const computed_tag = state.mac(ad.len, m.len);165 mem.copy(u8, src[0 .. m.len % 32], m[i .. i + m.len % 32]);
172 var acc: u8 = 0;166 state.enc(&dst, &src);
173 for (computed_tag, 0..) |_, j| {167 mem.copy(u8, c[i .. i + m.len % 32], dst[0 .. m.len % 32]);
174 acc |= (computed_tag[j] ^ tag[j]);168 }
175 }169 tag.* = state.mac(tag_bits, ad.len, m.len);
176 if (acc != 0) {170 }
177 @memset(m.ptr, undefined, m.len);171
178 return error.AuthenticationFailed;172 /// m: message: output buffer should be of size c.len
173 /// c: ciphertext
174 /// tag: authentication tag
175 /// ad: Associated Data
176 /// npub: public nonce
177 /// k: private key
178 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 {
179 assert(c.len == m.len);
180 var state = State128L.init(key, npub);
181 var src: [32]u8 align(16) = undefined;
182 var dst: [32]u8 align(16) = undefined;
183 var i: usize = 0;
184 while (i + 32 <= ad.len) : (i += 32) {
185 state.absorb(ad[i..][0..32]);
186 }
187 if (ad.len % 32 != 0) {
188 mem.set(u8, src[0..], 0);
189 mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]);
190 state.absorb(&src);
191 }
192 i = 0;
193 while (i + 32 <= m.len) : (i += 32) {
194 state.dec(m[i..][0..32], c[i..][0..32]);
195 }
196 if (m.len % 32 != 0) {
197 mem.set(u8, src[0..], 0);
198 mem.copy(u8, src[0 .. m.len % 32], c[i .. i + m.len % 32]);
199 state.dec(&dst, &src);
200 mem.copy(u8, m[i .. i + m.len % 32], dst[0 .. m.len % 32]);
201 mem.set(u8, dst[0 .. m.len % 32], 0);
202 const blocks = &state.blocks;
203 blocks[0] = blocks[0].xorBlocks(AesBlock.fromBytes(dst[0..16]));
204 blocks[4] = blocks[4].xorBlocks(AesBlock.fromBytes(dst[16..32]));
205 }
206 const computed_tag = state.mac(tag_bits, ad.len, m.len);
207 var acc: u8 = 0;
208 for (computed_tag, 0..) |_, j| {
209 acc |= (computed_tag[j] ^ tag[j]);
210 }
211 if (acc != 0) {
212 @memset(m.ptr, undefined, m.len);
213 return error.AuthenticationFailed;
214 }
179 }215 }
180 }216 };
181};217}
182218
183const State256 = struct {219const State256 = struct {
184 blocks: [6]AesBlock,220 blocks: [6]AesBlock,
...@@ -243,7 +279,7 @@ const State256 = struct {...@@ -243,7 +279,7 @@ const State256 = struct {
243 state.update(msg);279 state.update(msg);
244 }280 }
245281
246 fn mac(state: *State256, adlen: usize, mlen: usize) [16]u8 {282 fn mac(state: *State256, comptime tag_bits: u9, adlen: usize, mlen: usize) [tag_bits / 8]u8 {
247 const blocks = &state.blocks;283 const blocks = &state.blocks;
248 var sizes: [16]u8 = undefined;284 var sizes: [16]u8 = undefined;
249 mem.writeIntLittle(u64, sizes[0..8], adlen * 8);285 mem.writeIntLittle(u64, sizes[0..8], adlen * 8);
...@@ -253,8 +289,16 @@ const State256 = struct {...@@ -253,8 +289,16 @@ const State256 = struct {
253 while (i < 7) : (i += 1) {289 while (i < 7) : (i += 1) {
254 state.update(tmp);290 state.update(tmp);
255 }291 }
256 return blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4])292 return switch (tag_bits) {
257 .xorBlocks(blocks[5]).toBytes();293 128 => blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3])
294 .xorBlocks(blocks[4]).xorBlocks(blocks[5]).toBytes(),
295 256 => tag: {
296 const t1 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]);
297 const t2 = blocks[3].xorBlocks(blocks[4]).xorBlocks(blocks[5]);
298 break :tag t1.toBytes() ++ t2.toBytes();
299 },
300 else => unreachable,
301 };
258 }302 }
259};303};
260304
...@@ -263,111 +307,115 @@ const State256 = struct {...@@ -263,111 +307,115 @@ const State256 = struct {
263/// The 256 bit variant of AEGIS has a 256 bit key, a 256 bit nonce, and processes 128 bit message blocks.307/// The 256 bit variant of AEGIS has a 256 bit key, a 256 bit nonce, and processes 128 bit message blocks.
264///308///
265/// https://datatracker.ietf.org/doc/draft-irtf-cfrg-aegis-aead/309/// https://datatracker.ietf.org/doc/draft-irtf-cfrg-aegis-aead/
266pub const Aegis256 = struct {310fn Aegis256Generic(comptime tag_bits: u9) type {
267 pub const tag_length = 16;311 comptime assert(tag_bits == 128 or tag_bits == 256); // tag must be 128 or 256 bits
268 pub const nonce_length = 32;
269 pub const key_length = 32;
270 pub const block_length = 16;
271
272 const State = State256;
273
274 /// c: ciphertext: output buffer should be of size m.len
275 /// tag: authentication tag: output MAC
276 /// m: message
277 /// ad: Associated Data
278 /// npub: public nonce
279 /// k: private key
280 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void {
281 assert(c.len == m.len);
282 var state = State256.init(key, npub);
283 var src: [16]u8 align(16) = undefined;
284 var dst: [16]u8 align(16) = undefined;
285 var i: usize = 0;
286 while (i + 16 <= ad.len) : (i += 16) {
287 state.enc(&dst, ad[i..][0..16]);
288 }
289 if (ad.len % 16 != 0) {
290 mem.set(u8, src[0..], 0);
291 mem.copy(u8, src[0 .. ad.len % 16], ad[i .. i + ad.len % 16]);
292 state.enc(&dst, &src);
293 }
294 i = 0;
295 while (i + 16 <= m.len) : (i += 16) {
296 state.enc(c[i..][0..16], m[i..][0..16]);
297 }
298 if (m.len % 16 != 0) {
299 mem.set(u8, src[0..], 0);
300 mem.copy(u8, src[0 .. m.len % 16], m[i .. i + m.len % 16]);
301 state.enc(&dst, &src);
302 mem.copy(u8, c[i .. i + m.len % 16], dst[0 .. m.len % 16]);
303 }
304 tag.* = state.mac(ad.len, m.len);
305 }
306312
307 /// m: message: output buffer should be of size c.len313 return struct {
308 /// c: ciphertext314 pub const tag_length = tag_bits / 8;
309 /// tag: authentication tag315 pub const nonce_length = 32;
310 /// ad: Associated Data316 pub const key_length = 32;
311 /// npub: public nonce317 pub const block_length = 16;
312 /// k: private key318
313 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 {319 const State = State256;
314 assert(c.len == m.len);320
315 var state = State256.init(key, npub);321 /// c: ciphertext: output buffer should be of size m.len
316 var src: [16]u8 align(16) = undefined;322 /// tag: authentication tag: output MAC
317 var dst: [16]u8 align(16) = undefined;323 /// m: message
318 var i: usize = 0;324 /// ad: Associated Data
319 while (i + 16 <= ad.len) : (i += 16) {325 /// npub: public nonce
320 state.enc(&dst, ad[i..][0..16]);326 /// k: private key
321 }327 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void {
322 if (ad.len % 16 != 0) {328 assert(c.len == m.len);
323 mem.set(u8, src[0..], 0);329 var state = State256.init(key, npub);
324 mem.copy(u8, src[0 .. ad.len % 16], ad[i .. i + ad.len % 16]);330 var src: [16]u8 align(16) = undefined;
325 state.enc(&dst, &src);331 var dst: [16]u8 align(16) = undefined;
326 }332 var i: usize = 0;
327 i = 0;333 while (i + 16 <= ad.len) : (i += 16) {
328 while (i + 16 <= m.len) : (i += 16) {334 state.enc(&dst, ad[i..][0..16]);
329 state.dec(m[i..][0..16], c[i..][0..16]);335 }
330 }336 if (ad.len % 16 != 0) {
331 if (m.len % 16 != 0) {337 mem.set(u8, src[0..], 0);
332 mem.set(u8, src[0..], 0);338 mem.copy(u8, src[0 .. ad.len % 16], ad[i .. i + ad.len % 16]);
333 mem.copy(u8, src[0 .. m.len % 16], c[i .. i + m.len % 16]);339 state.enc(&dst, &src);
334 state.dec(&dst, &src);340 }
335 mem.copy(u8, m[i .. i + m.len % 16], dst[0 .. m.len % 16]);341 i = 0;
336 mem.set(u8, dst[0 .. m.len % 16], 0);342 while (i + 16 <= m.len) : (i += 16) {
337 const blocks = &state.blocks;343 state.enc(c[i..][0..16], m[i..][0..16]);
338 blocks[0] = blocks[0].xorBlocks(AesBlock.fromBytes(&dst));344 }
339 }345 if (m.len % 16 != 0) {
340 const computed_tag = state.mac(ad.len, m.len);346 mem.set(u8, src[0..], 0);
341 var acc: u8 = 0;347 mem.copy(u8, src[0 .. m.len % 16], m[i .. i + m.len % 16]);
342 for (computed_tag, 0..) |_, j| {348 state.enc(&dst, &src);
343 acc |= (computed_tag[j] ^ tag[j]);349 mem.copy(u8, c[i .. i + m.len % 16], dst[0 .. m.len % 16]);
344 }350 }
345 if (acc != 0) {351 tag.* = state.mac(tag_bits, ad.len, m.len);
346 @memset(m.ptr, undefined, m.len);352 }
347 return error.AuthenticationFailed;353
354 /// m: message: output buffer should be of size c.len
355 /// c: ciphertext
356 /// tag: authentication tag
357 /// ad: Associated Data
358 /// npub: public nonce
359 /// k: private key
360 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 {
361 assert(c.len == m.len);
362 var state = State256.init(key, npub);
363 var src: [16]u8 align(16) = undefined;
364 var dst: [16]u8 align(16) = undefined;
365 var i: usize = 0;
366 while (i + 16 <= ad.len) : (i += 16) {
367 state.enc(&dst, ad[i..][0..16]);
368 }
369 if (ad.len % 16 != 0) {
370 mem.set(u8, src[0..], 0);
371 mem.copy(u8, src[0 .. ad.len % 16], ad[i .. i + ad.len % 16]);
372 state.enc(&dst, &src);
373 }
374 i = 0;
375 while (i + 16 <= m.len) : (i += 16) {
376 state.dec(m[i..][0..16], c[i..][0..16]);
377 }
378 if (m.len % 16 != 0) {
379 mem.set(u8, src[0..], 0);
380 mem.copy(u8, src[0 .. m.len % 16], c[i .. i + m.len % 16]);
381 state.dec(&dst, &src);
382 mem.copy(u8, m[i .. i + m.len % 16], dst[0 .. m.len % 16]);
383 mem.set(u8, dst[0 .. m.len % 16], 0);
384 const blocks = &state.blocks;
385 blocks[0] = blocks[0].xorBlocks(AesBlock.fromBytes(&dst));
386 }
387 const computed_tag = state.mac(tag_bits, ad.len, m.len);
388 var acc: u8 = 0;
389 for (computed_tag, 0..) |_, j| {
390 acc |= (computed_tag[j] ^ tag[j]);
391 }
392 if (acc != 0) {
393 @memset(m.ptr, undefined, m.len);
394 return error.AuthenticationFailed;
395 }
348 }396 }
349 }397 };
350};398}
351399
352/// The AEGIS-128L message authentication function outputs 128 bit tags.400/// The `Aegis128LMac` message authentication function outputs 256 bit tags.
353/// In addition to being extremely fast, its large state, non-linearity401/// In addition to being extremely fast, its large state, non-linearity
354/// and non-invertibility provides the following properties:402/// and non-invertibility provides the following properties:
355/// - 128 bit security, stronger than GHash/Polyval/Poly1305.403/// - 128 bit security, stronger than GHash/Polyval/Poly1305.
356/// - Recovering the secret key from the state would require ~2^128 attempts,404/// - Recovering the secret key from the state would require ~2^128 attempts,
357/// which is infeasible for any practical adversary.405/// which is infeasible for any practical adversary.
358/// - It has a large security margin against internal collisions.406/// - It has a large security margin against internal collisions.
359pub const Aegis128LMac = AegisMac(Aegis128L);407pub const Aegis128LMac = AegisMac(Aegis128L_256);
360408
361/// The AEGIS-256 message authentication function has a 256-bit key size,409/// The `Aegis256Mac` message authentication function has a 256-bit key size,
362/// but outputs 128 bit tags. Unless theoretical multi-target attacks are a410/// and outputs 256 bit tags. Unless theoretical multi-target attacks are a
363/// concern, the AEGIS-128L variant should be preferred.411/// concern, the AEGIS-128L variant should be preferred.
364/// AEGIS' large state, non-linearity and non-invertibility provides the412/// AEGIS' large state, non-linearity and non-invertibility provides the
365/// following properties:413/// following properties:
366/// - 128 bit security, stronger than GHash/Polyval/Poly1305.414/// - 256 bit security against forgery.
367/// - Recovering the secret key from the state would require ~2^128 attempts,415/// - Recovering the secret key from the state would require ~2^256 attempts,
368/// which is infeasible for any practical adversary.416/// which is infeasible for any practical adversary.
369/// - It has a large security margin against internal collisions.417/// - It has a large security margin against internal collisions.
370pub const Aegis256Mac = AegisMac(Aegis256);418pub const Aegis256Mac = AegisMac(Aegis256_256);
371419
372fn AegisMac(comptime T: type) type {420fn AegisMac(comptime T: type) type {
373 return struct {421 return struct {
...@@ -420,7 +468,7 @@ fn AegisMac(comptime T: type) type {...@@ -420,7 +468,7 @@ fn AegisMac(comptime T: type) type {
420 mem.copy(u8, pad[0..], self.buf[0..self.off]);468 mem.copy(u8, pad[0..], self.buf[0..self.off]);
421 self.state.absorb(&pad);469 self.state.absorb(&pad);
422 }470 }
423 out.* = self.state.mac(self.msg_len, 0);471 out.* = self.state.mac(T.tag_length * 8, self.msg_len, 0);
424 }472 }
425473
426 /// Return an authentication tag for a message and a key474 /// Return an authentication tag for a message and a key
...@@ -572,23 +620,23 @@ test "Aegis MAC" {...@@ -572,23 +620,23 @@ test "Aegis MAC" {
572 st.update(msg[0..32]);620 st.update(msg[0..32]);
573 st.update(msg[32..]);621 st.update(msg[32..]);
574 st.final(&tag);622 st.final(&tag);
575 try htest.assertEqual("b4e8e46cee04a401ec67bad73df4aa60", &tag);623 try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag);
576624
577 st = st_init;625 st = st_init;
578 st.update(msg[0..31]);626 st.update(msg[0..31]);
579 st.update(msg[31..]);627 st.update(msg[31..]);
580 st.final(&tag);628 st.final(&tag);
581 try htest.assertEqual("b4e8e46cee04a401ec67bad73df4aa60", &tag);629 try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag);
582630
583 st = st_init;631 st = st_init;
584 st.update(msg[0..14]);632 st.update(msg[0..14]);
585 st.update(msg[14..30]);633 st.update(msg[14..30]);
586 st.update(msg[30..]);634 st.update(msg[30..]);
587 st.final(&tag);635 st.final(&tag);
588 try htest.assertEqual("b4e8e46cee04a401ec67bad73df4aa60", &tag);636 try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag);
589637
590 var empty: [0]u8 = undefined;638 var empty: [0]u8 = undefined;
591 const nonce = [_]u8{0x00} ** Aegis128L.nonce_length;639 const nonce = [_]u8{0x00} ** Aegis128L_256.nonce_length;
592 Aegis128L.encrypt(&empty, &tag, &empty, &msg, nonce, key);640 Aegis128L_256.encrypt(&empty, &tag, &empty, &msg, nonce, key);
593 try htest.assertEqual("b4e8e46cee04a401ec67bad73df4aa60", &tag);641 try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag);
594}642}