| ... | @@ -42,6 +42,12 @@ const State128L = struct { | ... | @@ -42,6 +42,12 @@ const State128L = struct { |
| 42 | blocks[4] = blocks[4].xorBlocks(d2); | 42 | blocks[4] = blocks[4].xorBlocks(d2); |
| 43 | } | 43 | } |
| 44 | | 44 | |
| | 45 | fn absorb(state: *State128L, src: *const [32]u8) void { |
| | 46 | const msg0 = AesBlock.fromBytes(src[0..16]); |
| | 47 | const msg1 = AesBlock.fromBytes(src[16..32]); |
| | 48 | state.update(msg0, msg1); |
| | 49 | } |
| | 50 | |
| 45 | fn enc(state: *State128L, dst: *[32]u8, src: *const [32]u8) void { | 51 | fn enc(state: *State128L, dst: *[32]u8, src: *const [32]u8) void { |
| 46 | const blocks = &state.blocks; | 52 | const blocks = &state.blocks; |
| 47 | const msg0 = AesBlock.fromBytes(src[0..16]); | 53 | const msg0 = AesBlock.fromBytes(src[0..16]); |
| ... | @@ -86,11 +92,14 @@ const State128L = struct { | ... | @@ -86,11 +92,14 @@ const State128L = struct { |
| 86 | /// The 128L variant of AEGIS has a 128 bit key, a 128 bit nonce, and processes 256 bit message blocks. | 92 | /// The 128L variant of AEGIS has a 128 bit key, a 128 bit nonce, and processes 256 bit message blocks. |
| 87 | /// It was designed to fully exploit the parallelism and built-in AES support of recent Intel and ARM CPUs. | 93 | /// It was designed to fully exploit the parallelism and built-in AES support of recent Intel and ARM CPUs. |
| 88 | /// | 94 | /// |
| 89 | /// https://competitions.cr.yp.to/round3/aegisv11.pdf | 95 | /// https://datatracker.ietf.org/doc/draft-irtf-cfrg-aegis-aead/ |
| 90 | pub const Aegis128L = struct { | 96 | pub const Aegis128L = struct { |
| 91 | pub const tag_length = 16; | 97 | pub const tag_length = 16; |
| 92 | pub const nonce_length = 16; | 98 | pub const nonce_length = 16; |
| 93 | pub const key_length = 16; | 99 | pub const key_length = 16; |
| | 100 | pub const block_length = 32; |
| | 101 | |
| | 102 | const State = State128L; |
| 94 | | 103 | |
| 95 | /// c: ciphertext: output buffer should be of size m.len | 104 | /// c: ciphertext: output buffer should be of size m.len |
| 96 | /// tag: authentication tag: output MAC | 105 | /// tag: authentication tag: output MAC |
| ... | @@ -105,12 +114,12 @@ pub const Aegis128L = struct { | ... | @@ -105,12 +114,12 @@ pub const Aegis128L = struct { |
| 105 | var dst: [32]u8 align(16) = undefined; | 114 | var dst: [32]u8 align(16) = undefined; |
| 106 | var i: usize = 0; | 115 | var i: usize = 0; |
| 107 | while (i + 32 <= ad.len) : (i += 32) { | 116 | while (i + 32 <= ad.len) : (i += 32) { |
| 108 | state.enc(&dst, ad[i..][0..32]); | 117 | state.absorb(ad[i..][0..32]); |
| 109 | } | 118 | } |
| 110 | if (ad.len % 32 != 0) { | 119 | if (ad.len % 32 != 0) { |
| 111 | mem.set(u8, src[0..], 0); | 120 | mem.set(u8, src[0..], 0); |
| 112 | mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]); | 121 | mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]); |
| 113 | state.enc(&dst, &src); | 122 | state.absorb(&src); |
| 114 | } | 123 | } |
| 115 | i = 0; | 124 | i = 0; |
| 116 | while (i + 32 <= m.len) : (i += 32) { | 125 | while (i + 32 <= m.len) : (i += 32) { |
| ... | @@ -138,12 +147,12 @@ pub const Aegis128L = struct { | ... | @@ -138,12 +147,12 @@ pub const Aegis128L = struct { |
| 138 | var dst: [32]u8 align(16) = undefined; | 147 | var dst: [32]u8 align(16) = undefined; |
| 139 | var i: usize = 0; | 148 | var i: usize = 0; |
| 140 | while (i + 32 <= ad.len) : (i += 32) { | 149 | while (i + 32 <= ad.len) : (i += 32) { |
| 141 | state.enc(&dst, ad[i..][0..32]); | 150 | state.absorb(ad[i..][0..32]); |
| 142 | } | 151 | } |
| 143 | if (ad.len % 32 != 0) { | 152 | if (ad.len % 32 != 0) { |
| 144 | mem.set(u8, src[0..], 0); | 153 | mem.set(u8, src[0..], 0); |
| 145 | mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]); | 154 | mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]); |
| 146 | state.enc(&dst, &src); | 155 | state.absorb(&src); |
| 147 | } | 156 | } |
| 148 | i = 0; | 157 | i = 0; |
| 149 | while (i + 32 <= m.len) : (i += 32) { | 158 | while (i + 32 <= m.len) : (i += 32) { |
| ... | @@ -212,6 +221,11 @@ const State256 = struct { | ... | @@ -212,6 +221,11 @@ const State256 = struct { |
| 212 | blocks[0] = tmp.xorBlocks(d); | 221 | blocks[0] = tmp.xorBlocks(d); |
| 213 | } | 222 | } |
| 214 | | 223 | |
| | 224 | fn absorb(state: *State256, src: *const [16]u8) void { |
| | 225 | const msg = AesBlock.fromBytes(src); |
| | 226 | state.update(msg); |
| | 227 | } |
| | 228 | |
| 215 | fn enc(state: *State256, dst: *[16]u8, src: *const [16]u8) void { | 229 | fn enc(state: *State256, dst: *[16]u8, src: *const [16]u8) void { |
| 216 | const blocks = &state.blocks; | 230 | const blocks = &state.blocks; |
| 217 | const msg = AesBlock.fromBytes(src); | 231 | const msg = AesBlock.fromBytes(src); |
| ... | @@ -248,11 +262,14 @@ const State256 = struct { | ... | @@ -248,11 +262,14 @@ const State256 = struct { |
| 248 | /// | 262 | /// |
| 249 | /// The 256 bit variant of AEGIS has a 256 bit key, a 256 bit nonce, and processes 128 bit message blocks. | 263 | /// The 256 bit variant of AEGIS has a 256 bit key, a 256 bit nonce, and processes 128 bit message blocks. |
| 250 | /// | 264 | /// |
| 251 | /// https://competitions.cr.yp.to/round3/aegisv11.pdf | 265 | /// https://datatracker.ietf.org/doc/draft-irtf-cfrg-aegis-aead/ |
| 252 | pub const Aegis256 = struct { | 266 | pub const Aegis256 = struct { |
| 253 | pub const tag_length = 16; | 267 | pub const tag_length = 16; |
| 254 | pub const nonce_length = 32; | 268 | pub const nonce_length = 32; |
| 255 | pub const key_length = 32; | 269 | pub const key_length = 32; |
| | 270 | pub const block_length = 16; |
| | 271 | |
| | 272 | const State = State256; |
| 256 | | 273 | |
| 257 | /// c: ciphertext: output buffer should be of size m.len | 274 | /// c: ciphertext: output buffer should be of size m.len |
| 258 | /// tag: authentication tag: output MAC | 275 | /// tag: authentication tag: output MAC |
| ... | @@ -332,6 +349,101 @@ pub const Aegis256 = struct { | ... | @@ -332,6 +349,101 @@ pub const Aegis256 = struct { |
| 332 | } | 349 | } |
| 333 | }; | 350 | }; |
| 334 | | 351 | |
| | 352 | /// The AEGIS-128L message authentication function outputs 128 bit tags. |
| | 353 | /// In addition to being extremely fast, its large state, non-linearity |
| | 354 | /// and non-invertibility provides the following properties: |
| | 355 | /// - 128 bit security, stronger than GHash/Polyval/Poly1305. |
| | 356 | /// - Recovering the secret key from the state would require ~2^128 attempts, |
| | 357 | /// which is infeasible for any practical adversary. |
| | 358 | /// - It has a large security margin against internal collisions. |
| | 359 | pub const Aegis128LMac = AegisMac(Aegis128L); |
| | 360 | |
| | 361 | /// The AEGIS-256 message authentication function has a 256-bit key size, |
| | 362 | /// but outputs 128 bit tags. Unless theoretical multi-target attacks are a |
| | 363 | /// concern, the AEGIS-128L variant should be preferred. |
| | 364 | /// AEGIS' large state, non-linearity and non-invertibility provides the |
| | 365 | /// following properties: |
| | 366 | /// - 128 bit security, stronger than GHash/Polyval/Poly1305. |
| | 367 | /// - Recovering the secret key from the state would require ~2^128 attempts, |
| | 368 | /// which is infeasible for any practical adversary. |
| | 369 | /// - It has a large security margin against internal collisions. |
| | 370 | pub const Aegis256Mac = AegisMac(Aegis256); |
| | 371 | |
| | 372 | fn AegisMac(comptime T: type) type { |
| | 373 | return struct { |
| | 374 | const Self = @This(); |
| | 375 | |
| | 376 | pub const mac_length = T.tag_length; |
| | 377 | pub const key_length = T.key_length; |
| | 378 | pub const block_length = T.block_length; |
| | 379 | |
| | 380 | state: T.State, |
| | 381 | buf: [block_length]u8 = undefined, |
| | 382 | off: usize = 0, |
| | 383 | msg_len: usize = 0, |
| | 384 | |
| | 385 | /// Initialize a state for the MAC function |
| | 386 | pub fn init(key: *const [key_length]u8) Self { |
| | 387 | const nonce = [_]u8{0} ** T.nonce_length; |
| | 388 | return Self{ |
| | 389 | .state = T.State.init(key.*, nonce), |
| | 390 | }; |
| | 391 | } |
| | 392 | |
| | 393 | /// Add data to the state |
| | 394 | pub fn update(self: *Self, b: []const u8) void { |
| | 395 | self.msg_len += b.len; |
| | 396 | |
| | 397 | const len_partial = @min(b.len, block_length - self.off); |
| | 398 | mem.copy(u8, self.buf[self.off..][0..len_partial], b[0..len_partial]); |
| | 399 | self.off += len_partial; |
| | 400 | if (self.off < block_length) { |
| | 401 | return; |
| | 402 | } |
| | 403 | self.state.absorb(&self.buf); |
| | 404 | |
| | 405 | var i = len_partial; |
| | 406 | self.off = 0; |
| | 407 | while (i + block_length <= b.len) : (i += block_length) { |
| | 408 | self.state.absorb(b[i..][0..block_length]); |
| | 409 | } |
| | 410 | if (i != b.len) { |
| | 411 | mem.copy(u8, self.buf[0..], b[i..]); |
| | 412 | self.off = b.len - i; |
| | 413 | } |
| | 414 | } |
| | 415 | |
| | 416 | /// Return an authentication tag for the current state |
| | 417 | pub fn final(self: *Self, out: *[mac_length]u8) void { |
| | 418 | if (self.off > 0) { |
| | 419 | var pad = [_]u8{0} ** block_length; |
| | 420 | mem.copy(u8, pad[0..], self.buf[0..self.off]); |
| | 421 | self.state.absorb(&pad); |
| | 422 | } |
| | 423 | out.* = self.state.mac(self.msg_len, 0); |
| | 424 | } |
| | 425 | |
| | 426 | /// Return an authentication tag for a message and a key |
| | 427 | pub fn create(out: *[mac_length]u8, msg: []const u8, key: *const [key_length]u8) void { |
| | 428 | var ctx = Self.init(key); |
| | 429 | ctx.update(msg); |
| | 430 | ctx.final(out); |
| | 431 | } |
| | 432 | |
| | 433 | pub const Error = error{}; |
| | 434 | pub const Writer = std.io.Writer(*Self, Error, write); |
| | 435 | |
| | 436 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| | 437 | self.update(bytes); |
| | 438 | return bytes.len; |
| | 439 | } |
| | 440 | |
| | 441 | pub fn writer(self: *Self) Writer { |
| | 442 | return .{ .context = self }; |
| | 443 | } |
| | 444 | }; |
| | 445 | } |
| | 446 | |
| 335 | const htest = @import("test.zig"); | 447 | const htest = @import("test.zig"); |
| 336 | const testing = std.testing; | 448 | const testing = std.testing; |
| 337 | | 449 | |
| ... | @@ -446,3 +558,37 @@ test "Aegis256 test vector 3" { | ... | @@ -446,3 +558,37 @@ test "Aegis256 test vector 3" { |
| 446 | | 558 | |
| 447 | try htest.assertEqual("f7a0878f68bd083e8065354071fc27c3", &tag); | 559 | try htest.assertEqual("f7a0878f68bd083e8065354071fc27c3", &tag); |
| 448 | } | 560 | } |
| | 561 | |
| | 562 | test "Aegis MAC" { |
| | 563 | const key = [_]u8{0x00} ** Aegis128LMac.key_length; |
| | 564 | var msg: [64]u8 = undefined; |
| | 565 | for (msg) |*m, i| { |
| | 566 | m.* = @truncate(u8, i); |
| | 567 | } |
| | 568 | const st_init = Aegis128LMac.init(&key); |
| | 569 | var st = st_init; |
| | 570 | var tag: [Aegis128LMac.mac_length]u8 = undefined; |
| | 571 | |
| | 572 | st.update(msg[0..32]); |
| | 573 | st.update(msg[32..]); |
| | 574 | st.final(&tag); |
| | 575 | try htest.assertEqual("b4e8e46cee04a401ec67bad73df4aa60", &tag); |
| | 576 | |
| | 577 | st = st_init; |
| | 578 | st.update(msg[0..31]); |
| | 579 | st.update(msg[31..]); |
| | 580 | st.final(&tag); |
| | 581 | try htest.assertEqual("b4e8e46cee04a401ec67bad73df4aa60", &tag); |
| | 582 | |
| | 583 | st = st_init; |
| | 584 | st.update(msg[0..14]); |
| | 585 | st.update(msg[14..30]); |
| | 586 | st.update(msg[30..]); |
| | 587 | st.final(&tag); |
| | 588 | try htest.assertEqual("b4e8e46cee04a401ec67bad73df4aa60", &tag); |
| | 589 | |
| | 590 | var empty: [0]u8 = undefined; |
| | 591 | const nonce = [_]u8{0x00} ** Aegis128L.nonce_length; |
| | 592 | Aegis128L.encrypt(&empty, &tag, &empty, &msg, nonce, key); |
| | 593 | try htest.assertEqual("b4e8e46cee04a401ec67bad73df4aa60", &tag); |
| | 594 | } |