From 0ae582b733b235819381bd72243bd8b994eaf7ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20=C5=A0pinka?= Date: Sat, 1 Feb 2025 17:19:19 +0100 Subject: [PATCH] Refactor tests into a separate module. --- build.zig | 54 +- src/primitive/blockcipher/aes.zig | 674 ++++-------------- src/primitive/blockcipher/des.zig | 4 - src/primitive/blockcipher/index.zig | 1 + src/primitive/blockcipher/mode_cbc.zig | 167 +++++ src/primitive/blockcipher/padding.zig | 10 +- src/primitive/digest/sha_1.zig | 66 +- src/primitive/digest/sha_2.zig | 330 ++------- src/primitive/digest/sha_core.zig | 45 -- src/primitive/streamcipher/chacha20.zig | 281 ++------ src/root.zig | 1 + src/utility/byte_operations.zig | 21 + src/utility/index.zig | 1 + src/test.zig => test/index.zig | 6 +- test/primitive/blockcipher/aes.zig | 322 +++++++++ .../primitive/blockcipher/operation_modes.zig | 78 ++ test/primitive/digest/sha.zig | 327 +++++++++ test/primitive/streamcipher/chacha20.zig | 129 ++++ test/primitive/streamcipher/salsa20.zig | 0 19 files changed, 1348 insertions(+), 1169 deletions(-) create mode 100644 src/primitive/blockcipher/mode_cbc.zig create mode 100644 src/utility/byte_operations.zig create mode 100644 src/utility/index.zig rename src/test.zig => test/index.zig (55%) create mode 100644 test/primitive/blockcipher/aes.zig create mode 100644 test/primitive/blockcipher/operation_modes.zig create mode 100644 test/primitive/digest/sha.zig create mode 100644 test/primitive/streamcipher/chacha20.zig create mode 100644 test/primitive/streamcipher/salsa20.zig diff --git a/build.zig b/build.zig index 6b18dca..8802832 100644 --- a/build.zig +++ b/build.zig @@ -15,10 +15,13 @@ pub fn build(b: *std.Build) void { // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); + // Define a private module for importing source files in tests. + const src_module = b.createModule(.{ + .root_source_file = .{ .cwd_relative = "src/root.zig" }, + }); + const lib = b.addStaticLibrary(.{ .name = "crypto", - // In this case the main source file is merely a path, however, in more - // complicated build scripts, this could be a generated file. .root_source_file = b.path("src/root.zig"), .target = target, .optimize = optimize, @@ -29,63 +32,20 @@ pub fn build(b: *std.Build) void { // running `zig build`). b.installArtifact(lib); - //const exe = b.addExecutable(.{ - // .name = "crypto", - // .root_source_file = b.path("src/main.zig"), - // .target = target, - // .optimize = optimize, - //}); - - // This declares intent for the executable to be installed into the - // standard location when the user invokes the "install" step (the default - // step when running `zig build`). - //b.installArtifact(exe); - - // This *creates* a Run step in the build graph, to be executed when another - // step is evaluated that depends on it. The next line below will establish - // such a dependency. - //const run_cmd = b.addRunArtifact(exe); - - // By making the run step depend on the install step, it will be run from the - // installation directory rather than directly from within the cache directory. - // This is not necessary, however, if the application depends on other installed - // files, this ensures they will be present and in the expected location. - //run_cmd.step.dependOn(b.getInstallStep()); - - // This allows the user to pass arguments to the application in the build - // command itself, like this: `zig build run -- arg1 arg2 etc` - //if (b.args) |args| { - // run_cmd.addArgs(args); - //} - - // This creates a build step. It will be visible in the `zig build --help` menu, - // and can be selected like this: `zig build run` - // This will evaluate the `run` step rather than the default, which is "install". - //const run_step = b.step("run", "Run the app"); - //run_step.dependOn(&run_cmd.step); - // Creates a step for unit testing. This only builds the test executable // but does not run it. const lib_unit_tests = b.addTest(.{ - .root_source_file = b.path("src/test.zig"), + .root_source_file = b.path("test/index.zig"), .target = target, .optimize = optimize, }); + lib_unit_tests.root_module.addImport("ziggy", src_module); const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); - //const exe_unit_tests = b.addTest(.{ - // .root_source_file = b.path("src/main.zig"), - // .target = target, - // .optimize = optimize, - //}); - - //const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); - // Similar to creating the run step earlier, this exposes a `test` step to // the `zig build --help` menu, providing a way for the user to request // running the unit tests. const test_step = b.step("test", "Run unit tests"); test_step.dependOn(&run_lib_unit_tests.step); - //test_step.dependOn(&run_exe_unit_tests.step); } diff --git a/src/primitive/blockcipher/aes.zig b/src/primitive/blockcipher/aes.zig index 2f45913..dcc54f1 100644 --- a/src/primitive/blockcipher/aes.zig +++ b/src/primitive/blockcipher/aes.zig @@ -1,93 +1,45 @@ const std = @import("std"); const testing = std.testing; -// ----------------------------------- AES CONSTANTS ----------------------------------- // +const byte_operations = @import("../../utility/byte_operations.zig"); +const word_to_bytes = byte_operations.word_to_bytes_be; +const bytes_to_word = byte_operations.bytes_to_word_be; -pub const AES_BLOCK_SIZE = 128 / 8; +// ----------------------------------- PUBLIC CONSTANTS ----------------------------------- // -pub const Aes128Parameters = struct { - pub const KEY_SIZE = 128 / 8; - pub const N_ROUNDS = 10; -}; -pub const Aes192Parameters = struct { - pub const KEY_SIZE = 192 / 8; - pub const N_ROUNDS = 12; -}; -pub const Aes256Parameters = struct { - pub const KEY_SIZE = 256 / 8; - pub const N_ROUNDS = 14; -}; +pub const BLOCK_SIZE = 128 / 8; -const AES_SBOX = [_]u8{ - 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, - 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, - 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, - 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, - 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, - 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, - 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, - 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, - 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, - 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, - 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, - 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, - 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, - 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, - 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, - 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, -}; +pub const KEY_SIZE_128 = 128 / 8; +pub const KEY_SIZE_192 = 192 / 8; +pub const KEY_SIZE_256 = 256 / 8; -const AES_INV_SBOX = [_]u8{ - 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, - 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, - 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, - 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, - 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, - 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, - 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, - 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, - 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, - 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, - 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, - 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, - 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, - 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, - 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, - 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d, -}; +// ----------------------------------- ENCRYPTION/DECRYPTION ----------------------------------- // -const AES_RCON = [_]u32{ - 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, - 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000, -}; - -// ----------------------------------- ENCRYPTION/DECRYPTION ----------------------------------- // - -pub fn aes_encrypt_block( +pub fn encrypt_block( n_rounds: comptime_int, - block_in: *const [AES_BLOCK_SIZE]u8, - block_out: *[AES_BLOCK_SIZE]u8, + block_in: *const [BLOCK_SIZE]u8, + block_out: *[BLOCK_SIZE]u8, expanded_key: *const [4 * (n_rounds + 1)]u32, ) void { // Copy input buffer into state (we're treating the buffer as a column-first matrix). - var state: [AES_BLOCK_SIZE]u8 = undefined; + var state: [BLOCK_SIZE]u8 = undefined; @memcpy(state[0..], block_in); // Initial AddRoundKey. - aes_add_round_key(&state, expanded_key[0..4]); + add_round_key(&state, expanded_key[0..4]); // Nr - 1 identical rounds. for (1..n_rounds) |round| { - aes_sub_bytes(&state); - aes_shift_rows(&state); - aes_mix_columns(&state); - aes_add_round_key(&state, @ptrCast(expanded_key[(4 * round)..(4 * round + 4)])); + sub_bytes(&state); + shift_rows(&state); + mix_columns(&state); + add_round_key(&state, @ptrCast(expanded_key[(4 * round)..(4 * round + 4)])); } // Last round is without MixColumns. - aes_sub_bytes(&state); - aes_shift_rows(&state); - aes_add_round_key(&state, @ptrCast(expanded_key[(4 * n_rounds)..(4 * n_rounds + 4)])); + sub_bytes(&state); + shift_rows(&state); + add_round_key(&state, @ptrCast(expanded_key[(4 * n_rounds)..(4 * n_rounds + 4)])); // Write the result into the destination buffer. @memcpy(block_out, &state); @@ -96,27 +48,32 @@ pub fn aes_encrypt_block( @memset(state[0..], 0); } -pub fn aes_decrypt_block(n_rounds: comptime_int, block_in: *const [AES_BLOCK_SIZE]u8, block_out: *[AES_BLOCK_SIZE]u8, expanded_key: *const [4 * (n_rounds + 1)]u32) void { +pub fn decrypt_block( + n_rounds: comptime_int, + block_in: *const [BLOCK_SIZE]u8, + block_out: *[BLOCK_SIZE]u8, + expanded_key: *const [4 * (n_rounds + 1)]u32, +) void { // Copy input buffer into state (we're treating the buffer as a column-first matrix). - var state: [AES_BLOCK_SIZE]u8 = undefined; + var state: [BLOCK_SIZE]u8 = undefined; @memcpy(state[0..], block_in); // Reverse the AddRoundKey that was applied after the last encryption round. - aes_add_round_key(&state, @ptrCast(expanded_key[(4 * n_rounds)..(4 * n_rounds + 4)])); + add_round_key(&state, @ptrCast(expanded_key[(4 * n_rounds)..(4 * n_rounds + 4)])); // Nr - 1 identical rounds. for (1..n_rounds) |inv_round| { const round = n_rounds - inv_round; - aes_inv_shift_rows(&state); - aes_inv_sub_bytes(&state); - aes_add_round_key(&state, @ptrCast(expanded_key[(4 * round)..(4 * round + 4)])); - aes_inv_mix_columns(&state); + inv_shift_rows(&state); + inv_sub_bytes(&state); + add_round_key(&state, @ptrCast(expanded_key[(4 * round)..(4 * round + 4)])); + inv_mix_columns(&state); } // Finish last round. - aes_inv_shift_rows(&state); - aes_inv_sub_bytes(&state); - aes_add_round_key(&state, expanded_key[0..4]); + inv_shift_rows(&state); + inv_sub_bytes(&state); + add_round_key(&state, expanded_key[0..4]); // Write the result into the destination buffer. @memcpy(block_out, &state); @@ -125,117 +82,87 @@ pub fn aes_decrypt_block(n_rounds: comptime_int, block_in: *const [AES_BLOCK_SIZ @memset(state[0..], 0); } -pub fn aes_128_encrypt_block( - key: *const [Aes128Parameters.KEY_SIZE]u8, - block_in: *const [AES_BLOCK_SIZE]u8, - block_out: *[AES_BLOCK_SIZE]u8, +pub fn aes128_encrypt_block( + key: *const [KEY_SIZE_128]u8, + block_in: *const [BLOCK_SIZE]u8, + block_out: *[BLOCK_SIZE]u8, ) void { // Prepare the subkeys for AddRoundKey. - var expanded_key = aes_128_expand_key(key); + var expanded_key = expand_key_128(key); defer @memset(&expanded_key, 0); // Call the generic encryption procedure. - aes_encrypt_block( - Aes128Parameters.N_ROUNDS, - block_in, - block_out, - &expanded_key, - ); + encrypt_block(N_ROUNDS_128, block_in, block_out, &expanded_key); } -pub fn aes_128_decrypt_block( - key: *const [Aes128Parameters.KEY_SIZE]u8, - block_in: *const [AES_BLOCK_SIZE]u8, - block_out: *[AES_BLOCK_SIZE]u8, +pub fn aes128_decrypt_block( + key: *const [KEY_SIZE_128]u8, + block_in: *const [BLOCK_SIZE]u8, + block_out: *[BLOCK_SIZE]u8, ) void { // Prepare the subkeys for AddRoundKey. - var expanded_key = aes_128_expand_key(key); + var expanded_key = expand_key_128(key); defer @memset(&expanded_key, 0); // Call the generic decryption procedure. - aes_decrypt_block( - Aes128Parameters.N_ROUNDS, - block_in, - block_out, - &expanded_key, - ); + decrypt_block(N_ROUNDS_128, block_in, block_out, &expanded_key); } -pub fn aes_192_encrypt_block( - key: *const [Aes192Parameters.KEY_SIZE]u8, - block_in: *const [AES_BLOCK_SIZE]u8, - block_out: *[AES_BLOCK_SIZE]u8, +pub fn aes192_encrypt_block( + key: *const [KEY_SIZE_192]u8, + block_in: *const [BLOCK_SIZE]u8, + block_out: *[BLOCK_SIZE]u8, ) void { // Prepare the subkeys for AddRoundKey. - var expanded_key = aes_192_expand_key(key); + var expanded_key = expand_key_192(key); defer @memset(&expanded_key, 0); // Call the generic encryption procedure. - aes_encrypt_block( - Aes192Parameters.N_ROUNDS, - block_in, - block_out, - &expanded_key, - ); + encrypt_block(N_ROUNDS_192, block_in, block_out, &expanded_key); } -pub fn aes_192_decrypt_block( - key: *const [Aes192Parameters.KEY_SIZE]u8, - block_in: *const [AES_BLOCK_SIZE]u8, - block_out: *[AES_BLOCK_SIZE]u8, +pub fn aes192_decrypt_block( + key: *const [KEY_SIZE_192]u8, + block_in: *const [BLOCK_SIZE]u8, + block_out: *[BLOCK_SIZE]u8, ) void { // Prepare the subkeys for AddRoundKey. - var expanded_key = aes_192_expand_key(key); + var expanded_key = expand_key_192(key); defer @memset(&expanded_key, 0); // Call the generic decryption procedure. - aes_decrypt_block( - Aes192Parameters.N_ROUNDS, - block_in, - block_out, - &expanded_key, - ); + decrypt_block(N_ROUNDS_192, block_in, block_out, &expanded_key); } -pub fn aes_256_encrypt_block( - key: *const [Aes256Parameters.KEY_SIZE]u8, - block_in: *const [AES_BLOCK_SIZE]u8, - block_out: *[AES_BLOCK_SIZE]u8, +pub fn aes256_encrypt_block( + key: *const [KEY_SIZE_256]u8, + block_in: *const [BLOCK_SIZE]u8, + block_out: *[BLOCK_SIZE]u8, ) void { // Prepare the subkeys for AddRoundKey. - var expanded_key = aes_256_expand_key(key); + var expanded_key = expand_key_256(key); defer @memset(&expanded_key, 0); // Call the generic encryption procedure. - aes_encrypt_block( - Aes256Parameters.N_ROUNDS, - block_in, - block_out, - &expanded_key, - ); + encrypt_block(N_ROUNDS_256, block_in, block_out, &expanded_key); } -pub fn aes_256_decrypt_block( - key: *const [Aes256Parameters.KEY_SIZE]u8, - block_in: *const [AES_BLOCK_SIZE]u8, - block_out: *[AES_BLOCK_SIZE]u8, +pub fn aes256_decrypt_block( + key: *const [KEY_SIZE_256]u8, + block_in: *const [BLOCK_SIZE]u8, + block_out: *[BLOCK_SIZE]u8, ) void { // Prepare the subkeys for AddRoundKey. - var expanded_key = aes_256_expand_key(key); + var expanded_key = expand_key_256(key); defer @memset(&expanded_key, 0); // Call the generic decryption procedure. - aes_decrypt_block( - Aes256Parameters.N_ROUNDS, - block_in, - block_out, - &expanded_key, - ); + decrypt_block(N_ROUNDS_256, block_in, block_out, &expanded_key); } -// ----------------------------------- KEY EXPANSION ----------------------------------- // +// ----------------------------------- KEY EXPANSION ----------------------------------- // -pub fn aes_expand_key( +pub fn expand_key( n_rounds: comptime_int, n_key_words: comptime_int, key: *const [n_key_words * 4]u8, @@ -249,9 +176,9 @@ pub fn aes_expand_key( while (i <= 4 * n_rounds + 3) : (i += 1) { var temp = expanded_key[i - 1]; if (i % n_key_words == 0) { - temp = aes_sub_word(aes_rot_word(temp)) ^ AES_RCON[i / n_key_words - 1]; + temp = sub_word(rot_word(temp)) ^ RCON[i / n_key_words - 1]; } else if (n_key_words > 6 and i % n_key_words == 4) { - temp = aes_sub_word(temp); + temp = sub_word(temp); } expanded_key[i] = expanded_key[i - n_key_words] ^ temp; } @@ -259,48 +186,34 @@ pub fn aes_expand_key( return expanded_key; } -pub fn aes_128_expand_key(key: *const [Aes128Parameters.KEY_SIZE]u8) [4 * (Aes128Parameters.N_ROUNDS + 1)]u32 { - return aes_expand_key( - Aes128Parameters.N_ROUNDS, - Aes128Parameters.KEY_SIZE / 4, - key, - ); +pub fn expand_key_128(key: *const [KEY_SIZE_128]u8) [4 * (N_ROUNDS_128 + 1)]u32 { + return expand_key(N_ROUNDS_128, KEY_SIZE_128 / 4, key); } -pub fn aes_192_expand_key(key: *const [Aes192Parameters.KEY_SIZE]u8) [4 * (Aes192Parameters.N_ROUNDS + 1)]u32 { - return aes_expand_key( - Aes192Parameters.N_ROUNDS, - Aes192Parameters.KEY_SIZE / 4, - key, - ); +pub fn expand_key_192(key: *const [KEY_SIZE_192]u8) [4 * (N_ROUNDS_192 + 1)]u32 { + return expand_key(N_ROUNDS_192, KEY_SIZE_192 / 4, key); } -pub fn aes_256_expand_key(key: *const [Aes256Parameters.KEY_SIZE]u8) [4 * (Aes256Parameters.N_ROUNDS + 1)]u32 { - return aes_expand_key( - Aes256Parameters.N_ROUNDS, - Aes256Parameters.KEY_SIZE / 4, - key, - ); +pub fn expand_key_256(key: *const [KEY_SIZE_256]u8) [4 * (N_ROUNDS_256 + 1)]u32 { + return expand_key(N_ROUNDS_256, KEY_SIZE_256 / 4, key); } -// ----------------------------------- AES OPERATIONS ----------------------------------- // +// ----------------------------------- AES OPERATIONS ----------------------------------- // -fn aes_add_round_key(state: *[AES_BLOCK_SIZE]u8, subkey: *const [4]u32) void { +pub fn add_round_key(state: *[BLOCK_SIZE]u8, subkey: *const [4]u32) void { for (0..4) |wi| { const subkey_bytes = word_to_bytes(subkey[wi]); - for (0..4) |bi| { + for (0..4) |bi| state[wi * 4 + bi] ^= subkey_bytes[bi]; - } } } -fn aes_sub_bytes(state: *[AES_BLOCK_SIZE]u8) void { - for (0..state.len) |i| { - state[i] = AES_SBOX[state[i]]; - } +pub fn sub_bytes(state: *[BLOCK_SIZE]u8) void { + for (0..state.len) |i| + state[i] = SBOX[state[i]]; } -fn aes_shift_rows(state: *[AES_BLOCK_SIZE]u8) void { +pub fn shift_rows(state: *[BLOCK_SIZE]u8) void { var tmp: u8 = undefined; // Note: Since we store the state matrix as an array of columns, @@ -329,7 +242,7 @@ fn aes_shift_rows(state: *[AES_BLOCK_SIZE]u8) void { state[1 * 4 + 3] = tmp; } -fn aes_mix_one_column(column: *[4]u8) void { +fn mix_one_column(column: *[4]u8) void { const c0 = column[0]; const c1 = column[1]; const c2 = column[2]; @@ -341,19 +254,17 @@ fn aes_mix_one_column(column: *[4]u8) void { column[3] = (xtime(c0) ^ c0) ^ c1 ^ c2 ^ xtime(c3); } -fn aes_mix_columns(state: *[AES_BLOCK_SIZE]u8) void { - for (0..4) |i| { - aes_mix_one_column(@ptrCast(state[(4 * i)..(4 * i + 4)])); - } +pub fn mix_columns(state: *[BLOCK_SIZE]u8) void { + for (0..4) |i| + mix_one_column(@ptrCast(state[(4 * i)..(4 * i + 4)])); } -fn aes_inv_sub_bytes(state: *[AES_BLOCK_SIZE]u8) void { - for (0..state.len) |i| { - state[i] = AES_INV_SBOX[state[i]]; - } +pub fn inv_sub_bytes(state: *[BLOCK_SIZE]u8) void { + for (0..state.len) |i| + state[i] = INV_SBOX[state[i]]; } -fn aes_inv_shift_rows(state: *[AES_BLOCK_SIZE]u8) void { +pub fn inv_shift_rows(state: *[BLOCK_SIZE]u8) void { var tmp: u8 = undefined; // Note: Since we store the state matrix as an array of columns, @@ -382,7 +293,7 @@ fn aes_inv_shift_rows(state: *[AES_BLOCK_SIZE]u8) void { state[2 * 4 + 3] = tmp; } -fn aes_inv_mix_one_column(column: *[4]u8) void { +fn inv_mix_one_column(column: *[4]u8) void { const c0 = column[0]; const c1 = column[1]; const c2 = column[2]; @@ -394,26 +305,24 @@ fn aes_inv_mix_one_column(column: *[4]u8) void { column[3] = gfmult(0x0b, c0) ^ gfmult(0x0d, c1) ^ gfmult(0x09, c2) ^ gfmult(0x0e, c3); } -fn aes_inv_mix_columns(state: *[AES_BLOCK_SIZE]u8) void { - for (0..4) |i| { - aes_inv_mix_one_column(@ptrCast(state[(4 * i)..(4 * i + 4)])); - } +pub fn inv_mix_columns(state: *[BLOCK_SIZE]u8) void { + for (0..4) |i| + inv_mix_one_column(@ptrCast(state[(4 * i)..(4 * i + 4)])); } -fn aes_sub_word(word: u32) u32 { +fn sub_word(word: u32) u32 { var bytes = word_to_bytes(word); - for (0..4) |i| { - bytes[i] = AES_SBOX[bytes[i]]; - } + for (0..4) |i| + bytes[i] = SBOX[bytes[i]]; return bytes_to_word(&bytes); } -fn aes_rot_word(word: u32) u32 { +fn rot_word(word: u32) u32 { const bytes = word_to_bytes(word); return bytes_to_word(&.{ bytes[1], bytes[2], bytes[3], bytes[0] }); } -// ----------------------------------- GALOIS FIELD HELPERS ----------------------------------- // +// ----------------------------------- GALOIS FIELD HELPERS ----------------------------------- // fn xtime(element: u8) u8 { return if (element & 0x80 != 0) @@ -444,334 +353,51 @@ fn gfmult(factor: comptime_int, element: u8) u8 { unreachable; } -// ----------------------------------- ENDIANNESS HELPERS ----------------------------------- // +// ----------------------------------- CRYPTOGRAPHIC CONSTANTS ----------------------------------- // -fn word_to_bytes(word: u32) [4]u8 { - var bytes: [4]u8 = undefined; - std.mem.writeInt(u32, &bytes, word, .big); - return bytes; -} +pub const N_ROUNDS_128 = 10; +pub const N_ROUNDS_192 = 12; +pub const N_ROUNDS_256 = 14; -fn bytes_to_word(bytes: *const [4]u8) u32 { - return std.mem.readInt(u32, bytes, .big); -} +pub const SBOX = [_]u8{ + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, +}; -// ----------------------------------- TEST VECTORS ----------------------------------- // +pub const INV_SBOX = [_]u8{ + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d, +}; -test "AES-128 key expansion test vector from FIPS 197 A.1" { - const key = [Aes128Parameters.KEY_SIZE]u8{ - 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, - 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c, - }; - const expected_expansion = [(Aes128Parameters.N_ROUNDS + 1) * 4]u32{ - 0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x09cf4f3c, - 0xa0fafe17, 0x88542cb1, 0x23a33939, 0x2a6c7605, - 0xf2c295f2, 0x7a96b943, 0x5935807a, 0x7359f67f, - 0x3d80477d, 0x4716fe3e, 0x1e237e44, 0x6d7a883b, - 0xef44a541, 0xa8525b7f, 0xb671253b, 0xdb0bad00, - 0xd4d1c6f8, 0x7c839d87, 0xcaf2b8bc, 0x11f915bc, - 0x6d88a37a, 0x110b3efd, 0xdbf98641, 0xca0093fd, - 0x4e54f70e, 0x5f5fc9f3, 0x84a64fb2, 0x4ea6dc4f, - 0xead27321, 0xb58dbad2, 0x312bf560, 0x7f8d292f, - 0xac7766f3, 0x19fadc21, 0x28d12941, 0x575c006e, - 0xd014f9a8, 0xc9ee2589, 0xe13f0cc8, 0xb6630ca6, - }; - try testing.expectEqualSlices(u32, &expected_expansion, &aes_128_expand_key(&key)); -} - -test "AES-192 key expansion test vector from FIPS 197 A.1" { - const key = [Aes192Parameters.KEY_SIZE]u8{ - 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, - 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, - 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b, - }; - const expected_expansion = [(Aes192Parameters.N_ROUNDS + 1) * 4]u32{ - 0x8e73b0f7, 0xda0e6452, 0xc810f32b, 0x809079e5, - 0x62f8ead2, 0x522c6b7b, 0xfe0c91f7, 0x2402f5a5, - 0xec12068e, 0x6c827f6b, 0x0e7a95b9, 0x5c56fec2, - 0x4db7b4bd, 0x69b54118, 0x85a74796, 0xe92538fd, - 0xe75fad44, 0xbb095386, 0x485af057, 0x21efb14f, - 0xa448f6d9, 0x4d6dce24, 0xaa326360, 0x113b30e6, - 0xa25e7ed5, 0x83b1cf9a, 0x27f93943, 0x6a94f767, - 0xc0a69407, 0xd19da4e1, 0xec1786eb, 0x6fa64971, - 0x485f7032, 0x22cb8755, 0xe26d1352, 0x33f0b7b3, - 0x40beeb28, 0x2f18a259, 0x6747d26b, 0x458c553e, - 0xa7e1466c, 0x9411f1df, 0x821f750a, 0xad07d753, - 0xca400538, 0x8fcc5006, 0x282d166a, 0xbc3ce7b5, - 0xe98ba06f, 0x448c773c, 0x8ecc7204, 0x01002202, - }; - try testing.expectEqualSlices(u32, &expected_expansion, &aes_192_expand_key(&key)); -} - -test "AES-256 key expansion test vector from FIPS 197 A.1" { - const key = [Aes256Parameters.KEY_SIZE]u8{ - 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, - 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, - 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, - 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4, - }; - const expected_expansion = [(Aes256Parameters.N_ROUNDS + 1) * 4]u32{ - 0x603deb10, 0x15ca71be, 0x2b73aef0, 0x857d7781, - 0x1f352c07, 0x3b6108d7, 0x2d9810a3, 0x0914dff4, - 0x9ba35411, 0x8e6925af, 0xa51a8b5f, 0x2067fcde, - 0xa8b09c1a, 0x93d194cd, 0xbe49846e, 0xb75d5b9a, - 0xd59aecb8, 0x5bf3c917, 0xfee94248, 0xde8ebe96, - 0xb5a9328a, 0x2678a647, 0x98312229, 0x2f6c79b3, - 0x812c81ad, 0xdadf48ba, 0x24360af2, 0xfab8b464, - 0x98c5bfc9, 0xbebd198e, 0x268c3ba7, 0x09e04214, - 0x68007bac, 0xb2df3316, 0x96e939e4, 0x6c518d80, - 0xc814e204, 0x76a9fb8a, 0x5025c02d, 0x59c58239, - 0xde136967, 0x6ccc5a71, 0xfa256395, 0x9674ee15, - 0x5886ca5d, 0x2e2f31d7, 0x7e0af1fa, 0x27cf73c3, - 0x749c47ab, 0x18501dda, 0xe2757e4f, 0x7401905a, - 0xcafaaae3, 0xe4d59b34, 0x9adf6ace, 0xbd10190d, - 0xfe4890d1, 0xe6188d0b, 0x046df344, 0x706c631e, - }; - try testing.expectEqualSlices(u32, &expected_expansion, &aes_256_expand_key(&key)); -} - -// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values -test "AES-128 ECB encryption" { - const key = [Aes128Parameters.KEY_SIZE]u8{ - 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C, - }; - const plaintext = [_][AES_BLOCK_SIZE]u8{ - .{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A }, - .{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 }, - .{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF }, - .{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 }, - }; - const ciphertext = [_][AES_BLOCK_SIZE]u8{ - .{ 0x3A, 0xD7, 0x7B, 0xB4, 0x0D, 0x7A, 0x36, 0x60, 0xA8, 0x9E, 0xCA, 0xF3, 0x24, 0x66, 0xEF, 0x97 }, - .{ 0xF5, 0xD3, 0xD5, 0x85, 0x03, 0xB9, 0x69, 0x9D, 0xE7, 0x85, 0x89, 0x5A, 0x96, 0xFD, 0xBA, 0xAF }, - .{ 0x43, 0xB1, 0xCD, 0x7F, 0x59, 0x8E, 0xCE, 0x23, 0x88, 0x1B, 0x00, 0xE3, 0xED, 0x03, 0x06, 0x88 }, - .{ 0x7B, 0x0C, 0x78, 0x5E, 0x27, 0xE8, 0xAD, 0x3F, 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5D, 0xD4 }, - }; - - var buffer: [AES_BLOCK_SIZE]u8 = undefined; - for (plaintext, 0..) |pt, i| { - aes_128_encrypt_block(&key, &pt, &buffer); - try testing.expectEqualSlices(u8, &ciphertext[i], &buffer); - } -} - -// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values -test "AES-128 ECB decryption" { - const key = [Aes128Parameters.KEY_SIZE]u8{ - 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C, - }; - const plaintext = [_][AES_BLOCK_SIZE]u8{ - .{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A }, - .{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 }, - .{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF }, - .{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 }, - }; - const ciphertext = [_][AES_BLOCK_SIZE]u8{ - .{ 0x3A, 0xD7, 0x7B, 0xB4, 0x0D, 0x7A, 0x36, 0x60, 0xA8, 0x9E, 0xCA, 0xF3, 0x24, 0x66, 0xEF, 0x97 }, - .{ 0xF5, 0xD3, 0xD5, 0x85, 0x03, 0xB9, 0x69, 0x9D, 0xE7, 0x85, 0x89, 0x5A, 0x96, 0xFD, 0xBA, 0xAF }, - .{ 0x43, 0xB1, 0xCD, 0x7F, 0x59, 0x8E, 0xCE, 0x23, 0x88, 0x1B, 0x00, 0xE3, 0xED, 0x03, 0x06, 0x88 }, - .{ 0x7B, 0x0C, 0x78, 0x5E, 0x27, 0xE8, 0xAD, 0x3F, 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5D, 0xD4 }, - }; - - var buffer: [AES_BLOCK_SIZE]u8 = undefined; - for (ciphertext, 0..) |ct, i| { - aes_128_decrypt_block(&key, &ct, &buffer); - try testing.expectEqualSlices(u8, &plaintext[i], &buffer); - } -} - -// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values -test "AES-192 ECB encryption" { - const key = [Aes192Parameters.KEY_SIZE]u8{ - 0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, - 0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5, - 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B, - }; - const plaintext = [_][AES_BLOCK_SIZE]u8{ - .{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A }, - .{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 }, - .{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF }, - .{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 }, - }; - const ciphertext = [_][AES_BLOCK_SIZE]u8{ - .{ 0xBD, 0x33, 0x4F, 0x1D, 0x6E, 0x45, 0xF2, 0x5F, 0xF7, 0x12, 0xA2, 0x14, 0x57, 0x1F, 0xA5, 0xCC }, - .{ 0x97, 0x41, 0x04, 0x84, 0x6D, 0x0A, 0xD3, 0xAD, 0x77, 0x34, 0xEC, 0xB3, 0xEC, 0xEE, 0x4E, 0xEF }, - .{ 0xEF, 0x7A, 0xFD, 0x22, 0x70, 0xE2, 0xE6, 0x0A, 0xDC, 0xE0, 0xBA, 0x2F, 0xAC, 0xE6, 0x44, 0x4E }, - .{ 0x9A, 0x4B, 0x41, 0xBA, 0x73, 0x8D, 0x6C, 0x72, 0xFB, 0x16, 0x69, 0x16, 0x03, 0xC1, 0x8E, 0x0E }, - }; - - var buffer: [AES_BLOCK_SIZE]u8 = undefined; - for (plaintext, 0..) |pt, i| { - aes_192_encrypt_block(&key, &pt, &buffer); - try testing.expectEqualSlices(u8, &ciphertext[i], &buffer); - } -} - -// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values -test "AES-192 ECB decryption" { - const key = [Aes192Parameters.KEY_SIZE]u8{ - 0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, - 0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5, - 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B, - }; - const plaintext = [_][AES_BLOCK_SIZE]u8{ - .{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A }, - .{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 }, - .{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF }, - .{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 }, - }; - const ciphertext = [_][AES_BLOCK_SIZE]u8{ - .{ 0xBD, 0x33, 0x4F, 0x1D, 0x6E, 0x45, 0xF2, 0x5F, 0xF7, 0x12, 0xA2, 0x14, 0x57, 0x1F, 0xA5, 0xCC }, - .{ 0x97, 0x41, 0x04, 0x84, 0x6D, 0x0A, 0xD3, 0xAD, 0x77, 0x34, 0xEC, 0xB3, 0xEC, 0xEE, 0x4E, 0xEF }, - .{ 0xEF, 0x7A, 0xFD, 0x22, 0x70, 0xE2, 0xE6, 0x0A, 0xDC, 0xE0, 0xBA, 0x2F, 0xAC, 0xE6, 0x44, 0x4E }, - .{ 0x9A, 0x4B, 0x41, 0xBA, 0x73, 0x8D, 0x6C, 0x72, 0xFB, 0x16, 0x69, 0x16, 0x03, 0xC1, 0x8E, 0x0E }, - }; - - var buffer: [AES_BLOCK_SIZE]u8 = undefined; - for (ciphertext, 0..) |ct, i| { - aes_192_decrypt_block(&key, &ct, &buffer); - try testing.expectEqualSlices(u8, &plaintext[i], &buffer); - } -} - -// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values -test "AES-256 ECB encryption" { - const key = [Aes256Parameters.KEY_SIZE]u8{ - 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81, - 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4, - }; - const plaintext = [_][AES_BLOCK_SIZE]u8{ - .{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A }, - .{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 }, - .{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF }, - .{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 }, - }; - const ciphertext = [_][AES_BLOCK_SIZE]u8{ - .{ 0xF3, 0xEE, 0xD1, 0xBD, 0xB5, 0xD2, 0xA0, 0x3C, 0x06, 0x4B, 0x5A, 0x7E, 0x3D, 0xB1, 0x81, 0xF8 }, - .{ 0x59, 0x1C, 0xCB, 0x10, 0xD4, 0x10, 0xED, 0x26, 0xDC, 0x5B, 0xA7, 0x4A, 0x31, 0x36, 0x28, 0x70 }, - .{ 0xB6, 0xED, 0x21, 0xB9, 0x9C, 0xA6, 0xF4, 0xF9, 0xF1, 0x53, 0xE7, 0xB1, 0xBE, 0xAF, 0xED, 0x1D }, - .{ 0x23, 0x30, 0x4B, 0x7A, 0x39, 0xF9, 0xF3, 0xFF, 0x06, 0x7D, 0x8D, 0x8F, 0x9E, 0x24, 0xEC, 0xC7 }, - }; - - var buffer: [AES_BLOCK_SIZE]u8 = undefined; - for (plaintext, 0..) |pt, i| { - aes_256_encrypt_block(&key, &pt, &buffer); - try testing.expectEqualSlices(u8, &ciphertext[i], &buffer); - } -} - -// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values -test "AES-256 ECB decryption" { - const key = [Aes256Parameters.KEY_SIZE]u8{ - 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81, - 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4, - }; - const plaintext = [_][AES_BLOCK_SIZE]u8{ - .{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A }, - .{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 }, - .{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF }, - .{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 }, - }; - const ciphertext = [_][AES_BLOCK_SIZE]u8{ - .{ 0xF3, 0xEE, 0xD1, 0xBD, 0xB5, 0xD2, 0xA0, 0x3C, 0x06, 0x4B, 0x5A, 0x7E, 0x3D, 0xB1, 0x81, 0xF8 }, - .{ 0x59, 0x1C, 0xCB, 0x10, 0xD4, 0x10, 0xED, 0x26, 0xDC, 0x5B, 0xA7, 0x4A, 0x31, 0x36, 0x28, 0x70 }, - .{ 0xB6, 0xED, 0x21, 0xB9, 0x9C, 0xA6, 0xF4, 0xF9, 0xF1, 0x53, 0xE7, 0xB1, 0xBE, 0xAF, 0xED, 0x1D }, - .{ 0x23, 0x30, 0x4B, 0x7A, 0x39, 0xF9, 0xF3, 0xFF, 0x06, 0x7D, 0x8D, 0x8F, 0x9E, 0x24, 0xEC, 0xC7 }, - }; - - var buffer: [AES_BLOCK_SIZE]u8 = undefined; - for (ciphertext, 0..) |ct, i| { - aes_256_decrypt_block(&key, &ct, &buffer); - try testing.expectEqualSlices(u8, &plaintext[i], &buffer); - } -} - -test "AES S-box inverse" { - for (0..256) |x| { - try testing.expectEqual(x, AES_INV_SBOX[AES_SBOX[x]]); - } -} - -test "AES SubBytes" { - var state = [AES_BLOCK_SIZE]u8{ - 0x40, 0xBF, 0xAB, 0xF4, 0x06, 0xEE, 0x4D, 0x30, - 0x42, 0xCA, 0x6B, 0x99, 0x7A, 0x5C, 0x58, 0x16, - }; - const reference = [AES_BLOCK_SIZE]u8{ - 0x09, 0x08, 0x62, 0xBF, 0x6F, 0x28, 0xE3, 0x04, - 0x2C, 0x74, 0x7F, 0xEE, 0xDA, 0x4A, 0x6A, 0x47, - }; - - aes_sub_bytes(&state); - try testing.expectEqualSlices(u8, &reference, &state); -} - -test "AES ShiftRows" { - var state = [AES_BLOCK_SIZE]u8{ - 0x09, 0x08, 0x62, 0xBF, 0x6F, 0x28, 0xE3, 0x04, - 0x2C, 0x74, 0x7F, 0xEE, 0xDA, 0x4A, 0x6A, 0x47, - }; - const reference = [AES_BLOCK_SIZE]u8{ - 0x09, 0x28, 0x7F, 0x47, 0x6F, 0x74, 0x6A, 0xBF, - 0x2C, 0x4A, 0x62, 0x04, 0xDA, 0x08, 0xE3, 0xEE, - }; - - aes_shift_rows(&state); - try testing.expectEqualSlices(u8, &reference, &state); -} - -test "AES MixColumns" { - var state = [AES_BLOCK_SIZE]u8{ - 0x09, 0x28, 0x7F, 0x47, 0x6F, 0x74, 0x6A, 0xBF, - 0x2C, 0x4A, 0x62, 0x04, 0xDA, 0x08, 0xE3, 0xEE, - }; - const reference = [AES_BLOCK_SIZE]u8{ - 0x52, 0x9F, 0x16, 0xC2, 0x97, 0x86, 0x15, 0xCA, - 0xE0, 0x1A, 0xAE, 0x54, 0xBA, 0x1A, 0x26, 0x59, - }; - - aes_mix_columns(&state); - try testing.expectEqualSlices(u8, &reference, &state); -} - -test "AES InvSubBytes" { - const reference = [AES_BLOCK_SIZE]u8{ - 0x40, 0xBF, 0xAB, 0xF4, 0x06, 0xEE, 0x4D, 0x30, - 0x42, 0xCA, 0x6B, 0x99, 0x7A, 0x5C, 0x58, 0x16, - }; - var state = [AES_BLOCK_SIZE]u8{ - 0x09, 0x08, 0x62, 0xBF, 0x6F, 0x28, 0xE3, 0x04, - 0x2C, 0x74, 0x7F, 0xEE, 0xDA, 0x4A, 0x6A, 0x47, - }; - - aes_inv_sub_bytes(&state); - try testing.expectEqualSlices(u8, &reference, &state); -} - -test "AES InvShiftRows" { - const reference = [AES_BLOCK_SIZE]u8{ - 0x09, 0x08, 0x62, 0xBF, 0x6F, 0x28, 0xE3, 0x04, - 0x2C, 0x74, 0x7F, 0xEE, 0xDA, 0x4A, 0x6A, 0x47, - }; - var state = [AES_BLOCK_SIZE]u8{ - 0x09, 0x28, 0x7F, 0x47, 0x6F, 0x74, 0x6A, 0xBF, - 0x2C, 0x4A, 0x62, 0x04, 0xDA, 0x08, 0xE3, 0xEE, - }; - - aes_inv_shift_rows(&state); - try testing.expectEqualSlices(u8, &reference, &state); -} - -test "AES InvMixColumns" { - const reference = [AES_BLOCK_SIZE]u8{ - 0x09, 0x28, 0x7F, 0x47, 0x6F, 0x74, 0x6A, 0xBF, - 0x2C, 0x4A, 0x62, 0x04, 0xDA, 0x08, 0xE3, 0xEE, - }; - var state = [AES_BLOCK_SIZE]u8{ - 0x52, 0x9F, 0x16, 0xC2, 0x97, 0x86, 0x15, 0xCA, - 0xE0, 0x1A, 0xAE, 0x54, 0xBA, 0x1A, 0x26, 0x59, - }; - - aes_inv_mix_columns(&state); - try testing.expectEqualSlices(u8, &reference, &state); -} +pub const RCON = [_]u32{ + 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, + 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000, +}; diff --git a/src/primitive/blockcipher/des.zig b/src/primitive/blockcipher/des.zig index d7f061f..532b91c 100644 --- a/src/primitive/blockcipher/des.zig +++ b/src/primitive/blockcipher/des.zig @@ -186,7 +186,3 @@ fn rotate_halves_left(cd: *[DES_TRUE_KEY_SIZE]u8, positions: comptime_int) void // TODO _ = .{ cd, positions }; } - -// ----------------------------------- TEST VECTORS ----------------------------------- // - -// TODO diff --git a/src/primitive/blockcipher/index.zig b/src/primitive/blockcipher/index.zig index 6a8e2ae..6718624 100644 --- a/src/primitive/blockcipher/index.zig +++ b/src/primitive/blockcipher/index.zig @@ -5,4 +5,5 @@ pub const padding = @import("padding.zig"); pub const operation_mode = struct { pub const gcm = @import("mode_gcm.zig"); + pub const cbc = @import("mode_cbc.zig"); }; diff --git a/src/primitive/blockcipher/mode_cbc.zig b/src/primitive/blockcipher/mode_cbc.zig new file mode 100644 index 0000000..887268c --- /dev/null +++ b/src/primitive/blockcipher/mode_cbc.zig @@ -0,0 +1,167 @@ +const std = @import("std"); +const testing = std.testing; + +const padding = @import("padding.zig"); + +const CryptoError = error{ + PlaintextExceedsBlockSize, + BufferSizeMismatch, +}; + +pub fn cipher_fn_t(comptime block_size: u8, comptime key_size: usize) type { + return fn ( + key: *const [key_size]u8, + block_in: *const [block_size]u8, + block_out: *[block_size]u8, + ) void; +} + +pub fn CbcPkcs7Ctx( + comptime block_size: u8, + comptime key_size: usize, + cipher_fn: cipher_fn_t(block_size, key_size), + comptime decrypt: bool, +) type { + return struct { + const BLOCK_SIZE = block_size; + const KEY_SIZE = key_size; + const CIPHER_FN = cipher_fn; + const DECRYPT = decrypt; + + iv: [block_size]u8, + key: [key_size]u8, + }; +} + +pub fn get_padded_length(comptime block_size: u8, plaintext_length: usize) usize { + return (plaintext_length / block_size + 1) * block_size; +} + +pub fn get_padded_buffer_length(comptime block_size: u8, plaintext: []const u8) usize { + return get_padded_length(block_size, plaintext.len); +} + +pub fn cbc_pkcs7_encrypt_new( + comptime block_size: u8, + comptime key_size: usize, + cipher_fn: cipher_fn_t(block_size, key_size), + key: *const [key_size]u8, + iv: *const [block_size]u8, +) CbcPkcs7Ctx(block_size, key_size, cipher_fn, false) { + return CbcPkcs7Ctx(block_size, key_size, cipher_fn, false){ + .iv = iv.*, + .key = key.*, + }; +} + +pub fn cbc_pkcs7_encrypt_block( + comptime block_size: u8, + comptime key_size: usize, + cipher_fn: cipher_fn_t(block_size, key_size), + ctx: *CbcPkcs7Ctx(block_size, key_size, cipher_fn, false), + plaintext: *const [block_size]u8, + ciphertext: *[block_size]u8, +) void { + var tmp: [block_size]u8 = plaintext.*; + defer @memset(&tmp, 0); + + xor(block_size, tmp[0..], ctx.iv[0..], tmp[0..]); + cipher_fn(&ctx.key, &tmp, &tmp); + + @memcpy(ctx.iv[0..], tmp[0..]); + @memcpy(ciphertext[0..], tmp[0..]); +} + +pub fn cbc_pkcs7_encrypt_final( + comptime block_size: u8, + comptime key_size: usize, + cipher_fn: cipher_fn_t(block_size, key_size), + ctx: *CbcPkcs7Ctx(block_size, key_size, cipher_fn, false), + plaintext: []const u8, + ciphertext: *[block_size]u8, +) !void { + if (plaintext.len >= block_size) + return CryptoError.PlaintextExceedsBlockSize; + + var last_block: [block_size]u8 = undefined; + @memcpy(last_block[0..plaintext.len], plaintext[0..]); + defer @memset(&last_block, 0); + + padding.pkcs7_pad(block_size, &last_block, plaintext.len); + return cbc_pkcs7_encrypt_block(block_size, key_size, cipher_fn, ctx, &last_block, ciphertext); +} + +pub fn cbc_pkcs7_encrypt_destroy( + comptime block_size: u8, + comptime key_size: usize, + cipher_fn: cipher_fn_t(block_size, key_size), + ctx: *CbcPkcs7Ctx(block_size, key_size, cipher_fn, false), +) void { + const ctx_size = @sizeOf(@TypeOf(ctx.*)); + const CtxByteSlice = *[ctx_size]u8; + + @memset(@as(CtxByteSlice, @ptrCast(ctx)), 0); +} + +pub fn cbc_pkcs7_decrypt_new( + comptime block_size: u8, + comptime key_size: usize, + cipher_fn: cipher_fn_t(block_size, key_size), + key: *const [key_size]u8, + iv: *const [block_size]u8, +) CbcPkcs7Ctx(block_size, key_size, cipher_fn, true) { + return CbcPkcs7Ctx(block_size, key_size, cipher_fn, true){ + .iv = iv.*, + .key = key.*, + }; +} + +pub fn cbc_pkcs7_decrypt_block( + comptime block_size: u8, + comptime key_size: usize, + cipher_fn: cipher_fn_t(block_size, key_size), + ctx: *CbcPkcs7Ctx(block_size, key_size, cipher_fn, true), + ciphertext: *const [block_size]u8, + plaintext: *[block_size]u8, +) void { + var tmp: [block_size]u8 = undefined; + defer @memset(&tmp, 0); + + cipher_fn(&ctx.key, ciphertext, &tmp); + xor(block_size, tmp[0..], ctx.iv[0..], tmp[0..]); + + @memcpy(ctx.iv[0..], ciphertext[0..]); + @memcpy(plaintext[0..], tmp[0..]); +} + +pub fn cbc_pkcs7_decrypt_final( + comptime block_size: u8, + comptime key_size: usize, + cipher_fn: cipher_fn_t(block_size, key_size), + ctx: *CbcPkcs7Ctx(block_size, key_size, cipher_fn, true), + ciphertext: []const u8, + plaintext: []u8, +) !u8 { + if (ciphertext.len != block_size or plaintext.len != block_size) + return CryptoError.BufferSizeMismatch; + + cbc_pkcs7_decrypt_block(block_size, key_size, cipher_fn, ctx, @ptrCast(ciphertext), @ptrCast(plaintext)); + return padding.pkcs7_unpad(block_size, @ptrCast(plaintext)); +} + +pub fn cbc_pkcs7_decrypt_destroy( + comptime block_size: u8, + comptime key_size: usize, + cipher_fn: cipher_fn_t(block_size, key_size), + ctx: *CbcPkcs7Ctx(block_size, key_size, cipher_fn, true), +) void { + const ctx_size = @sizeOf(@TypeOf(ctx.*)); + const CtxByteSlice = *[ctx_size]u8; + + @memset(@as(CtxByteSlice, @ptrCast(ctx)), 0); +} + +fn xor(comptime block_size: u8, x: *const [block_size]u8, y: *const [block_size]u8, out: *[block_size]u8) void { + for (0..block_size) |i| + out[i] = x[i] ^ y[i]; +} diff --git a/src/primitive/blockcipher/padding.zig b/src/primitive/blockcipher/padding.zig index 3866f0a..be8f07e 100644 --- a/src/primitive/blockcipher/padding.zig +++ b/src/primitive/blockcipher/padding.zig @@ -18,7 +18,7 @@ pub fn pkcs7_pad( block: *[block_size]u8, plaintext_length: usize, ) void { - const plaintext_block_residue_length: u8 = plaintext_length % block_size; + const plaintext_block_residue_length: u8 = @intCast(plaintext_length % block_size); const padding_val: u8 = block_size - plaintext_block_residue_length; @memset(block[plaintext_block_residue_length..], padding_val); } @@ -39,11 +39,3 @@ pub fn pkcs7_unpad( return plaintext_residue_length; } - -test "PKCS #7 padding" { - // TODO -} - -test "PKCS #7 unpadding" { - // TODO -} diff --git a/src/primitive/digest/sha_1.zig b/src/primitive/digest/sha_1.zig index 3810c92..7ecddca 100644 --- a/src/primitive/digest/sha_1.zig +++ b/src/primitive/digest/sha_1.zig @@ -2,23 +2,22 @@ const std = @import("std"); const testing = std.testing; const sha = @import("./sha_core.zig"); +pub const MessageLengthLimitExceeded = sha.MessageLengthLimitExceeded; -pub const SHA_1_DIGEST_LENGTH = 160 / 8; -pub const SHA_1_MESSAGE_BITS_LIMIT = 1 << 64; +pub const DIGEST_LENGTH = 160 / 8; +pub const MESSAGE_BITS_LIMIT = 1 << 64; pub const Sha1Ctx = struct { pub const BLOCK_SIZE = 512 / 8; pub const MESSAGE_SCHEDULE_WORDS = 80; pub const WordType = u32; - hash: [SHA_1_DIGEST_LENGTH / @sizeOf(WordType)]WordType, + hash: [DIGEST_LENGTH / @sizeOf(WordType)]WordType, message_buffer: [BLOCK_SIZE]u8, message_length: u64, }; -const SHA_1_IV = [_]u32{ - 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0, -}; +const SHA_1_IV = [_]u32{ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 }; pub fn sha1_new() Sha1Ctx { var ctx = Sha1Ctx{ @@ -33,19 +32,19 @@ pub fn sha1_new() Sha1Ctx { pub fn sha1_update(ctx: *Sha1Ctx, message: []const u8) !void { return sha.generic_update( Sha1Ctx, - SHA_1_MESSAGE_BITS_LIMIT, + MESSAGE_BITS_LIMIT, &sha1_compress_block, ctx, message, ); } -pub fn sha1_final(ctx: *Sha1Ctx, out: *[SHA_1_DIGEST_LENGTH]u8) void { +pub fn sha1_final(ctx: *Sha1Ctx, out: *[DIGEST_LENGTH]u8) void { return sha.generic_final( Sha1Ctx, Sha1Ctx.WordType, u64, - SHA_1_DIGEST_LENGTH, + DIGEST_LENGTH, sha1_compress_block, ctx, out, @@ -110,52 +109,3 @@ inline fn sha1_k(t: comptime_int) u32 { else => @compileError("SHA-1 `k` constant requested with invalid value of `t`."), }; } - -// https://www.di-mgt.com.au/sha_testvectors.html -test "SHA-1 basic test" { - try sha.run_hash_precomputed_tests( - Sha1Ctx, - SHA_1_DIGEST_LENGTH, - sha1_new, - sha1_update, - sha1_final, - &.{ - .{ .message = "", .hash = "da39a3ee5e6b4b0d3255bfef95601890afd80709" }, - .{ .message = "abc", .hash = "a9993e364706816aba3e25717850c26c9cd0d89d" }, - .{ - .message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - .hash = "84983e441c3bd26ebaae4aa1f95129e5e54670f1", - }, - .{ - .message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", - .hash = "a49b2446a02c645bf419f995b67091253a04a259", - }, - }, - ); -} - -test "SHA-1 padding test" { - // Here we test every possible length of the message in the last block - // to make sure that the padding is correct in every single case. - - // The following are the hashes of the ASCII strings '', 'a', 'aa', etc. - // up until 63 (= [SHA-1 block size in bits] / 8 - 1) concatenated 'a's. - const reference = [64]*const [2 * SHA_1_DIGEST_LENGTH]u8{ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", "e0c9035898dd52fc65c41454cec9c4d2611bfb37", "7e240de74fb1ed08fa08d38063f6a6a91462a815", "70c881d4a26984ddce795f6f71817c9cf4480e79", "df51e37c269aa94d38f93e537bf6e2020b21406c", "f7a9e24777ec23212c54d7a350bc5bea5477fdbb", "e93b4e3c464ffd51732fbd6ded717e9efda28aad", "b480c074d6b75947c02681f31c90c668c46bf6b8", "2882f38e575101ba615f725af5e59bf2333a9a68", "3495ff69d34671d1e15b33a63c1379fdedd3a32a", "755c001f4ae3c8843e5a50dd6aa2fa23893dd3ad", "384fcd160ab3b33174ea279ad26052eee191508a", "897b99631295d204db13e863b296a09e70ab1d65", "128c484ff69fcdc1f82cd3781595cac5185e688f", "7e13c003a8256cd421055563c5da6571d50713c9", "3499c60eea227453c779de50fc84e217e9a53a18", "321a618ba6830de900738b0814d0c9f28ff2fece", "0478095c8ece0bbc11f94663ac2c4f10b29666de", "1335bfa62671b0015c6e20766c07035868edb8f4", "38666b8ba500faa5c2406f4575d42a92379844c2", "035a4ee5d60816878caec161d6cb8e00e9cc539b", "8c2a4e5c8f210b6aaa6c95e1c8e21351959f4541", "85e3737bb8ab36e2866501e517c46fffc085313e", "b1c76aec7674865d5346b3b0d1cb2c223c53e73e", "44f4647e1542a79d7d68ceb7f75d1dbf77fdebfc", "de8280c3a1c7db377f1ec7107c7fb62d374cc09c", "52a00b8461593ce33409d7c5d0411699cbf9cda3", "06587751ce11a8703abc64cab55b0b96d88341aa", "498a75f314a645671bc79a118df385d0d9948484", "cd762363c1c11ecb48611583520bba111f0034d4", "65cf771ad2cc0b1e8f89361e3b7bec2365b3ad24", "68f84a59a3ca2d0e5cb1646fbb164da409b5d8f2", "2368a1ac71c68c4b47b4fb2806508e0eb447aa64", "82d5343f4b2f0fcf6e28672d1f1a10c434f213d5", "f4d3e057abac5109b7e953578fa97968ea34f43a", "6c783ce5cc13ea5ce572eddfaba02f9d1bb90905", "9e55bf6ab8f14b37cc6f69eb7374be6c5cbd2d07", "1290c28910a6c12c9a131f0ecb523114f20f14c2", "4f5fc75bd3c93bccc09fc2de9c95442456053faf", "a56559418dc7908ce5f0b24b05c78e055cb863dc", "52cedd6b110e4330b5186478736afa5203c4f9ea", "32e067e0414932c3edd95fc4176a54bff1ddfe29", "7bd258f2f4cc4b02fca4ea157f55f6d88d26d954", "df51a19b291586bf46450aec1d775f3e02799b55", "4642fe68c57cd01fc68fc11b7f22b940328a7cc4", "03a4de84c189a836eaee643041b34ad2386db70d", "25883f7a0e732e9ab10e594ea59425dfe4d90359", "3e3d6e12b933133de2caa248ea12bd193a67f206", "1e666934c5a35f509aa31bbd9af8a37a1ed13ba6", "6c177354157989a2c6cd7bac80465b13bea25832", "aca32b501c231ef8e2d8703e71415bfbe4ccbc64", "e6479c70bbac662e4cc134cb8bdaade59ff55b66", "d9b66a0801459c8094398ef8f04700a8569c9906", "b05d71c64979cb95fa74a33cdb31a40d258ae02e", "c1c8bbdc22796e28c0e15163d20899b65621d65a", "c2db330f6083854c99d4b5bfb6e8f29f201be699", "f08f24908d682555111be7ff6f004e78283d989a", "5ee0f8895f4e1aae6a6661de5c432e34188a5a2d", "dbc8b8f59ff85a2b1448ed873484b14bf0507246", "13d956033d9af449bfe2c4ef78c17c20469c4bf1", "aeab141db28af3353283b5ccb2a322df0b9b5f56", "67b4b3923fa178d788a9611b76446c96431071f2", "03f09f5b158a7a8cdad920bddc29b81c18a551f5" }; - - var digest_buffer: [SHA_1_DIGEST_LENGTH]u8 = undefined; - for (0..64) |i| { - var ctx = sha1_new(); - for (0..i) |_| - try sha1_update(&ctx, "a"); - sha1_final(&ctx, &digest_buffer); - - const ref = sha.hex_to_bytes(SHA_1_DIGEST_LENGTH, reference[i]); - try testing.expectEqualSlices(u8, ref[0..], digest_buffer[0..]); - } -} - -test "SHA-1 maximum length violation (simulated)" { - var ctx = sha1_new(); - ctx.message_length = (1 << 61) - 1; // 2^64 - 8 bits - try testing.expectError(sha.MessageLengthLimitExceeded, sha1_update(&ctx, "a")); -} diff --git a/src/primitive/digest/sha_2.zig b/src/primitive/digest/sha_2.zig index 5344bce..3b83a52 100644 --- a/src/primitive/digest/sha_2.zig +++ b/src/primitive/digest/sha_2.zig @@ -1,10 +1,10 @@ const std = @import("std"); +const builtin = @import("builtin"); const testing = std.testing; +const dbg = builtin.mode == std.builtin.Mode.Debug; const sha = @import("./sha_core.zig"); - -const builtin = @import("builtin"); -const dbg = builtin.mode == std.builtin.Mode.Debug; +pub const MessageLengthLimitExceeded = sha.MessageLengthLimitExceeded; pub const SHA_224_DIGEST_LENGTH = 224 / 8; pub const SHA_224_MESSAGE_BITS_LIMIT = 1 << 64; @@ -51,29 +51,6 @@ pub const Sha512Ctx = struct { t_is_384: bool, }; -const SHA_224_IV = [_]u32{ - 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4, -}; -const SHA_256_IV = [_]u32{ - 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, -}; -const SHA_384_IV = [_]u64{ - 0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939, - 0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4, -}; -const SHA_512_IV = [_]u64{ - 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, - 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179, -}; -const SHA_512_224_IV = [_]u64{ - 0x8C3D37C819544DA2, 0x73E1996689DCD4D6, 0x1DFAB7AE32FF9C82, 0x679DD514582F9FCF, - 0x0F6D2B697BD44DA8, 0x77E36F7304C48942, 0x3F9D85A86A1D36C8, 0x1112E6AD91D692A1, -}; -const SHA_512_256_IV = [_]u64{ - 0x22312194FC2BF72C, 0x9F555FA3C84C64C2, 0x2393B86B6F53B151, 0x963877195940EABD, - 0x96283EE2A88EFFE3, 0xBE5E1E2553863992, 0x2B0199FC2C85B8AA, 0x0EB72DDC81C52CA2, -}; - fn sha256_context_new(t: comptime_int) Sha256Ctx { if (comptime t != 224 and t != 256) @compileError("SHA-2 context can only be initialized in 224-bit and 256-bit mode."); @@ -161,17 +138,6 @@ fn sha256_compress_block(ctx: *Sha256Ctx) void { ctx.hash[7] +%= h; } -const SHA2_K_256 = [Sha256Ctx.MESSAGE_SCHEDULE_WORDS]u32{ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, -}; - fn sha2_Sigma_256_0(x: u32) u32 { return sha.rotr(u32, x, 2) ^ sha.rotr(u32, x, 13) ^ sha.rotr(u32, x, 22); } @@ -314,29 +280,6 @@ fn sha512_compress_block(ctx: *Sha512Ctx) void { ctx.hash[7] +%= h; } -const SHA2_K_512 = [Sha512Ctx.MESSAGE_SCHEDULE_WORDS]u64{ - 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, - 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, - 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, - 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, - 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, - 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, - 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, - 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, - 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, - 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, - 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, - 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, - 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, - 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, - 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, - 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, - 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, - 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, - 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, - 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817, -}; - fn sha2_Sigma_512_0(x: u64) u64 { return sha.rotr(u64, x, 28) ^ sha.rotr(u64, x, 34) ^ sha.rotr(u64, x, 39); } @@ -417,218 +360,61 @@ pub fn sha512_256_final(ctx: *Sha512Ctx, out: *[SHA_512_256_DIGEST_LENGTH]u8) vo return sha512_context_final(ctx, SHA_512_256_DIGEST_LENGTH, out); } -// https://www.di-mgt.com.au/sha_testvectors.html -test "SHA-224 basic test" { - try sha.run_hash_precomputed_tests( - Sha256Ctx, - SHA_224_DIGEST_LENGTH, - sha224_new, - sha224_update, - sha224_final, - &.{ - .{ .message = "", .hash = "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" }, - .{ .message = "abc", .hash = "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7" }, - .{ - .message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - .hash = "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525", - }, - .{ - .message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", - .hash = "c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", - }, - }, - ); -} +// ----------------------------------- CRYPTOGRAPHIC CONSTANTS ----------------------------------- // -test "SHA-224 padding test" { - // Here we test every possible length of the message in the last block - // to make sure that the padding is correct in every single case. +const SHA_224_IV = [_]u32{ + 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4, +}; +const SHA_256_IV = [_]u32{ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, +}; +const SHA_384_IV = [_]u64{ + 0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939, + 0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4, +}; +const SHA_512_IV = [_]u64{ + 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179, +}; +const SHA_512_224_IV = [_]u64{ + 0x8C3D37C819544DA2, 0x73E1996689DCD4D6, 0x1DFAB7AE32FF9C82, 0x679DD514582F9FCF, + 0x0F6D2B697BD44DA8, 0x77E36F7304C48942, 0x3F9D85A86A1D36C8, 0x1112E6AD91D692A1, +}; +const SHA_512_256_IV = [_]u64{ + 0x22312194FC2BF72C, 0x9F555FA3C84C64C2, 0x2393B86B6F53B151, 0x963877195940EABD, + 0x96283EE2A88EFFE3, 0xBE5E1E2553863992, 0x2B0199FC2C85B8AA, 0x0EB72DDC81C52CA2, +}; - // The following are the hashes of the ASCII strings '', 'a', 'aa', etc. - // up until 63 (= [SHA-224 block size in bits] / 8 - 1) concatenated 'a's. - const reference = [64]*const [2 * SHA_224_DIGEST_LENGTH]u8{ "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", "abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5", "2ef29e646f6de95de993a59eb1a94cbf52986892949a0abc015a01f7", "ed782653bfec275cf37d027511a68cece08d1e53df1360c762ce043a", "fb56e0a07d1d4ba4a73d42a07d671f65f96513dc12f9d25f2b5ee86e", "04c15392c64c3db52f9a7fcfb7c0c370900e0308007617899430d088", "2886915dd9dedb0ce015f500ffb570901a69a8357728f893eef45344", "b1de26e908098a9b462c11fdd577613d356a7ff06ddb42fda3ab002b", "57dde80cb5dd10cee89424d7d9d8c4fd7949c882da78110e660dec06", "be63cd48d0b068927261c401a33a386136911c3420a5c335e00599fd", "194c4c5045aee1019b77cb1be88ac68034eb2ddd5a8368054141a11f", "5fa4fb5daff0a9b069061839e5605caff0446465f82268775a226333", "85e4d4fcca88691c8008ce19bd9a3de6b9814dd055c8d21bfd4037bb", "da79142fa39f7f11b990aa9dc4994b6e9ce49454058d5423869acf4c", "bd3278d31dec829db380cfe7d7100fecb4b853f054684687f4c27717", "e541fff1289f562eff589da3c8358d91e4dd589bf5dcc37555eb84fe", "0e0a4176c0c6966926acc6fccd2febd418a407df26f76645139d1320", "d5f1096a707710cf5038ed370f7839543eaea0ed054c7484319e2f9c", "e3a0b5557f7894cbb18d6ed06268242212f098d61e179e80a5e96dbc", "fbb6819175a23811c01073f6142af14e80f2d0c598bed6649f307e1f", "43586eff52cbaf9f22482f34a9437ff45bd2e7312ad586b3dd82802f", "2b9d728b01e2c27a40c5fc27d51d5c93f6a160f0f25da8d924e2b570", "eb7ff45dd3d5871ffd6aee24400367cc835aa0d2311d46b2a48b0424", "ac4f39fb481df8f86b6ddf6e527e61893d259b49810416584b468d22", "c4da7f3f63b9da485b734a270f1af6d132b15f177e06445faec5586b", "91a67814117203c32cfc333869bc74f8c721c10672c3516042d88a0b", "7824f1cc7591643231cefe91bbf6fa88b233bf1e8229a59a2cd01c15", "735d169738ebd05edc44cc49a6a99352815cafec12c10d5bd27e8359", "919523d5847d1ee4f9d72f6bd86e3d1bfb785831237a9d580b87448c", "4c166aebdf561231f2b679d8e8457667c0f374043de76d8e7306cb49", "31723190cc4bc6ae686caeea44c54b7b50e30941b8dca3404fe180d8", "f9b67111fdab7e860d1dbe801efa9b75b563e0ee606a9cbabc1288b9", "37e7740b3af132a8edc5e1636817ce1625b831e0ffd47e5c93d4e3b8", "21093fb6a83a36a2b55afa76da42d66de4ebc3b99fb340739d068500", "62d7fcd624a2c1d674ead77584bd66e06d2a6ed160702a594dd6a26e", "507fba56abf0fe3856d0d61527d2a96b18e234bad594bdc969fbfdf4", "6d3a8a79bcc6222986f3b3f17b716774de66d321d51ec34e49995b18", "b622cd8b687970603662b70401a36e464f6860ad337be21c86f17dce", "9c63438f755166862211cc708592f918d31c24e7e70080d6d08e3685", "0126a8fdfaea25bb65fa0b8d6586cd64eee956369b5d2c814607e9f9", "47e263d6419979086bd9a8c4f93591cba373912b68e2ae86085bd2df", "7d8c659cacc50ffc474948b79d65ed98bdab16f6b8a511d184593c86", "da1619b92df1a450be79a14a21e270cb2257230f7dfdc61128e0b49e", "babea0fecfea0aa82d1580abca47dad191f5b9d5a51788d64f678393", "61dd4e5d414a5ae61e76a9b7524223f3cdaf7c9c02b73c175b3e03dd", "fc50179169329ce825404d027fdab44058efc9f28ccddd694a31960e", "b5b7bef9dba5e6a57cecdc03d9a539667f3ca131343de3b6763d7463", "58756e846cce4e08b2ae1103ec3dd2c5755c15f94c1127782dde82c5", "73e0009122e9f4d311459277b81009e9cecc4b3dccf785d4ad476a14", "c0af565a56aeccfd2d40455f20d2c9431a7ab88c61e94973c97cff91", "df427221dc453d5c1466081d9d6e9da3155d5d0dff2a90eb0425036c", "7820fc9fc80c5ed788738da53fbfa6cf1fa981d656a3bb1e68cdf281", "163a72bc0462179bf0486f8a139da514913670d12bbe1d84efc44556", "3fae7c2d692c1610c4a20a17a790d256c3b0071bcdf6fb7fb9538681", "282e1dec88fa36a1070631cca69e3c08a5e18e29fb0b6f6927fbcc0d", "fb0bd626a70c28541dfa781bb5cc4d7d7f56622a58f01a0b1ddd646f", "d40854fc9caf172067136f2e29e1380b14626bf6f0dd06779f820dcd", "b5d09534784ab6578128bce7f28a96a56e3b45c4f734f74739076249", "00df3f1eaa489fd28a9de6e6d7b55402c4e3a56928c5043d77240237", "a82137820aaae9e66f277c3a9254f4a6078c47b410bc9d9a761c2e0b", "efda4316fe2d457d622cf1fc42993d41566f77449b7494b38e250c41", "54e3b540f6792b6a4570f5225717686fbf670fd0dfd3802e4ace9d77", "0daa67402af98b9988c65471b2589dbcdd8bb39569ed77c592aca4a4", "1d4e051f4d6fed2a63fd2421e65834cec00d64456553de3496ae8b1d" }; +const SHA2_K_256 = [Sha256Ctx.MESSAGE_SCHEDULE_WORDS]u32{ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +}; - var digest_buffer: [SHA_224_DIGEST_LENGTH]u8 = undefined; - for (0..64) |i| { - var ctx = sha224_new(); - for (0..i) |_| - try sha224_update(&ctx, "a"); - sha224_final(&ctx, &digest_buffer); - - const ref = sha.hex_to_bytes(SHA_224_DIGEST_LENGTH, reference[i]); - try testing.expectEqualSlices(u8, ref[0..], digest_buffer[0..]); - } -} - -test "SHA-224 maximum length violation (simulated)" { - var ctx = sha224_new(); - ctx.message_length = (1 << 61) - 1; // 2^64 - 8 bits - try testing.expectError(sha.MessageLengthLimitExceeded, sha224_update(&ctx, "a")); -} - -// https://www.di-mgt.com.au/sha_testvectors.html -test "SHA-256 basic test" { - try sha.run_hash_precomputed_tests( - Sha256Ctx, - SHA_256_DIGEST_LENGTH, - sha256_new, - sha256_update, - sha256_final, - &.{ - .{ .message = "", .hash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }, - .{ .message = "abc", .hash = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" }, - .{ - .message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - .hash = "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1", - }, - .{ - .message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", - .hash = "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1", - }, - }, - ); -} - -test "SHA-256 padding test" { - // Here we test every possible length of the message in the last block - // to make sure that the padding is correct in every single case. - - // The following are the hashes of the ASCII strings '', 'a', 'aa', etc. - // up until 63 (= [SHA-256 block size in bits] / 8 - 1) concatenated 'a's. - const reference = [64]*const [2 * SHA_256_DIGEST_LENGTH]u8{ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", "961b6dd3ede3cb8ecbaacbd68de040cd78eb2ed5889130cceb4c49268ea4d506", "9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0", "61be55a8e2f6b4e172338bddf184d6dbee29c98853e0a0485ecee7f27b9af0b4", "ed968e840d10d2d313a870bc131a4e2c311d7ad09bdf32b3418147221f51a6e2", "ed02457b5c41d964dbd2f2a609d63fe1bb7528dbe55e1abf5b52c249cd735797", "e46240714b5db3a23eee60479a623efba4d633d27fe4f03c904b9e219a7fbe60", "1f3ce40415a2081fa3eee75fc39fff8e56c22270d1a978a7249b592dcebd20b4", "f2aca93b80cae681221f0445fa4e2cae8a1f9f8fa1e1741d9639caad222f537d", "bf2cb58a68f684d95a3b78ef8f661c9a4e5b09e82cc8f9cc88cce90528caeb27", "28cb017dfc99073aa1b47c1b30f413e3ce774c4991eb4158de50f9dbb36d8043", "f24abc34b13fade76e805799f71187da6cd90b9cac373ae65ed57f143bd664e5", "a689d786e81340e45511dec6c7ab2d978434e5db123362450fe10cfac70d19d0", "82cab7df0abfb9d95dca4e5937ce2968c798c726fea48c016bf9763221efda13", "ef2df0b539c6c23de0f4cbe42648c301ae0e22e887340a4599fb4ef4e2678e48", "0c0beacef8877bbf2416eb00f2b5dc96354e26dd1df5517320459b1236860f8c", "b860666ee2966dd8f903be44ee605c6e1366f926d9f17a8f49937d11624eb99d", "c926defaaa3d13eda2fc63a553bb7fb7326bece6e7cb67ca5296e4727d89bab4", "a0b4aaab8a966e2193ba172d68162c4656860197f256b5f45f0203397ff3f99c", "42492da06234ad0ac76f5d5debdb6d1ae027cffbe746a1c13b89bb8bc0139137", "7df8e299c834de198e264c3e374bc58ecd9382252a705c183beb02f275571e3b", "ec7c494df6d2a7ea36668d656e6b8979e33641bfea378c15038af3964db057a3", "897d3e95b65f26676081f8b9f3a98b6ee4424566303e8d4e7c7522ebae219eab", "09f61f8d9cd65e6a0c258087c485b6293541364e42bd97b2d7936580c8aa3c54", "2f521e2a7d0bd812cbc035f4ed6806eb8d851793b04ba147e8f66b72f5d1f20f", "9976d549a25115dab4e36d0c1fb8f31cb07da87dd83275977360eb7dc09e88de", "cc0616e61cbd6e8e5e34e9fb2d320f37de915820206f5696c31f1fbd24aa16de", "9c547cb8115a44883b9f70ba68f75117cd55359c92611875e386f8af98c172ab", "6913c9c7fd42fe23df8b6bcd4dbaf1c17748948d97f2980b432319c39eddcf6c", "3a54fc0cbc0b0ef48b6507b7788096235d10292dd3ae24e22f5aa062d4f9864a", "61c60b487d1a921e0bcc9bf853dda0fb159b30bf57b2e2d2c753b00be15b5a09", "3ba3f5f43b92602683c19aee62a20342b084dd5971ddd33808d81a328879a547", "852785c805c77e71a22340a54e9d95933ed49121e7d2bf3c2d358854bc1359ea", "a27c896c4859204843166af66f0e902b9c3b3ed6d2fd13d435abc020065c526f", "629362afc62c74497caed2272e30f8125ecd0965f8d8d7cfc4e260f7f8dd319d", "22c1d24bcd03e9aee9832efccd6da613fc702793178e5f12c945c7b67ddda933", "21ec055b38ce759cd4d0f477e9bdec2c5b8199945db4439bae334a964df6246c", "365a9c3e2c2af0a56e47a9dac51c2c5381bf8f41273bad3175e0e619126ad087", "b4d5e56e929ba4cda349e9274e3603d0be246b82016bca20f363963c5f2d6845", "e33cdf9c7f7120b98e8c78408953e07f2ecd183006b5606df349b4c212acf43e", "c0f8bd4dbc2b0c03107c1c37913f2a7501f521467f45dd0fef6958e9a4692719", "7a538607fdaab9296995929f451565bbb8142e1844117322aafd2b3d76b01aff", "66d34fba71f8f450f7e45598853e53bfc23bbd129027cbb131a2f4ffd7878cd0", "16849877c6c21ef0bfa68e4f6747300ddb171b170b9f00e189edc4c2fc4db93e", "52789e3423b72beeb898456a4f49662e46b0cbb960784c5ef4b1399d327e7c27", "6643110c5628fff59edf76d82d5bf573bf800f16a4d65dfb1e5d6f1a46296d0b", "11eaed932c6c6fddfc2efc394e609facf4abe814fc6180d03b14fce13a07d0e5", "97daac0ee9998dfcad6c9c0970da5ca411c86233a944c25b47566f6a7bc1ddd5", "8f9bec6a62dd28ebd36d1227745592de6658b36974a3bb98a4c582f683ea6c42", "160b4e433e384e05e537dc59b467f7cb2403f0214db15c5db58862a3f1156d2e", "bfc5fe0e360152ca98c50fab4ed7e3078c17debc2917740d5000913b686ca129", "6c1b3dc7a706b9dc81352a6716b9c666c608d8626272c64b914ab05572fc6e84", "abe346a7259fc90b4c27185419628e5e6af6466b1ae9b5446cac4bfc26cf05c4", "a3f01b6939256127582ac8ae9fb47a382a244680806a3f613a118851c1ca1d47", "9f4390f8d30c2dd92ec9f095b65e2b9ae9b0a925a5258e241c9f1e910f734318", "b35439a4ac6f0948b6d6f9e3c6af0f5f590ce20f1bde7090ef7970686ec6738a", "f13b2d724659eb3bf47f2dd6af1accc87b81f09f59f2b75e5c0bed6589dfe8c6", "d5c039b748aa64665782974ec3dc3025c042edf54dcdc2b5de31385b094cb678", "111bb261277afd65f0744b247cd3e47d386d71563d0ed995517807d5ebd4fba3", "11ee391211c6256460b6ed375957fadd8061cafbb31daf967db875aebd5aaad4", "35d5fc17cfbbadd00f5e710ada39f194c5ad7c766ad67072245f1fad45f0f530", "f506898cc7c2e092f9eb9fadae7ba50383f5b46a2a4fe5597dbb553a78981268", "7d3e74a05d7db15bce4ad9ec0658ea98e3f06eeecf16b4c6fff2da457ddc2f34" }; - - var digest_buffer: [SHA_256_DIGEST_LENGTH]u8 = undefined; - for (0..64) |i| { - var ctx = sha256_new(); - for (0..i) |_| - try sha256_update(&ctx, "a"); - sha256_final(&ctx, &digest_buffer); - - const ref = sha.hex_to_bytes(SHA_256_DIGEST_LENGTH, reference[i]); - try testing.expectEqualSlices(u8, ref[0..], digest_buffer[0..]); - } -} - -test "SHA-256 maximum length violation (simulated)" { - var ctx = sha256_new(); - ctx.message_length = (1 << 61) - 1; // 2^64 - 8 bits - try testing.expectError(sha.MessageLengthLimitExceeded, sha256_update(&ctx, "a")); -} - -// https://www.di-mgt.com.au/sha_testvectors.html -test "SHA-512 basic test" { - try sha.run_hash_precomputed_tests( - Sha512Ctx, - SHA_512_DIGEST_LENGTH, - sha512_new, - sha512_update, - sha512_final, - &.{ - .{ .message = "", .hash = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" }, - .{ .message = "abc", .hash = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f" }, - .{ - .message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - .hash = "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445", - }, - .{ - .message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", - .hash = "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909", - }, - }, - ); -} - -test "SHA-512 padding test" { - // Here we test every possible length of the message in the last block - // to make sure that the padding is correct in every single case. - - // The following are the hashes of the ASCII strings '', 'a', 'aa', etc. - // up until 127 (= [SHA-512 block size in bits] / 8 - 1) concatenated 'a's. - const reference = [128]*const [2 * SHA_512_DIGEST_LENGTH]u8{ "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75", "f6c5600ed1dbdcfdf829081f5417dccbbd2b9288e0b427e65c8cf67e274b69009cd142475e15304f599f429f260a661b5df4de26746459a3cef7f32006e5d1c1", "d6f644b19812e97b5d871658d6d3400ecd4787faeb9b8990c1e7608288664be77257104a58d033bcf1a0e0945ff06468ebe53e2dff36e248424c7273117dac09", "1b86355f13a7f0b90c8b6053c0254399994dfbb3843e08d603e292ca13b8f672ed5e58791c10f3e36daec9699cc2fbdc88b4fe116efa7fce016938b787043818", "f368a29b71bd201a7ef78b5df88b1361fbe83f959756d33793837a5d7b2eaf660f2f6c7e2fbace01965683c4cfafded3ff28aab34e329aa79bc81e7703f68b86", "7a9b2a35095dcdfedfdf0ef810310b409e38c92c20cbd51088ea5e4bc4873bdacfeb29f14b7f2ed033d87fad00036da83d5c597a7e7429bc70cec378db4de6a6", "d1ff70451d1f0b94f551461c5ca239e498f571add4542c381e3eef84eac24aea6d12f8333ef4a05847e205ef4bb094921314364e7648176f567863d982042a85", "f74f2603939a53656948480ce71f1ce466685b6654fd22c61c1f2ce4e2c96d1cd02d162b560c4beaf1ae45f3471dc5cbc1ce040701c0b5c38457988aa00fe97f", "507294e03fdbc784dc3f575706c3ce53b3c4c37065e89ddd6b6de8bd2be655e15412e27695d32d2d6a3a0eaabe36ebddde78af0122a65ec41128c98c6fd30554", "4714870aff6c97ca09d135834fdb58a6389a50c11fef8ec4afef466fb60a23ac6b7a9c92658f14df4993d6b40a4e4d8424196afc347e97640d68de61e1cf14b0", "2d5be0f423fee59bf2149f996e72d9f5f8df90540a7d23b68c0d0d9a9a32d2c144891ca8fe4a3c713cb6eb2991578541dad291ba623dbd7107c6a891ba00bcc8", "a88ac22ca41e71e252c1f0d925de1ec174346e097c695e948d23016ab54cf21da6f0b0490ffa752bcc4893afc0c2caa64307705d1996f2959a3e03dc420c68cf", "b0b8828df9473f2763f9a48b0a9683451e98155436c2eff64c628fedbba0cca2360312271f3971f2969b1f828b1bb8251d3a43e12361824aca14f9a9affe2171", "831ce89c92608efda86cdf89d36e855bc73c5d17b2162c0013c14a2676ef4794dc53de6a54c1e3fb782acef5dd1192d36d0ab7fa88262cf0f6a16950e44a828c", "0b6f7fb0679a1ef009d5c8c70551d3b7013a5881b41ec2d597c3cf22e6aa13a1cf9a925b7a012feb67956c83b91cc32bef1de4cdbc6b2ff0cffbba319b54d15e", "987d0fc93db6a73fdb16493690fb42455c7c6fbafe9a276965424b12afad3512fb808d902faa8a019d639dc5ad07c235805e08f396147cf435913cfed501f65a", "06ef0364617146f6200c2cbc4280202226d701c2961940f57e7b60677587c66087f23bbcffa0de8692221f9434ac9a21e6df6428377cd145e1a456e2359d2cf6", "10dbd292472d3ff7279f3dac7fdb83c296bd61cbe80b0e26fbc14f871fd9771180d83879e812ec9841ba15a110e84a589c0eedfc14427c23ba56fa4fb7773de0", "1d383178e64d7071e749b2d560a22abc97e6514c31e800b5cc12c6f72ad43a9a0c4ce7db246219f3dea09afae6044a484de203148cb55f1057ee37b9420073bb", "d87a10a0bee363dcdf764831e807df5ee5500483c09056b38f854606f9e665566264b15af9fee8f9b84f3a7b6ddb67b92996ef790d10e899ba0758d5ab650caf", "1f60925cd5271a8ec9eb49ea4bf187f6a7dbc22eeb0e2dbc89d8381d0e73dea5bff5375a6db7e49fc427cb4fdf9f7ece577037adf91decfc38f303b1cb79ad44", "0ff3b2ffbda4cf938263e9449735618103a4d6a0cdeeda57367f6377d23849c3dce6851377f8f1b3d2ce3ff1dd6de0d64920d7790994782b4a8e2697e31f1900", "c6020ac00b701699227ccc9355156da0ad1d521ada5949cc89dd00661725be08fea4a2519ceb1e50acdd16e7127783f7ed5bfabe5238ce0da7ad2b4174c5509a", "e37ff6da226042c6fdd066c20f00e0d09c4f4dea104d8ea1fc513496ef24a0e17cd4bfb2e95781329a45d3885ca0e20f88e453dc9a4c4dc2acd0be756e3356b8", "4d272d73d4000f885ad1be048b7c7f92c2a8e5a01f30a96ed82849223606ad639f73155c85a128fbd2c26d3de30fb207e57b9f7ff21bfc79e0d7f0e2fb5189dc", "90cce547b76967676972c60e83944ffdc143078b6b40c722a0f2ac90d78eed0057843213076a9a7df528d0c0ebf3c00a91ae1c37f8850173fa2c03c41b6168ea", "4cea8c7ef657f9177c286081f8f016adae91a131a496e939ac86060e691afba57accc08ddbc423eb9d0817725faad9554c60f314929f30e881871e8782228918", "24077df741cb7ba88537d62c55fbff3ea81b603c31e6fd0d2e5d28e1a505f6192d5b2c1f98011152fef2c75901f66d489c045a4a3f98705c2b244c004f1579d4", "2af97e464526c024ef466db4616559919b769b350b7f6830ecfa5ffdeacd6eb570daf0ed25c0c56b194119f15247f63f5b94b54e01283b4b7a832586acac9e09", "abd9c33f8c791b27dd614e80ad77f1ff33c2621663b4dcbe5a88417a8b95b8d6788a9320678b589aa5b405897b2113523df1defa304953ea2ae1a229f2736450", "ad9e7ae1f68786c33ca713d4632b29ebcc9c9c040fc176ead8acb395a14c08324e824f7531f6a50ba0d4a17de958a08e54c9597dcc30781e22c0d953d06f9f4d", "020089a47cb0761c222c323aec2bdecdaa7a0d0ec094cda8c5755ba26844453c25b37e4bc98aab8adc55c9da75bcd83af62905d62e9044a5d64cd93d93b54b34", "d578a2d3fab982fbc7f1fa20630713c5a2a2cd9654a53822978d3efd2becc2a02e1fa1391dd11e139d1489aba688367ea9286e2a9ba8ef67009c80df81998614", "c1de14e1a09b03c688bd568ff4b4fa086baed2181d0d99a219fb937484ba67f093efe36966b0ea5209dadb6ef4f67c2d1f753d49c083a6241d2ab4557509404e", "cbb50e7e8a14cb9df08642609b6d737302d78cdcff74e1f53e895ae4a7cb093a571364dbbc2797962f54366ef65ead1c41a44ffe2ab0d56b7ae01e99a7a4e6fb", "85a564722dbefd268ed2e2e70fb377306c207a9c7edb634adcd79b8829aaad700c3a26cce44eba99aff46c4349f5e5056a87fcd2b63dd08b8b7b1f2f3ea06d6b", "ae77859a42c40e3973aa42bc8fbe8713444f65173580507d7c4bcc7c85d7f8c93204f433d506e912504ea37c766af17e649bdf6c8356f6e8e65bf4e9321987cb", "5b7c791e8018b14752ca7b91386d3ddbd3f9307a69ca71d977e274171aa5cae0b1a03960e842ca05fc0b95205a243fc8b28c36e4dd60ff000a47fb63547e6a0c", "f140bd9a11c309eb9da6ae1c8360cf2bc952a41a9ff228c066c0811df508313f59f1b6e6ffc6d14ef967f477c69463974aefd78d1c1dec9d8d35ff0c81dc29e8", "e411795f8b2a38c99a7b86c888f84c9b26d0f47f2c086d71a2c9282caf6a898820e2c1f3dc1fa45b20178da40f6cb7e4479d3d7155845ed7a4b8698b398f3d0c", "9178f65d74628c56ce3ace5b9ae7ecb84fc8a840ae33367a9c5534e6556301dc4fea4927d82289483496c39b929afb4a4ea92ded82c02057a7b8029828d8fb8d", "e11e1d056266f561bf3a9dede38228700e59971b3be992fea66a687887441976d8b29193707211dfb94dd1f7918473c3e99ff48a7c91068a1aaf7054febb9e2d", "acbaf243155ab6ca5f44c13061757fa060acbc5cf43d996b4f47209c22bf70c29af8dbc5c0a68ca45e42142db1540d2db70f6f27a917a3019dc92dadd0f639d6", "647b6deadb5aeb56e4087414fe2a76d6f57083dd6303a19e152445d108dc2bcd17926981d500b19b913b36a3b343b2e6781c805c1897664a218a2cfecc6a5238", "3ca97cdefcc384485ec2b6bebffe63d98f5675132a8b43d1f38bad4ff1982264fc4876ec637e918f855d855945b9b84eb82386bc6fc1e92695ec623001f8ddd1", "7b549433b4ef39abb90dcd3eb90c63562b7f3daa056670b2f712ebb7e9e78adfe7423e4b39810c1109fb640e84d32047468b155fe342d13e7f4d7ee019fa5922", "ec1d753e2280b8136b686ec81b03b3f8a7f98152868e3a68f0a2c456082c2faaa93c39ad573a6d21f4a3350df602249dc89ad28620d27ecc1d9e1f258badcd04", "bd582a787a21036df7049d501879977625601527d7ddd6f707463cb8b3839fbedbe233b8e69f1696d0e82b168d3491a3dbb6005b6224c198601dafbd50e14365", "4b4ff3bc763a976c16afdd8082efc7a5c98d60342f0ed5a654f567dacbc6414833e60ed1d6770bd42638fdae605c69be0219532125a186609f0825376ab59e45", "bdba173e58132092b0aa67ea5080f247e5b3710630a789c519b311f3848588f0bac8db3091ff8fd16875601636bef625e43b3d82cb51eb6693cdd1b2a5c872b8", "78a0eb5d7c0b05284056e1f19cbb42a99470bc81de4f9bc48708d28c5a877626e69167c58d4e840a7aa699bc6dddd972564d84ea502b41d83878e98e68f83c81", "a45021322d3f30747b3ceb7c1b1975ac4698984be76915f82cdeefe769f115d9dc5c70549e897b0ab8d5d61fc9e73ad1f7f49db39bb4e1298ac833d290eb1d04", "0b08accaae7044e54074fdcb7404a10c0703144d4499a644d9cfc60f973dc27dcdc65ac31750f7407ba96d025fb699e64ddcd1acd0dabafeeafccb5733225d3b", "07a2200290a2b7423a94f71892554b17196e2301e2e446ce09f65abcb45523268274128038925489671af9b899747b80e35a0a1b8613ecfb44e6be3152a2fd93", "b0220c772cbf6c1822e2cb38a437d0e1d58772417a4bbb21c961364f8b6143e05aa6316dca8d1d7b19e16448419076395f6086cb55101fbd6d5497b148e1745f", "962b64aae357d2a4fee3ded8b539bdc9d325081822b0bfc55583133aab44f18bafe11d72a7ae16c79ce2ba620ae2242d5144809161945f1367f41b3972e26e04", "d3115798e872fc1ca6b276368e8ea0926daec6ab1f8f08297e4348ff5f5fe4c6e5205413271babafd4929b070754bc5800e5db44790666ec4e2f6ac52a17e163", "2282084c042e92d7ba1a9e1ee5527762e91c4ffee7a8676c4a4a0facefad352bed2d3c322368cfe813186084c5386e9f22f803dfe0a1b424cab3e0a95a6dc3f9", "fd4eaf2071e8d9cf36688c3be714f5e363a5b4932f509914c613d1b8987d188e82cdd12b6ab07ea2f676fad1789275ef37253260a817a61079bc0ea567ee094a", "5ac08e89d884de3f086c60e8f36e754cf0ae9be2f018a87b7f71b15c81356410077eaa075010eb48959783ba490dc7c9fec53573848d8929bd5fc0574552f58f", "0202004b03bf7be513c96ef3fa6e48fce6e02f858d3bd95edba5adbdce60b2d7a4aa8700de15fc421b5e6847d8fb8be1bd24acd16314cfd94f0fa69ff6d637b4", "9814d48ae1bfd731b32f0a829f20507ec9bd6b77609053718f7e2053b53c7a264bbab6a96d3d54a7f9a736570d11b1f99afb1735149f43cfee9b6f87886d3ff6", "c1b0f5c6d3b03dfe4a2602e67242f54e344090b66e01100a469b129f583f016c7e27dddeaa438393dcc7ec54b0b57c9ba7af007f9b56db5f6fb677d972a31362", "01d35c10c6c38c2dcf48f7eebb3235fb5ad74a65ec4cd016e2354c637a8fb49b695ef3c1d6f7ae4cd74d78cc9c9bcac9d4f23a73019998a7f73038a5c9b2dbde", "b83086cd8494e55708ad7ecd82dfb4bca1bda61ecbb7caf0c68967902e709345e5d8305eb7ac0d588afc6cbb75161aa9c8c7e0ea986bd833dafe5e1ccd37345a", "f2f1cb2b1da21f7df43034baf8ec6bc992a46a022a40f81339240fdae572dbdf34fcf26e97cabc0e001c0aa65607b45585d107c48d676d6e2f389fd801d1fed7", "1b049c5022acb0a6f886cb607629db83dee7ee8f623f8f0fcf352b8f5052036cc7e992e9f79bc424173abb07df8ccfb058f13cfe2a14925a1bb67f4447dd8929", "6c450032dd6b928bdb327b9892d15808163d314aeff37089380ca01ee4b1c8db739f71de29446c385fc8e0f12482ccb04ca1572e243affc7d77ed7bbc083be0d", "73fa82cfc129fb937094b53346e04ff29e44c67250f6952b63ef561bc7cc1169fd94368a252ae408f496c17684145d65cd46ec9c5a03eb59ecc35f6a1d2fc159", "d7ef283e6194befc2498bbced7f58bdf60cfcf10011fc5817b69cb13d63725017aa1e632ea3c609f6a5eb8a057ddb82953538f3e2a738262a11ddcd47f13752d", "216d4ffba1e94e8f281b06feb558346eeb0ae567c0a1d0c56ba2df704f45b2a6e6d91f97c5c00ebbcdfeb14b438bd9e56f2eb36ca64d22392520f3496f28fef5", "7e076f0892677d21072e99258203151146d4bc78ad6ed68edc939ba080c473ab66b10d38834e33abde71830dbd8529d895c7ea5f5773f1457d7c71bc3824b7c8", "1a3d403b46c595edfb71d10b4cb9e1b9ce4e44e28db6ba2a0334195816b85e6eba147bc6160864a0fe28166f99148476893a031a38a814e7136497296865f3c9", "4c4c8dcd6ba88f47a51df4dabdf227c335d70d5f4941b76e698536693e53c50241ef0264ea6f6dc5527485ddbe7a76900405158e32fef5ed184919943148da67", "8ea2d14c839946461666ab0a5966a10886e29d0a890104b123bb94d0af9011d8a961681fb95df98d00d5d351985f61f2e2eba2d91fd8032566b856d8408a09b0", "c642ba36e76cc1660c342d163fb32e4be8482072e641dd6b3662c447ecbc24f1b5e16ad4b83eed093c6f5999f1b2a0086fc23526cef9241a5a052c720bb5afde", "b9451e8c39c4276c2192939d49cbcb2b85a048e4f38bb5d3282e24c417de893ac2ff0acdef20036ed4deddbb526f992cd56f992aaba93d4edd3a628a4e53c811", "9a8c06cf6123391e9ee4d2441b7e534fc9551c242fb2b96fad45a7210edc010c36704b9ca1a07e935e6ff1413768e2f27726b213b16961633341ea82d75c5df3", "54b998867e8ac0d3eebcbf2252c107ad6dc5b557db5b7cb65a147475db99831011878784a62678a6fada687705ef68d048047f05b51db9c09168c4a7ad877036", "cb8d0d18db405d9d964ab61d1a5c00024df3805a329bf1500bec74d3ec1f1d0574da0b86153c9d8e317603bdb09e46d54d44551992a2464f0335a8398a2f2aee", "2fe6df89dcac80c7a03c2bc39633c12ae2898019117aacf77e490fa54cb8deb34a0d29ce778ee4f674831921853a15b541773486d5ac785163744e6d24ba388d", "c3e410354f6f890d0f3027805da471340f91db2a858501059124d3175eb7d637ca3637f7f95bafde0d74d026be7bf086e48931e299d68edc43e0a7ac4eac75c4", "107068fb436d658c0a96157316af41d323e582ea9c81146933ead563bf2c2a05b2c77ceeef57c01cd09ec28f6507238e930b1b7241d731f83194440f9256e5a6", "62b5337f5be290d028dc41dde08682ed7b0a7a842eae36dc6f7220e220012aeca98b2dac28325d1f78beef84352689c07c3a45f549e98ba908b010abceca9978", "6b6f3ac1316d9e8d1505ad163b70077df1df92568139721b32c23e5d84dc2fd742a4bad56bf0efacb3f3e63bbfb08a829b16df8cc1799eb199cd5d56be2b9d52", "bf8ed43d3aeebeb9b00d91013fbbb463b2f4b13e7ffc42741aeb9f0190a91b0401bb4fac68cc009d314287876c54d2f18891e6eee86fbe7125171559be6a03d7", "056ffe9a8b3a346abb92cf36efb74417748a044c4ca07f94e7bb076eeafa67073a85fcc1b17e7953138f304bbea7d0592e910e55b489e22c9015dc4e04ba76dc", "a945652aabf28d5ed6bb284a35fd4296a9a0ddebc81bf59991759ecaa7fb95a59628cf1ae75c88177fa3993e0cd0f138a807cdc01d17ca3922817ad1dc1c39e7", "bb4ec00ac4a82ed71af3936559c5940582218da063554c6f3efbb6d67cc808a2d6dbf088d0f371a4a1259efb1f1edeefa8093cab25551519d7ac6142711e50fe", "3a60fa8bede0f822c5dea75eff151ed5841d11b301c474a13571aff2dd0e216b4bc072b9ce409a70c6e6ff35bcae2f0950880d943f95775dd8f54d94b12d47c3", "bed8ac47aef0271fe40227247ddcdfd6b4885effeba3042f34b6fd525ab56cbdf72050cb71b1d42ae0ee1c548b36668b9297279d661380dffa39e66aa2959f99", "dfecce5852f67e858304fc5dc0c15cb29e28c69af4e2c117d333ea46d2ef2b0379a983507bc16e827b86c2433404159b759de91eb9ae975f338bacf38ad20371", "51585d172675d427009ea1658ac2a4d67a600e65034cb7f8eb34a39add704b67ae0a2798b7d7e7a16ee0f6902a165a0646cd9fe1cc777a07c6bfa14028c8eec8", "9e4246d4d3725a67a909dd1a4f06c627627942c0bb31eb4c614cab842e6bfb9faa7e8938575a2402832ac353a6fb47f4918b31d754eb9764e714f6925462b54e", "89e0446c3ff5a04b6d707ef43a77e2b349791f402930dbdb74bbab73d5215e294146ba7bd2fa269aee38564ef11a9ccaf5278f9e82687126dcf20d481d470617", "39ba3c74b23cb7deffb5d59624e320c08692637057daaaeea4d847e1d3b6a2ce6895ff3c609d57da490484b030ed231d5bdfafcfe264bd3d91cddb39c2d036ab", "db3a1fb5909f50e02e1626616247de6867e9e332d0eeef4650367cf0058f4764eb4a3869d3931b5ef6fc7a044a868b5fa894462df15c3954e88cd70c9a1de1b2", "86497b815f64702e2ac6aca1f1d16f7159b4f0b34f6e92a41e632982a7291465957e0ef171042b9630bb66c6e35051613f99bdc95c371eeb46bff8c897eba6e9", "21883a9b2ffa353c93fea49ea8b92be22797e6e8b360ebac8ed894b702766458a825adf67d9561d6758f5f9cc3aec7a4b2e4464a08e6959029dbc0b2f3fc6105", "70ff99fd241905992cc3fff2f6e3f562c8719d689bfe0e53cbc75e53286d82d8767aed0959b8c63aadf55b5730babee75ea082e88414700d7507b988c44c47bc", "2327e3b2946432dd2f4bce390ca652ec5e90f44fced0e921f612cf6d594cfc5e21b56e30a30dc0157e2c37a59cd37951f20cb9e2bc2d815a2676c01c2f827d51", "ec90d76ee1a1643126f53609a2721ad4a130c57d4dd0416a5d1b0bc43419ed6b3b0e82e0ff5eb76e94accfacb8bf72d7c92b622a0842d9a5b8b6e40fa2fc5231", "48e257ba5ef0c4b0b9769d26d5990d87f058430e368802c1f9a47195a6fa23ede9bbadc4c46ef2a8480cbfa0ced25dad522ca1752a66d5b43a72486f82c7b934", "e4f39bcd76fe94bfa84b31b0b9f3d2fe065b1e01ff2c3c0cd6f26b942f3c73a35031b9ecb4d82418a52892dabb459b27f0ba04af5e90636edf0b2caaa2d7906a", "3b6dd73c9552f2381107bf206b49c7967fdc5f5011d877d9c576bb4da6d74fbbabf46a1105242d7c645978e54c0b44adaf06d9f7aa4703e8a58829f6d87c5168", "470edb01e9dc9db187acdc9fa594e35b40831f9ddf76309d4a99a7aef1f0d9f79b5a4c9a22a38aeca3a1c2d6ceaeb603899577a30643a97872717c025a9a4fdc", "8cfcdd655481cca50730fe51ee985e9b51946f1345cb6a1801e5e0ed64ef979f431d5a7c3bd2a479d6d82e354210741956d194ee0febbc132b35907f4e2be32f", "a53d93726f1ba688a57267326473eceddc4ccf992d5c53429ca3edd4b122b4fe0b0568887d65c220cbac93fc4f612f97a09eb95e9f903409c78a22eee4fa1781", "0cda6b04d9466bb7f3995c16732e1347f29c23a64fe0b085fadba0995644cc5aa71587423c274c10e09518310c5f866cfaceb229fabb574219f12182eb114182", "c825949632e509824543f7eaf159fb6041722fce3c1cdcbb613b3d37ff107c519417baac32f8e74fe29d7f4823bf6886956603dca5354a6ed6e4a542e06b7d28", "fa9121c7b32b9e01733d034cfc78cbf67f926c7ed83e82200ef86818196921760b4beff48404df811b953828274461673c68d04e297b0eb7b2b4d60fc6b566a2", "c01d080efd492776a1c43bd23dd99d0a2e626d481e16782e75d54c2503b5dc32bd05f0f1ba33e568b88fd2d970929b719ecbb152f58f130a407c8830604b70ca", "55ddd8ac210a6e18ba1ee055af84c966e0dbff091c43580ae1be703bdb85da31acf6948cf5bd90c55a20e5450f22fb89bd8d0085e39f85a86cc46abbca75e24d", "5e9eb0e4b270d086e77eeaf3ce8b1cfc615031b8c463dc34f5c139786f274f22accb4d89e8f40d1a0c2acc84c4dc0f2bab390a9d9495493bd617ed004271bb64", "eaa30f93760743ac7d0a6cb8ed5ef3b30c59097bc44d0ec337344301deba9fb92b20c488d55de415f6aaed0df4925b42894b81d2e1cde89d91ec7f6cc67262b4", "a8bff469314a1ce0c990bb3fd539d92accb6249cc674b559bc9d3898b7a126fee597197fa42c971443470053c7d7f54b09371a59b0f7af87b1917c5347e8f8e0", "c0c27aea8dbe169c4cf25176cbf12db708fd6303db8cf94a1cfb402c1680d3d68f39bc5b9a10970dd5373cb0fe1cb36fa50e33165140d72933ba87af9d5d1ffe", "d6f856c92a5a694dec299f5a4765bed80e4e7431aa5505f82b21584dd1f1fe970f698bec5a3f4faa593d1aac944a96c21b85463a773cdf3ad87c4a00fb9e5073", "130396a75cb483f2eee8c56d8a668bb3d2641f5243212c0bee2bd33da096ad9eb8179fe18f9eaacf76e09fae9de4c3f14ba13341e345be05bf76c182cc3468cb", "f241de612b01aa2fa3cf01531d2a8e5e17fc761dfd48a704a834a47f57d6eade7804ecc39be42fdef16ec6adeaf7c01c2fd0c4cc97d3860907cfa4a3b36d0c05", "0ae7a79758a9ffbd1c04aa080bfa82daf9641f9c2a1cc82b628cbe4006bd47701c78e5022d2ca5ca5384d26d93fb16d595b9775dab17c88ef38e4ce9fdac4b52", "139de16e90ad012e39f72279140f4f6b12bb93f1cbcffbd1b132f39e7f92822d2b56beafc9ed83a0bf59c5525ffd125b83294b65f51f6e8ebbf85eb1aba85b87", "ccd869ee70892a0f5f3c269b9e21ffc99703855c1c652774febbaf1311bd58c80fb66bc3f747dd98b2f11ad9f5d8311b7ca706d456fc82ccd46bfb01f19e8d87", "77469b56910b022f45f509dcfca04494d8e7978073debf96398cb5a86f31bbe55f2a807a3271b8fe124171416917ab01a87acc7bf005977caaf7b484d87d6a93", "577a80f7cb393ac140af066b524166bb02a8059980b65fd100ecdcbec7721d2d0519a151ae730d4b6d9b97a8e5d2415aa8157856aeae4a7444171ef2a9db252b", "9986e67bf52a755f8924f28dae9627f889a45d466ce8616c4ed68ec3afd7a3a14785c335c6c68d62e7379af762b2bc17117a902083a99fae337a268a5d4f4427", "828613968b501dc00a97e08c73b118aa8876c26b8aac93df128502ab360f91bab50a51e088769a5c1eff4782ace147dce3642554199876374291f5d921629502" }; - - var digest_buffer: [SHA_512_DIGEST_LENGTH]u8 = undefined; - for (0..64) |i| { - var ctx = sha512_new(); - for (0..i) |_| - try sha512_update(&ctx, "a"); - sha512_final(&ctx, &digest_buffer); - - const ref = sha.hex_to_bytes(SHA_512_DIGEST_LENGTH, reference[i]); - try testing.expectEqualSlices(u8, ref[0..], digest_buffer[0..]); - } -} - -test "SHA-512 maximum length violation (simulated)" { - var ctx = sha512_new(); - ctx.message_length = (1 << 125) - 1; // 2^128 - 8 bits - try testing.expectError(sha.MessageLengthLimitExceeded, sha512_update(&ctx, "a")); -} - -// Reference hashes computed with PyCryptodome. -test "SHA-512/224 basic test" { - try sha.run_hash_precomputed_tests( - Sha512Ctx, - SHA_512_224_DIGEST_LENGTH, - sha512_224_new, - sha512_224_update, - sha512_224_final, - &.{ - .{ .message = "", .hash = "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4" }, - .{ .message = "abc", .hash = "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa" }, - .{ - .message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - .hash = "e5302d6d54bb242275d1e7622d68df6eb02dedd13f564c13dbda2174", - }, - .{ - .message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", - .hash = "23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9", - }, - }, - ); -} - -// https://www.di-mgt.com.au/sha_testvectors.html -test "SHA-512/256 basic test" { - try sha.run_hash_precomputed_tests( - Sha512Ctx, - SHA_512_256_DIGEST_LENGTH, - sha512_256_new, - sha512_256_update, - sha512_256_final, - &.{ - .{ .message = "", .hash = "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a" }, - .{ .message = "abc", .hash = "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23" }, - .{ - .message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - .hash = "bde8e1f9f19bb9fd3406c90ec6bc47bd36d8ada9f11880dbc8a22a7078b6a461", - }, - .{ - .message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", - .hash = "3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a", - }, - }, - ); -} - -// https://www.di-mgt.com.au/sha_testvectors.html -test "SHA-384 basic test" { - try sha.run_hash_precomputed_tests( - Sha512Ctx, - SHA_384_DIGEST_LENGTH, - sha384_new, - sha384_update, - sha384_final, - &.{ - .{ .message = "", .hash = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b" }, - .{ .message = "abc", .hash = "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7" }, - .{ - .message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - .hash = "3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b", - }, - .{ - .message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", - .hash = "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039", - }, - }, - ); -} +const SHA2_K_512 = [Sha512Ctx.MESSAGE_SCHEDULE_WORDS]u64{ + 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, + 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, + 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, + 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, + 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, + 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, + 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, + 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, + 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, + 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, + 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, + 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, + 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, + 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, + 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, + 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, + 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, + 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, + 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, + 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817, +}; diff --git a/src/primitive/digest/sha_core.zig b/src/primitive/digest/sha_core.zig index 20c39ef..e4dad39 100644 --- a/src/primitive/digest/sha_core.zig +++ b/src/primitive/digest/sha_core.zig @@ -164,48 +164,3 @@ pub fn deserialize_int_big_endian(T: type, bytes: *const [@sizeOf(T)]u8) T { res |= @as(T, bytes[i]) << @intCast(8 * (@sizeOf(T) - i - 1)); return res; } - -// ----------------------------------- TEST HELPERS ----------------------------------- // - -const testing = @import("std").testing; - -pub fn hex_nibble_to_int(ascii_hex: u8) u4 { - const x = ascii_hex; - return @intCast(if (x >= '0' and x <= '9') - x - '0' - else if (x >= 'a' and x <= 'f') - 10 + (x - 'a') - else if (x >= 'A' and x <= 'F') - 10 + (x - 'A') - else - @panic("Argument is not a valid hex digit!")); -} - -pub fn hex_to_bytes(L: comptime_int, hex_string: *const [2 * L]u8) [L]u8 { - var res: [L]u8 = undefined; - for (0..L) |i| { - res[i] = @as(u8, hex_nibble_to_int(hex_string[2 * i])) << 4; - res[i] |= hex_nibble_to_int(hex_string[2 * i + 1]); - } - return res; -} - -pub fn run_hash_precomputed_tests( - Ctx: type, - L: comptime_int, - fn_new: *const fn () Ctx, - fn_update: *const fn (*Ctx, []const u8) anyerror!void, - fn_final: *const fn (*Ctx, *[L]u8) void, - tests: []const struct { message: []const u8, hash: *const [2 * L]u8 }, -) !void { - var digest_buffer: [L]u8 = undefined; - - for (tests) |t| { - var ctx = fn_new(); - try fn_update(&ctx, t.message); - fn_final(&ctx, &digest_buffer); - - const reference = hex_to_bytes(L, t.hash); - try testing.expectEqualSlices(u8, reference[0..], digest_buffer[0..]); - } -} diff --git a/src/primitive/streamcipher/chacha20.zig b/src/primitive/streamcipher/chacha20.zig index 8f44eb3..61eb80a 100644 --- a/src/primitive/streamcipher/chacha20.zig +++ b/src/primitive/streamcipher/chacha20.zig @@ -1,59 +1,63 @@ const std = @import("std"); const testing = std.testing; +const byte_operations = @import("TODO").utility.byte_operations; +const word_to_bytes = byte_operations.word_to_bytes_le; +const bytes_to_word = byte_operations.bytes_to_word_le; + // ----------------------------------- ERROR DEFINITIONS ----------------------------------- // -const KeyStreamDepleted = error.KeyStreamDepleted; +pub const KeyStreamDepleted = error.KeyStreamDepleted; // ----------------------------------- ChaCha20 CONSTANTS ----------------------------------- // -const CHACHA20_BLOCK_SIZE = 512 / 8; -const CHACHA20_BLOCK_WORDS = CHACHA20_BLOCK_SIZE / 4; +pub const BLOCK_SIZE = 512 / 8; +pub const BLOCK_WORDS = BLOCK_SIZE / 4; -const CHACHA20_KEY_SIZE = 256 / 8; -const CHACHA20_KEY_WORDS = CHACHA20_KEY_SIZE / 4; +pub const KEY_SIZE = 256 / 8; +pub const KEY_WORDS = KEY_SIZE / 4; -const CHACHA20_NONCE_SIZE = 128 / 8; -const CHACHA20_NONCE_WORDS = CHACHA20_NONCE_SIZE / 4; +pub const NONCE_SIZE = 128 / 8; +pub const NONCE_WORDS = NONCE_SIZE / 4; -const CHACHA20_CONSTANTS = [128 / 8 / 4]u32{ - bytes_to_word_le("expa"), - bytes_to_word_le("nd 3"), - bytes_to_word_le("2-by"), - bytes_to_word_le("te k"), -}; - -pub const ChaCha20_Bernstein_Parameters = struct { +pub const Parameters_Bernstein = struct { pub const NONCE_SIZE = 64 / 8; pub const COUNTER_SIZE = 64 / 8; }; -pub const ChaCha20_RFC7539_Parameters = struct { +pub const Parameters_RFC7539 = struct { pub const NONCE_SIZE = 96 / 8; pub const COUNTER_SIZE = 32 / 8; }; +const CONSTANTS = [128 / 8 / 4]u32{ + bytes_to_word("expa"), + bytes_to_word("nd 3"), + bytes_to_word("2-by"), + bytes_to_word("te k"), +}; + // ----------------------------------- CONTEXT MANAGEMENT ----------------------------------- // pub const ChaCha20Ctx = struct { - key: [CHACHA20_KEY_WORDS]u32, - nonce: [CHACHA20_NONCE_WORDS]u32, - state: [CHACHA20_BLOCK_WORDS]u32, - working_state: [CHACHA20_BLOCK_WORDS]u32, + key: [KEY_WORDS]u32, + nonce: [NONCE_WORDS]u32, + state: [BLOCK_WORDS]u32, + working_state: [BLOCK_WORDS]u32, counter_idx_lsw: u8, counter_idx_msw: u8, keystream_idx: u8, }; pub fn chacha20_new( - key: *const [CHACHA20_KEY_SIZE]u8, + key: *const [KEY_SIZE]u8, counter_size: comptime_int, counter: *const [counter_size]u8, nonce_size: comptime_int, nonce: *const [nonce_size]u8, ) ChaCha20Ctx { - if (comptime counter_size + nonce_size != CHACHA20_NONCE_SIZE) - @panic("Invalid ChaCha initialization: The lengths of the counter and nonce must add up to 16 bytes."); + if (comptime counter_size + nonce_size != NONCE_SIZE) + @compileError("Invalid ChaCha initialization: The lengths of the counter and nonce must add up to 16 bytes."); const counter_words = comptime counter_size / 4; const nonce_words = comptime nonce_size / 4; @@ -68,11 +72,11 @@ pub fn chacha20_new( .keystream_idx = undefined, }; - chacha20_deserialize(CHACHA20_KEY_WORDS, key, &ctx.key); - chacha20_deserialize(counter_words, counter, ctx.nonce[0..counter_words]); - chacha20_deserialize(nonce_words, nonce, ctx.nonce[counter_words .. counter_words + nonce_words]); + deserialize(KEY_WORDS, key, &ctx.key); + deserialize(counter_words, counter, ctx.nonce[0..counter_words]); + deserialize(nonce_words, nonce, ctx.nonce[counter_words .. counter_words + nonce_words]); - chacha20_block_function(&ctx); + block_function(&ctx); ctx.keystream_idx = 0; return ctx; @@ -87,38 +91,38 @@ pub fn chacha20_destroy(ctx: *ChaCha20Ctx) void { } pub fn chacha20_bernstein_new( - key: *const [CHACHA20_KEY_SIZE]u8, - nonce: *const [ChaCha20_Bernstein_Parameters.NONCE_SIZE]u8, - counter: *const [ChaCha20_Bernstein_Parameters.COUNTER_SIZE]u8, + key: *const [KEY_SIZE]u8, + nonce: *const [Parameters_Bernstein.NONCE_SIZE]u8, + counter: *const [Parameters_Bernstein.COUNTER_SIZE]u8, ) ChaCha20Ctx { return chacha20_new( key, - ChaCha20_Bernstein_Parameters.COUNTER_SIZE, + Parameters_Bernstein.COUNTER_SIZE, counter, - ChaCha20_Bernstein_Parameters.NONCE_SIZE, + Parameters_Bernstein.NONCE_SIZE, nonce, ); } pub fn chacha20_rfc7539_new( - key: *const [CHACHA20_KEY_SIZE]u8, - nonce: *const [ChaCha20_RFC7539_Parameters.NONCE_SIZE]u8, - counter: *const [ChaCha20_RFC7539_Parameters.COUNTER_SIZE]u8, + key: *const [KEY_SIZE]u8, + nonce: *const [Parameters_RFC7539.NONCE_SIZE]u8, + counter: *const [Parameters_RFC7539.COUNTER_SIZE]u8, ) ChaCha20Ctx { return chacha20_new( key, - ChaCha20_RFC7539_Parameters.COUNTER_SIZE, + Parameters_RFC7539.COUNTER_SIZE, counter, - ChaCha20_RFC7539_Parameters.NONCE_SIZE, + Parameters_RFC7539.NONCE_SIZE, nonce, ); } // ----------------------------------- ENCRYPTION/DECRYPTION ----------------------------------- // -pub fn chacha20_encrypt_inplace(ctx: *ChaCha20Ctx, plaintext: []u8) !void { +pub fn encrypt_inplace(ctx: *ChaCha20Ctx, plaintext: []u8) !void { for (0..plaintext.len) |i| { - try chacha20_ensure_keystream_expanded(ctx); + try ensure_keystream_expanded(ctx); const keystream: [*]const u8 = @ptrCast(&ctx.state); plaintext[i] ^= keystream[ctx.keystream_idx]; @@ -126,19 +130,19 @@ pub fn chacha20_encrypt_inplace(ctx: *ChaCha20Ctx, plaintext: []u8) !void { } } -pub fn chacha20_decrypt_inplace(ctx: *ChaCha20Ctx, ciphertext: []u8) !void { - return chacha20_encrypt_inplace(ctx, ciphertext); +pub fn decrypt_inplace(ctx: *ChaCha20Ctx, ciphertext: []u8) !void { + return encrypt_inplace(ctx, ciphertext); } -fn chacha20_ensure_keystream_expanded(ctx: *ChaCha20Ctx) !void { - if (ctx.keystream_idx == CHACHA20_BLOCK_SIZE) { - try chacha20_increment_counter(ctx); - chacha20_block_function(ctx); +pub fn ensure_keystream_expanded(ctx: *ChaCha20Ctx) !void { + if (ctx.keystream_idx == BLOCK_SIZE) { + try increment_counter(ctx); + block_function(ctx); ctx.keystream_idx = 0; } } -fn chacha20_increment_counter(ctx: *ChaCha20Ctx) !void { +pub fn increment_counter(ctx: *ChaCha20Ctx) !void { for (ctx.counter_idx_lsw..ctx.counter_idx_msw + 1) |idx| { const ov = @addWithOverflow(ctx.nonce[idx], 1); ctx.nonce[idx] = ov[0]; @@ -151,7 +155,7 @@ fn chacha20_increment_counter(ctx: *ChaCha20Ctx) !void { // ----------------------------------- KEYSTREAM EXPANSION ----------------------------------- // -pub fn chacha20_quarter_round(state: *[CHACHA20_BLOCK_WORDS]u32, ia: u8, ib: u8, ic: u8, id: u8) void { +pub fn quarter_round(state: *[BLOCK_WORDS]u32, ia: u8, ib: u8, ic: u8, id: u8) void { var a = state[ia]; var b = state[ib]; var c = state[ic]; @@ -176,194 +180,59 @@ pub fn chacha20_quarter_round(state: *[CHACHA20_BLOCK_WORDS]u32, ia: u8, ib: u8, state[id] = d; } -pub fn chacha20_inner_block(state: *[CHACHA20_BLOCK_WORDS]u32) void { - chacha20_quarter_round(state, 0, 4, 8, 12); - chacha20_quarter_round(state, 1, 5, 9, 13); - chacha20_quarter_round(state, 2, 6, 10, 14); - chacha20_quarter_round(state, 3, 7, 11, 15); - chacha20_quarter_round(state, 0, 5, 10, 15); - chacha20_quarter_round(state, 1, 6, 11, 12); - chacha20_quarter_round(state, 2, 7, 8, 13); - chacha20_quarter_round(state, 3, 4, 9, 14); +pub fn double_round(state: *[BLOCK_WORDS]u32) void { + quarter_round(state, 0, 4, 8, 12); + quarter_round(state, 1, 5, 9, 13); + quarter_round(state, 2, 6, 10, 14); + quarter_round(state, 3, 7, 11, 15); + quarter_round(state, 0, 5, 10, 15); + quarter_round(state, 1, 6, 11, 12); + quarter_round(state, 2, 7, 8, 13); + quarter_round(state, 3, 4, 9, 14); } -pub fn chacha20_block_function(ctx: *ChaCha20Ctx) void { +pub fn block_function(ctx: *ChaCha20Ctx) void { // Reset state. { comptime var i = 0; - @memcpy(ctx.state[i .. i + CHACHA20_CONSTANTS.len], &CHACHA20_CONSTANTS); - i += CHACHA20_CONSTANTS.len; + @memcpy(ctx.state[i .. i + CONSTANTS.len], &CONSTANTS); + i += CONSTANTS.len; - @memcpy(ctx.state[i .. i + CHACHA20_KEY_WORDS], ctx.key[0..]); - i += CHACHA20_KEY_WORDS; + @memcpy(ctx.state[i .. i + KEY_WORDS], ctx.key[0..]); + i += KEY_WORDS; - @memcpy(ctx.state[i .. i + CHACHA20_NONCE_WORDS], ctx.nonce[0..]); - i += CHACHA20_NONCE_WORDS; + @memcpy(ctx.state[i .. i + NONCE_WORDS], ctx.nonce[0..]); + i += NONCE_WORDS; - if (comptime i != CHACHA20_BLOCK_WORDS) - @panic("Invalid ChaCha20 parameters: |constants + key + nonce + counter| != block_size!"); + if (comptime i != BLOCK_WORDS) + @compileError("Invalid ChaCha20 parameters: |constants + key + nonce + counter| != block_size."); } // Copy state to working_state. @memcpy(&ctx.working_state, &ctx.state); // Perform all 20 rounds (10 column rounds and 10 diagonal rounds). - for (0..10) |_| { - chacha20_inner_block(&ctx.working_state); - } + for (0..10) |_| + double_round(&ctx.working_state); // Add the working_state to the state. - for (0..CHACHA20_BLOCK_WORDS) |i| { + for (0..BLOCK_WORDS) |i| ctx.state[i] +%= ctx.working_state[i]; - } } -// ----------------------------------- LITTLE ENDIAN HELPERS ----------------------------------- // +// ----------------------------------- HELPERS ----------------------------------- // -fn chacha20_serialize(L: comptime_int, words: *const [L]u32, bytes: *[L * 4]u8) void { +pub fn serialize(L: comptime_int, words: *const [L]u32, bytes: *[L * 4]u8) void { for (0..L) |i| std.mem.writeInt(u32, @ptrCast(bytes[(i * 4)..(i * 4 + 4)]), words[i], .little); } -fn chacha20_deserialize(L: comptime_int, bytes: *const [L * 4]u8, words: *[L]u32) void { +pub fn deserialize(L: comptime_int, bytes: *const [L * 4]u8, words: *[L]u32) void { for (0..L) |i| words[i] = std.mem.readInt(u32, @ptrCast(bytes[(i * 4)..(i * 4 + 4)]), .little); } -fn bytes_to_word_le(bytes: *const [4]u8) u32 { - return std.mem.readInt(u32, bytes, .little); -} - -fn word_to_bytes_le(word: u32) [4]u8 { - var bytes: [4]u8 = undefined; - std.mem.writeInt(u32, &bytes, word, .little); - return bytes; -} - -// ----------------------------------- GENERIC HELPERS ----------------------------------- // - fn rol(word: u32, bits: comptime_int) u32 { return word << bits | word >> (32 - bits); } - -// ----------------------------------- TEST VECTORS ----------------------------------- // - -// https://www.rfc-editor.org/rfc/rfc7539#section-2.2.1 -test "ChaCha Quarter Round" { - var state = [CHACHA20_BLOCK_WORDS]u32{ - 0x879531e0, 0xc5ecf37d, 0x516461b1, 0xc9a62f8a, - 0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0x2a5f714c, - 0x53372767, 0xb00a5631, 0x974c541a, 0x359e9963, - 0x5c971061, 0x3d631689, 0x2098d9d6, 0x91dbd320, - }; - const reference = [CHACHA20_BLOCK_WORDS]u32{ - 0x879531e0, 0xc5ecf37d, 0xbdb886dc, 0xc9a62f8a, - 0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0xcfacafd2, - 0xe46bea80, 0xb00a5631, 0x974c541a, 0x359e9963, - 0x5c971061, 0xccc07c79, 0x2098d9d6, 0x91dbd320, - }; - - chacha20_quarter_round(&state, 2, 7, 8, 13); - try testing.expectEqualSlices(u32, reference[0..], state[0..]); -} - -// https://www.rfc-editor.org/rfc/rfc7539#section-2.3.2 -test "ChaCha20 Block Function" { - const key = [CHACHA20_KEY_SIZE]u8{ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - }; - const nonce = [ChaCha20_RFC7539_Parameters.NONCE_SIZE]u8{ - 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, - }; - - const reference = [CHACHA20_BLOCK_SIZE]u8{ - 0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15, 0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, 0x71, 0xc4, - 0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03, 0x04, 0x22, 0xaa, 0x9a, 0xc3, 0xd4, 0x6c, 0x4e, - 0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09, 0x14, 0xc2, 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2, - 0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9, 0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e, - }; - - var chacha = chacha20_rfc7539_new(&key, &nonce, &word_to_bytes_le(1)); - defer chacha20_destroy(&chacha); - - chacha20_block_function(&chacha); - - var buffer: [CHACHA20_BLOCK_SIZE]u8 = undefined; - chacha20_serialize(CHACHA20_BLOCK_WORDS, &chacha.state, &buffer); - - try testing.expectEqualSlices(u8, reference[0..], buffer[0..]); -} - -// https://www.rfc-editor.org/rfc/rfc7539#section-2.4.2 -test "ChaCha20 Cipher" { - const key = [CHACHA20_KEY_SIZE]u8{ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - }; - const nonce = [ChaCha20_RFC7539_Parameters.NONCE_SIZE]u8{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, - }; - const counter = word_to_bytes_le(1); - const plaintext = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; - const reference = [_]u8{ - 0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80, 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81, - 0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2, 0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b, - 0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab, 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57, - 0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab, 0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8, - 0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61, 0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e, - 0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06, 0x81, 0x8c, 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36, - 0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6, 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42, - 0x87, 0x4d, - }; - - var chacha = chacha20_rfc7539_new(&key, &nonce, &counter); - defer chacha20_destroy(&chacha); - - var buffer: [plaintext.len]u8 = undefined; - @memcpy(&buffer, plaintext); - - try chacha20_encrypt_inplace(&chacha, &buffer); - try testing.expectEqualSlices(u8, reference[0..], buffer[0..]); -} - -test "ChaCha20 32-bit counter increment edge cases" { - const key = [CHACHA20_KEY_SIZE]u8{ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - }; - const nonce = [ChaCha20_RFC7539_Parameters.NONCE_SIZE]u8{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, - }; - const counter = word_to_bytes_le(std.math.maxInt(u32) - 1); - - var chacha = chacha20_rfc7539_new(&key, &nonce, &counter); - defer chacha20_destroy(&chacha); - - // Counter: 2^32 - 2 -> 2^32 - 1, OK - try chacha20_increment_counter(&chacha); - - // Counter: 2^32 - 1 -> 2^32, overflow - try testing.expectError(KeyStreamDepleted, chacha20_increment_counter(&chacha)); -} - -test "ChaCha20 64-bit counter increment edge cases" { - const key = [CHACHA20_KEY_SIZE]u8{ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - }; - const nonce = [ChaCha20_Bernstein_Parameters.NONCE_SIZE]u8{ 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00 }; - const counter = [ChaCha20_Bernstein_Parameters.COUNTER_SIZE]u8{ 0xff, 0xff, 0xff, 0xff, 0xef, 0xbe, 0xad, 0xde }; - - var chacha = chacha20_bernstein_new(&key, &nonce, &counter); - defer chacha20_destroy(&chacha); - - // Counter: 0xdeadbeefffffffff -> 0xdeadbef000000000, OK - try chacha20_increment_counter(&chacha); - try testing.expectEqualSlices(u32, &.{ 0x00000000, 0xdeadbef0 }, chacha.nonce[0..2]); - - // Counter: 0xffffffffffffffff -> 0x10000000000000000, overflow - @memset(chacha.nonce[0..2], std.math.maxInt(u32)); - try testing.expectError(KeyStreamDepleted, chacha20_increment_counter(&chacha)); -} diff --git a/src/root.zig b/src/root.zig index 7106ca5..a546842 100644 --- a/src/root.zig +++ b/src/root.zig @@ -1 +1,2 @@ pub const primitive = @import("./primitive/index.zig"); +pub const utility = @import("./utility/index.zig"); diff --git a/src/utility/byte_operations.zig b/src/utility/byte_operations.zig new file mode 100644 index 0000000..7b2c24d --- /dev/null +++ b/src/utility/byte_operations.zig @@ -0,0 +1,21 @@ +const std = @import("std"); + +pub fn word_to_bytes_le(word: u32) [4]u8 { + var bytes: [4]u8 = undefined; + std.mem.writeInt(u32, &bytes, word, .little); + return bytes; +} + +pub fn bytes_to_word_le(bytes: *const [4]u8) u32 { + return std.mem.readInt(u32, bytes, .little); +} + +pub fn word_to_bytes_be(word: u32) [4]u8 { + var bytes: [4]u8 = undefined; + std.mem.writeInt(u32, &bytes, word, .big); + return bytes; +} + +pub fn bytes_to_word_be(bytes: *const [4]u8) u32 { + return std.mem.readInt(u32, bytes, .big); +} diff --git a/src/utility/index.zig b/src/utility/index.zig new file mode 100644 index 0000000..e4b92e5 --- /dev/null +++ b/src/utility/index.zig @@ -0,0 +1 @@ +pub const byte_operations = @import("byte_operations.zig"); diff --git a/src/test.zig b/test/index.zig similarity index 55% rename from src/test.zig rename to test/index.zig index 566f842..0f93271 100644 --- a/src/test.zig +++ b/test/index.zig @@ -1,10 +1,8 @@ comptime { _ = .{ @import("./primitive/blockcipher/aes.zig"), - @import("./primitive/blockcipher/des.zig"), - @import("./primitive/blockcipher/mode_gcm.zig"), - @import("./primitive/digest/sha_1.zig"), - @import("./primitive/digest/sha_2.zig"), + @import("./primitive/blockcipher/operation_modes.zig"), + @import("./primitive/digest/sha.zig"), @import("./primitive/streamcipher/chacha20.zig"), @import("./primitive/streamcipher/salsa20.zig"), }; diff --git a/test/primitive/blockcipher/aes.zig b/test/primitive/blockcipher/aes.zig new file mode 100644 index 0000000..540b256 --- /dev/null +++ b/test/primitive/blockcipher/aes.zig @@ -0,0 +1,322 @@ +const std = @import("std"); +const testing = std.testing; + +const aes = @import("ziggy").primitive.blockcipher.aes; + +test "AES-128 key expansion (FIPS 197)" { + const key = [aes.KEY_SIZE_128]u8{ + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c, + }; + const expected_expansion = [(aes.N_ROUNDS_128 + 1) * 4]u32{ + 0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x09cf4f3c, + 0xa0fafe17, 0x88542cb1, 0x23a33939, 0x2a6c7605, + 0xf2c295f2, 0x7a96b943, 0x5935807a, 0x7359f67f, + 0x3d80477d, 0x4716fe3e, 0x1e237e44, 0x6d7a883b, + 0xef44a541, 0xa8525b7f, 0xb671253b, 0xdb0bad00, + 0xd4d1c6f8, 0x7c839d87, 0xcaf2b8bc, 0x11f915bc, + 0x6d88a37a, 0x110b3efd, 0xdbf98641, 0xca0093fd, + 0x4e54f70e, 0x5f5fc9f3, 0x84a64fb2, 0x4ea6dc4f, + 0xead27321, 0xb58dbad2, 0x312bf560, 0x7f8d292f, + 0xac7766f3, 0x19fadc21, 0x28d12941, 0x575c006e, + 0xd014f9a8, 0xc9ee2589, 0xe13f0cc8, 0xb6630ca6, + }; + try testing.expectEqualSlices(u32, &expected_expansion, &aes.expand_key_128(&key)); +} + +test "AES-192 key expansion (FIPS 197)" { + const key = [aes.KEY_SIZE_192]u8{ + 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, + 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, + 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b, + }; + const expected_expansion = [(aes.N_ROUNDS_192 + 1) * 4]u32{ + 0x8e73b0f7, 0xda0e6452, 0xc810f32b, 0x809079e5, + 0x62f8ead2, 0x522c6b7b, 0xfe0c91f7, 0x2402f5a5, + 0xec12068e, 0x6c827f6b, 0x0e7a95b9, 0x5c56fec2, + 0x4db7b4bd, 0x69b54118, 0x85a74796, 0xe92538fd, + 0xe75fad44, 0xbb095386, 0x485af057, 0x21efb14f, + 0xa448f6d9, 0x4d6dce24, 0xaa326360, 0x113b30e6, + 0xa25e7ed5, 0x83b1cf9a, 0x27f93943, 0x6a94f767, + 0xc0a69407, 0xd19da4e1, 0xec1786eb, 0x6fa64971, + 0x485f7032, 0x22cb8755, 0xe26d1352, 0x33f0b7b3, + 0x40beeb28, 0x2f18a259, 0x6747d26b, 0x458c553e, + 0xa7e1466c, 0x9411f1df, 0x821f750a, 0xad07d753, + 0xca400538, 0x8fcc5006, 0x282d166a, 0xbc3ce7b5, + 0xe98ba06f, 0x448c773c, 0x8ecc7204, 0x01002202, + }; + try testing.expectEqualSlices(u32, &expected_expansion, &aes.expand_key_192(&key)); +} + +test "AES-256 key expansion (FIPS 197)" { + const key = [aes.KEY_SIZE_256]u8{ + 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, + 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, + 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, + 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4, + }; + const expected_expansion = [(aes.N_ROUNDS_256 + 1) * 4]u32{ + 0x603deb10, 0x15ca71be, 0x2b73aef0, 0x857d7781, + 0x1f352c07, 0x3b6108d7, 0x2d9810a3, 0x0914dff4, + 0x9ba35411, 0x8e6925af, 0xa51a8b5f, 0x2067fcde, + 0xa8b09c1a, 0x93d194cd, 0xbe49846e, 0xb75d5b9a, + 0xd59aecb8, 0x5bf3c917, 0xfee94248, 0xde8ebe96, + 0xb5a9328a, 0x2678a647, 0x98312229, 0x2f6c79b3, + 0x812c81ad, 0xdadf48ba, 0x24360af2, 0xfab8b464, + 0x98c5bfc9, 0xbebd198e, 0x268c3ba7, 0x09e04214, + 0x68007bac, 0xb2df3316, 0x96e939e4, 0x6c518d80, + 0xc814e204, 0x76a9fb8a, 0x5025c02d, 0x59c58239, + 0xde136967, 0x6ccc5a71, 0xfa256395, 0x9674ee15, + 0x5886ca5d, 0x2e2f31d7, 0x7e0af1fa, 0x27cf73c3, + 0x749c47ab, 0x18501dda, 0xe2757e4f, 0x7401905a, + 0xcafaaae3, 0xe4d59b34, 0x9adf6ace, 0xbd10190d, + 0xfe4890d1, 0xe6188d0b, 0x046df344, 0x706c631e, + }; + try testing.expectEqualSlices(u32, &expected_expansion, &aes.expand_key_256(&key)); +} + +// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values +test "AES-128 ECB encryption (NIST)" { + const key = [aes.KEY_SIZE_128]u8{ + 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C, + }; + const plaintext = [_][aes.BLOCK_SIZE]u8{ + .{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A }, + .{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 }, + .{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF }, + .{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 }, + }; + const ciphertext = [_][aes.BLOCK_SIZE]u8{ + .{ 0x3A, 0xD7, 0x7B, 0xB4, 0x0D, 0x7A, 0x36, 0x60, 0xA8, 0x9E, 0xCA, 0xF3, 0x24, 0x66, 0xEF, 0x97 }, + .{ 0xF5, 0xD3, 0xD5, 0x85, 0x03, 0xB9, 0x69, 0x9D, 0xE7, 0x85, 0x89, 0x5A, 0x96, 0xFD, 0xBA, 0xAF }, + .{ 0x43, 0xB1, 0xCD, 0x7F, 0x59, 0x8E, 0xCE, 0x23, 0x88, 0x1B, 0x00, 0xE3, 0xED, 0x03, 0x06, 0x88 }, + .{ 0x7B, 0x0C, 0x78, 0x5E, 0x27, 0xE8, 0xAD, 0x3F, 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5D, 0xD4 }, + }; + + var buffer: [aes.BLOCK_SIZE]u8 = undefined; + for (plaintext, 0..) |pt, i| { + aes.aes128_encrypt_block(&key, &pt, &buffer); + try testing.expectEqualSlices(u8, &ciphertext[i], &buffer); + } +} + +// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values +test "AES-128 ECB decryption (NIST)" { + const key = [aes.KEY_SIZE_128]u8{ + 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C, + }; + const plaintext = [_][aes.BLOCK_SIZE]u8{ + .{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A }, + .{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 }, + .{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF }, + .{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 }, + }; + const ciphertext = [_][aes.BLOCK_SIZE]u8{ + .{ 0x3A, 0xD7, 0x7B, 0xB4, 0x0D, 0x7A, 0x36, 0x60, 0xA8, 0x9E, 0xCA, 0xF3, 0x24, 0x66, 0xEF, 0x97 }, + .{ 0xF5, 0xD3, 0xD5, 0x85, 0x03, 0xB9, 0x69, 0x9D, 0xE7, 0x85, 0x89, 0x5A, 0x96, 0xFD, 0xBA, 0xAF }, + .{ 0x43, 0xB1, 0xCD, 0x7F, 0x59, 0x8E, 0xCE, 0x23, 0x88, 0x1B, 0x00, 0xE3, 0xED, 0x03, 0x06, 0x88 }, + .{ 0x7B, 0x0C, 0x78, 0x5E, 0x27, 0xE8, 0xAD, 0x3F, 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5D, 0xD4 }, + }; + + var buffer: [aes.BLOCK_SIZE]u8 = undefined; + for (ciphertext, 0..) |ct, i| { + aes.aes128_decrypt_block(&key, &ct, &buffer); + try testing.expectEqualSlices(u8, &plaintext[i], &buffer); + } +} + +// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values +test "AES-192 ECB encryption (NIST)" { + const key = [aes.KEY_SIZE_192]u8{ + 0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, + 0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5, + 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B, + }; + const plaintext = [_][aes.BLOCK_SIZE]u8{ + .{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A }, + .{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 }, + .{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF }, + .{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 }, + }; + const ciphertext = [_][aes.BLOCK_SIZE]u8{ + .{ 0xBD, 0x33, 0x4F, 0x1D, 0x6E, 0x45, 0xF2, 0x5F, 0xF7, 0x12, 0xA2, 0x14, 0x57, 0x1F, 0xA5, 0xCC }, + .{ 0x97, 0x41, 0x04, 0x84, 0x6D, 0x0A, 0xD3, 0xAD, 0x77, 0x34, 0xEC, 0xB3, 0xEC, 0xEE, 0x4E, 0xEF }, + .{ 0xEF, 0x7A, 0xFD, 0x22, 0x70, 0xE2, 0xE6, 0x0A, 0xDC, 0xE0, 0xBA, 0x2F, 0xAC, 0xE6, 0x44, 0x4E }, + .{ 0x9A, 0x4B, 0x41, 0xBA, 0x73, 0x8D, 0x6C, 0x72, 0xFB, 0x16, 0x69, 0x16, 0x03, 0xC1, 0x8E, 0x0E }, + }; + + var buffer: [aes.BLOCK_SIZE]u8 = undefined; + for (plaintext, 0..) |pt, i| { + aes.aes192_encrypt_block(&key, &pt, &buffer); + try testing.expectEqualSlices(u8, &ciphertext[i], &buffer); + } +} + +// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values +test "AES-192 ECB decryption (NIST)" { + const key = [aes.KEY_SIZE_192]u8{ + 0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, + 0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5, + 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B, + }; + const plaintext = [_][aes.BLOCK_SIZE]u8{ + .{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A }, + .{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 }, + .{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF }, + .{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 }, + }; + const ciphertext = [_][aes.BLOCK_SIZE]u8{ + .{ 0xBD, 0x33, 0x4F, 0x1D, 0x6E, 0x45, 0xF2, 0x5F, 0xF7, 0x12, 0xA2, 0x14, 0x57, 0x1F, 0xA5, 0xCC }, + .{ 0x97, 0x41, 0x04, 0x84, 0x6D, 0x0A, 0xD3, 0xAD, 0x77, 0x34, 0xEC, 0xB3, 0xEC, 0xEE, 0x4E, 0xEF }, + .{ 0xEF, 0x7A, 0xFD, 0x22, 0x70, 0xE2, 0xE6, 0x0A, 0xDC, 0xE0, 0xBA, 0x2F, 0xAC, 0xE6, 0x44, 0x4E }, + .{ 0x9A, 0x4B, 0x41, 0xBA, 0x73, 0x8D, 0x6C, 0x72, 0xFB, 0x16, 0x69, 0x16, 0x03, 0xC1, 0x8E, 0x0E }, + }; + + var buffer: [aes.BLOCK_SIZE]u8 = undefined; + for (ciphertext, 0..) |ct, i| { + aes.aes192_decrypt_block(&key, &ct, &buffer); + try testing.expectEqualSlices(u8, &plaintext[i], &buffer); + } +} + +// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values +test "AES-256 ECB encryption (NIST)" { + const key = [aes.KEY_SIZE_256]u8{ + 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81, + 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4, + }; + const plaintext = [_][aes.BLOCK_SIZE]u8{ + .{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A }, + .{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 }, + .{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF }, + .{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 }, + }; + const ciphertext = [_][aes.BLOCK_SIZE]u8{ + .{ 0xF3, 0xEE, 0xD1, 0xBD, 0xB5, 0xD2, 0xA0, 0x3C, 0x06, 0x4B, 0x5A, 0x7E, 0x3D, 0xB1, 0x81, 0xF8 }, + .{ 0x59, 0x1C, 0xCB, 0x10, 0xD4, 0x10, 0xED, 0x26, 0xDC, 0x5B, 0xA7, 0x4A, 0x31, 0x36, 0x28, 0x70 }, + .{ 0xB6, 0xED, 0x21, 0xB9, 0x9C, 0xA6, 0xF4, 0xF9, 0xF1, 0x53, 0xE7, 0xB1, 0xBE, 0xAF, 0xED, 0x1D }, + .{ 0x23, 0x30, 0x4B, 0x7A, 0x39, 0xF9, 0xF3, 0xFF, 0x06, 0x7D, 0x8D, 0x8F, 0x9E, 0x24, 0xEC, 0xC7 }, + }; + + var buffer: [aes.BLOCK_SIZE]u8 = undefined; + for (plaintext, 0..) |pt, i| { + aes.aes256_encrypt_block(&key, &pt, &buffer); + try testing.expectEqualSlices(u8, &ciphertext[i], &buffer); + } +} + +// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values +test "AES-256 ECB decryption (NIST)" { + const key = [aes.KEY_SIZE_256]u8{ + 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81, + 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4, + }; + const plaintext = [_][aes.BLOCK_SIZE]u8{ + .{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A }, + .{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 }, + .{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF }, + .{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 }, + }; + const ciphertext = [_][aes.BLOCK_SIZE]u8{ + .{ 0xF3, 0xEE, 0xD1, 0xBD, 0xB5, 0xD2, 0xA0, 0x3C, 0x06, 0x4B, 0x5A, 0x7E, 0x3D, 0xB1, 0x81, 0xF8 }, + .{ 0x59, 0x1C, 0xCB, 0x10, 0xD4, 0x10, 0xED, 0x26, 0xDC, 0x5B, 0xA7, 0x4A, 0x31, 0x36, 0x28, 0x70 }, + .{ 0xB6, 0xED, 0x21, 0xB9, 0x9C, 0xA6, 0xF4, 0xF9, 0xF1, 0x53, 0xE7, 0xB1, 0xBE, 0xAF, 0xED, 0x1D }, + .{ 0x23, 0x30, 0x4B, 0x7A, 0x39, 0xF9, 0xF3, 0xFF, 0x06, 0x7D, 0x8D, 0x8F, 0x9E, 0x24, 0xEC, 0xC7 }, + }; + + var buffer: [aes.BLOCK_SIZE]u8 = undefined; + for (ciphertext, 0..) |ct, i| { + aes.aes256_decrypt_block(&key, &ct, &buffer); + try testing.expectEqualSlices(u8, &plaintext[i], &buffer); + } +} + +test "AES S-box inverse is the inverse S-box" { + for (0..256) |x| { + try testing.expectEqual(x, aes.INV_SBOX[aes.SBOX[x]]); + } +} + +test "AES SubBytes" { + var state = [aes.BLOCK_SIZE]u8{ + 0x40, 0xBF, 0xAB, 0xF4, 0x06, 0xEE, 0x4D, 0x30, + 0x42, 0xCA, 0x6B, 0x99, 0x7A, 0x5C, 0x58, 0x16, + }; + const reference = [aes.BLOCK_SIZE]u8{ + 0x09, 0x08, 0x62, 0xBF, 0x6F, 0x28, 0xE3, 0x04, + 0x2C, 0x74, 0x7F, 0xEE, 0xDA, 0x4A, 0x6A, 0x47, + }; + + aes.sub_bytes(&state); + try testing.expectEqualSlices(u8, &reference, &state); +} + +test "AES ShiftRows" { + var state = [aes.BLOCK_SIZE]u8{ + 0x09, 0x08, 0x62, 0xBF, 0x6F, 0x28, 0xE3, 0x04, + 0x2C, 0x74, 0x7F, 0xEE, 0xDA, 0x4A, 0x6A, 0x47, + }; + const reference = [aes.BLOCK_SIZE]u8{ + 0x09, 0x28, 0x7F, 0x47, 0x6F, 0x74, 0x6A, 0xBF, + 0x2C, 0x4A, 0x62, 0x04, 0xDA, 0x08, 0xE3, 0xEE, + }; + + aes.shift_rows(&state); + try testing.expectEqualSlices(u8, &reference, &state); +} + +test "AES MixColumns" { + var state = [aes.BLOCK_SIZE]u8{ + 0x09, 0x28, 0x7F, 0x47, 0x6F, 0x74, 0x6A, 0xBF, + 0x2C, 0x4A, 0x62, 0x04, 0xDA, 0x08, 0xE3, 0xEE, + }; + const reference = [aes.BLOCK_SIZE]u8{ + 0x52, 0x9F, 0x16, 0xC2, 0x97, 0x86, 0x15, 0xCA, + 0xE0, 0x1A, 0xAE, 0x54, 0xBA, 0x1A, 0x26, 0x59, + }; + + aes.mix_columns(&state); + try testing.expectEqualSlices(u8, &reference, &state); +} + +test "AES InvSubBytes" { + const reference = [aes.BLOCK_SIZE]u8{ + 0x40, 0xBF, 0xAB, 0xF4, 0x06, 0xEE, 0x4D, 0x30, + 0x42, 0xCA, 0x6B, 0x99, 0x7A, 0x5C, 0x58, 0x16, + }; + var state = [aes.BLOCK_SIZE]u8{ + 0x09, 0x08, 0x62, 0xBF, 0x6F, 0x28, 0xE3, 0x04, + 0x2C, 0x74, 0x7F, 0xEE, 0xDA, 0x4A, 0x6A, 0x47, + }; + + aes.inv_sub_bytes(&state); + try testing.expectEqualSlices(u8, &reference, &state); +} + +test "AES InvShiftRows" { + const reference = [aes.BLOCK_SIZE]u8{ + 0x09, 0x08, 0x62, 0xBF, 0x6F, 0x28, 0xE3, 0x04, + 0x2C, 0x74, 0x7F, 0xEE, 0xDA, 0x4A, 0x6A, 0x47, + }; + var state = [aes.BLOCK_SIZE]u8{ + 0x09, 0x28, 0x7F, 0x47, 0x6F, 0x74, 0x6A, 0xBF, + 0x2C, 0x4A, 0x62, 0x04, 0xDA, 0x08, 0xE3, 0xEE, + }; + + aes.inv_shift_rows(&state); + try testing.expectEqualSlices(u8, &reference, &state); +} + +test "AES InvMixColumns" { + const reference = [aes.BLOCK_SIZE]u8{ + 0x09, 0x28, 0x7F, 0x47, 0x6F, 0x74, 0x6A, 0xBF, + 0x2C, 0x4A, 0x62, 0x04, 0xDA, 0x08, 0xE3, 0xEE, + }; + var state = [aes.BLOCK_SIZE]u8{ + 0x52, 0x9F, 0x16, 0xC2, 0x97, 0x86, 0x15, 0xCA, + 0xE0, 0x1A, 0xAE, 0x54, 0xBA, 0x1A, 0x26, 0x59, + }; + + aes.inv_mix_columns(&state); + try testing.expectEqualSlices(u8, &reference, &state); +} diff --git a/test/primitive/blockcipher/operation_modes.zig b/test/primitive/blockcipher/operation_modes.zig new file mode 100644 index 0000000..b5ee2d1 --- /dev/null +++ b/test/primitive/blockcipher/operation_modes.zig @@ -0,0 +1,78 @@ +const std = @import("std"); +const testing = std.testing; + +// Use AES to test all of the operation modes. +const aes = @import("ziggy").primitive.blockcipher.aes; + +// ----------------------------------- CBC MODE ----------------------------------- // + +const cbc = @import("ziggy").primitive.blockcipher.operation_mode.cbc; + +test "AES-128-CBC basic test" { + const BS = aes.BLOCK_SIZE; + const KS = aes.KEY_SIZE_128; + const ENC = aes.aes128_encrypt_block; + const DEC = aes.aes128_decrypt_block; + + // Plaintext source: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_block_chaining_(CBC) + // Retrieved 2025/02/01 + const key = [KS]u8{ 0x0c, 0x49, 0x08, 0x75, 0xd4, 0x63, 0x2c, 0x2a, 0x9a, 0x73, 0xab, 0xb6, 0x5c, 0x67, 0x06, 0x0d }; + const iv = [BS]u8{ 0xbe, 0x10, 0x47, 0x59, 0xac, 0xf3, 0x9c, 0x10, 0x1e, 0x2e, 0x37, 0x77, 0x85, 0xe0, 0x13, 0x0e }; + const plaintext = "Ehrsam, Meyer, Smith and Tuchman invented the cipher block chaining (CBC) mode of operation in 1976.[23] In CBC mode, each block of plaintext is XORed with the previous ciphertext block before being encrypted. This way, each ciphertext block depends on all plaintext blocks processed up to that point. To make each message unique, an initialization vector must be used in the first block."; + const ciphertext = [_]u8{ 0x1c, 0x15, 0x02, 0xb8, 0x88, 0xfe, 0xf2, 0x23, 0x72, 0xbf, 0x14, 0x7a, 0xf7, 0x11, 0xde, 0x5a, 0x53, 0x7b, 0x3e, 0xf3, 0xe3, 0xea, 0xb7, 0xd8, 0xe2, 0x4d, 0x9a, 0x82, 0xd8, 0x84, 0x96, 0xc2, 0xa4, 0x8a, 0x82, 0xd5, 0xe9, 0x46, 0x3d, 0x4e, 0xf2, 0xc2, 0xcc, 0x62, 0x73, 0x9d, 0x6e, 0xf8, 0x86, 0xd0, 0xac, 0xb0, 0x0a, 0x80, 0xef, 0x3a, 0x83, 0x74, 0xb7, 0xf9, 0x4f, 0xc5, 0x52, 0x2c, 0x5c, 0x52, 0xd8, 0x41, 0xd4, 0x0b, 0xc2, 0xa9, 0xa6, 0xa3, 0x7b, 0xcc, 0x2e, 0x15, 0xd5, 0xc9, 0x8e, 0x6a, 0x0b, 0x50, 0xe2, 0xbf, 0xcb, 0x09, 0x61, 0x1b, 0x26, 0x78, 0x1a, 0x23, 0xe7, 0xd5, 0x0c, 0x50, 0x3f, 0xc8, 0xa9, 0xb4, 0xea, 0x97, 0xa1, 0xaf, 0xf4, 0xcd, 0xb4, 0x76, 0x0e, 0x4c, 0xa9, 0xfc, 0xf8, 0xbd, 0x45, 0xbb, 0x7b, 0xd3, 0x84, 0x52, 0xac, 0x29, 0xa3, 0xee, 0x62, 0x23, 0xc1, 0x6a, 0xca, 0x1c, 0x62, 0x0a, 0x8b, 0xce, 0x36, 0x9e, 0x7e, 0xb5, 0x5b, 0x2a, 0xa8, 0x85, 0x37, 0x51, 0x0c, 0xed, 0x05, 0xca, 0xb6, 0xc6, 0xd9, 0xc0, 0x0d, 0x9d, 0x57, 0x2a, 0xcd, 0x79, 0xf6, 0x23, 0x36, 0x6d, 0x50, 0x29, 0xff, 0xa2, 0x71, 0x5a, 0x77, 0x0e, 0x26, 0x41, 0x2f, 0x22, 0xc6, 0x8f, 0xdf, 0x2a, 0x0f, 0xb6, 0x19, 0xb7, 0xdb, 0x6b, 0x62, 0x04, 0x50, 0x97, 0x13, 0x8d, 0x60, 0xfa, 0x31, 0x89, 0x36, 0x9e, 0x46, 0xf9, 0x51, 0xcc, 0xd0, 0x90, 0x8b, 0x3d, 0x7a, 0x67, 0xfa, 0x16, 0xdf, 0x3a, 0xe8, 0x96, 0x9c, 0xac, 0x18, 0x7a, 0x12, 0x55, 0x52, 0xcf, 0xc0, 0xeb, 0x0b, 0x49, 0xc2, 0xda, 0x1a, 0x20, 0xaa, 0xee, 0x04, 0x14, 0xa5, 0x23, 0xbf, 0x60, 0x95, 0x09, 0x19, 0x55, 0xa4, 0x93, 0xfe, 0xab, 0x33, 0x6d, 0x4f, 0x51, 0xcb, 0xff, 0xe5, 0x69, 0x39, 0x4c, 0x1a, 0x8c, 0xf9, 0x12, 0x0d, 0x67, 0x7c, 0x10, 0xe9, 0xf7, 0xdf, 0xa6, 0x92, 0x4f, 0x6c, 0x55, 0xca, 0x42, 0xa2, 0x7b, 0xd1, 0xbf, 0x3b, 0xcf, 0xc1, 0xda, 0xad, 0x80, 0x12, 0x95, 0xe7, 0x86, 0x4a, 0x04, 0x1f, 0x01, 0x9e, 0x64, 0xaf, 0x02, 0xcd, 0xb3, 0x84, 0x39, 0x42, 0x8b, 0x04, 0x04, 0x5d, 0x76, 0x20, 0x49, 0x3b, 0x87, 0x03, 0x31, 0xa5, 0x40, 0x4d, 0xca, 0x0e, 0x15, 0x28, 0x03, 0x77, 0xbc, 0xe4, 0x70, 0x69, 0x0d, 0x21, 0x80, 0xa7, 0x92, 0xf7, 0xe0, 0x74, 0x4d, 0xe7, 0xf0, 0x26, 0x55, 0xe7, 0x14, 0xb7, 0xde, 0xa0, 0xf5, 0xd3, 0xe7, 0xea, 0x2b, 0x0b, 0x28, 0x7c, 0xd5, 0x36, 0x0f, 0xb0, 0x83, 0x42, 0x0d, 0x1d, 0xf5, 0xc9, 0xcc, 0xe2, 0x68, 0x16, 0xa1, 0xcc, 0xcb, 0x09, 0x2c, 0x85, 0xdf, 0xec, 0x02, 0xf5, 0x5f, 0x67, 0x6d, 0x04, 0xae, 0x3a, 0x34, 0xa8, 0x46, 0x9d, 0x80, 0xd8, 0xee, 0xfb, 0x12, 0x9f, 0x25, 0xd5, 0x0b, 0x38, 0xfe, 0x99, 0x59, 0xeb, 0xb9 }; + + // Test encryption. + { + const ct_length = comptime cbc.get_padded_buffer_length(BS, plaintext); + var ct_buffer: [ct_length]u8 = undefined; + + var ctx = cbc.cbc_pkcs7_encrypt_new(BS, KS, ENC, &key, &iv); + defer cbc.cbc_pkcs7_encrypt_destroy(BS, KS, ENC, &ctx); + + var pt_processed_bytes: usize = 0; + while (plaintext.len - pt_processed_bytes >= aes.BLOCK_SIZE) : (pt_processed_bytes += aes.BLOCK_SIZE) + cbc.cbc_pkcs7_encrypt_block( + BS, + KS, + ENC, + &ctx, + @ptrCast(plaintext[pt_processed_bytes .. pt_processed_bytes + aes.BLOCK_SIZE]), + @ptrCast(ct_buffer[pt_processed_bytes .. pt_processed_bytes + aes.BLOCK_SIZE]), + ); + + try cbc.cbc_pkcs7_encrypt_final(BS, KS, ENC, &ctx, plaintext[pt_processed_bytes..], @ptrCast(ct_buffer[pt_processed_bytes..])); + + try testing.expectEqualSlices(u8, ciphertext[0..], ct_buffer[0..]); + } + + // Test decryption. + { + var pt_buffer: [ciphertext.len]u8 = undefined; + + var ctx = cbc.cbc_pkcs7_decrypt_new(BS, KS, DEC, &key, &iv); + defer cbc.cbc_pkcs7_decrypt_destroy(BS, KS, DEC, &ctx); + + var ct_processed_bytes: usize = 0; + while (ciphertext.len - ct_processed_bytes >= 2 * aes.BLOCK_SIZE) : (ct_processed_bytes += aes.BLOCK_SIZE) + cbc.cbc_pkcs7_decrypt_block( + BS, + KS, + DEC, + &ctx, + @ptrCast(ciphertext[ct_processed_bytes .. ct_processed_bytes + aes.BLOCK_SIZE]), + @ptrCast(pt_buffer[ct_processed_bytes .. ct_processed_bytes + aes.BLOCK_SIZE]), + ); + + const residue_length = try cbc.cbc_pkcs7_decrypt_final( + BS, + KS, + DEC, + &ctx, + ciphertext[ct_processed_bytes..], + pt_buffer[ct_processed_bytes..], + ); + + try testing.expectEqual(plaintext.len % aes.BLOCK_SIZE, residue_length); + try testing.expectEqualSlices(u8, plaintext[0..], pt_buffer[0..plaintext.len]); + } +} diff --git a/test/primitive/digest/sha.zig b/test/primitive/digest/sha.zig new file mode 100644 index 0000000..b7b8bf0 --- /dev/null +++ b/test/primitive/digest/sha.zig @@ -0,0 +1,327 @@ +const std = @import("std"); +const testing = @import("std").testing; + +const sha1 = @import("ziggy").primitive.digest.sha1; +const sha2 = @import("ziggy").primitive.digest.sha2; + +// ----------------------------------- SHA-1 ----------------------------------- // + +// https://www.di-mgt.com.au/sha_testvectors.html +test "SHA-1 basic test" { + try run_hash_precomputed_tests( + sha1.Sha1Ctx, + sha1.DIGEST_LENGTH, + sha1.sha1_new, + sha1.sha1_update, + sha1.sha1_final, + &.{ + .{ .message = "", .hash = "da39a3ee5e6b4b0d3255bfef95601890afd80709" }, + .{ .message = "abc", .hash = "a9993e364706816aba3e25717850c26c9cd0d89d" }, + .{ + .message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .hash = "84983e441c3bd26ebaae4aa1f95129e5e54670f1", + }, + .{ + .message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + .hash = "a49b2446a02c645bf419f995b67091253a04a259", + }, + }, + ); +} + +test "SHA-1 padding test" { + // Here we test every possible length of the message in the last block + // to make sure that the padding is correct in every single case. + + // The following are the hashes of the ASCII strings '', 'a', 'aa', etc. + // up until 63 (= [SHA-1 block size in bits] / 8 - 1) concatenated 'a's. + const reference = [64]*const [2 * sha1.DIGEST_LENGTH]u8{ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", "e0c9035898dd52fc65c41454cec9c4d2611bfb37", "7e240de74fb1ed08fa08d38063f6a6a91462a815", "70c881d4a26984ddce795f6f71817c9cf4480e79", "df51e37c269aa94d38f93e537bf6e2020b21406c", "f7a9e24777ec23212c54d7a350bc5bea5477fdbb", "e93b4e3c464ffd51732fbd6ded717e9efda28aad", "b480c074d6b75947c02681f31c90c668c46bf6b8", "2882f38e575101ba615f725af5e59bf2333a9a68", "3495ff69d34671d1e15b33a63c1379fdedd3a32a", "755c001f4ae3c8843e5a50dd6aa2fa23893dd3ad", "384fcd160ab3b33174ea279ad26052eee191508a", "897b99631295d204db13e863b296a09e70ab1d65", "128c484ff69fcdc1f82cd3781595cac5185e688f", "7e13c003a8256cd421055563c5da6571d50713c9", "3499c60eea227453c779de50fc84e217e9a53a18", "321a618ba6830de900738b0814d0c9f28ff2fece", "0478095c8ece0bbc11f94663ac2c4f10b29666de", "1335bfa62671b0015c6e20766c07035868edb8f4", "38666b8ba500faa5c2406f4575d42a92379844c2", "035a4ee5d60816878caec161d6cb8e00e9cc539b", "8c2a4e5c8f210b6aaa6c95e1c8e21351959f4541", "85e3737bb8ab36e2866501e517c46fffc085313e", "b1c76aec7674865d5346b3b0d1cb2c223c53e73e", "44f4647e1542a79d7d68ceb7f75d1dbf77fdebfc", "de8280c3a1c7db377f1ec7107c7fb62d374cc09c", "52a00b8461593ce33409d7c5d0411699cbf9cda3", "06587751ce11a8703abc64cab55b0b96d88341aa", "498a75f314a645671bc79a118df385d0d9948484", "cd762363c1c11ecb48611583520bba111f0034d4", "65cf771ad2cc0b1e8f89361e3b7bec2365b3ad24", "68f84a59a3ca2d0e5cb1646fbb164da409b5d8f2", "2368a1ac71c68c4b47b4fb2806508e0eb447aa64", "82d5343f4b2f0fcf6e28672d1f1a10c434f213d5", "f4d3e057abac5109b7e953578fa97968ea34f43a", "6c783ce5cc13ea5ce572eddfaba02f9d1bb90905", "9e55bf6ab8f14b37cc6f69eb7374be6c5cbd2d07", "1290c28910a6c12c9a131f0ecb523114f20f14c2", "4f5fc75bd3c93bccc09fc2de9c95442456053faf", "a56559418dc7908ce5f0b24b05c78e055cb863dc", "52cedd6b110e4330b5186478736afa5203c4f9ea", "32e067e0414932c3edd95fc4176a54bff1ddfe29", "7bd258f2f4cc4b02fca4ea157f55f6d88d26d954", "df51a19b291586bf46450aec1d775f3e02799b55", "4642fe68c57cd01fc68fc11b7f22b940328a7cc4", "03a4de84c189a836eaee643041b34ad2386db70d", "25883f7a0e732e9ab10e594ea59425dfe4d90359", "3e3d6e12b933133de2caa248ea12bd193a67f206", "1e666934c5a35f509aa31bbd9af8a37a1ed13ba6", "6c177354157989a2c6cd7bac80465b13bea25832", "aca32b501c231ef8e2d8703e71415bfbe4ccbc64", "e6479c70bbac662e4cc134cb8bdaade59ff55b66", "d9b66a0801459c8094398ef8f04700a8569c9906", "b05d71c64979cb95fa74a33cdb31a40d258ae02e", "c1c8bbdc22796e28c0e15163d20899b65621d65a", "c2db330f6083854c99d4b5bfb6e8f29f201be699", "f08f24908d682555111be7ff6f004e78283d989a", "5ee0f8895f4e1aae6a6661de5c432e34188a5a2d", "dbc8b8f59ff85a2b1448ed873484b14bf0507246", "13d956033d9af449bfe2c4ef78c17c20469c4bf1", "aeab141db28af3353283b5ccb2a322df0b9b5f56", "67b4b3923fa178d788a9611b76446c96431071f2", "03f09f5b158a7a8cdad920bddc29b81c18a551f5" }; + + var digest_buffer: [sha1.DIGEST_LENGTH]u8 = undefined; + for (0..64) |i| { + var ctx = sha1.sha1_new(); + for (0..i) |_| + try sha1.sha1_update(&ctx, "a"); + sha1.sha1_final(&ctx, &digest_buffer); + + const ref = hex_to_bytes(sha1.DIGEST_LENGTH, reference[i]); + try testing.expectEqualSlices(u8, ref[0..], digest_buffer[0..]); + } +} + +test "SHA-1 maximum length violation (simulated)" { + var ctx = sha1.sha1_new(); + ctx.message_length = (1 << 61) - 1; // 2^64 - 8 bits + try testing.expectError(sha1.MessageLengthLimitExceeded, sha1.sha1_update(&ctx, "a")); +} + +// ----------------------------------- SHA-224 ----------------------------------- // + +// https://www.di-mgt.com.au/sha_testvectors.html +test "SHA-224 basic test" { + try run_hash_precomputed_tests( + sha2.Sha256Ctx, + sha2.SHA_224_DIGEST_LENGTH, + sha2.sha224_new, + sha2.sha224_update, + sha2.sha224_final, + &.{ + .{ .message = "", .hash = "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" }, + .{ .message = "abc", .hash = "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7" }, + .{ + .message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .hash = "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525", + }, + .{ + .message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + .hash = "c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", + }, + }, + ); +} + +test "SHA-224 padding test" { + // Here we test every possible length of the message in the last block + // to make sure that the padding is correct in every single case. + + // The following are the hashes of the ASCII strings '', 'a', 'aa', etc. + // up until 63 (= [SHA-224 block size in bits] / 8 - 1) concatenated 'a's. + const reference = [64]*const [2 * sha2.SHA_224_DIGEST_LENGTH]u8{ "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", "abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5", "2ef29e646f6de95de993a59eb1a94cbf52986892949a0abc015a01f7", "ed782653bfec275cf37d027511a68cece08d1e53df1360c762ce043a", "fb56e0a07d1d4ba4a73d42a07d671f65f96513dc12f9d25f2b5ee86e", "04c15392c64c3db52f9a7fcfb7c0c370900e0308007617899430d088", "2886915dd9dedb0ce015f500ffb570901a69a8357728f893eef45344", "b1de26e908098a9b462c11fdd577613d356a7ff06ddb42fda3ab002b", "57dde80cb5dd10cee89424d7d9d8c4fd7949c882da78110e660dec06", "be63cd48d0b068927261c401a33a386136911c3420a5c335e00599fd", "194c4c5045aee1019b77cb1be88ac68034eb2ddd5a8368054141a11f", "5fa4fb5daff0a9b069061839e5605caff0446465f82268775a226333", "85e4d4fcca88691c8008ce19bd9a3de6b9814dd055c8d21bfd4037bb", "da79142fa39f7f11b990aa9dc4994b6e9ce49454058d5423869acf4c", "bd3278d31dec829db380cfe7d7100fecb4b853f054684687f4c27717", "e541fff1289f562eff589da3c8358d91e4dd589bf5dcc37555eb84fe", "0e0a4176c0c6966926acc6fccd2febd418a407df26f76645139d1320", "d5f1096a707710cf5038ed370f7839543eaea0ed054c7484319e2f9c", "e3a0b5557f7894cbb18d6ed06268242212f098d61e179e80a5e96dbc", "fbb6819175a23811c01073f6142af14e80f2d0c598bed6649f307e1f", "43586eff52cbaf9f22482f34a9437ff45bd2e7312ad586b3dd82802f", "2b9d728b01e2c27a40c5fc27d51d5c93f6a160f0f25da8d924e2b570", "eb7ff45dd3d5871ffd6aee24400367cc835aa0d2311d46b2a48b0424", "ac4f39fb481df8f86b6ddf6e527e61893d259b49810416584b468d22", "c4da7f3f63b9da485b734a270f1af6d132b15f177e06445faec5586b", "91a67814117203c32cfc333869bc74f8c721c10672c3516042d88a0b", "7824f1cc7591643231cefe91bbf6fa88b233bf1e8229a59a2cd01c15", "735d169738ebd05edc44cc49a6a99352815cafec12c10d5bd27e8359", "919523d5847d1ee4f9d72f6bd86e3d1bfb785831237a9d580b87448c", "4c166aebdf561231f2b679d8e8457667c0f374043de76d8e7306cb49", "31723190cc4bc6ae686caeea44c54b7b50e30941b8dca3404fe180d8", "f9b67111fdab7e860d1dbe801efa9b75b563e0ee606a9cbabc1288b9", "37e7740b3af132a8edc5e1636817ce1625b831e0ffd47e5c93d4e3b8", "21093fb6a83a36a2b55afa76da42d66de4ebc3b99fb340739d068500", "62d7fcd624a2c1d674ead77584bd66e06d2a6ed160702a594dd6a26e", "507fba56abf0fe3856d0d61527d2a96b18e234bad594bdc969fbfdf4", "6d3a8a79bcc6222986f3b3f17b716774de66d321d51ec34e49995b18", "b622cd8b687970603662b70401a36e464f6860ad337be21c86f17dce", "9c63438f755166862211cc708592f918d31c24e7e70080d6d08e3685", "0126a8fdfaea25bb65fa0b8d6586cd64eee956369b5d2c814607e9f9", "47e263d6419979086bd9a8c4f93591cba373912b68e2ae86085bd2df", "7d8c659cacc50ffc474948b79d65ed98bdab16f6b8a511d184593c86", "da1619b92df1a450be79a14a21e270cb2257230f7dfdc61128e0b49e", "babea0fecfea0aa82d1580abca47dad191f5b9d5a51788d64f678393", "61dd4e5d414a5ae61e76a9b7524223f3cdaf7c9c02b73c175b3e03dd", "fc50179169329ce825404d027fdab44058efc9f28ccddd694a31960e", "b5b7bef9dba5e6a57cecdc03d9a539667f3ca131343de3b6763d7463", "58756e846cce4e08b2ae1103ec3dd2c5755c15f94c1127782dde82c5", "73e0009122e9f4d311459277b81009e9cecc4b3dccf785d4ad476a14", "c0af565a56aeccfd2d40455f20d2c9431a7ab88c61e94973c97cff91", "df427221dc453d5c1466081d9d6e9da3155d5d0dff2a90eb0425036c", "7820fc9fc80c5ed788738da53fbfa6cf1fa981d656a3bb1e68cdf281", "163a72bc0462179bf0486f8a139da514913670d12bbe1d84efc44556", "3fae7c2d692c1610c4a20a17a790d256c3b0071bcdf6fb7fb9538681", "282e1dec88fa36a1070631cca69e3c08a5e18e29fb0b6f6927fbcc0d", "fb0bd626a70c28541dfa781bb5cc4d7d7f56622a58f01a0b1ddd646f", "d40854fc9caf172067136f2e29e1380b14626bf6f0dd06779f820dcd", "b5d09534784ab6578128bce7f28a96a56e3b45c4f734f74739076249", "00df3f1eaa489fd28a9de6e6d7b55402c4e3a56928c5043d77240237", "a82137820aaae9e66f277c3a9254f4a6078c47b410bc9d9a761c2e0b", "efda4316fe2d457d622cf1fc42993d41566f77449b7494b38e250c41", "54e3b540f6792b6a4570f5225717686fbf670fd0dfd3802e4ace9d77", "0daa67402af98b9988c65471b2589dbcdd8bb39569ed77c592aca4a4", "1d4e051f4d6fed2a63fd2421e65834cec00d64456553de3496ae8b1d" }; + + var digest_buffer: [sha2.SHA_224_DIGEST_LENGTH]u8 = undefined; + for (0..64) |i| { + var ctx = sha2.sha224_new(); + for (0..i) |_| + try sha2.sha224_update(&ctx, "a"); + sha2.sha224_final(&ctx, &digest_buffer); + + const ref = hex_to_bytes(sha2.SHA_224_DIGEST_LENGTH, reference[i]); + try testing.expectEqualSlices(u8, ref[0..], digest_buffer[0..]); + } +} + +test "SHA-224 maximum length violation (simulated)" { + var ctx = sha2.sha224_new(); + ctx.message_length = (1 << 61) - 1; // 2^64 - 8 bits + try testing.expectError(sha2.MessageLengthLimitExceeded, sha2.sha224_update(&ctx, "a")); +} + +// ----------------------------------- SHA-256 ----------------------------------- // + +// https://www.di-mgt.com.au/sha_testvectors.html +test "SHA-256 basic test" { + try run_hash_precomputed_tests( + sha2.Sha256Ctx, + sha2.SHA_256_DIGEST_LENGTH, + sha2.sha256_new, + sha2.sha256_update, + sha2.sha256_final, + &.{ + .{ .message = "", .hash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }, + .{ .message = "abc", .hash = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" }, + .{ + .message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .hash = "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1", + }, + .{ + .message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + .hash = "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1", + }, + }, + ); +} + +test "SHA-256 padding test" { + // Here we test every possible length of the message in the last block + // to make sure that the padding is correct in every single case. + + // The following are the hashes of the ASCII strings '', 'a', 'aa', etc. + // up until 63 (= [SHA-256 block size in bits] / 8 - 1) concatenated 'a's. + const reference = [64]*const [2 * sha2.SHA_256_DIGEST_LENGTH]u8{ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", "961b6dd3ede3cb8ecbaacbd68de040cd78eb2ed5889130cceb4c49268ea4d506", "9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0", "61be55a8e2f6b4e172338bddf184d6dbee29c98853e0a0485ecee7f27b9af0b4", "ed968e840d10d2d313a870bc131a4e2c311d7ad09bdf32b3418147221f51a6e2", "ed02457b5c41d964dbd2f2a609d63fe1bb7528dbe55e1abf5b52c249cd735797", "e46240714b5db3a23eee60479a623efba4d633d27fe4f03c904b9e219a7fbe60", "1f3ce40415a2081fa3eee75fc39fff8e56c22270d1a978a7249b592dcebd20b4", "f2aca93b80cae681221f0445fa4e2cae8a1f9f8fa1e1741d9639caad222f537d", "bf2cb58a68f684d95a3b78ef8f661c9a4e5b09e82cc8f9cc88cce90528caeb27", "28cb017dfc99073aa1b47c1b30f413e3ce774c4991eb4158de50f9dbb36d8043", "f24abc34b13fade76e805799f71187da6cd90b9cac373ae65ed57f143bd664e5", "a689d786e81340e45511dec6c7ab2d978434e5db123362450fe10cfac70d19d0", "82cab7df0abfb9d95dca4e5937ce2968c798c726fea48c016bf9763221efda13", "ef2df0b539c6c23de0f4cbe42648c301ae0e22e887340a4599fb4ef4e2678e48", "0c0beacef8877bbf2416eb00f2b5dc96354e26dd1df5517320459b1236860f8c", "b860666ee2966dd8f903be44ee605c6e1366f926d9f17a8f49937d11624eb99d", "c926defaaa3d13eda2fc63a553bb7fb7326bece6e7cb67ca5296e4727d89bab4", "a0b4aaab8a966e2193ba172d68162c4656860197f256b5f45f0203397ff3f99c", "42492da06234ad0ac76f5d5debdb6d1ae027cffbe746a1c13b89bb8bc0139137", "7df8e299c834de198e264c3e374bc58ecd9382252a705c183beb02f275571e3b", "ec7c494df6d2a7ea36668d656e6b8979e33641bfea378c15038af3964db057a3", "897d3e95b65f26676081f8b9f3a98b6ee4424566303e8d4e7c7522ebae219eab", "09f61f8d9cd65e6a0c258087c485b6293541364e42bd97b2d7936580c8aa3c54", "2f521e2a7d0bd812cbc035f4ed6806eb8d851793b04ba147e8f66b72f5d1f20f", "9976d549a25115dab4e36d0c1fb8f31cb07da87dd83275977360eb7dc09e88de", "cc0616e61cbd6e8e5e34e9fb2d320f37de915820206f5696c31f1fbd24aa16de", "9c547cb8115a44883b9f70ba68f75117cd55359c92611875e386f8af98c172ab", "6913c9c7fd42fe23df8b6bcd4dbaf1c17748948d97f2980b432319c39eddcf6c", "3a54fc0cbc0b0ef48b6507b7788096235d10292dd3ae24e22f5aa062d4f9864a", "61c60b487d1a921e0bcc9bf853dda0fb159b30bf57b2e2d2c753b00be15b5a09", "3ba3f5f43b92602683c19aee62a20342b084dd5971ddd33808d81a328879a547", "852785c805c77e71a22340a54e9d95933ed49121e7d2bf3c2d358854bc1359ea", "a27c896c4859204843166af66f0e902b9c3b3ed6d2fd13d435abc020065c526f", "629362afc62c74497caed2272e30f8125ecd0965f8d8d7cfc4e260f7f8dd319d", "22c1d24bcd03e9aee9832efccd6da613fc702793178e5f12c945c7b67ddda933", "21ec055b38ce759cd4d0f477e9bdec2c5b8199945db4439bae334a964df6246c", "365a9c3e2c2af0a56e47a9dac51c2c5381bf8f41273bad3175e0e619126ad087", "b4d5e56e929ba4cda349e9274e3603d0be246b82016bca20f363963c5f2d6845", "e33cdf9c7f7120b98e8c78408953e07f2ecd183006b5606df349b4c212acf43e", "c0f8bd4dbc2b0c03107c1c37913f2a7501f521467f45dd0fef6958e9a4692719", "7a538607fdaab9296995929f451565bbb8142e1844117322aafd2b3d76b01aff", "66d34fba71f8f450f7e45598853e53bfc23bbd129027cbb131a2f4ffd7878cd0", "16849877c6c21ef0bfa68e4f6747300ddb171b170b9f00e189edc4c2fc4db93e", "52789e3423b72beeb898456a4f49662e46b0cbb960784c5ef4b1399d327e7c27", "6643110c5628fff59edf76d82d5bf573bf800f16a4d65dfb1e5d6f1a46296d0b", "11eaed932c6c6fddfc2efc394e609facf4abe814fc6180d03b14fce13a07d0e5", "97daac0ee9998dfcad6c9c0970da5ca411c86233a944c25b47566f6a7bc1ddd5", "8f9bec6a62dd28ebd36d1227745592de6658b36974a3bb98a4c582f683ea6c42", "160b4e433e384e05e537dc59b467f7cb2403f0214db15c5db58862a3f1156d2e", "bfc5fe0e360152ca98c50fab4ed7e3078c17debc2917740d5000913b686ca129", "6c1b3dc7a706b9dc81352a6716b9c666c608d8626272c64b914ab05572fc6e84", "abe346a7259fc90b4c27185419628e5e6af6466b1ae9b5446cac4bfc26cf05c4", "a3f01b6939256127582ac8ae9fb47a382a244680806a3f613a118851c1ca1d47", "9f4390f8d30c2dd92ec9f095b65e2b9ae9b0a925a5258e241c9f1e910f734318", "b35439a4ac6f0948b6d6f9e3c6af0f5f590ce20f1bde7090ef7970686ec6738a", "f13b2d724659eb3bf47f2dd6af1accc87b81f09f59f2b75e5c0bed6589dfe8c6", "d5c039b748aa64665782974ec3dc3025c042edf54dcdc2b5de31385b094cb678", "111bb261277afd65f0744b247cd3e47d386d71563d0ed995517807d5ebd4fba3", "11ee391211c6256460b6ed375957fadd8061cafbb31daf967db875aebd5aaad4", "35d5fc17cfbbadd00f5e710ada39f194c5ad7c766ad67072245f1fad45f0f530", "f506898cc7c2e092f9eb9fadae7ba50383f5b46a2a4fe5597dbb553a78981268", "7d3e74a05d7db15bce4ad9ec0658ea98e3f06eeecf16b4c6fff2da457ddc2f34" }; + + var digest_buffer: [sha2.SHA_256_DIGEST_LENGTH]u8 = undefined; + for (0..64) |i| { + var ctx = sha2.sha256_new(); + for (0..i) |_| + try sha2.sha256_update(&ctx, "a"); + sha2.sha256_final(&ctx, &digest_buffer); + + const ref = hex_to_bytes(sha2.SHA_256_DIGEST_LENGTH, reference[i]); + try testing.expectEqualSlices(u8, ref[0..], digest_buffer[0..]); + } +} + +test "SHA-256 maximum length violation (simulated)" { + var ctx = sha2.sha256_new(); + ctx.message_length = (1 << 61) - 1; // 2^64 - 8 bits + try testing.expectError(sha2.MessageLengthLimitExceeded, sha2.sha256_update(&ctx, "a")); +} + +// ----------------------------------- SHA-512 ----------------------------------- // + +// https://www.di-mgt.com.au/sha_testvectors.html +test "SHA-512 basic test" { + try run_hash_precomputed_tests( + sha2.Sha512Ctx, + sha2.SHA_512_DIGEST_LENGTH, + sha2.sha512_new, + sha2.sha512_update, + sha2.sha512_final, + &.{ + .{ .message = "", .hash = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" }, + .{ .message = "abc", .hash = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f" }, + .{ + .message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .hash = "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445", + }, + .{ + .message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + .hash = "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909", + }, + }, + ); +} + +test "SHA-512 padding test" { + // Here we test every possible length of the message in the last block + // to make sure that the padding is correct in every single case. + + // The following are the hashes of the ASCII strings '', 'a', 'aa', etc. + // up until 127 (= [SHA-512 block size in bits] / 8 - 1) concatenated 'a's. + const reference = [128]*const [2 * sha2.SHA_512_DIGEST_LENGTH]u8{ "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75", "f6c5600ed1dbdcfdf829081f5417dccbbd2b9288e0b427e65c8cf67e274b69009cd142475e15304f599f429f260a661b5df4de26746459a3cef7f32006e5d1c1", "d6f644b19812e97b5d871658d6d3400ecd4787faeb9b8990c1e7608288664be77257104a58d033bcf1a0e0945ff06468ebe53e2dff36e248424c7273117dac09", "1b86355f13a7f0b90c8b6053c0254399994dfbb3843e08d603e292ca13b8f672ed5e58791c10f3e36daec9699cc2fbdc88b4fe116efa7fce016938b787043818", "f368a29b71bd201a7ef78b5df88b1361fbe83f959756d33793837a5d7b2eaf660f2f6c7e2fbace01965683c4cfafded3ff28aab34e329aa79bc81e7703f68b86", "7a9b2a35095dcdfedfdf0ef810310b409e38c92c20cbd51088ea5e4bc4873bdacfeb29f14b7f2ed033d87fad00036da83d5c597a7e7429bc70cec378db4de6a6", "d1ff70451d1f0b94f551461c5ca239e498f571add4542c381e3eef84eac24aea6d12f8333ef4a05847e205ef4bb094921314364e7648176f567863d982042a85", "f74f2603939a53656948480ce71f1ce466685b6654fd22c61c1f2ce4e2c96d1cd02d162b560c4beaf1ae45f3471dc5cbc1ce040701c0b5c38457988aa00fe97f", "507294e03fdbc784dc3f575706c3ce53b3c4c37065e89ddd6b6de8bd2be655e15412e27695d32d2d6a3a0eaabe36ebddde78af0122a65ec41128c98c6fd30554", "4714870aff6c97ca09d135834fdb58a6389a50c11fef8ec4afef466fb60a23ac6b7a9c92658f14df4993d6b40a4e4d8424196afc347e97640d68de61e1cf14b0", "2d5be0f423fee59bf2149f996e72d9f5f8df90540a7d23b68c0d0d9a9a32d2c144891ca8fe4a3c713cb6eb2991578541dad291ba623dbd7107c6a891ba00bcc8", "a88ac22ca41e71e252c1f0d925de1ec174346e097c695e948d23016ab54cf21da6f0b0490ffa752bcc4893afc0c2caa64307705d1996f2959a3e03dc420c68cf", "b0b8828df9473f2763f9a48b0a9683451e98155436c2eff64c628fedbba0cca2360312271f3971f2969b1f828b1bb8251d3a43e12361824aca14f9a9affe2171", "831ce89c92608efda86cdf89d36e855bc73c5d17b2162c0013c14a2676ef4794dc53de6a54c1e3fb782acef5dd1192d36d0ab7fa88262cf0f6a16950e44a828c", "0b6f7fb0679a1ef009d5c8c70551d3b7013a5881b41ec2d597c3cf22e6aa13a1cf9a925b7a012feb67956c83b91cc32bef1de4cdbc6b2ff0cffbba319b54d15e", "987d0fc93db6a73fdb16493690fb42455c7c6fbafe9a276965424b12afad3512fb808d902faa8a019d639dc5ad07c235805e08f396147cf435913cfed501f65a", "06ef0364617146f6200c2cbc4280202226d701c2961940f57e7b60677587c66087f23bbcffa0de8692221f9434ac9a21e6df6428377cd145e1a456e2359d2cf6", "10dbd292472d3ff7279f3dac7fdb83c296bd61cbe80b0e26fbc14f871fd9771180d83879e812ec9841ba15a110e84a589c0eedfc14427c23ba56fa4fb7773de0", "1d383178e64d7071e749b2d560a22abc97e6514c31e800b5cc12c6f72ad43a9a0c4ce7db246219f3dea09afae6044a484de203148cb55f1057ee37b9420073bb", "d87a10a0bee363dcdf764831e807df5ee5500483c09056b38f854606f9e665566264b15af9fee8f9b84f3a7b6ddb67b92996ef790d10e899ba0758d5ab650caf", "1f60925cd5271a8ec9eb49ea4bf187f6a7dbc22eeb0e2dbc89d8381d0e73dea5bff5375a6db7e49fc427cb4fdf9f7ece577037adf91decfc38f303b1cb79ad44", "0ff3b2ffbda4cf938263e9449735618103a4d6a0cdeeda57367f6377d23849c3dce6851377f8f1b3d2ce3ff1dd6de0d64920d7790994782b4a8e2697e31f1900", "c6020ac00b701699227ccc9355156da0ad1d521ada5949cc89dd00661725be08fea4a2519ceb1e50acdd16e7127783f7ed5bfabe5238ce0da7ad2b4174c5509a", "e37ff6da226042c6fdd066c20f00e0d09c4f4dea104d8ea1fc513496ef24a0e17cd4bfb2e95781329a45d3885ca0e20f88e453dc9a4c4dc2acd0be756e3356b8", "4d272d73d4000f885ad1be048b7c7f92c2a8e5a01f30a96ed82849223606ad639f73155c85a128fbd2c26d3de30fb207e57b9f7ff21bfc79e0d7f0e2fb5189dc", "90cce547b76967676972c60e83944ffdc143078b6b40c722a0f2ac90d78eed0057843213076a9a7df528d0c0ebf3c00a91ae1c37f8850173fa2c03c41b6168ea", "4cea8c7ef657f9177c286081f8f016adae91a131a496e939ac86060e691afba57accc08ddbc423eb9d0817725faad9554c60f314929f30e881871e8782228918", "24077df741cb7ba88537d62c55fbff3ea81b603c31e6fd0d2e5d28e1a505f6192d5b2c1f98011152fef2c75901f66d489c045a4a3f98705c2b244c004f1579d4", "2af97e464526c024ef466db4616559919b769b350b7f6830ecfa5ffdeacd6eb570daf0ed25c0c56b194119f15247f63f5b94b54e01283b4b7a832586acac9e09", "abd9c33f8c791b27dd614e80ad77f1ff33c2621663b4dcbe5a88417a8b95b8d6788a9320678b589aa5b405897b2113523df1defa304953ea2ae1a229f2736450", "ad9e7ae1f68786c33ca713d4632b29ebcc9c9c040fc176ead8acb395a14c08324e824f7531f6a50ba0d4a17de958a08e54c9597dcc30781e22c0d953d06f9f4d", "020089a47cb0761c222c323aec2bdecdaa7a0d0ec094cda8c5755ba26844453c25b37e4bc98aab8adc55c9da75bcd83af62905d62e9044a5d64cd93d93b54b34", "d578a2d3fab982fbc7f1fa20630713c5a2a2cd9654a53822978d3efd2becc2a02e1fa1391dd11e139d1489aba688367ea9286e2a9ba8ef67009c80df81998614", "c1de14e1a09b03c688bd568ff4b4fa086baed2181d0d99a219fb937484ba67f093efe36966b0ea5209dadb6ef4f67c2d1f753d49c083a6241d2ab4557509404e", "cbb50e7e8a14cb9df08642609b6d737302d78cdcff74e1f53e895ae4a7cb093a571364dbbc2797962f54366ef65ead1c41a44ffe2ab0d56b7ae01e99a7a4e6fb", "85a564722dbefd268ed2e2e70fb377306c207a9c7edb634adcd79b8829aaad700c3a26cce44eba99aff46c4349f5e5056a87fcd2b63dd08b8b7b1f2f3ea06d6b", "ae77859a42c40e3973aa42bc8fbe8713444f65173580507d7c4bcc7c85d7f8c93204f433d506e912504ea37c766af17e649bdf6c8356f6e8e65bf4e9321987cb", "5b7c791e8018b14752ca7b91386d3ddbd3f9307a69ca71d977e274171aa5cae0b1a03960e842ca05fc0b95205a243fc8b28c36e4dd60ff000a47fb63547e6a0c", "f140bd9a11c309eb9da6ae1c8360cf2bc952a41a9ff228c066c0811df508313f59f1b6e6ffc6d14ef967f477c69463974aefd78d1c1dec9d8d35ff0c81dc29e8", "e411795f8b2a38c99a7b86c888f84c9b26d0f47f2c086d71a2c9282caf6a898820e2c1f3dc1fa45b20178da40f6cb7e4479d3d7155845ed7a4b8698b398f3d0c", "9178f65d74628c56ce3ace5b9ae7ecb84fc8a840ae33367a9c5534e6556301dc4fea4927d82289483496c39b929afb4a4ea92ded82c02057a7b8029828d8fb8d", "e11e1d056266f561bf3a9dede38228700e59971b3be992fea66a687887441976d8b29193707211dfb94dd1f7918473c3e99ff48a7c91068a1aaf7054febb9e2d", "acbaf243155ab6ca5f44c13061757fa060acbc5cf43d996b4f47209c22bf70c29af8dbc5c0a68ca45e42142db1540d2db70f6f27a917a3019dc92dadd0f639d6", "647b6deadb5aeb56e4087414fe2a76d6f57083dd6303a19e152445d108dc2bcd17926981d500b19b913b36a3b343b2e6781c805c1897664a218a2cfecc6a5238", "3ca97cdefcc384485ec2b6bebffe63d98f5675132a8b43d1f38bad4ff1982264fc4876ec637e918f855d855945b9b84eb82386bc6fc1e92695ec623001f8ddd1", "7b549433b4ef39abb90dcd3eb90c63562b7f3daa056670b2f712ebb7e9e78adfe7423e4b39810c1109fb640e84d32047468b155fe342d13e7f4d7ee019fa5922", "ec1d753e2280b8136b686ec81b03b3f8a7f98152868e3a68f0a2c456082c2faaa93c39ad573a6d21f4a3350df602249dc89ad28620d27ecc1d9e1f258badcd04", "bd582a787a21036df7049d501879977625601527d7ddd6f707463cb8b3839fbedbe233b8e69f1696d0e82b168d3491a3dbb6005b6224c198601dafbd50e14365", "4b4ff3bc763a976c16afdd8082efc7a5c98d60342f0ed5a654f567dacbc6414833e60ed1d6770bd42638fdae605c69be0219532125a186609f0825376ab59e45", "bdba173e58132092b0aa67ea5080f247e5b3710630a789c519b311f3848588f0bac8db3091ff8fd16875601636bef625e43b3d82cb51eb6693cdd1b2a5c872b8", "78a0eb5d7c0b05284056e1f19cbb42a99470bc81de4f9bc48708d28c5a877626e69167c58d4e840a7aa699bc6dddd972564d84ea502b41d83878e98e68f83c81", "a45021322d3f30747b3ceb7c1b1975ac4698984be76915f82cdeefe769f115d9dc5c70549e897b0ab8d5d61fc9e73ad1f7f49db39bb4e1298ac833d290eb1d04", "0b08accaae7044e54074fdcb7404a10c0703144d4499a644d9cfc60f973dc27dcdc65ac31750f7407ba96d025fb699e64ddcd1acd0dabafeeafccb5733225d3b", "07a2200290a2b7423a94f71892554b17196e2301e2e446ce09f65abcb45523268274128038925489671af9b899747b80e35a0a1b8613ecfb44e6be3152a2fd93", "b0220c772cbf6c1822e2cb38a437d0e1d58772417a4bbb21c961364f8b6143e05aa6316dca8d1d7b19e16448419076395f6086cb55101fbd6d5497b148e1745f", "962b64aae357d2a4fee3ded8b539bdc9d325081822b0bfc55583133aab44f18bafe11d72a7ae16c79ce2ba620ae2242d5144809161945f1367f41b3972e26e04", "d3115798e872fc1ca6b276368e8ea0926daec6ab1f8f08297e4348ff5f5fe4c6e5205413271babafd4929b070754bc5800e5db44790666ec4e2f6ac52a17e163", "2282084c042e92d7ba1a9e1ee5527762e91c4ffee7a8676c4a4a0facefad352bed2d3c322368cfe813186084c5386e9f22f803dfe0a1b424cab3e0a95a6dc3f9", "fd4eaf2071e8d9cf36688c3be714f5e363a5b4932f509914c613d1b8987d188e82cdd12b6ab07ea2f676fad1789275ef37253260a817a61079bc0ea567ee094a", "5ac08e89d884de3f086c60e8f36e754cf0ae9be2f018a87b7f71b15c81356410077eaa075010eb48959783ba490dc7c9fec53573848d8929bd5fc0574552f58f", "0202004b03bf7be513c96ef3fa6e48fce6e02f858d3bd95edba5adbdce60b2d7a4aa8700de15fc421b5e6847d8fb8be1bd24acd16314cfd94f0fa69ff6d637b4", "9814d48ae1bfd731b32f0a829f20507ec9bd6b77609053718f7e2053b53c7a264bbab6a96d3d54a7f9a736570d11b1f99afb1735149f43cfee9b6f87886d3ff6", "c1b0f5c6d3b03dfe4a2602e67242f54e344090b66e01100a469b129f583f016c7e27dddeaa438393dcc7ec54b0b57c9ba7af007f9b56db5f6fb677d972a31362", "01d35c10c6c38c2dcf48f7eebb3235fb5ad74a65ec4cd016e2354c637a8fb49b695ef3c1d6f7ae4cd74d78cc9c9bcac9d4f23a73019998a7f73038a5c9b2dbde", "b83086cd8494e55708ad7ecd82dfb4bca1bda61ecbb7caf0c68967902e709345e5d8305eb7ac0d588afc6cbb75161aa9c8c7e0ea986bd833dafe5e1ccd37345a", "f2f1cb2b1da21f7df43034baf8ec6bc992a46a022a40f81339240fdae572dbdf34fcf26e97cabc0e001c0aa65607b45585d107c48d676d6e2f389fd801d1fed7", "1b049c5022acb0a6f886cb607629db83dee7ee8f623f8f0fcf352b8f5052036cc7e992e9f79bc424173abb07df8ccfb058f13cfe2a14925a1bb67f4447dd8929", "6c450032dd6b928bdb327b9892d15808163d314aeff37089380ca01ee4b1c8db739f71de29446c385fc8e0f12482ccb04ca1572e243affc7d77ed7bbc083be0d", "73fa82cfc129fb937094b53346e04ff29e44c67250f6952b63ef561bc7cc1169fd94368a252ae408f496c17684145d65cd46ec9c5a03eb59ecc35f6a1d2fc159", "d7ef283e6194befc2498bbced7f58bdf60cfcf10011fc5817b69cb13d63725017aa1e632ea3c609f6a5eb8a057ddb82953538f3e2a738262a11ddcd47f13752d", "216d4ffba1e94e8f281b06feb558346eeb0ae567c0a1d0c56ba2df704f45b2a6e6d91f97c5c00ebbcdfeb14b438bd9e56f2eb36ca64d22392520f3496f28fef5", "7e076f0892677d21072e99258203151146d4bc78ad6ed68edc939ba080c473ab66b10d38834e33abde71830dbd8529d895c7ea5f5773f1457d7c71bc3824b7c8", "1a3d403b46c595edfb71d10b4cb9e1b9ce4e44e28db6ba2a0334195816b85e6eba147bc6160864a0fe28166f99148476893a031a38a814e7136497296865f3c9", "4c4c8dcd6ba88f47a51df4dabdf227c335d70d5f4941b76e698536693e53c50241ef0264ea6f6dc5527485ddbe7a76900405158e32fef5ed184919943148da67", "8ea2d14c839946461666ab0a5966a10886e29d0a890104b123bb94d0af9011d8a961681fb95df98d00d5d351985f61f2e2eba2d91fd8032566b856d8408a09b0", "c642ba36e76cc1660c342d163fb32e4be8482072e641dd6b3662c447ecbc24f1b5e16ad4b83eed093c6f5999f1b2a0086fc23526cef9241a5a052c720bb5afde", "b9451e8c39c4276c2192939d49cbcb2b85a048e4f38bb5d3282e24c417de893ac2ff0acdef20036ed4deddbb526f992cd56f992aaba93d4edd3a628a4e53c811", "9a8c06cf6123391e9ee4d2441b7e534fc9551c242fb2b96fad45a7210edc010c36704b9ca1a07e935e6ff1413768e2f27726b213b16961633341ea82d75c5df3", "54b998867e8ac0d3eebcbf2252c107ad6dc5b557db5b7cb65a147475db99831011878784a62678a6fada687705ef68d048047f05b51db9c09168c4a7ad877036", "cb8d0d18db405d9d964ab61d1a5c00024df3805a329bf1500bec74d3ec1f1d0574da0b86153c9d8e317603bdb09e46d54d44551992a2464f0335a8398a2f2aee", "2fe6df89dcac80c7a03c2bc39633c12ae2898019117aacf77e490fa54cb8deb34a0d29ce778ee4f674831921853a15b541773486d5ac785163744e6d24ba388d", "c3e410354f6f890d0f3027805da471340f91db2a858501059124d3175eb7d637ca3637f7f95bafde0d74d026be7bf086e48931e299d68edc43e0a7ac4eac75c4", "107068fb436d658c0a96157316af41d323e582ea9c81146933ead563bf2c2a05b2c77ceeef57c01cd09ec28f6507238e930b1b7241d731f83194440f9256e5a6", "62b5337f5be290d028dc41dde08682ed7b0a7a842eae36dc6f7220e220012aeca98b2dac28325d1f78beef84352689c07c3a45f549e98ba908b010abceca9978", "6b6f3ac1316d9e8d1505ad163b70077df1df92568139721b32c23e5d84dc2fd742a4bad56bf0efacb3f3e63bbfb08a829b16df8cc1799eb199cd5d56be2b9d52", "bf8ed43d3aeebeb9b00d91013fbbb463b2f4b13e7ffc42741aeb9f0190a91b0401bb4fac68cc009d314287876c54d2f18891e6eee86fbe7125171559be6a03d7", "056ffe9a8b3a346abb92cf36efb74417748a044c4ca07f94e7bb076eeafa67073a85fcc1b17e7953138f304bbea7d0592e910e55b489e22c9015dc4e04ba76dc", "a945652aabf28d5ed6bb284a35fd4296a9a0ddebc81bf59991759ecaa7fb95a59628cf1ae75c88177fa3993e0cd0f138a807cdc01d17ca3922817ad1dc1c39e7", "bb4ec00ac4a82ed71af3936559c5940582218da063554c6f3efbb6d67cc808a2d6dbf088d0f371a4a1259efb1f1edeefa8093cab25551519d7ac6142711e50fe", "3a60fa8bede0f822c5dea75eff151ed5841d11b301c474a13571aff2dd0e216b4bc072b9ce409a70c6e6ff35bcae2f0950880d943f95775dd8f54d94b12d47c3", "bed8ac47aef0271fe40227247ddcdfd6b4885effeba3042f34b6fd525ab56cbdf72050cb71b1d42ae0ee1c548b36668b9297279d661380dffa39e66aa2959f99", "dfecce5852f67e858304fc5dc0c15cb29e28c69af4e2c117d333ea46d2ef2b0379a983507bc16e827b86c2433404159b759de91eb9ae975f338bacf38ad20371", "51585d172675d427009ea1658ac2a4d67a600e65034cb7f8eb34a39add704b67ae0a2798b7d7e7a16ee0f6902a165a0646cd9fe1cc777a07c6bfa14028c8eec8", "9e4246d4d3725a67a909dd1a4f06c627627942c0bb31eb4c614cab842e6bfb9faa7e8938575a2402832ac353a6fb47f4918b31d754eb9764e714f6925462b54e", "89e0446c3ff5a04b6d707ef43a77e2b349791f402930dbdb74bbab73d5215e294146ba7bd2fa269aee38564ef11a9ccaf5278f9e82687126dcf20d481d470617", "39ba3c74b23cb7deffb5d59624e320c08692637057daaaeea4d847e1d3b6a2ce6895ff3c609d57da490484b030ed231d5bdfafcfe264bd3d91cddb39c2d036ab", "db3a1fb5909f50e02e1626616247de6867e9e332d0eeef4650367cf0058f4764eb4a3869d3931b5ef6fc7a044a868b5fa894462df15c3954e88cd70c9a1de1b2", "86497b815f64702e2ac6aca1f1d16f7159b4f0b34f6e92a41e632982a7291465957e0ef171042b9630bb66c6e35051613f99bdc95c371eeb46bff8c897eba6e9", "21883a9b2ffa353c93fea49ea8b92be22797e6e8b360ebac8ed894b702766458a825adf67d9561d6758f5f9cc3aec7a4b2e4464a08e6959029dbc0b2f3fc6105", "70ff99fd241905992cc3fff2f6e3f562c8719d689bfe0e53cbc75e53286d82d8767aed0959b8c63aadf55b5730babee75ea082e88414700d7507b988c44c47bc", "2327e3b2946432dd2f4bce390ca652ec5e90f44fced0e921f612cf6d594cfc5e21b56e30a30dc0157e2c37a59cd37951f20cb9e2bc2d815a2676c01c2f827d51", "ec90d76ee1a1643126f53609a2721ad4a130c57d4dd0416a5d1b0bc43419ed6b3b0e82e0ff5eb76e94accfacb8bf72d7c92b622a0842d9a5b8b6e40fa2fc5231", "48e257ba5ef0c4b0b9769d26d5990d87f058430e368802c1f9a47195a6fa23ede9bbadc4c46ef2a8480cbfa0ced25dad522ca1752a66d5b43a72486f82c7b934", "e4f39bcd76fe94bfa84b31b0b9f3d2fe065b1e01ff2c3c0cd6f26b942f3c73a35031b9ecb4d82418a52892dabb459b27f0ba04af5e90636edf0b2caaa2d7906a", "3b6dd73c9552f2381107bf206b49c7967fdc5f5011d877d9c576bb4da6d74fbbabf46a1105242d7c645978e54c0b44adaf06d9f7aa4703e8a58829f6d87c5168", "470edb01e9dc9db187acdc9fa594e35b40831f9ddf76309d4a99a7aef1f0d9f79b5a4c9a22a38aeca3a1c2d6ceaeb603899577a30643a97872717c025a9a4fdc", "8cfcdd655481cca50730fe51ee985e9b51946f1345cb6a1801e5e0ed64ef979f431d5a7c3bd2a479d6d82e354210741956d194ee0febbc132b35907f4e2be32f", "a53d93726f1ba688a57267326473eceddc4ccf992d5c53429ca3edd4b122b4fe0b0568887d65c220cbac93fc4f612f97a09eb95e9f903409c78a22eee4fa1781", "0cda6b04d9466bb7f3995c16732e1347f29c23a64fe0b085fadba0995644cc5aa71587423c274c10e09518310c5f866cfaceb229fabb574219f12182eb114182", "c825949632e509824543f7eaf159fb6041722fce3c1cdcbb613b3d37ff107c519417baac32f8e74fe29d7f4823bf6886956603dca5354a6ed6e4a542e06b7d28", "fa9121c7b32b9e01733d034cfc78cbf67f926c7ed83e82200ef86818196921760b4beff48404df811b953828274461673c68d04e297b0eb7b2b4d60fc6b566a2", "c01d080efd492776a1c43bd23dd99d0a2e626d481e16782e75d54c2503b5dc32bd05f0f1ba33e568b88fd2d970929b719ecbb152f58f130a407c8830604b70ca", "55ddd8ac210a6e18ba1ee055af84c966e0dbff091c43580ae1be703bdb85da31acf6948cf5bd90c55a20e5450f22fb89bd8d0085e39f85a86cc46abbca75e24d", "5e9eb0e4b270d086e77eeaf3ce8b1cfc615031b8c463dc34f5c139786f274f22accb4d89e8f40d1a0c2acc84c4dc0f2bab390a9d9495493bd617ed004271bb64", "eaa30f93760743ac7d0a6cb8ed5ef3b30c59097bc44d0ec337344301deba9fb92b20c488d55de415f6aaed0df4925b42894b81d2e1cde89d91ec7f6cc67262b4", "a8bff469314a1ce0c990bb3fd539d92accb6249cc674b559bc9d3898b7a126fee597197fa42c971443470053c7d7f54b09371a59b0f7af87b1917c5347e8f8e0", "c0c27aea8dbe169c4cf25176cbf12db708fd6303db8cf94a1cfb402c1680d3d68f39bc5b9a10970dd5373cb0fe1cb36fa50e33165140d72933ba87af9d5d1ffe", "d6f856c92a5a694dec299f5a4765bed80e4e7431aa5505f82b21584dd1f1fe970f698bec5a3f4faa593d1aac944a96c21b85463a773cdf3ad87c4a00fb9e5073", "130396a75cb483f2eee8c56d8a668bb3d2641f5243212c0bee2bd33da096ad9eb8179fe18f9eaacf76e09fae9de4c3f14ba13341e345be05bf76c182cc3468cb", "f241de612b01aa2fa3cf01531d2a8e5e17fc761dfd48a704a834a47f57d6eade7804ecc39be42fdef16ec6adeaf7c01c2fd0c4cc97d3860907cfa4a3b36d0c05", "0ae7a79758a9ffbd1c04aa080bfa82daf9641f9c2a1cc82b628cbe4006bd47701c78e5022d2ca5ca5384d26d93fb16d595b9775dab17c88ef38e4ce9fdac4b52", "139de16e90ad012e39f72279140f4f6b12bb93f1cbcffbd1b132f39e7f92822d2b56beafc9ed83a0bf59c5525ffd125b83294b65f51f6e8ebbf85eb1aba85b87", "ccd869ee70892a0f5f3c269b9e21ffc99703855c1c652774febbaf1311bd58c80fb66bc3f747dd98b2f11ad9f5d8311b7ca706d456fc82ccd46bfb01f19e8d87", "77469b56910b022f45f509dcfca04494d8e7978073debf96398cb5a86f31bbe55f2a807a3271b8fe124171416917ab01a87acc7bf005977caaf7b484d87d6a93", "577a80f7cb393ac140af066b524166bb02a8059980b65fd100ecdcbec7721d2d0519a151ae730d4b6d9b97a8e5d2415aa8157856aeae4a7444171ef2a9db252b", "9986e67bf52a755f8924f28dae9627f889a45d466ce8616c4ed68ec3afd7a3a14785c335c6c68d62e7379af762b2bc17117a902083a99fae337a268a5d4f4427", "828613968b501dc00a97e08c73b118aa8876c26b8aac93df128502ab360f91bab50a51e088769a5c1eff4782ace147dce3642554199876374291f5d921629502" }; + + var digest_buffer: [sha2.SHA_512_DIGEST_LENGTH]u8 = undefined; + for (0..64) |i| { + var ctx = sha2.sha512_new(); + for (0..i) |_| + try sha2.sha512_update(&ctx, "a"); + sha2.sha512_final(&ctx, &digest_buffer); + + const ref = hex_to_bytes(sha2.SHA_512_DIGEST_LENGTH, reference[i]); + try testing.expectEqualSlices(u8, ref[0..], digest_buffer[0..]); + } +} + +test "SHA-512 maximum length violation (simulated)" { + var ctx = sha2.sha512_new(); + ctx.message_length = (1 << 125) - 1; // 2^128 - 8 bits + try testing.expectError(sha2.MessageLengthLimitExceeded, sha2.sha512_update(&ctx, "a")); +} + +// ----------------------------------- SHA-512/224 ----------------------------------- // + +// Reference hashes computed with PyCryptodome. +test "SHA-512/224 basic test" { + try run_hash_precomputed_tests( + sha2.Sha512Ctx, + sha2.SHA_512_224_DIGEST_LENGTH, + sha2.sha512_224_new, + sha2.sha512_224_update, + sha2.sha512_224_final, + &.{ + .{ .message = "", .hash = "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4" }, + .{ .message = "abc", .hash = "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa" }, + .{ + .message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .hash = "e5302d6d54bb242275d1e7622d68df6eb02dedd13f564c13dbda2174", + }, + .{ + .message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + .hash = "23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9", + }, + }, + ); +} + +// ----------------------------------- SHA-512/256 ----------------------------------- // + +// https://www.di-mgt.com.au/sha_testvectors.html +test "SHA-512/256 basic test" { + try run_hash_precomputed_tests( + sha2.Sha512Ctx, + sha2.SHA_512_256_DIGEST_LENGTH, + sha2.sha512_256_new, + sha2.sha512_256_update, + sha2.sha512_256_final, + &.{ + .{ .message = "", .hash = "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a" }, + .{ .message = "abc", .hash = "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23" }, + .{ + .message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .hash = "bde8e1f9f19bb9fd3406c90ec6bc47bd36d8ada9f11880dbc8a22a7078b6a461", + }, + .{ + .message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + .hash = "3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a", + }, + }, + ); +} + +// ----------------------------------- SHA-384 ----------------------------------- // + +// https://www.di-mgt.com.au/sha_testvectors.html +test "SHA-384 basic test" { + try run_hash_precomputed_tests( + sha2.Sha512Ctx, + sha2.SHA_384_DIGEST_LENGTH, + sha2.sha384_new, + sha2.sha384_update, + sha2.sha384_final, + &.{ + .{ .message = "", .hash = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b" }, + .{ .message = "abc", .hash = "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7" }, + .{ + .message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + .hash = "3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b", + }, + .{ + .message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + .hash = "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039", + }, + }, + ); +} + +// ----------------------------------- TEST HELPERS ----------------------------------- // + +fn hex_nibble_to_int(ascii_hex: u8) u4 { + const x = ascii_hex; + return @intCast(if (x >= '0' and x <= '9') + x - '0' + else if (x >= 'a' and x <= 'f') + 10 + (x - 'a') + else if (x >= 'A' and x <= 'F') + 10 + (x - 'A') + else + @panic("Argument is not a valid hex digit!")); +} + +fn hex_to_bytes(L: comptime_int, hex_string: *const [2 * L]u8) [L]u8 { + var res: [L]u8 = undefined; + for (0..L) |i| { + res[i] = @as(u8, hex_nibble_to_int(hex_string[2 * i])) << 4; + res[i] |= hex_nibble_to_int(hex_string[2 * i + 1]); + } + return res; +} + +fn run_hash_precomputed_tests( + Ctx: type, + L: comptime_int, + fn_new: *const fn () Ctx, + fn_update: *const fn (*Ctx, []const u8) anyerror!void, + fn_final: *const fn (*Ctx, *[L]u8) void, + tests: []const struct { message: []const u8, hash: *const [2 * L]u8 }, +) !void { + var digest_buffer: [L]u8 = undefined; + + for (tests) |t| { + var ctx = fn_new(); + try fn_update(&ctx, t.message); + fn_final(&ctx, &digest_buffer); + + const reference = hex_to_bytes(L, t.hash); + try testing.expectEqualSlices(u8, reference[0..], digest_buffer[0..]); + } +} diff --git a/test/primitive/streamcipher/chacha20.zig b/test/primitive/streamcipher/chacha20.zig new file mode 100644 index 0000000..8e59206 --- /dev/null +++ b/test/primitive/streamcipher/chacha20.zig @@ -0,0 +1,129 @@ +const std = @import("std"); +const testing = std.testing; + +const byte_operations = @import("ziggy").utility.byte_operations; +const word_to_bytes = byte_operations.word_to_bytes_le; +const bytes_to_word = byte_operations.bytes_to_word_le; + +const chacha20 = @import("ziggy").primitive.streamcipher.chacha20; + +// ----------------------------------- TEST VECTORS ----------------------------------- // + +// https://www.rfc-editor.org/rfc/rfc7539#section-2.2.1 +test "ChaCha Quarter Round" { + var state = [chacha20.BLOCK_WORDS]u32{ + 0x879531e0, 0xc5ecf37d, 0x516461b1, 0xc9a62f8a, + 0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0x2a5f714c, + 0x53372767, 0xb00a5631, 0x974c541a, 0x359e9963, + 0x5c971061, 0x3d631689, 0x2098d9d6, 0x91dbd320, + }; + const reference = [chacha20.BLOCK_WORDS]u32{ + 0x879531e0, 0xc5ecf37d, 0xbdb886dc, 0xc9a62f8a, + 0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0xcfacafd2, + 0xe46bea80, 0xb00a5631, 0x974c541a, 0x359e9963, + 0x5c971061, 0xccc07c79, 0x2098d9d6, 0x91dbd320, + }; + + chacha20.quarter_round(&state, 2, 7, 8, 13); + try testing.expectEqualSlices(u32, reference[0..], state[0..]); +} + +// https://www.rfc-editor.org/rfc/rfc7539#section-2.3.2 +test "ChaCha20 Block Function" { + const key = [chacha20.KEY_SIZE]u8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + }; + const nonce = [chacha20.Parameters_RFC7539.NONCE_SIZE]u8{ + 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, + }; + + const reference = [chacha20.BLOCK_SIZE]u8{ + 0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15, 0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, 0x71, 0xc4, + 0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03, 0x04, 0x22, 0xaa, 0x9a, 0xc3, 0xd4, 0x6c, 0x4e, + 0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09, 0x14, 0xc2, 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2, + 0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9, 0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e, + }; + + var chacha = chacha20.chacha20_rfc7539_new(&key, &nonce, &word_to_bytes(1)); + defer chacha20.chacha20_destroy(&chacha); + + chacha20.block_function(&chacha); + + var buffer: [chacha20.BLOCK_SIZE]u8 = undefined; + chacha20.serialize(chacha20.BLOCK_WORDS, &chacha.state, &buffer); + + try testing.expectEqualSlices(u8, reference[0..], buffer[0..]); +} + +// https://www.rfc-editor.org/rfc/rfc7539#section-2.4.2 +test "ChaCha20 Cipher" { + const key = [chacha20.KEY_SIZE]u8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + }; + const nonce = [chacha20.Parameters_RFC7539.NONCE_SIZE]u8{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, + }; + const counter = word_to_bytes(1); + const plaintext = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; + const reference = [_]u8{ + 0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80, 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81, + 0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2, 0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b, + 0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab, 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57, + 0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab, 0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8, + 0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61, 0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e, + 0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06, 0x81, 0x8c, 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36, + 0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6, 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42, + 0x87, 0x4d, + }; + + var chacha = chacha20.chacha20_rfc7539_new(&key, &nonce, &counter); + defer chacha20.chacha20_destroy(&chacha); + + var buffer: [plaintext.len]u8 = undefined; + @memcpy(&buffer, plaintext); + + try chacha20.encrypt_inplace(&chacha, &buffer); + try testing.expectEqualSlices(u8, reference[0..], buffer[0..]); +} + +test "ChaCha20 32-bit counter increment edge cases" { + const key = [chacha20.KEY_SIZE]u8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + }; + const nonce = [chacha20.Parameters_RFC7539.NONCE_SIZE]u8{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, + }; + const counter = word_to_bytes(std.math.maxInt(u32) - 1); + + var chacha = chacha20.chacha20_rfc7539_new(&key, &nonce, &counter); + defer chacha20.chacha20_destroy(&chacha); + + // Counter: 2^32 - 2 -> 2^32 - 1, OK + try chacha20.increment_counter(&chacha); + + // Counter: 2^32 - 1 -> 2^32, overflow + try testing.expectError(chacha20.KeyStreamDepleted, chacha20.increment_counter(&chacha)); +} + +test "ChaCha20 64-bit counter increment edge cases" { + const key = [chacha20.KEY_SIZE]u8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + }; + const nonce = [chacha20.Parameters_Bernstein.NONCE_SIZE]u8{ 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00 }; + const counter = [chacha20.Parameters_Bernstein.COUNTER_SIZE]u8{ 0xff, 0xff, 0xff, 0xff, 0xef, 0xbe, 0xad, 0xde }; + + var chacha = chacha20.chacha20_bernstein_new(&key, &nonce, &counter); + defer chacha20.chacha20_destroy(&chacha); + + // Counter: 0xdeadbeefffffffff -> 0xdeadbef000000000, OK + try chacha20.increment_counter(&chacha); + try testing.expectEqualSlices(u32, &.{ 0x00000000, 0xdeadbef0 }, chacha.nonce[0..2]); + + // Counter: 0xffffffffffffffff -> 0x10000000000000000, overflow + @memset(chacha.nonce[0..2], std.math.maxInt(u32)); + try testing.expectError(chacha20.KeyStreamDepleted, chacha20.increment_counter(&chacha)); +} diff --git a/test/primitive/streamcipher/salsa20.zig b/test/primitive/streamcipher/salsa20.zig new file mode 100644 index 0000000..e69de29