Refactor tests into a separate module.
This commit is contained in:
54
build.zig
54
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);
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -186,7 +186,3 @@ fn rotate_halves_left(cd: *[DES_TRUE_KEY_SIZE]u8, positions: comptime_int) void
|
||||
// TODO
|
||||
_ = .{ cd, positions };
|
||||
}
|
||||
|
||||
// ----------------------------------- TEST VECTORS ----------------------------------- //
|
||||
|
||||
// TODO
|
||||
|
||||
@ -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");
|
||||
};
|
||||
|
||||
167
src/primitive/blockcipher/mode_cbc.zig
Normal file
167
src/primitive/blockcipher/mode_cbc.zig
Normal file
@ -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];
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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"));
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -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..]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
@ -1 +1,2 @@
|
||||
pub const primitive = @import("./primitive/index.zig");
|
||||
pub const utility = @import("./utility/index.zig");
|
||||
|
||||
21
src/utility/byte_operations.zig
Normal file
21
src/utility/byte_operations.zig
Normal file
@ -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);
|
||||
}
|
||||
1
src/utility/index.zig
Normal file
1
src/utility/index.zig
Normal file
@ -0,0 +1 @@
|
||||
pub const byte_operations = @import("byte_operations.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"),
|
||||
};
|
||||
322
test/primitive/blockcipher/aes.zig
Normal file
322
test/primitive/blockcipher/aes.zig
Normal file
@ -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);
|
||||
}
|
||||
78
test/primitive/blockcipher/operation_modes.zig
Normal file
78
test/primitive/blockcipher/operation_modes.zig
Normal file
@ -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]);
|
||||
}
|
||||
}
|
||||
327
test/primitive/digest/sha.zig
Normal file
327
test/primitive/digest/sha.zig
Normal file
File diff suppressed because one or more lines are too long
129
test/primitive/streamcipher/chacha20.zig
Normal file
129
test/primitive/streamcipher/chacha20.zig
Normal file
@ -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));
|
||||
}
|
||||
0
test/primitive/streamcipher/salsa20.zig
Normal file
0
test/primitive/streamcipher/salsa20.zig
Normal file
Reference in New Issue
Block a user