Refactor tests into a separate module.

This commit is contained in:
Milan Špinka
2025-02-01 17:19:19 +01:00
parent 17e28b8279
commit 0ae582b733
19 changed files with 1348 additions and 1169 deletions

13
test/index.zig Normal file
View File

@ -0,0 +1,13 @@
comptime {
_ = .{
@import("./primitive/blockcipher/aes.zig"),
@import("./primitive/blockcipher/operation_modes.zig"),
@import("./primitive/digest/sha.zig"),
@import("./primitive/streamcipher/chacha20.zig"),
@import("./primitive/streamcipher/salsa20.zig"),
};
}
test {
@import("std").testing.refAllDecls(@This());
}

View File

@ -0,0 +1,322 @@
const std = @import("std");
const testing = std.testing;
const aes = @import("ziggy").primitive.blockcipher.aes;
test "AES-128 key expansion (FIPS 197)" {
const key = [aes.KEY_SIZE_128]u8{
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
};
const expected_expansion = [(aes.N_ROUNDS_128 + 1) * 4]u32{
0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x09cf4f3c,
0xa0fafe17, 0x88542cb1, 0x23a33939, 0x2a6c7605,
0xf2c295f2, 0x7a96b943, 0x5935807a, 0x7359f67f,
0x3d80477d, 0x4716fe3e, 0x1e237e44, 0x6d7a883b,
0xef44a541, 0xa8525b7f, 0xb671253b, 0xdb0bad00,
0xd4d1c6f8, 0x7c839d87, 0xcaf2b8bc, 0x11f915bc,
0x6d88a37a, 0x110b3efd, 0xdbf98641, 0xca0093fd,
0x4e54f70e, 0x5f5fc9f3, 0x84a64fb2, 0x4ea6dc4f,
0xead27321, 0xb58dbad2, 0x312bf560, 0x7f8d292f,
0xac7766f3, 0x19fadc21, 0x28d12941, 0x575c006e,
0xd014f9a8, 0xc9ee2589, 0xe13f0cc8, 0xb6630ca6,
};
try testing.expectEqualSlices(u32, &expected_expansion, &aes.expand_key_128(&key));
}
test "AES-192 key expansion (FIPS 197)" {
const key = [aes.KEY_SIZE_192]u8{
0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52,
0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b,
};
const expected_expansion = [(aes.N_ROUNDS_192 + 1) * 4]u32{
0x8e73b0f7, 0xda0e6452, 0xc810f32b, 0x809079e5,
0x62f8ead2, 0x522c6b7b, 0xfe0c91f7, 0x2402f5a5,
0xec12068e, 0x6c827f6b, 0x0e7a95b9, 0x5c56fec2,
0x4db7b4bd, 0x69b54118, 0x85a74796, 0xe92538fd,
0xe75fad44, 0xbb095386, 0x485af057, 0x21efb14f,
0xa448f6d9, 0x4d6dce24, 0xaa326360, 0x113b30e6,
0xa25e7ed5, 0x83b1cf9a, 0x27f93943, 0x6a94f767,
0xc0a69407, 0xd19da4e1, 0xec1786eb, 0x6fa64971,
0x485f7032, 0x22cb8755, 0xe26d1352, 0x33f0b7b3,
0x40beeb28, 0x2f18a259, 0x6747d26b, 0x458c553e,
0xa7e1466c, 0x9411f1df, 0x821f750a, 0xad07d753,
0xca400538, 0x8fcc5006, 0x282d166a, 0xbc3ce7b5,
0xe98ba06f, 0x448c773c, 0x8ecc7204, 0x01002202,
};
try testing.expectEqualSlices(u32, &expected_expansion, &aes.expand_key_192(&key));
}
test "AES-256 key expansion (FIPS 197)" {
const key = [aes.KEY_SIZE_256]u8{
0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4,
};
const expected_expansion = [(aes.N_ROUNDS_256 + 1) * 4]u32{
0x603deb10, 0x15ca71be, 0x2b73aef0, 0x857d7781,
0x1f352c07, 0x3b6108d7, 0x2d9810a3, 0x0914dff4,
0x9ba35411, 0x8e6925af, 0xa51a8b5f, 0x2067fcde,
0xa8b09c1a, 0x93d194cd, 0xbe49846e, 0xb75d5b9a,
0xd59aecb8, 0x5bf3c917, 0xfee94248, 0xde8ebe96,
0xb5a9328a, 0x2678a647, 0x98312229, 0x2f6c79b3,
0x812c81ad, 0xdadf48ba, 0x24360af2, 0xfab8b464,
0x98c5bfc9, 0xbebd198e, 0x268c3ba7, 0x09e04214,
0x68007bac, 0xb2df3316, 0x96e939e4, 0x6c518d80,
0xc814e204, 0x76a9fb8a, 0x5025c02d, 0x59c58239,
0xde136967, 0x6ccc5a71, 0xfa256395, 0x9674ee15,
0x5886ca5d, 0x2e2f31d7, 0x7e0af1fa, 0x27cf73c3,
0x749c47ab, 0x18501dda, 0xe2757e4f, 0x7401905a,
0xcafaaae3, 0xe4d59b34, 0x9adf6ace, 0xbd10190d,
0xfe4890d1, 0xe6188d0b, 0x046df344, 0x706c631e,
};
try testing.expectEqualSlices(u32, &expected_expansion, &aes.expand_key_256(&key));
}
// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values
test "AES-128 ECB encryption (NIST)" {
const key = [aes.KEY_SIZE_128]u8{
0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C,
};
const plaintext = [_][aes.BLOCK_SIZE]u8{
.{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A },
.{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 },
.{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF },
.{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 },
};
const ciphertext = [_][aes.BLOCK_SIZE]u8{
.{ 0x3A, 0xD7, 0x7B, 0xB4, 0x0D, 0x7A, 0x36, 0x60, 0xA8, 0x9E, 0xCA, 0xF3, 0x24, 0x66, 0xEF, 0x97 },
.{ 0xF5, 0xD3, 0xD5, 0x85, 0x03, 0xB9, 0x69, 0x9D, 0xE7, 0x85, 0x89, 0x5A, 0x96, 0xFD, 0xBA, 0xAF },
.{ 0x43, 0xB1, 0xCD, 0x7F, 0x59, 0x8E, 0xCE, 0x23, 0x88, 0x1B, 0x00, 0xE3, 0xED, 0x03, 0x06, 0x88 },
.{ 0x7B, 0x0C, 0x78, 0x5E, 0x27, 0xE8, 0xAD, 0x3F, 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5D, 0xD4 },
};
var buffer: [aes.BLOCK_SIZE]u8 = undefined;
for (plaintext, 0..) |pt, i| {
aes.aes128_encrypt_block(&key, &pt, &buffer);
try testing.expectEqualSlices(u8, &ciphertext[i], &buffer);
}
}
// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values
test "AES-128 ECB decryption (NIST)" {
const key = [aes.KEY_SIZE_128]u8{
0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C,
};
const plaintext = [_][aes.BLOCK_SIZE]u8{
.{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A },
.{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 },
.{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF },
.{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 },
};
const ciphertext = [_][aes.BLOCK_SIZE]u8{
.{ 0x3A, 0xD7, 0x7B, 0xB4, 0x0D, 0x7A, 0x36, 0x60, 0xA8, 0x9E, 0xCA, 0xF3, 0x24, 0x66, 0xEF, 0x97 },
.{ 0xF5, 0xD3, 0xD5, 0x85, 0x03, 0xB9, 0x69, 0x9D, 0xE7, 0x85, 0x89, 0x5A, 0x96, 0xFD, 0xBA, 0xAF },
.{ 0x43, 0xB1, 0xCD, 0x7F, 0x59, 0x8E, 0xCE, 0x23, 0x88, 0x1B, 0x00, 0xE3, 0xED, 0x03, 0x06, 0x88 },
.{ 0x7B, 0x0C, 0x78, 0x5E, 0x27, 0xE8, 0xAD, 0x3F, 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5D, 0xD4 },
};
var buffer: [aes.BLOCK_SIZE]u8 = undefined;
for (ciphertext, 0..) |ct, i| {
aes.aes128_decrypt_block(&key, &ct, &buffer);
try testing.expectEqualSlices(u8, &plaintext[i], &buffer);
}
}
// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values
test "AES-192 ECB encryption (NIST)" {
const key = [aes.KEY_SIZE_192]u8{
0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52,
0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5,
0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B,
};
const plaintext = [_][aes.BLOCK_SIZE]u8{
.{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A },
.{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 },
.{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF },
.{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 },
};
const ciphertext = [_][aes.BLOCK_SIZE]u8{
.{ 0xBD, 0x33, 0x4F, 0x1D, 0x6E, 0x45, 0xF2, 0x5F, 0xF7, 0x12, 0xA2, 0x14, 0x57, 0x1F, 0xA5, 0xCC },
.{ 0x97, 0x41, 0x04, 0x84, 0x6D, 0x0A, 0xD3, 0xAD, 0x77, 0x34, 0xEC, 0xB3, 0xEC, 0xEE, 0x4E, 0xEF },
.{ 0xEF, 0x7A, 0xFD, 0x22, 0x70, 0xE2, 0xE6, 0x0A, 0xDC, 0xE0, 0xBA, 0x2F, 0xAC, 0xE6, 0x44, 0x4E },
.{ 0x9A, 0x4B, 0x41, 0xBA, 0x73, 0x8D, 0x6C, 0x72, 0xFB, 0x16, 0x69, 0x16, 0x03, 0xC1, 0x8E, 0x0E },
};
var buffer: [aes.BLOCK_SIZE]u8 = undefined;
for (plaintext, 0..) |pt, i| {
aes.aes192_encrypt_block(&key, &pt, &buffer);
try testing.expectEqualSlices(u8, &ciphertext[i], &buffer);
}
}
// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values
test "AES-192 ECB decryption (NIST)" {
const key = [aes.KEY_SIZE_192]u8{
0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52,
0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5,
0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B,
};
const plaintext = [_][aes.BLOCK_SIZE]u8{
.{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A },
.{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 },
.{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF },
.{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 },
};
const ciphertext = [_][aes.BLOCK_SIZE]u8{
.{ 0xBD, 0x33, 0x4F, 0x1D, 0x6E, 0x45, 0xF2, 0x5F, 0xF7, 0x12, 0xA2, 0x14, 0x57, 0x1F, 0xA5, 0xCC },
.{ 0x97, 0x41, 0x04, 0x84, 0x6D, 0x0A, 0xD3, 0xAD, 0x77, 0x34, 0xEC, 0xB3, 0xEC, 0xEE, 0x4E, 0xEF },
.{ 0xEF, 0x7A, 0xFD, 0x22, 0x70, 0xE2, 0xE6, 0x0A, 0xDC, 0xE0, 0xBA, 0x2F, 0xAC, 0xE6, 0x44, 0x4E },
.{ 0x9A, 0x4B, 0x41, 0xBA, 0x73, 0x8D, 0x6C, 0x72, 0xFB, 0x16, 0x69, 0x16, 0x03, 0xC1, 0x8E, 0x0E },
};
var buffer: [aes.BLOCK_SIZE]u8 = undefined;
for (ciphertext, 0..) |ct, i| {
aes.aes192_decrypt_block(&key, &ct, &buffer);
try testing.expectEqualSlices(u8, &plaintext[i], &buffer);
}
}
// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values
test "AES-256 ECB encryption (NIST)" {
const key = [aes.KEY_SIZE_256]u8{
0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81,
0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4,
};
const plaintext = [_][aes.BLOCK_SIZE]u8{
.{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A },
.{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 },
.{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF },
.{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 },
};
const ciphertext = [_][aes.BLOCK_SIZE]u8{
.{ 0xF3, 0xEE, 0xD1, 0xBD, 0xB5, 0xD2, 0xA0, 0x3C, 0x06, 0x4B, 0x5A, 0x7E, 0x3D, 0xB1, 0x81, 0xF8 },
.{ 0x59, 0x1C, 0xCB, 0x10, 0xD4, 0x10, 0xED, 0x26, 0xDC, 0x5B, 0xA7, 0x4A, 0x31, 0x36, 0x28, 0x70 },
.{ 0xB6, 0xED, 0x21, 0xB9, 0x9C, 0xA6, 0xF4, 0xF9, 0xF1, 0x53, 0xE7, 0xB1, 0xBE, 0xAF, 0xED, 0x1D },
.{ 0x23, 0x30, 0x4B, 0x7A, 0x39, 0xF9, 0xF3, 0xFF, 0x06, 0x7D, 0x8D, 0x8F, 0x9E, 0x24, 0xEC, 0xC7 },
};
var buffer: [aes.BLOCK_SIZE]u8 = undefined;
for (plaintext, 0..) |pt, i| {
aes.aes256_encrypt_block(&key, &pt, &buffer);
try testing.expectEqualSlices(u8, &ciphertext[i], &buffer);
}
}
// https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values
test "AES-256 ECB decryption (NIST)" {
const key = [aes.KEY_SIZE_256]u8{
0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81,
0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4,
};
const plaintext = [_][aes.BLOCK_SIZE]u8{
.{ 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A },
.{ 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 },
.{ 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF },
.{ 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 },
};
const ciphertext = [_][aes.BLOCK_SIZE]u8{
.{ 0xF3, 0xEE, 0xD1, 0xBD, 0xB5, 0xD2, 0xA0, 0x3C, 0x06, 0x4B, 0x5A, 0x7E, 0x3D, 0xB1, 0x81, 0xF8 },
.{ 0x59, 0x1C, 0xCB, 0x10, 0xD4, 0x10, 0xED, 0x26, 0xDC, 0x5B, 0xA7, 0x4A, 0x31, 0x36, 0x28, 0x70 },
.{ 0xB6, 0xED, 0x21, 0xB9, 0x9C, 0xA6, 0xF4, 0xF9, 0xF1, 0x53, 0xE7, 0xB1, 0xBE, 0xAF, 0xED, 0x1D },
.{ 0x23, 0x30, 0x4B, 0x7A, 0x39, 0xF9, 0xF3, 0xFF, 0x06, 0x7D, 0x8D, 0x8F, 0x9E, 0x24, 0xEC, 0xC7 },
};
var buffer: [aes.BLOCK_SIZE]u8 = undefined;
for (ciphertext, 0..) |ct, i| {
aes.aes256_decrypt_block(&key, &ct, &buffer);
try testing.expectEqualSlices(u8, &plaintext[i], &buffer);
}
}
test "AES S-box inverse is the inverse S-box" {
for (0..256) |x| {
try testing.expectEqual(x, aes.INV_SBOX[aes.SBOX[x]]);
}
}
test "AES SubBytes" {
var state = [aes.BLOCK_SIZE]u8{
0x40, 0xBF, 0xAB, 0xF4, 0x06, 0xEE, 0x4D, 0x30,
0x42, 0xCA, 0x6B, 0x99, 0x7A, 0x5C, 0x58, 0x16,
};
const reference = [aes.BLOCK_SIZE]u8{
0x09, 0x08, 0x62, 0xBF, 0x6F, 0x28, 0xE3, 0x04,
0x2C, 0x74, 0x7F, 0xEE, 0xDA, 0x4A, 0x6A, 0x47,
};
aes.sub_bytes(&state);
try testing.expectEqualSlices(u8, &reference, &state);
}
test "AES ShiftRows" {
var state = [aes.BLOCK_SIZE]u8{
0x09, 0x08, 0x62, 0xBF, 0x6F, 0x28, 0xE3, 0x04,
0x2C, 0x74, 0x7F, 0xEE, 0xDA, 0x4A, 0x6A, 0x47,
};
const reference = [aes.BLOCK_SIZE]u8{
0x09, 0x28, 0x7F, 0x47, 0x6F, 0x74, 0x6A, 0xBF,
0x2C, 0x4A, 0x62, 0x04, 0xDA, 0x08, 0xE3, 0xEE,
};
aes.shift_rows(&state);
try testing.expectEqualSlices(u8, &reference, &state);
}
test "AES MixColumns" {
var state = [aes.BLOCK_SIZE]u8{
0x09, 0x28, 0x7F, 0x47, 0x6F, 0x74, 0x6A, 0xBF,
0x2C, 0x4A, 0x62, 0x04, 0xDA, 0x08, 0xE3, 0xEE,
};
const reference = [aes.BLOCK_SIZE]u8{
0x52, 0x9F, 0x16, 0xC2, 0x97, 0x86, 0x15, 0xCA,
0xE0, 0x1A, 0xAE, 0x54, 0xBA, 0x1A, 0x26, 0x59,
};
aes.mix_columns(&state);
try testing.expectEqualSlices(u8, &reference, &state);
}
test "AES InvSubBytes" {
const reference = [aes.BLOCK_SIZE]u8{
0x40, 0xBF, 0xAB, 0xF4, 0x06, 0xEE, 0x4D, 0x30,
0x42, 0xCA, 0x6B, 0x99, 0x7A, 0x5C, 0x58, 0x16,
};
var state = [aes.BLOCK_SIZE]u8{
0x09, 0x08, 0x62, 0xBF, 0x6F, 0x28, 0xE3, 0x04,
0x2C, 0x74, 0x7F, 0xEE, 0xDA, 0x4A, 0x6A, 0x47,
};
aes.inv_sub_bytes(&state);
try testing.expectEqualSlices(u8, &reference, &state);
}
test "AES InvShiftRows" {
const reference = [aes.BLOCK_SIZE]u8{
0x09, 0x08, 0x62, 0xBF, 0x6F, 0x28, 0xE3, 0x04,
0x2C, 0x74, 0x7F, 0xEE, 0xDA, 0x4A, 0x6A, 0x47,
};
var state = [aes.BLOCK_SIZE]u8{
0x09, 0x28, 0x7F, 0x47, 0x6F, 0x74, 0x6A, 0xBF,
0x2C, 0x4A, 0x62, 0x04, 0xDA, 0x08, 0xE3, 0xEE,
};
aes.inv_shift_rows(&state);
try testing.expectEqualSlices(u8, &reference, &state);
}
test "AES InvMixColumns" {
const reference = [aes.BLOCK_SIZE]u8{
0x09, 0x28, 0x7F, 0x47, 0x6F, 0x74, 0x6A, 0xBF,
0x2C, 0x4A, 0x62, 0x04, 0xDA, 0x08, 0xE3, 0xEE,
};
var state = [aes.BLOCK_SIZE]u8{
0x52, 0x9F, 0x16, 0xC2, 0x97, 0x86, 0x15, 0xCA,
0xE0, 0x1A, 0xAE, 0x54, 0xBA, 0x1A, 0x26, 0x59,
};
aes.inv_mix_columns(&state);
try testing.expectEqualSlices(u8, &reference, &state);
}

