corecrypto/ccspake/crypto_test/ccspaketest.c

725 lines
28 KiB
C
Raw 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/ccspake.h>
#include "ccspake_priv.h"
#include "cc_priv.h"
#include "testmore.h"
#include "ccec_internal.h"
#include <corecrypto/ccaes.h>
#include <corecrypto/ccsha2.h>
static int generate_point(ccspake_const_cp_t scp, uint8_t *out)
{
ccec_const_cp_t cp = ccspake_cp_ec(scp);
ccec_full_ctx_decl_cp(cp, full);
ccec_ctx_init(cp, full);
is(ccecdh_generate_key(cp, global_test_rng, full), 0, "ccecdh_generate_key");
ccec_export_pub(ccec_ctx_pub(full), out);
ccec_full_ctx_clear_cp(cp, full);
return 0;
}
static int test_2_rtt(ccspake_const_cp_t cp, ccspake_const_mac_t mac, size_t sk_len)
{
struct ccrng_state *rng = global_test_rng;
ccspake_ctx_decl(cp, ctx_p);
ccspake_ctx_decl(cp, ctx_v);
size_t w_size = ccspake_sizeof_w(cp);
size_t pt_size = ccspake_sizeof_point(cp);
uint8_t w0[w_size], w1[w_size];
ccrng_generate(rng, sizeof(w0), w0);
ccrng_generate(rng, sizeof(w1), w1);
uint8_t aad[16];
ccrng_generate(rng, sizeof(aad), aad);
uint8_t L[pt_size];
is(ccspake_generate_L(cp, sizeof(w1), w1, sizeof(L), L, rng), 0, "Generate L from w1");
// Passing the wrong w1 length must fail.
is(ccspake_generate_L(cp, w_size + 1, w1, sizeof(L), L, rng),
CCERR_PARAMETER, "Passing the wrong w1 length must fail");
// Passing the wrong L length must fail.
is(ccspake_generate_L(cp, sizeof(w1), w1, pt_size + 1, L, rng),
CCERR_PARAMETER, "Passing the wrong L length must fail");
// Passing the wrong w length must fail.
is(ccspake_prover_init(ctx_p, cp, mac, rng, sizeof(aad), aad, w_size + 1, w0, w1),
CCERR_PARAMETER, "Passing the wrong w length must fail");
// Passing the wrong w0 length must fail.
is(ccspake_verifier_init(ctx_v, cp, mac, rng, sizeof(aad), aad, w_size + 1, w0, sizeof(L), L),
CCERR_PARAMETER, "Passing the wrong w0 length must fail");
// Passing the wrong L length must fail.
is(ccspake_verifier_init(ctx_v, cp, mac, rng, sizeof(aad), aad, sizeof(w0), w0, pt_size + 1, L),
CCERR_PARAMETER, "Passing the wrong L length must fail");
is(ccspake_prover_init(ctx_p, cp, mac, rng, sizeof(aad), aad, sizeof(w0), w0, w1), 0, "Initialize SPAKE2+ prover");
is(ccspake_verifier_init(ctx_v, cp, mac, rng, sizeof(aad), aad, sizeof(w0), w0, sizeof(L), L), 0, "Initialize SPAKE2+ verifier");
uint8_t U[pt_size], V[pt_size];
// Passing a wrong key share buffer length must fail.
is(ccspake_kex_generate(ctx_p, pt_size + 1, U),
CCERR_PARAMETER, "Passing a wrong key share buffer length must fail");
is(ccspake_kex_generate(ctx_p, sizeof(U), U), 0, "Generate U");
is(ccspake_kex_generate(ctx_v, sizeof(V), V), 0, "Generate V");
is(ccspake_kex_process(ctx_p, sizeof(V), V), 0, "Process V");
is(ccspake_kex_process(ctx_v, sizeof(U), U), 0, "Process U");
uint8_t mac_p[16], mac_v[16];
// Passing an excessive tag length must fail.
isnt(ccspake_mac_compute(ctx_p, mac->di->output_size + 1, mac_p), CCERR_OK, "Generate mac_p");
is(ccspake_mac_compute(ctx_p, sizeof(mac_p), mac_p), 0, "Generate mac_p");
is(ccspake_mac_compute(ctx_v, sizeof(mac_v), mac_v), 0, "Generate mac_v");
uint8_t sk_p[sk_len], sk_v[sk_len];
// Passing the wrong shared key length must fail.
is(ccspake_mac_verify_and_get_session_key(ctx_p, sizeof(mac_v), mac_v, sk_len + 1, sk_p),
CCERR_PARAMETER, "Passing the wrong shared key length must fail");
// Passing an excessive tag length must fail.
isnt(ccspake_mac_verify_and_get_session_key(ctx_p, mac->di->output_size + 1, mac_v, sizeof(sk_p), sk_p),
CCERR_OK, "Passing an excessive tag length must fail");
is(ccspake_mac_verify_and_get_session_key(ctx_p, sizeof(mac_v), mac_v, sizeof(sk_p), sk_p), 0, "Verify mac_v");
is(ccspake_mac_verify_and_get_session_key(ctx_v, sizeof(mac_p), mac_p, sizeof(sk_v), sk_v), 0, "Verify mac_p");
ok_memcmp_or_fail(sk_p, sk_v, sizeof(sk_p), "Shared keys don't match");
ccspake_ctx_clear(cp, ctx_p);
ccspake_ctx_clear(cp, ctx_v);
return 0;
}
static int test_1p5_rtt(ccspake_const_cp_t cp, ccspake_const_mac_t mac)
{
struct ccrng_state *rng = global_test_rng;
ccspake_ctx_decl(cp, ctx_p);
ccspake_ctx_decl(cp, ctx_v);
size_t w_size = ccspake_sizeof_w(cp);
size_t pt_size = ccspake_sizeof_point(cp);
uint8_t w0[w_size], w1[w_size];
ccrng_generate(rng, sizeof(w0), w0);
ccrng_generate(rng, sizeof(w1), w1);
uint8_t aad[16];
ccrng_generate(rng, sizeof(aad), aad);
is(ccspake_prover_init(ctx_p, cp, mac, rng, sizeof(aad), aad, sizeof(w0), w0, w1), 0, "Initialize SPAKE2+ prover");
uint8_t U[pt_size];
is(ccspake_kex_generate(ctx_p, sizeof(U), U), 0, "Generate U");
/* SEND FLIGHT 1/3 */
uint8_t L[pt_size];
is(ccspake_generate_L(cp, sizeof(w1), w1, sizeof(L), L, rng), 0, "Generate L from w1");
is(ccspake_verifier_init(ctx_v, cp, mac, rng, sizeof(aad), aad, sizeof(w0), w0, sizeof(L), L), 0, "Initialize SPAKE2+ verifier");
is(ccspake_kex_process(ctx_v, sizeof(U), U), 0, "Process U");
uint8_t V[pt_size];
is(ccspake_kex_generate(ctx_v, sizeof(V), V), 0, "Generate V");
uint8_t mac_v[16];
is(ccspake_mac_compute(ctx_v, sizeof(mac_v), mac_v), 0, "Generate mac_v");
/* SEND FLIGHT 2/3 */
is(ccspake_kex_process(ctx_p, sizeof(V), V), 0, "Process V");
uint8_t sk_p[16];
is(ccspake_mac_verify_and_get_session_key(ctx_p, sizeof(mac_v), mac_v, sizeof(sk_p), sk_p), 0, "Verify mac_v");
uint8_t mac_p[16];
is(ccspake_mac_compute(ctx_p, sizeof(mac_p), mac_p), 0, "Generate mac_p");
/* SEND FLIGHT 3/3 */
uint8_t sk_v[16];
is(ccspake_mac_verify_and_get_session_key(ctx_v, sizeof(mac_p), mac_p, sizeof(sk_v), sk_v), 0, "Verify mac_p");
ok_memcmp_or_fail(sk_p, sk_v, sizeof(sk_p), "Shared keys don't match");
ccspake_ctx_clear(cp, ctx_p);
ccspake_ctx_clear(cp, ctx_v);
return 0;
}
static int test_bogus_points(ccspake_const_cp_t cp)
{
struct ccrng_state *rng = global_test_rng;
ccspake_ctx_decl(cp, ctx);
ccspake_const_mac_t mac = ccspake_mac_hkdf_hmac_sha256();
size_t w_size = ccspake_sizeof_w(cp);
size_t pt_size = ccspake_sizeof_point(cp);
uint8_t w0[w_size], w1[w_size];
ccrng_generate(rng, sizeof(w0), w0);
ccrng_generate(rng, sizeof(w1), w1);
uint8_t L[pt_size];
is(ccspake_generate_L(cp, sizeof(w1), w1, sizeof(L), L, rng), 0, "Generate L from w1");
uint8_t B[pt_size];
memcpy(B, L, pt_size);
B[pt_size - 1] ^= 0x55;
// Reject points that aren't on the curve.
isnt(ccspake_verifier_init(ctx, cp, mac, rng, 0, NULL, sizeof(w0), w0, sizeof(B), B), 0,
"Initialize SPAKE2+ verifier should fail");
uint8_t Z[pt_size];
Z[0] = 0x04;
cc_clear(pt_size - 1, Z + 1);
// Reject the point at infinity.
isnt(ccspake_verifier_init(ctx, cp, mac, rng, 0, NULL, sizeof(w0), w0, sizeof(Z), Z), 0,
"Initialize SPAKE2+ verifier should fail");
is(ccspake_verifier_init(ctx, cp, mac, rng, 0, NULL, sizeof(w0), w0, sizeof(L), L), 0, "Initialize SPAKE2+ verifier");
uint8_t V[pt_size];
is(ccspake_kex_generate(ctx, sizeof(V), V), 0, "Generate V");
// We shouldn't get the same point back.
isnt(ccspake_kex_process(ctx, sizeof(V), V), 0, "Process V should fail");
// Reject points that aren't on the curve.
isnt(ccspake_kex_process(ctx, sizeof(B), B), 0, "Process B should fail");
// Reject the point at infinity.
isnt(ccspake_kex_process(ctx, sizeof(Z), Z), 0, "Process Z should fail");
// Reject invalid point lengths.
isnt(ccspake_kex_process(ctx, sizeof(Z) - 1, Z), 0, "Process Z-1 should fail");
ccspake_ctx_clear(cp, ctx);
return 0;
}
static int test_mac_mismatch(ccspake_const_cp_t cp, ccspake_const_mac_t mac1, ccspake_const_mac_t mac2)
{
struct ccrng_state *rng = global_test_rng;
ccspake_ctx_decl(cp, ctx_p);
ccspake_ctx_decl(cp, ctx_v);
size_t w_size = ccspake_sizeof_w(cp);
size_t pt_size = ccspake_sizeof_point(cp);
uint8_t w0[w_size], w1[w_size];
ccrng_generate(rng, sizeof(w0), w0);
ccrng_generate(rng, sizeof(w1), w1);
uint8_t aad[16];
ccrng_generate(rng, sizeof(aad), aad);
uint8_t L[pt_size];
is(ccspake_generate_L(cp, sizeof(w1), w1, sizeof(L), L, rng), 0, "Generate L from w1");
is(ccspake_prover_init(ctx_p, cp, mac1, rng, sizeof(aad), aad, sizeof(w0), w0, w1), 0, "Initialize SPAKE2+ prover");
w0[w_size - 1] ^= 0x55;
is(ccspake_verifier_init(ctx_v, cp, mac2, rng, sizeof(aad), aad, sizeof(w0), w0, sizeof(L), L), 0, "Initialize SPAKE2+ verifier");
uint8_t U[pt_size], V[pt_size];
is(ccspake_kex_generate(ctx_p, sizeof(U), U), 0, "Generate U");
is(ccspake_kex_generate(ctx_v, sizeof(V), V), 0, "Generate V");
is(ccspake_kex_process(ctx_p, sizeof(V), V), 0, "Process V");
is(ccspake_kex_process(ctx_v, sizeof(U), U), 0, "Process U");
uint8_t mac_p[16], mac_v[16];
is(ccspake_mac_compute(ctx_p, sizeof(mac_p), mac_p), 0, "Generate mac_p");
is(ccspake_mac_compute(ctx_v, sizeof(mac_v), mac_v), 0, "Generate mac_v");
uint8_t sk_p[16], sk_v[16];
isnt(ccspake_mac_verify_and_get_session_key(ctx_p, sizeof(mac_v), mac_v, sizeof(sk_p), sk_p), 0, "Verify mac_v should fail");
isnt(ccspake_mac_verify_and_get_session_key(ctx_v, sizeof(mac_p), mac_p, sizeof(sk_v), sk_v), 0, "Verify mac_p should fail");
ccspake_ctx_clear(cp, ctx_p);
ccspake_ctx_clear(cp, ctx_v);
return 0;
}
static int test_w0_mismatch(ccspake_const_cp_t cp, ccspake_const_mac_t mac)
{
struct ccrng_state *rng = global_test_rng;
ccspake_ctx_decl(cp, ctx_p);
ccspake_ctx_decl(cp, ctx_v);
size_t w_size = ccspake_sizeof_w(cp);
size_t pt_size = ccspake_sizeof_point(cp);
uint8_t w0[w_size], w1[w_size];
ccrng_generate(rng, sizeof(w0), w0);
ccrng_generate(rng, sizeof(w1), w1);
uint8_t aad[16];
ccrng_generate(rng, sizeof(aad), aad);
uint8_t L[pt_size];
is(ccspake_generate_L(cp, sizeof(w1), w1, sizeof(L), L, rng), 0, "Generate L from w1");
is(ccspake_prover_init(ctx_p, cp, mac, rng, sizeof(aad), aad, sizeof(w0), w0, w1), 0, "Initialize SPAKE2+ prover");
w0[w_size - 1] ^= 0x55;
is(ccspake_verifier_init(ctx_v, cp, mac, rng, sizeof(aad), aad, sizeof(w0), w0, sizeof(L), L), 0, "Initialize SPAKE2+ verifier");
uint8_t U[pt_size], V[pt_size];
is(ccspake_kex_generate(ctx_p, sizeof(U), U), 0, "Generate U");
is(ccspake_kex_generate(ctx_v, sizeof(V), V), 0, "Generate V");
is(ccspake_kex_process(ctx_p, sizeof(V), V), 0, "Process V");
is(ccspake_kex_process(ctx_v, sizeof(U), U), 0, "Process U");
uint8_t mac_p[16], mac_v[16];
is(ccspake_mac_compute(ctx_p, sizeof(mac_p), mac_p), 0, "Generate mac_p");
is(ccspake_mac_compute(ctx_v, sizeof(mac_v), mac_v), 0, "Generate mac_v");
uint8_t sk_p[16], sk_v[16];
isnt(ccspake_mac_verify_and_get_session_key(ctx_p, sizeof(mac_v), mac_v, sizeof(sk_p), sk_p), 0, "Verify mac_v should fail");
isnt(ccspake_mac_verify_and_get_session_key(ctx_v, sizeof(mac_p), mac_p, sizeof(sk_v), sk_v), 0, "Verify mac_p should fail");
ccspake_ctx_clear(cp, ctx_p);
ccspake_ctx_clear(cp, ctx_v);
return 0;
}
static int test_w1_mismatch(ccspake_const_cp_t cp, ccspake_const_mac_t mac)
{
struct ccrng_state *rng = global_test_rng;
ccspake_ctx_decl(cp, ctx_p);
ccspake_ctx_decl(cp, ctx_v);
size_t w_size = ccspake_sizeof_w(cp);
size_t pt_size = ccspake_sizeof_point(cp);
uint8_t w0[w_size], w1[w_size];
ccrng_generate(rng, sizeof(w0), w0);
ccrng_generate(rng, sizeof(w1), w1);
uint8_t aad[16];
ccrng_generate(rng, sizeof(aad), aad);
uint8_t L[pt_size];
is(ccspake_generate_L(cp, sizeof(w1), w1, sizeof(L), L, rng), 0, "Generate L from w1");
w1[w_size - 1] ^= 0x55;
is(ccspake_prover_init(ctx_p, cp, mac, rng, sizeof(aad), aad, sizeof(w0), w0, w1), 0, "Initialize SPAKE2+ prover");
is(ccspake_verifier_init(ctx_v, cp, mac, rng, sizeof(aad), aad, sizeof(w0), w0, sizeof(L), L), 0, "Initialize SPAKE2+ verifier");
uint8_t U[pt_size], V[pt_size];
is(ccspake_kex_generate(ctx_p, sizeof(U), U), 0, "Generate U");
is(ccspake_kex_generate(ctx_v, sizeof(V), V), 0, "Generate V");
is(ccspake_kex_process(ctx_p, sizeof(V), V), 0, "Process V");
is(ccspake_kex_process(ctx_v, sizeof(U), U), 0, "Process U");
uint8_t mac_p[16], mac_v[16];
is(ccspake_mac_compute(ctx_p, sizeof(mac_p), mac_p), 0, "Generate mac_p");
is(ccspake_mac_compute(ctx_v, sizeof(mac_v), mac_v), 0, "Generate mac_v");
uint8_t sk_p[16], sk_v[16];
isnt(ccspake_mac_verify_and_get_session_key(ctx_p, sizeof(mac_v), mac_v, sizeof(sk_p), sk_p), 0, "Verify mac_v should fail");
isnt(ccspake_mac_verify_and_get_session_key(ctx_v, sizeof(mac_p), mac_p, sizeof(sk_v), sk_v), 0, "Verify mac_p should fail");
ccspake_ctx_clear(cp, ctx_p);
ccspake_ctx_clear(cp, ctx_v);
return 0;
}
static int test_aad_mismatch(ccspake_const_cp_t cp, ccspake_const_mac_t mac)
{
struct ccrng_state *rng = global_test_rng;
ccspake_ctx_decl(cp, ctx_p);
ccspake_ctx_decl(cp, ctx_v);
size_t w_size = ccspake_sizeof_w(cp);
size_t pt_size = ccspake_sizeof_point(cp);
uint8_t w0[w_size], w1[w_size];
ccrng_generate(rng, sizeof(w0), w0);
ccrng_generate(rng, sizeof(w1), w1);
uint8_t aad1[16], aad2[16];
ccrng_generate(rng, sizeof(aad1), aad1);
ccrng_generate(rng, sizeof(aad2), aad2);
uint8_t L[pt_size];
is(ccspake_generate_L(cp, sizeof(w1), w1, sizeof(L), L, rng), 0, "Generate L from w1");
is(ccspake_prover_init(ctx_p, cp, mac, rng, sizeof(aad1), aad1, sizeof(w0), w0, w1), 0, "Initialize SPAKE2+ prover");
is(ccspake_verifier_init(ctx_v, cp, mac, rng, sizeof(aad2), aad2, sizeof(w0), w0, sizeof(L), L), 0, "Initialize SPAKE2+ verifier");
uint8_t U[pt_size], V[pt_size];
is(ccspake_kex_generate(ctx_p, sizeof(U), U), 0, "Generate U");
is(ccspake_kex_generate(ctx_v, sizeof(V), V), 0, "Generate V");
is(ccspake_kex_process(ctx_p, sizeof(V), V), 0, "Process V");
is(ccspake_kex_process(ctx_v, sizeof(U), U), 0, "Process U");
uint8_t mac_p[16], mac_v[16];
is(ccspake_mac_compute(ctx_p, sizeof(mac_p), mac_p), 0, "Generate mac_p");
is(ccspake_mac_compute(ctx_v, sizeof(mac_v), mac_v), 0, "Generate mac_v");
uint8_t sk_p[16], sk_v[16];
isnt(ccspake_mac_verify_and_get_session_key(ctx_p, sizeof(mac_v), mac_v, sizeof(sk_p), sk_p), 0, "Verify mac_v should fail");
isnt(ccspake_mac_verify_and_get_session_key(ctx_v, sizeof(mac_p), mac_p, sizeof(sk_v), sk_v), 0, "Verify mac_p should fail");
ccspake_ctx_clear(cp, ctx_p);
ccspake_ctx_clear(cp, ctx_v);
return 0;
}
static int test_bogus_kex(ccspake_const_cp_t cp, ccspake_const_mac_t mac)
{
struct ccrng_state *rng = global_test_rng;
ccspake_ctx_decl(cp, ctx_p);
ccspake_ctx_decl(cp, ctx_v);
size_t w_size = ccspake_sizeof_w(cp);
size_t pt_size = ccspake_sizeof_point(cp);
uint8_t w0[w_size], w1[w_size];
ccrng_generate(rng, sizeof(w0), w0);
ccrng_generate(rng, sizeof(w1), w1);
uint8_t aad[16];
ccrng_generate(rng, sizeof(aad), aad);
uint8_t L[pt_size];
is(ccspake_generate_L(cp, sizeof(w1), w1, sizeof(L), L, rng), 0, "Generate L from w1");
is(ccspake_prover_init(ctx_p, cp, mac, rng, sizeof(aad), aad, sizeof(w0), w0, w1), 0, "Initialize SPAKE2+ prover");
is(ccspake_verifier_init(ctx_v, cp, mac, rng, sizeof(aad), aad, sizeof(w0), w0, sizeof(L), L), 0, "Initialize SPAKE2+ verifier");
uint8_t U[pt_size], V[pt_size];
is(ccspake_kex_generate(ctx_p, sizeof(U), U), 0, "Generate U");
is(ccspake_kex_generate(ctx_v, sizeof(V), V), 0, "Generate V");
is(generate_point(cp, V), 0, "Override V");
is(ccspake_kex_process(ctx_p, sizeof(V), V), 0, "Process V");
is(ccspake_kex_process(ctx_v, sizeof(U), U), 0, "Process U");
uint8_t mac_p[16], mac_v[16];
is(ccspake_mac_compute(ctx_p, sizeof(mac_p), mac_p), 0, "Generate mac_p");
is(ccspake_mac_compute(ctx_v, sizeof(mac_v), mac_v), 0, "Generate mac_v");
uint8_t sk_p[16], sk_v[16];
isnt(ccspake_mac_verify_and_get_session_key(ctx_p, sizeof(mac_v), mac_v, sizeof(sk_p), sk_p), 0, "Verify mac_v should fail");
isnt(ccspake_mac_verify_and_get_session_key(ctx_v, sizeof(mac_p), mac_p, sizeof(sk_v), sk_v), 0, "Verify mac_p should fail");
ccspake_ctx_clear(cp, ctx_p);
ccspake_ctx_clear(cp, ctx_v);
return 0;
}
static int test_state_machine()
{
ccspake_const_cp_t cp = ccspake_cp_256();
struct ccrng_state *rng = global_test_rng;
ccspake_ctx_decl(cp, ctx_p);
ccspake_ctx_decl(cp, ctx_v);
ccspake_const_mac_t mac = ccspake_mac_hkdf_hmac_sha256();
size_t w_size = ccspake_sizeof_w(cp);
size_t pt_size = ccspake_sizeof_point(cp);
uint8_t w0[w_size], w1[w_size];
ccrng_generate(rng, sizeof(w0), w0);
ccrng_generate(rng, sizeof(w1), w1);
uint8_t aad[16];
ccrng_generate(rng, sizeof(aad), aad);
uint8_t L[pt_size];
is(ccspake_generate_L(cp, sizeof(w1), w1, sizeof(L), L, rng), 0, "Generate L from w1");
is(ccspake_prover_init(ctx_p, cp, mac, rng, sizeof(aad), aad, sizeof(w0), w0, w1), 0, "Initialize SPAKE2+ prover");
is(ccspake_verifier_init(ctx_v, cp, mac, rng, sizeof(aad), aad, sizeof(w0), w0, sizeof(L), L), 0, "Initialize SPAKE2+ verifier");
// P=STATE_INIT, V=STATE_INIT
uint8_t U[pt_size];
is(ccspake_kex_generate(ctx_p, sizeof(U), U), 0, "Generate U");
is(ccspake_kex_generate(ctx_p, sizeof(U), U), CCERR_CALL_SEQUENCE, "Generate U twice should fail");
is(ccspake_kex_process(ctx_v, sizeof(U), U), 0, "Process U");
is(ccspake_kex_process(ctx_v, sizeof(U), U), CCERR_CALL_SEQUENCE, "Process U twice should fail");
// P=STATE_KEX_GENERATE, V=STATE_KEX_PROCESS
uint8_t mac_p[16], mac_v[16];
is(ccspake_mac_compute(ctx_p, sizeof(mac_p), mac_p), CCERR_CALL_SEQUENCE, "Generate mac_p should fail");
is(ccspake_mac_compute(ctx_v, sizeof(mac_v), mac_v), CCERR_CALL_SEQUENCE, "Generate mac_v should fail");
uint8_t sk_p[16], sk_v[16];
is(ccspake_mac_verify_and_get_session_key(ctx_p, sizeof(mac_v), mac_v, sizeof(sk_p), sk_p), CCERR_CALL_SEQUENCE, "Verify mac_v should fail");
is(ccspake_mac_verify_and_get_session_key(ctx_v, sizeof(mac_p), mac_p, sizeof(sk_v), sk_v), CCERR_CALL_SEQUENCE, "Verify mac_p should fail");
uint8_t V[pt_size];
is(ccspake_kex_generate(ctx_v, sizeof(V), V), 0, "Generate V");
isnt(ccspake_kex_generate(ctx_v, sizeof(V), V), 0, "Generate V twice should fail");
is(ccspake_kex_process(ctx_p, sizeof(V), V), 0, "Process V");
isnt(ccspake_kex_process(ctx_p, sizeof(V), V), 0, "Process V twice should fail");
// P=STATE_KEX_BOTH, V=STATE_KEX_BOTH
is(ccspake_mac_compute(ctx_p, sizeof(mac_p), mac_p), 0, "Generate mac_p");
is(ccspake_mac_compute(ctx_p, sizeof(mac_p), mac_p), CCERR_CALL_SEQUENCE, "Generate mac_p twice should fail");
is(ccspake_mac_verify_and_get_session_key(ctx_v, sizeof(mac_p), mac_p, sizeof(sk_v), sk_v), 0, "Verify mac_p");
is(ccspake_mac_verify_and_get_session_key(ctx_v, sizeof(mac_p), mac_p, sizeof(sk_v), sk_v), CCERR_CALL_SEQUENCE, "Verify mac_p twice should fail");
// P=STATE_MAC_GENERATE, V=STATE_MAC_VERIFY
is(ccspake_mac_compute(ctx_v, sizeof(mac_v), mac_v), 0, "Generate mac_v");
is(ccspake_mac_compute(ctx_v, sizeof(mac_v), mac_v), CCERR_CALL_SEQUENCE, "Generate mac_v twice should fail");
is(ccspake_mac_verify_and_get_session_key(ctx_p, sizeof(mac_v), mac_v, sizeof(sk_p), sk_p), 0, "Verify mac_v");
is(ccspake_mac_verify_and_get_session_key(ctx_p, sizeof(mac_v), mac_v, sizeof(sk_p), sk_p), CCERR_CALL_SEQUENCE, "Verify mac_v twice should fail");
// P=STATE_MAC_BOTH, V=STATE_MAC_BOTH
ok_memcmp_or_fail(sk_p, sk_v, sizeof(sk_p), "MACs don't match");
ccspake_ctx_clear(cp, ctx_p);
ccspake_ctx_clear(cp, ctx_v);
return 0;
}
static void iterated_hash(const struct ccdigest_info *di, size_t seed_len, const uint8_t *seed, size_t n, uint8_t *out)
{
ccdigest(di, seed_len, seed, out);
for (size_t i = 1; i < n; i++) {
ccdigest(di, di->output_size, out, out);
}
}
static void bighash(size_t seed_len, const uint8_t *seed, uint16_t start, size_t sz, uint8_t *out)
{
const struct ccdigest_info *di = ccsha256_di();
size_t n = cc_ceiling(sz, di->output_size);
uint8_t digest[di->output_size];
for (size_t i = 0; i < n - 1; i++) {
iterated_hash(di, seed_len, seed, i + start, digest);
memcpy(out, digest, sizeof(digest));
out += sizeof(digest);
}
iterated_hash(di, seed_len, seed, n - 1 + start, digest);
memcpy(out, digest, sz - di->output_size * (n - 1));
}
static int test_fixed_point(ccec_const_cp_t cp, size_t seed_len, const uint8_t *seed, cc_unit *order, const cc_unit *xy)
{
struct ccrng_state *rng = global_test_rng;
cc_size n = ccec_cp_n(cp);
uint8_t encoded[1 + ccec_cp_prime_size(cp)];
ccec_point_decl_cp(cp, r);
ccec_point_decl_cp(cp, s);
for (uint16_t i = 1; i < 1000; i++) {
// Derive a pseudo-random point.
bighash(seed_len, seed, i, sizeof(encoded), encoded);
// Turn the first byte into either 0x02 or 0x03 (compressed format).
encoded[0] = (encoded[0] & 1) | 2;
cc_unit x[n];
is(ccn_read_uint(n, x, sizeof(encoded) - 1, encoded + 1), 0, "Reading x failed");
// Try to reconstruct a point from the given x-coordinate.
if (ccec_affine_point_from_x(cp, (ccec_affine_point_t)s, x) != 0) {
continue;
}
if (ccec_validate_pub_and_projectify(cp, r, (ccec_const_affine_point_t)s, rng) != 0) {
continue;
}
// Check that (r * #E) is a point on the curve.
if (ccec_mult(cp, s, order, r, rng) != 0 || !ccec_is_point(cp, s)) {
continue;
}
if (ccec_affinify(cp, (ccec_affine_point_t)r, r) != 0) {
continue;
}
// Compare the x-coordinate to the curve parameters.
is(ccn_cmp(n, xy, ccec_point_x(r, cp)), 0, "Wrong x-coordinate");
cc_unit *y = ccec_point_y(r, cp);
// Compute (p - y) if we have the wrong y.
if ((encoded[0] == 0x02) != (ccn_bit(y, 0) == 0)) {
ccn_sub(n, y, cczp_prime(ccec_cp_zp(cp)), y);
}
// Compare the y-coordinate to the curve parameters.
is(ccn_cmp(n, xy + n, y), 0, "Wrong y-coordinate");
return 0;
}
return -1;
}
/*
* Points for common groups as defined by the CFRG spec.
*
* Ensure the fixed points given by the spec were derived as stated and match
* the uncompressed versions listed in our SPAKE2+ curve parameter definitions.
*
* <https://tools.ietf.org/html/draft-irtf-cfrg-spake2-06#section-4>
*/
static int test_points_m_n()
{
const uint8_t seed256_m[] = "1.2.840.10045.3.1.7 point generation seed (M)";
const uint8_t seed256_n[] = "1.2.840.10045.3.1.7 point generation seed (N)";
const uint8_t seed384_m[] = "1.3.132.0.34 point generation seed (M)";
const uint8_t seed384_n[] = "1.3.132.0.34 point generation seed (N)";
const uint8_t seed521_m[] = "1.3.132.0.35 point generation seed (M)";
const uint8_t seed521_n[] = "1.3.132.0.35 point generation seed (N)";
/* clang-format off */
static cc_unit order256[CCN256_N] = {
CCN256_C(ff,ff,ff,ff,00,00,00,00,ff,ff,ff,ff,ff,ff,ff,ff,bc,e6,fa,ad,a7,17,9e,84,f3,b9,ca,c2,fc,63,25,51)
};
static cc_unit order384[CCN384_N] = {
CCN384_C(ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,c7,63,4d,81,f4,37,2d,df,58,1a,0d,b2,48,b0,a7,7a,ec,ec,19,6a,cc,c5,29,73)
};
static cc_unit order521[CCN521_N] = {
CCN528_C(00,7f,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,fd,15,b6,c6,47,46,fc,85,f7,36,b8,af,5e,7e,c5,3f,04,fb,d8,c4,56,9a,8f,1f,45,40,ea,24,35,f5,18,0d,6b)
};
/* clang-format on */
ccspake_const_cp_t scp256 = ccspake_cp_256();
ccec_const_cp_t cp256 = ccspake_cp_ec(scp256);
const cc_unit *M256 = ccspake_cp_ccn(scp256);
const cc_unit *N256 = M256 + CCN256_N * 2;
is(test_fixed_point(cp256, sizeof(seed256_m) - 1, seed256_m, order256, M256), 0, "Verifying M for P-256 failed");
is(test_fixed_point(cp256, sizeof(seed256_n) - 1, seed256_n, order256, N256), 0, "Verifying N for P-256 failed");
ccspake_const_cp_t scp384 = ccspake_cp_384();
ccec_const_cp_t cp384 = ccspake_cp_ec(scp384);
const cc_unit *M384 = ccspake_cp_ccn(scp384);
const cc_unit *N384 = M384 + CCN384_N * 2;
is(test_fixed_point(cp384, sizeof(seed384_m) - 1, seed384_m, order384, M384), 0, "Verifying M for P-384 failed");
is(test_fixed_point(cp384, sizeof(seed384_n) - 1, seed384_n, order384, N384), 0, "Verifying N for P-384 failed");
ccspake_const_cp_t scp521 = ccspake_cp_521();
ccec_const_cp_t cp521 = ccspake_cp_ec(scp521);
const cc_unit *M521 = ccspake_cp_ccn(scp521);
const cc_unit *N521 = M521 + CCN521_N * 2;
is(test_fixed_point(cp521, sizeof(seed521_m) - 1, seed521_m, order521, M521), 0, "Verifying M for P-521 failed");
is(test_fixed_point(cp521, sizeof(seed521_n) - 1, seed521_n, order521, N521), 0, "Verifying N for P-521 failed");
return 0;
}
int ccspake_tests(TM_UNUSED int argc, TM_UNUSED char *const *argv)
{
ccspake_const_cp_t curves[] = { ccspake_cp_256(), ccspake_cp_384(), ccspake_cp_521() };
ccspake_const_mac_t hkdf_cmac_sha256 = ccspake_mac_hkdf_cmac_aes128_sha256();
ccspake_const_mac_t hkdf_hmac_sha256 = ccspake_mac_hkdf_hmac_sha256();
ccspake_const_mac_t hkdf_hmac_sha512 = ccspake_mac_hkdf_hmac_sha512();
const struct ccspake_mac *macs[] = { hkdf_cmac_sha256, hkdf_hmac_sha256 };
int num_tests = 0;
num_tests += 13 * 2; // rtt tests
num_tests += 12 * 3; // w0/w1/aad mismatch
num_tests += 14; // bogus KEX
num_tests *= CC_ARRAY_LEN(curves) * CC_ARRAY_LEN(macs);
num_tests += 9 * CC_ARRAY_LEN(curves); // bogus points
num_tests += 13 * CC_ARRAY_LEN(curves); // 256-bit 2-rtt
num_tests += 12 * CC_ARRAY_LEN(curves); // mac mismatch
num_tests += 25; // state machine
num_tests += 742; // fixed point tests
plan_tests(num_tests);
for (size_t i = 0; i < CC_ARRAY_LEN(curves); i++) {
ccspake_const_cp_t cp = curves[i];
for (size_t j = 0; j < CC_ARRAY_LEN(macs); j++) {
ccspake_const_mac_t mac = macs[j];
is(test_2_rtt(cp, mac, 16), 0, "SPAKE2+ 2-RTT tests failed");
is(test_1p5_rtt(cp, mac), 0, "SPAKE2+ 1.5-RTT tests failed");
is(test_w0_mismatch(cp, mac), 0, "w0 mismatch tests failed");
is(test_w1_mismatch(cp, mac), 0, "w1 mismatch tests failed");
is(test_aad_mismatch(cp, mac), 0, "AAD mismatch tests failed");
is(test_bogus_kex(cp, mac), 0, "Bogus KEX tests failed");
}
is(test_bogus_points(cp), 0, "Bogus point tests failed");
is(test_2_rtt(cp, hkdf_hmac_sha512, 32), 0, "SPAKE2+ 256-bit 2-RTT tests failed");
is(test_mac_mismatch(cp, hkdf_cmac_sha256, hkdf_hmac_sha256), 0, "MAC mismatch test failed");
}
is(test_state_machine(), 0, "State machine tests failed");
is(test_points_m_n(), 0, "Deriving points M,N failed");
return 0;
}