corecrypto/ccsae/crypto_test/ccsaetest.c

782 lines
32 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Copyright (c) (2018,2019,2020) Apple Inc. All rights reserved.
*
* corecrypto is licensed under Apple Inc.s Internal Use License Agreement (which
* is contained in the License.txt file distributed with corecrypto) and only to
* people who accept that license. IMPORTANT: Any license rights granted to you by
* Apple Inc. (if any) are limited to internal use within your organization only on
* devices and computers you own or control, for the sole purpose of verifying the
* security characteristics and correct functioning of the Apple Software. You may
* not, directly or indirectly, redistribute the Apple Software or any portions thereof.
*/
#include <corecrypto/ccsha2.h>
#include <corecrypto/ccrng_sequence_non_repeat.h>
#include "ccsae.h"
#include "ccsae_priv.h"
#include "ccec_internal.h"
#include "cc_priv.h"
#include "ccsaetest.h"
#include "cczp_internal.h"
#include "testmore.h"
#include "testccnBuffer.h"
#include "testbyteBuffer.h"
// Defines a buffer containing uniformly random data.
// Buffer size is random between 1 and 32 bytes.
#define DEF_RANDOM_BYTE_BUF(_name_) \
uint64_t _name_##_len_pre; \
is(ccrng_uniform(rng, 32, &_name_##_len_pre), 0, "RNG error"); \
size_t _name_##_len = (size_t)_name_##_len_pre + 1; \
uint8_t _name_[_name_##_len]; \
is(ccrng_generate(rng, _name_##_len, _name_), 0, "RNG error");
const struct ccsae_test_vector ccsae_test_vectors[] = {
#include "../test_vectors/ccsae_vectors.inc"
// End
{
.di = NULL,
}
};
const struct ccsae_test_vector ccsae_3_loop_vector[] = {
#include "../test_vectors/ccsae_3_loop_vector.inc"
// End
{
.di = NULL,
}
};
static int ccsae_test_pwe_not_found(const struct ccsae_test_vector *vector)
{
int status = 0;
byteBuffer password = hexStringToBytes(vector->password);
byteBuffer password_identifier = hexStringToBytes(vector->password_identifier);
byteBuffer A_name = hexStringToBytes(vector->A);
byteBuffer B_name = hexStringToBytes(vector->B);
byteBuffer rand_seq = hexStringToBytes(vector->rand_seq);
ccec_const_cp_t cp = vector->curve();
const struct ccdigest_info *di = vector->di;
struct ccrng_sequence_state sequence_ctx;
uint8_t rand_buffer[4096] = { 0 };
memcpy(rand_buffer, rand_seq->bytes, rand_seq->len);
ccrng_sequence_non_repeat_init(&sequence_ctx, 4096, rand_buffer);
ccsae_ctx_decl(cp, A);
int init_status = ccsae_init(A, cp, (struct ccrng_state *)&sequence_ctx, di);
size_t commit_size = 1;
if (init_status == CCERR_OK) {
commit_size = ccsae_sizeof_commitment(A);
}
uint8_t commitment[commit_size];
is_or_goto(init_status, CCERR_OK, "Error during initilization", errout);
// Now artificially limit the number of hunting and pecking loops
is_or_goto(ccsae_generate_commitment_init(A), CCERR_OK, "commit init failure", errout);
A->iterations = 2;
is_or_goto(ccsae_generate_commitment_partial(A,
A_name->bytes,
A_name->len,
B_name->bytes,
B_name->len,
password->bytes,
password->len,
password_identifier->bytes,
password_identifier->len,
2),
CCERR_OK,
"commit partial failure",
errout);
is_or_goto(ccsae_generate_commitment_finalize(A, commitment),
CCSAE_HUNTPECK_EXCEEDED_MAX_TRIALS,
"Hunting and Pecking phase should fail",
errout);
status = 1;
errout:
free(password);
free(password_identifier);
free(A_name);
free(B_name);
free(rand_seq);
return status;
}
static int ccsae_test_vector(const struct ccsae_test_vector *vector)
{
int status = 0;
byteBuffer password = hexStringToBytes(vector->password);
byteBuffer password_identifier = hexStringToBytes(vector->password_identifier);
byteBuffer A_name = hexStringToBytes(vector->A);
byteBuffer B_name = hexStringToBytes(vector->B);
byteBuffer rand_seq = hexStringToBytes(vector->rand_seq);
byteBuffer commit_vector = hexStringToBytes(vector->commit);
byteBuffer peer_commitment = hexStringToBytes(vector->peer_commit);
byteBuffer send_confirm = hexStringToBytes(vector->send_confirm);
byteBuffer confirm_vector = hexStringToBytes(vector->confirm);
byteBuffer peer_send_confirm = hexStringToBytes(vector->peer_send_confirm);
byteBuffer peer_confirmation = hexStringToBytes(vector->peer_confirm);
byteBuffer kck_vector = hexStringToBytes(vector->kck);
byteBuffer pmk_vector = hexStringToBytes(vector->pmk);
byteBuffer pmkid_vector = hexStringToBytes(vector->pmkid);
uint8_t kck[32];
uint8_t pmk[32];
uint8_t pmkid[16];
uint8_t kck_iuf[32];
uint8_t pmk_iuf[32];
uint8_t pmkid_iuf[16];
ccec_const_cp_t cp = vector->curve();
const struct ccdigest_info *di = vector->di;
struct ccrng_sequence_state sequence_ctx;
struct ccrng_sequence_state sequence_ctx_iuf;
uint8_t rand_buffer[4096] = { 0 };
memcpy(rand_buffer, rand_seq->bytes, rand_seq->len);
ccrng_sequence_non_repeat_init(&sequence_ctx, 4096, rand_buffer);
ccrng_sequence_non_repeat_init(&sequence_ctx_iuf, 4096, rand_buffer);
ccsae_ctx_decl(cp, A);
ccsae_ctx_decl(cp, A_iuf);
int init_status = ccsae_init(A, cp, (struct ccrng_state *)&sequence_ctx, di);
size_t commit_size = 1;
size_t confirm_size = 1;
if (init_status == CCERR_OK) {
commit_size = ccsae_sizeof_commitment(A);
confirm_size = ccsae_sizeof_confirmation(A);
}
int init_status_iuf = ccsae_init(A_iuf, cp, (struct ccrng_state *)&sequence_ctx_iuf, di);
uint8_t commitment[commit_size];
uint8_t commitment_iuf[commit_size];
uint8_t confirmation[confirm_size];
uint8_t confirmation_iuf[confirm_size];
is_or_goto(init_status, CCERR_OK, "Error during initilization", errout);
is_or_goto(init_status_iuf, CCERR_OK, "Error during initilization", errout);
is_or_goto(commit_size, commit_vector->len, "Wrong Commitmment Size", errout);
is_or_goto(confirm_size, confirm_vector->len, "Wrong Confirmation Size", errout);
// Generate Commitment
is_or_goto(ccsae_generate_commitment(A,
A_name->bytes,
A_name->len,
B_name->bytes,
B_name->len,
password->bytes,
password->len,
password_identifier->bytes,
password_identifier->len,
commitment),
CCERR_OK,
"Generate Commit Failure",
errout);
ok_memcmp_or_goto(commitment, commit_vector->bytes, commit_size, errout, "Incorrect Commitment");
is_or_goto(ccsae_generate_commitment_init(A_iuf), CCERR_OK, "Commitment Init Failure", errout);
for (int i = 0; i < 40; i++) {
int gen_commit_ret = ccsae_generate_commitment_partial(A_iuf,
A_name->bytes,
A_name->len,
B_name->bytes,
B_name->len,
password->bytes,
password->len,
password_identifier->bytes,
password_identifier->len,
1);
ok_or_goto(gen_commit_ret == CCERR_OK || gen_commit_ret == CCSAE_GENERATE_COMMIT_CALL_AGAIN, "Commitment partial Failure", errout);
}
is_or_goto(ccsae_generate_commitment_finalize(A_iuf, commitment_iuf), CCERR_OK, "Commitment Finalize Failure", errout);
ok_memcmp_or_goto(commitment_iuf, commit_vector->bytes, commit_size, errout, "Incorrect Commitment IUF");
// Verify commitment
is_or_goto(ccsae_verify_commitment(A, peer_commitment->bytes), CCERR_OK, "Verification error", errout);
is_or_goto(ccsae_verify_commitment(A_iuf, peer_commitment->bytes), CCERR_OK, "Verification error IUF", errout);
// Generate confirmation
is_or_goto(
ccsae_generate_confirmation(A, send_confirm->bytes, confirmation), CCERR_OK, "Generate confirmation error", errout);
ok_memcmp_or_goto(confirmation, confirm_vector->bytes, confirm_size, errout, "Incorrect confirmation");
is_or_goto(ccsae_generate_confirmation(A_iuf, send_confirm->bytes, confirmation_iuf),
CCERR_OK,
"Generate confirmation error IUF",
errout);
ok_memcmp_or_goto(confirmation_iuf, confirm_vector->bytes, confirm_size, errout, "Incorrect confirmation IUF");
is_or_goto(ccsae_verify_confirmation(A, peer_send_confirm->bytes, peer_confirmation->bytes),
CCERR_OK,
"Incorrect Peer Confirmation",
errout);
is_or_goto(ccsae_verify_confirmation(A_iuf, peer_send_confirm->bytes, peer_confirmation->bytes),
CCERR_OK,
"Incorrect Peer Confirmation IUF",
errout);
is_or_goto(ccsae_get_keys(A, kck, pmk, pmkid), CCERR_OK, "Key Grab Error", errout);
ok_memcmp_or_goto(kck, kck_vector->bytes, kck_vector->len, errout, "Incorrect kck");
ok_memcmp_or_goto(pmk, pmk_vector->bytes, pmk_vector->len, errout, "Incorrect pmk");
ok_memcmp_or_goto(pmkid, pmkid_vector->bytes, pmkid_vector->len, errout, "Incorrect pmkid");
is_or_goto(ccsae_get_keys(A_iuf, kck_iuf, pmk_iuf, pmkid_iuf), CCERR_OK, "Key Grab Error IUF", errout);
ok_memcmp_or_goto(kck_iuf, kck_vector->bytes, kck_vector->len, errout, "Incorrect kck IUF");
ok_memcmp_or_goto(pmk_iuf, pmk_vector->bytes, pmk_vector->len, errout, "Incorrect pmk IUF");
ok_memcmp_or_goto(pmkid_iuf, pmkid_vector->bytes, pmkid_vector->len, errout, "Incorrect pmkid IUF");
status = 1;
errout:
if (status != 1) {
cc_printf("Failed test: %s\n", vector->test_desc);
}
free(password);
free(password_identifier);
free(A_name);
free(B_name);
free(rand_seq);
free(commit_vector);
free(peer_commitment);
free(send_confirm);
free(confirm_vector);
free(peer_send_confirm);
free(peer_confirmation);
free(kck_vector);
free(pmk_vector);
free(pmkid_vector);
return status;
}
static int ccsae_vector_tests(const struct ccsae_test_vector *vectors)
{
size_t test_counter = 0;
int test_status = 1;
const struct ccsae_test_vector *current_vector = &vectors[test_counter++];
while (current_vector->di != NULL && test_status) {
test_status = ccsae_test_vector(current_vector);
current_vector = &vectors[test_counter++];
}
return test_status;
}
static int ccsae_test_invalid_peer_values(ccec_const_cp_t cp, const struct ccdigest_info *di)
{
struct ccrng_state *rng = global_test_rng;
cc_size n = ccec_cp_n(cp);
cc_size tn = ccec_cp_prime_size(cp);
ccsae_ctx_decl(cp, ctxA);
is(ccsae_init(ctxA, cp, rng, di), 0, "Initialize Error");
DEF_RANDOM_BYTE_BUF(A);
DEF_RANDOM_BYTE_BUF(B);
DEF_RANDOM_BYTE_BUF(password);
DEF_RANDOM_BYTE_BUF(password_identifier);
cc_size A_commit_size = ccsae_sizeof_commitment(ctxA);
uint8_t A_commit[A_commit_size];
uint8_t test_commitment[A_commit_size];
memset(test_commitment, 0, A_commit_size);
ccn_write_uint_padded(n, ccec_const_point_x(ccec_cp_g(cp), cp), tn, test_commitment + tn);
ccn_write_uint_padded(n, ccec_const_point_y(ccec_cp_g(cp), cp), tn, test_commitment + tn + tn);
is(ccsae_generate_commitment(
ctxA, A, A_len, B, B_len, password, password_len, password_identifier, password_identifier_len, A_commit),
0,
"A generate commitment failure");
isnt(ccsae_verify_commitment(ctxA, test_commitment), CCERR_OK, "Failure - accepted cs = 0");
test_commitment[A_commit_size - 1] = 1;
isnt(ccsae_verify_commitment(ctxA, test_commitment), CCERR_OK, "Failure - accepted cs = 1");
ccn_write_uint(n, cczp_prime(ccec_cp_zq(cp)), tn, test_commitment);
isnt(ccsae_verify_commitment(ctxA, test_commitment), CCERR_OK, "Failure - accepted cs = q");
memcpy(test_commitment, A_commit, tn);
isnt(ccsae_verify_commitment(ctxA, test_commitment), CCERR_OK, "Failure - peerCS = myCS");
memcpy(test_commitment, A_commit + tn, tn); // Set the commit scalar to something "random" so we hit the following cases
memcpy(test_commitment + tn, A_commit + tn, tn);
isnt(ccsae_verify_commitment(ctxA, test_commitment), CCERR_OK, "Failure - peerX = myX");
memset(test_commitment + tn, 0xff, tn);
memcpy(test_commitment + tn + tn, A_commit + 2 * tn, tn);
isnt(ccsae_verify_commitment(ctxA, test_commitment), CCERR_OK, "Failure - peerY = myY");
return 0;
}
static int ccsae_test_curves_and_hashes(ccec_const_cp_t cp, const struct ccdigest_info *di)
{
struct ccrng_state *rng = global_test_rng;
cc_size n_bytes = ccec_cp_prime_size(cp);
ccsae_ctx_decl(cp, ctxA);
ccsae_ctx_decl(cp, ctxB);
is(ccsae_init(ctxA, cp, rng, di), 0, "Initialize Error");
is(ccsae_init(ctxB, cp, rng, di), 0, "Initialize Error");
// Generate random names...
DEF_RANDOM_BYTE_BUF(A);
DEF_RANDOM_BYTE_BUF(B);
DEF_RANDOM_BYTE_BUF(password);
DEF_RANDOM_BYTE_BUF(password_identifier);
uint8_t A_send_confirmation[2] = { 0, 1 };
uint8_t B_send_confirmation[2] = { 0, 1 };
ccrng_generate(rng, 2, A_send_confirmation);
ccrng_generate(rng, 2, B_send_confirmation);
cc_size A_commit_size = ccsae_sizeof_commitment(ctxA);
cc_size B_commit_size = ccsae_sizeof_commitment(ctxB);
cmp_ok(A_commit_size, ==, B_commit_size, "Commit size mismatch");
uint8_t A_commit[A_commit_size];
uint8_t B_commit[B_commit_size];
is(ccsae_generate_commitment(
ctxA, A, A_len, B, B_len, password, password_len, password_identifier, password_identifier_len, A_commit),
0,
"A generate commitment failure");
is(ccsae_generate_commitment(
ctxB, A, A_len, B, B_len, password, password_len, password_identifier, password_identifier_len, B_commit),
0,
"B generate commitment failure");
isnt(memcmp(A_commit, B_commit, A_commit_size), 0, "Generated commits are the same");
uint8_t AB_scalar[n_bytes];
uint8_t AB_CE[2 * n_bytes];
memcpy(AB_scalar, B_commit, n_bytes);
memcpy(AB_CE, B_commit + n_bytes, 2 * n_bytes);
is(ccsae_verify_commitment(ctxA, B_commit),
CCERR_OK,
"A->B Commitment Verification Error: curve%zu",
ccec_cp_prime_bitlen(cp));
memcpy(AB_scalar, A_commit, n_bytes);
memcpy(AB_CE, A_commit + n_bytes, 2 * n_bytes);
is(ccsae_verify_commitment(ctxB, A_commit), CCERR_OK, "B->A Commitment Verification Error");
uint8_t A_confirmation[di->output_size];
uint8_t B_confirmation[di->output_size];
is(ccsae_generate_confirmation(ctxA, A_send_confirmation, A_confirmation), CCERR_OK, "A confirmation generation error");
is(ccsae_generate_confirmation(ctxB, B_send_confirmation, B_confirmation), CCERR_OK, "A confirmation generation error");
isnt(memcmp(A_confirmation, B_confirmation, di->output_size), 0, "Generated confirmations are the same...");
ok_memcmp(ccsae_ctx_KCK(ctxA), ccsae_ctx_KCK(ctxB), 32, "KCK comparison failure");
ok_memcmp(ccsae_ctx_PMK(ctxA), ccsae_ctx_PMK(ctxB), 32, "PMK comparison failure");
is(ccsae_verify_confirmation(ctxA, B_send_confirmation, B_confirmation), 0, "A->B Confirmation Verification Error");
is(ccsae_verify_confirmation(ctxB, A_send_confirmation, A_confirmation), 0, "B->A Confirmation Verification Error");
uint8_t Akck[32], Bkck[32];
uint8_t Apmk[32], Bpmk[32];
uint8_t Apmkid[16], Bpmkid[16];
is(ccsae_get_keys(ctxA, Akck, Apmk, Apmkid), 0, "A Get Keys Failure");
is(ccsae_get_keys(ctxB, Bkck, Bpmk, Bpmkid), 0, "B Get Keys Failure");
ok_memcmp(Akck, Bkck, 32, "Akck != Bkck");
ok_memcmp(Apmk, Bpmk, 32, "Apmk != Bpmk");
ok_memcmp(Apmkid, Bpmkid, 16, "Apmkid != Bpmlkid");
return 0;
}
static int compute_path(uint8_t pathA[5], uint8_t pathB[5])
{
uint8_t paths[2][5];
memcpy(paths[0], pathA, 5);
memcpy(paths[1], pathB, 5);
int result = 0;
struct ccrng_state *rng = global_test_rng;
struct ccsae_ctx ctxs[2][CCSAE_SIZE_P256_SHA256];
ccsae_init_p256_sha256(ctxs[0], rng);
ccsae_init_p256_sha256(ctxs[1], rng);
DEF_RANDOM_BYTE_BUF(A);
DEF_RANDOM_BYTE_BUF(B);
DEF_RANDOM_BYTE_BUF(password);
DEF_RANDOM_BYTE_BUF(password_identifier);
uint8_t send_confirmation[2] = { 0, 1 };
uint8_t commits[2][ccsae_sizeof_commitment(ctxs[0])];
uint8_t confirmations[2][ccsae_sizeof_confirmation(ctxs[0])];
uint8_t kck[CCSAE_KCK_PMK_SIZE];
uint8_t pmk[CCSAE_KCK_PMK_SIZE];
uint8_t pmkid[16];
int cc, occ;
for (int i = 0; i < 5; i++) {
cc = (pathA[i] <= pathB[i]) ? 0 : 1;
occ = (pathA[i] <= pathB[i]) ? 1 : 0;
for (int j = 0; j < 2; j++) {
if (paths[cc][i] == 1) {
result = ccsae_generate_commitment(ctxs[cc],
A,
A_len,
B,
B_len,
password,
password_len,
password_identifier,
password_identifier_len,
commits[cc]);
} else if (paths[cc][i] == 2) {
result = ccsae_verify_commitment(ctxs[cc], commits[occ]);
} else if (paths[cc][i] == 3) {
result = ccsae_generate_confirmation(ctxs[cc], send_confirmation, confirmations[cc]);
} else if (paths[cc][i] == 4) {
result = ccsae_verify_confirmation(ctxs[cc], send_confirmation, confirmations[occ]);
} else if (paths[cc][i] == 5) {
result = ccsae_get_keys(ctxs[cc], kck, pmk, pmkid);
} else {
cc_assert(false);
}
if (result != CCERR_OK)
return result;
result = cc;
cc = occ;
occ = result;
}
}
return CCERR_OK;
}
static int permute(uint8_t arr[5])
{
int k = -1;
int l = 0;
int s = 0;
for (s = 0; s < 4; s++) {
if (arr[s] < arr[s + 1]) {
k = s;
}
}
if (k == -1)
return 0;
for (s = 0; s < 5; s++) {
if (arr[k] < arr[s]) {
l = s;
}
}
CC_SWAP(arr[k], arr[l]);
int numswaps = (4 - k) / 2;
for (s = 0; s < numswaps; s++) {
CC_SWAP(arr[k + 1 + s], arr[4 - s]);
}
return 1;
}
static int ccsae_test_state_machine()
{
/* Valid state transitions:
Init ->
Commit generate ->
Commit Verify ->
Generate Confirmation ->
OR
Verify Confirmation ->
-> Get Keys
Valid paths are [0,1,2,3,4,5] & [0,1,2,4,3,5]
*/
uint8_t pathA[5] = { 1, 2, 3, 4, 5 };
uint8_t pathB[5] = { 1, 2, 3, 4, 5 };
int valid_paths = 0;
do {
if (compute_path(pathA, pathB) == CCERR_OK) {
valid_paths += 1;
}
} while (permute(pathA));
return valid_paths == 2;
}
static int ccsae_test_commit_scalar_conditions()
{
/*
The commit scalar needs to be > 1.
In the first test, we set the first generated scalar to q - 1 (notice that the random buffer
is actually q - 2. This is because ccec_generate_scalar_fips_retry adds 1 to the result).
The resultant commit scalar value will therefore be zero.
In the second test we set the second generated value to 2 making the commit scalar one.
*/
int result;
struct ccsae_ctx ctx[CCSAE_SIZE_P256_SHA256];
ccec_const_cp_t cp = ccec_cp_256();
cc_size n = ccec_cp_n(cp);
size_t tn = ccec_cp_prime_size(cp);
cc_unit q[n];
struct ccrng_sequence_state sequence_ctx;
uint8_t rng_buffer[4096] = { 0 };
const uint8_t password[5] = { 0, 1, 2, 3, 4 };
ccsae_init_p256_sha256(ctx, (struct ccrng_state *)&sequence_ctx);
uint8_t commit[ccsae_sizeof_commitment(ctx)];
ccn_sub1(n, q, cczp_prime(ccec_cp_zq(cp)), 2);
ccn_swap(n, q);
ccn_write_uint_padded(ccec_cp_n(cp), q, tn, rng_buffer);
ccrng_sequence_non_repeat_init(&sequence_ctx, 4096, rng_buffer);
result = ccsae_generate_commitment(ctx, password, 5, password, 5, password, 5, NULL, 0, commit);
isnt(result, CCERR_OK, "Error: CS == 0 should fail!");
ccsae_ctx_clear(cp, ctx);
ccn_write_uint_padded(ccec_cp_n(cp), q, tn, rng_buffer);
rng_buffer[32] = 0x01;
ccrng_sequence_non_repeat_init(&sequence_ctx, 4096, rng_buffer);
ccsae_init_p256_sha256(ctx, (struct ccrng_state *)&sequence_ctx);
result = ccsae_generate_commitment(ctx, password, 5, password, 5, password, 5, NULL, 0, commit);
isnt(result, CCERR_OK, "Error: CS == 1 should fail!");
return 1;
}
#define y2_vs_ap_test_trials 1000
static int ccsae_test_y2_vs_affine_point(ccec_const_cp_t cp)
{
cc_size n = ccec_cp_n(cp);
struct ccrng_state *rng = global_test_rng;
int ret = CCERR_OK;
int y2_res;
int affine_res;
cc_unit x[n];
cc_unit y[n];
cc_unit y2[n];
ccec_point_decl_cp(cp, result);
// Make sure that we correctly tolerate inputs larger than p (and fail)
ccn_set(n, x, ccec_cp_p(cp));
ccn_add1(n, x, x, 1);
CC_DECL_WORKSPACE_OR_FAIL(ws, CCSAE_Y2_FROM_X_WORKSPACE_N(n));
y2_res = ccsae_y2_from_x_ws(ws, cp, y, x);
CC_FREE_WORKSPACE(ws);
is(y2_res, 0, "x = p + 1 should never succeed ccsae_y2_from_x_ws");
for (int t = 0; t < y2_vs_ap_test_trials; t++) {
// Quickly find a potential x-coordinate in GF(p)
ccrng_generate(rng, ccn_sizeof_n(n), x);
cc_size lbits = ccec_cp_prime_bitlen(cp) & (CCN_UNIT_BITS - 1);
if (lbits) {
cc_unit msuMask = (~CC_UNIT_C(0)) >> (CCN_UNIT_BITS - lbits);
x[n - 1] &= msuMask;
}
// Returns 0 on success (found a sqrt), "random" on failure
affine_res = ccec_affine_point_from_x(cp, (ccec_affine_point_t)result, x);
// Returns 1 on success (its a qr), 0 on failure (not a qr)
CC_DECL_WORKSPACE_OR_FAIL(ws, CCSAE_Y2_FROM_X_WORKSPACE_N(n));
y2_res = ccsae_y2_from_x_ws(ws, cp, y, x);
CC_FREE_WORKSPACE(ws);
cczp_sqrt((cczp_const_t)ccec_cp_zp(cp), y2, y);
if ((ret = cczp_from((cczp_const_t)ccec_cp_zp(cp), y2, y2))) {
return ret;
}
ccn_sub(n, y, ccec_cp_p(cp), y2);
// Either the result is no sqrt or it's valid and the sqrt roots match
ok((y2_res == 0 && affine_res != 0) ||
(y2_res == 1 && affine_res == 0 &&
((ccn_cmp(n, y, ccec_const_point_y(result, cp)) == 0) || (ccn_cmp(n, y2, ccec_const_point_y(result, cp)) == 0))),
"Error: sqrt of ccsae_y2_from_x is incorrect!");
}
return ret;
}
static int ccsae_test_CCSAE_SIZE_P256_SHA256_size(void)
{
ccec_const_cp_t cp = ccec_cp_256();
size_t ctx_actual_size = ccsae_sizeof_ctx(cp);
size_t ctx_macro_size = CCSAE_SIZE_P256_SHA256 * sizeof(struct ccsae_ctx);
cmp_ok(ctx_macro_size, >=, ctx_actual_size, "CCSAE_SIZE_P256_SHA256 context size < necessary size");
cmp_ok(ctx_macro_size,
<
, ctx_actual_size + sizeof(struct ccsae_ctx), "CCSAE_SIZE_P256_SHA256 size should be close to actual size");
return 1;
}
static int ccsae_test_iuf_generate_commitment(ccec_const_cp_t cp, const struct ccdigest_info *di)
{
struct ccrng_state *rng = global_test_rng;
uint8_t rng_ss[2048];
ccrng_generate(rng, sizeof(rng_ss), rng_ss);
struct ccrng_sequence_state sequence_ctx;
int err = ccrng_sequence_init(&sequence_ctx, sizeof(rng_ss), rng_ss);
ok_or_fail(err == 0, "ccrng_sequence_init failure");
DEF_RANDOM_BYTE_BUF(A);
DEF_RANDOM_BYTE_BUF(B);
DEF_RANDOM_BYTE_BUF(password);
DEF_RANDOM_BYTE_BUF(password_identifier);
ccsae_ctx_decl(cp, ctx);
ccsae_ctx_decl(cp, ctx_iuf);
// Generate first commitment
ok_or_fail(ccsae_init(ctx, cp, (struct ccrng_state *)&sequence_ctx, di) == CCERR_OK, "Init Failure");
size_t ctx_csz = ccsae_sizeof_commitment(ctx);
uint8_t ctx_commitment[ctx_csz];
err = ccsae_generate_commitment(
ctx, A, A_len, B, B_len, password, password_len, password_identifier, password_identifier_len, ctx_commitment);
ok_or_fail(err == CCERR_OK, "ctx generate commitment failure");
err = ccrng_sequence_init(&sequence_ctx, sizeof(rng_ss), rng_ss);
ok_or_fail(err == 0, "ccrng_sequence_init failure");
// Generate second commitment
ok_or_fail(ccsae_init(ctx_iuf, cp, (struct ccrng_state *)&sequence_ctx, di) == CCERR_OK, "Init Failure");
size_t ctx_iuf_csz = ccsae_sizeof_commitment(ctx);
ok_or_fail(ctx_csz == ctx_iuf_csz, "Context commitment sizes differ !");
uint8_t ctx_iuf_commitment[ctx_iuf_csz];
ok_or_fail(ccsae_generate_commitment_init(ctx_iuf) == CCERR_OK, "gen commit init failure");
ok_or_fail(ccsae_generate_commitment_partial(
ctx_iuf, A, A_len, B, B_len, password, password_len, password_identifier, password_identifier_len, 10) ==
CCSAE_GENERATE_COMMIT_CALL_AGAIN,
"gen commit partial error");
ok_or_fail(ccsae_generate_commitment_partial(
ctx_iuf, A, A_len, B, B_len, password, password_len, password_identifier, password_identifier_len, 10) ==
CCSAE_GENERATE_COMMIT_CALL_AGAIN,
"gen commit partial error");
ok_or_fail(ccsae_generate_commitment_partial(
ctx_iuf, A, A_len, B, B_len, password, password_len, password_identifier, password_identifier_len, 10) ==
CCSAE_GENERATE_COMMIT_CALL_AGAIN,
"gen commit partial error");
ok_or_fail(ccsae_generate_commitment_partial(
ctx_iuf, A, A_len, B, B_len, password, password_len, password_identifier, password_identifier_len, 10) ==
CCERR_OK,
"gen commit partial error");
err = ccsae_generate_commitment_finalize(ctx_iuf, ctx_iuf_commitment);
ok_or_fail(err == CCERR_OK, "gen commit finalize failure");
ok_memcmp_or_fail(ctx_commitment, ctx_iuf_commitment, ctx_csz, "ctx commitment differs");
return 1;
}
static int ccsae_test_iuf_state_tests(void)
{
struct ccrng_state *rng = global_test_rng;
ccec_const_cp_t cp = ccec_cp_256();
const struct ccdigest_info *di = ccsha256_di();
DEF_RANDOM_BYTE_BUF(A);
DEF_RANDOM_BYTE_BUF(B);
DEF_RANDOM_BYTE_BUF(password);
DEF_RANDOM_BYTE_BUF(password_identifier);
ccsae_ctx_decl(cp, ctx);
ok_or_fail(ccsae_init(ctx, cp, rng, di) == CCERR_OK, "Init Failure");
size_t ctx_csz = ccsae_sizeof_commitment(ctx);
uint8_t ctx_commitment[ctx_csz];
// Need to commitment_init before partial (and definitely before finalize)
ok_or_fail(ccsae_generate_commitment_partial(
ctx, A, A_len, B, B_len, password, password_len, password_identifier, password_identifier_len, 10) ==
CCERR_CALL_SEQUENCE,
"gen commit partial error");
ok_or_fail(ccsae_generate_commitment_finalize(ctx, ctx_commitment) == CCERR_CALL_SEQUENCE, "gen commit finalize should fail");
ok_or_fail(ccsae_generate_commitment_init(ctx) == CCERR_OK, "Init Commit Failure");
// Need to have called update and have performed the proper amount of calls
ok_or_fail(ccsae_generate_commitment_finalize(ctx, ctx_commitment) == CCERR_CALL_SEQUENCE, "gen commit finalize should fail");
// Zero iterations
ok_or_fail(ccsae_generate_commitment_partial(
ctx, A, A_len, B, B_len, password, password_len, password_identifier, password_identifier_len, 0) ==
CCERR_PARAMETER,
"gen commit partial error");
ok_or_fail(ccsae_generate_commitment_partial(
ctx, A, A_len, B, B_len, password, password_len, password_identifier, password_identifier_len, SAE_HUNT_AND_PECK_ITERATIONS - 1) ==
CCSAE_GENERATE_COMMIT_CALL_AGAIN,
"gen commit partial error");
// Need to have called partial and have performed the proper amount of calls
ok_or_fail(ccsae_generate_commitment_finalize(ctx, ctx_commitment) == CCSAE_NOT_ENOUGH_COMMIT_PARTIAL_CALLS,
"gen commit partial error");
ok_or_fail(ccsae_generate_commitment_partial(
ctx, A, A_len, B, B_len, password, password_len, password_identifier, password_identifier_len, 253) ==
CCERR_OK,
"gen commit partial error");
int resy = ccsae_generate_commitment_partial(ctx, A, A_len, B, B_len, password, password_len, password_identifier, password_identifier_len, 1);
ok_or_fail(resy == CCERR_OK,
"gen commit partial error");
ok_or_fail(ccsae_generate_commitment_finalize(ctx, ctx_commitment) == CCERR_OK, "gen commit finalize failure");
return 1;
}
int ccsae_tests(TM_UNUSED int argc, TM_UNUSED char *const *argv)
{
ccec_const_cp_t curves[] = { ccec_cp_192(), ccec_cp_224(), ccec_cp_256(), ccec_cp_384(), ccec_cp_521() };
const struct ccdigest_info *digests[] = { ccsha224_di(), ccsha256_di(), ccsha384_di(), ccsha512_di() };
int num_test_vectors = CC_ARRAY_LEN(ccsae_test_vectors) - 1;
int num_tests = 0;
num_tests += (29 * (CC_ARRAY_LEN(curves) * CC_ARRAY_LEN(digests))); // # tests of test_curves_and_hashes, # curves, # hashes
num_tests += 1 + (num_test_vectors * 65); // ccsae_vector_tests.
num_tests += (17 * (CC_ARRAY_LEN(curves) * CC_ARRAY_LEN(digests))); // invalid_peer_values
num_tests += 5; // Hunting and Peck Loop Failure
num_tests += (120 * 8) + 1; // ccsae_test_state_machine (# permutations x # of tests)
num_tests += 3; // ccsae_test_commit_scalar_conditions
num_tests += ((y2_vs_ap_test_trials + 1) * CC_ARRAY_LEN(curves)); // ccsae_test_y2_vs_affine_point
num_tests += 1 + 2; // ccsae_test_CCSAE_SIZE_P256_SHA256_size
num_tests += (22 * (CC_ARRAY_LEN(curves) * CC_ARRAY_LEN(digests))); // ccsae_test_iuf_generate_commitment
num_tests += 20; // ccsae_test_iuf_state_tests
plan_tests(num_tests);
for (size_t i = 0; i < CC_ARRAY_LEN(curves); i++) {
ccec_const_cp_t cp = curves[i];
for (size_t j = 0; j < CC_ARRAY_LEN(digests); j++) {
const struct ccdigest_info *di = digests[j];
is(ccsae_test_curves_and_hashes(cp, di), 0, "Curves & Hashes Failure: curve = %zu, digest = %zu", i, j);
is(ccsae_test_invalid_peer_values(cp, di), 0, "Invalid peer curves Failure: curve = %zu, digest = %zu", i, j);
is(ccsae_test_iuf_generate_commitment(cp, di),
1,
"Init / Update / Finalize Commitment Failure: curve = %zu, digest = %zu",
i,
j);
}
}
ok(ccsae_test_pwe_not_found(&ccsae_3_loop_vector[0]), "Hunting and Peck Loop Failure");
ok(ccsae_vector_tests(ccsae_test_vectors), "Test Vectors");
ok(ccsae_test_state_machine(), "State Machine");
ok(ccsae_test_commit_scalar_conditions(), "Commit Scalar Conditions");
for (size_t i = 0; i < CC_ARRAY_LEN(curves); i++) {
ccec_const_cp_t cp = curves[i];
ccsae_test_y2_vs_affine_point(cp);
}
ok(ccsae_test_CCSAE_SIZE_P256_SHA256_size(), "CCSAE_SIZE_P256_SHA256 Size Tests");
ok(ccsae_test_iuf_state_tests(), "IUF state tests failure");
return 0;
}