Compare commits
10 Commits
4b96179ac9
...
7ed0fd2eaa
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ed0fd2eaa | |||
| 54fcf93d11 | |||
| fd304c69b8 | |||
| f7c8ef2377 | |||
| ba3802ebcc | |||
| 3afe9895c5 | |||
| 0ae582b733 | |||
| 17e28b8279 | |||
| 4ea665a621 | |||
| 9d8a08eb46 |
@ -21,6 +21,8 @@ Most (**theoretical!**) users should directly use one of the cryptographic *prot
|
|||||||
### Primitives
|
### Primitives
|
||||||
|
|
||||||
- Advanced Encryption Standard (FIPS 197): AES-128, AES-192, AES-256
|
- Advanced Encryption Standard (FIPS 197): AES-128, AES-192, AES-256
|
||||||
|
- Serpent (AES finalist)
|
||||||
|
- Block Cipher Operation Modes: CBC-PKCS7
|
||||||
- ChaCha20 (RFC 7539): ChaCha20 with 64-bit nonce and 64-bit counter, ChaCha20 with 96-bit nonce and 32-bit counter
|
- ChaCha20 (RFC 7539): ChaCha20 with 64-bit nonce and 64-bit counter, ChaCha20 with 96-bit nonce and 32-bit counter
|
||||||
- Salsa20 (Bernstein, not standardized): Salsa20/20 with 256-key, Salsa20/20 with 128-bit key
|
- Salsa20 (Bernstein, not standardized): Salsa20/20 with 256-key, Salsa20/20 with 128-bit key
|
||||||
- Secure Hash Standard (FIPS 180-4): SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256
|
- Secure Hash Standard (FIPS 180-4): SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256
|
||||||
@ -37,15 +39,16 @@ Most (**theoretical!**) users should directly use one of the cryptographic *prot
|
|||||||
|
|
||||||
### Primitives
|
### Primitives
|
||||||
|
|
||||||
- DES, 3DES
|
- AES finalists: Rijndael (generalize AES), Twofish, RC6, MARS
|
||||||
- Block cipher modes: CBC-PKCS7, CFB, OFB, CTR, GCM
|
- Lucifer, DES, 3DES
|
||||||
|
- Block cipher modes: CFB, OFB, CTR, GCM
|
||||||
- Poly1305
|
- Poly1305
|
||||||
- SHA-512/t, SHA-3, HMAC
|
- SHA-512/t, SHA-3, HMAC
|
||||||
- BigIntegers & modular arithmetic
|
- BigIntegers & modular arithmetic
|
||||||
- Cryptographically secure random BigInteger generation & primality testing
|
- Cryptographically secure random BigInteger generation & primality testing
|
||||||
- Elliptic Curve groups (over Fp fields)
|
- Elliptic Curve groups (over Fp fields)
|
||||||
- ASN.1 (de)serialization from/into DER/PEM
|
- ASN.1 (de)serialization from/into DER/PEM
|
||||||
- Optimal Assymetric Encryption Padding (OAEP)
|
- Optimal Asymmetric Encryption Padding (OAEP)
|
||||||
|
|
||||||
### Protocols
|
### Protocols
|
||||||
|
|
||||||
|
|||||||
79
build.zig
79
build.zig
@ -1,91 +1,38 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
// Although this function looks imperative, note that its job is to
|
|
||||||
// declaratively construct a build graph that will be executed by an external
|
|
||||||
// runner.
|
|
||||||
pub fn build(b: *std.Build) void {
|
pub fn build(b: *std.Build) void {
|
||||||
// Standard target options allows the person running `zig build` to choose
|
|
||||||
// what target to build for. Here we do not override the defaults, which
|
|
||||||
// means any target is allowed, and the default is native. Other options
|
|
||||||
// for restricting supported target set are available.
|
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
// Standard optimization options allow the person running `zig build` to select
|
|
||||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
|
|
||||||
// set a preferred release mode, allowing the user to decide how to optimize.
|
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const utility_module = b.createModule(.{
|
||||||
|
.root_source_file = .{ .cwd_relative = "src/utility//index.zig" },
|
||||||
|
});
|
||||||
|
const primitive_module = b.createModule(.{
|
||||||
|
.root_source_file = .{ .cwd_relative = "src/primitive/index.zig" },
|
||||||
|
});
|
||||||
|
primitive_module.addImport("utility", utility_module);
|
||||||
|
|
||||||
const lib = b.addStaticLibrary(.{
|
const lib = b.addStaticLibrary(.{
|
||||||
.name = "crypto",
|
.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"),
|
.root_source_file = b.path("src/root.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
lib.root_module.addImport("primitive", primitive_module);
|
||||||
|
lib.root_module.addImport("utility", utility_module);
|
||||||
|
|
||||||
// This declares intent for the library to be installed into the standard
|
|
||||||
// location when the user invokes the "install" step (the default step when
|
|
||||||
// running `zig build`).
|
|
||||||
b.installArtifact(lib);
|
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(.{
|
const lib_unit_tests = b.addTest(.{
|
||||||
.root_source_file = b.path("src/test.zig"),
|
.root_source_file = b.path("test/index.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
lib_unit_tests.root_module.addImport("primitive", primitive_module);
|
||||||
|
lib_unit_tests.root_module.addImport("utility", utility_module);
|
||||||
|
|
||||||
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
|
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");
|
const test_step = b.step("test", "Run unit tests");
|
||||||
test_step.dependOn(&run_lib_unit_tests.step);
|
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 std = @import("std");
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
|
||||||
// ----------------------------------- AES CONSTANTS ----------------------------------- //
|
const byte_operations = @import("utility").byte_operations;
|
||||||
|
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 BLOCK_SIZE = 128 / 8;
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
const AES_SBOX = [_]u8{
|
pub const KEY_SIZE_128 = 128 / 8;
|
||||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
pub const KEY_SIZE_192 = 192 / 8;
|
||||||
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
pub const KEY_SIZE_256 = 256 / 8;
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
const AES_RCON = [_]u32{
|
|
||||||
0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
|
|
||||||
0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000,
|
|
||||||
};
|
|
||||||
|
|
||||||
// ----------------------------------- ENCRYPTION/DECRYPTION ----------------------------------- //
|
// ----------------------------------- ENCRYPTION/DECRYPTION ----------------------------------- //
|
||||||
|
|
||||||
pub fn aes_encrypt_block(
|
pub fn encrypt_block(
|
||||||
n_rounds: comptime_int,
|
n_rounds: comptime_int,
|
||||||
block_in: *const [AES_BLOCK_SIZE]u8,
|
block_in: *const [BLOCK_SIZE]u8,
|
||||||
block_out: *[AES_BLOCK_SIZE]u8,
|
block_out: *[BLOCK_SIZE]u8,
|
||||||
expanded_key: *const [4 * (n_rounds + 1)]u32,
|
expanded_key: *const [4 * (n_rounds + 1)]u32,
|
||||||
) void {
|
) void {
|
||||||
// Copy input buffer into state (we're treating the buffer as a column-first matrix).
|
// 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);
|
@memcpy(state[0..], block_in);
|
||||||
|
|
||||||
// Initial AddRoundKey.
|
// Initial AddRoundKey.
|
||||||
aes_add_round_key(&state, expanded_key[0..4]);
|
add_round_key(&state, expanded_key[0..4]);
|
||||||
|
|
||||||
// Nr - 1 identical rounds.
|
// Nr - 1 identical rounds.
|
||||||
for (1..n_rounds) |round| {
|
for (1..n_rounds) |round| {
|
||||||
aes_sub_bytes(&state);
|
sub_bytes(&state);
|
||||||
aes_shift_rows(&state);
|
shift_rows(&state);
|
||||||
aes_mix_columns(&state);
|
mix_columns(&state);
|
||||||
aes_add_round_key(&state, @ptrCast(expanded_key[(4 * round)..(4 * round + 4)]));
|
add_round_key(&state, @ptrCast(expanded_key[(4 * round)..(4 * round + 4)]));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last round is without MixColumns.
|
// Last round is without MixColumns.
|
||||||
aes_sub_bytes(&state);
|
sub_bytes(&state);
|
||||||
aes_shift_rows(&state);
|
shift_rows(&state);
|
||||||
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)]));
|
||||||
|
|
||||||
// Write the result into the destination buffer.
|
// Write the result into the destination buffer.
|
||||||
@memcpy(block_out, &state);
|
@memcpy(block_out, &state);
|
||||||
@ -96,27 +48,32 @@ pub fn aes_encrypt_block(
|
|||||||
@memset(state[0..], 0);
|
@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).
|
// 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);
|
@memcpy(state[0..], block_in);
|
||||||
|
|
||||||
// Reverse the AddRoundKey that was applied after the last encryption round.
|
// 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.
|
// Nr - 1 identical rounds.
|
||||||
for (1..n_rounds) |inv_round| {
|
for (1..n_rounds) |inv_round| {
|
||||||
const round = n_rounds - inv_round;
|
const round = n_rounds - inv_round;
|
||||||
aes_inv_shift_rows(&state);
|
inv_shift_rows(&state);
|
||||||
aes_inv_sub_bytes(&state);
|
inv_sub_bytes(&state);
|
||||||
aes_add_round_key(&state, @ptrCast(expanded_key[(4 * round)..(4 * round + 4)]));
|
add_round_key(&state, @ptrCast(expanded_key[(4 * round)..(4 * round + 4)]));
|
||||||
aes_inv_mix_columns(&state);
|
inv_mix_columns(&state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finish last round.
|
// Finish last round.
|
||||||
aes_inv_shift_rows(&state);
|
inv_shift_rows(&state);
|
||||||
aes_inv_sub_bytes(&state);
|
inv_sub_bytes(&state);
|
||||||
aes_add_round_key(&state, expanded_key[0..4]);
|
add_round_key(&state, expanded_key[0..4]);
|
||||||
|
|
||||||
// Write the result into the destination buffer.
|
// Write the result into the destination buffer.
|
||||||
@memcpy(block_out, &state);
|
@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);
|
@memset(state[0..], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aes_128_encrypt_block(
|
pub fn aes128_encrypt_block(
|
||||||
key: *const [Aes128Parameters.KEY_SIZE]u8,
|
key: *const [KEY_SIZE_128]u8,
|
||||||
block_in: *const [AES_BLOCK_SIZE]u8,
|
block_in: *const [BLOCK_SIZE]u8,
|
||||||
block_out: *[AES_BLOCK_SIZE]u8,
|
block_out: *[BLOCK_SIZE]u8,
|
||||||
) void {
|
) void {
|
||||||
// Prepare the subkeys for AddRoundKey.
|
// 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);
|
defer @memset(&expanded_key, 0);
|
||||||
|
|
||||||
// Call the generic encryption procedure.
|
// Call the generic encryption procedure.
|
||||||
aes_encrypt_block(
|
encrypt_block(N_ROUNDS_128, block_in, block_out, &expanded_key);
|
||||||
Aes128Parameters.N_ROUNDS,
|
|
||||||
block_in,
|
|
||||||
block_out,
|
|
||||||
&expanded_key,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aes_128_decrypt_block(
|
pub fn aes128_decrypt_block(
|
||||||
key: *const [Aes128Parameters.KEY_SIZE]u8,
|
key: *const [KEY_SIZE_128]u8,
|
||||||
block_in: *const [AES_BLOCK_SIZE]u8,
|
block_in: *const [BLOCK_SIZE]u8,
|
||||||
block_out: *[AES_BLOCK_SIZE]u8,
|
block_out: *[BLOCK_SIZE]u8,
|
||||||
) void {
|
) void {
|
||||||
// Prepare the subkeys for AddRoundKey.
|
// 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);
|
defer @memset(&expanded_key, 0);
|
||||||
|
|
||||||
// Call the generic decryption procedure.
|
// Call the generic decryption procedure.
|
||||||
aes_decrypt_block(
|
decrypt_block(N_ROUNDS_128, block_in, block_out, &expanded_key);
|
||||||
Aes128Parameters.N_ROUNDS,
|
|
||||||
block_in,
|
|
||||||
block_out,
|
|
||||||
&expanded_key,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aes_192_encrypt_block(
|
pub fn aes192_encrypt_block(
|
||||||
key: *const [Aes192Parameters.KEY_SIZE]u8,
|
key: *const [KEY_SIZE_192]u8,
|
||||||
block_in: *const [AES_BLOCK_SIZE]u8,
|
block_in: *const [BLOCK_SIZE]u8,
|
||||||
block_out: *[AES_BLOCK_SIZE]u8,
|
block_out: *[BLOCK_SIZE]u8,
|
||||||
) void {
|
) void {
|
||||||
// Prepare the subkeys for AddRoundKey.
|
// 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);
|
defer @memset(&expanded_key, 0);
|
||||||
|
|
||||||
// Call the generic encryption procedure.
|
// Call the generic encryption procedure.
|
||||||
aes_encrypt_block(
|
encrypt_block(N_ROUNDS_192, block_in, block_out, &expanded_key);
|
||||||
Aes192Parameters.N_ROUNDS,
|
|
||||||
block_in,
|
|
||||||
block_out,
|
|
||||||
&expanded_key,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aes_192_decrypt_block(
|
pub fn aes192_decrypt_block(
|
||||||
key: *const [Aes192Parameters.KEY_SIZE]u8,
|
key: *const [KEY_SIZE_192]u8,
|
||||||
block_in: *const [AES_BLOCK_SIZE]u8,
|
block_in: *const [BLOCK_SIZE]u8,
|
||||||
block_out: *[AES_BLOCK_SIZE]u8,
|
block_out: *[BLOCK_SIZE]u8,
|
||||||
) void {
|
) void {
|
||||||
// Prepare the subkeys for AddRoundKey.
|
// 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);
|
defer @memset(&expanded_key, 0);
|
||||||
|
|
||||||
// Call the generic decryption procedure.
|
// Call the generic decryption procedure.
|
||||||
aes_decrypt_block(
|
decrypt_block(N_ROUNDS_192, block_in, block_out, &expanded_key);
|
||||||
Aes192Parameters.N_ROUNDS,
|
|
||||||
block_in,
|
|
||||||
block_out,
|
|
||||||
&expanded_key,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aes_256_encrypt_block(
|
pub fn aes256_encrypt_block(
|
||||||
key: *const [Aes256Parameters.KEY_SIZE]u8,
|
key: *const [KEY_SIZE_256]u8,
|
||||||
block_in: *const [AES_BLOCK_SIZE]u8,
|
block_in: *const [BLOCK_SIZE]u8,
|
||||||
block_out: *[AES_BLOCK_SIZE]u8,
|
block_out: *[BLOCK_SIZE]u8,
|
||||||
) void {
|
) void {
|
||||||
// Prepare the subkeys for AddRoundKey.
|
// 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);
|
defer @memset(&expanded_key, 0);
|
||||||
|
|
||||||
// Call the generic encryption procedure.
|
// Call the generic encryption procedure.
|
||||||
aes_encrypt_block(
|
encrypt_block(N_ROUNDS_256, block_in, block_out, &expanded_key);
|
||||||
Aes256Parameters.N_ROUNDS,
|
|
||||||
block_in,
|
|
||||||
block_out,
|
|
||||||
&expanded_key,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aes_256_decrypt_block(
|
pub fn aes256_decrypt_block(
|
||||||
key: *const [Aes256Parameters.KEY_SIZE]u8,
|
key: *const [KEY_SIZE_256]u8,
|
||||||
block_in: *const [AES_BLOCK_SIZE]u8,
|
block_in: *const [BLOCK_SIZE]u8,
|
||||||
block_out: *[AES_BLOCK_SIZE]u8,
|
block_out: *[BLOCK_SIZE]u8,
|
||||||
) void {
|
) void {
|
||||||
// Prepare the subkeys for AddRoundKey.
|
// 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);
|
defer @memset(&expanded_key, 0);
|
||||||
|
|
||||||
// Call the generic decryption procedure.
|
// Call the generic decryption procedure.
|
||||||
aes_decrypt_block(
|
decrypt_block(N_ROUNDS_256, block_in, block_out, &expanded_key);
|
||||||
Aes256Parameters.N_ROUNDS,
|
|
||||||
block_in,
|
|
||||||
block_out,
|
|
||||||
&expanded_key,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- KEY EXPANSION ----------------------------------- //
|
// ----------------------------------- KEY EXPANSION ----------------------------------- //
|
||||||
|
|
||||||
pub fn aes_expand_key(
|
pub fn expand_key(
|
||||||
n_rounds: comptime_int,
|
n_rounds: comptime_int,
|
||||||
n_key_words: comptime_int,
|
n_key_words: comptime_int,
|
||||||
key: *const [n_key_words * 4]u8,
|
key: *const [n_key_words * 4]u8,
|
||||||
@ -249,9 +176,9 @@ pub fn aes_expand_key(
|
|||||||
while (i <= 4 * n_rounds + 3) : (i += 1) {
|
while (i <= 4 * n_rounds + 3) : (i += 1) {
|
||||||
var temp = expanded_key[i - 1];
|
var temp = expanded_key[i - 1];
|
||||||
if (i % n_key_words == 0) {
|
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) {
|
} 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;
|
expanded_key[i] = expanded_key[i - n_key_words] ^ temp;
|
||||||
}
|
}
|
||||||
@ -259,48 +186,34 @@ pub fn aes_expand_key(
|
|||||||
return expanded_key;
|
return expanded_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aes_128_expand_key(key: *const [Aes128Parameters.KEY_SIZE]u8) [4 * (Aes128Parameters.N_ROUNDS + 1)]u32 {
|
pub fn expand_key_128(key: *const [KEY_SIZE_128]u8) [4 * (N_ROUNDS_128 + 1)]u32 {
|
||||||
return aes_expand_key(
|
return expand_key(N_ROUNDS_128, KEY_SIZE_128 / 4, key);
|
||||||
Aes128Parameters.N_ROUNDS,
|
|
||||||
Aes128Parameters.KEY_SIZE / 4,
|
|
||||||
key,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aes_192_expand_key(key: *const [Aes192Parameters.KEY_SIZE]u8) [4 * (Aes192Parameters.N_ROUNDS + 1)]u32 {
|
pub fn expand_key_192(key: *const [KEY_SIZE_192]u8) [4 * (N_ROUNDS_192 + 1)]u32 {
|
||||||
return aes_expand_key(
|
return expand_key(N_ROUNDS_192, KEY_SIZE_192 / 4, key);
|
||||||
Aes192Parameters.N_ROUNDS,
|
|
||||||
Aes192Parameters.KEY_SIZE / 4,
|
|
||||||
key,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aes_256_expand_key(key: *const [Aes256Parameters.KEY_SIZE]u8) [4 * (Aes256Parameters.N_ROUNDS + 1)]u32 {
|
pub fn expand_key_256(key: *const [KEY_SIZE_256]u8) [4 * (N_ROUNDS_256 + 1)]u32 {
|
||||||
return aes_expand_key(
|
return expand_key(N_ROUNDS_256, KEY_SIZE_256 / 4, key);
|
||||||
Aes256Parameters.N_ROUNDS,
|
|
||||||
Aes256Parameters.KEY_SIZE / 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| {
|
for (0..4) |wi| {
|
||||||
const subkey_bytes = word_to_bytes(subkey[wi]);
|
const subkey_bytes = word_to_bytes(subkey[wi]);
|
||||||
for (0..4) |bi| {
|
for (0..4) |bi|
|
||||||
state[wi * 4 + bi] ^= subkey_bytes[bi];
|
state[wi * 4 + bi] ^= subkey_bytes[bi];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sub_bytes(state: *[BLOCK_SIZE]u8) void {
|
||||||
|
for (0..state.len) |i|
|
||||||
|
state[i] = SBOX[state[i]];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn aes_sub_bytes(state: *[AES_BLOCK_SIZE]u8) void {
|
pub fn shift_rows(state: *[BLOCK_SIZE]u8) void {
|
||||||
for (0..state.len) |i| {
|
|
||||||
state[i] = AES_SBOX[state[i]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn aes_shift_rows(state: *[AES_BLOCK_SIZE]u8) void {
|
|
||||||
var tmp: u8 = undefined;
|
var tmp: u8 = undefined;
|
||||||
|
|
||||||
// Note: Since we store the state matrix as an array of columns,
|
// 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;
|
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 c0 = column[0];
|
||||||
const c1 = column[1];
|
const c1 = column[1];
|
||||||
const c2 = column[2];
|
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);
|
column[3] = (xtime(c0) ^ c0) ^ c1 ^ c2 ^ xtime(c3);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn aes_mix_columns(state: *[AES_BLOCK_SIZE]u8) void {
|
pub fn mix_columns(state: *[BLOCK_SIZE]u8) void {
|
||||||
for (0..4) |i| {
|
for (0..4) |i|
|
||||||
aes_mix_one_column(@ptrCast(state[(4 * i)..(4 * i + 4)]));
|
mix_one_column(@ptrCast(state[(4 * i)..(4 * i + 4)]));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn aes_inv_sub_bytes(state: *[AES_BLOCK_SIZE]u8) void {
|
pub fn inv_sub_bytes(state: *[BLOCK_SIZE]u8) void {
|
||||||
for (0..state.len) |i| {
|
for (0..state.len) |i|
|
||||||
state[i] = AES_INV_SBOX[state[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;
|
var tmp: u8 = undefined;
|
||||||
|
|
||||||
// Note: Since we store the state matrix as an array of columns,
|
// 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;
|
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 c0 = column[0];
|
||||||
const c1 = column[1];
|
const c1 = column[1];
|
||||||
const c2 = column[2];
|
const c2 = column[2];
|
||||||
@ -394,21 +305,19 @@ fn aes_inv_mix_one_column(column: *[4]u8) void {
|
|||||||
column[3] = gfmult(0x0b, c0) ^ gfmult(0x0d, c1) ^ gfmult(0x09, c2) ^ gfmult(0x0e, c3);
|
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 {
|
pub fn inv_mix_columns(state: *[BLOCK_SIZE]u8) void {
|
||||||
for (0..4) |i| {
|
for (0..4) |i|
|
||||||
aes_inv_mix_one_column(@ptrCast(state[(4 * i)..(4 * i + 4)]));
|
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);
|
var bytes = word_to_bytes(word);
|
||||||
for (0..4) |i| {
|
for (0..4) |i|
|
||||||
bytes[i] = AES_SBOX[bytes[i]];
|
bytes[i] = SBOX[bytes[i]];
|
||||||
}
|
|
||||||
return bytes_to_word(&bytes);
|
return bytes_to_word(&bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn aes_rot_word(word: u32) u32 {
|
fn rot_word(word: u32) u32 {
|
||||||
const bytes = word_to_bytes(word);
|
const bytes = word_to_bytes(word);
|
||||||
return bytes_to_word(&.{ bytes[1], bytes[2], bytes[3], bytes[0] });
|
return bytes_to_word(&.{ bytes[1], bytes[2], bytes[3], bytes[0] });
|
||||||
}
|
}
|
||||||
@ -444,334 +353,51 @@ fn gfmult(factor: comptime_int, element: u8) u8 {
|
|||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- ENDIANNESS HELPERS ----------------------------------- //
|
// ----------------------------------- CRYPTOGRAPHIC CONSTANTS ----------------------------------- //
|
||||||
|
|
||||||
fn word_to_bytes(word: u32) [4]u8 {
|
pub const N_ROUNDS_128 = 10;
|
||||||
var bytes: [4]u8 = undefined;
|
pub const N_ROUNDS_192 = 12;
|
||||||
std.mem.writeInt(u32, &bytes, word, .big);
|
pub const N_ROUNDS_256 = 14;
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bytes_to_word(bytes: *const [4]u8) u32 {
|
pub const SBOX = [_]u8{
|
||||||
return std.mem.readInt(u32, bytes, .big);
|
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,
|
||||||
// ----------------------------------- TEST VECTORS ----------------------------------- //
|
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,
|
||||||
test "AES-128 key expansion test vector from FIPS 197 A.1" {
|
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||||
const key = [Aes128Parameters.KEY_SIZE]u8{
|
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
||||||
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
|
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
||||||
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
|
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,
|
||||||
const expected_expansion = [(Aes128Parameters.N_ROUNDS + 1) * 4]u32{
|
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
||||||
0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x09cf4f3c,
|
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||||
0xa0fafe17, 0x88542cb1, 0x23a33939, 0x2a6c7605,
|
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
||||||
0xf2c295f2, 0x7a96b943, 0x5935807a, 0x7359f67f,
|
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
||||||
0x3d80477d, 0x4716fe3e, 0x1e237e44, 0x6d7a883b,
|
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||||
0xef44a541, 0xa8525b7f, 0xb671253b, 0xdb0bad00,
|
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
|
||||||
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;
|
pub const INV_SBOX = [_]u8{
|
||||||
for (plaintext, 0..) |pt, i| {
|
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
|
||||||
aes_128_encrypt_block(&key, &pt, &buffer);
|
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
|
||||||
try testing.expectEqualSlices(u8, &ciphertext[i], &buffer);
|
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,
|
||||||
// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values
|
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
|
||||||
test "AES-128 ECB decryption" {
|
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
|
||||||
const key = [Aes128Parameters.KEY_SIZE]u8{
|
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
|
||||||
0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C,
|
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,
|
||||||
const plaintext = [_][AES_BLOCK_SIZE]u8{
|
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
|
||||||
.{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A },
|
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
|
||||||
.{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 },
|
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
|
||||||
.{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF },
|
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
|
||||||
.{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 },
|
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
|
||||||
};
|
|
||||||
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;
|
pub const RCON = [_]u32{
|
||||||
for (ciphertext, 0..) |ct, i| {
|
0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
|
||||||
aes_128_decrypt_block(&key, &ct, &buffer);
|
0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000,
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -10,43 +10,54 @@ pub const DES_N_ROUNDS = 16;
|
|||||||
pub const DES_SUBKEY_SIZE = 48 / 8;
|
pub const DES_SUBKEY_SIZE = 48 / 8;
|
||||||
|
|
||||||
const DES_INITIAL_PERMUTATION = [_]u6{
|
const DES_INITIAL_PERMUTATION = [_]u6{
|
||||||
58, 50, 42, 35, 26, 18, 10, 2,
|
58 - 1, 50 - 1, 42 - 1, 35 - 1, 26 - 1, 18 - 1, 10 - 1, 2 - 1,
|
||||||
60, 52, 44, 36, 28, 20, 12, 4,
|
60 - 1, 52 - 1, 44 - 1, 36 - 1, 28 - 1, 20 - 1, 12 - 1, 4 - 1,
|
||||||
62, 54, 46, 38, 30, 22, 14, 6,
|
62 - 1, 54 - 1, 46 - 1, 38 - 1, 30 - 1, 22 - 1, 14 - 1, 6 - 1,
|
||||||
64, 56, 48, 40, 32, 24, 16, 8,
|
64 - 1, 56 - 1, 48 - 1, 40 - 1, 32 - 1, 24 - 1, 16 - 1, 8 - 1,
|
||||||
57, 49, 41, 33, 25, 17, 9, 1,
|
57 - 1, 49 - 1, 41 - 1, 33 - 1, 25 - 1, 17 - 1, 9 - 1, 1 - 1,
|
||||||
59, 51, 43, 35, 27, 19, 11, 3,
|
59 - 1, 51 - 1, 43 - 1, 35 - 1, 27 - 1, 19 - 1, 11 - 1, 3 - 1,
|
||||||
61, 53, 45, 37, 29, 21, 13, 5,
|
61 - 1, 53 - 1, 45 - 1, 37 - 1, 29 - 1, 21 - 1, 13 - 1, 5 - 1,
|
||||||
63, 55, 47, 39, 31, 23, 15, 7,
|
63 - 1, 55 - 1, 47 - 1, 39 - 1, 31 - 1, 23 - 1, 15 - 1, 7 - 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DES_INV_INITIAL_PERMUTATION = [_]u6{
|
const DES_INV_INITIAL_PERMUTATION = [_]u6{
|
||||||
40, 8, 48, 16, 56, 24, 64, 32,
|
40 - 1, 8 - 1, 48 - 1, 16 - 1, 56 - 1, 24 - 1, 64 - 1, 32 - 1,
|
||||||
39, 7, 47, 15, 55, 23, 63, 31,
|
39 - 1, 7 - 1, 47 - 1, 15 - 1, 55 - 1, 23 - 1, 63 - 1, 31 - 1,
|
||||||
38, 6, 46, 14, 54, 22, 62, 30,
|
38 - 1, 6 - 1, 46 - 1, 14 - 1, 54 - 1, 22 - 1, 62 - 1, 30 - 1,
|
||||||
37, 5, 45, 13, 53, 21, 61, 29,
|
37 - 1, 5 - 1, 45 - 1, 13 - 1, 53 - 1, 21 - 1, 61 - 1, 29 - 1,
|
||||||
36, 4, 44, 12, 52, 20, 60, 28,
|
36 - 1, 4 - 1, 44 - 1, 12 - 1, 52 - 1, 20 - 1, 60 - 1, 28 - 1,
|
||||||
35, 3, 43, 11, 51, 19, 59, 27,
|
35 - 1, 3 - 1, 43 - 1, 11 - 1, 51 - 1, 19 - 1, 59 - 1, 27 - 1,
|
||||||
34, 2, 42, 10, 50, 18, 58, 26,
|
34 - 1, 2 - 1, 42 - 1, 10 - 1, 50 - 1, 18 - 1, 58 - 1, 26 - 1,
|
||||||
33, 1, 41, 9, 49, 17, 57, 25,
|
33 - 1, 1 - 1, 41 - 1, 9 - 1, 49 - 1, 17 - 1, 57 - 1, 25 - 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DES_BIT_SELECTION_TABLE_E = [_]u5{
|
const DES_BIT_SELECTION_TABLE_E = [_]u6{
|
||||||
32, 1, 2, 3, 4, 5,
|
32 - 1, 1 - 1, 2 - 1, 3 - 1, 4 - 1, 5 - 1,
|
||||||
4, 5, 6, 7, 8, 9,
|
4 - 1, 5 - 1, 6 - 1, 7 - 1, 8 - 1, 9 - 1,
|
||||||
8, 9, 10, 11, 12, 13,
|
8 - 1, 9 - 1, 10 - 1, 11 - 1, 12 - 1, 13 - 1,
|
||||||
12, 13, 14, 15, 16, 17,
|
12 - 1, 13 - 1, 14 - 1, 15 - 1, 16 - 1, 17 - 1,
|
||||||
16, 17, 18, 19, 20, 21,
|
16 - 1, 17 - 1, 18 - 1, 19 - 1, 20 - 1, 21 - 1,
|
||||||
20, 21, 22, 23, 24, 25,
|
20 - 1, 21 - 1, 22 - 1, 23 - 1, 24 - 1, 25 - 1,
|
||||||
24, 25, 26, 27, 28, 29,
|
24 - 1, 25 - 1, 26 - 1, 27 - 1, 28 - 1, 29 - 1,
|
||||||
28, 29, 30, 31, 32, 1,
|
28 - 1, 29 - 1, 30 - 1, 31 - 1, 32 - 1, 1 - 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DES_PERMUTATION_FUNCTION_P = [_]u5{
|
const DES_S_BOXES = [8][64]u4{
|
||||||
16, 7, 20, 21, 29, 12, 28, 17,
|
.{ 14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1, 3, 10, 10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8, 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7, 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13 },
|
||||||
1, 15, 23, 26, 5, 18, 31, 10,
|
.{ 15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14, 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5, 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2, 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9 },
|
||||||
2, 8, 24, 14, 32, 27, 3, 9,
|
.{ 10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10, 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1, 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7, 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12 },
|
||||||
19, 13, 30, 6, 22, 11, 4, 25,
|
.{ 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3, 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9, 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8, 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14 },
|
||||||
|
.{ 2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1, 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6, 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13, 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3 },
|
||||||
|
.{ 12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5, 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8, 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10, 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13 },
|
||||||
|
.{ 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10, 3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6, 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7, 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12 },
|
||||||
|
.{ 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4, 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2, 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13, 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const DES_PERMUTATION_FUNCTION_P = [_]u6{
|
||||||
|
16 - 1, 7 - 1, 20 - 1, 21 - 1, 29 - 1, 12 - 1, 28 - 1, 17 - 1,
|
||||||
|
1 - 1, 15 - 1, 23 - 1, 26 - 1, 5 - 1, 18 - 1, 31 - 1, 10 - 1,
|
||||||
|
2 - 1, 8 - 1, 24 - 1, 14 - 1, 32 - 1, 27 - 1, 3 - 1, 9 - 1,
|
||||||
|
19 - 1, 13 - 1, 30 - 1, 6 - 1, 22 - 1, 11 - 1, 4 - 1, 25 - 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DES_KS_SHIFT_SCHEDULE = .{ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };
|
const DES_KS_SHIFT_SCHEDULE = .{ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };
|
||||||
@ -54,26 +65,26 @@ const DES_KS_SHIFT_SCHEDULE = .{ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
|
|||||||
// ----------------------------------- ENCRYPTION/DECRYPTION ----------------------------------- //
|
// ----------------------------------- ENCRYPTION/DECRYPTION ----------------------------------- //
|
||||||
|
|
||||||
pub fn des_encrypt_block(
|
pub fn des_encrypt_block(
|
||||||
|
key: *const [DES_ENCODED_KEY_SIZE]u8,
|
||||||
block_in: *const [DES_BLOCK_SIZE]u8,
|
block_in: *const [DES_BLOCK_SIZE]u8,
|
||||||
block_out: *[DES_BLOCK_SIZE]u8,
|
block_out: *[DES_BLOCK_SIZE]u8,
|
||||||
key: *const [DES_ENCODED_KEY_SIZE]u8,
|
|
||||||
) void {
|
) void {
|
||||||
var state: [DES_BLOCK_SIZE]u8 = undefined;
|
var state: [DES_BLOCK_SIZE]u8 = undefined;
|
||||||
des_initial_permutation(block_in, &state);
|
des_initial_permutation(block_in, &state);
|
||||||
|
|
||||||
var expanded_key = des_expand_key(key);
|
var expanded_key = des_expand_key(key);
|
||||||
defer @memset(&expanded_key, 0);
|
defer @memset(&expanded_key, .{ 0, 0, 0, 0, 0, 0 });
|
||||||
|
|
||||||
for (0..DES_N_ROUNDS) |round|
|
for (0..DES_N_ROUNDS) |round|
|
||||||
des_perform_round(&state, expanded_key[round]);
|
des_perform_round(&state, &expanded_key[round]);
|
||||||
|
|
||||||
des_inv_initial_permutation(&state, block_out);
|
des_inv_initial_permutation(&state, block_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn des_decrypt_block(
|
pub fn des_decrypt_block(
|
||||||
|
key: *const [DES_ENCODED_KEY_SIZE]u8,
|
||||||
block_in: *const [DES_BLOCK_SIZE]u8,
|
block_in: *const [DES_BLOCK_SIZE]u8,
|
||||||
block_out: *[DES_BLOCK_SIZE]u8,
|
block_out: *[DES_BLOCK_SIZE]u8,
|
||||||
key: *const [DES_ENCODED_KEY_SIZE]u8,
|
|
||||||
) void {
|
) void {
|
||||||
var state: [DES_BLOCK_SIZE]u8 = undefined;
|
var state: [DES_BLOCK_SIZE]u8 = undefined;
|
||||||
des_inv_initial_permutation(block_in, &state);
|
des_inv_initial_permutation(block_in, &state);
|
||||||
@ -88,45 +99,61 @@ pub fn des_decrypt_block(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn des_expand_key(key: *const [DES_ENCODED_KEY_SIZE]u8) [DES_N_ROUNDS][DES_SUBKEY_SIZE]u8 {
|
pub fn des_expand_key(key: *const [DES_ENCODED_KEY_SIZE]u8) [DES_N_ROUNDS][DES_SUBKEY_SIZE]u8 {
|
||||||
var cd = des_permuted_choice_1(key);
|
const cd = des_permuted_choice_1(key);
|
||||||
|
|
||||||
|
// Probably the least stupid software implementation that I could think of.
|
||||||
|
var c_i: u28 = @truncate(std.mem.readInt(u56, &cd, .big) >> 28);
|
||||||
|
var d_i: u28 = @truncate(std.mem.readInt(u56, &cd, .big));
|
||||||
|
|
||||||
var subkeys: [DES_N_ROUNDS][DES_SUBKEY_SIZE]u8 = undefined;
|
var subkeys: [DES_N_ROUNDS][DES_SUBKEY_SIZE]u8 = undefined;
|
||||||
|
|
||||||
for (0..DES_N_ROUNDS) |i| {
|
inline for (0..DES_N_ROUNDS) |i| {
|
||||||
rotate_halves_left(cd[0..], DES_KS_SHIFT_SCHEDULE[i]);
|
c_i = rotate_left(c_i, DES_KS_SHIFT_SCHEDULE[i]);
|
||||||
des_permuted_choice_2(cd, &subkeys[i]);
|
d_i = rotate_left(d_i, DES_KS_SHIFT_SCHEDULE[i]);
|
||||||
|
des_permuted_choice_2(c_i, d_i, &subkeys[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return subkeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn des_permuted_choice_1(key: *const [DES_ENCODED_KEY_SIZE]u8) [DES_TRUE_KEY_SIZE]u8 {
|
pub fn des_permuted_choice_1(key: *const [DES_ENCODED_KEY_SIZE]u8) [DES_TRUE_KEY_SIZE]u8 {
|
||||||
const PC1 = .{
|
const PC1 = .{
|
||||||
57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36,
|
57 - 1, 49 - 1, 41 - 1, 33 - 1, 25 - 1, 17 - 1, 9 - 1, 1 - 1, 58 - 1, 50 - 1, 42 - 1, 34 - 1, 26 - 1, 18 - 1,
|
||||||
63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4,
|
10 - 1, 2 - 1, 59 - 1, 51 - 1, 43 - 1, 35 - 1, 27 - 1, 19 - 1, 11 - 1, 3 - 1, 60 - 1, 52 - 1, 44 - 1, 36 - 1,
|
||||||
|
63 - 1, 55 - 1, 47 - 1, 39 - 1, 31 - 1, 23 - 1, 15 - 1, 7 - 1, 62 - 1, 54 - 1, 46 - 1, 38 - 1, 30 - 1, 22 - 1,
|
||||||
|
14 - 1, 6 - 1, 61 - 1, 53 - 1, 45 - 1, 37 - 1, 29 - 1, 21 - 1, 13 - 1, 5 - 1, 28 - 1, 20 - 1, 12 - 1, 4 - 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
var result: [DES_TRUE_KEY_SIZE]u8 = undefined;
|
var result: [DES_TRUE_KEY_SIZE]u8 = undefined;
|
||||||
permute_bits(DES_TRUE_KEY_SIZE, key, &result, PC1);
|
permute_bits(DES_TRUE_KEY_SIZE, key, &result, &PC1);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn des_permuted_choice_2(cd: *const [DES_TRUE_KEY_SIZE]u8, out: *[DES_SUBKEY_SIZE]u8) void {
|
pub fn des_permuted_choice_2(c: u28, d: u28, out: *[DES_SUBKEY_SIZE]u8) void {
|
||||||
const PC2 = .{
|
const PC2 = .{
|
||||||
14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2,
|
14 - 1, 17 - 1, 11 - 1, 24 - 1, 1 - 1, 5 - 1, 3 - 1, 28 - 1, 15 - 1, 6 - 1, 21 - 1, 10 - 1,
|
||||||
41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32,
|
23 - 1, 19 - 1, 12 - 1, 4 - 1, 26 - 1, 8 - 1, 16 - 1, 7 - 1, 27 - 1, 20 - 1, 13 - 1, 2 - 1,
|
||||||
|
41 - 1, 52 - 1, 31 - 1, 37 - 1, 47 - 1, 55 - 1, 30 - 1, 40 - 1, 51 - 1, 45 - 1, 33 - 1, 48 - 1,
|
||||||
|
44 - 1, 49 - 1, 39 - 1, 56 - 1, 34 - 1, 53 - 1, 46 - 1, 42 - 1, 50 - 1, 36 - 1, 29 - 1, 32 - 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
permute_bits(DES_SUBKEY_SIZE, cd, out, PC2);
|
const cd = @as(u56, c) << 28 | @as(u56, d);
|
||||||
|
var cd_bytes: [DES_TRUE_KEY_SIZE]u8 = undefined;
|
||||||
|
std.mem.writeInt(u56, &cd_bytes, cd, .big);
|
||||||
|
|
||||||
|
permute_bits(DES_SUBKEY_SIZE, &cd_bytes, out, &PC2);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn des_perform_round(state: *[DES_BLOCK_SIZE]u8, subkey: *[DES_SUBKEY_SIZE]u8) void {
|
pub fn des_perform_round(state: *[DES_BLOCK_SIZE]u8, subkey: *[DES_SUBKEY_SIZE]u8) void {
|
||||||
const f_output = des_cipher_function_f(state[DES_BLOCK_SIZE / 2 ..], subkey);
|
var f_output = des_cipher_function_f(state[DES_BLOCK_SIZE / 2 ..], subkey);
|
||||||
const new_r = xor(DES_BLOCK_SIZE / 2, state[0 .. DES_BLOCK_SIZE / 2], f_output);
|
const new_r = xor(DES_BLOCK_SIZE / 2, state[0 .. DES_BLOCK_SIZE / 2], &f_output);
|
||||||
|
|
||||||
@memcpy(state[0 .. DES_BLOCK_SIZE / 2], state[DES_BLOCK_SIZE / 2 ..]);
|
@memcpy(state[0 .. DES_BLOCK_SIZE / 2], state[DES_BLOCK_SIZE / 2 ..]);
|
||||||
@memcpy(state[DES_BLOCK_SIZE / 2 ..], new_r[0..]);
|
@memcpy(state[DES_BLOCK_SIZE / 2 ..], new_r[0..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn des_cipher_function_f(word: [DES_BLOCK_SIZE / 2]u8, subkey: *const [DES_SUBKEY_SIZE]u8) [DES_BLOCK_SIZE / 2]u8 {
|
pub fn des_cipher_function_f(word: *const [DES_BLOCK_SIZE / 2]u8, subkey: *const [DES_SUBKEY_SIZE]u8) [DES_BLOCK_SIZE / 2]u8 {
|
||||||
// The input word is expanded to 48 bits.
|
// The input word is expanded to 48 bits.
|
||||||
var expanded_word: [DES_SUBKEY_SIZE]u8 = undefined;
|
var expanded_word: [DES_SUBKEY_SIZE]u8 = undefined;
|
||||||
permute_bits(DES_SUBKEY_SIZE, word, &expanded_word, &DES_BIT_SELECTION_TABLE_E);
|
permute_bits(DES_SUBKEY_SIZE, word, &expanded_word, &DES_BIT_SELECTION_TABLE_E);
|
||||||
@ -134,59 +161,73 @@ pub fn des_cipher_function_f(word: [DES_BLOCK_SIZE / 2]u8, subkey: *const [DES_S
|
|||||||
for (0..DES_SUBKEY_SIZE) |i|
|
for (0..DES_SUBKEY_SIZE) |i|
|
||||||
expanded_word[i] ^= subkey[i];
|
expanded_word[i] ^= subkey[i];
|
||||||
|
|
||||||
// todo
|
var sbox_output = std.mem.zeroes([DES_BLOCK_SIZE / 2]u8);
|
||||||
_ = .{ word, subkey };
|
|
||||||
|
inline for (0..8) |i| {
|
||||||
|
var sbox_input: u6 = @as(u6, get_nth_bit(DES_SUBKEY_SIZE, &expanded_word, i * 6));
|
||||||
|
inline for (1..6) |j| {
|
||||||
|
sbox_input <<= 1;
|
||||||
|
sbox_input |= @as(u6, get_nth_bit(DES_SUBKEY_SIZE, &expanded_word, i * 6 + j));
|
||||||
|
}
|
||||||
|
sbox_output[i / 2] |= @as(u8, DES_S_BOXES[i][sbox_input]) << if (i % 2 == 0) 4 else 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var expansion_output: [DES_BLOCK_SIZE / 2]u8 = undefined;
|
||||||
|
permute_bits(DES_BLOCK_SIZE / 2, &sbox_output, &expansion_output, &DES_PERMUTATION_FUNCTION_P);
|
||||||
|
|
||||||
|
return expansion_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn des_initial_permutation(in: *const [DES_BLOCK_SIZE]u8, out: *[DES_BLOCK_SIZE]u8) void {
|
pub fn des_initial_permutation(in: *const [DES_BLOCK_SIZE]u8, out: *[DES_BLOCK_SIZE]u8) void {
|
||||||
permute_bits(DES_BLOCK_SIZE, in, out, DES_INITIAL_PERMUTATION);
|
permute_bits(DES_BLOCK_SIZE, in[0..], out, &DES_INITIAL_PERMUTATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn des_inv_initial_permutation(in: *const [DES_BLOCK_SIZE]u8, out: *[DES_BLOCK_SIZE]u8) void {
|
pub fn des_inv_initial_permutation(in: *const [DES_BLOCK_SIZE]u8, out: *[DES_BLOCK_SIZE]u8) void {
|
||||||
permute_bits(DES_BLOCK_SIZE, in, out, DES_INV_INITIAL_PERMUTATION);
|
permute_bits(DES_BLOCK_SIZE, in[0..], out, &DES_INV_INITIAL_PERMUTATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- HELPERS ----------------------------------- //
|
// ----------------------------------- HELPERS ----------------------------------- //
|
||||||
|
|
||||||
fn permute_bits(L: comptime_int, in: []u8, out: *[L]u8, key: [8 * L]u6) void {
|
fn permute_bits(L: comptime_int, in: []const u8, out: *[L]u8, key: *const [8 * L]u6) void {
|
||||||
for (0..8 * L) |i| {
|
for (0..8 * L) |i| {
|
||||||
const pi = key[i];
|
const pi = key[i];
|
||||||
if (pi >= in.len * 8)
|
if (pi >= in.len * 8)
|
||||||
@panic("Bit index out of range!");
|
@panic("Bit index out of range!");
|
||||||
const bit = get_nth_bit(in, pi);
|
const bit = get_nth_bit(8, in, pi);
|
||||||
set_nth_bit(out, pi, bit);
|
set_nth_bit(L, out, @intCast(i), bit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_nth_bit(bytes: *const [8]u8, n: u6) u1 {
|
fn get_nth_bit(comptime L: u4, bytes: []const u8, n: u6) u1 {
|
||||||
|
if (L > 8 or n >= bytes.len * 8)
|
||||||
|
@panic("Bit index out of range!");
|
||||||
|
|
||||||
const byte_idx = n / 8;
|
const byte_idx = n / 8;
|
||||||
const bit_idx = n % 8;
|
const bit_idx = n % 8;
|
||||||
|
|
||||||
return @truncate(bytes[byte_idx] >> (7 - bit_idx));
|
return @truncate(bytes[byte_idx] >> @intCast(7 - bit_idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_nth_bit(bytes: *const [8]u8, n: u6, bit: u1) void {
|
fn set_nth_bit(comptime L: u4, bytes: []u8, n: u6, bit: u1) void {
|
||||||
|
if (L > 8 or n >= bytes.len * 8)
|
||||||
|
@panic("Bit index out of range!");
|
||||||
|
|
||||||
const byte_idx = n / 8;
|
const byte_idx = n / 8;
|
||||||
const bit_idx = n % 8;
|
const bit_idx = n % 8;
|
||||||
|
|
||||||
if (bit == 1)
|
if (bit == 1)
|
||||||
bytes[byte_idx] |= (1 << (7 - bit_idx))
|
bytes[byte_idx] |= (@as(u8, 1) << @intCast(7 - bit_idx))
|
||||||
else
|
else
|
||||||
bytes[byte_idx] &= ~(1 << (7 - bit_idx));
|
bytes[byte_idx] &= ~(@as(u8, 1) << @intCast(7 - bit_idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xor(L: comptime_int, a: [L]u8, b: [L]u8) [L]u8 {
|
fn xor(L: comptime_int, a: *const [L]u8, b: *const [L]u8) [L]u8 {
|
||||||
var result: [L]u8 = undefined;
|
var result: [L]u8 = undefined;
|
||||||
for (0..L) |i|
|
for (0..L) |i|
|
||||||
result[i] = a[i] ^ b[i];
|
result[i] = a[i] ^ b[i];
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rotate_halves_left(cd: *[DES_TRUE_KEY_SIZE]u8, positions: comptime_int) void {
|
fn rotate_left(x: u28, positions: comptime_int) u28 {
|
||||||
// TODO
|
return x << positions | x >> (28 - positions);
|
||||||
_ = .{ cd, positions };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- TEST VECTORS ----------------------------------- //
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|||||||
@ -1,2 +1,10 @@
|
|||||||
pub const aes = @import("aes.zig");
|
pub const aes = @import("aes.zig");
|
||||||
pub const des = @import("des.zig");
|
pub const des = @import("des.zig");
|
||||||
|
pub const serpent = @import("serpent.zig");
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
324
src/primitive/blockcipher/mode_gcm.zig
Normal file
324
src/primitive/blockcipher/mode_gcm.zig
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
const CryptoError = error{
|
||||||
|
MessageLengthLimitExceeded,
|
||||||
|
BufferSizeMismatch,
|
||||||
|
InvalidIVLength,
|
||||||
|
InvalidTagLength,
|
||||||
|
IncorrectAuthenticationTag,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const GCM128_BLOCK_SIZE = 128 / 8;
|
||||||
|
|
||||||
|
pub fn gcm128_cipher_fn_t(comptime key_size: usize) type {
|
||||||
|
return fn (
|
||||||
|
key: *const [key_size]u8,
|
||||||
|
block_in: *const [GCM128_BLOCK_SIZE]u8,
|
||||||
|
block_out: *[GCM128_BLOCK_SIZE]u8,
|
||||||
|
) void;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Gcm128Ctx(
|
||||||
|
comptime cipher_key_size: usize,
|
||||||
|
cipher_encrypt_fn: gcm128_cipher_fn_t(cipher_key_size),
|
||||||
|
) type {
|
||||||
|
return struct {
|
||||||
|
const BLOCK_SIZE = GCM128_BLOCK_SIZE;
|
||||||
|
const AAD_MAX_BYTES = (1 << 64) / 8;
|
||||||
|
const TEXT_MAX_BYTES = ((1 << 39) - 256) / 8;
|
||||||
|
const TAG_MAX_BYTES = 128 / 8;
|
||||||
|
|
||||||
|
const KEY_SIZE = cipher_key_size;
|
||||||
|
const CIPHER_FN = cipher_encrypt_fn;
|
||||||
|
|
||||||
|
key: [cipher_key_size]u8,
|
||||||
|
keystream: [BLOCK_SIZE]u8,
|
||||||
|
counter: [BLOCK_SIZE]u8,
|
||||||
|
ghash_x: [BLOCK_SIZE]u8,
|
||||||
|
h: [BLOCK_SIZE]u8,
|
||||||
|
y0: [BLOCK_SIZE]u8,
|
||||||
|
ciphertext_buffer: [BLOCK_SIZE]u8,
|
||||||
|
ciphertext_length: u64,
|
||||||
|
aad_length: u64,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcm128_new(
|
||||||
|
comptime cipher_key_size: usize,
|
||||||
|
cipher_encrypt_fn: gcm128_cipher_fn_t(cipher_key_size),
|
||||||
|
key: *const [cipher_key_size]u8,
|
||||||
|
iv: []const u8,
|
||||||
|
) !Gcm128Ctx(cipher_key_size, cipher_encrypt_fn) {
|
||||||
|
if (iv.len == 0)
|
||||||
|
return CryptoError.InvalidIVLength;
|
||||||
|
|
||||||
|
var ctx = Gcm128Ctx(cipher_key_size, cipher_encrypt_fn){
|
||||||
|
.key = undefined,
|
||||||
|
.keystream = undefined,
|
||||||
|
.counter = undefined,
|
||||||
|
.ghash_x = undefined,
|
||||||
|
.h = undefined,
|
||||||
|
.y0 = undefined,
|
||||||
|
.ciphertext_buffer = undefined,
|
||||||
|
.ciphertext_length = 0,
|
||||||
|
.aad_length = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the encryption/decryption key.
|
||||||
|
@memcpy(&ctx.key, key);
|
||||||
|
|
||||||
|
// Compute the `H` value.
|
||||||
|
cipher_encrypt_fn(&ctx.key, &std.mem.zeroes([GCM128_BLOCK_SIZE]u8), &ctx.h);
|
||||||
|
|
||||||
|
// Set the counter initial value (`Y_0`).
|
||||||
|
if (iv.len == 96 / 8) {
|
||||||
|
@memcpy(ctx.counter[0 .. 96 / 8], iv);
|
||||||
|
@memset(ctx.counter[(96 / 8)..(ctx.counter.len - 1)], 0x00);
|
||||||
|
ctx.counter[ctx.counter.len - 1] = 0x01;
|
||||||
|
} else {
|
||||||
|
gcm128_ghash(&ctx.h, &.{}, iv, &ctx.counter);
|
||||||
|
}
|
||||||
|
@memcpy(&ctx.y0, &ctx.counter);
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcm128_destroy(
|
||||||
|
comptime cipher_key_size: usize,
|
||||||
|
cipher_encrypt_fn: gcm128_cipher_fn_t(cipher_key_size),
|
||||||
|
ctx: *Gcm128Ctx(cipher_key_size, cipher_encrypt_fn),
|
||||||
|
) void {
|
||||||
|
@memset(&ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Important: this function must only be called at most once
|
||||||
|
// AFTER the context is initialized but BEFORE any data is en/decrypted.
|
||||||
|
// TODO: Enforce at API level.
|
||||||
|
pub fn gcm128_authenticate_data_once(
|
||||||
|
comptime cipher_key_size: usize,
|
||||||
|
cipher_encrypt_fn: gcm128_cipher_fn_t(cipher_key_size),
|
||||||
|
ctx: *Gcm128Ctx(cipher_key_size, cipher_encrypt_fn),
|
||||||
|
data: []const u8,
|
||||||
|
) !void {
|
||||||
|
// The maximum bit length of the AAD is 2^64.
|
||||||
|
if (data.len >= Gcm128Ctx.AAD_MAX_BYTES)
|
||||||
|
return CryptoError.MessageLengthLimitExceeded;
|
||||||
|
|
||||||
|
// Compute and store `X_m` where m is the length in blocks of the AAD.
|
||||||
|
gcm128_ghash_pad_chunk(&std.mem.zeroes([]u8), &ctx.h, data, &ctx.ghash_x);
|
||||||
|
|
||||||
|
// Store the AAD length for the tag computation.
|
||||||
|
ctx.aad_length = data.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcm128_encrypt(
|
||||||
|
comptime cipher_key_size: usize,
|
||||||
|
cipher_encrypt_fn: gcm128_cipher_fn_t(cipher_key_size),
|
||||||
|
ctx: *Gcm128Ctx(cipher_key_size, cipher_encrypt_fn),
|
||||||
|
plaintext: []const u8,
|
||||||
|
ciphertext: []u8,
|
||||||
|
) !void {
|
||||||
|
if (plaintext.len != ciphertext.len)
|
||||||
|
return CryptoError.BufferSizeMismatch;
|
||||||
|
|
||||||
|
if (ctx.ciphertext_length + plaintext.len >= @TypeOf(ctx.*).TEXT_MAX_BYTES)
|
||||||
|
return CryptoError.MessageLengthLimitExceeded;
|
||||||
|
|
||||||
|
const BS = GCM128_BLOCK_SIZE;
|
||||||
|
const KS = @TypeOf(ctx.*).KEY_SIZE;
|
||||||
|
const ENC = @TypeOf(ctx.*).CIPHER_FN;
|
||||||
|
|
||||||
|
const bytes_buffered_ct = ctx.ciphertext_length % BS;
|
||||||
|
var bytes_processed: usize = 0;
|
||||||
|
|
||||||
|
// If we're not at the beginning of a new block, use the existing keystream to encrypt the plaintext.
|
||||||
|
if (bytes_buffered_ct != 0) {
|
||||||
|
// Simplest case - no block border is reached - just XOR-encrypt and we're done.
|
||||||
|
if (bytes_buffered_ct + plaintext.len < BS) {
|
||||||
|
return gcm128_crypt_xor(KS, ENC, ctx, plaintext, ciphertext, false);
|
||||||
|
}
|
||||||
|
// Otherwise, encrypt the remainder of the block with the existing
|
||||||
|
// keystream and then authenticate the completed ciphertext block.
|
||||||
|
else {
|
||||||
|
const len = BS - bytes_buffered_ct;
|
||||||
|
gcm128_crypt_xor(KS, ENC, ctx, plaintext[0..len], ciphertext[0..len], false);
|
||||||
|
gcm128_authenticate_ciphertext_block(KS, ENC, ctx);
|
||||||
|
bytes_processed += BS - bytes_buffered_ct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// As long as we have another whole block worth of plaintext, encrypt it
|
||||||
|
// and authenticate the CT.
|
||||||
|
while ((plaintext.len - bytes_processed) / BS > 0) : (bytes_processed += BS) {
|
||||||
|
const start = bytes_processed;
|
||||||
|
const end = bytes_processed + BS;
|
||||||
|
gcm128_update_keystream(KS, ENC, ctx);
|
||||||
|
gcm128_crypt_xor(KS, ENC, ctx, plaintext[start..end], ciphertext[start..end], false);
|
||||||
|
gcm128_authenticate_ciphertext_block(KS, ENC, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, if there's still plaintext left, update the keystream and encrypt the rest of it.
|
||||||
|
if (bytes_processed < plaintext.len) {
|
||||||
|
gcm128_update_keystream(KS, ENC, ctx);
|
||||||
|
gcm128_crypt_xor(KS, ENC, ctx, plaintext[bytes_processed..], ciphertext[bytes_processed..], false);
|
||||||
|
bytes_processed += plaintext.len - bytes_processed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gcm128_crypt_xor(
|
||||||
|
comptime cipher_key_size: usize,
|
||||||
|
cipher_encrypt_fn: gcm128_cipher_fn_t(cipher_key_size),
|
||||||
|
ctx: *Gcm128Ctx(cipher_key_size, cipher_encrypt_fn),
|
||||||
|
input_slice: []const u8,
|
||||||
|
output_slice: []u8,
|
||||||
|
decrypt: bool,
|
||||||
|
) void {
|
||||||
|
const block_offset = ctx.ciphertext_length % GCM128_BLOCK_SIZE;
|
||||||
|
|
||||||
|
if (input_slice.len != output_slice.len or
|
||||||
|
block_offset + input_slice.len > GCM128_BLOCK_SIZE)
|
||||||
|
@panic("gcm128_crypt_xor contract violated!");
|
||||||
|
|
||||||
|
const len = input_slice.len;
|
||||||
|
for (0..len, block_offset..block_offset + len) |ct_i, ks_i| {
|
||||||
|
ctx.ciphertext_buffer[ks_i] = if (decrypt)
|
||||||
|
input_slice[ct_i]
|
||||||
|
else
|
||||||
|
input_slice[ct_i] ^ ctx.keystream[ks_i];
|
||||||
|
|
||||||
|
output_slice[ct_i] = ctx.ciphertext_buffer[ks_i];
|
||||||
|
}
|
||||||
|
ctx.ciphertext_length += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gcm128_update_keystream(
|
||||||
|
comptime cipher_key_size: usize,
|
||||||
|
cipher_encrypt_fn: gcm128_cipher_fn_t(cipher_key_size),
|
||||||
|
ctx: *Gcm128Ctx(cipher_key_size, cipher_encrypt_fn),
|
||||||
|
) void {
|
||||||
|
gcm128_incr(&ctx.counter);
|
||||||
|
cipher_encrypt_fn(&ctx.key, &ctx.counter, &ctx.keystream);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gcm128_authenticate_ciphertext_block(
|
||||||
|
comptime cipher_key_size: usize,
|
||||||
|
cipher_encrypt_fn: gcm128_cipher_fn_t(cipher_key_size),
|
||||||
|
ctx: *Gcm128Ctx(cipher_key_size, cipher_encrypt_fn),
|
||||||
|
) void {
|
||||||
|
xor(GCM128_BLOCK_SIZE, &ctx.ghash_x, &ctx.ciphertext_buffer, &ctx.ghash_x);
|
||||||
|
gcm128_mult(&ctx.ghash_x, &ctx.h, &ctx.ghash_x);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcm128_encrypt_final(
|
||||||
|
comptime cipher_key_size: usize,
|
||||||
|
cipher_encrypt_fn: gcm128_cipher_fn_t(cipher_key_size),
|
||||||
|
ctx: *Gcm128Ctx(cipher_key_size, cipher_encrypt_fn),
|
||||||
|
tag: []u8,
|
||||||
|
) !void {
|
||||||
|
if (tag.len > @TypeOf(ctx.*).TAG_MAX_BYTES)
|
||||||
|
return CryptoError.InvalidTagLength;
|
||||||
|
|
||||||
|
const KS = @TypeOf(ctx.*).KEY_SIZE;
|
||||||
|
const ENC = @TypeOf(ctx.*).CIPHER_FN;
|
||||||
|
|
||||||
|
// The last ciphertext block is 0-padded and authenticated.
|
||||||
|
const ct_residue_bytes = ctx.ciphertext_length % GCM128_BLOCK_SIZE;
|
||||||
|
@memset(ctx.ciphertext_buffer[ct_residue_bytes..], 0);
|
||||||
|
gcm128_authenticate_ciphertext_block(KS, ENC, ctx);
|
||||||
|
|
||||||
|
// Lastly, authenticate the lengths of the AAD and the ciphertext.
|
||||||
|
std.mem.writeInt(u64, ctx.ciphertext_buffer[0..8], ctx.aad_length, .big);
|
||||||
|
std.mem.writeInt(u64, ctx.ciphertext_buffer[8..], ctx.ciphertext_length, .big);
|
||||||
|
gcm128_authenticate_ciphertext_block(KS, ENC, ctx);
|
||||||
|
|
||||||
|
// The authentication tag is the [tag.len] leftmost bytes of the final `X` XORed with `E(K,Y0)`.
|
||||||
|
cipher_encrypt_fn(&ctx.key, &ctx.y0, &ctx.keystream);
|
||||||
|
xor(GCM128_BLOCK_SIZE, &ctx.ghash_x, &ctx.keystream, &ctx.ghash_x);
|
||||||
|
|
||||||
|
@memcpy(tag[0..], ctx.ghash_x[0..tag.len]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcm128_incr(ctr: *[GCM128_BLOCK_SIZE]u8) void {
|
||||||
|
const val32 = std.mem.readInt(u32, ctr[ctr.len - 4 .. ctr.len], .big);
|
||||||
|
std.mem.writeInt(u32, ctr[ctr.len - 4 .. ctr.len], val32 +% 1, .big);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcm128_ghash_pad_chunk(
|
||||||
|
iv: *const [GCM128_BLOCK_SIZE]u8,
|
||||||
|
h: *const [GCM128_BLOCK_SIZE]u8,
|
||||||
|
chunk: []const u8,
|
||||||
|
out: *[GCM128_BLOCK_SIZE]u8,
|
||||||
|
) void {
|
||||||
|
const BS = GCM128_BLOCK_SIZE;
|
||||||
|
|
||||||
|
const m = chunk.len / BS;
|
||||||
|
// Note: This definition of `v` is different from the one in the standard,
|
||||||
|
// it is the number of residue *bytes* in the last block, not bits.
|
||||||
|
const v = chunk.len % BS;
|
||||||
|
|
||||||
|
var x_i: [GCM128_BLOCK_SIZE]u8 = iv.*;
|
||||||
|
var i: usize = 0;
|
||||||
|
|
||||||
|
while (i < m) : (i += 1) {
|
||||||
|
xor(BS, x_i[0..], @ptrCast(chunk[i * BS .. (i + 1) * BS]), &x_i);
|
||||||
|
gcm128_mult(&x_i, h, &x_i);
|
||||||
|
}
|
||||||
|
|
||||||
|
var padded: [GCM128_BLOCK_SIZE]u8 = undefined;
|
||||||
|
@memcpy(padded[0..v], chunk[i * BS ..]);
|
||||||
|
@memset(padded[v..], 0x00);
|
||||||
|
|
||||||
|
xor(BS, x_i[0..], padded[0..], &x_i);
|
||||||
|
gcm128_mult(&x_i, h, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcm128_ghash(
|
||||||
|
h: *const [GCM128_BLOCK_SIZE]u8,
|
||||||
|
aad_chunk: []const u8,
|
||||||
|
ciphertext_chunk: []const u8,
|
||||||
|
out: *[GCM128_BLOCK_SIZE]u8,
|
||||||
|
) void {
|
||||||
|
gcm128_ghash_pad_chunk(&std.mem.zeroes([GCM128_BLOCK_SIZE]u8), h, aad_chunk, out);
|
||||||
|
gcm128_ghash_pad_chunk(out, h, ciphertext_chunk, out);
|
||||||
|
|
||||||
|
const BS = GCM128_BLOCK_SIZE;
|
||||||
|
|
||||||
|
var lengths: [BS]u8 = undefined;
|
||||||
|
std.mem.writeInt(u64, lengths[0 .. BS / 2], aad_chunk.len * 8, .big);
|
||||||
|
std.mem.writeInt(u64, lengths[BS / 2 ..], ciphertext_chunk.len * 8, .big);
|
||||||
|
|
||||||
|
xor(BS, lengths[0..], out[0..], out);
|
||||||
|
gcm128_mult(out, h, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Naive algorithm: implement optimized versions with table lookups.
|
||||||
|
pub fn gcm128_mult(
|
||||||
|
a: *const [128 / 8]u8,
|
||||||
|
b: *const [128 / 8]u8,
|
||||||
|
out: *[128 / 8]u8,
|
||||||
|
) void {
|
||||||
|
const gcm_128_irreducible_R: u128 = 0xe100_0000_0000_0000_0000_0000_0000_0000;
|
||||||
|
|
||||||
|
var z: u128 = 0;
|
||||||
|
var v: u128 = std.mem.readInt(u128, a, .big);
|
||||||
|
const y: u128 = std.mem.readInt(u128, b, .big);
|
||||||
|
|
||||||
|
for (0..128) |i| {
|
||||||
|
const bit_index = 127 - @as(u7, @intCast(i));
|
||||||
|
if ((y >> bit_index) & 1 == 1)
|
||||||
|
z ^= v;
|
||||||
|
|
||||||
|
if (v & 1 == 0)
|
||||||
|
v = v >> 1
|
||||||
|
else
|
||||||
|
v = (v >> 1) ^ gcm_128_irreducible_R;
|
||||||
|
}
|
||||||
|
|
||||||
|
std.mem.writeInt(u128, out, z, .big);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn xor(L: comptime_int, a: *const [L]u8, b: *const [L]u8, out: *[L]u8) void {
|
||||||
|
for (0..L) |i|
|
||||||
|
out[i] = a[i] ^ b[i];
|
||||||
|
}
|
||||||
41
src/primitive/blockcipher/padding.zig
Normal file
41
src/primitive/blockcipher/padding.zig
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
const CryptoError = error{
|
||||||
|
InvalidPadding,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn pkcs5_pad(block: *[8]u8, plaintext_length: usize) void {
|
||||||
|
return pkcs7_pad(8, block, plaintext_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pkcs5_unpad(block: *[8]u8) !u8 {
|
||||||
|
return pkcs7_unpad(8, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pkcs7_pad(
|
||||||
|
comptime block_size: u8,
|
||||||
|
block: *[block_size]u8,
|
||||||
|
plaintext_length: usize,
|
||||||
|
) void {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pkcs7_unpad(
|
||||||
|
comptime block_size: u8,
|
||||||
|
block: *const [block_size]u8,
|
||||||
|
) !u8 {
|
||||||
|
const padding_val = block[block_size - 1];
|
||||||
|
if (padding_val == 0 or padding_val > block_size)
|
||||||
|
return CryptoError.InvalidPadding;
|
||||||
|
|
||||||
|
const plaintext_residue_length = block_size - padding_val;
|
||||||
|
|
||||||
|
for (plaintext_residue_length..block_size) |i|
|
||||||
|
if (block[i] != padding_val)
|
||||||
|
return CryptoError.InvalidPadding;
|
||||||
|
|
||||||
|
return plaintext_residue_length;
|
||||||
|
}
|
||||||
237
src/primitive/blockcipher/serpent.zig
Normal file
237
src/primitive/blockcipher/serpent.zig
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
const byte_operations = @import("utility").byte_operations;
|
||||||
|
const word_to_bytes = byte_operations.word_to_bytes_le;
|
||||||
|
const bytes_to_word = byte_operations.bytes_to_word_le;
|
||||||
|
|
||||||
|
const CryptoError = error{
|
||||||
|
InvalidKeyLength,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------- CONSTANTS ----------------------------------- //
|
||||||
|
|
||||||
|
pub const BLOCK_SIZE = 128 / 8;
|
||||||
|
|
||||||
|
pub const KEY_SIZE_128 = 128 / 8;
|
||||||
|
pub const KEY_SIZE_192 = 192 / 8;
|
||||||
|
pub const KEY_SIZE_256 = 256 / 8;
|
||||||
|
|
||||||
|
pub const MAX_KEY_SIZE = KEY_SIZE_256;
|
||||||
|
|
||||||
|
const N_ROUNDS = 32;
|
||||||
|
const SUBKEY_COUNT = N_ROUNDS + 1;
|
||||||
|
|
||||||
|
const SBOXES = [_][16]u8{
|
||||||
|
.{ 3, 8, 15, 1, 10, 6, 5, 11, 14, 13, 4, 2, 7, 0, 9, 12 }, // S0
|
||||||
|
.{ 15, 12, 2, 7, 9, 0, 5, 10, 1, 11, 14, 8, 6, 13, 3, 4 }, // S1
|
||||||
|
.{ 8, 6, 7, 9, 3, 12, 10, 15, 13, 1, 14, 4, 0, 11, 5, 2 }, // S2
|
||||||
|
.{ 0, 15, 11, 8, 12, 9, 6, 3, 13, 1, 2, 4, 10, 7, 5, 14 }, // S3
|
||||||
|
.{ 1, 15, 8, 3, 12, 0, 11, 6, 2, 5, 4, 10, 9, 14, 7, 13 }, // S4
|
||||||
|
.{ 15, 5, 2, 11, 4, 10, 9, 12, 0, 3, 14, 8, 13, 6, 7, 1 }, // S5
|
||||||
|
.{ 7, 2, 12, 5, 8, 4, 6, 11, 14, 9, 1, 15, 13, 3, 10, 0 }, // S6
|
||||||
|
.{ 1, 13, 15, 0, 14, 8, 2, 11, 7, 4, 12, 10, 9, 3, 5, 6 }, // S7
|
||||||
|
};
|
||||||
|
|
||||||
|
const PHI_FRAC: u32 = 0x9e3779b9;
|
||||||
|
|
||||||
|
// ----------------------------------- KEY EXPANSION ----------------------------------- //
|
||||||
|
|
||||||
|
pub fn expand_key(key: []const u8) ![SUBKEY_COUNT][BLOCK_SIZE / 4]u32 {
|
||||||
|
// Pad short keys to 256 bits.
|
||||||
|
const full_key = try pad_user_key(key);
|
||||||
|
|
||||||
|
// Prepare the prekey.
|
||||||
|
var prekey: [140]u32 = undefined;
|
||||||
|
@memcpy(prekey[0..8], full_key[0..]);
|
||||||
|
|
||||||
|
for (0..132) |_j| {
|
||||||
|
const j: u32 = @intCast(_j);
|
||||||
|
const i: u32 = @intCast(j + 8);
|
||||||
|
const tmp = prekey[i - 8] ^ prekey[i - 5] ^ prekey[i - 3] ^ prekey[i - 1] ^ PHI_FRAC ^ j;
|
||||||
|
prekey[i] = (tmp << 11) | (tmp >> 21);
|
||||||
|
}
|
||||||
|
|
||||||
|
//for (0..SUBKEY_COUNT) |i| {
|
||||||
|
// std.debug.print("{d}: {x} {x} {x} {x}\n", .{
|
||||||
|
// i,
|
||||||
|
// prekey[8 + i * 4 + 0],
|
||||||
|
// prekey[8 + i * 4 + 1],
|
||||||
|
// prekey[8 + i * 4 + 2],
|
||||||
|
// prekey[8 + i * 4 + 3],
|
||||||
|
// });
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Compute the subkeys.
|
||||||
|
var subkeys: [SUBKEY_COUNT][BLOCK_SIZE / 4]u32 = undefined;
|
||||||
|
var tmp: [4]u32 = undefined;
|
||||||
|
|
||||||
|
inline for (0..SUBKEY_COUNT) |i| {
|
||||||
|
sbox_bitslice(
|
||||||
|
(32 + 3 - i) % 8,
|
||||||
|
prekey[8 + i * 4 + 0],
|
||||||
|
prekey[8 + i * 4 + 1],
|
||||||
|
prekey[8 + i * 4 + 2],
|
||||||
|
prekey[8 + i * 4 + 3],
|
||||||
|
&tmp[0],
|
||||||
|
&tmp[1],
|
||||||
|
&tmp[2],
|
||||||
|
&tmp[3],
|
||||||
|
);
|
||||||
|
subkeys[i] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
//for (0..SUBKEY_COUNT) |i| {
|
||||||
|
// std.debug.print("{d}: {x} {x} {x} {x}\n", .{ i, subkeys[i][0], subkeys[i][1], subkeys[i][2], subkeys[i][3] });
|
||||||
|
//}
|
||||||
|
|
||||||
|
return subkeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pad_user_key(key: []const u8) ![MAX_KEY_SIZE / 4]u32 {
|
||||||
|
if (key.len > MAX_KEY_SIZE)
|
||||||
|
return CryptoError.InvalidKeyLength;
|
||||||
|
|
||||||
|
var long_key: [MAX_KEY_SIZE]u8 = undefined;
|
||||||
|
@memcpy(long_key[0..key.len], key[0..]);
|
||||||
|
if (key.len < MAX_KEY_SIZE) {
|
||||||
|
long_key[key.len] = 0x01;
|
||||||
|
@memset(long_key[key.len + 1 ..], 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
//std.debug.print("{any}\n", .{long_key});
|
||||||
|
|
||||||
|
var key_words: [MAX_KEY_SIZE / 4]u32 = undefined;
|
||||||
|
inline for (0..MAX_KEY_SIZE / 4) |i|
|
||||||
|
key_words[i] = bytes_to_word(@ptrCast(long_key[i * 4 .. (i + 1) * 4]));
|
||||||
|
return key_words;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------- ENCRYPTION ----------------------------------- //
|
||||||
|
|
||||||
|
pub fn encrypt_block(
|
||||||
|
block_in: *const [BLOCK_SIZE]u8,
|
||||||
|
block_out: *[BLOCK_SIZE]u8,
|
||||||
|
key_schedule: *const [SUBKEY_COUNT][BLOCK_SIZE / 4]u32,
|
||||||
|
) void {
|
||||||
|
var state = [BLOCK_SIZE / 4]u32{
|
||||||
|
bytes_to_word(block_in[0..4]),
|
||||||
|
bytes_to_word(block_in[4..8]),
|
||||||
|
bytes_to_word(block_in[8..12]),
|
||||||
|
bytes_to_word(block_in[12..16]),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Apply all rounds but the last.
|
||||||
|
inline for (0..N_ROUNDS - 1) |i| {
|
||||||
|
add_round_key(&state, &key_schedule[i]);
|
||||||
|
sbox_bitslice(
|
||||||
|
i % 8,
|
||||||
|
state[0],
|
||||||
|
state[1],
|
||||||
|
state[2],
|
||||||
|
state[3],
|
||||||
|
&state[0],
|
||||||
|
&state[1],
|
||||||
|
&state[2],
|
||||||
|
&state[3],
|
||||||
|
);
|
||||||
|
linear_transformation(&state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the last round.
|
||||||
|
add_round_key(&state, &key_schedule[N_ROUNDS - 1]);
|
||||||
|
sbox_bitslice(
|
||||||
|
(N_ROUNDS - 1) % 8,
|
||||||
|
state[0],
|
||||||
|
state[1],
|
||||||
|
state[2],
|
||||||
|
state[3],
|
||||||
|
&state[0],
|
||||||
|
&state[1],
|
||||||
|
&state[2],
|
||||||
|
&state[3],
|
||||||
|
);
|
||||||
|
add_round_key(&state, &key_schedule[N_ROUNDS]);
|
||||||
|
|
||||||
|
block_out.* = word_to_bytes(state[0]) ++ word_to_bytes(state[1]) ++ word_to_bytes(state[2]) ++ word_to_bytes(state[3]);
|
||||||
|
std.crypto.utils.secureZero(u32, &state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------- COMMON OPERATIONS ----------------------------------- //
|
||||||
|
|
||||||
|
pub fn add_round_key(state: *[BLOCK_SIZE / 4]u32, subkey: *const [BLOCK_SIZE / 4]u32) void {
|
||||||
|
for (0..state.len) |i|
|
||||||
|
state[i] ^= subkey[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn linear_transformation(state: *[BLOCK_SIZE / 4]u32) void {
|
||||||
|
var x0 = state[0];
|
||||||
|
var x1 = state[1];
|
||||||
|
var x2 = state[2];
|
||||||
|
var x3 = state[3];
|
||||||
|
|
||||||
|
x0 = rol(x0, 13);
|
||||||
|
x2 = rol(x2, 3);
|
||||||
|
x1 ^= x0 ^ x2;
|
||||||
|
x3 ^= (x0 << 3) ^ x2;
|
||||||
|
x1 = rol(x1, 1);
|
||||||
|
x3 = rol(x3, 7);
|
||||||
|
x0 ^= x1 ^ x3;
|
||||||
|
x2 ^= (x1 << 7) ^ x3;
|
||||||
|
x0 = rol(x0, 5);
|
||||||
|
x2 = rol(x2, 22);
|
||||||
|
|
||||||
|
state[0] = x0;
|
||||||
|
state[1] = x1;
|
||||||
|
state[2] = x2;
|
||||||
|
state[3] = x3;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sbox_bitslice(
|
||||||
|
sbox_id: comptime_int,
|
||||||
|
x0: u32,
|
||||||
|
x1: u32,
|
||||||
|
x2: u32,
|
||||||
|
x3: u32,
|
||||||
|
y0: *u32,
|
||||||
|
y1: *u32,
|
||||||
|
y2: *u32,
|
||||||
|
y3: *u32,
|
||||||
|
) void {
|
||||||
|
y0.* = 0;
|
||||||
|
y1.* = 0;
|
||||||
|
y2.* = 0;
|
||||||
|
y3.* = 0;
|
||||||
|
|
||||||
|
for (0..32) |i| {
|
||||||
|
const bitPos: u5 = @intCast(i);
|
||||||
|
|
||||||
|
var z = ((x0 >> bitPos) & 1) << 0 | ((x1 >> bitPos) & 1) << 1 | ((x2 >> bitPos) & 1) << 2 | ((x3 >> bitPos) & 1) << 3;
|
||||||
|
|
||||||
|
z = SBOXES[sbox_id][z];
|
||||||
|
|
||||||
|
if (((z >> 0) & 1) == 1)
|
||||||
|
y0.* |= (@as(u32, 1) << bitPos)
|
||||||
|
else
|
||||||
|
y0.* &= ~(@as(u32, 1) << bitPos);
|
||||||
|
|
||||||
|
if (((z >> 1) & 1) == 1)
|
||||||
|
y1.* |= (@as(u32, 1) << bitPos)
|
||||||
|
else
|
||||||
|
y1.* &= ~(@as(u32, 1) << bitPos);
|
||||||
|
|
||||||
|
if (((z >> 2) & 1) == 1)
|
||||||
|
y2.* |= (@as(u32, 1) << bitPos)
|
||||||
|
else
|
||||||
|
y2.* &= ~(@as(u32, 1) << bitPos);
|
||||||
|
|
||||||
|
if (((z >> 3) & 1) == 1)
|
||||||
|
y3.* |= (@as(u32, 1) << bitPos)
|
||||||
|
else
|
||||||
|
y3.* &= ~(@as(u32, 1) << bitPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rol(word: u32, by: comptime_int) u32 {
|
||||||
|
return (word << by) | (word >> (32 - by));
|
||||||
|
}
|
||||||
@ -2,23 +2,22 @@ const std = @import("std");
|
|||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
|
||||||
const sha = @import("./sha_core.zig");
|
const sha = @import("./sha_core.zig");
|
||||||
|
pub const MessageLengthLimitExceeded = sha.MessageLengthLimitExceeded;
|
||||||
|
|
||||||
pub const SHA_1_DIGEST_LENGTH = 160 / 8;
|
pub const DIGEST_LENGTH = 160 / 8;
|
||||||
pub const SHA_1_MESSAGE_BITS_LIMIT = 1 << 64;
|
pub const MESSAGE_BITS_LIMIT = 1 << 64;
|
||||||
|
|
||||||
pub const Sha1Ctx = struct {
|
pub const Sha1Ctx = struct {
|
||||||
pub const BLOCK_SIZE = 512 / 8;
|
pub const BLOCK_SIZE = 512 / 8;
|
||||||
pub const MESSAGE_SCHEDULE_WORDS = 80;
|
pub const MESSAGE_SCHEDULE_WORDS = 80;
|
||||||
pub const WordType = u32;
|
pub const WordType = u32;
|
||||||
|
|
||||||
hash: [SHA_1_DIGEST_LENGTH / @sizeOf(WordType)]WordType,
|
hash: [DIGEST_LENGTH / @sizeOf(WordType)]WordType,
|
||||||
message_buffer: [BLOCK_SIZE]u8,
|
message_buffer: [BLOCK_SIZE]u8,
|
||||||
message_length: u64,
|
message_length: u64,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SHA_1_IV = [_]u32{
|
const SHA_1_IV = [_]u32{ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 };
|
||||||
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn sha1_new() Sha1Ctx {
|
pub fn sha1_new() Sha1Ctx {
|
||||||
var ctx = Sha1Ctx{
|
var ctx = Sha1Ctx{
|
||||||
@ -33,19 +32,19 @@ pub fn sha1_new() Sha1Ctx {
|
|||||||
pub fn sha1_update(ctx: *Sha1Ctx, message: []const u8) !void {
|
pub fn sha1_update(ctx: *Sha1Ctx, message: []const u8) !void {
|
||||||
return sha.generic_update(
|
return sha.generic_update(
|
||||||
Sha1Ctx,
|
Sha1Ctx,
|
||||||
SHA_1_MESSAGE_BITS_LIMIT,
|
MESSAGE_BITS_LIMIT,
|
||||||
&sha1_compress_block,
|
&sha1_compress_block,
|
||||||
ctx,
|
ctx,
|
||||||
message,
|
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(
|
return sha.generic_final(
|
||||||
Sha1Ctx,
|
Sha1Ctx,
|
||||||
Sha1Ctx.WordType,
|
Sha1Ctx.WordType,
|
||||||
u64,
|
u64,
|
||||||
SHA_1_DIGEST_LENGTH,
|
DIGEST_LENGTH,
|
||||||
sha1_compress_block,
|
sha1_compress_block,
|
||||||
ctx,
|
ctx,
|
||||||
out,
|
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`."),
|
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));
|
res |= @as(T, bytes[i]) << @intCast(8 * (@sizeOf(T) - i - 1));
|
||||||
return res;
|
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 std = @import("std");
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
|
||||||
|
const byte_operations = @import("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 ----------------------------------- //
|
// ----------------------------------- ERROR DEFINITIONS ----------------------------------- //
|
||||||
|
|
||||||
const KeyStreamDepleted = error.KeyStreamDepleted;
|
pub const KeyStreamDepleted = error.KeyStreamDepleted;
|
||||||
|
|
||||||
// ----------------------------------- ChaCha20 CONSTANTS ----------------------------------- //
|
// ----------------------------------- ChaCha20 CONSTANTS ----------------------------------- //
|
||||||
|
|
||||||
const CHACHA20_BLOCK_SIZE = 512 / 8;
|
pub const BLOCK_SIZE = 512 / 8;
|
||||||
const CHACHA20_BLOCK_WORDS = CHACHA20_BLOCK_SIZE / 4;
|
pub const BLOCK_WORDS = BLOCK_SIZE / 4;
|
||||||
|
|
||||||
const CHACHA20_KEY_SIZE = 256 / 8;
|
pub const KEY_SIZE = 256 / 8;
|
||||||
const CHACHA20_KEY_WORDS = CHACHA20_KEY_SIZE / 4;
|
pub const KEY_WORDS = KEY_SIZE / 4;
|
||||||
|
|
||||||
const CHACHA20_NONCE_SIZE = 128 / 8;
|
pub const NONCE_SIZE = 128 / 8;
|
||||||
const CHACHA20_NONCE_WORDS = CHACHA20_NONCE_SIZE / 4;
|
pub const NONCE_WORDS = NONCE_SIZE / 4;
|
||||||
|
|
||||||
const CHACHA20_CONSTANTS = [128 / 8 / 4]u32{
|
pub const Parameters_Bernstein = struct {
|
||||||
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 NONCE_SIZE = 64 / 8;
|
pub const NONCE_SIZE = 64 / 8;
|
||||||
pub const COUNTER_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 NONCE_SIZE = 96 / 8;
|
||||||
pub const COUNTER_SIZE = 32 / 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 ----------------------------------- //
|
// ----------------------------------- CONTEXT MANAGEMENT ----------------------------------- //
|
||||||
|
|
||||||
pub const ChaCha20Ctx = struct {
|
pub const ChaCha20Ctx = struct {
|
||||||
key: [CHACHA20_KEY_WORDS]u32,
|
key: [KEY_WORDS]u32,
|
||||||
nonce: [CHACHA20_NONCE_WORDS]u32,
|
nonce: [NONCE_WORDS]u32,
|
||||||
state: [CHACHA20_BLOCK_WORDS]u32,
|
state: [BLOCK_WORDS]u32,
|
||||||
working_state: [CHACHA20_BLOCK_WORDS]u32,
|
working_state: [BLOCK_WORDS]u32,
|
||||||
counter_idx_lsw: u8,
|
counter_idx_lsw: u8,
|
||||||
counter_idx_msw: u8,
|
counter_idx_msw: u8,
|
||||||
keystream_idx: u8,
|
keystream_idx: u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn chacha20_new(
|
pub fn chacha20_new(
|
||||||
key: *const [CHACHA20_KEY_SIZE]u8,
|
key: *const [KEY_SIZE]u8,
|
||||||
counter_size: comptime_int,
|
counter_size: comptime_int,
|
||||||
counter: *const [counter_size]u8,
|
counter: *const [counter_size]u8,
|
||||||
nonce_size: comptime_int,
|
nonce_size: comptime_int,
|
||||||
nonce: *const [nonce_size]u8,
|
nonce: *const [nonce_size]u8,
|
||||||
) ChaCha20Ctx {
|
) ChaCha20Ctx {
|
||||||
if (comptime counter_size + nonce_size != CHACHA20_NONCE_SIZE)
|
if (comptime counter_size + nonce_size != NONCE_SIZE)
|
||||||
@panic("Invalid ChaCha initialization: The lengths of the counter and nonce must add up to 16 bytes.");
|
@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 counter_words = comptime counter_size / 4;
|
||||||
const nonce_words = comptime nonce_size / 4;
|
const nonce_words = comptime nonce_size / 4;
|
||||||
@ -68,11 +72,11 @@ pub fn chacha20_new(
|
|||||||
.keystream_idx = undefined,
|
.keystream_idx = undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
chacha20_deserialize(CHACHA20_KEY_WORDS, key, &ctx.key);
|
deserialize(KEY_WORDS, key, &ctx.key);
|
||||||
chacha20_deserialize(counter_words, counter, ctx.nonce[0..counter_words]);
|
deserialize(counter_words, counter, ctx.nonce[0..counter_words]);
|
||||||
chacha20_deserialize(nonce_words, nonce, ctx.nonce[counter_words .. counter_words + nonce_words]);
|
deserialize(nonce_words, nonce, ctx.nonce[counter_words .. counter_words + nonce_words]);
|
||||||
|
|
||||||
chacha20_block_function(&ctx);
|
block_function(&ctx);
|
||||||
ctx.keystream_idx = 0;
|
ctx.keystream_idx = 0;
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
@ -87,38 +91,38 @@ pub fn chacha20_destroy(ctx: *ChaCha20Ctx) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn chacha20_bernstein_new(
|
pub fn chacha20_bernstein_new(
|
||||||
key: *const [CHACHA20_KEY_SIZE]u8,
|
key: *const [KEY_SIZE]u8,
|
||||||
nonce: *const [ChaCha20_Bernstein_Parameters.NONCE_SIZE]u8,
|
nonce: *const [Parameters_Bernstein.NONCE_SIZE]u8,
|
||||||
counter: *const [ChaCha20_Bernstein_Parameters.COUNTER_SIZE]u8,
|
counter: *const [Parameters_Bernstein.COUNTER_SIZE]u8,
|
||||||
) ChaCha20Ctx {
|
) ChaCha20Ctx {
|
||||||
return chacha20_new(
|
return chacha20_new(
|
||||||
key,
|
key,
|
||||||
ChaCha20_Bernstein_Parameters.COUNTER_SIZE,
|
Parameters_Bernstein.COUNTER_SIZE,
|
||||||
counter,
|
counter,
|
||||||
ChaCha20_Bernstein_Parameters.NONCE_SIZE,
|
Parameters_Bernstein.NONCE_SIZE,
|
||||||
nonce,
|
nonce,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chacha20_rfc7539_new(
|
pub fn chacha20_rfc7539_new(
|
||||||
key: *const [CHACHA20_KEY_SIZE]u8,
|
key: *const [KEY_SIZE]u8,
|
||||||
nonce: *const [ChaCha20_RFC7539_Parameters.NONCE_SIZE]u8,
|
nonce: *const [Parameters_RFC7539.NONCE_SIZE]u8,
|
||||||
counter: *const [ChaCha20_RFC7539_Parameters.COUNTER_SIZE]u8,
|
counter: *const [Parameters_RFC7539.COUNTER_SIZE]u8,
|
||||||
) ChaCha20Ctx {
|
) ChaCha20Ctx {
|
||||||
return chacha20_new(
|
return chacha20_new(
|
||||||
key,
|
key,
|
||||||
ChaCha20_RFC7539_Parameters.COUNTER_SIZE,
|
Parameters_RFC7539.COUNTER_SIZE,
|
||||||
counter,
|
counter,
|
||||||
ChaCha20_RFC7539_Parameters.NONCE_SIZE,
|
Parameters_RFC7539.NONCE_SIZE,
|
||||||
nonce,
|
nonce,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- ENCRYPTION/DECRYPTION ----------------------------------- //
|
// ----------------------------------- 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| {
|
for (0..plaintext.len) |i| {
|
||||||
try chacha20_ensure_keystream_expanded(ctx);
|
try ensure_keystream_expanded(ctx);
|
||||||
const keystream: [*]const u8 = @ptrCast(&ctx.state);
|
const keystream: [*]const u8 = @ptrCast(&ctx.state);
|
||||||
|
|
||||||
plaintext[i] ^= keystream[ctx.keystream_idx];
|
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 {
|
pub fn decrypt_inplace(ctx: *ChaCha20Ctx, ciphertext: []u8) !void {
|
||||||
return chacha20_encrypt_inplace(ctx, ciphertext);
|
return encrypt_inplace(ctx, ciphertext);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chacha20_ensure_keystream_expanded(ctx: *ChaCha20Ctx) !void {
|
pub fn ensure_keystream_expanded(ctx: *ChaCha20Ctx) !void {
|
||||||
if (ctx.keystream_idx == CHACHA20_BLOCK_SIZE) {
|
if (ctx.keystream_idx == BLOCK_SIZE) {
|
||||||
try chacha20_increment_counter(ctx);
|
try increment_counter(ctx);
|
||||||
chacha20_block_function(ctx);
|
block_function(ctx);
|
||||||
ctx.keystream_idx = 0;
|
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| {
|
for (ctx.counter_idx_lsw..ctx.counter_idx_msw + 1) |idx| {
|
||||||
const ov = @addWithOverflow(ctx.nonce[idx], 1);
|
const ov = @addWithOverflow(ctx.nonce[idx], 1);
|
||||||
ctx.nonce[idx] = ov[0];
|
ctx.nonce[idx] = ov[0];
|
||||||
@ -151,7 +155,7 @@ fn chacha20_increment_counter(ctx: *ChaCha20Ctx) !void {
|
|||||||
|
|
||||||
// ----------------------------------- KEYSTREAM EXPANSION ----------------------------------- //
|
// ----------------------------------- 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 a = state[ia];
|
||||||
var b = state[ib];
|
var b = state[ib];
|
||||||
var c = state[ic];
|
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;
|
state[id] = d;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chacha20_inner_block(state: *[CHACHA20_BLOCK_WORDS]u32) void {
|
pub fn double_round(state: *[BLOCK_WORDS]u32) void {
|
||||||
chacha20_quarter_round(state, 0, 4, 8, 12);
|
quarter_round(state, 0, 4, 8, 12);
|
||||||
chacha20_quarter_round(state, 1, 5, 9, 13);
|
quarter_round(state, 1, 5, 9, 13);
|
||||||
chacha20_quarter_round(state, 2, 6, 10, 14);
|
quarter_round(state, 2, 6, 10, 14);
|
||||||
chacha20_quarter_round(state, 3, 7, 11, 15);
|
quarter_round(state, 3, 7, 11, 15);
|
||||||
chacha20_quarter_round(state, 0, 5, 10, 15);
|
quarter_round(state, 0, 5, 10, 15);
|
||||||
chacha20_quarter_round(state, 1, 6, 11, 12);
|
quarter_round(state, 1, 6, 11, 12);
|
||||||
chacha20_quarter_round(state, 2, 7, 8, 13);
|
quarter_round(state, 2, 7, 8, 13);
|
||||||
chacha20_quarter_round(state, 3, 4, 9, 14);
|
quarter_round(state, 3, 4, 9, 14);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chacha20_block_function(ctx: *ChaCha20Ctx) void {
|
pub fn block_function(ctx: *ChaCha20Ctx) void {
|
||||||
// Reset state.
|
// Reset state.
|
||||||
{
|
{
|
||||||
comptime var i = 0;
|
comptime var i = 0;
|
||||||
|
|
||||||
@memcpy(ctx.state[i .. i + CHACHA20_CONSTANTS.len], &CHACHA20_CONSTANTS);
|
@memcpy(ctx.state[i .. i + CONSTANTS.len], &CONSTANTS);
|
||||||
i += CHACHA20_CONSTANTS.len;
|
i += CONSTANTS.len;
|
||||||
|
|
||||||
@memcpy(ctx.state[i .. i + CHACHA20_KEY_WORDS], ctx.key[0..]);
|
@memcpy(ctx.state[i .. i + KEY_WORDS], ctx.key[0..]);
|
||||||
i += CHACHA20_KEY_WORDS;
|
i += KEY_WORDS;
|
||||||
|
|
||||||
@memcpy(ctx.state[i .. i + CHACHA20_NONCE_WORDS], ctx.nonce[0..]);
|
@memcpy(ctx.state[i .. i + NONCE_WORDS], ctx.nonce[0..]);
|
||||||
i += CHACHA20_NONCE_WORDS;
|
i += NONCE_WORDS;
|
||||||
|
|
||||||
if (comptime i != CHACHA20_BLOCK_WORDS)
|
if (comptime i != BLOCK_WORDS)
|
||||||
@panic("Invalid ChaCha20 parameters: |constants + key + nonce + counter| != block_size!");
|
@compileError("Invalid ChaCha20 parameters: |constants + key + nonce + counter| != block_size.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy state to working_state.
|
// Copy state to working_state.
|
||||||
@memcpy(&ctx.working_state, &ctx.state);
|
@memcpy(&ctx.working_state, &ctx.state);
|
||||||
|
|
||||||
// Perform all 20 rounds (10 column rounds and 10 diagonal rounds).
|
// Perform all 20 rounds (10 column rounds and 10 diagonal rounds).
|
||||||
for (0..10) |_| {
|
for (0..10) |_|
|
||||||
chacha20_inner_block(&ctx.working_state);
|
double_round(&ctx.working_state);
|
||||||
}
|
|
||||||
|
|
||||||
// Add the working_state to the 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];
|
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|
|
for (0..L) |i|
|
||||||
std.mem.writeInt(u32, @ptrCast(bytes[(i * 4)..(i * 4 + 4)]), words[i], .little);
|
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|
|
for (0..L) |i|
|
||||||
words[i] = std.mem.readInt(u32, @ptrCast(bytes[(i * 4)..(i * 4 + 4)]), .little);
|
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 {
|
fn rol(word: u32, bits: comptime_int) u32 {
|
||||||
return word << bits | word >> (32 - bits);
|
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,58 +1,62 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
|
||||||
|
const byte_operations = @import("utility").byte_operations;
|
||||||
|
const bytes_to_word = byte_operations.bytes_to_word_le;
|
||||||
|
const word_to_bytes = byte_operations.word_to_bytes_le;
|
||||||
|
|
||||||
// ----------------------------------- ERROR DEFINITIONS ----------------------------------- //
|
// ----------------------------------- ERROR DEFINITIONS ----------------------------------- //
|
||||||
|
|
||||||
const KeyStreamDepleted = error.KeyStreamDepleted;
|
pub const KeyStreamDepleted = error.KeyStreamDepleted;
|
||||||
|
|
||||||
// ----------------------------------- ChaCha20 CONSTANTS ----------------------------------- //
|
// ----------------------------------- ChaCha20 CONSTANTS ----------------------------------- //
|
||||||
|
|
||||||
const SALSA20_BLOCK_SIZE = 512 / 8;
|
pub const BLOCK_SIZE = 512 / 8;
|
||||||
const SALSA20_BLOCK_WORDS = SALSA20_BLOCK_SIZE / 4;
|
pub const BLOCK_WORDS = BLOCK_SIZE / 4;
|
||||||
|
|
||||||
const SALSA20_KEY_SIZE = 256 / 8;
|
pub const KEY_SIZE = 256 / 8;
|
||||||
const SALSA20_KEY_WORDS = SALSA20_KEY_SIZE / 4;
|
pub const KEY_WORDS = KEY_SIZE / 4;
|
||||||
const SALSA20_128_KEY_SIZE = 128 / 8;
|
pub const KEY_SIZE_128 = 128 / 8;
|
||||||
const SALSA20_128_KEY_WORDS = SALSA20_128_KEY_SIZE / 4;
|
pub const KEY_WORDS_128 = KEY_SIZE_128 / 4;
|
||||||
|
|
||||||
const SALSA20_NONCE_SIZE = 64 / 8;
|
pub const NONCE_SIZE = 64 / 8;
|
||||||
const SALSA20_NONCE_WORDS = SALSA20_NONCE_SIZE / 4;
|
pub const NONCE_WORDS = NONCE_SIZE / 4;
|
||||||
|
|
||||||
const SALSA20_COUNTER_SIZE = 64 / 8;
|
pub const COUNTER_SIZE = 64 / 8;
|
||||||
const SALSA20_COUNTER_WORDS = SALSA20_COUNTER_SIZE / 4;
|
pub const COUNTER_WORDS = COUNTER_SIZE / 4;
|
||||||
|
|
||||||
const SALSA20_CONSTANT_WORDS = 128 / 8 / 4;
|
pub const CONSTANT_WORDS = 128 / 8 / 4;
|
||||||
|
|
||||||
const SALSA20_256_CONSTANTS = [SALSA20_CONSTANT_WORDS]u32{
|
pub const CONSTANTS_256 = [CONSTANT_WORDS]u32{
|
||||||
bytes_to_word_le("expa"),
|
bytes_to_word("expa"),
|
||||||
bytes_to_word_le("nd 3"),
|
bytes_to_word("nd 3"),
|
||||||
bytes_to_word_le("2-by"),
|
bytes_to_word("2-by"),
|
||||||
bytes_to_word_le("te k"),
|
bytes_to_word("te k"),
|
||||||
};
|
};
|
||||||
const SALSA20_128_CONSTANTS = [SALSA20_CONSTANT_WORDS]u32{
|
pub const CONSTANTS_128 = [CONSTANT_WORDS]u32{
|
||||||
bytes_to_word_le("expa"),
|
bytes_to_word("expa"),
|
||||||
bytes_to_word_le("nd 1"),
|
bytes_to_word("nd 1"),
|
||||||
bytes_to_word_le("6-by"),
|
bytes_to_word("6-by"),
|
||||||
bytes_to_word_le("te k"),
|
bytes_to_word("te k"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------- CONTEXT MANAGEMENT ----------------------------------- //
|
// ----------------------------------- CONTEXT MANAGEMENT ----------------------------------- //
|
||||||
|
|
||||||
pub const Salsa20Ctx = struct {
|
pub const Salsa20Ctx = struct {
|
||||||
key: [SALSA20_KEY_WORDS]u32,
|
key: [KEY_WORDS]u32,
|
||||||
nonce: [SALSA20_NONCE_WORDS]u32,
|
nonce: [NONCE_WORDS]u32,
|
||||||
counter: [SALSA20_COUNTER_WORDS]u32,
|
counter: [COUNTER_WORDS]u32,
|
||||||
constants: [SALSA20_CONSTANT_WORDS]u32,
|
constants: [CONSTANT_WORDS]u32,
|
||||||
state: [SALSA20_BLOCK_WORDS]u32,
|
state: [BLOCK_WORDS]u32,
|
||||||
working_state: [SALSA20_BLOCK_WORDS]u32,
|
working_state: [BLOCK_WORDS]u32,
|
||||||
keystream_idx: u8,
|
keystream_idx: u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn salsa20_new(
|
pub fn salsa20_new(
|
||||||
key: *const [SALSA20_KEY_SIZE]u8,
|
key: *const [KEY_SIZE]u8,
|
||||||
nonce: *const [SALSA20_NONCE_SIZE]u8,
|
nonce: *const [NONCE_SIZE]u8,
|
||||||
counter: *const [SALSA20_COUNTER_SIZE]u8,
|
counter: *const [COUNTER_SIZE]u8,
|
||||||
constants: *const [SALSA20_CONSTANT_WORDS]u32,
|
constants: *const [CONSTANT_WORDS]u32,
|
||||||
) Salsa20Ctx {
|
) Salsa20Ctx {
|
||||||
var ctx = Salsa20Ctx{
|
var ctx = Salsa20Ctx{
|
||||||
.key = undefined,
|
.key = undefined,
|
||||||
@ -64,12 +68,12 @@ pub fn salsa20_new(
|
|||||||
.keystream_idx = undefined,
|
.keystream_idx = undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
salsa20_deserialize(SALSA20_KEY_WORDS, key, &ctx.key);
|
deserialize(KEY_WORDS, key, &ctx.key);
|
||||||
salsa20_deserialize(SALSA20_COUNTER_WORDS, counter, &ctx.counter);
|
deserialize(COUNTER_WORDS, counter, &ctx.counter);
|
||||||
salsa20_deserialize(SALSA20_NONCE_WORDS, nonce, &ctx.nonce);
|
deserialize(NONCE_WORDS, nonce, &ctx.nonce);
|
||||||
@memcpy(&ctx.constants, constants);
|
@memcpy(&ctx.constants, constants);
|
||||||
|
|
||||||
salsa20_block_function(&ctx);
|
block_function(&ctx);
|
||||||
ctx.keystream_idx = 0;
|
ctx.keystream_idx = 0;
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
@ -85,9 +89,9 @@ pub fn salsa20_destroy(ctx: *Salsa20Ctx) void {
|
|||||||
|
|
||||||
// ----------------------------------- ENCRYPTION/DECRYPTION ----------------------------------- //
|
// ----------------------------------- ENCRYPTION/DECRYPTION ----------------------------------- //
|
||||||
|
|
||||||
pub fn salsa20_encrypt_inplace(ctx: *Salsa20Ctx, plaintext: []u8) !void {
|
pub fn encrypt_inplace(ctx: *Salsa20Ctx, plaintext: []u8) !void {
|
||||||
for (0..plaintext.len) |i| {
|
for (0..plaintext.len) |i| {
|
||||||
try salsa20_ensure_keystream_expanded(ctx);
|
try ensure_keystream_expanded(ctx);
|
||||||
const keystream: [*]const u8 = @ptrCast(&ctx.state);
|
const keystream: [*]const u8 = @ptrCast(&ctx.state);
|
||||||
|
|
||||||
plaintext[i] ^= keystream[ctx.keystream_idx];
|
plaintext[i] ^= keystream[ctx.keystream_idx];
|
||||||
@ -95,20 +99,20 @@ pub fn salsa20_encrypt_inplace(ctx: *Salsa20Ctx, plaintext: []u8) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn salsa20_decrypt_inplace(ctx: *Salsa20Ctx, ciphertext: []u8) !void {
|
pub fn decrypt_inplace(ctx: *Salsa20Ctx, ciphertext: []u8) !void {
|
||||||
return salsa20_encrypt_inplace(ctx, ciphertext);
|
return encrypt_inplace(ctx, ciphertext);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn salsa20_ensure_keystream_expanded(ctx: *Salsa20Ctx) !void {
|
pub fn ensure_keystream_expanded(ctx: *Salsa20Ctx) !void {
|
||||||
if (ctx.keystream_idx == SALSA20_BLOCK_SIZE) {
|
if (ctx.keystream_idx == BLOCK_SIZE) {
|
||||||
try salsa20_increment_counter(ctx);
|
try increment_counter(ctx);
|
||||||
salsa20_block_function(ctx);
|
block_function(ctx);
|
||||||
ctx.keystream_idx = 0;
|
ctx.keystream_idx = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn salsa20_increment_counter(ctx: *Salsa20Ctx) !void {
|
pub fn increment_counter(ctx: *Salsa20Ctx) !void {
|
||||||
for (0..SALSA20_COUNTER_WORDS) |idx| {
|
for (0..COUNTER_WORDS) |idx| {
|
||||||
const ov = @addWithOverflow(ctx.counter[idx], 1);
|
const ov = @addWithOverflow(ctx.counter[idx], 1);
|
||||||
ctx.counter[idx] = ov[0];
|
ctx.counter[idx] = ov[0];
|
||||||
|
|
||||||
@ -120,7 +124,7 @@ fn salsa20_increment_counter(ctx: *Salsa20Ctx) !void {
|
|||||||
|
|
||||||
// ----------------------------------- KEYSTREAM EXPANSION ----------------------------------- //
|
// ----------------------------------- KEYSTREAM EXPANSION ----------------------------------- //
|
||||||
|
|
||||||
pub fn salsa20_quarter_round(state: *[SALSA20_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 a = state[ia];
|
||||||
var b = state[ib];
|
var b = state[ib];
|
||||||
var c = state[ic];
|
var c = state[ic];
|
||||||
@ -137,41 +141,41 @@ pub fn salsa20_quarter_round(state: *[SALSA20_BLOCK_WORDS]u32, ia: u8, ib: u8, i
|
|||||||
state[id] = d;
|
state[id] = d;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn salsa20_column_round(state: *[SALSA20_BLOCK_WORDS]u32) void {
|
pub fn column_round(state: *[BLOCK_WORDS]u32) void {
|
||||||
salsa20_quarter_round(state, 0, 4, 8, 12);
|
quarter_round(state, 0, 4, 8, 12);
|
||||||
salsa20_quarter_round(state, 5, 9, 13, 1);
|
quarter_round(state, 5, 9, 13, 1);
|
||||||
salsa20_quarter_round(state, 10, 14, 2, 6);
|
quarter_round(state, 10, 14, 2, 6);
|
||||||
salsa20_quarter_round(state, 15, 3, 7, 11);
|
quarter_round(state, 15, 3, 7, 11);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn salsa20_row_round(state: *[SALSA20_BLOCK_WORDS]u32) void {
|
pub fn row_round(state: *[BLOCK_WORDS]u32) void {
|
||||||
salsa20_quarter_round(state, 0, 1, 2, 3);
|
quarter_round(state, 0, 1, 2, 3);
|
||||||
salsa20_quarter_round(state, 5, 6, 7, 4);
|
quarter_round(state, 5, 6, 7, 4);
|
||||||
salsa20_quarter_round(state, 10, 11, 8, 9);
|
quarter_round(state, 10, 11, 8, 9);
|
||||||
salsa20_quarter_round(state, 15, 12, 13, 14);
|
quarter_round(state, 15, 12, 13, 14);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn salsa20_double_round(state: *[SALSA20_BLOCK_WORDS]u32) void {
|
pub fn double_round(state: *[BLOCK_WORDS]u32) void {
|
||||||
salsa20_column_round(state);
|
column_round(state);
|
||||||
salsa20_row_round(state);
|
row_round(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn salsa20_hash_function(ctx: *Salsa20Ctx) void {
|
pub fn hash_function(ctx: *Salsa20Ctx) void {
|
||||||
// Copy state to working_state.
|
// Copy state to working_state.
|
||||||
@memcpy(&ctx.working_state, &ctx.state);
|
@memcpy(&ctx.working_state, &ctx.state);
|
||||||
|
|
||||||
// Perform all 20 rounds (10 row and 10 column rounds).
|
// Perform all 20 rounds (10 row and 10 column rounds).
|
||||||
for (0..10) |_| {
|
for (0..10) |_| {
|
||||||
salsa20_double_round(&ctx.working_state);
|
double_round(&ctx.working_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the working_state to the state.
|
// Add the working_state to the state.
|
||||||
for (0..SALSA20_BLOCK_WORDS) |i| {
|
for (0..BLOCK_WORDS) |i| {
|
||||||
ctx.state[i] +%= ctx.working_state[i];
|
ctx.state[i] +%= ctx.working_state[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn salsa20_block_function(ctx: *Salsa20Ctx) void {
|
pub fn block_function(ctx: *Salsa20Ctx) void {
|
||||||
// Reset state.
|
// Reset state.
|
||||||
{
|
{
|
||||||
ctx.state[0] = ctx.constants[0];
|
ctx.state[0] = ctx.constants[0];
|
||||||
@ -195,286 +199,21 @@ pub fn salsa20_block_function(ctx: *Salsa20Ctx) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Perform the hashing.
|
// Perform the hashing.
|
||||||
salsa20_hash_function(ctx);
|
hash_function(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- LITTLE ENDIAN HELPERS ----------------------------------- //
|
// ----------------------------------- HELPERS ----------------------------------- //
|
||||||
|
|
||||||
fn salsa20_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|
|
for (0..L) |i|
|
||||||
std.mem.writeInt(u32, @ptrCast(bytes[(i * 4)..(i * 4 + 4)]), words[i], .little);
|
std.mem.writeInt(u32, @ptrCast(bytes[(i * 4)..(i * 4 + 4)]), words[i], .little);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn salsa20_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|
|
for (0..L) |i|
|
||||||
words[i] = std.mem.readInt(u32, @ptrCast(bytes[(i * 4)..(i * 4 + 4)]), .little);
|
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 {
|
fn rol(word: u32, bits: comptime_int) u32 {
|
||||||
return word << bits | word >> (32 - bits);
|
return word << bits | word >> (32 - bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- TEST VECTORS ----------------------------------- //
|
|
||||||
|
|
||||||
fn test_quarter_round_function(y0: u32, y1: u32, y2: u32, y3: u32, z0: u32, z1: u32, z2: u32, z3: u32) !void {
|
|
||||||
var dummy_state: [SALSA20_BLOCK_WORDS]u32 = undefined;
|
|
||||||
dummy_state[0] = y0;
|
|
||||||
dummy_state[1] = y1;
|
|
||||||
dummy_state[2] = y2;
|
|
||||||
dummy_state[3] = y3;
|
|
||||||
salsa20_quarter_round(&dummy_state, 0, 1, 2, 3);
|
|
||||||
try testing.expectEqualSlices(u32, &.{ z0, z1, z2, z3 }, dummy_state[0..4]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://cr.yp.to/snuffle/spec.pdf
|
|
||||||
test "Salsa20 quarterround function" {
|
|
||||||
try test_quarter_round_function(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000);
|
|
||||||
try test_quarter_round_function(0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x08008145, 0x00000080, 0x00010200, 0x20500000);
|
|
||||||
try test_quarter_round_function(0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x88000100, 0x00000001, 0x00000200, 0x00402000);
|
|
||||||
try test_quarter_round_function(0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x80040000, 0x00000000, 0x00000001, 0x00002000);
|
|
||||||
try test_quarter_round_function(0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00048044, 0x00000080, 0x00010000, 0x20100001);
|
|
||||||
try test_quarter_round_function(0xe7e8c006, 0xc4f9417d, 0x6479b4b2, 0x68c67137, 0xe876d72b, 0x9361dfd5, 0xf1460244, 0x948541a3);
|
|
||||||
try test_quarter_round_function(0xd3917c5b, 0x55f1c407, 0x52a58a7a, 0x8f887a3b, 0x3e2f308c, 0xd90a8f36, 0x6ab2a923, 0x2883524c);
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://cr.yp.to/snuffle/spec.pdf
|
|
||||||
test "Salsa20 rowround function" {
|
|
||||||
var y1 = [_]u32{
|
|
||||||
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
|
||||||
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
|
||||||
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
|
||||||
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
|
||||||
};
|
|
||||||
const z1 = [_]u32{
|
|
||||||
0x08008145, 0x00000080, 0x00010200, 0x20500000,
|
|
||||||
0x20100001, 0x00048044, 0x00000080, 0x00010000,
|
|
||||||
0x00000001, 0x00002000, 0x80040000, 0x00000000,
|
|
||||||
0x00000001, 0x00000200, 0x00402000, 0x88000100,
|
|
||||||
};
|
|
||||||
salsa20_row_round(&y1);
|
|
||||||
try testing.expectEqualSlices(u32, z1[0..], y1[0..]);
|
|
||||||
|
|
||||||
var y2 = [_]u32{
|
|
||||||
0x08521bd6, 0x1fe88837, 0xbb2aa576, 0x3aa26365,
|
|
||||||
0xc54c6a5b, 0x2fc74c2f, 0x6dd39cc3, 0xda0a64f6,
|
|
||||||
0x90a2f23d, 0x067f95a6, 0x06b35f61, 0x41e4732e,
|
|
||||||
0xe859c100, 0xea4d84b7, 0x0f619bff, 0xbc6e965a,
|
|
||||||
};
|
|
||||||
const z2 = [_]u32{
|
|
||||||
0xa890d39d, 0x65d71596, 0xe9487daa, 0xc8ca6a86,
|
|
||||||
0x949d2192, 0x764b7754, 0xe408d9b9, 0x7a41b4d1,
|
|
||||||
0x3402e183, 0x3c3af432, 0x50669f96, 0xd89ef0a8,
|
|
||||||
0x0040ede5, 0xb545fbce, 0xd257ed4f, 0x1818882d,
|
|
||||||
};
|
|
||||||
salsa20_row_round(&y2);
|
|
||||||
try testing.expectEqualSlices(u32, z2[0..], y2[0..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://cr.yp.to/snuffle/spec.pdf
|
|
||||||
test "Salsa20 columnround function" {
|
|
||||||
var x1 = [_]u32{
|
|
||||||
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
|
||||||
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
|
||||||
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
|
||||||
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
|
||||||
};
|
|
||||||
const y1 = [_]u32{
|
|
||||||
0x10090288, 0x00000000, 0x00000000, 0x00000000,
|
|
||||||
0x00000101, 0x00000000, 0x00000000, 0x00000000,
|
|
||||||
0x00020401, 0x00000000, 0x00000000, 0x00000000,
|
|
||||||
0x40a04001, 0x00000000, 0x00000000, 0x00000000,
|
|
||||||
};
|
|
||||||
salsa20_column_round(&x1);
|
|
||||||
try testing.expectEqualSlices(u32, y1[0..], x1[0..]);
|
|
||||||
|
|
||||||
var x2 = [_]u32{
|
|
||||||
0x08521bd6, 0x1fe88837, 0xbb2aa576, 0x3aa26365,
|
|
||||||
0xc54c6a5b, 0x2fc74c2f, 0x6dd39cc3, 0xda0a64f6,
|
|
||||||
0x90a2f23d, 0x067f95a6, 0x06b35f61, 0x41e4732e,
|
|
||||||
0xe859c100, 0xea4d84b7, 0x0f619bff, 0xbc6e965a,
|
|
||||||
};
|
|
||||||
const y2 = [_]u32{
|
|
||||||
0x8c9d190a, 0xce8e4c90, 0x1ef8e9d3, 0x1326a71a,
|
|
||||||
0x90a20123, 0xead3c4f3, 0x63a091a0, 0xf0708d69,
|
|
||||||
0x789b010c, 0xd195a681, 0xeb7d5504, 0xa774135c,
|
|
||||||
0x481c2027, 0x53a8e4b5, 0x4c1f89c5, 0x3f78c9c8,
|
|
||||||
};
|
|
||||||
salsa20_column_round(&x2);
|
|
||||||
try testing.expectEqualSlices(u32, y2[0..], x2[0..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://cr.yp.to/snuffle/spec.pdf
|
|
||||||
test "Salsa20 doubleround function" {
|
|
||||||
var x1 = [_]u32{
|
|
||||||
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
|
||||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
||||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
||||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
||||||
};
|
|
||||||
const z1 = [_]u32{
|
|
||||||
0x8186a22d, 0x0040a284, 0x82479210, 0x06929051,
|
|
||||||
0x08000090, 0x02402200, 0x00004000, 0x00800000,
|
|
||||||
0x00010200, 0x20400000, 0x08008104, 0x00000000,
|
|
||||||
0x20500000, 0xa0000040, 0x0008180a, 0x612a8020,
|
|
||||||
};
|
|
||||||
salsa20_double_round(&x1);
|
|
||||||
try testing.expectEqualSlices(u32, z1[0..], x1[0..]);
|
|
||||||
|
|
||||||
var x2 = [_]u32{
|
|
||||||
0xde501066, 0x6f9eb8f7, 0xe4fbbd9b, 0x454e3f57,
|
|
||||||
0xb75540d3, 0x43e93a4c, 0x3a6f2aa0, 0x726d6b36,
|
|
||||||
0x9243f484, 0x9145d1e8, 0x4fa9d247, 0xdc8dee11,
|
|
||||||
0x054bf545, 0x254dd653, 0xd9421b6d, 0x67b276c1,
|
|
||||||
};
|
|
||||||
const z2 = [_]u32{
|
|
||||||
0xccaaf672, 0x23d960f7, 0x9153e63a, 0xcd9a60d0,
|
|
||||||
0x50440492, 0xf07cad19, 0xae344aa0, 0xdf4cfdfc,
|
|
||||||
0xca531c29, 0x8e7943db, 0xac1680cd, 0xd503ca00,
|
|
||||||
0xa74b2ad6, 0xbc331c5c, 0x1dda24c7, 0xee928277,
|
|
||||||
};
|
|
||||||
salsa20_double_round(&x2);
|
|
||||||
try testing.expectEqualSlices(u32, z2[0..], x2[0..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_hash_function(count: comptime_int, input: *const [SALSA20_BLOCK_SIZE]u8, reference: *const [SALSA20_BLOCK_SIZE]u8) !void {
|
|
||||||
var dummy_ctx = Salsa20Ctx{
|
|
||||||
.key = undefined,
|
|
||||||
.nonce = undefined,
|
|
||||||
.counter = undefined,
|
|
||||||
.constants = undefined,
|
|
||||||
.state = undefined,
|
|
||||||
.working_state = undefined,
|
|
||||||
.keystream_idx = undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
salsa20_deserialize(SALSA20_BLOCK_WORDS, input, dummy_ctx.state[0..]);
|
|
||||||
for (0..count) |_|
|
|
||||||
salsa20_hash_function(&dummy_ctx);
|
|
||||||
|
|
||||||
var serialized_result: [SALSA20_BLOCK_SIZE]u8 = undefined;
|
|
||||||
salsa20_serialize(SALSA20_BLOCK_WORDS, dummy_ctx.state[0..], serialized_result[0..]);
|
|
||||||
|
|
||||||
try testing.expectEqualSlices(u8, reference, serialized_result[0..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://cr.yp.to/snuffle/spec.pdf
|
|
||||||
test "Salsa20 hash function" {
|
|
||||||
try test_hash_function(1, &std.mem.zeroes([SALSA20_BLOCK_SIZE]u8), &std.mem.zeroes([SALSA20_BLOCK_SIZE]u8));
|
|
||||||
try test_hash_function(1, &.{
|
|
||||||
211, 159, 13, 115, 76, 55, 82, 183, 3, 117, 222, 37, 191, 187, 234, 136,
|
|
||||||
49, 237, 179, 48, 1, 106, 178, 219, 175, 199, 166, 48, 86, 16, 179, 207,
|
|
||||||
31, 240, 32, 63, 15, 83, 93, 161, 116, 147, 48, 113, 238, 55, 204, 36,
|
|
||||||
79, 201, 235, 79, 3, 81, 156, 47, 203, 26, 244, 243, 88, 118, 104, 54,
|
|
||||||
}, &.{
|
|
||||||
109, 42, 178, 168, 156, 240, 248, 238, 168, 196, 190, 203, 26, 110, 170, 154,
|
|
||||||
29, 29, 150, 26, 150, 30, 235, 249, 190, 163, 251, 48, 69, 144, 51, 57,
|
|
||||||
118, 40, 152, 157, 180, 57, 27, 94, 107, 42, 236, 35, 27, 111, 114, 114,
|
|
||||||
219, 236, 232, 135, 111, 155, 110, 18, 24, 232, 95, 158, 179, 19, 48, 202,
|
|
||||||
});
|
|
||||||
try test_hash_function(1, &.{
|
|
||||||
88, 118, 104, 54, 79, 201, 235, 79, 3, 81, 156, 47, 203, 26, 244, 243,
|
|
||||||
191, 187, 234, 136, 211, 159, 13, 115, 76, 55, 82, 183, 3, 117, 222, 37,
|
|
||||||
86, 16, 179, 207, 49, 237, 179, 48, 1, 106, 178, 219, 175, 199, 166, 48,
|
|
||||||
238, 55, 204, 36, 31, 240, 32, 63, 15, 83, 93, 161, 116, 147, 48, 113,
|
|
||||||
}, &.{
|
|
||||||
179, 19, 48, 202, 219, 236, 232, 135, 111, 155, 110, 18, 24, 232, 95, 158,
|
|
||||||
26, 110, 170, 154, 109, 42, 178, 168, 156, 240, 248, 238, 168, 196, 190, 203,
|
|
||||||
69, 144, 51, 57, 29, 29, 150, 26, 150, 30, 235, 249, 190, 163, 251, 48,
|
|
||||||
27, 111, 114, 114, 118, 40, 152, 157, 180, 57, 27, 94, 107, 42, 236, 35,
|
|
||||||
});
|
|
||||||
try test_hash_function(1000000, &.{
|
|
||||||
6, 124, 83, 146, 38, 191, 9, 50, 4, 161, 47, 222, 122, 182, 223, 185,
|
|
||||||
75, 27, 0, 216, 16, 122, 7, 89, 162, 104, 101, 147, 213, 21, 54, 95,
|
|
||||||
225, 253, 139, 176, 105, 132, 23, 116, 76, 41, 176, 207, 221, 34, 157, 108,
|
|
||||||
94, 94, 99, 52, 90, 117, 91, 220, 146, 190, 239, 143, 196, 176, 130, 186,
|
|
||||||
}, &.{
|
|
||||||
8, 18, 38, 199, 119, 76, 215, 67, 173, 127, 144, 162, 103, 212, 176, 217,
|
|
||||||
192, 19, 233, 33, 159, 197, 154, 160, 128, 243, 219, 65, 171, 136, 135, 225,
|
|
||||||
123, 11, 68, 86, 237, 82, 20, 155, 133, 189, 9, 83, 167, 116, 194, 78,
|
|
||||||
122, 127, 195, 185, 185, 204, 188, 90, 245, 9, 183, 248, 226, 85, 245, 104,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://cr.yp.to/snuffle/spec.pdf
|
|
||||||
test "Salsa20 expansion function" {
|
|
||||||
const key1 = [SALSA20_KEY_SIZE]u8{
|
|
||||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
|
||||||
201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216,
|
|
||||||
};
|
|
||||||
const nonce1 = [SALSA20_NONCE_SIZE]u8{ 101, 102, 103, 104, 105, 106, 107, 108 };
|
|
||||||
const pos1 = [SALSA20_NONCE_SIZE]u8{ 109, 110, 111, 112, 113, 114, 115, 116 };
|
|
||||||
const reference1 = [SALSA20_BLOCK_SIZE]u8{
|
|
||||||
69, 37, 68, 39, 41, 15, 107, 193, 255, 139, 122, 6, 170, 233, 217, 98,
|
|
||||||
89, 144, 182, 106, 21, 51, 200, 65, 239, 49, 222, 34, 215, 114, 40, 126,
|
|
||||||
104, 197, 7, 225, 197, 153, 31, 2, 102, 78, 76, 176, 84, 245, 246, 184,
|
|
||||||
177, 160, 133, 130, 6, 72, 149, 119, 192, 195, 132, 236, 234, 103, 246, 74,
|
|
||||||
};
|
|
||||||
|
|
||||||
var ctx1 = salsa20_new(&key1, &nonce1, &pos1, &SALSA20_256_CONSTANTS);
|
|
||||||
defer salsa20_destroy(&ctx1);
|
|
||||||
|
|
||||||
var keystream_buffer: [SALSA20_BLOCK_SIZE]u8 = undefined;
|
|
||||||
salsa20_serialize(SALSA20_BLOCK_WORDS, ctx1.state[0..], keystream_buffer[0..]);
|
|
||||||
|
|
||||||
try testing.expectEqualSlices(u8, reference1[0..], keystream_buffer[0..]);
|
|
||||||
|
|
||||||
const key2 = [SALSA20_KEY_SIZE]u8{
|
|
||||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
|
||||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
|
||||||
};
|
|
||||||
const reference2 = [SALSA20_BLOCK_SIZE]u8{
|
|
||||||
39, 173, 46, 248, 30, 200, 82, 17, 48, 67, 254, 239, 37, 18, 13, 247,
|
|
||||||
241, 200, 61, 144, 10, 55, 50, 185, 6, 47, 246, 253, 143, 86, 187, 225,
|
|
||||||
134, 85, 110, 246, 161, 163, 43, 235, 231, 94, 171, 51, 145, 214, 112, 29,
|
|
||||||
14, 232, 5, 16, 151, 140, 183, 141, 171, 9, 122, 181, 104, 182, 177, 193,
|
|
||||||
};
|
|
||||||
|
|
||||||
var ctx2 = salsa20_new(&key2, &nonce1, &pos1, &SALSA20_128_CONSTANTS);
|
|
||||||
defer salsa20_destroy(&ctx2);
|
|
||||||
|
|
||||||
salsa20_serialize(SALSA20_BLOCK_WORDS, ctx2.state[0..], keystream_buffer[0..]);
|
|
||||||
try testing.expectEqualSlices(u8, reference2[0..], keystream_buffer[0..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "Salsa20 encryption" {
|
|
||||||
const key = [SALSA20_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 = [SALSA20_NONCE_SIZE]u8{
|
|
||||||
0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
};
|
|
||||||
const counter = word_to_bytes_le(0) ++ word_to_bytes_le(0);
|
|
||||||
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{
|
|
||||||
0x13, 0xfc, 0x5e, 0x28, 0x51, 0xf0, 0x79, 0x9d, 0x97, 0x52, 0x1b, 0xa6, 0xa9, 0x37, 0x11, 0x87,
|
|
||||||
0x00, 0x95, 0x7c, 0x2e, 0xf9, 0x96, 0xe4, 0x2c, 0x59, 0x84, 0x9d, 0x45, 0x0c, 0x6e, 0xa2, 0x2e,
|
|
||||||
0x91, 0xcd, 0xcc, 0xea, 0x39, 0x1e, 0xe0, 0xa3, 0x57, 0xbb, 0x56, 0xcd, 0xf5, 0x2c, 0x56, 0xce,
|
|
||||||
0x01, 0x38, 0x07, 0xa9, 0x45, 0xb9, 0x17, 0xee, 0x6c, 0xcb, 0x18, 0xce, 0xca, 0xbe, 0x4b, 0xf4,
|
|
||||||
0x09, 0xba, 0x72, 0xfb, 0xdf, 0x90, 0xdb, 0x02, 0x8d, 0x22, 0x61, 0x7f, 0x1c, 0xa9, 0x84, 0x15,
|
|
||||||
0x6c, 0xa2, 0x72, 0x47, 0x3a, 0xf4, 0xf0, 0xe4, 0xcb, 0x3d, 0x85, 0x8b, 0x7a, 0xb4, 0x67, 0xae,
|
|
||||||
0x14, 0x71, 0x87, 0xab, 0xac, 0xb7, 0xc6, 0xe9, 0xaf, 0x6f, 0x2f, 0x47, 0x28, 0x7e, 0x2e, 0x0c,
|
|
||||||
0xb3, 0x18,
|
|
||||||
};
|
|
||||||
|
|
||||||
var ctx = salsa20_new(&key, &nonce, &counter, &SALSA20_256_CONSTANTS);
|
|
||||||
defer salsa20_destroy(&ctx);
|
|
||||||
|
|
||||||
var buffer: [plaintext.len]u8 = undefined;
|
|
||||||
@memcpy(&buffer, plaintext);
|
|
||||||
|
|
||||||
try salsa20_encrypt_inplace(&ctx, &buffer);
|
|
||||||
try testing.expectEqualSlices(u8, reference[0..], buffer[0..]);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
pub const primitive = @import("./primitive/index.zig");
|
pub const primitive = @import("primitive");
|
||||||
|
pub const utility = @import("utility");
|
||||||
|
|||||||
42
src/utility/byte_operations.zig
Normal file
42
src/utility/byte_operations.zig
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
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 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,9 +1,10 @@
|
|||||||
comptime {
|
comptime {
|
||||||
_ = .{
|
_ = .{
|
||||||
@import("./primitive/blockcipher/aes.zig"),
|
@import("./primitive/blockcipher/aes.zig"),
|
||||||
@import("./primitive/blockcipher/des.zig"),
|
//@import("./primitive/blockcipher/des.zig"),
|
||||||
@import("./primitive/digest/sha_1.zig"),
|
@import("./primitive/blockcipher/serpent.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/chacha20.zig"),
|
||||||
@import("./primitive/streamcipher/salsa20.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("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);
|
||||||
|
}
|
||||||
328
test/primitive/blockcipher/des.zig
Normal file
328
test/primitive/blockcipher/des.zig
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
const des = @import("primitive").blockcipher.des;
|
||||||
|
|
||||||
|
test "DES ECB (NBS SP 500-20)" {
|
||||||
|
const tests = [_]struct { key: u64, pt: u64, ct: u64 }{
|
||||||
|
// Initial Permutation and Expansion test
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x95F8A5E5DD31D900, .ct = 0x8000000000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xDD7F121CA5015619, .ct = 0x4000000000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x2E8653104F3834EA, .ct = 0x2000000000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x4BD388FF6CD81D4F, .ct = 0x1000000000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x20B9E767B2FB1456, .ct = 0x0800000000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x55579380D77138EF, .ct = 0x0400000000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x6CC5DEFAAF04512F, .ct = 0x0200000000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0D9F279BA5D87260, .ct = 0x0100000000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xD9031B0271BD5A0A, .ct = 0x0080000000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x424250B37C3DD951, .ct = 0x0040000000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xB8061B7ECD9A21E5, .ct = 0x0020000000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xF15D0F286B65BD28, .ct = 0x0010000000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xADD0CC8D6E5DEBA1, .ct = 0x0008000000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xE6D5F82752AD63D1, .ct = 0x0004000000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xECBFE3BD3F591A5E, .ct = 0x0002000000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xF356834379D165CD, .ct = 0x0001000000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x2B9F982F20037FA9, .ct = 0x0000800000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x889DE068A16F0BE6, .ct = 0x0000400000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xE19E275D846A1298, .ct = 0x0000200000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x329A8ED523D71AEC, .ct = 0x0000100000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xE7FCE22557D23C97, .ct = 0x0000080000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x12A9F5817FF2D65D, .ct = 0x0000040000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xA484C3AD38DC9C19, .ct = 0x0000020000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xFBE00A8A1EF8AD72, .ct = 0x0000010000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x750D079407521363, .ct = 0x0000008000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x64FEED9C724C2FAF, .ct = 0x0000004000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xF02B263B328E2B60, .ct = 0x0000002000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x9D64555A9A10B852, .ct = 0x0000001000000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xD106FF0BED5255D7, .ct = 0x0000000800000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xE1652C6B138C64A5, .ct = 0x0000000400000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xE428581186EC8F46, .ct = 0x0000000200000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xAEB5F5EDE22D1A36, .ct = 0x0000000100000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xE943D7568AEC0C5C, .ct = 0x0000000080000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xDF98C8276F54B04B, .ct = 0x0000000040000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xB160E4680F6C696F, .ct = 0x0000000020000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xFA0752B07D9C4AB8, .ct = 0x0000000010000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xCA3A2B036DBC8502, .ct = 0x0000000008000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x5E0905517BB59BCF, .ct = 0x0000000004000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x814EEB3B91D90726, .ct = 0x0000000002000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x4D49DB1532919C9F, .ct = 0x0000000001000000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x25EB5FC3F8CF0621, .ct = 0x0000000000800000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xAB6A20C0620D1C6F, .ct = 0x0000000000400000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x79E90DBC98F92CCA, .ct = 0x0000000000200000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x866ECEDD8072BB0E, .ct = 0x0000000000100000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x8B54536F2F3E64A8, .ct = 0x0000000000080000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xEA51D3975595B86B, .ct = 0x0000000000040000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xCAFFC6AC4542DE31, .ct = 0x0000000000020000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x8DD45A2DDF90796C, .ct = 0x0000000000010000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x1029D55E880EC2D0, .ct = 0x0000000000008000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x5D86CB23639DBEA9, .ct = 0x0000000000004000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x1D1CA853AE7C0C5F, .ct = 0x0000000000002000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xCE332329248F3228, .ct = 0x0000000000001000 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x8405D1ABE24FB942, .ct = 0x0000000000000800 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xE643D78090CA4207, .ct = 0x0000000000000400 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x48221B9937748A23, .ct = 0x0000000000000200 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xDD7C0BBD61FAFD54, .ct = 0x0000000000000100 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x2FBC291A570DB5C4, .ct = 0x0000000000000080 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xE07C30D7E4E26E12, .ct = 0x0000000000000040 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0953E2258E8E90A1, .ct = 0x0000000000000020 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x5B711BC4CEEBF2EE, .ct = 0x0000000000000010 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xCC083F1E6D9E85F6, .ct = 0x0000000000000008 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0xD2FD8867D50D2DFE, .ct = 0x0000000000000004 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x06E7EA22CE92708F, .ct = 0x0000000000000002 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x166B40B44ABA4BD6, .ct = 0x0000000000000001 },
|
||||||
|
|
||||||
|
// Inverse Permutation and Expansion test
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x8000000000000000, .ct = 0x95F8A5E5DD31D900 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x4000000000000000, .ct = 0xDD7F121CA5015619 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x2000000000000000, .ct = 0x2E8653104F3834EA },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x1000000000000000, .ct = 0x4BD388FF6CD81D4F },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0800000000000000, .ct = 0x20B9E767B2FB1456 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0400000000000000, .ct = 0x55579380D77138EF },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0200000000000000, .ct = 0x6CC5DEFAAF04512F },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0100000000000000, .ct = 0x0D9F279BA5D87260 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0080000000000000, .ct = 0xD9031B0271BD5A0A },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0040000000000000, .ct = 0x424250B37C3DD951 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0020000000000000, .ct = 0xB8061B7ECD9A21E5 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0010000000000000, .ct = 0xF15D0F286B65BD28 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0008000000000000, .ct = 0xADD0CC8D6E5DEBA1 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0004000000000000, .ct = 0xE6D5F82752AD63D1 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0002000000000000, .ct = 0xECBFE3BD3F591A5E },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0001000000000000, .ct = 0xF356834379D165CD },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000800000000000, .ct = 0x2B9F982F20037FA9 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000400000000000, .ct = 0x889DE068A16F0BE6 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000200000000000, .ct = 0xE19E275D846A1298 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000100000000000, .ct = 0x329A8ED523D71AEC },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000080000000000, .ct = 0xE7FCE22557D23C97 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000040000000000, .ct = 0x12A9F5817FF2D65D },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000020000000000, .ct = 0xA484C3AD38DC9C19 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000010000000000, .ct = 0xFBE00A8A1EF8AD72 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000008000000000, .ct = 0x750D079407521363 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000004000000000, .ct = 0x64FEED9C724C2FAF },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000002000000000, .ct = 0xF02B263B328E2B60 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000001000000000, .ct = 0x9D64555A9A10B852 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000800000000, .ct = 0xD106FF0BED5255D7 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000400000000, .ct = 0xE1652C6B138C64A5 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000200000000, .ct = 0xE428581186EC8F46 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000100000000, .ct = 0xAEB5F5EDE22D1A36 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000080000000, .ct = 0xE943D7568AEC0C5C },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000040000000, .ct = 0xDF98C8276F54B04B },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000020000000, .ct = 0xB160E4680F6C696F },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000010000000, .ct = 0xFA0752B07D9C4AB8 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000008000000, .ct = 0xCA3A2B036DBC8502 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000004000000, .ct = 0x5E0905517BB59BCF },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000002000000, .ct = 0x814EEB3B91D90726 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000001000000, .ct = 0x4D49DB1532919C9F },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000800000, .ct = 0x25EB5FC3F8CF0621 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000400000, .ct = 0xAB6A20C0620D1C6F },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000200000, .ct = 0x79E90DBC98F92CCA },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000100000, .ct = 0x866ECEDD8072BB0E },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000080000, .ct = 0x8B54536F2F3E64A8 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000040000, .ct = 0xEA51D3975595B86B },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000020000, .ct = 0xCAFFC6AC4542DE31 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000010000, .ct = 0x8DD45A2DDF90796C },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000008000, .ct = 0x1029D55E880EC2D0 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000004000, .ct = 0x5D86CB23639DBEA9 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000002000, .ct = 0x1D1CA853AE7C0C5F },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000001000, .ct = 0xCE332329248F3228 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000000800, .ct = 0x8405D1ABE24FB942 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000000400, .ct = 0xE643D78090CA4207 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000000200, .ct = 0x48221B9937748A23 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000000100, .ct = 0xDD7C0BBD61FAFD54 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000000080, .ct = 0x2FBC291A570DB5C4 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000000040, .ct = 0xE07C30D7E4E26E12 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000000020, .ct = 0x0953E2258E8E90A1 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000000010, .ct = 0x5B711BC4CEEBF2EE },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000000008, .ct = 0xCC083F1E6D9E85F6 },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000000004, .ct = 0xD2FD8867D50D2DFE },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000000002, .ct = 0x06E7EA22CE92708F },
|
||||||
|
.{ .key = 0x0101010101010101, .pt = 0x0000000000000001, .ct = 0x166B40B44ABA4BD6 },
|
||||||
|
|
||||||
|
// Key Permutation tests
|
||||||
|
.{ .key = 0x8001010101010101, .pt = 0x0000000000000000, .ct = 0x95A8D72813DAA94D },
|
||||||
|
.{ .key = 0x4001010101010101, .pt = 0x0000000000000000, .ct = 0x0EEC1487DD8C26D5 },
|
||||||
|
.{ .key = 0x2001010101010101, .pt = 0x0000000000000000, .ct = 0x7AD16FFB79C45926 },
|
||||||
|
.{ .key = 0x1001010101010101, .pt = 0x0000000000000000, .ct = 0xD3746294CA6A6CF3 },
|
||||||
|
.{ .key = 0x0801010101010101, .pt = 0x0000000000000000, .ct = 0x809F5F873C1FD761 },
|
||||||
|
.{ .key = 0x0401010101010101, .pt = 0x0000000000000000, .ct = 0xC02FAFFEC989D1FC },
|
||||||
|
.{ .key = 0x0201010101010101, .pt = 0x0000000000000000, .ct = 0x4615AA1D33E72F10 },
|
||||||
|
.{ .key = 0x0180010101010101, .pt = 0x0000000000000000, .ct = 0x2055123350C00858 },
|
||||||
|
.{ .key = 0x0140010101010101, .pt = 0x0000000000000000, .ct = 0xDF3B99D6577397C8 },
|
||||||
|
.{ .key = 0x0120010101010101, .pt = 0x0000000000000000, .ct = 0x31FE17369B5288C9 },
|
||||||
|
.{ .key = 0x0110010101010101, .pt = 0x0000000000000000, .ct = 0xDFDD3CC64DAE1642 },
|
||||||
|
.{ .key = 0x0108010101010101, .pt = 0x0000000000000000, .ct = 0x178C83CE2B399D94 },
|
||||||
|
.{ .key = 0x0104010101010101, .pt = 0x0000000000000000, .ct = 0x50F636324A9B7F80 },
|
||||||
|
.{ .key = 0x0102010101010101, .pt = 0x0000000000000000, .ct = 0xA8468EE3BC18F06D },
|
||||||
|
.{ .key = 0x0101800101010101, .pt = 0x0000000000000000, .ct = 0xA2DC9E92FD3CDE92 },
|
||||||
|
.{ .key = 0x0101400101010101, .pt = 0x0000000000000000, .ct = 0xCAC09F797D031287 },
|
||||||
|
.{ .key = 0x0101200101010101, .pt = 0x0000000000000000, .ct = 0x90BA680B22AEB525 },
|
||||||
|
.{ .key = 0x0101100101010101, .pt = 0x0000000000000000, .ct = 0xCE7A24F350E280B6 },
|
||||||
|
.{ .key = 0x0101080101010101, .pt = 0x0000000000000000, .ct = 0x882BFF0AA01A0B87 },
|
||||||
|
.{ .key = 0x0101040101010101, .pt = 0x0000000000000000, .ct = 0x25610288924511C2 },
|
||||||
|
.{ .key = 0x0101020101010101, .pt = 0x0000000000000000, .ct = 0xC71516C29C75D170 },
|
||||||
|
.{ .key = 0x0101018001010101, .pt = 0x0000000000000000, .ct = 0x5199C29A52C9F059 },
|
||||||
|
.{ .key = 0x0101014001010101, .pt = 0x0000000000000000, .ct = 0xC22F0A294A71F29F },
|
||||||
|
.{ .key = 0x0101012001010101, .pt = 0x0000000000000000, .ct = 0xEE371483714C02EA },
|
||||||
|
.{ .key = 0x0101011001010101, .pt = 0x0000000000000000, .ct = 0xA81FBD448F9E522F },
|
||||||
|
.{ .key = 0x0101010801010101, .pt = 0x0000000000000000, .ct = 0x4F644C92E192DFED },
|
||||||
|
.{ .key = 0x0101010401010101, .pt = 0x0000000000000000, .ct = 0x1AFA9A66A6DF92AE },
|
||||||
|
.{ .key = 0x0101010201010101, .pt = 0x0000000000000000, .ct = 0xB3C1CC715CB879D8 },
|
||||||
|
.{ .key = 0x0101010180010101, .pt = 0x0000000000000000, .ct = 0x19D032E64AB0BD8B },
|
||||||
|
.{ .key = 0x0101010140010101, .pt = 0x0000000000000000, .ct = 0x3CFAA7A7DC8720DC },
|
||||||
|
.{ .key = 0x0101010120010101, .pt = 0x0000000000000000, .ct = 0xB7265F7F447AC6F3 },
|
||||||
|
.{ .key = 0x0101010110010101, .pt = 0x0000000000000000, .ct = 0x9DB73B3C0D163F54 },
|
||||||
|
.{ .key = 0x0101010108010101, .pt = 0x0000000000000000, .ct = 0x8181B65BABF4A975 },
|
||||||
|
.{ .key = 0x0101010104010101, .pt = 0x0000000000000000, .ct = 0x93C9B64042EAA240 },
|
||||||
|
.{ .key = 0x0101010102010101, .pt = 0x0000000000000000, .ct = 0x5570530829705592 },
|
||||||
|
.{ .key = 0x0101010101800101, .pt = 0x0000000000000000, .ct = 0x8638809E878787A0 },
|
||||||
|
.{ .key = 0x0101010101400101, .pt = 0x0000000000000000, .ct = 0x41B9A79AF79AC208 },
|
||||||
|
.{ .key = 0x0101010101200101, .pt = 0x0000000000000000, .ct = 0x7A9BE42F2009A892 },
|
||||||
|
.{ .key = 0x0101010101100101, .pt = 0x0000000000000000, .ct = 0x29038D56BA6D2745 },
|
||||||
|
.{ .key = 0x0101010101080101, .pt = 0x0000000000000000, .ct = 0x5495C6ABF1E5DF51 },
|
||||||
|
.{ .key = 0x0101010101040101, .pt = 0x0000000000000000, .ct = 0xAE13DBD561488933 },
|
||||||
|
.{ .key = 0x0101010101020101, .pt = 0x0000000000000000, .ct = 0x024D1FFA8904E389 },
|
||||||
|
.{ .key = 0x0101010101018001, .pt = 0x0000000000000000, .ct = 0xD1399712F99BF02E },
|
||||||
|
.{ .key = 0x0101010101014001, .pt = 0x0000000000000000, .ct = 0x14C1D7C1CFFEC79E },
|
||||||
|
.{ .key = 0x0101010101012001, .pt = 0x0000000000000000, .ct = 0x1DE5279DAE3BED6F },
|
||||||
|
.{ .key = 0x0101010101011001, .pt = 0x0000000000000000, .ct = 0xE941A33F85501303 },
|
||||||
|
.{ .key = 0x0101010101010801, .pt = 0x0000000000000000, .ct = 0xDA99DBBC9A03F379 },
|
||||||
|
.{ .key = 0x0101010101010401, .pt = 0x0000000000000000, .ct = 0xB7FC92F91D8E92E9 },
|
||||||
|
.{ .key = 0x0101010101010201, .pt = 0x0000000000000000, .ct = 0xAE8E5CAA3CA04E85 },
|
||||||
|
.{ .key = 0x0101010101010180, .pt = 0x0000000000000000, .ct = 0x9CC62DF43B6EED74 },
|
||||||
|
.{ .key = 0x0101010101010140, .pt = 0x0000000000000000, .ct = 0xD863DBB5C59A91A0 },
|
||||||
|
.{ .key = 0x0101010101010120, .pt = 0x0000000000000000, .ct = 0xA1AB2190545B91D7 },
|
||||||
|
.{ .key = 0x0101010101010110, .pt = 0x0000000000000000, .ct = 0x0875041E64C570F7 },
|
||||||
|
.{ .key = 0x0101010101010108, .pt = 0x0000000000000000, .ct = 0x5A594528BEBEF1CC },
|
||||||
|
.{ .key = 0x0101010101010104, .pt = 0x0000000000000000, .ct = 0xFCDB3291DE21F0C0 },
|
||||||
|
.{ .key = 0x0101010101010102, .pt = 0x0000000000000000, .ct = 0x869EFD7F9F265A09 },
|
||||||
|
|
||||||
|
// Test of right-shifts in Decryption
|
||||||
|
.{ .key = 0x8001010101010101, .pt = 0x95A8D72813DAA94D, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x4001010101010101, .pt = 0x0EEC1487DD8C26D5, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x2001010101010101, .pt = 0x7AD16FFB79C45926, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x1001010101010101, .pt = 0xD3746294CA6A6CF3, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0801010101010101, .pt = 0x809F5F873C1FD761, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0401010101010101, .pt = 0xC02FAFFEC989D1FC, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0201010101010101, .pt = 0x4615AA1D33E72F10, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0180010101010101, .pt = 0x2055123350C00858, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0140010101010101, .pt = 0xDF3B99D6577397C8, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0120010101010101, .pt = 0x31FE17369B5288C9, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0110010101010101, .pt = 0xDFDD3CC64DAE1642, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0108010101010101, .pt = 0x178C83CE2B399D94, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0104010101010101, .pt = 0x50F636324A9B7F80, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0102010101010101, .pt = 0xA8468EE3BC18F06D, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101800101010101, .pt = 0xA2DC9E92FD3CDE92, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101400101010101, .pt = 0xCAC09F797D031287, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101200101010101, .pt = 0x90BA680B22AEB525, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101100101010101, .pt = 0xCE7A24F350E280B6, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101080101010101, .pt = 0x882BFF0AA01A0B87, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101040101010101, .pt = 0x25610288924511C2, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101020101010101, .pt = 0xC71516C29C75D170, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101018001010101, .pt = 0x5199C29A52C9F059, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101014001010101, .pt = 0xC22F0A294A71F29F, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101012001010101, .pt = 0xEE371483714C02EA, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101011001010101, .pt = 0xA81FBD448F9E522F, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010801010101, .pt = 0x4F644C92E192DFED, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010401010101, .pt = 0x1AFA9A66A6DF92AE, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010201010101, .pt = 0xB3C1CC715CB879D8, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010180010101, .pt = 0x19D032E64AB0BD8B, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010140010101, .pt = 0x3CFAA7A7DC8720DC, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010120010101, .pt = 0xB7265F7F447AC6F3, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010110010101, .pt = 0x9DB73B3C0D163F54, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010108010101, .pt = 0x8181B65BABF4A975, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010104010101, .pt = 0x93C9B64042EAA240, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010102010101, .pt = 0x5570530829705592, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101800101, .pt = 0x8638809E878787A0, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101400101, .pt = 0x41B9A79AF79AC208, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101200101, .pt = 0x7A9BE42F2009A892, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101100101, .pt = 0x29038D56BA6D2745, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101080101, .pt = 0x5495C6ABF1E5DF51, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101040101, .pt = 0xAE13DBD561488933, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101020101, .pt = 0x024D1FFA8904E389, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101018001, .pt = 0xD1399712F99BF02E, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101014001, .pt = 0x14C1D7C1CFFEC79E, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101012001, .pt = 0x1DE5279DAE3BED6F, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101011001, .pt = 0xE941A33F85501303, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101010801, .pt = 0xDA99DBBC9A03F379, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101010401, .pt = 0xB7FC92F91D8E92E9, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101010201, .pt = 0xAE8E5CAA3CA04E85, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101010180, .pt = 0x9CC62DF43B6EED74, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101010140, .pt = 0xD863DBB5C59A91A0, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101010120, .pt = 0xA1AB2190545B91D7, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101010110, .pt = 0x0875041E64C570F7, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101010108, .pt = 0x5A594528BEBEF1CC, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101010104, .pt = 0xFCDB3291DE21F0C0, .ct = 0x0000000000000000 },
|
||||||
|
.{ .key = 0x0101010101010102, .pt = 0x869EFD7F9F265A09, .ct = 0x0000000000000000 },
|
||||||
|
|
||||||
|
// Data permutation test
|
||||||
|
.{ .key = 0x1046913489980131, .pt = 0x0000000000000000, .ct = 0x88D55E54F54C97B4 },
|
||||||
|
.{ .key = 0x1007103489988020, .pt = 0x0000000000000000, .ct = 0x0C0CC00C83EA48FD },
|
||||||
|
.{ .key = 0x10071034C8980120, .pt = 0x0000000000000000, .ct = 0x83BC8EF3A6570183 },
|
||||||
|
.{ .key = 0x1046103489988020, .pt = 0x0000000000000000, .ct = 0xDF725DCAD94EA2E9 },
|
||||||
|
.{ .key = 0x1086911519190101, .pt = 0x0000000000000000, .ct = 0xE652B53B550BE8B0 },
|
||||||
|
.{ .key = 0x1086911519580101, .pt = 0x0000000000000000, .ct = 0xAF527120C485CBB0 },
|
||||||
|
.{ .key = 0x5107B01519580101, .pt = 0x0000000000000000, .ct = 0x0F04CE393DB926D5 },
|
||||||
|
.{ .key = 0x1007B01519190101, .pt = 0x0000000000000000, .ct = 0xC9F00FFC74079067 },
|
||||||
|
.{ .key = 0x3107915498080101, .pt = 0x0000000000000000, .ct = 0x7CFD82A593252B4E },
|
||||||
|
.{ .key = 0x3107919498080101, .pt = 0x0000000000000000, .ct = 0xCB49A2F9E91363E3 },
|
||||||
|
.{ .key = 0x10079115B9080140, .pt = 0x0000000000000000, .ct = 0x00B588BE70D23F56 },
|
||||||
|
.{ .key = 0x3107911598080140, .pt = 0x0000000000000000, .ct = 0x406A9A6AB43399AE },
|
||||||
|
.{ .key = 0x1007D01589980101, .pt = 0x0000000000000000, .ct = 0x6CB773611DCA9ADA },
|
||||||
|
.{ .key = 0x9107911589980101, .pt = 0x0000000000000000, .ct = 0x67FD21C17DBB5D70 },
|
||||||
|
.{ .key = 0x9107D01589190101, .pt = 0x0000000000000000, .ct = 0x9592CB4110430787 },
|
||||||
|
.{ .key = 0x1007D01598980120, .pt = 0x0000000000000000, .ct = 0xA6B7FF68A318DDD3 },
|
||||||
|
.{ .key = 0x1007940498190101, .pt = 0x0000000000000000, .ct = 0x4D102196C914CA16 },
|
||||||
|
.{ .key = 0x0107910491190401, .pt = 0x0000000000000000, .ct = 0x2DFA9F4573594965 },
|
||||||
|
.{ .key = 0x0107910491190101, .pt = 0x0000000000000000, .ct = 0xB46604816C0E0774 },
|
||||||
|
.{ .key = 0x0107940491190401, .pt = 0x0000000000000000, .ct = 0x6E7E6221A4F34E87 },
|
||||||
|
.{ .key = 0x19079210981A0101, .pt = 0x0000000000000000, .ct = 0xAA85E74643233199 },
|
||||||
|
.{ .key = 0x1007911998190801, .pt = 0x0000000000000000, .ct = 0x2E5A19DB4D1962D6 },
|
||||||
|
.{ .key = 0x10079119981A0801, .pt = 0x0000000000000000, .ct = 0x23A866A809D30894 },
|
||||||
|
.{ .key = 0x1007921098190101, .pt = 0x0000000000000000, .ct = 0xD812D961F017D320 },
|
||||||
|
.{ .key = 0x100791159819010B, .pt = 0x0000000000000000, .ct = 0x055605816E58608F },
|
||||||
|
.{ .key = 0x1004801598190101, .pt = 0x0000000000000000, .ct = 0xABD88E8B1B7716F1 },
|
||||||
|
.{ .key = 0x1004801598190102, .pt = 0x0000000000000000, .ct = 0x537AC95BE69DA1E1 },
|
||||||
|
.{ .key = 0x1004801598190108, .pt = 0x0000000000000000, .ct = 0xAED0F6AE3C25CDD8 },
|
||||||
|
.{ .key = 0x1002911498100104, .pt = 0x0000000000000000, .ct = 0xB3E35A5EE53E7B8D },
|
||||||
|
.{ .key = 0x1002911598190104, .pt = 0x0000000000000000, .ct = 0x61C79C71921A2EF8 },
|
||||||
|
.{ .key = 0x1002911598100201, .pt = 0x0000000000000000, .ct = 0xE2F5728F0995013C },
|
||||||
|
.{ .key = 0x1002911698100101, .pt = 0x0000000000000000, .ct = 0x1AEAC39A61F0A464 },
|
||||||
|
|
||||||
|
// S-Box test
|
||||||
|
.{ .key = 0x7CA110454A1A6E57, .pt = 0x01A1D6D039776742, .ct = 0x690F5B0D9A26939B },
|
||||||
|
.{ .key = 0x0131D9619DC1376E, .pt = 0x5CD54CA83DEF57DA, .ct = 0x7A389D10354BD271 },
|
||||||
|
.{ .key = 0x07A1133E4A0B2686, .pt = 0x0248D43806F67172, .ct = 0x868EBB51CAB4599A },
|
||||||
|
.{ .key = 0x3849674C2602319E, .pt = 0x51454B582DDF440A, .ct = 0x7178876E01F19B2A },
|
||||||
|
.{ .key = 0x04B915BA43FEB5B6, .pt = 0x42FD443059577FA2, .ct = 0xAF37FB421F8C4095 },
|
||||||
|
.{ .key = 0x0113B970FD34F2CE, .pt = 0x059B5E0851CF143A, .ct = 0x86A560F10EC6D85B },
|
||||||
|
.{ .key = 0x0170F175468FB5E6, .pt = 0x0756D8E0774761D2, .ct = 0x0CD3DA020021DC09 },
|
||||||
|
.{ .key = 0x43297FAD38E373FE, .pt = 0x762514B829BF486A, .ct = 0xEA676B2CB7DB2B7A },
|
||||||
|
.{ .key = 0x07A7137045DA2A16, .pt = 0x3BDD119049372802, .ct = 0xDFD64A815CAF1A0F },
|
||||||
|
.{ .key = 0x04689104C2FD3B2F, .pt = 0x26955F6835AF609A, .ct = 0x5C513C9C4886C088 },
|
||||||
|
.{ .key = 0x37D06BB516CB7546, .pt = 0x164D5E404F275232, .ct = 0x0A2AEEAE3FF4AB77 },
|
||||||
|
.{ .key = 0x1F08260D1AC2465E, .pt = 0x6B056E18759F5CCA, .ct = 0xEF1BF03E5DFA575A },
|
||||||
|
.{ .key = 0x584023641ABA6176, .pt = 0x004BD6EF09176062, .ct = 0x88BF0DB6D70DEE56 },
|
||||||
|
.{ .key = 0x025816164629B007, .pt = 0x480D39006EE762F2, .ct = 0xA1F9915541020B56 },
|
||||||
|
.{ .key = 0x49793EBC79B3258F, .pt = 0x437540C8698F3CFA, .ct = 0x6FBF1CAFCFFD0556 },
|
||||||
|
.{ .key = 0x4FB05E1515AB73A7, .pt = 0x072D43A077075292, .ct = 0x2F22E49BAB7CA1AC },
|
||||||
|
.{ .key = 0x49E95D6D4CA229BF, .pt = 0x02FE55778117F12A, .ct = 0x5A6B612CC26CCE4A },
|
||||||
|
.{ .key = 0x018310DC409B26D6, .pt = 0x1D9D5C5018F728C2, .ct = 0x5F4C038ED12B2E41 },
|
||||||
|
.{ .key = 0x1C587F1C13924FEF, .pt = 0x305532286D6F295A, .ct = 0x63FAC0D034D9F793 },
|
||||||
|
};
|
||||||
|
|
||||||
|
var key: [des.DES_ENCODED_KEY_SIZE]u8 = undefined;
|
||||||
|
var pt: [des.DES_BLOCK_SIZE]u8 = undefined;
|
||||||
|
var ct: [des.DES_BLOCK_SIZE]u8 = undefined;
|
||||||
|
var buffer: [des.DES_BLOCK_SIZE]u8 = undefined;
|
||||||
|
|
||||||
|
for (tests) |t| {
|
||||||
|
std.mem.writeInt(u64, &key, t.key, .big);
|
||||||
|
std.mem.writeInt(u64, &pt, t.pt, .big);
|
||||||
|
std.mem.writeInt(u64, &ct, t.ct, .big);
|
||||||
|
|
||||||
|
// Test encryption.
|
||||||
|
des.des_encrypt_block(&key, &pt, &buffer);
|
||||||
|
try testing.expectEqualSlices(u8, &ct, &buffer);
|
||||||
|
|
||||||
|
// TODO: Test decryption.
|
||||||
|
}
|
||||||
|
}
|
||||||
140
test/primitive/blockcipher/operation_modes.zig
Normal file
140
test/primitive/blockcipher/operation_modes.zig
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
const blockcipher = @import("primitive").blockcipher;
|
||||||
|
const hex_to_bytes = @import("utility").byte_operations.hex_to_bytes;
|
||||||
|
|
||||||
|
// Use AES to test all of the operation modes.
|
||||||
|
const aes = blockcipher.aes;
|
||||||
|
|
||||||
|
// ----------------------------------- CBC MODE ----------------------------------- //
|
||||||
|
|
||||||
|
const cbc = 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------- GCM MODE ----------------------------------- //
|
||||||
|
|
||||||
|
const gcm = @import("primitive").blockcipher.operation_mode.gcm;
|
||||||
|
|
||||||
|
// https://csrc.nist.rip/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
|
||||||
|
test "AES-GCM Test Case 1" {
|
||||||
|
const K = hex_to_bytes(128 / 8, "00000000000000000000000000000000");
|
||||||
|
const P = hex_to_bytes(0, "");
|
||||||
|
const IV = hex_to_bytes(96 / 8, "000000000000000000000000");
|
||||||
|
|
||||||
|
const H = hex_to_bytes(128 / 8, "66e94bd4ef8a2c3b884cfa59ca342b2e");
|
||||||
|
const Y0 = hex_to_bytes(128 / 8, "00000000000000000000000000000001");
|
||||||
|
// E_K_Y0 = 58e2fccefa7e3061367f1d57a4e7455a
|
||||||
|
// len_A_len_C = 00000000000000000000000000000000
|
||||||
|
// GHASH_H_A_C = 00000000000000000000000000000000
|
||||||
|
const C = hex_to_bytes(0, "");
|
||||||
|
const T = hex_to_bytes(128 / 8, "58e2fccefa7e3061367f1d57a4e7455a");
|
||||||
|
|
||||||
|
var ctx = try gcm.gcm128_new(aes.KEY_SIZE_128, aes.aes128_encrypt_block, &K, &IV);
|
||||||
|
try testing.expectEqualSlices(u8, &H, &ctx.h);
|
||||||
|
try testing.expectEqualSlices(u8, &Y0, &ctx.counter);
|
||||||
|
|
||||||
|
var ciphertext_buffer: @TypeOf(P) = undefined;
|
||||||
|
try gcm.gcm128_encrypt(aes.KEY_SIZE_128, aes.aes128_encrypt_block, &ctx, &P, &ciphertext_buffer);
|
||||||
|
try testing.expectEqualSlices(u8, &C, &ciphertext_buffer);
|
||||||
|
|
||||||
|
var tag_buffer: @TypeOf(T) = undefined;
|
||||||
|
try gcm.gcm128_encrypt_final(aes.KEY_SIZE_128, aes.aes128_encrypt_block, &ctx, &tag_buffer);
|
||||||
|
try testing.expectEqualSlices(u8, &T, &tag_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "AES-GCM Test Case 2" {
|
||||||
|
const K = hex_to_bytes(128 / 8, "00000000000000000000000000000000");
|
||||||
|
const P = hex_to_bytes(128 / 8, "00000000000000000000000000000000");
|
||||||
|
const IV = hex_to_bytes(96 / 8, "000000000000000000000000");
|
||||||
|
const H = hex_to_bytes(128 / 8, "66e94bd4ef8a2c3b884cfa59ca342b2e");
|
||||||
|
const Y0 = hex_to_bytes(128 / 8, "00000000000000000000000000000001");
|
||||||
|
// E_K_Y0 = 58e2fccefa7e3061367f1d57a4e7455a
|
||||||
|
// Y1 = 00000000000000000000000000000002
|
||||||
|
// E_K_Y1 = 0388dace60b6a392f328c2b971b2fe78
|
||||||
|
// X1 = 5e2ec746917062882c85b0685353deb7
|
||||||
|
// len_A_len_C = 00000000000000000000000000000080
|
||||||
|
// GHASH_H_A_C = f38cbb1ad69223dcc3457ae5b6b0f885
|
||||||
|
const C = hex_to_bytes(128 / 8, "0388dace60b6a392f328c2b971b2fe78");
|
||||||
|
const T = hex_to_bytes(128 / 8, "ab6e47d42cec13bdf53a67b21257bddf");
|
||||||
|
|
||||||
|
var ctx = try gcm.gcm128_new(aes.KEY_SIZE_128, aes.aes128_encrypt_block, &K, &IV);
|
||||||
|
try testing.expectEqualSlices(u8, &H, &ctx.h);
|
||||||
|
try testing.expectEqualSlices(u8, &Y0, &ctx.counter);
|
||||||
|
|
||||||
|
var ciphertext_buffer: @TypeOf(P) = undefined;
|
||||||
|
try gcm.gcm128_encrypt(aes.KEY_SIZE_128, aes.aes128_encrypt_block, &ctx, &P, &ciphertext_buffer);
|
||||||
|
try testing.expectEqualSlices(u8, &C, &ciphertext_buffer);
|
||||||
|
|
||||||
|
var tag_buffer: @TypeOf(T) = undefined;
|
||||||
|
try gcm.gcm128_encrypt_final(aes.KEY_SIZE_128, aes.aes128_encrypt_block, &ctx, &tag_buffer);
|
||||||
|
try testing.expectEqualSlices(u8, &T, &tag_buffer);
|
||||||
|
}
|
||||||
170
test/primitive/blockcipher/serpent.zig
Normal file
170
test/primitive/blockcipher/serpent.zig
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
const serpent = @import("primitive").blockcipher.serpent;
|
||||||
|
const bytes_to_word = @import("utility").byte_operations.bytes_to_word_le;
|
||||||
|
|
||||||
|
test "Serpent linear transformation (bitslice mode)" {
|
||||||
|
var state = [_]u32{
|
||||||
|
bytes_to_word(&.{ 0x5C, 0x80, 0xE4, 0xA9 }),
|
||||||
|
bytes_to_word(&.{ 0x63, 0xC1, 0x2A, 0x74 }),
|
||||||
|
bytes_to_word(&.{ 0x9C, 0x62, 0x65, 0x4A }),
|
||||||
|
bytes_to_word(&.{ 0xF1, 0xCC, 0xED, 0xBA }),
|
||||||
|
};
|
||||||
|
|
||||||
|
serpent.linear_transformation(&state);
|
||||||
|
|
||||||
|
try testing.expectEqualSlices(u32, &.{
|
||||||
|
0x64fdbe66, 0x6e14817b, 0x35a514d4, 0xcd38f9b4,
|
||||||
|
}, &state);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Serpent-128 key expansion (bitslice mode)" {
|
||||||
|
const key = std.mem.zeroes([16]u8);
|
||||||
|
const subkeys = [_][4]u32{
|
||||||
|
.{ 0x6f579dd2, 0xa7a3a3ce, 0xf29990ed, 0x8ed77392 },
|
||||||
|
.{ 0x80125b0e, 0xec264d48, 0x6023c3d7, 0x41d3a19f },
|
||||||
|
.{ 0xf8c9ea64, 0x85e629c5, 0xedc06a3c, 0x5208c226 },
|
||||||
|
.{ 0x996edca3, 0x87c0e675, 0x28d9d59c, 0x0a69f259 },
|
||||||
|
.{ 0x3b9094e6, 0x0b918e16, 0xeb3f3d7a, 0x81a43b80 },
|
||||||
|
.{ 0xde2068fe, 0x3c0a592a, 0x6987f660, 0xb85c1049 },
|
||||||
|
.{ 0x57fe226c, 0x7e01d297, 0xfe13d038, 0x145fb65a },
|
||||||
|
.{ 0x4f5df0c5, 0x6648582b, 0xc385f12f, 0x984381b0 },
|
||||||
|
.{ 0x447bcf29, 0x68941f24, 0x523b25d2, 0x086144f0 },
|
||||||
|
.{ 0xf45443f8, 0x9fe43d24, 0x9cb5b319, 0x3eda4321 },
|
||||||
|
.{ 0xb4146dd5, 0x2cd8083e, 0x9ed2f57f, 0x6e55b8b8 },
|
||||||
|
.{ 0x94d3397a, 0xc6e90a6f, 0x9f35d452, 0x39146b55 },
|
||||||
|
.{ 0xfcf11184, 0x357ba27f, 0xd1e4e6d0, 0x2ebf4f5e },
|
||||||
|
.{ 0x2eaa8274, 0xde5a7089, 0xe2ff0356, 0x17482ffc },
|
||||||
|
.{ 0xeeb5bb44, 0xee77bde8, 0x7ecc739d, 0x357ba7ec },
|
||||||
|
.{ 0x755c9a06, 0x0dbd73d7, 0x5a16a42e, 0xbd4253f7 },
|
||||||
|
.{ 0xcae3885c, 0xa594cc7a, 0x6487de0e, 0x83cb2331 },
|
||||||
|
.{ 0x213c9fbb, 0x106c6af8, 0x0c9c9f4c, 0xd554b5cc },
|
||||||
|
.{ 0x656f9008, 0x85db98ce, 0x04ec632b, 0x83f0fd5e },
|
||||||
|
.{ 0x9c72345e, 0x0c547d37, 0x3c580e7b, 0x68c52253 },
|
||||||
|
.{ 0x5a8033f3, 0xf0f5c3ff, 0xa17aac1d, 0x48d929c1 },
|
||||||
|
.{ 0x7275b839, 0xbcd32557, 0x6242379f, 0x9f18aef2 },
|
||||||
|
.{ 0x3e641fbf, 0x2bf25532, 0xd80d8936, 0x7a38e7a8 },
|
||||||
|
.{ 0x75878446, 0x3b59afdd, 0x6f9a2417, 0xd374c9e8 },
|
||||||
|
.{ 0x92372bc2, 0x0fe107a2, 0x7ea908d8, 0xd2f3ec0f },
|
||||||
|
.{ 0x7afe62ef, 0x3d876138, 0x8e783eb4, 0x3313e04d },
|
||||||
|
.{ 0x7b554140, 0x3b1c350d, 0xa75c40ed, 0xdcab53af },
|
||||||
|
.{ 0xad845ca5, 0x24bbd3f9, 0x53bd7cb2, 0x5149d6c4 },
|
||||||
|
.{ 0x4e10ee30, 0xcf3be057, 0xfabc555c, 0x8b4276e9 },
|
||||||
|
.{ 0x6f4408ec, 0x1cd43a6d, 0xa021e8fd, 0x6169bcc0 },
|
||||||
|
.{ 0x4d0a99c5, 0xc9e96286, 0xfa234c4e, 0x92541cdb },
|
||||||
|
.{ 0x1f08f310, 0x7aaf29b2, 0xebf4e180, 0xc23175a7 },
|
||||||
|
.{ 0x5dba09c8, 0x0735e815, 0x86801046, 0x4af628f4 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const expanded_key = try serpent.expand_key(&key);
|
||||||
|
try testing.expectEqualDeep(subkeys, expanded_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://biham.cs.technion.ac.il/Reports/Serpent/Serpent-128-128.verified.test-vectors
|
||||||
|
|
||||||
|
test "NESSIE Serpent-128 test vector set 1" {
|
||||||
|
try test_encryption(
|
||||||
|
&fromhex(16, "80000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "00000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "264e5481eff42a4606abda06c0bfda3d"),
|
||||||
|
);
|
||||||
|
try test_encryption(
|
||||||
|
&fromhex(16, "40000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "00000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "4A231B3BC727993407AC6EC8350E8524"),
|
||||||
|
);
|
||||||
|
try test_encryption(
|
||||||
|
&fromhex(16, "20000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "00000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "E03269F9E9FD853C7D8156DF14B98D56"),
|
||||||
|
);
|
||||||
|
try test_encryption(
|
||||||
|
&fromhex(16, "10000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "00000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "A798181C3081AC59D5BA89754DACC48F"),
|
||||||
|
);
|
||||||
|
// TODO: ...
|
||||||
|
}
|
||||||
|
|
||||||
|
test "NESSIE Serpent-128 test vector set 2" {
|
||||||
|
try test_encryption(
|
||||||
|
&fromhex(16, "00000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "80000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "A3B35DE7C358DDD82644678C64B8BCBB"),
|
||||||
|
);
|
||||||
|
try test_encryption(
|
||||||
|
&fromhex(16, "00000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "40000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "04ABCFE4E0AF27FF92A2BB10949D7DD2"),
|
||||||
|
);
|
||||||
|
try test_encryption(
|
||||||
|
&fromhex(16, "00000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "20000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "8F773194B78EF2B2740237EF12D08608"),
|
||||||
|
);
|
||||||
|
try test_encryption(
|
||||||
|
&fromhex(16, "00000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "10000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "8B1EA69EE8D7C8D95B1DE4A670EC6997"),
|
||||||
|
);
|
||||||
|
// TODO: ...
|
||||||
|
}
|
||||||
|
|
||||||
|
test "NESSIE Serpent-128 test vector set 3" {
|
||||||
|
try test_encryption(
|
||||||
|
&fromhex(16, "00000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "00000000000000000000000000000000"),
|
||||||
|
&fromhex(16, "3620B17AE6A993D09618B8768266BAE9"),
|
||||||
|
);
|
||||||
|
try test_encryption(
|
||||||
|
&fromhex(16, "01010101010101010101010101010101"),
|
||||||
|
&fromhex(16, "01010101010101010101010101010101"),
|
||||||
|
&fromhex(16, "5107E36DBE81D9996D1EF7F3656FFC63"),
|
||||||
|
);
|
||||||
|
try test_encryption(
|
||||||
|
&fromhex(16, "02020202020202020202020202020202"),
|
||||||
|
&fromhex(16, "02020202020202020202020202020202"),
|
||||||
|
&fromhex(16, "1AE5355487F88F824B6462B45C4C6AA5"),
|
||||||
|
);
|
||||||
|
try test_encryption(
|
||||||
|
&fromhex(16, "03030303030303030303030303030303"),
|
||||||
|
&fromhex(16, "03030303030303030303030303030303"),
|
||||||
|
&fromhex(16, "1F830AF7D2A1B18F7A011C6FD0EEE8FB"),
|
||||||
|
);
|
||||||
|
// TODO: ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
|
||||||
|
fn test_encryption(key: *const [16]u8, plain: *const [16]u8, cipher: *const [16]u8) !void {
|
||||||
|
var output: [16]u8 = undefined;
|
||||||
|
|
||||||
|
const key_schedule = try serpent.expand_key(key);
|
||||||
|
serpent.encrypt_block(plain, &output, &key_schedule);
|
||||||
|
|
||||||
|
try testing.expectEqualSlices(u8, cipher, &output);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This monstrosity is only temporary...
|
||||||
|
fn fromhex(L: comptime_int, comptime s: *const [2 * L]u8) [L]u8 {
|
||||||
|
var result: [L]u8 = undefined;
|
||||||
|
inline for (0..L) |i| {
|
||||||
|
result[i] = (if (s[2 * i] >= '0' and s[2 * i] <= '9')
|
||||||
|
s[2 * i] - '0'
|
||||||
|
else if (s[2 * i] >= 'a' and s[2 * i] <= 'f')
|
||||||
|
s[2 * i] - 'a' + 10
|
||||||
|
else if (s[2 * i] >= 'A' and s[2 * i] <= 'F')
|
||||||
|
s[2 * i] - 'A' + 10
|
||||||
|
else
|
||||||
|
@compileError("Invalid hex string.")) * 16 + (if (s[2 * i + 1] >= '0' and s[2 * i + 1] <= '9')
|
||||||
|
s[2 * i + 1] - '0'
|
||||||
|
else if (s[2 * i + 1] >= 'a' and s[2 * i + 1] <= 'f')
|
||||||
|
s[2 * i + 1] - 'a' + 10
|
||||||
|
else if (s[2 * i + 1] >= 'A' and s[2 * i + 1] <= 'F')
|
||||||
|
s[2 * i + 1] - 'A' + 10
|
||||||
|
else
|
||||||
|
@compileError("Invalid hex string."));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
308
test/primitive/digest/sha.zig
Normal file
308
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("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("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));
|
||||||
|
}
|
||||||
257
test/primitive/streamcipher/salsa20.zig
Normal file
257
test/primitive/streamcipher/salsa20.zig
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
const salsa20 = @import("primitive").streamcipher.salsa20;
|
||||||
|
|
||||||
|
// https://cr.yp.to/snuffle/spec.pdf
|
||||||
|
test "Salsa20 quarterround function" {
|
||||||
|
try test_quarter_round_function(0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000);
|
||||||
|
try test_quarter_round_function(0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x08008145, 0x00000080, 0x00010200, 0x20500000);
|
||||||
|
try test_quarter_round_function(0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x88000100, 0x00000001, 0x00000200, 0x00402000);
|
||||||
|
try test_quarter_round_function(0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x80040000, 0x00000000, 0x00000001, 0x00002000);
|
||||||
|
try test_quarter_round_function(0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00048044, 0x00000080, 0x00010000, 0x20100001);
|
||||||
|
try test_quarter_round_function(0xe7e8c006, 0xc4f9417d, 0x6479b4b2, 0x68c67137, 0xe876d72b, 0x9361dfd5, 0xf1460244, 0x948541a3);
|
||||||
|
try test_quarter_round_function(0xd3917c5b, 0x55f1c407, 0x52a58a7a, 0x8f887a3b, 0x3e2f308c, 0xd90a8f36, 0x6ab2a923, 0x2883524c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://cr.yp.to/snuffle/spec.pdf
|
||||||
|
test "Salsa20 rowround function" {
|
||||||
|
var y1 = [_]u32{
|
||||||
|
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
};
|
||||||
|
const z1 = [_]u32{
|
||||||
|
0x08008145, 0x00000080, 0x00010200, 0x20500000,
|
||||||
|
0x20100001, 0x00048044, 0x00000080, 0x00010000,
|
||||||
|
0x00000001, 0x00002000, 0x80040000, 0x00000000,
|
||||||
|
0x00000001, 0x00000200, 0x00402000, 0x88000100,
|
||||||
|
};
|
||||||
|
salsa20.row_round(&y1);
|
||||||
|
try testing.expectEqualSlices(u32, z1[0..], y1[0..]);
|
||||||
|
|
||||||
|
var y2 = [_]u32{
|
||||||
|
0x08521bd6, 0x1fe88837, 0xbb2aa576, 0x3aa26365,
|
||||||
|
0xc54c6a5b, 0x2fc74c2f, 0x6dd39cc3, 0xda0a64f6,
|
||||||
|
0x90a2f23d, 0x067f95a6, 0x06b35f61, 0x41e4732e,
|
||||||
|
0xe859c100, 0xea4d84b7, 0x0f619bff, 0xbc6e965a,
|
||||||
|
};
|
||||||
|
const z2 = [_]u32{
|
||||||
|
0xa890d39d, 0x65d71596, 0xe9487daa, 0xc8ca6a86,
|
||||||
|
0x949d2192, 0x764b7754, 0xe408d9b9, 0x7a41b4d1,
|
||||||
|
0x3402e183, 0x3c3af432, 0x50669f96, 0xd89ef0a8,
|
||||||
|
0x0040ede5, 0xb545fbce, 0xd257ed4f, 0x1818882d,
|
||||||
|
};
|
||||||
|
salsa20.row_round(&y2);
|
||||||
|
try testing.expectEqualSlices(u32, z2[0..], y2[0..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://cr.yp.to/snuffle/spec.pdf
|
||||||
|
test "Salsa20 columnround function" {
|
||||||
|
var x1 = [_]u32{
|
||||||
|
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
};
|
||||||
|
const y1 = [_]u32{
|
||||||
|
0x10090288, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000101, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00020401, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x40a04001, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
};
|
||||||
|
salsa20.column_round(&x1);
|
||||||
|
try testing.expectEqualSlices(u32, y1[0..], x1[0..]);
|
||||||
|
|
||||||
|
var x2 = [_]u32{
|
||||||
|
0x08521bd6, 0x1fe88837, 0xbb2aa576, 0x3aa26365,
|
||||||
|
0xc54c6a5b, 0x2fc74c2f, 0x6dd39cc3, 0xda0a64f6,
|
||||||
|
0x90a2f23d, 0x067f95a6, 0x06b35f61, 0x41e4732e,
|
||||||
|
0xe859c100, 0xea4d84b7, 0x0f619bff, 0xbc6e965a,
|
||||||
|
};
|
||||||
|
const y2 = [_]u32{
|
||||||
|
0x8c9d190a, 0xce8e4c90, 0x1ef8e9d3, 0x1326a71a,
|
||||||
|
0x90a20123, 0xead3c4f3, 0x63a091a0, 0xf0708d69,
|
||||||
|
0x789b010c, 0xd195a681, 0xeb7d5504, 0xa774135c,
|
||||||
|
0x481c2027, 0x53a8e4b5, 0x4c1f89c5, 0x3f78c9c8,
|
||||||
|
};
|
||||||
|
salsa20.column_round(&x2);
|
||||||
|
try testing.expectEqualSlices(u32, y2[0..], x2[0..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://cr.yp.to/snuffle/spec.pdf
|
||||||
|
test "Salsa20 doubleround function" {
|
||||||
|
var x1 = [_]u32{
|
||||||
|
0x00000001, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||||
|
};
|
||||||
|
const z1 = [_]u32{
|
||||||
|
0x8186a22d, 0x0040a284, 0x82479210, 0x06929051,
|
||||||
|
0x08000090, 0x02402200, 0x00004000, 0x00800000,
|
||||||
|
0x00010200, 0x20400000, 0x08008104, 0x00000000,
|
||||||
|
0x20500000, 0xa0000040, 0x0008180a, 0x612a8020,
|
||||||
|
};
|
||||||
|
salsa20.double_round(&x1);
|
||||||
|
try testing.expectEqualSlices(u32, z1[0..], x1[0..]);
|
||||||
|
|
||||||
|
var x2 = [_]u32{
|
||||||
|
0xde501066, 0x6f9eb8f7, 0xe4fbbd9b, 0x454e3f57,
|
||||||
|
0xb75540d3, 0x43e93a4c, 0x3a6f2aa0, 0x726d6b36,
|
||||||
|
0x9243f484, 0x9145d1e8, 0x4fa9d247, 0xdc8dee11,
|
||||||
|
0x054bf545, 0x254dd653, 0xd9421b6d, 0x67b276c1,
|
||||||
|
};
|
||||||
|
const z2 = [_]u32{
|
||||||
|
0xccaaf672, 0x23d960f7, 0x9153e63a, 0xcd9a60d0,
|
||||||
|
0x50440492, 0xf07cad19, 0xae344aa0, 0xdf4cfdfc,
|
||||||
|
0xca531c29, 0x8e7943db, 0xac1680cd, 0xd503ca00,
|
||||||
|
0xa74b2ad6, 0xbc331c5c, 0x1dda24c7, 0xee928277,
|
||||||
|
};
|
||||||
|
salsa20.double_round(&x2);
|
||||||
|
try testing.expectEqualSlices(u32, z2[0..], x2[0..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://cr.yp.to/snuffle/spec.pdf
|
||||||
|
test "Salsa20 hash function" {
|
||||||
|
try test_hash_function(1, &std.mem.zeroes([salsa20.BLOCK_SIZE]u8), &std.mem.zeroes([salsa20.BLOCK_SIZE]u8));
|
||||||
|
try test_hash_function(1, &.{
|
||||||
|
211, 159, 13, 115, 76, 55, 82, 183, 3, 117, 222, 37, 191, 187, 234, 136,
|
||||||
|
49, 237, 179, 48, 1, 106, 178, 219, 175, 199, 166, 48, 86, 16, 179, 207,
|
||||||
|
31, 240, 32, 63, 15, 83, 93, 161, 116, 147, 48, 113, 238, 55, 204, 36,
|
||||||
|
79, 201, 235, 79, 3, 81, 156, 47, 203, 26, 244, 243, 88, 118, 104, 54,
|
||||||
|
}, &.{
|
||||||
|
109, 42, 178, 168, 156, 240, 248, 238, 168, 196, 190, 203, 26, 110, 170, 154,
|
||||||
|
29, 29, 150, 26, 150, 30, 235, 249, 190, 163, 251, 48, 69, 144, 51, 57,
|
||||||
|
118, 40, 152, 157, 180, 57, 27, 94, 107, 42, 236, 35, 27, 111, 114, 114,
|
||||||
|
219, 236, 232, 135, 111, 155, 110, 18, 24, 232, 95, 158, 179, 19, 48, 202,
|
||||||
|
});
|
||||||
|
try test_hash_function(1, &.{
|
||||||
|
88, 118, 104, 54, 79, 201, 235, 79, 3, 81, 156, 47, 203, 26, 244, 243,
|
||||||
|
191, 187, 234, 136, 211, 159, 13, 115, 76, 55, 82, 183, 3, 117, 222, 37,
|
||||||
|
86, 16, 179, 207, 49, 237, 179, 48, 1, 106, 178, 219, 175, 199, 166, 48,
|
||||||
|
238, 55, 204, 36, 31, 240, 32, 63, 15, 83, 93, 161, 116, 147, 48, 113,
|
||||||
|
}, &.{
|
||||||
|
179, 19, 48, 202, 219, 236, 232, 135, 111, 155, 110, 18, 24, 232, 95, 158,
|
||||||
|
26, 110, 170, 154, 109, 42, 178, 168, 156, 240, 248, 238, 168, 196, 190, 203,
|
||||||
|
69, 144, 51, 57, 29, 29, 150, 26, 150, 30, 235, 249, 190, 163, 251, 48,
|
||||||
|
27, 111, 114, 114, 118, 40, 152, 157, 180, 57, 27, 94, 107, 42, 236, 35,
|
||||||
|
});
|
||||||
|
try test_hash_function(1000000, &.{
|
||||||
|
6, 124, 83, 146, 38, 191, 9, 50, 4, 161, 47, 222, 122, 182, 223, 185,
|
||||||
|
75, 27, 0, 216, 16, 122, 7, 89, 162, 104, 101, 147, 213, 21, 54, 95,
|
||||||
|
225, 253, 139, 176, 105, 132, 23, 116, 76, 41, 176, 207, 221, 34, 157, 108,
|
||||||
|
94, 94, 99, 52, 90, 117, 91, 220, 146, 190, 239, 143, 196, 176, 130, 186,
|
||||||
|
}, &.{
|
||||||
|
8, 18, 38, 199, 119, 76, 215, 67, 173, 127, 144, 162, 103, 212, 176, 217,
|
||||||
|
192, 19, 233, 33, 159, 197, 154, 160, 128, 243, 219, 65, 171, 136, 135, 225,
|
||||||
|
123, 11, 68, 86, 237, 82, 20, 155, 133, 189, 9, 83, 167, 116, 194, 78,
|
||||||
|
122, 127, 195, 185, 185, 204, 188, 90, 245, 9, 183, 248, 226, 85, 245, 104,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://cr.yp.to/snuffle/spec.pdf
|
||||||
|
test "Salsa20 expansion function" {
|
||||||
|
const key1 = [salsa20.KEY_SIZE]u8{
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||||
|
201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216,
|
||||||
|
};
|
||||||
|
const nonce1 = [salsa20.NONCE_SIZE]u8{ 101, 102, 103, 104, 105, 106, 107, 108 };
|
||||||
|
const pos1 = [salsa20.NONCE_SIZE]u8{ 109, 110, 111, 112, 113, 114, 115, 116 };
|
||||||
|
const reference1 = [salsa20.BLOCK_SIZE]u8{
|
||||||
|
69, 37, 68, 39, 41, 15, 107, 193, 255, 139, 122, 6, 170, 233, 217, 98,
|
||||||
|
89, 144, 182, 106, 21, 51, 200, 65, 239, 49, 222, 34, 215, 114, 40, 126,
|
||||||
|
104, 197, 7, 225, 197, 153, 31, 2, 102, 78, 76, 176, 84, 245, 246, 184,
|
||||||
|
177, 160, 133, 130, 6, 72, 149, 119, 192, 195, 132, 236, 234, 103, 246, 74,
|
||||||
|
};
|
||||||
|
|
||||||
|
var ctx1 = salsa20.salsa20_new(&key1, &nonce1, &pos1, &salsa20.CONSTANTS_256);
|
||||||
|
defer salsa20.salsa20_destroy(&ctx1);
|
||||||
|
|
||||||
|
var keystream_buffer: [salsa20.BLOCK_SIZE]u8 = undefined;
|
||||||
|
salsa20.serialize(salsa20.BLOCK_WORDS, ctx1.state[0..], keystream_buffer[0..]);
|
||||||
|
|
||||||
|
try testing.expectEqualSlices(u8, reference1[0..], keystream_buffer[0..]);
|
||||||
|
|
||||||
|
const key2 = [salsa20.KEY_SIZE]u8{
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||||
|
};
|
||||||
|
const reference2 = [salsa20.BLOCK_SIZE]u8{
|
||||||
|
39, 173, 46, 248, 30, 200, 82, 17, 48, 67, 254, 239, 37, 18, 13, 247,
|
||||||
|
241, 200, 61, 144, 10, 55, 50, 185, 6, 47, 246, 253, 143, 86, 187, 225,
|
||||||
|
134, 85, 110, 246, 161, 163, 43, 235, 231, 94, 171, 51, 145, 214, 112, 29,
|
||||||
|
14, 232, 5, 16, 151, 140, 183, 141, 171, 9, 122, 181, 104, 182, 177, 193,
|
||||||
|
};
|
||||||
|
|
||||||
|
var ctx2 = salsa20.salsa20_new(&key2, &nonce1, &pos1, &salsa20.CONSTANTS_128);
|
||||||
|
defer salsa20.salsa20_destroy(&ctx2);
|
||||||
|
|
||||||
|
salsa20.serialize(salsa20.BLOCK_WORDS, ctx2.state[0..], keystream_buffer[0..]);
|
||||||
|
try testing.expectEqualSlices(u8, reference2[0..], keystream_buffer[0..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Salsa20 encryption" {
|
||||||
|
const key = [salsa20.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 = [salsa20.NONCE_SIZE]u8{
|
||||||
|
0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
};
|
||||||
|
const counter = std.mem.zeroes([salsa20.COUNTER_SIZE]u8);
|
||||||
|
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{
|
||||||
|
0x13, 0xfc, 0x5e, 0x28, 0x51, 0xf0, 0x79, 0x9d, 0x97, 0x52, 0x1b, 0xa6, 0xa9, 0x37, 0x11, 0x87,
|
||||||
|
0x00, 0x95, 0x7c, 0x2e, 0xf9, 0x96, 0xe4, 0x2c, 0x59, 0x84, 0x9d, 0x45, 0x0c, 0x6e, 0xa2, 0x2e,
|
||||||
|
0x91, 0xcd, 0xcc, 0xea, 0x39, 0x1e, 0xe0, 0xa3, 0x57, 0xbb, 0x56, 0xcd, 0xf5, 0x2c, 0x56, 0xce,
|
||||||
|
0x01, 0x38, 0x07, 0xa9, 0x45, 0xb9, 0x17, 0xee, 0x6c, 0xcb, 0x18, 0xce, 0xca, 0xbe, 0x4b, 0xf4,
|
||||||
|
0x09, 0xba, 0x72, 0xfb, 0xdf, 0x90, 0xdb, 0x02, 0x8d, 0x22, 0x61, 0x7f, 0x1c, 0xa9, 0x84, 0x15,
|
||||||
|
0x6c, 0xa2, 0x72, 0x47, 0x3a, 0xf4, 0xf0, 0xe4, 0xcb, 0x3d, 0x85, 0x8b, 0x7a, 0xb4, 0x67, 0xae,
|
||||||
|
0x14, 0x71, 0x87, 0xab, 0xac, 0xb7, 0xc6, 0xe9, 0xaf, 0x6f, 0x2f, 0x47, 0x28, 0x7e, 0x2e, 0x0c,
|
||||||
|
0xb3, 0x18,
|
||||||
|
};
|
||||||
|
|
||||||
|
var ctx = salsa20.salsa20_new(&key, &nonce, &counter, &salsa20.CONSTANTS_256);
|
||||||
|
defer salsa20.salsa20_destroy(&ctx);
|
||||||
|
|
||||||
|
var buffer: [plaintext.len]u8 = undefined;
|
||||||
|
@memcpy(&buffer, plaintext);
|
||||||
|
|
||||||
|
try salsa20.encrypt_inplace(&ctx, &buffer);
|
||||||
|
try testing.expectEqualSlices(u8, reference[0..], buffer[0..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------- TEST HELPERS ----------------------------------- //
|
||||||
|
|
||||||
|
fn test_quarter_round_function(y0: u32, y1: u32, y2: u32, y3: u32, z0: u32, z1: u32, z2: u32, z3: u32) !void {
|
||||||
|
var dummy_state: [salsa20.BLOCK_WORDS]u32 = undefined;
|
||||||
|
dummy_state[0] = y0;
|
||||||
|
dummy_state[1] = y1;
|
||||||
|
dummy_state[2] = y2;
|
||||||
|
dummy_state[3] = y3;
|
||||||
|
salsa20.quarter_round(&dummy_state, 0, 1, 2, 3);
|
||||||
|
try testing.expectEqualSlices(u32, &.{ z0, z1, z2, z3 }, dummy_state[0..4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_hash_function(count: comptime_int, input: *const [salsa20.BLOCK_SIZE]u8, reference: *const [salsa20.BLOCK_SIZE]u8) !void {
|
||||||
|
var dummy_ctx = salsa20.Salsa20Ctx{
|
||||||
|
.key = undefined,
|
||||||
|
.nonce = undefined,
|
||||||
|
.counter = undefined,
|
||||||
|
.constants = undefined,
|
||||||
|
.state = undefined,
|
||||||
|
.working_state = undefined,
|
||||||
|
.keystream_idx = undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
salsa20.deserialize(salsa20.BLOCK_WORDS, input, dummy_ctx.state[0..]);
|
||||||
|
for (0..count) |_|
|
||||||
|
salsa20.hash_function(&dummy_ctx);
|
||||||
|
|
||||||
|
var serialized_result: [salsa20.BLOCK_SIZE]u8 = undefined;
|
||||||
|
salsa20.serialize(salsa20.BLOCK_WORDS, dummy_ctx.state[0..], serialized_result[0..]);
|
||||||
|
|
||||||
|
try testing.expectEqualSlices(u8, reference, serialized_result[0..]);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user