View File

@ -0,0 +1,78 @@
const std = @import("std");
const testing = std.testing;
// Use AES to test all of the operation modes.
const aes = @import("ziggy").primitive.blockcipher.aes;
// ----------------------------------- CBC MODE ----------------------------------- //
const cbc = @import("ziggy").primitive.blockcipher.operation_mode.cbc;
test "AES-128-CBC basic test" {
const BS = aes.BLOCK_SIZE;
const KS = aes.KEY_SIZE_128;
const ENC = aes.aes128_encrypt_block;
const DEC = aes.aes128_decrypt_block;
// Plaintext source: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_block_chaining_(CBC)
// Retrieved 2025/02/01
const key = [KS]u8{ 0x0c, 0x49, 0x08, 0x75, 0xd4, 0x63, 0x2c, 0x2a, 0x9a, 0x73, 0xab, 0xb6, 0x5c, 0x67, 0x06, 0x0d };
const iv = [BS]u8{ 0xbe, 0x10, 0x47, 0x59, 0xac, 0xf3, 0x9c, 0x10, 0x1e, 0x2e, 0x37, 0x77, 0x85, 0xe0, 0x13, 0x0e };
const plaintext = "Ehrsam, Meyer, Smith and Tuchman invented the cipher block chaining (CBC) mode of operation in 1976.[23] In CBC mode, each block of plaintext is XORed with the previous ciphertext block before being encrypted. This way, each ciphertext block depends on all plaintext blocks processed up to that point. To make each message unique, an initialization vector must be used in the first block.";
const ciphertext = [_]u8{ 0x1c, 0x15, 0x02, 0xb8, 0x88, 0xfe, 0xf2, 0x23, 0x72, 0xbf, 0x14, 0x7a, 0xf7, 0x11, 0xde, 0x5a, 0x53, 0x7b, 0x3e, 0xf3, 0xe3, 0xea, 0xb7, 0xd8, 0xe2, 0x4d, 0x9a, 0x82, 0xd8, 0x84, 0x96, 0xc2, 0xa4, 0x8a, 0x82, 0xd5, 0xe9, 0x46, 0x3d, 0x4e, 0xf2, 0xc2, 0xcc, 0x62, 0x73, 0x9d, 0x6e, 0xf8, 0x86, 0xd0, 0xac, 0xb0, 0x0a, 0x80, 0xef, 0x3a, 0x83, 0x74, 0xb7, 0xf9, 0x4f, 0xc5, 0x52, 0x2c, 0x5c, 0x52, 0xd8, 0x41, 0xd4, 0x0b, 0xc2, 0xa9, 0xa6, 0xa3, 0x7b, 0xcc, 0x2e, 0x15, 0xd5, 0xc9, 0x8e, 0x6a, 0x0b, 0x50, 0xe2, 0xbf, 0xcb, 0x09, 0x61, 0x1b, 0x26, 0x78, 0x1a, 0x23, 0xe7, 0xd5, 0x0c, 0x50, 0x3f, 0xc8, 0xa9, 0xb4, 0xea, 0x97, 0xa1, 0xaf, 0xf4, 0xcd, 0xb4, 0x76, 0x0e, 0x4c, 0xa9, 0xfc, 0xf8, 0xbd, 0x45, 0xbb, 0x7b, 0xd3, 0x84, 0x52, 0xac, 0x29, 0xa3, 0xee, 0x62, 0x23, 0xc1, 0x6a, 0xca, 0x1c, 0x62, 0x0a, 0x8b, 0xce, 0x36, 0x9e, 0x7e, 0xb5, 0x5b, 0x2a, 0xa8, 0x85, 0x37, 0x51, 0x0c, 0xed, 0x05, 0xca, 0xb6, 0xc6, 0xd9, 0xc0, 0x0d, 0x9d, 0x57, 0x2a, 0xcd, 0x79, 0xf6, 0x23, 0x36, 0x6d, 0x50, 0x29, 0xff, 0xa2, 0x71, 0x5a, 0x77, 0x0e, 0x26, 0x41, 0x2f, 0x22, 0xc6, 0x8f, 0xdf, 0x2a, 0x0f, 0xb6, 0x19, 0xb7, 0xdb, 0x6b, 0x62, 0x04, 0x50, 0x97, 0x13, 0x8d, 0x60, 0xfa, 0x31, 0x89, 0x36, 0x9e, 0x46, 0xf9, 0x51, 0xcc, 0xd0, 0x90, 0x8b, 0x3d, 0x7a, 0x67, 0xfa, 0x16, 0xdf, 0x3a, 0xe8, 0x96, 0x9c, 0xac, 0x18, 0x7a, 0x12, 0x55, 0x52, 0xcf, 0xc0, 0xeb, 0x0b, 0x49, 0xc2, 0xda, 0x1a, 0x20, 0xaa, 0xee, 0x04, 0x14, 0xa5, 0x23, 0xbf, 0x60, 0x95, 0x09, 0x19, 0x55, 0xa4, 0x93, 0xfe, 0xab, 0x33, 0x6d, 0x4f, 0x51, 0xcb, 0xff, 0xe5, 0x69, 0x39, 0x4c, 0x1a, 0x8c, 0xf9, 0x12, 0x0d, 0x67, 0x7c, 0x10, 0xe9, 0xf7, 0xdf, 0xa6, 0x92, 0x4f, 0x6c, 0x55, 0xca, 0x42, 0xa2, 0x7b, 0xd1, 0xbf, 0x3b, 0xcf, 0xc1, 0xda, 0xad, 0x80, 0x12, 0x95, 0xe7, 0x86, 0x4a, 0x04, 0x1f, 0x01, 0x9e, 0x64, 0xaf, 0x02, 0xcd, 0xb3, 0x84, 0x39, 0x42, 0x8b, 0x04, 0x04, 0x5d, 0x76, 0x20, 0x49, 0x3b, 0x87, 0x03, 0x31, 0xa5, 0x40, 0x4d, 0xca, 0x0e, 0x15, 0x28, 0x03, 0x77, 0xbc, 0xe4, 0x70, 0x69, 0x0d, 0x21, 0x80, 0xa7, 0x92, 0xf7, 0xe0, 0x74, 0x4d, 0xe7, 0xf0, 0x26, 0x55, 0xe7, 0x14, 0xb7, 0xde, 0xa0, 0xf5, 0xd3, 0xe7, 0xea, 0x2b, 0x0b, 0x28, 0x7c, 0xd5, 0x36, 0x0f, 0xb0, 0x83, 0x42, 0x0d, 0x1d, 0xf5, 0xc9, 0xcc, 0xe2, 0x68, 0x16, 0xa1, 0xcc, 0xcb, 0x09, 0x2c, 0x85, 0xdf, 0xec, 0x02, 0xf5, 0x5f, 0x67, 0x6d, 0x04, 0xae, 0x3a, 0x34, 0xa8, 0x46, 0x9d, 0x80, 0xd8, 0xee, 0xfb, 0x12, 0x9f, 0x25, 0xd5, 0x0b, 0x38, 0xfe, 0x99, 0x59, 0xeb, 0xb9 };
// Test encryption.
{
const ct_length = comptime cbc.get_padded_buffer_length(BS, plaintext);
var ct_buffer: [ct_length]u8 = undefined;
var ctx = cbc.cbc_pkcs7_encrypt_new(BS, KS, ENC, &key, &iv);
defer cbc.cbc_pkcs7_encrypt_destroy(BS, KS, ENC, &ctx);
var pt_processed_bytes: usize = 0;
while (plaintext.len - pt_processed_bytes >= aes.BLOCK_SIZE) : (pt_processed_bytes += aes.BLOCK_SIZE)
cbc.cbc_pkcs7_encrypt_block(
BS,
KS,
ENC,
&ctx,
@ptrCast(plaintext[pt_processed_bytes .. pt_processed_bytes + aes.BLOCK_SIZE]),
@ptrCast(ct_buffer[pt_processed_bytes .. pt_processed_bytes + aes.BLOCK_SIZE]),
);
try cbc.cbc_pkcs7_encrypt_final(BS, KS, ENC, &ctx, plaintext[pt_processed_bytes..], @ptrCast(ct_buffer[pt_processed_bytes..]));
try testing.expectEqualSlices(u8, ciphertext[0..], ct_buffer[0..]);
}
// Test decryption.
{
var pt_buffer: [ciphertext.len]u8 = undefined;
var ctx = cbc.cbc_pkcs7_decrypt_new(BS, KS, DEC, &key, &iv);
defer cbc.cbc_pkcs7_decrypt_destroy(BS, KS, DEC, &ctx);
var ct_processed_bytes: usize = 0;
while (ciphertext.len - ct_processed_bytes >= 2 * aes.BLOCK_SIZE) : (ct_processed_bytes += aes.BLOCK_SIZE)
cbc.cbc_pkcs7_decrypt_block(
BS,
KS,
DEC,
&ctx,
@ptrCast(ciphertext[ct_processed_bytes .. ct_processed_bytes + aes.BLOCK_SIZE]),
@ptrCast(pt_buffer[ct_processed_bytes .. ct_processed_bytes + aes.BLOCK_SIZE]),
);
const residue_length = try cbc.cbc_pkcs7_decrypt_final(
BS,
KS,
DEC,
&ctx,
ciphertext[ct_processed_bytes..],
pt_buffer[ct_processed_bytes..],
);
try testing.expectEqual(plaintext.len % aes.BLOCK_SIZE, residue_length);
try testing.expectEqualSlices(u8, plaintext[0..], pt_buffer[0..plaintext.len]);
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,129 @@
const std = @import("std");
const testing = std.testing;
const byte_operations = @import("ziggy").utility.byte_operations;
const word_to_bytes = byte_operations.word_to_bytes_le;
const bytes_to_word = byte_operations.bytes_to_word_le;
const chacha20 = @import("ziggy").primitive.streamcipher.chacha20;
// ----------------------------------- TEST VECTORS ----------------------------------- //
// https://www.rfc-editor.org/rfc/rfc7539#section-2.2.1
test "ChaCha Quarter Round" {
var state = [chacha20.BLOCK_WORDS]u32{
0x879531e0, 0xc5ecf37d, 0x516461b1, 0xc9a62f8a,
0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0x2a5f714c,
0x53372767, 0xb00a5631, 0x974c541a, 0x359e9963,
0x5c971061, 0x3d631689, 0x2098d9d6, 0x91dbd320,
};
const reference = [chacha20.BLOCK_WORDS]u32{
0x879531e0, 0xc5ecf37d, 0xbdb886dc, 0xc9a62f8a,
0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0xcfacafd2,
0xe46bea80, 0xb00a5631, 0x974c541a, 0x359e9963,
0x5c971061, 0xccc07c79, 0x2098d9d6, 0x91dbd320,
};
chacha20.quarter_round(&state, 2, 7, 8, 13);
try testing.expectEqualSlices(u32, reference[0..], state[0..]);
}
// https://www.rfc-editor.org/rfc/rfc7539#section-2.3.2
test "ChaCha20 Block Function" {
const key = [chacha20.KEY_SIZE]u8{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
};
const nonce = [chacha20.Parameters_RFC7539.NONCE_SIZE]u8{
0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00,
};
const reference = [chacha20.BLOCK_SIZE]u8{
0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15, 0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, 0x71, 0xc4,
0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03, 0x04, 0x22, 0xaa, 0x9a, 0xc3, 0xd4, 0x6c, 0x4e,
0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09, 0x14, 0xc2, 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2,
0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9, 0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e,
};
var chacha = chacha20.chacha20_rfc7539_new(&key, &nonce, &word_to_bytes(1));
defer chacha20.chacha20_destroy(&chacha);
chacha20.block_function(&chacha);
var buffer: [chacha20.BLOCK_SIZE]u8 = undefined;
chacha20.serialize(chacha20.BLOCK_WORDS, &chacha.state, &buffer);
try testing.expectEqualSlices(u8, reference[0..], buffer[0..]);
}
// https://www.rfc-editor.org/rfc/rfc7539#section-2.4.2
test "ChaCha20 Cipher" {
const key = [chacha20.KEY_SIZE]u8{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
};
const nonce = [chacha20.Parameters_RFC7539.NONCE_SIZE]u8{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00,
};
const counter = word_to_bytes(1);
const plaintext = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
const reference = [_]u8{
0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80, 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81,
0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2, 0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b,
0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab, 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57,
0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab, 0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8,
0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61, 0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e,
0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06, 0x81, 0x8c, 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36,
0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6, 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
0x87, 0x4d,
};
var chacha = chacha20.chacha20_rfc7539_new(&key, &nonce, &counter);
defer chacha20.chacha20_destroy(&chacha);
var buffer: [plaintext.len]u8 = undefined;
@memcpy(&buffer, plaintext);
try chacha20.encrypt_inplace(&chacha, &buffer);
try testing.expectEqualSlices(u8, reference[0..], buffer[0..]);
}
test "ChaCha20 32-bit counter increment edge cases" {
const key = [chacha20.KEY_SIZE]u8{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
};
const nonce = [chacha20.Parameters_RFC7539.NONCE_SIZE]u8{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00,
};
const counter = word_to_bytes(std.math.maxInt(u32) - 1);
var chacha = chacha20.chacha20_rfc7539_new(&key, &nonce, &counter);
defer chacha20.chacha20_destroy(&chacha);
// Counter: 2^32 - 2 -> 2^32 - 1, OK
try chacha20.increment_counter(&chacha);
// Counter: 2^32 - 1 -> 2^32, overflow
try testing.expectError(chacha20.KeyStreamDepleted, chacha20.increment_counter(&chacha));
}
test "ChaCha20 64-bit counter increment edge cases" {
const key = [chacha20.KEY_SIZE]u8{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
};
const nonce = [chacha20.Parameters_Bernstein.NONCE_SIZE]u8{ 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00 };
const counter = [chacha20.Parameters_Bernstein.COUNTER_SIZE]u8{ 0xff, 0xff, 0xff, 0xff, 0xef, 0xbe, 0xad, 0xde };
var chacha = chacha20.chacha20_bernstein_new(&key, &nonce, &counter);
defer chacha20.chacha20_destroy(&chacha);
// Counter: 0xdeadbeefffffffff -> 0xdeadbef000000000, OK
try chacha20.increment_counter(&chacha);
try testing.expectEqualSlices(u32, &.{ 0x00000000, 0xdeadbef0 }, chacha.nonce[0..2]);
// Counter: 0xffffffffffffffff -> 0x10000000000000000, overflow
@memset(chacha.nonce[0..2], std.math.maxInt(u32));
try testing.expectError(chacha20.KeyStreamDepleted, chacha20.increment_counter(&chacha));
}

View File