From 24b84738ea328ac9d29e26a4c952e2d305d42d44 Mon Sep 17 00:00:00 2001 From: Loup Vaillant Date: Mon, 5 Dec 2022 19:35:37 +0100 Subject: [PATCH] Reorganised tests Separating by primitive/functionality makes it easier to find my tests. Also, asserts makes things simpler. --- tests/test.c | 1283 +++++++++++++++++++++++-------------------------- tests/utils.c | 9 +- tests/utils.h | 21 +- 3 files changed, 626 insertions(+), 687 deletions(-) diff --git a/tests/test.c b/tests/test.c index e16530b..b47038b 100644 --- a/tests/test.c +++ b/tests/test.c @@ -57,14 +57,54 @@ #include "utils.h" #include "vectors.h" -#define CHACHA_BLOCK_SIZE 64 -#define POLY1305_BLOCK_SIZE 16 -#define BLAKE2B_BLOCK_SIZE 128 -#define SHA_512_BLOCK_SIZE 128 - -//////////////////////////// -/// Tests aginst vectors /// -//////////////////////////// +#define VECTORS(n) ASSERT_OK(vector_test(n, #n, nb_##n##_vectors, n##_vectors)) + +//////////////////////////////// +/// Constant time comparison /// +//////////////////////////////// +static void p_verify(size_t size, int (*compare)(const u8*, const u8*)) +{ + printf("\tcrypto_verify%zu\n", size); + u8 a[64]; // size <= 64 + u8 b[64]; // size <= 64 + FOR (i, 0, 2) { + FOR (j, 0, 2) { + // Set every byte to the chosen value, then compare + FOR (k, 0, size) { + a[k] = (u8)i; + b[k] = (u8)j; + } + int cmp = compare(a, b); + if (i == j) { ASSERT(cmp == 0); } + else { ASSERT(cmp != 0); } + // Set only two bytes to the chosen value, then compare + FOR (k, 0, size / 2) { + FOR (l, 0, size) { + a[l] = 0; + b[l] = 0; + } + a[k] = (u8)i; a[k + size/2 - 1] = (u8)i; + b[k] = (u8)j; b[k + size/2 - 1] = (u8)j; + cmp = compare(a, b); + if (i == j) { ASSERT(cmp == 0); } + else { ASSERT(cmp != 0); } + } + } + } +} + +static void test_verify() +{ + p_verify(16, crypto_verify16); + p_verify(32, crypto_verify32); + p_verify(64, crypto_verify64); +} + +//////////////// +/// Chacha20 /// +//////////////// +#define CHACHA_BLOCK_SIZE 64 + static void chacha20(vector_reader *reader) { vector key = next_input(reader); @@ -97,14 +137,6 @@ static void ietf_chacha20(vector_reader *reader) } } -static void hchacha20(vector_reader *reader) -{ - vector key = next_input(reader); - vector nonce = next_input(reader); - vector out = next_output(reader); - crypto_hchacha20(out.buf, key.buf, nonce.buf); -} - static void xchacha20(vector_reader *reader) { vector key = next_input(reader); @@ -121,303 +153,33 @@ static void xchacha20(vector_reader *reader) } } -static void poly1305(vector_reader *reader) -{ - vector key = next_input(reader); - vector msg = next_input(reader); - vector out = next_output(reader); - crypto_poly1305(out.buf, msg.buf, msg.size, key.buf); -} - -static void aead_ietf(vector_reader *reader) +static void hchacha20(vector_reader *reader) { vector key = next_input(reader); vector nonce = next_input(reader); - vector ad = next_input(reader); - vector text = next_input(reader); vector out = next_output(reader); - crypto_lock_aead(out.buf, out.buf + 16, key.buf, nonce.buf, - ad.buf, ad.size, text.buf, text.size); -} - - -static void blake2b(vector_reader *reader) -{ - vector msg = next_input(reader); - vector key = next_input(reader); - vector out = next_output(reader); - crypto_blake2b_general(out.buf, out.size, - key.buf, key.size, - msg.buf, msg.size); -} - -static void sha512(vector_reader *reader) -{ - vector in = next_input(reader); - vector out = next_output(reader); - crypto_sha512(out.buf, in.buf, in.size); -} - -static void hmac_sha512(vector_reader *reader) -{ - vector key = next_input(reader); - vector msg = next_input(reader); - vector out = next_output(reader); - crypto_hmac_sha512(out.buf, key.buf, key.size, msg.buf, msg.size); -} - -static void argon2i(vector_reader *reader) -{ - u64 nb_blocks = load64_le(next_input(reader).buf); - u64 nb_iterations = load64_le(next_input(reader).buf); - vector password = next_input(reader); - vector salt = next_input(reader); - vector key = next_input(reader); - vector ad = next_input(reader); - vector out = next_output(reader); - void *work_area = alloc(nb_blocks * 1024); - crypto_argon2i_general(out.buf, (u32)out.size, - work_area, (u32)nb_blocks, (u32)nb_iterations, - password.buf, (u32)password.size, - salt .buf, (u32)salt .size, - key .buf, (u32)key .size, - ad .buf, (u32)ad .size); - free(work_area); -} - -static void x25519(vector_reader *reader) -{ - vector scalar = next_input(reader); - vector point = next_input(reader); - vector out = next_output(reader); - crypto_x25519(out.buf, scalar.buf, point.buf); -} - -static void x25519_pk(vector_reader *reader) -{ - vector in = next_input(reader); - vector out = next_output(reader); - crypto_x25519_public_key(out.buf, in.buf); -} - -static void edDSA(vector_reader *reader) -{ - vector secret_k = next_input(reader); - vector public_k = next_input(reader); - vector msg = next_input(reader); - vector out = next_output(reader); - u8 fat_secret_key[64]; - memcpy(fat_secret_key , secret_k.buf, 32); - memcpy(fat_secret_key + 32, public_k.buf, 32); - crypto_eddsa_sign(out.buf, fat_secret_key, msg.buf, msg.size); -} - -static void edDSA_pk(vector_reader *reader) -{ - vector in = next_input(reader); - vector out = next_output(reader); - u8 seed [32]; - u8 secret_key[64]; - u8 public_key[32]; - memcpy(seed, in.buf, 32); - crypto_eddsa_key_pair(secret_key, public_key, seed); - memcpy(out.buf, public_key, 32); - - u8 zeroes[32] = {0}; - if (memcmp(seed, zeroes, 32)) { - printf("FAILURE: seed has not been wiped\n"); - exit(1); - } - if (memcmp(secret_key, in.buf, 32)) { - printf("FAILURE: first half of secret key is not the seed\n"); - exit(1); - } - if (memcmp(secret_key + 32, public_key, 32)) { - printf("FAILURE: second half of secret key is not the public key\n"); - exit(1); - } -} - -static void ed_25519(vector_reader *reader) -{ - vector secret_k = next_input(reader); - vector public_k = next_input(reader); - vector msg = next_input(reader); - vector out = next_output(reader); - u8 fat_secret_key[64]; - memcpy(fat_secret_key , secret_k.buf, 32); - memcpy(fat_secret_key + 32, public_k.buf, 32); - crypto_ed25519_sign(out.buf, fat_secret_key, msg.buf, msg.size); -} - -static void ed_25519_pk(vector_reader *reader) -{ - vector in = next_input(reader); - vector out = next_output(reader); - u8 seed [32]; - u8 secret_key[64]; - u8 public_key[32]; - memcpy(seed, in.buf, 32); - crypto_ed25519_key_pair(secret_key, public_key, seed); - memcpy(out.buf, public_key, 32); - - u8 zeroes[32] = {0}; - if (memcmp(seed, zeroes, 32)) { - printf("FAILURE: seed has not been wiped\n"); - exit(1); - } - if (memcmp(secret_key, in.buf, 32)) { - printf("FAILURE: first half of secret key is not the seed\n"); - exit(1); - } - if (memcmp(secret_key + 32, public_key, 32)) { - printf("FAILURE: second half of secret key is not the public key\n"); - exit(1); - } -} - -static void ed_25519_check(vector_reader *reader) -{ - vector public_k = next_input(reader); - vector msg = next_input(reader); - vector sig = next_input(reader); - vector out = next_output(reader); - out.buf[0] = (u8)crypto_ed25519_check(sig.buf, public_k.buf, - msg.buf, msg.size); -} - -static void iterate_x25519(u8 k[32], u8 u[32]) -{ - u8 tmp[32]; - crypto_x25519(tmp , k, u); - memcpy(u, k , 32); - memcpy(k, tmp, 32); -} - -static int test_x25519() -{ - u8 _1 [32] = {0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, - 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7, 0x27, 0x9f, - 0x78, 0x97, 0xb8, 0x7b, 0xb6, 0x85, 0x4b, 0x78, - 0x3c, 0x60, 0xe8, 0x03, 0x11, 0xae, 0x30, 0x79}; - u8 k[32] = {9}; - u8 u[32] = {9}; - - crypto_x25519_public_key(k, u); - int status = memcmp(k, _1, 32); - printf("%s: x25519 1\n", status != 0 ? "FAILED" : "OK"); - - u8 _1k [32] = {0x68, 0x4c, 0xf5, 0x9b, 0xa8, 0x33, 0x09, 0x55, - 0x28, 0x00, 0xef, 0x56, 0x6f, 0x2f, 0x4d, 0x3c, - 0x1c, 0x38, 0x87, 0xc4, 0x93, 0x60, 0xe3, 0x87, - 0x5f, 0x2e, 0xb9, 0x4d, 0x99, 0x53, 0x2c, 0x51}; - FOR (i, 1, 1000) { iterate_x25519(k, u); } - status |= memcmp(k, _1k, 32); - printf("%s: x25519 1K\n", status != 0 ? "FAILED" : "OK"); - - // too long; didn't run - //u8 _1M[32] = {0x7c, 0x39, 0x11, 0xe0, 0xab, 0x25, 0x86, 0xfd, - // 0x86, 0x44, 0x97, 0x29, 0x7e, 0x57, 0x5e, 0x6f, - // 0x3b, 0xc6, 0x01, 0xc0, 0x88, 0x3c, 0x30, 0xdf, - // 0x5f, 0x4d, 0xd2, 0xd2, 0x4f, 0x66, 0x54, 0x24}; - //FOR (i, 1000, 1000000) { iterate_x25519(k, u); } - //status |= memcmp(k, _1M, 32); - //printf("%s: x25519 1M\n", status != 0 ? "FAILED" : "OK"); - return status; -} - -static void elligator_dir(vector_reader *reader) -{ - vector in = next_input(reader); - vector out = next_output(reader); - crypto_hidden_to_curve(out.buf, in.buf); -} - -static void elligator_inv(vector_reader *reader) -{ - vector point = next_input(reader); - u8 tweak = next_input(reader).buf[0]; - u8 failure = next_input(reader).buf[0]; - vector out = next_output(reader); - int check = crypto_curve_to_hidden(out.buf, point.buf, tweak); - if ((u8)check != failure) { - printf("Elligator inverse map: failure mismatch\n"); - exit(1); - } + crypto_hchacha20(out.buf, key.buf, nonce.buf); } -////////////////////////////// -/// Self consistency tests /// -////////////////////////////// -static int p_verify(size_t size, int (*compare)(const u8*, const u8*)) +static void test_chacha20() { - int status = 0; - u8 a[64]; // size <= 64 - u8 b[64]; // size <= 64 - FOR (i, 0, 2) { - FOR (j, 0, 2) { - // Set every byte to the chosen value, then compare - FOR (k, 0, size) { - a[k] = (u8)i; - b[k] = (u8)j; - } - int cmp = compare(a, b); - status |= (i == j ? cmp : ~cmp); - // Set only two bytes to the chosen value, then compare - FOR (k, 0, size / 2) { - FOR (l, 0, size) { - a[l] = 0; - b[l] = 0; - } - a[k] = (u8)i; a[k + size/2 - 1] = (u8)i; - b[k] = (u8)j; b[k + size/2 - 1] = (u8)j; - cmp = compare(a, b); - status |= (i == j ? cmp : ~cmp); - } - } + VECTORS(chacha20); + printf("\tChacha20 (ctr)\n"); + { + RANDOM_INPUT(key , 32); + RANDOM_INPUT(nonce, 24); + RANDOM_INPUT(plain, 128); + u8 out_full[128]; + u8 out1 [64]; + u8 out2 [64]; + crypto_chacha20 (out_full, plain , 128, key, nonce); + crypto_chacha20_ctr(out1 , plain + 0, 64, key, nonce, 0); + crypto_chacha20_ctr(out2 , plain + 64, 64, key, nonce, 1); + ASSERT_EQUAL(out_full , out1, 64); + ASSERT_EQUAL(out_full + 64, out2, 64); } - printf("%s: crypto_verify%zu\n", status != 0 ? "FAILED" : "OK", size); - return status; -} -static int p_verify16(){ return p_verify(16, crypto_verify16); } -static int p_verify32(){ return p_verify(32, crypto_verify32); } -static int p_verify64(){ return p_verify(64, crypto_verify64); } - -static int p_chacha20_ctr() -{ - int status = 0; - RANDOM_INPUT(key , 32); - RANDOM_INPUT(nonce, 24); - RANDOM_INPUT(plain, 128); - u8 out_full[128]; - u8 out1 [64]; - u8 out2 [64]; - crypto_chacha20 (out_full, plain , 128, key, nonce); - crypto_chacha20_ctr(out1 , plain + 0, 64, key, nonce, 0); - crypto_chacha20_ctr(out2 , plain + 64, 64, key, nonce, 1); - status |= memcmp(out_full , out1, 64); - status |= memcmp(out_full + 64, out2, 64); - - crypto_ietf_chacha20 (out_full, plain , 128, key, nonce); - crypto_ietf_chacha20_ctr(out1 , plain + 0, 64, key, nonce, 0); - crypto_ietf_chacha20_ctr(out2 , plain + 64, 64, key, nonce, 1); - status |= memcmp(out_full , out1, 64); - status |= memcmp(out_full + 64, out2, 64); - - crypto_xchacha20 (out_full, plain , 128, key, nonce); - crypto_xchacha20_ctr(out1 , plain + 0, 64, key, nonce, 0); - crypto_xchacha20_ctr(out2 , plain + 64, 64, key, nonce, 1); - status |= memcmp(out_full , out1, 64); - status |= memcmp(out_full + 64, out2, 64); - - printf("%s: Chacha20 (ctr)\n", status != 0 ? "FAILED" : "OK"); - return status; -} -// Tests that Chacha20(nullptr) == Chacha20(all-zeroes) -static int p_chacha20_stream() -{ - int status = 0; + printf("\tChacha20 (nullptr == zeroes)\n"); #define INPUT_SIZE (CHACHA_BLOCK_SIZE * 2 + 1) FOR (i, 0, INPUT_SIZE) { u8 output_normal[INPUT_SIZE]; @@ -427,32 +189,56 @@ static int p_chacha20_stream() RANDOM_INPUT(nonce, 8); crypto_chacha20(output_normal, zeroes, i, key, nonce); crypto_chacha20(output_stream, 0 , i, key, nonce); - status |= memcmp(output_normal, output_stream, i); + ASSERT_EQUAL(output_normal, output_stream, i); } - printf("%s: Chacha20 (nullptr == zeroes)\n", status != 0 ? "FAILED" : "OK"); - return status; -} -// Tests that output and input can be the same pointer -static int p_chacha20_same_ptr() -{ + printf("\tChacha20 (output == input)\n"); + { #undef INPUT_SIZE #define INPUT_SIZE (CHACHA_BLOCK_SIZE * 4) // total input size - int status = 0; - u8 output[INPUT_SIZE]; - RANDOM_INPUT(input, INPUT_SIZE); - RANDOM_INPUT(key , 32); - RANDOM_INPUT(nonce, 8); - crypto_chacha20(output, input, INPUT_SIZE, key, nonce); - crypto_chacha20(input , input, INPUT_SIZE, key, nonce); - status |= memcmp(output, input, INPUT_SIZE); - printf("%s: Chacha20 (output == input)\n", status != 0 ? "FAILED" : "OK"); - return status; -} + u8 output[INPUT_SIZE]; + RANDOM_INPUT(input, INPUT_SIZE); + RANDOM_INPUT(key , 32); + RANDOM_INPUT(nonce, 8); + crypto_chacha20(output, input, INPUT_SIZE, key, nonce); + crypto_chacha20(input , input, INPUT_SIZE, key, nonce); + ASSERT_EQUAL(output, input, INPUT_SIZE); + } -static int p_hchacha20() -{ - int status = 0; + VECTORS(ietf_chacha20); + printf("\tietf Chacha20 (ctr)\n"); + { + RANDOM_INPUT(key , 32); + RANDOM_INPUT(nonce, 24); + RANDOM_INPUT(plain, 128); + u8 out_full[128]; + u8 out1 [64]; + u8 out2 [64]; + crypto_ietf_chacha20 (out_full, plain , 128, key, nonce); + crypto_ietf_chacha20_ctr(out1 , plain + 0, 64, key, nonce, 0); + crypto_ietf_chacha20_ctr(out2 , plain + 64, 64, key, nonce, 1); + ASSERT_EQUAL(out_full , out1, 64); + ASSERT_EQUAL(out_full + 64, out2, 64); + } + + VECTORS(xchacha20); + printf("\tXChacha20 (ctr)\n"); + { + RANDOM_INPUT(key , 32); + RANDOM_INPUT(nonce, 24); + RANDOM_INPUT(plain, 128); + u8 out_full[128]; + u8 out1 [64]; + u8 out2 [64]; + crypto_xchacha20 (out_full, plain , 128, key, nonce); + crypto_xchacha20_ctr(out1 , plain + 0, 64, key, nonce, 0); + crypto_xchacha20_ctr(out2 , plain + 64, 64, key, nonce, 1); + ASSERT_EQUAL(out_full , out1, 64); + ASSERT_EQUAL(out_full + 64, out2, 64); + } + + VECTORS(hchacha20); + printf("\tHChacha20 (overlap)\n"); FOR (i, 0, 100) { RANDOM_INPUT(buffer, 80); size_t out_idx = rand64() % 48; @@ -465,19 +251,31 @@ static int p_hchacha20() u8 out[32]; crypto_hchacha20(out, key, in); crypto_hchacha20(buffer + out_idx, buffer + key_idx, buffer + in_idx); - status |= memcmp(out, buffer + out_idx, 32); + ASSERT_EQUAL(out, buffer + out_idx, 32); } - printf("%s: HChacha20 (overlap)\n", status != 0 ? "FAILED" : "OK"); - return status; } -// Tests that authenticating bit by bit yields the same mac than -// authenticating all at once -static int p_poly1305() + +///////////////// +/// Poly 1305 /// +///////////////// +#define POLY1305_BLOCK_SIZE 16 + +static void poly1305(vector_reader *reader) { + vector key = next_input(reader); + vector msg = next_input(reader); + vector out = next_output(reader); + crypto_poly1305(out.buf, msg.buf, msg.size, key.buf); +} + +static void test_poly1305() +{ + VECTORS(poly1305); + + printf("\tPoly1305 (incremental)\n"); #undef INPUT_SIZE #define INPUT_SIZE (POLY1305_BLOCK_SIZE * 4) // total input size - int status = 0; FOR (i, 0, INPUT_SIZE) { // outputs u8 mac_chunk[16]; @@ -496,40 +294,98 @@ static int p_poly1305() // Authenticate all at once crypto_poly1305(mac_whole, input, INPUT_SIZE, key); - // Compare the results (must be the same) - status |= memcmp(mac_chunk, mac_whole, 16); + // Compare the results + ASSERT_EQUAL(mac_chunk, mac_whole, 16); } - printf("%s: Poly1305 (incremental)\n", status != 0 ? "FAILED" : "OK"); - return status; -} -// Tests that the input and output buffers of poly1305 can overlap. -static int p_poly1305_overlap() -{ + printf("\tPoly1305 (overlapping i/o)\n"); #undef INPUT_SIZE #define INPUT_SIZE (POLY1305_BLOCK_SIZE + (2 * 16)) // total input size - int status = 0; FOR (i, 0, POLY1305_BLOCK_SIZE + 16) { RANDOM_INPUT(input, INPUT_SIZE); RANDOM_INPUT(key , 32); u8 mac [16]; crypto_poly1305(mac , input + 16, POLY1305_BLOCK_SIZE, key); crypto_poly1305(input+i, input + 16, POLY1305_BLOCK_SIZE, key); - status |= memcmp(mac, input + i, 16); + ASSERT_EQUAL(mac, input + i, 16); } - printf("%s: Poly1305 (overlapping i/o)\n", status != 0 ? "FAILED" : "OK"); - return status; } -// Tests that hashing bit by bit yields the same hash than hashing all -// at once. Note: I figured we didn't need to test keyed mode, or -// different hash sizes, again. This test sticks to the simplified -// interface. -static int p_blake2b() +//////////////////////////////// +/// Authenticated encryption /// +//////////////////////////////// +static void aead_ietf(vector_reader *reader) { + vector key = next_input(reader); + vector nonce = next_input(reader); + vector ad = next_input(reader); + vector text = next_input(reader); + vector out = next_output(reader); + crypto_lock_aead(out.buf, out.buf + 16, key.buf, nonce.buf, + ad.buf, ad.size, text.buf, text.size); +} + +static void test_aead() +{ + VECTORS(aead_ietf); + + printf("\taead (roundtrip)\n"); + FOR (i, 0, 1000) { + RANDOM_INPUT(key , 32); + RANDOM_INPUT(nonce , 24); + RANDOM_INPUT(ad , 4); + RANDOM_INPUT(plaintext, 8); + u8 box[24], box2[24]; + u8 out[8]; + // AEAD roundtrip + crypto_lock_aead(box, box+16, key, nonce, ad, 4, plaintext, 8); + ASSERT_OK(crypto_unlock_aead(out, key, nonce, box, ad, 4, box+16, 8)); + ASSERT_EQUAL(plaintext, out, 8); + box[0]++; + ASSERT_KO(crypto_unlock_aead(out, key, nonce, box, ad, 4, box+16, 8)); + + // Authenticated roundtrip (easy interface) + // Make and accept message + crypto_lock(box, box + 16, key, nonce, plaintext, 8); + ASSERT_OK(crypto_unlock(out, key, nonce, box, box + 16, 8)); + // Make sure decrypted text and original text are the same + ASSERT_EQUAL(plaintext, out, 8); + // Make and reject forgery + box[0]++; + ASSERT_KO(crypto_unlock(out, key, nonce, box, box + 16, 8)); + box[0]--; // undo forgery + + // Same result for both interfaces + crypto_lock_aead(box2, box2 + 16, key, nonce, 0, 0, plaintext, 8); + ASSERT_EQUAL(box, box2, 24); + } +} + +/////////////// +/// Blake2b /// +/////////////// +#define BLAKE2B_BLOCK_SIZE 128 + +static void blake2b(vector_reader *reader) +{ + vector msg = next_input(reader); + vector key = next_input(reader); + vector out = next_output(reader); + crypto_blake2b_general(out.buf, out.size, + key.buf, key.size, + msg.buf, msg.size); +} + +static void test_blake2b() +{ + VECTORS(blake2b); + + printf("\tBLAKE2b (incremental)\n"); + // Note: I figured we didn't need to test keyed mode, or different + // hash sizes, a second time. This test sticks to the simplified + // interface. #undef INPUT_SIZE #define INPUT_SIZE (BLAKE2B_BLOCK_SIZE * 4 - 32) // total input size - int status = 0; FOR (i, 0, INPUT_SIZE) { // outputs u8 hash_chunk[64]; @@ -548,36 +404,40 @@ static int p_blake2b() crypto_blake2b(hash_whole, input, INPUT_SIZE); // Compare the results (must be the same) - status |= memcmp(hash_chunk, hash_whole, 64); + ASSERT_EQUAL(hash_chunk, hash_whole, 64); } - printf("%s: BLAKE2b (incremental)\n", status != 0 ? "FAILED" : "OK"); - return status; -} -// Tests that the input and output buffers of BLAKE2b can overlap. -static int p_blake2b_overlap() -{ + printf("\tBLAKE2b (overlapping i/o)\n"); #undef INPUT_SIZE #define INPUT_SIZE (BLAKE2B_BLOCK_SIZE + (2 * 64)) // total input size - int status = 0; FOR (i, 0, BLAKE2B_BLOCK_SIZE + 64) { u8 hash [64]; RANDOM_INPUT(input, INPUT_SIZE); crypto_blake2b(hash , input + 64, BLAKE2B_BLOCK_SIZE); crypto_blake2b(input+i, input + 64, BLAKE2B_BLOCK_SIZE); - status |= memcmp(hash, input + i, 64); + ASSERT_EQUAL(hash, input + i, 64); } - printf("%s: BLAKE2b (overlapping i/o)\n", status != 0 ? "FAILED" : "OK"); - return status; } -// Tests that hashing bit by bit yields the same hash than hashing all -// at once. (for sha512) -static int p_sha512() +/////////////// +/// SHA 512 /// +/////////////// +#define SHA_512_BLOCK_SIZE 128 + +static void sha512(vector_reader *reader) +{ + vector in = next_input(reader); + vector out = next_output(reader); + crypto_sha512(out.buf, in.buf, in.size); +} + +static void test_sha512() { + VECTORS(sha512); + + printf("\tSHA-512 (incremental)\n"); #undef INPUT_SIZE #define INPUT_SIZE (SHA_512_BLOCK_SIZE * 4 - 32) // total input size - int status = 0; FOR (i, 0, INPUT_SIZE) { // outputs u8 hash_chunk[64]; @@ -596,36 +456,41 @@ static int p_sha512() crypto_sha512(hash_whole, input, INPUT_SIZE); // Compare the results (must be the same) - status |= memcmp(hash_chunk, hash_whole, 64); + ASSERT_EQUAL(hash_chunk, hash_whole, 64); } - printf("%s: SHA-512 (incremental)\n", status != 0 ? "FAILED" : "OK"); - return status; -} -// Tests that the input and output buffers of crypto_sha_512 can overlap. -static int p_sha512_overlap() -{ + printf("\tSHA-512 (overlapping i/o)\n"); #undef INPUT_SIZE #define INPUT_SIZE (SHA_512_BLOCK_SIZE + (2 * 64)) // total input size - int status = 0; FOR (i, 0, SHA_512_BLOCK_SIZE + 64) { u8 hash [64]; RANDOM_INPUT(input, INPUT_SIZE); crypto_sha512(hash , input + 64, SHA_512_BLOCK_SIZE); crypto_sha512(input+i, input + 64, SHA_512_BLOCK_SIZE); - status |= memcmp(hash, input + i, 64); + ASSERT_EQUAL(hash, input + i, 64); } - printf("%s: SHA-512 (overlapping i/o)\n", status != 0 ? "FAILED" : "OK"); - return status; } -// Tests that hashing bit by bit yields the same hash than hashing all -// at once. (for hmac) -static int p_hmac_sha512() + +//////////////////// +/// HMAC SHA 512 /// +//////////////////// +static void hmac_sha512(vector_reader *reader) +{ + vector key = next_input(reader); + vector msg = next_input(reader); + vector out = next_output(reader); + crypto_hmac_sha512(out.buf, key.buf, key.size, msg.buf, msg.size); +} + +static void test_hmac_sha512() { + VECTORS(hmac_sha512); + + + printf("\tHMAC SHA-512 (incremental)\n"); #undef INPUT_SIZE #define INPUT_SIZE (SHA_512_BLOCK_SIZE * 4 - 32) // total input size - int status = 0; FOR (i, 0, INPUT_SIZE) { // outputs u8 hash_chunk[64]; @@ -645,50 +510,63 @@ static int p_hmac_sha512() crypto_hmac_sha512(hash_whole, key, 32, input, INPUT_SIZE); // Compare the results (must be the same) - status |= memcmp(hash_chunk, hash_whole, 64); + ASSERT_EQUAL(hash_chunk, hash_whole, 64); } - printf("%s: HMAC SHA-512 (incremental)\n", status != 0 ? "FAILED" : "OK"); - return status; -} -// Tests that the input and output buffers of HMAC can overlap. -static int p_hmac_sha512_overlap() -{ + printf("\tHMAC SHA-512 (overlapping i/o)\n"); #undef INPUT_SIZE #define INPUT_SIZE (SHA_512_BLOCK_SIZE + (2 * 64)) // total input size - int status = 0; FOR (i, 0, SHA_512_BLOCK_SIZE + 64) { u8 hash [64]; RANDOM_INPUT(key , 32); RANDOM_INPUT(input, INPUT_SIZE); crypto_hmac_sha512(hash , key, 32, input + 64, SHA_512_BLOCK_SIZE); crypto_hmac_sha512(input+i, key, 32, input + 64, SHA_512_BLOCK_SIZE); - status |= memcmp(hash, input + i, 64); + ASSERT_EQUAL(hash, input + i, 64); } - printf("%s: HMAC SHA-512 (overlapping i/o)\n", status != 0 ? "FAILED":"OK"); - return status; } -static int p_argon2i_easy() +/////////////// +/// Argon2i /// +/////////////// +static void argon2i(vector_reader *reader) { - int status = 0; - void *work_area = alloc(8 * 1024); - RANDOM_INPUT(password , 32); - RANDOM_INPUT(salt , 16); - u8 hash_general[32]; - u8 hash_easy [32]; - crypto_argon2i_general(hash_general, 32, work_area, 8, 1, - password, 32, salt, 16, 0, 0, 0, 0); - crypto_argon2i(hash_easy, 32, work_area, 8, 1, password, 32, salt, 16); - status |= memcmp(hash_general, hash_easy, 32); + u64 nb_blocks = load64_le(next_input(reader).buf); + u64 nb_iterations = load64_le(next_input(reader).buf); + vector password = next_input(reader); + vector salt = next_input(reader); + vector key = next_input(reader); + vector ad = next_input(reader); + vector out = next_output(reader); + void *work_area = alloc(nb_blocks * 1024); + crypto_argon2i_general(out.buf, (u32)out.size, + work_area, (u32)nb_blocks, (u32)nb_iterations, + password.buf, (u32)password.size, + salt .buf, (u32)salt .size, + key .buf, (u32)key .size, + ad .buf, (u32)ad .size); free(work_area); - printf("%s: Argon2i (easy interface)\n", status != 0 ? "FAILED" : "OK"); - return status; } -static int p_argon2i_overlap() +static void test_argon2i() { - int status = 0; + VECTORS(argon2i); + + printf("\tArgon2i (easy interface)\n"); + { + void *work_area = alloc(8 * 1024); + RANDOM_INPUT(password , 32); + RANDOM_INPUT(salt , 16); + u8 hash_general[32]; + u8 hash_easy [32]; + crypto_argon2i_general(hash_general, 32, work_area, 8, 1, + password, 32, salt, 16, 0, 0, 0, 0); + crypto_argon2i(hash_easy, 32, work_area, 8, 1, password, 32, salt, 16); + ASSERT_EQUAL(hash_general, hash_easy, 32); + free(work_area); + } + + printf("\tArgon2i (overlapping i/o)\n"); u8 *work_area = (u8*)alloc(8 * 1024); u8 *clean_work_area = (u8*)alloc(8 * 1024); FOR (i, 0, 10) { @@ -712,19 +590,79 @@ static int p_argon2i_overlap() work_area + salt_offset, 16, work_area + key_offset, 32, work_area + ad_offset, 32); - status |= memcmp(hash1, hash2, 32); + ASSERT_EQUAL(hash1, hash2, 32); } free(work_area); free(clean_work_area); - printf("%s: Argon2i (overlapping i/o)\n", status != 0 ? "FAILED" : "OK"); - return status; } -// Tests that the shared key and secret key buffers of crypto_x25519 can -// overlap. -static int p_x25519_overlap() +////////////// +/// X25519 /// +////////////// +static void x25519(vector_reader *reader) +{ + vector scalar = next_input(reader); + vector point = next_input(reader); + vector out = next_output(reader); + crypto_x25519(out.buf, scalar.buf, point.buf); +} + +static void x25519_pk(vector_reader *reader) +{ + vector in = next_input(reader); + vector out = next_output(reader); + crypto_x25519_public_key(out.buf, in.buf); +} + +static void iterate_x25519(u8 k[32], u8 u[32]) +{ + u8 tmp[32]; + crypto_x25519(tmp , k, u); + memcpy(u, k , 32); + memcpy(k, tmp, 32); +} + +static void test_x25519() { - int status = 0; + VECTORS(x25519); + VECTORS(x25519_pk); + + { + printf("\tx25519 1\n"); + u8 _1 [32] = { + 0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, + 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7, 0x27, 0x9f, + 0x78, 0x97, 0xb8, 0x7b, 0xb6, 0x85, 0x4b, 0x78, + 0x3c, 0x60, 0xe8, 0x03, 0x11, 0xae, 0x30, 0x79 + }; + u8 k[32] = {9}; + u8 u[32] = {9}; + crypto_x25519_public_key(k, u); + ASSERT_EQUAL(k, _1, 32); + + printf("\tx25519 1K\n"); + u8 _1k [32] = { + 0x68, 0x4c, 0xf5, 0x9b, 0xa8, 0x33, 0x09, 0x55, + 0x28, 0x00, 0xef, 0x56, 0x6f, 0x2f, 0x4d, 0x3c, + 0x1c, 0x38, 0x87, 0xc4, 0x93, 0x60, 0xe3, 0x87, + 0x5f, 0x2e, 0xb9, 0x4d, 0x99, 0x53, 0x2c, 0x51 + }; + FOR (i, 1, 1000) { iterate_x25519(k, u); } + ASSERT_EQUAL(k, _1k, 32); + + // too long; didn't run + //printf("\tx25519 1M\n"); + //u8 _1M[32] = { + // 0x7c, 0x39, 0x11, 0xe0, 0xab, 0x25, 0x86, 0xfd, + // 0x86, 0x44, 0x97, 0x29, 0x7e, 0x57, 0x5e, 0x6f, + // 0x3b, 0xc6, 0x01, 0xc0, 0x88, 0x3c, 0x30, 0xdf, + // 0x5f, 0x4d, 0xd2, 0xd2, 0x4f, 0x66, 0x54, 0x24 + //}; + //FOR (i, 1000, 1000000) { iterate_x25519(k, u); } + //ASSERT_EQUAL(k, _1M, 32); + } + + printf("\tx25519 (overlapping i/o)\n"); FOR (i, 0, 62) { u8 overlapping[94]; u8 separate[32]; @@ -733,16 +671,108 @@ static int p_x25519_overlap() memcpy(overlapping + 31, sk, 32); crypto_x25519(overlapping + i, overlapping + 31, pk); crypto_x25519(separate, sk, pk); - status |= memcmp(separate, overlapping + i, 32); + ASSERT_EQUAL(separate, overlapping + i, 32); + } + + printf("\tx25519_inverse\n"); + { + RANDOM_INPUT(b, 32); + u8 base[32]; // random point (cofactor is cleared). + crypto_x25519_public_key(base, b); + // check round trip + FOR (i, 0, 50) { + RANDOM_INPUT(sk, 32); + u8 pk [32]; + u8 blind[32]; + crypto_x25519(pk, sk, base); + crypto_x25519_inverse(blind, sk, pk); + ASSERT_EQUAL(blind, base, 32); + } + + // check cofactor clearing + // (Multiplying by a low order point yields zero + u8 low_order[4][32] = { + {0}, {1}, + {0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, + 0xb1, 0xd0, 0xb1, 0x55, 0x9c, 0x83, 0xef, 0x5b, + 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c, 0x8e, 0x86, + 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0x57,}, + {0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, + 0x16, 0x56, 0xe3, 0xfa, 0xf1, 0x9f, 0xc4, 0x6a, + 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd, + 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00,}, + }; + u8 zero[32] = {0}; + FOR (i, 0, 32) { + u8 blind[32]; + RANDOM_INPUT(sk, 32); + crypto_x25519_inverse(blind, sk, low_order[i%4]); + ASSERT_EQUAL(blind, zero, 32); + } + } + + printf("\tx25519 inverse (overlapping i/o)\n"); + FOR (i, 0, 62) { + u8 overlapping[94]; + u8 separate[32]; + RANDOM_INPUT(sk, 32); + RANDOM_INPUT(pk, 32); + memcpy(overlapping + 31, sk, 32); + crypto_x25519_inverse(overlapping + i, overlapping + 31, pk); + crypto_x25519_inverse(separate, sk, pk); + ASSERT_EQUAL(separate, overlapping + i, 32); + } +} + + +///////////// +/// EdDSA /// +///////////// +static void edDSA(vector_reader *reader) +{ + vector secret_k = next_input(reader); + vector public_k = next_input(reader); + vector msg = next_input(reader); + vector out = next_output(reader); + u8 fat_secret_key[64]; + memcpy(fat_secret_key , secret_k.buf, 32); + memcpy(fat_secret_key + 32, public_k.buf, 32); + crypto_eddsa_sign(out.buf, fat_secret_key, msg.buf, msg.size); +} + +static void edDSA_pk(vector_reader *reader) +{ + vector in = next_input(reader); + vector out = next_output(reader); + u8 seed [32]; + u8 secret_key[64]; + u8 public_key[32]; + memcpy(seed, in.buf, 32); + crypto_eddsa_key_pair(secret_key, public_key, seed); + memcpy(out.buf, public_key, 32); + + u8 zeroes[32] = {0}; + if (memcmp(seed, zeroes, 32)) { + printf("FAILURE: seed has not been wiped\n"); + exit(1); + } + if (memcmp(secret_key, in.buf, 32)) { + printf("FAILURE: first half of secret key is not the seed\n"); + exit(1); + } + if (memcmp(secret_key + 32, public_key, 32)) { + printf("FAILURE: second half of secret key is not the public key\n"); + exit(1); } - printf("%s: x25519 (overlapping i/o)\n", status != 0 ? "FAILED" : "OK"); - return status; } -static int p_eddsa_roundtrip() +static void test_edDSA() { + VECTORS(edDSA); + VECTORS(edDSA_pk); + + printf("\tEdDSA (roundtrip)\n"); #define MESSAGE_SIZE 30 - int status = 0; FOR (i, 0, MESSAGE_SIZE) { RANDOM_INPUT(message, MESSAGE_SIZE); RANDOM_INPUT(seed, 32); @@ -751,39 +781,34 @@ static int p_eddsa_roundtrip() u8 signature[64]; crypto_eddsa_key_pair(sk, pk, seed); crypto_eddsa_sign(signature, sk, message, i); - status |= crypto_eddsa_check(signature, pk, message, i); + ASSERT_OK(crypto_eddsa_check(signature, pk, message, i)); // reject forgeries u8 zero [64] = {0}; - status |= !crypto_eddsa_check(zero , pk, message, i); + ASSERT_KO(crypto_eddsa_check(zero , pk, message, i)); FOR (j, 0, 64) { u8 forgery[64]; memcpy(forgery, signature, 64); forgery[j] = signature[j] + 1; - status |= !crypto_eddsa_check(forgery, pk, message, i); + ASSERT_KO(crypto_eddsa_check(forgery, pk, message, i)); } } - printf("%s: EdDSA (roundtrip)\n", status != 0 ? "FAILED" : "OK"); - return status; -} -// Verifies that random signatures are all invalid. Uses random -// public keys to see what happens outside of the curve (it should -// yield an invalid signature). -static int p_eddsa_random() -{ - int status = 0; - FOR (i, 0, 100) { + printf("\tEdDSA (random)\n"); + { + // Verifies that random signatures are all invalid. Uses random + // public keys to see what happens outside of the curve (it should + // yield an invalid signature). + FOR (i, 0, 100) { + RANDOM_INPUT(message, MESSAGE_SIZE); + RANDOM_INPUT(pk, 32); + RANDOM_INPUT(signature , 64); + ASSERT_KO(crypto_eddsa_check(signature, pk, message, MESSAGE_SIZE)); + } + // Testing S == L (for code coverage) RANDOM_INPUT(message, MESSAGE_SIZE); RANDOM_INPUT(pk, 32); - RANDOM_INPUT(signature , 64); - status |= ~crypto_eddsa_check(signature, pk, message, MESSAGE_SIZE); - } - // Testing S == L (for code coverage) - RANDOM_INPUT(message, MESSAGE_SIZE); - RANDOM_INPUT(pk, 32); - static const u8 signature[64] = - { + static const u8 signature[64] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, @@ -791,16 +816,10 @@ static int p_eddsa_random() 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, }; - status |= ~crypto_eddsa_check(signature, pk, message, MESSAGE_SIZE); - - printf("%s: EdDSA (random)\n", status != 0 ? "FAILED" : "OK"); - return status; -} + ASSERT_KO(crypto_eddsa_check(signature, pk, message, MESSAGE_SIZE)); + } -// Tests that the input and output buffers of crypto_sign() can overlap. -static int p_eddsa_overlap() -{ - int status = 0; + printf("\tEdDSA (overlap)\n"); FOR(i, 0, MESSAGE_SIZE + 64) { #undef INPUT_SIZE #define INPUT_SIZE (MESSAGE_SIZE + (2 * 64)) // total input size @@ -812,52 +831,96 @@ static int p_eddsa_overlap() crypto_eddsa_key_pair(sk, pk, seed); crypto_eddsa_sign(signature, sk, input + 64, MESSAGE_SIZE); crypto_eddsa_sign(input+i , sk, input + 64, MESSAGE_SIZE); - status |= memcmp(signature, input + i, 64); + ASSERT_EQUAL(signature, input + i, 64); } - printf("%s: EdDSA (overlap)\n", status != 0 ? "FAILED" : "OK"); - return status; } -static int p_aead() +/////////////// +/// Ed25519 /// +/////////////// +static void ed_25519(vector_reader *reader) { - int status = 0; - FOR (i, 0, 1000) { - RANDOM_INPUT(key , 32); - RANDOM_INPUT(nonce , 24); - RANDOM_INPUT(ad , 4); - RANDOM_INPUT(plaintext, 8); - u8 box[24], box2[24]; - u8 out[8]; - // AEAD roundtrip - crypto_lock_aead(box, box+16, key, nonce, ad, 4, plaintext, 8); - status |= crypto_unlock_aead(out, key, nonce, box, ad, 4, box+16, 8); - status |= memcmp(plaintext, out, 8); - box[0]++; - status |= !crypto_unlock_aead(out, key, nonce, box, ad, 4, box+16, 8); + vector secret_k = next_input(reader); + vector public_k = next_input(reader); + vector msg = next_input(reader); + vector out = next_output(reader); + u8 fat_secret_key[64]; + memcpy(fat_secret_key , secret_k.buf, 32); + memcpy(fat_secret_key + 32, public_k.buf, 32); + crypto_ed25519_sign(out.buf, fat_secret_key, msg.buf, msg.size); +} - // Authenticated roundtrip (easy interface) - // Make and accept message - crypto_lock(box, box + 16, key, nonce, plaintext, 8); - status |= crypto_unlock(out, key, nonce, box, box + 16, 8); - // Make sure decrypted text and original text are the same - status |= memcmp(plaintext, out, 8); - // Make and reject forgery - box[0]++; - status |= !crypto_unlock(out, key, nonce, box, box + 16, 8); - box[0]--; // undo forgery +static void ed_25519_pk(vector_reader *reader) +{ + vector in = next_input(reader); + vector out = next_output(reader); + u8 seed [32]; + u8 secret_key[64]; + u8 public_key[32]; + memcpy(seed, in.buf, 32); + crypto_ed25519_key_pair(secret_key, public_key, seed); + memcpy(out.buf, public_key, 32); - // Same result for both interfaces - crypto_lock_aead(box2, box2 + 16, key, nonce, 0, 0, plaintext, 8); - status |= memcmp(box, box2, 24); + u8 zeroes[32] = {0}; + if (memcmp(seed, zeroes, 32)) { + printf("FAILURE: seed has not been wiped\n"); + exit(1); + } + if (memcmp(secret_key, in.buf, 32)) { + printf("FAILURE: first half of secret key is not the seed\n"); + exit(1); + } + if (memcmp(secret_key + 32, public_key, 32)) { + printf("FAILURE: second half of secret key is not the public key\n"); + exit(1); + } +} + +static void ed_25519_check(vector_reader *reader) +{ + vector public_k = next_input(reader); + vector msg = next_input(reader); + vector sig = next_input(reader); + vector out = next_output(reader); + out.buf[0] = (u8)crypto_ed25519_check(sig.buf, public_k.buf, + msg.buf, msg.size); +} + +static void test_ed25519() +{ + VECTORS(ed_25519); + VECTORS(ed_25519_pk); + VECTORS(ed_25519_check); +} + +///////////////// +/// Elligator /// +///////////////// +static void elligator_dir(vector_reader *reader) +{ + vector in = next_input(reader); + vector out = next_output(reader); + crypto_hidden_to_curve(out.buf, in.buf); +} + +static void elligator_inv(vector_reader *reader) +{ + vector point = next_input(reader); + u8 tweak = next_input(reader).buf[0]; + u8 failure = next_input(reader).buf[0]; + vector out = next_output(reader); + int check = crypto_curve_to_hidden(out.buf, point.buf, tweak); + if ((u8)check != failure) { + printf("Elligator inverse map: failure mismatch\n"); + exit(1); } - printf("%s: aead (roundtrip)\n", status != 0 ? "FAILED" : "OK"); - return status; } -// Elligator direct mapping must ignore the most significant bits -static int p_elligator_direct_msb() +static void test_elligator() { - int status = 0; + VECTORS(elligator_dir); + + printf("\telligator direct (msb)\n"); FOR (i, 0, 20) { RANDOM_INPUT(r, 32); u8 r1[32]; memcpy(r1, r, 32); r1[31] = (r[31] & 0x3f) | 0x00; @@ -869,18 +932,13 @@ static int p_elligator_direct_msb() u8 u2[32]; crypto_hidden_to_curve(u2, r2); u8 u3[32]; crypto_hidden_to_curve(u3, r3); u8 u4[32]; crypto_hidden_to_curve(u4, r4); - status |= memcmp(u, u1, 32); - status |= memcmp(u, u2, 32); - status |= memcmp(u, u3, 32); - status |= memcmp(u, u4, 32); + ASSERT_EQUAL(u, u1, 32); + ASSERT_EQUAL(u, u2, 32); + ASSERT_EQUAL(u, u3, 32); + ASSERT_EQUAL(u, u4, 32); } - printf("%s: elligator direct (msb)\n", status != 0 ? "FAILED" : "OK"); - return status; -} -static int p_elligator_direct_overlap() -{ - int status = 0; + printf("\telligator direct (overlapping i/o)\n"); FOR (i, 0, 62) { u8 overlapping[94]; u8 separate[32]; @@ -888,16 +946,12 @@ static int p_elligator_direct_overlap() memcpy(overlapping + 31, r, 32); crypto_hidden_to_curve(overlapping + i, overlapping + 31); crypto_hidden_to_curve(separate, r); - status |= memcmp(separate, overlapping + i, 32); + ASSERT_EQUAL(separate, overlapping + i, 32); } - printf("%s: elligator direct (overlapping i/o)\n", - status != 0 ? "FAILED" : "OK"); - return status; -} -static int p_elligator_inverse_overlap() -{ - int status = 0; + VECTORS(elligator_inv); + + printf("\telligator inverse (overlapping i/o)\n"); FOR (i, 0, 62) { u8 overlapping[94]; u8 separate[32]; @@ -906,20 +960,14 @@ static int p_elligator_inverse_overlap() memcpy(overlapping + 31, pk, 32); int a = crypto_curve_to_hidden(overlapping+i, overlapping+31, tweak); int b = crypto_curve_to_hidden(separate, pk, tweak); - status |= a - b; + ASSERT(a == b); if (a == 0) { // The buffers are the same only if written to to begin with - status |= memcmp(separate, overlapping + i, 32); + ASSERT_EQUAL(separate, overlapping + i, 32); } } - printf("%s: elligator inverse (overlapping i/o)\n", - status != 0 ? "FAILED" : "OK"); - return status; -} -static int p_elligator_x25519() -{ - int status = 0; + printf("\telligator x25519\n"); int i = 0; while (i < 64) { RANDOM_INPUT(sk1, 32); @@ -932,16 +980,16 @@ static int p_elligator_x25519() u8 pk1 [32]; crypto_x25519_public_key (pk1 , sk1); // Both dirty functions behave the same - status |= memcmp(pks, pkf, 32); + ASSERT_EQUAL(pks, pkf, 32); // Dirty functions behave cleanly if we clear the 3 lsb first - status |= memcmp(pksc, pk1, 32); - status |= memcmp(pkfc, pk1, 32); + ASSERT_EQUAL(pksc, pk1, 32); + ASSERT_EQUAL(pkfc, pk1, 32); // Dirty functions behave the same as the clean one if the lsb // are 0, differently if it is not - if ((sk1[0] & 7) == 0) { status |= memcmp(pk1, pkf, 32); } - else { status |= memcmp(pk1, pkf, 32) == 0; } + if ((sk1[0] & 7) == 0) { ASSERT_EQUAL (pk1, pkf, 32); } + else { ASSERT_DIFFERENT(pk1, pkf, 32); } // Maximise tweak diversity. // We want to set the bits 1 (sign) and 6-7 (padding) @@ -951,25 +999,20 @@ static int p_elligator_x25519() continue; // retry untill success (doesn't increment the tweak) } // Verify that the tweak's msb are copied to the representative - status |= (tweak >> 6) ^ (r[31] >> 6); + ASSERT((tweak >> 6) == (r[31] >> 6)); // Round trip u8 pkr[32]; crypto_hidden_to_curve(pkr, r); - status |= memcmp(pkr, pkf, 32); + ASSERT_EQUAL(pkr, pkf, 32); // Dirty and safe keys are compatible u8 e1 [32]; crypto_x25519(e1, sk2, pk1); u8 e2 [32]; crypto_x25519(e2, sk2, pkr); - status |= memcmp(e1, e2, 32); + ASSERT_EQUAL(e1, e2, 32); i++; } - printf("%s: elligator x25519\n", status != 0 ? "FAILED" : "OK"); - return status; -} -static int p_elligator_key_pair() -{ - int status = 0; + printf("\telligator key pair\n"); FOR(i, 0, 32) { RANDOM_INPUT(seed, 32); RANDOM_INPUT(sk2 , 32); @@ -979,16 +1022,10 @@ static int p_elligator_key_pair() u8 pk1[32]; crypto_x25519_public_key(pk1, sk1); u8 e1 [32]; crypto_x25519(e1, sk2, pk1); u8 e2 [32]; crypto_x25519(e2, sk2, pkr); - status |= memcmp(e1, e2, 32); + ASSERT_EQUAL(e1, e2, 32); } - printf("%s: elligator key pair\n", status != 0 ? "FAILED" : "OK"); - return status; -} - -static int p_elligator_key_pair_overlap() -{ - int status = 0; + printf("\telligator key pair (overlapping i/o)\n"); FOR (i, 0, 94) { u8 over[158]; u8 sep [ 64]; @@ -997,75 +1034,17 @@ static int p_elligator_key_pair_overlap() memcpy(s2, s1, 32); crypto_hidden_key_pair(sep , sep + 32, s1); crypto_hidden_key_pair(over + i, over + i + 32, s2); - status |= memcmp(sep, over + i, 64); - } - - printf("%s: elligator key pair (overlapping i/o)\n", - status != 0 ? "FAILED" : "OK"); - return status; -} - -static int p_x25519_inverse() -{ - int status = 0; - RANDOM_INPUT(b, 32); - u8 base[32]; // random point (cofactor is cleared). - crypto_x25519_public_key(base, b); - // check round trip - FOR (i, 0, 50) { - RANDOM_INPUT(sk, 32); - u8 pk [32]; - u8 blind[32]; - crypto_x25519(pk, sk, base); - crypto_x25519_inverse(blind, sk, pk); - status |= memcmp(blind, base, 32); - } - - // check cofactor clearing - // (Multiplying by a low order point yields zero - u8 low_order[4][32] = { - {0}, {1}, - {0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, - 0xb1, 0xd0, 0xb1, 0x55, 0x9c, 0x83, 0xef, 0x5b, - 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c, 0x8e, 0x86, - 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0x57,}, - {0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, - 0x16, 0x56, 0xe3, 0xfa, 0xf1, 0x9f, 0xc4, 0x6a, - 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd, - 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00,}, - }; - u8 zero[32] = {0}; - FOR (i, 0, 32) { - u8 blind[32]; - RANDOM_INPUT(sk, 32); - crypto_x25519_inverse(blind, sk, low_order[i%4]); - status |= memcmp(blind, zero, 32); + ASSERT_EQUAL(sep, over + i, 64); } - printf("%s: x25519_inverse\n", status != 0 ? "FAILED" : "OK"); - return status; } -static int p_x25519_inverse_overlap() -{ - int status = 0; - FOR (i, 0, 62) { - u8 overlapping[94]; - u8 separate[32]; - RANDOM_INPUT(sk, 32); - RANDOM_INPUT(pk, 32); - memcpy(overlapping + 31, sk, 32); - crypto_x25519_inverse(overlapping + i, overlapping + 31, pk); - crypto_x25519_inverse(separate, sk, pk); - status |= memcmp(separate, overlapping + i, 32); - } - printf("%s: x25519 inverse (overlapping i/o)\n", - status != 0 ? "FAILED" : "OK"); - return status; -} -static int p_from_eddsa() +//////////////////////// +/// X25519 <-> EdDSA /// +//////////////////////// +static void test_conversions() { - int status = 0; + printf("\tfrom_eddsa\n"); FOR (i, 0, 32) { RANDOM_INPUT(ed_seed, 32); u8 secret [64]; @@ -1073,15 +1052,10 @@ static int p_from_eddsa() u8 x_private[32]; crypto_from_eddsa_private(x_private, secret); u8 x_public1[32]; crypto_from_eddsa_public (x_public1, public); u8 x_public2[32]; crypto_x25519_public_key (x_public2, x_private); - status |= memcmp(x_public1, x_public2, 32); + ASSERT_EQUAL(x_public1, x_public2, 32); } - printf("%s: from_eddsa\n", status != 0 ? "FAILED" : "OK"); - return status; -} -static int p_from_ed25519() -{ - int status = 0; + printf("\tfrom_ed25519\n"); FOR (i, 0, 32) { RANDOM_INPUT(ed_seed, 32); u8 secret [64]; @@ -1089,81 +1063,44 @@ static int p_from_ed25519() u8 x_private[32]; crypto_from_ed25519_private(x_private, secret); u8 x_public1[32]; crypto_from_ed25519_public (x_public1, public); u8 x_public2[32]; crypto_x25519_public_key (x_public2, x_private); - status |= memcmp(x_public1, x_public2, 32); + ASSERT_EQUAL(x_public1, x_public2, 32); } - printf("%s: from_ed25519\n", status != 0 ? "FAILED" : "OK"); - return status; } -int vector_test(void (*f)(vector_reader*), - const char *name, size_t nb_vectors, const char *vectors[]); - -#define TEST(name) vector_test(name, #name, nb_##name##_vectors, name##_vectors) - int main(int argc, char *argv[]) { if (argc > 1) { sscanf(argv[1], "%" PRIu64 "", &random_state); } - printf("\nRandom seed: %" PRIu64 "\n", random_state); - - int status = 0; - printf("\nTest against vectors"); - printf("\n--------------------\n"); - status |= TEST(chacha20); - status |= TEST(ietf_chacha20); - status |= TEST(hchacha20); - status |= TEST(xchacha20); - status |= TEST(poly1305); - status |= TEST(aead_ietf); - status |= TEST(blake2b); - status |= TEST(sha512); - status |= TEST(hmac_sha512); - status |= TEST(argon2i); - status |= TEST(x25519); - status |= TEST(x25519_pk); - status |= TEST(edDSA); - status |= TEST(edDSA_pk); - status |= TEST(ed_25519); - status |= TEST(ed_25519_pk); - status |= TEST(ed_25519_check); - status |= TEST(elligator_dir); - status |= TEST(elligator_inv); - status |= test_x25519(); - printf("\nProperty based tests"); - printf("\n--------------------\n"); - status |= p_verify16(); - status |= p_verify32(); - status |= p_verify64(); - status |= p_chacha20_ctr(); - status |= p_chacha20_stream(); - status |= p_chacha20_same_ptr(); - status |= p_hchacha20(); - status |= p_poly1305(); - status |= p_poly1305_overlap(); - status |= p_blake2b(); - status |= p_blake2b_overlap(); - status |= p_sha512(); - status |= p_sha512_overlap(); - status |= p_hmac_sha512(); - status |= p_hmac_sha512_overlap(); - status |= p_argon2i_easy(); - status |= p_argon2i_overlap(); - status |= p_x25519_overlap(); - status |= p_eddsa_roundtrip(); - status |= p_eddsa_random(); - status |= p_eddsa_overlap(); - status |= p_aead(); - status |= p_elligator_direct_msb(); - status |= p_elligator_direct_overlap(); - status |= p_elligator_inverse_overlap(); - status |= p_elligator_x25519(); - status |= p_elligator_key_pair(); - status |= p_elligator_key_pair_overlap(); - status |= p_x25519_inverse(); - status |= p_x25519_inverse_overlap(); - status |= p_from_eddsa(); - status |= p_from_ed25519(); - printf("\n%s\n\n", status != 0 ? "SOME TESTS FAILED" : "All tests OK!"); - return status; + printf("\nRandom seed = %" PRIu64 "\n\n", random_state); + + printf("Comparisons:\n"); + test_verify(); + + printf("Encryption:\n"); + test_chacha20(); + test_aead(); + + printf("Hashes:\n"); + test_poly1305(); + test_blake2b(); + test_sha512(); + test_hmac_sha512(); + test_argon2i(); + + printf("X25519:\n"); + test_x25519(); + + printf("EdDSA:\n"); + test_edDSA(); + test_ed25519(); + + printf("Elligator:\n"); + test_elligator(); + + printf("Curve25519 conversions:\n"); + test_conversions(); + + printf("\nAll tests OK!\n"); + return 0; } diff --git a/tests/utils.c b/tests/utils.c index f55f11e..f7235f0 100644 --- a/tests/utils.c +++ b/tests/utils.c @@ -185,8 +185,9 @@ vector next_output(vector_reader *reader) int vector_test(void (*f)(vector_reader*), const char *name, size_t nb_vectors, const char *vectors[]) { - int status = 0; - int nb_tests = 0; + int status = 0; + printf("\t%s\n", name); + vector_reader in; in.size = nb_vectors; in.next = vectors; @@ -202,10 +203,6 @@ int vector_test(void (*f)(vector_reader*), } free(in.out.buf); free(in.expected.buf); - nb_tests++; } - printf("%s %4d tests: %s\n", - status != 0 ? "FAILED" : "OK", nb_tests, name); return status; } - diff --git a/tests/utils.h b/tests/utils.h index 30db972..d0f5e6f 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -63,14 +63,19 @@ typedef int64_t i64; typedef uint64_t u64; #define FOR(i, start, end) for (size_t i = (start); i < (end); i++) -#define SODIUM_INIT \ - do { \ - if (sodium_init() == -1) { \ - printf("libsodium init failed. Abort.\n"); \ - return 1; \ +#define SODIUM_INIT ASSERT(sodium_init() != -1) +#define RANDOM_INPUT(name, size) u8 name[size]; p_random(name, size) +#define ASSERT(condition) do { \ + if (!(condition)) { \ + fprintf(stderr, "Assert failure(%s, %d): %s\n", \ + __FILE__, __LINE__, #condition); \ + exit(1); \ } \ } while (0) -#define RANDOM_INPUT(name, size) u8 name[size]; p_random(name, size) +#define ASSERT_EQUAL(a, b, size) ASSERT( memcmp(a, b, size) == 0) +#define ASSERT_DIFFERENT(a, b, size) ASSERT(!memcmp(a, b, size) == 0) +#define ASSERT_OK(exp) ASSERT((exp) == 0) +#define ASSERT_KO(exp) ASSERT((exp) != 0) extern u64 random_state; // state of the RNG @@ -99,7 +104,7 @@ void* alloc(size_t size); vector next_input (vector_reader *vectors); vector next_output(vector_reader *vectors); -int vector_test(void (*f)(vector_reader*), - const char *name, size_t nb_vectors, const char *vectors[]); +int vector_test(void (*f)(vector_reader*), + const char *name, size_t nb_vectors, const char *vectors[]); #endif // UTILS_H -- 2.47.3