diff --git a/test/default/core_keccak1600.c b/test/default/core_keccak1600.c index 6f6db72a..08f926a0 100644 --- a/test/default/core_keccak1600.c +++ b/test/default/core_keccak1600.c @@ -17,16 +17,16 @@ static int compare_states(const char *label, const unsigned char *actual, const unsigned char *expected, size_t len) { - size_t i; + size_t i, j; for (i = 0; i < len; i++) { if (actual[i] != expected[i]) { printf("FAIL: %s mismatch at byte %u\n", label, (unsigned int) i); printf(" Expected: "); - for (size_t j = 0; j < len; j++) { + for (j = 0; j < len; j++) { printf("%02x", expected[j]); } printf("\n Got: "); - for (size_t j = 0; j < len; j++) { + for (j = 0; j < len; j++) { printf("%02x", actual[j]); } printf("\n"); @@ -42,8 +42,13 @@ main(void) { unsigned char state[crypto_core_keccak1600_STATEBYTES]; unsigned char extracted[64]; + unsigned char test_data[64]; + unsigned char sentinel[64]; + unsigned char state_12[200], state_24[200]; + unsigned char last_byte; size_t i; int test_failures = 0; + int differs; /* Test vectors for Keccak-f[1600] (24 rounds) */ /* Test vector 1: All-zero input for Keccak-f[1600] */ @@ -145,10 +150,50 @@ main(void) /* Test 3: XOR and extract functions */ printf("Test 3: crypto_core_keccak1600_xor_bytes and extract_bytes\n"); crypto_core_keccak1600_init(state); - unsigned char test_data[64]; for (i = 0; i < sizeof test_data; i++) { test_data[i] = (unsigned char) i; } + + /* Edge case: zero-length XOR/extract should be a no-op (test at offset 0 and at end) */ + { + static const size_t offsets[] = { 0, crypto_core_keccak1600_STATEBYTES }; + size_t t; + for (t = 0; t < sizeof offsets / sizeof offsets[0]; t++) { + crypto_core_keccak1600_init(state); + memset(extracted, 0xA5, sizeof extracted); + memset(sentinel, 0xA5, sizeof sentinel); + crypto_core_keccak1600_xor_bytes(state, test_data, offsets[t], 0); + crypto_core_keccak1600_extract_bytes(state, extracted, offsets[t], 0); + for (i = 0; i < sizeof state; i++) { + if (state[i] != 0) { + printf(" FAIL: XOR length 0 at offset %u modified state\n\n", + (unsigned) offsets[t]); + test_failures++; + break; + } + } + if (memcmp(extracted, sentinel, sizeof extracted) != 0) { + printf(" FAIL: extract length 0 at offset %u modified output\n\n", + (unsigned) offsets[t]); + test_failures++; + } + } + } + + /* Edge case: XOR/extract last byte */ + crypto_core_keccak1600_init(state); + memset(extracted, 0, sizeof extracted); + last_byte = 0x5A; + crypto_core_keccak1600_xor_bytes(state, &last_byte, + crypto_core_keccak1600_STATEBYTES - 1, 1); + crypto_core_keccak1600_extract_bytes(state, extracted, + crypto_core_keccak1600_STATEBYTES - 1, 1); + if (extracted[0] != last_byte) { + printf(" FAIL: XOR/extract last byte mismatch\n\n"); + test_failures++; + } + + crypto_core_keccak1600_init(state); crypto_core_keccak1600_xor_bytes(state, test_data, 0, sizeof test_data); crypto_core_keccak1600_extract_bytes(state, extracted, 0, sizeof test_data); if (memcmp(extracted, test_data, sizeof test_data) == 0) { @@ -184,13 +229,12 @@ main(void) /* Test 7: Verify 12 and 24 rounds produce different outputs */ printf("Test 7: Verify 12-round and 24-round differ\n"); - unsigned char state_12[200], state_24[200]; crypto_core_keccak1600_init(state_12); crypto_core_keccak1600_init(state_24); crypto_core_keccak1600_permute_12(state_12); crypto_core_keccak1600_permute_24(state_24); - int differs = 0; + differs = 0; for (i = 0; i < 200; i++) { if (state_12[i] != state_24[i]) { differs = 1; diff --git a/test/default/ipcrypt.c b/test/default/ipcrypt.c index a837fdbe..f072b77a 100644 --- a/test/default/ipcrypt.c +++ b/test/default/ipcrypt.c @@ -1,7 +1,7 @@ #define TEST_NAME "ipcrypt" #include "cmptest.h" -static const unsigned char ipv4_mapped_prefix[12] = { 0U, 0U, 0U, 0U, 0U, 0U, +static const unsigned char ipv4_mapped_prefix[12] = { 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0xffU, 0xffU }; static int @@ -21,6 +21,39 @@ check_expected(const char *test_name, const unsigned char *actual, const char *e return 0; } +static void +flip_bit_msb(unsigned char buf[16], unsigned int bit_index) +{ + unsigned int byte_index; + unsigned int bit_in_byte; + + if (bit_index >= 128U) { + return; + } + byte_index = bit_index / 8U; + bit_in_byte = 7U - (bit_index % 8U); + buf[byte_index] ^= (unsigned char) (1U << bit_in_byte); +} + +static int +prefix_equal_msb(const unsigned char *a, const unsigned char *b, unsigned int prefix_len_bits) +{ + unsigned int full_bytes = prefix_len_bits / 8U; + unsigned int rem_bits = prefix_len_bits % 8U; + + if (prefix_len_bits == 0U) { + return 1; + } + if (memcmp(a, b, full_bytes) != 0) { + return 0; + } + if (rem_bits == 0U) { + return 1; + } + return (a[full_bytes] & (unsigned char) (0xffU << (8U - rem_bits))) == + (b[full_bytes] & (unsigned char) (0xffU << (8U - rem_bits))); +} + int main(void) { @@ -42,6 +75,7 @@ main(void) unsigned char encrypted1[crypto_ipcrypt_PFX_BYTES]; unsigned char encrypted2[crypto_ipcrypt_PFX_BYTES]; size_t i; + unsigned int prefix_len_bits; printf("crypto_ipcrypt_BYTES: %zu\n", crypto_ipcrypt_bytes()); printf("crypto_ipcrypt_KEYBYTES: %zu\n", crypto_ipcrypt_keybytes()); @@ -552,6 +586,49 @@ main(void) } printf("OK: IPv6 /32 prefix preserved for 2001:db8::/32\n"); + printf("\nsystematic prefix-length coverage\n"); + + memset(pfx_key, 0x6a, sizeof pfx_key); + for (prefix_len_bits = 0; prefix_len_bits <= 128U; prefix_len_bits++) { + unsigned char in1[16]; + unsigned char in2[16]; + unsigned char out1[16]; + unsigned char out2[16]; + + randombytes_buf(in1, sizeof in1); + in1[0] = 0x20; + in1[1] = 0x01; + memcpy(in2, in1, sizeof in1); + flip_bit_msb(in2, prefix_len_bits); + crypto_ipcrypt_pfx_encrypt(out1, in1, pfx_key); + crypto_ipcrypt_pfx_encrypt(out2, in2, pfx_key); + if (prefix_equal_msb(out1, out2, prefix_len_bits) == 0) { + printf("FAILED: IPv6 prefix length %u\n", prefix_len_bits); + return 1; + } + } + printf("OK: IPv6 prefix preservation for /0..128\n"); + + for (prefix_len_bits = 0; prefix_len_bits <= 32U; prefix_len_bits++) { + unsigned char in1[16]; + unsigned char in2[16]; + unsigned char out1[16]; + unsigned char out2[16]; + + memset(in1, 0, sizeof in1); + memcpy(in1, ipv4_mapped_prefix, 12); + randombytes_buf(in1 + 12, 4U); + memcpy(in2, in1, sizeof in1); + flip_bit_msb(in2, 96U + prefix_len_bits); + crypto_ipcrypt_pfx_encrypt(out1, in1, pfx_key); + crypto_ipcrypt_pfx_encrypt(out2, in2, pfx_key); + if (prefix_equal_msb(out1, out2, 96U + prefix_len_bits) == 0) { + printf("FAILED: IPv4-mapped prefix length %u\n", prefix_len_bits); + return 1; + } + } + printf("OK: IPv4-mapped prefix preservation for /0..32\n"); + printf("\nfunctional tests\n"); crypto_ipcrypt_keygen(key); @@ -740,6 +817,90 @@ main(void) } printf("OK: IPv6 all-ones round-trips\n"); + printf("\nrandomized property tests\n"); + + for (i = 0; i < 128U; i++) { + randombytes_buf(key, sizeof key); + randombytes_buf(ndx_key, sizeof ndx_key); + randombytes_buf(pfx_key, sizeof pfx_key); + randombytes_buf(tweak_nd, sizeof tweak_nd); + randombytes_buf(tweak_ndx, sizeof tweak_ndx); + randombytes_buf(input, sizeof input); + + crypto_ipcrypt_encrypt(output, input, key); + crypto_ipcrypt_decrypt(decrypted, output, key); + if (memcmp(input, decrypted, sizeof input) != 0) { + printf("FAILED: randomized ipcrypt round-trip\n"); + return 1; + } + + crypto_ipcrypt_nd_encrypt(nd_output, input, tweak_nd, key); + crypto_ipcrypt_nd_decrypt(decrypted, nd_output, key); + if (memcmp(input, decrypted, sizeof input) != 0) { + printf("FAILED: randomized nd round-trip\n"); + return 1; + } + + crypto_ipcrypt_ndx_encrypt(ndx_output, input, tweak_ndx, ndx_key); + crypto_ipcrypt_ndx_decrypt(decrypted, ndx_output, ndx_key); + if (memcmp(input, decrypted, sizeof input) != 0) { + printf("FAILED: randomized ndx round-trip\n"); + return 1; + } + + crypto_ipcrypt_pfx_encrypt(pfx_output, input, pfx_key); + crypto_ipcrypt_pfx_decrypt(decrypted, pfx_output, pfx_key); + if (memcmp(input, decrypted, sizeof input) != 0) { + printf("FAILED: randomized pfx round-trip\n"); + return 1; + } + } + printf("OK: randomized round-trip tests\n"); + + for (i = 0; i < 128U; i++) { + unsigned char in1[16]; + unsigned char in2[16]; + unsigned char out1[16]; + unsigned char out2[16]; + + randombytes_buf(pfx_key, sizeof pfx_key); + randombytes_buf(in1, sizeof in1); + in1[0] = 0x20; + in1[1] = 0x01; + memcpy(in2, in1, sizeof in1); + prefix_len_bits = (unsigned int) randombytes_uniform(129U); + flip_bit_msb(in2, prefix_len_bits); + crypto_ipcrypt_pfx_encrypt(out1, in1, pfx_key); + crypto_ipcrypt_pfx_encrypt(out2, in2, pfx_key); + if (prefix_equal_msb(out1, out2, prefix_len_bits) == 0) { + printf("FAILED: randomized IPv6 prefix length %u\n", prefix_len_bits); + return 1; + } + } + printf("OK: randomized IPv6 prefix preservation\n"); + + for (i = 0; i < 128U; i++) { + unsigned char in1[16]; + unsigned char in2[16]; + unsigned char out1[16]; + unsigned char out2[16]; + + randombytes_buf(pfx_key, sizeof pfx_key); + memset(in1, 0, sizeof in1); + memcpy(in1, ipv4_mapped_prefix, 12); + randombytes_buf(in1 + 12, 4U); + memcpy(in2, in1, sizeof in1); + prefix_len_bits = (unsigned int) randombytes_uniform(33U); + flip_bit_msb(in2, 96U + prefix_len_bits); + crypto_ipcrypt_pfx_encrypt(out1, in1, pfx_key); + crypto_ipcrypt_pfx_encrypt(out2, in2, pfx_key); + if (prefix_equal_msb(out1, out2, 96U + prefix_len_bits) == 0) { + printf("FAILED: randomized IPv4-mapped prefix length %u\n", prefix_len_bits); + return 1; + } + } + printf("OK: randomized IPv4-mapped prefix preservation\n"); + printf("\nAll specification test vectors passed!\n"); return 0; diff --git a/test/default/ipcrypt.exp b/test/default/ipcrypt.exp index 323a45d1..7dca8732 100644 --- a/test/default/ipcrypt.exp +++ b/test/default/ipcrypt.exp @@ -51,6 +51,10 @@ OK: pfx /32 test (2001:db8:9f27:0:b4e2:7a3d:5f91:c8e6) OK: pfx /32 test (2001:db8:d8b4:0:193c:a5e7:8b2f:46d1) OK: IPv6 /32 prefix preserved for 2001:db8::/32 +systematic prefix-length coverage +OK: IPv6 prefix preservation for /0..128 +OK: IPv4-mapped prefix preservation for /0..32 + functional tests OK: Key generation functions OK: In-place encryption/decryption @@ -66,4 +70,9 @@ OK: pfx in-place encryption/decryption OK: IPv6 :: round-trips OK: IPv6 all-ones round-trips +randomized property tests +OK: randomized round-trip tests +OK: randomized IPv6 prefix preservation +OK: randomized IPv4-mapped prefix preservation + All specification test vectors passed! diff --git a/test/default/xof_shake128.c b/test/default/xof_shake128.c index fabe107b..dc21b057 100644 --- a/test/default/xof_shake128.c +++ b/test/default/xof_shake128.c @@ -210,6 +210,61 @@ main(void) } } + /* Test zero-length update and squeeze are no-ops */ + crypto_xof_shake128_init(&state); + crypto_xof_shake128_update(&state, msg_abc, 1); + crypto_xof_shake128_update(&state, msg_abc, 0); + crypto_xof_shake128_update(&state, msg_abc + 1, 2); + crypto_xof_shake128_squeeze(&state, out, 0); + crypto_xof_shake128_squeeze(&state, out, 32); + if (memcmp(out, out_abc_32, 32) != 0) { + printf("Zero-length update/squeeze test failed\n"); + return 1; + } + + /* Test rate-sized input and long output (padding after full block) */ + { + unsigned char out_manual[crypto_xof_shake128_BLOCKBYTES + 7]; + unsigned char out_impl[crypto_xof_shake128_BLOCKBYTES + 7]; + + shake128_manual_with_domain(out_manual, sizeof out_manual, msg_rate_block, + sizeof msg_rate_block, crypto_xof_shake128_domain_standard()); + + crypto_xof_shake128_init(&state); + crypto_xof_shake128_update(&state, msg_rate_block, sizeof msg_rate_block); + crypto_xof_shake128_squeeze(&state, out_impl, sizeof out_impl); + + if (memcmp(out_manual, out_impl, sizeof out_manual) != 0) { + printf("Rate block long output test failed\n"); + return 1; + } + } + + /* Test chunked update across block boundary */ + { + unsigned char msg_rate_plus1[crypto_xof_shake128_BLOCKBYTES + 1]; + unsigned char out_manual[32]; + unsigned char out_impl[32]; + + for (i = 0; i < sizeof msg_rate_plus1; i++) { + msg_rate_plus1[i] = (unsigned char) i; + } + + shake128_manual_with_domain(out_manual, sizeof out_manual, msg_rate_plus1, + sizeof msg_rate_plus1, crypto_xof_shake128_domain_standard()); + + crypto_xof_shake128_init(&state); + crypto_xof_shake128_update(&state, msg_rate_plus1, 1); + crypto_xof_shake128_update(&state, msg_rate_plus1 + 1, crypto_xof_shake128_BLOCKBYTES - 1); + crypto_xof_shake128_update(&state, msg_rate_plus1 + crypto_xof_shake128_BLOCKBYTES, 1); + crypto_xof_shake128_squeeze(&state, out_impl, sizeof out_impl); + + if (memcmp(out_manual, out_impl, sizeof out_manual) != 0) { + printf("Chunked update boundary test failed\n"); + return 1; + } + } + printf("All SHAKE-128 tests passed\n"); return 0; } diff --git a/test/default/xof_shake256.c b/test/default/xof_shake256.c index 112b783b..d5bf3a5a 100644 --- a/test/default/xof_shake256.c +++ b/test/default/xof_shake256.c @@ -211,6 +211,61 @@ main(void) } } + /* Test zero-length update and squeeze are no-ops */ + crypto_xof_shake256_init(&state); + crypto_xof_shake256_update(&state, msg_abc, 1); + crypto_xof_shake256_update(&state, msg_abc, 0); + crypto_xof_shake256_update(&state, msg_abc + 1, 2); + crypto_xof_shake256_squeeze(&state, out, 0); + crypto_xof_shake256_squeeze(&state, out, 32); + if (memcmp(out, out_abc_32, 32) != 0) { + printf("Zero-length update/squeeze test failed\n"); + return 1; + } + + /* Test rate-sized input and long output (padding after full block) */ + { + unsigned char out_manual[crypto_xof_shake256_BLOCKBYTES + 7]; + unsigned char out_impl[crypto_xof_shake256_BLOCKBYTES + 7]; + + shake256_manual_with_domain(out_manual, sizeof out_manual, msg_rate_block, + sizeof msg_rate_block, crypto_xof_shake256_domain_standard()); + + crypto_xof_shake256_init(&state); + crypto_xof_shake256_update(&state, msg_rate_block, sizeof msg_rate_block); + crypto_xof_shake256_squeeze(&state, out_impl, sizeof out_impl); + + if (memcmp(out_manual, out_impl, sizeof out_manual) != 0) { + printf("Rate block long output test failed\n"); + return 1; + } + } + + /* Test chunked update across block boundary */ + { + unsigned char msg_rate_plus1[crypto_xof_shake256_BLOCKBYTES + 1]; + unsigned char out_manual[32]; + unsigned char out_impl[32]; + + for (i = 0; i < sizeof msg_rate_plus1; i++) { + msg_rate_plus1[i] = (unsigned char) i; + } + + shake256_manual_with_domain(out_manual, sizeof out_manual, msg_rate_plus1, + sizeof msg_rate_plus1, crypto_xof_shake256_domain_standard()); + + crypto_xof_shake256_init(&state); + crypto_xof_shake256_update(&state, msg_rate_plus1, 1); + crypto_xof_shake256_update(&state, msg_rate_plus1 + 1, crypto_xof_shake256_BLOCKBYTES - 1); + crypto_xof_shake256_update(&state, msg_rate_plus1 + crypto_xof_shake256_BLOCKBYTES, 1); + crypto_xof_shake256_squeeze(&state, out_impl, sizeof out_impl); + + if (memcmp(out_manual, out_impl, sizeof out_manual) != 0) { + printf("Chunked update boundary test failed\n"); + return 1; + } + } + printf("All SHAKE-256 tests passed\n"); return 0; } diff --git a/test/default/xof_turboshake128.c b/test/default/xof_turboshake128.c index c03f5ec1..82c5147a 100644 --- a/test/default/xof_turboshake128.c +++ b/test/default/xof_turboshake128.c @@ -210,6 +210,65 @@ main(void) } } + /* Test zero-length update and squeeze are no-ops */ + crypto_xof_turboshake128_init(&state); + crypto_xof_turboshake128_update(&state, msg_abc, 1); + crypto_xof_turboshake128_update(&state, msg_abc, 0); + crypto_xof_turboshake128_update(&state, msg_abc + 1, 2); + crypto_xof_turboshake128_squeeze(&state, out, 0); + crypto_xof_turboshake128_squeeze(&state, out, 32); + if (memcmp(out, out_abc_32, 32) != 0) { + printf("Zero-length update/squeeze test failed\n"); + return 1; + } + + /* Test rate-sized input and long output (padding after full block) */ + { + unsigned char out_manual[crypto_xof_turboshake128_BLOCKBYTES + 7]; + unsigned char out_impl[crypto_xof_turboshake128_BLOCKBYTES + 7]; + + turboshake128_manual_with_domain(out_manual, sizeof out_manual, msg_rate_block, + sizeof msg_rate_block, + crypto_xof_turboshake128_domain_standard()); + + crypto_xof_turboshake128_init(&state); + crypto_xof_turboshake128_update(&state, msg_rate_block, sizeof msg_rate_block); + crypto_xof_turboshake128_squeeze(&state, out_impl, sizeof out_impl); + + if (memcmp(out_manual, out_impl, sizeof out_manual) != 0) { + printf("Rate block long output test failed\n"); + return 1; + } + } + + /* Test chunked update across block boundary */ + { + unsigned char msg_rate_plus1[crypto_xof_turboshake128_BLOCKBYTES + 1]; + unsigned char out_manual[32]; + unsigned char out_impl[32]; + + for (i = 0; i < sizeof msg_rate_plus1; i++) { + msg_rate_plus1[i] = (unsigned char) i; + } + + turboshake128_manual_with_domain(out_manual, sizeof out_manual, msg_rate_plus1, + sizeof msg_rate_plus1, + crypto_xof_turboshake128_domain_standard()); + + crypto_xof_turboshake128_init(&state); + crypto_xof_turboshake128_update(&state, msg_rate_plus1, 1); + crypto_xof_turboshake128_update(&state, msg_rate_plus1 + 1, + crypto_xof_turboshake128_BLOCKBYTES - 1); + crypto_xof_turboshake128_update(&state, + msg_rate_plus1 + crypto_xof_turboshake128_BLOCKBYTES, 1); + crypto_xof_turboshake128_squeeze(&state, out_impl, sizeof out_impl); + + if (memcmp(out_manual, out_impl, sizeof out_manual) != 0) { + printf("Chunked update boundary test failed\n"); + return 1; + } + } + printf("All TurboSHAKE-128 tests passed\n"); return 0; } diff --git a/test/default/xof_turboshake256.c b/test/default/xof_turboshake256.c index 43c9ab0d..e8039be3 100644 --- a/test/default/xof_turboshake256.c +++ b/test/default/xof_turboshake256.c @@ -210,6 +210,65 @@ main(void) } } + /* Test zero-length update and squeeze are no-ops */ + crypto_xof_turboshake256_init(&state); + crypto_xof_turboshake256_update(&state, msg_abc, 1); + crypto_xof_turboshake256_update(&state, msg_abc, 0); + crypto_xof_turboshake256_update(&state, msg_abc + 1, 2); + crypto_xof_turboshake256_squeeze(&state, out, 0); + crypto_xof_turboshake256_squeeze(&state, out, 32); + if (memcmp(out, out_abc_32, 32) != 0) { + printf("Zero-length update/squeeze test failed\n"); + return 1; + } + + /* Test rate-sized input and long output (padding after full block) */ + { + unsigned char out_manual[crypto_xof_turboshake256_BLOCKBYTES + 7]; + unsigned char out_impl[crypto_xof_turboshake256_BLOCKBYTES + 7]; + + turboshake256_manual_with_domain(out_manual, sizeof out_manual, msg_rate_block, + sizeof msg_rate_block, + crypto_xof_turboshake256_domain_standard()); + + crypto_xof_turboshake256_init(&state); + crypto_xof_turboshake256_update(&state, msg_rate_block, sizeof msg_rate_block); + crypto_xof_turboshake256_squeeze(&state, out_impl, sizeof out_impl); + + if (memcmp(out_manual, out_impl, sizeof out_manual) != 0) { + printf("Rate block long output test failed\n"); + return 1; + } + } + + /* Test chunked update across block boundary */ + { + unsigned char msg_rate_plus1[crypto_xof_turboshake256_BLOCKBYTES + 1]; + unsigned char out_manual[32]; + unsigned char out_impl[32]; + + for (i = 0; i < sizeof msg_rate_plus1; i++) { + msg_rate_plus1[i] = (unsigned char) i; + } + + turboshake256_manual_with_domain(out_manual, sizeof out_manual, msg_rate_plus1, + sizeof msg_rate_plus1, + crypto_xof_turboshake256_domain_standard()); + + crypto_xof_turboshake256_init(&state); + crypto_xof_turboshake256_update(&state, msg_rate_plus1, 1); + crypto_xof_turboshake256_update(&state, msg_rate_plus1 + 1, + crypto_xof_turboshake256_BLOCKBYTES - 1); + crypto_xof_turboshake256_update(&state, + msg_rate_plus1 + crypto_xof_turboshake256_BLOCKBYTES, 1); + crypto_xof_turboshake256_squeeze(&state, out_impl, sizeof out_impl); + + if (memcmp(out_manual, out_impl, sizeof out_manual) != 0) { + printf("Chunked update boundary test failed\n"); + return 1; + } + } + printf("All TurboSHAKE-256 tests passed\n"); return 0; }