| ... | ... | @@ -25,12 +25,24 @@ fn Rp(a: usize, b: usize, c: usize, d: usize) QuarterRound { |
| 25 | 25 | }; |
| 26 | 26 | } |
| 27 | 27 | |
| 28 | | // The chacha family of ciphers are based on the salsa family. |
| 29 | | fn salsa20_wordtobyte(out: []u8, input: [16]u32) void { |
| 30 | | assert(out.len >= 64); |
| 28 | fn initContext(key: [8]u32, d: [4]u32) [16]u32 { |
| 29 | var ctx: [16]u32 = undefined; |
| 30 | const c = "expand 32-byte k"; |
| 31 | const constant_le = comptime [_]u32{ |
| 32 | mem.readIntLittle(u32, c[0..4]), |
| 33 | mem.readIntLittle(u32, c[4..8]), |
| 34 | mem.readIntLittle(u32, c[8..12]), |
| 35 | mem.readIntLittle(u32, c[12..16]), |
| 36 | }; |
| 37 | mem.copy(u32, ctx[0..], constant_le[0..4]); |
| 38 | mem.copy(u32, ctx[4..12], key[0..8]); |
| 39 | mem.copy(u32, ctx[12..16], d[0..4]); |
| 31 | 40 | |
| 32 | | var x: [16]u32 = undefined; |
| 41 | return ctx; |
| 42 | } |
| 33 | 43 | |
| 44 | // The chacha family of ciphers are based on the salsa family. |
| 45 | fn chacha20Core(x: []u32, input: [16]u32) void { |
| 34 | 46 | for (x) |_, i| |
| 35 | 47 | x[i] = input[i]; |
| 36 | 48 | |
| ... | ... | @@ -59,33 +71,27 @@ fn salsa20_wordtobyte(out: []u8, input: [16]u32) void { |
| 59 | 71 | x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 7)); |
| 60 | 72 | } |
| 61 | 73 | } |
| 74 | } |
| 62 | 75 | |
| 76 | fn hashToBytes(out: []u8, x: [16]u32) void { |
| 63 | 77 | for (x) |_, i| { |
| 64 | | mem.writeIntLittle(u32, out[4 * i ..][0..4], x[i] +% input[i]); |
| 78 | mem.writeIntLittle(u32, out[4 * i ..][0..4], x[i]); |
| 65 | 79 | } |
| 66 | 80 | } |
| 67 | 81 | |
| 68 | 82 | fn chaCha20_internal(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void { |
| 69 | | var ctx: [16]u32 = undefined; |
| 83 | var ctx = initContext(key, counter); |
| 70 | 84 | var remaining: usize = if (in.len > out.len) in.len else out.len; |
| 71 | 85 | var cursor: usize = 0; |
| 72 | 86 | |
| 73 | | const c = "expand 32-byte k"; |
| 74 | | const constant_le = [_]u32{ |
| 75 | | mem.readIntLittle(u32, c[0..4]), |
| 76 | | mem.readIntLittle(u32, c[4..8]), |
| 77 | | mem.readIntLittle(u32, c[8..12]), |
| 78 | | mem.readIntLittle(u32, c[12..16]), |
| 79 | | }; |
| 80 | | |
| 81 | | mem.copy(u32, ctx[0..], constant_le[0..4]); |
| 82 | | mem.copy(u32, ctx[4..12], key[0..8]); |
| 83 | | mem.copy(u32, ctx[12..16], counter[0..4]); |
| 84 | | |
| 85 | 87 | while (true) { |
| 88 | var x: [16]u32 = undefined; |
| 86 | 89 | var buf: [64]u8 = undefined; |
| 87 | | salsa20_wordtobyte(buf[0..], ctx); |
| 88 | | |
| 90 | chacha20Core(x[0..], ctx); |
| 91 | for (x) |_, i| { |
| 92 | x[i] +%= ctx[i]; |
| 93 | } |
| 94 | hashToBytes(buf[0..], x); |
| 89 | 95 | if (remaining < 64) { |
| 90 | 96 | var i: usize = 0; |
| 91 | 97 | while (i < remaining) : (i += 1) |
| ... | ... | @@ -104,6 +110,20 @@ fn chaCha20_internal(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) vo |
| 104 | 110 | } |
| 105 | 111 | } |
| 106 | 112 | |
| 113 | fn keyToWords(key: [32]u8) [8]u32 { |
| 114 | var k: [8]u32 = undefined; |
| 115 | k[0] = mem.readIntLittle(u32, key[0..4]); |
| 116 | k[1] = mem.readIntLittle(u32, key[4..8]); |
| 117 | k[2] = mem.readIntLittle(u32, key[8..12]); |
| 118 | k[3] = mem.readIntLittle(u32, key[12..16]); |
| 119 | k[4] = mem.readIntLittle(u32, key[16..20]); |
| 120 | k[5] = mem.readIntLittle(u32, key[20..24]); |
| 121 | k[6] = mem.readIntLittle(u32, key[24..28]); |
| 122 | k[7] = mem.readIntLittle(u32, key[28..32]); |
| 123 | |
| 124 | return k; |
| 125 | } |
| 126 | |
| 107 | 127 | /// ChaCha20 avoids the possibility of timing attacks, as there are no branches |
| 108 | 128 | /// on secret key data. |
| 109 | 129 | /// |
| ... | ... | @@ -116,23 +136,12 @@ pub fn chaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: |
| 116 | 136 | assert(in.len >= out.len); |
| 117 | 137 | assert((in.len >> 6) + counter <= maxInt(u32)); |
| 118 | 138 | |
| 119 | | var k: [8]u32 = undefined; |
| 120 | 139 | var c: [4]u32 = undefined; |
| 121 | | |
| 122 | | k[0] = mem.readIntLittle(u32, key[0..4]); |
| 123 | | k[1] = mem.readIntLittle(u32, key[4..8]); |
| 124 | | k[2] = mem.readIntLittle(u32, key[8..12]); |
| 125 | | k[3] = mem.readIntLittle(u32, key[12..16]); |
| 126 | | k[4] = mem.readIntLittle(u32, key[16..20]); |
| 127 | | k[5] = mem.readIntLittle(u32, key[20..24]); |
| 128 | | k[6] = mem.readIntLittle(u32, key[24..28]); |
| 129 | | k[7] = mem.readIntLittle(u32, key[28..32]); |
| 130 | | |
| 131 | 140 | c[0] = counter; |
| 132 | 141 | c[1] = mem.readIntLittle(u32, nonce[0..4]); |
| 133 | 142 | c[2] = mem.readIntLittle(u32, nonce[4..8]); |
| 134 | 143 | c[3] = mem.readIntLittle(u32, nonce[8..12]); |
| 135 | | chaCha20_internal(out, in, k, c); |
| 144 | chaCha20_internal(out, in, keyToWords(key), c); |
| 136 | 145 | } |
| 137 | 146 | |
| 138 | 147 | /// This is the original ChaCha20 before RFC 7539, which recommends using the |
| ... | ... | @@ -143,18 +152,8 @@ pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32] |
| 143 | 152 | assert(counter +% (in.len >> 6) >= counter); |
| 144 | 153 | |
| 145 | 154 | var cursor: usize = 0; |
| 146 | | var k: [8]u32 = undefined; |
| 155 | const k = keyToWords(key); |
| 147 | 156 | var c: [4]u32 = undefined; |
| 148 | | |
| 149 | | k[0] = mem.readIntLittle(u32, key[0..4]); |
| 150 | | k[1] = mem.readIntLittle(u32, key[4..8]); |
| 151 | | k[2] = mem.readIntLittle(u32, key[8..12]); |
| 152 | | k[3] = mem.readIntLittle(u32, key[12..16]); |
| 153 | | k[4] = mem.readIntLittle(u32, key[16..20]); |
| 154 | | k[5] = mem.readIntLittle(u32, key[20..24]); |
| 155 | | k[6] = mem.readIntLittle(u32, key[24..28]); |
| 156 | | k[7] = mem.readIntLittle(u32, key[28..32]); |
| 157 | | |
| 158 | 157 | c[0] = @truncate(u32, counter); |
| 159 | 158 | c[1] = @truncate(u32, counter >> 32); |
| 160 | 159 | c[2] = mem.readIntLittle(u32, nonce[0..4]); |
| ... | ... | @@ -437,15 +436,15 @@ test "crypto.chacha20 test vector 5" { |
| 437 | 436 | |
| 438 | 437 | pub const chacha20poly1305_tag_size = 16; |
| 439 | 438 | |
| 440 | | pub fn chacha20poly1305Seal(dst: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void { |
| 441 | | assert(dst.len >= plaintext.len + chacha20poly1305_tag_size); |
| 439 | pub fn chacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_size]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void { |
| 440 | assert(ciphertext.len >= plaintext.len); |
| 442 | 441 | |
| 443 | 442 | // derive poly1305 key |
| 444 | 443 | var polyKey = [_]u8{0} ** 32; |
| 445 | 444 | chaCha20IETF(polyKey[0..], polyKey[0..], 0, key, nonce); |
| 446 | 445 | |
| 447 | 446 | // encrypt plaintext |
| 448 | | chaCha20IETF(dst[0..plaintext.len], plaintext, 1, key, nonce); |
| 447 | chaCha20IETF(ciphertext[0..plaintext.len], plaintext, 1, key, nonce); |
| 449 | 448 | |
| 450 | 449 | // construct mac |
| 451 | 450 | var mac = Poly1305.init(polyKey[0..]); |
| ... | ... | @@ -455,7 +454,7 @@ pub fn chacha20poly1305Seal(dst: []u8, plaintext: []const u8, data: []const u8, |
| 455 | 454 | const padding = 16 - (data.len % 16); |
| 456 | 455 | mac.update(zeros[0..padding]); |
| 457 | 456 | } |
| 458 | | mac.update(dst[0..plaintext.len]); |
| 457 | mac.update(ciphertext[0..plaintext.len]); |
| 459 | 458 | if (plaintext.len % 16 != 0) { |
| 460 | 459 | const zeros = [_]u8{0} ** 16; |
| 461 | 460 | const padding = 16 - (plaintext.len % 16); |
| ... | ... | @@ -465,19 +464,17 @@ pub fn chacha20poly1305Seal(dst: []u8, plaintext: []const u8, data: []const u8, |
| 465 | 464 | mem.writeIntLittle(u64, lens[0..8], data.len); |
| 466 | 465 | mem.writeIntLittle(u64, lens[8..16], plaintext.len); |
| 467 | 466 | mac.update(lens[0..]); |
| 468 | | mac.final(dst[plaintext.len..]); |
| 467 | mac.final(tag); |
| 469 | 468 | } |
| 470 | 469 | |
| 471 | | /// Verifies and decrypts an authenticated message produced by chacha20poly1305Seal. |
| 472 | | pub fn chacha20poly1305Open(dst: []u8, msgAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void { |
| 473 | | if (msgAndTag.len < chacha20poly1305_tag_size) { |
| 474 | | return error.InvalidMessage; |
| 475 | | } |
| 470 | pub fn chacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void { |
| 471 | return chacha20poly1305SealDetached(ciphertextAndTag[0..plaintext.len], ciphertextAndTag[plaintext.len..][0..chacha20poly1305_tag_size], plaintext, data, key, nonce); |
| 472 | } |
| 476 | 473 | |
| 474 | /// Verifies and decrypts an authenticated message produced by chacha20poly1305SealDetached. |
| 475 | pub fn chacha20poly1305OpenDetached(dst: []u8, ciphertext: []const u8, tag: *const [chacha20poly1305_tag_size]u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void { |
| 477 | 476 | // split ciphertext and tag |
| 478 | | assert(dst.len >= msgAndTag.len - chacha20poly1305_tag_size); |
| 479 | | var ciphertext = msgAndTag[0 .. msgAndTag.len - chacha20poly1305_tag_size]; |
| 480 | | var polyTag = msgAndTag[ciphertext.len..]; |
| 477 | assert(dst.len >= ciphertext.len); |
| 481 | 478 | |
| 482 | 479 | // derive poly1305 key |
| 483 | 480 | var polyKey = [_]u8{0} ** 32; |
| ... | ... | @@ -510,7 +507,7 @@ pub fn chacha20poly1305Open(dst: []u8, msgAndTag: []const u8, data: []const u8, |
| 510 | 507 | // See https://github.com/ziglang/zig/issues/1776 |
| 511 | 508 | var acc: u8 = 0; |
| 512 | 509 | for (computedTag) |_, i| { |
| 513 | | acc |= (computedTag[i] ^ polyTag[i]); |
| 510 | acc |= (computedTag[i] ^ tag.*[i]); |
| 514 | 511 | } |
| 515 | 512 | if (acc != 0) { |
| 516 | 513 | return error.AuthenticationFailed; |
| ... | ... | @@ -520,6 +517,75 @@ pub fn chacha20poly1305Open(dst: []u8, msgAndTag: []const u8, data: []const u8, |
| 520 | 517 | chaCha20IETF(dst[0..ciphertext.len], ciphertext, 1, key, nonce); |
| 521 | 518 | } |
| 522 | 519 | |
| 520 | /// Verifies and decrypts an authenticated message produced by chacha20poly1305Seal. |
| 521 | pub fn chacha20poly1305Open(dst: []u8, ciphertextAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void { |
| 522 | if (ciphertextAndTag.len < chacha20poly1305_tag_size) { |
| 523 | return error.InvalidMessage; |
| 524 | } |
| 525 | const ciphertextLen = ciphertextAndTag.len - chacha20poly1305_tag_size; |
| 526 | return try chacha20poly1305OpenDetached(dst, ciphertextAndTag[0..ciphertextLen], ciphertextAndTag[ciphertextLen..][0..chacha20poly1305_tag_size], data, key, nonce); |
| 527 | } |
| 528 | |
| 529 | fn hchacha20(input: [16]u8, key: [32]u8) [32]u8 { |
| 530 | var c: [4]u32 = undefined; |
| 531 | for (c) |_, i| { |
| 532 | c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]); |
| 533 | } |
| 534 | const ctx = initContext(keyToWords(key), c); |
| 535 | var x: [16]u32 = undefined; |
| 536 | chacha20Core(x[0..], ctx); |
| 537 | var out: [32]u8 = undefined; |
| 538 | mem.writeIntLittle(u32, out[0..4], x[0]); |
| 539 | mem.writeIntLittle(u32, out[4..8], x[1]); |
| 540 | mem.writeIntLittle(u32, out[8..12], x[2]); |
| 541 | mem.writeIntLittle(u32, out[12..16], x[3]); |
| 542 | mem.writeIntLittle(u32, out[16..20], x[12]); |
| 543 | mem.writeIntLittle(u32, out[20..24], x[13]); |
| 544 | mem.writeIntLittle(u32, out[24..28], x[14]); |
| 545 | mem.writeIntLittle(u32, out[28..32], x[15]); |
| 546 | |
| 547 | return out; |
| 548 | } |
| 549 | |
| 550 | fn extend(key: [32]u8, nonce: [24]u8) struct { key: [32]u8, nonce: [12]u8 } { |
| 551 | var subnonce: [12]u8 = undefined; |
| 552 | mem.set(u8, subnonce[0..4], 0); |
| 553 | mem.copy(u8, subnonce[4..], nonce[16..24]); |
| 554 | return .{ |
| 555 | .key = hchacha20(nonce[0..16].*, key), |
| 556 | .nonce = subnonce, |
| 557 | }; |
| 558 | } |
| 559 | |
| 560 | pub fn xChaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [24]u8) void { |
| 561 | const extended = extend(key, nonce); |
| 562 | chaCha20IETF(out, in, counter, extended.key, extended.nonce); |
| 563 | } |
| 564 | |
| 565 | pub const xchacha20poly1305_tag_size = 16; |
| 566 | |
| 567 | pub fn xchacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_size]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) void { |
| 568 | const extended = extend(key, nonce); |
| 569 | return chacha20poly1305SealDetached(ciphertext, tag, plaintext, data, extended.key, extended.nonce); |
| 570 | } |
| 571 | |
| 572 | pub fn xchacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) void { |
| 573 | const extended = extend(key, nonce); |
| 574 | return chacha20poly1305Seal(ciphertextAndTag, plaintext, data, extended.key, extended.nonce); |
| 575 | } |
| 576 | |
| 577 | /// Verifies and decrypts an authenticated message produced by xchacha20poly1305SealDetached. |
| 578 | pub fn xchacha20poly1305OpenDetached(plaintext: []u8, ciphertext: []const u8, tag: *const [chacha20poly1305_tag_size]u8, data: []const u8, key: [32]u8, nonce: [24]u8) !void { |
| 579 | const extended = extend(key, nonce); |
| 580 | return try chacha20poly1305OpenDetached(plaintext, ciphertext, tag, data, extended.key, extended.nonce); |
| 581 | } |
| 582 | |
| 583 | /// Verifies and decrypts an authenticated message produced by xchacha20poly1305Seal. |
| 584 | pub fn xchacha20poly1305Open(ciphertextAndTag: []u8, msgAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) !void { |
| 585 | const extended = extend(key, nonce); |
| 586 | return try chacha20poly1305Open(ciphertextAndTag, msgAndTag, data, extended.key, extended.nonce); |
| 587 | } |
| 588 | |
| 523 | 589 | test "seal" { |
| 524 | 590 | { |
| 525 | 591 | const plaintext = ""; |
| ... | ... | @@ -636,3 +702,105 @@ test "open" { |
| 636 | 702 | testing.expectError(error.InvalidMessage, chacha20poly1305Open(out[0..], "", data[0..], key, bad_nonce)); |
| 637 | 703 | } |
| 638 | 704 | } |
| 705 | |
| 706 | test "crypto.xchacha20" { |
| 707 | const key = [_]u8{69} ** 32; |
| 708 | const nonce = [_]u8{42} ** 24; |
| 709 | const input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; |
| 710 | { |
| 711 | var ciphertext: [input.len]u8 = undefined; |
| 712 | xChaCha20IETF(ciphertext[0..], input[0..], 0, key, nonce); |
| 713 | var buf: [2 * ciphertext.len]u8 = undefined; |
| 714 | testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{ciphertext}), "E0A1BCF939654AFDBDC1746EC49832647C19D891F0D1A81FC0C1703B4514BDEA584B512F6908C2C5E9DD18D5CBC1805DE5803FE3B9CA5F193FB8359E91FAB0C3BB40309A292EB1CF49685C65C4A3ADF4F11DB0CD2B6B67FBC174BC2E860E8F769FD3565BBFAD1C845E05A0FED9BE167C240D"); |
| 715 | } |
| 716 | { |
| 717 | const data = "Additional data"; |
| 718 | var ciphertext: [input.len + xchacha20poly1305_tag_size]u8 = undefined; |
| 719 | xchacha20poly1305Seal(ciphertext[0..], input, data, key, nonce); |
| 720 | var out: [input.len]u8 = undefined; |
| 721 | try xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce); |
| 722 | var buf: [2 * ciphertext.len]u8 = undefined; |
| 723 | testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{ciphertext}), "994D2DD32333F48E53650C02C7A2ABB8E018B0836D7175AEC779F52E961780768F815C58F1AA52D211498DB89B9216763F569C9433A6BBFCEFB4D4A49387A4C5207FBB3B5A92B5941294DF30588C6740D39DC16FA1F0E634F7246CF7CDCB978E44347D89381B7A74EB7084F754B90BDE9AAF5A94B8F2A85EFD0B50692AE2D425E234"); |
| 724 | testing.expectEqualSlices(u8, out[0..], input); |
| 725 | ciphertext[0] += 1; |
| 726 | testing.expectError(error.AuthenticationFailed, xchacha20poly1305Open(out[0..], ciphertext[0..], data, key, nonce)); |
| 727 | } |
| 728 | } |
| 729 | |
| 730 | pub const Chacha20Poly1305 = struct { |
| 731 | pub const tag_length = 16; |
| 732 | pub const nonce_length = 12; |
| 733 | pub const key_length = 32; |
| 734 | |
| 735 | /// c: ciphertext: output buffer should be of size m.len |
| 736 | /// at: authentication tag: output MAC |
| 737 | /// m: message |
| 738 | /// ad: Associated Data |
| 739 | /// npub: public nonce |
| 740 | /// k: private key |
| 741 | pub fn encrypt(c: []u8, at: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void { |
| 742 | assert(c.len == m.len); |
| 743 | return chacha20poly1305SealDetached(c, at, m, ad, k, npub); |
| 744 | } |
| 745 | |
| 746 | /// m: message: output buffer should be of size c.len |
| 747 | /// c: ciphertext |
| 748 | /// at: authentication tag |
| 749 | /// ad: Associated Data |
| 750 | /// npub: public nonce |
| 751 | /// k: private key |
| 752 | /// NOTE: the check of the authentication tag is currently not done in constant time |
| 753 | pub fn decrypt(m: []u8, c: []const u8, at: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void { |
| 754 | assert(c.len == m.len); |
| 755 | return try chacha20poly1305OpenDetached(m, c, at[0..], ad, k, npub); |
| 756 | } |
| 757 | }; |
| 758 | |
| 759 | pub const XChacha20Poly1305 = struct { |
| 760 | pub const tag_length = 16; |
| 761 | pub const nonce_length = 24; |
| 762 | pub const key_length = 32; |
| 763 | |
| 764 | /// c: ciphertext: output buffer should be of size m.len |
| 765 | /// at: authentication tag: output MAC |
| 766 | /// m: message |
| 767 | /// ad: Associated Data |
| 768 | /// npub: public nonce |
| 769 | /// k: private key |
| 770 | pub fn encrypt(c: []u8, at: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void { |
| 771 | assert(c.len == m.len); |
| 772 | return xchacha20poly1305SealDetached(c, at, m, ad, k, npub); |
| 773 | } |
| 774 | |
| 775 | /// m: message: output buffer should be of size c.len |
| 776 | /// c: ciphertext |
| 777 | /// at: authentication tag |
| 778 | /// ad: Associated Data |
| 779 | /// npub: public nonce |
| 780 | /// k: private key |
| 781 | /// NOTE: the check of the authentication tag is currently not done in constant time |
| 782 | pub fn decrypt(m: []u8, c: []const u8, at: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void { |
| 783 | assert(c.len == m.len); |
| 784 | return try xchacha20poly1305OpenDetached(m, c, at[0..], ad, k, npub); |
| 785 | } |
| 786 | }; |
| 787 | |
| 788 | test "chacha20 AEAD API" { |
| 789 | const aeads = [_]type{ Chacha20Poly1305, XChacha20Poly1305 }; |
| 790 | const input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; |
| 791 | const data = "Additional data"; |
| 792 | |
| 793 | inline for (aeads) |aead| { |
| 794 | const key = [_]u8{69} ** aead.key_length; |
| 795 | const nonce = [_]u8{42} ** aead.nonce_length; |
| 796 | var ciphertext: [input.len]u8 = undefined; |
| 797 | var tag: [aead.tag_length]u8 = undefined; |
| 798 | var out: [input.len]u8 = undefined; |
| 799 | |
| 800 | aead.encrypt(ciphertext[0..], tag[0..], input, data, nonce, key); |
| 801 | try aead.decrypt(out[0..], ciphertext[0..], tag, data[0..], nonce, key); |
| 802 | testing.expectEqualSlices(u8, out[0..], input); |
| 803 | ciphertext[0] += 1; |
| 804 | testing.expectError(error.AuthenticationFailed, aead.decrypt(out[0..], ciphertext[0..], tag, data[0..], nonce, key)); |
| 805 | } |
| 806 | } |