462 lines
16 KiB
C
462 lines
16 KiB
C
/* Copyright (c) (2019) 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/ccckg.h>
|
||
#include "ccckg_priv.h"
|
||
|
||
#include "cc_priv.h"
|
||
#include "testmore.h"
|
||
|
||
#include <corecrypto/ccsha2.h>
|
||
|
||
static int test_full_run(ccec_const_cp_t cp, const struct ccdigest_info *di)
|
||
{
|
||
struct ccrng_state *rng = global_test_rng;
|
||
|
||
ccec_full_ctx_decl_cp(cp, P_owner);
|
||
ccec_pub_ctx_decl_cp(cp, P_contrib);
|
||
|
||
ccec_ctx_init(cp, P_owner);
|
||
ccec_ctx_init(cp, P_contrib);
|
||
|
||
int rv;
|
||
|
||
ccckg_ctx_decl(cp, di, ctx_a);
|
||
ccckg_ctx_decl(cp, di, ctx_b);
|
||
|
||
ccckg_init(ctx_a, cp, di, rng);
|
||
ccckg_init(ctx_b, cp, di, rng);
|
||
|
||
uint8_t commitment[ccckg_sizeof_commitment(cp, di)];
|
||
rv = ccckg_contributor_commit(ctx_a, sizeof(commitment), commitment);
|
||
is(rv, 0, "Generated commitment");
|
||
|
||
uint8_t share[ccckg_sizeof_share(cp, di)];
|
||
rv = ccckg_owner_generate_share(ctx_b, sizeof(commitment), commitment, sizeof(share), share);
|
||
is(rv, 0, "Generated share");
|
||
|
||
uint8_t sk_a[32];
|
||
uint8_t opening[ccckg_sizeof_opening(cp, di)];
|
||
rv = ccckg_contributor_finish(ctx_a, sizeof(share), share, sizeof(opening), opening, P_contrib, sizeof(sk_a), sk_a);
|
||
is(rv, 0, "Opened commitment");
|
||
|
||
uint8_t sk_b[32];
|
||
rv = ccckg_owner_finish(ctx_b, sizeof(opening), opening, P_owner, sizeof(sk_b), sk_b);
|
||
is(rv, 0, "Owner finished");
|
||
|
||
ok_ccn_cmp(ccec_cp_n(cp), ccec_ctx_x(P_contrib), ccec_ctx_x(P_owner), "Ps don't match");
|
||
ok_memcmp_or_fail(sk_a, sk_b, sizeof(sk_a), "SKs don't match");
|
||
|
||
ccckg_ctx_clear(cp, di, ctx_a);
|
||
ccckg_ctx_clear(cp, di, ctx_b);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int test_bogus_inputs()
|
||
{
|
||
struct ccrng_state *rng = global_test_rng;
|
||
const struct ccdigest_info *di = ccsha256_di();
|
||
ccec_const_cp_t cp = ccec_cp_256();
|
||
|
||
ccec_full_ctx_decl_cp(cp, P_owner);
|
||
ccec_pub_ctx_decl_cp(cp, P_contrib);
|
||
|
||
ccec_full_ctx_decl_cp(ccec_cp_384(), P_bogus_full);
|
||
ccec_pub_ctx_decl_cp(ccec_cp_384(), P_bogus);
|
||
|
||
ccec_ctx_init(cp, P_owner);
|
||
ccec_ctx_init(cp, P_contrib);
|
||
|
||
int rv;
|
||
|
||
ccckg_ctx_decl(cp, di, ctx_a);
|
||
ccckg_ctx_decl(cp, di, ctx_b);
|
||
|
||
ccckg_init(ctx_a, cp, di, rng);
|
||
ccckg_init(ctx_b, cp, di, rng);
|
||
|
||
uint8_t commitment[ccckg_sizeof_commitment(cp, di)];
|
||
|
||
// Passing the wrong commitment size must fail.
|
||
rv = ccckg_contributor_commit(ctx_a, sizeof(commitment) + 1, commitment);
|
||
is(rv, CCERR_PARAMETER, "Generated commitment");
|
||
|
||
rv = ccckg_contributor_commit(ctx_a, sizeof(commitment), commitment);
|
||
is(rv, 0, "Generated commitment");
|
||
|
||
uint8_t share[ccckg_sizeof_share(cp, di)];
|
||
|
||
// Passing the wrong commitment size must fail.
|
||
rv = ccckg_owner_generate_share(ctx_b, sizeof(commitment) + 1, commitment, sizeof(share), share);
|
||
is(rv, CCERR_PARAMETER, "ccckg_owner_generate_share should fail");
|
||
|
||
// Passing the wrong share size must fail.
|
||
rv = ccckg_owner_generate_share(ctx_b, sizeof(commitment), commitment, sizeof(share) + 1, share);
|
||
is(rv, CCERR_PARAMETER, "ccckg_owner_generate_share should fail");
|
||
|
||
rv = ccckg_owner_generate_share(ctx_b, sizeof(commitment), commitment, sizeof(share), share);
|
||
is(rv, 0, "Generated share");
|
||
|
||
uint8_t sk_a[32], sk_b[32];
|
||
uint8_t opening[ccckg_sizeof_opening(cp, di)];
|
||
|
||
// Passing the wrong share size must fail.
|
||
rv = ccckg_contributor_finish(ctx_a, sizeof(share) + 1, share, sizeof(opening), opening, P_contrib, sizeof(sk_a), sk_a);
|
||
is(rv, CCERR_PARAMETER, "ccckg_contributor_finish should fail");
|
||
|
||
// Passing the wrong opening size must fail.
|
||
rv = ccckg_contributor_finish(ctx_a, sizeof(share), share, sizeof(opening) + 1, opening, P_contrib, sizeof(sk_a), sk_a);
|
||
is(rv, CCERR_PARAMETER, "ccckg_contributor_finish should fail");
|
||
|
||
// Passing a point on the wrong curve must fail.
|
||
rv = ccckg_contributor_finish(ctx_a, sizeof(share), share, sizeof(opening), opening, P_bogus, sizeof(sk_a), sk_a);
|
||
isnt(rv, CCERR_OK, "ccckg_contributor_finish should fail");
|
||
|
||
// Passing a share with the wrong format must fail.
|
||
share[0] = 0x02;
|
||
rv = ccckg_contributor_finish(ctx_a, sizeof(share), share, sizeof(opening), opening, P_contrib, sizeof(sk_a), sk_a);
|
||
isnt(rv, CCERR_OK, "ccckg_contributor_finish should fail");
|
||
|
||
share[0] = 0x04;
|
||
rv = ccckg_contributor_finish(ctx_a, sizeof(share), share, sizeof(opening), opening, P_contrib, sizeof(sk_a), sk_a);
|
||
is(rv, 0, "Opened commitment");
|
||
|
||
// Passing the wrong opening size must fail.
|
||
rv = ccckg_owner_finish(ctx_b, sizeof(opening) + 1, opening, P_owner, sizeof(sk_b), sk_b);
|
||
is(rv, CCERR_PARAMETER, "ccckg_owner_finish should fail");
|
||
|
||
// Passing a point on the wrong curve must fail.
|
||
rv = ccckg_owner_finish(ctx_b, sizeof(opening), opening, P_bogus_full, sizeof(sk_b), sk_b);
|
||
is(rv, CCERR_PARAMETER, "ccckg_owner_finish should fail");
|
||
|
||
rv = ccckg_owner_finish(ctx_b, sizeof(opening), opening, P_owner, sizeof(sk_b), sk_b);
|
||
is(rv, 0, "Owner finished");
|
||
|
||
ok_ccn_cmp(ccec_cp_n(cp), ccec_ctx_x(P_contrib), ccec_ctx_x(P_owner), "Ps don't match");
|
||
ok_memcmp_or_fail(sk_a, sk_b, sizeof(sk_a), "SKs don't match");
|
||
|
||
ccckg_ctx_clear(cp, di, ctx_a);
|
||
ccckg_ctx_clear(cp, di, ctx_b);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int test_bogus_commitment()
|
||
{
|
||
ccec_const_cp_t cp = ccec_cp_256();
|
||
struct ccrng_state *rng = global_test_rng;
|
||
const struct ccdigest_info *di = ccsha256_di();
|
||
|
||
ccec_full_ctx_decl_cp(cp, P_owner);
|
||
ccec_pub_ctx_decl_cp(cp, P_contrib);
|
||
|
||
ccec_ctx_init(cp, P_owner);
|
||
ccec_ctx_init(cp, P_contrib);
|
||
|
||
int rv;
|
||
|
||
ccckg_ctx_decl(cp, di, ctx_a);
|
||
ccckg_ctx_decl(cp, di, ctx_b);
|
||
|
||
ccckg_init(ctx_a, cp, di, rng);
|
||
ccckg_init(ctx_b, cp, di, rng);
|
||
|
||
uint8_t commitment[ccckg_sizeof_commitment(cp, di)];
|
||
rv = ccckg_contributor_commit(ctx_a, sizeof(commitment), commitment);
|
||
is(rv, 0, "Generated commitment");
|
||
|
||
// Corrupt the commitment.
|
||
commitment[0] ^= 0x01;
|
||
|
||
uint8_t share[ccckg_sizeof_share(cp, di)];
|
||
rv = ccckg_owner_generate_share(ctx_b, sizeof(commitment), commitment, sizeof(share), share);
|
||
is(rv, 0, "Generated share");
|
||
|
||
uint8_t sk_a[32];
|
||
uint8_t opening[ccckg_sizeof_opening(cp, di)];
|
||
rv = ccckg_contributor_finish(ctx_a, sizeof(share), share, sizeof(opening), opening, P_contrib, sizeof(sk_a), sk_a);
|
||
is(rv, 0, "Opened commitment");
|
||
|
||
uint8_t sk_b[32];
|
||
rv = ccckg_owner_finish(ctx_b, sizeof(opening), opening, P_owner, sizeof(sk_b), sk_b);
|
||
is(rv, CCERR_INTEGRITY, "Invalid commitment");
|
||
|
||
ccckg_ctx_clear(cp, di, ctx_a);
|
||
ccckg_ctx_clear(cp, di, ctx_b);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int test_bogus_scalar()
|
||
{
|
||
ccec_const_cp_t cp = ccec_cp_256();
|
||
struct ccrng_state *rng = global_test_rng;
|
||
const struct ccdigest_info *di = ccsha256_di();
|
||
|
||
ccec_full_ctx_decl_cp(cp, P_owner);
|
||
ccec_ctx_init(cp, P_owner);
|
||
|
||
int rv;
|
||
|
||
ccckg_ctx_decl(cp, di, ctx_b);
|
||
ccckg_init(ctx_b, cp, di, rng);
|
||
|
||
// Assemble commitment data with an invalid scalar.
|
||
uint8_t commitment_data[ccckg_sizeof_opening(cp, di)];
|
||
ccn_write_uint_padded(ccec_cp_n(cp), cczp_prime(ccec_cp_zq(cp)), ccec_cp_order_size(cp), commitment_data);
|
||
|
||
// Build the commitment.
|
||
uint8_t commitment[ccckg_sizeof_commitment(cp, di)];
|
||
ccdigest(di, sizeof(commitment_data), commitment_data, commitment);
|
||
|
||
uint8_t share[ccckg_sizeof_share(cp, di)];
|
||
rv = ccckg_owner_generate_share(ctx_b, sizeof(commitment), commitment, sizeof(share), share);
|
||
is(rv, 0, "Generated share");
|
||
|
||
uint8_t sk_b[32];
|
||
rv = ccckg_owner_finish(ctx_b, sizeof(commitment_data), commitment_data, P_owner, sizeof(sk_b), sk_b);
|
||
is(rv, CCERR_PARAMETER, "Invalid scalar");
|
||
|
||
ccckg_ctx_clear(cp, di, ctx_b);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int test_bogus_share()
|
||
{
|
||
ccec_const_cp_t cp = ccec_cp_256();
|
||
struct ccrng_state *rng = global_test_rng;
|
||
const struct ccdigest_info *di = ccsha256_di();
|
||
|
||
ccec_pub_ctx_decl_cp(cp, P_contrib);
|
||
ccec_ctx_init(cp, P_contrib);
|
||
|
||
int rv;
|
||
|
||
ccckg_ctx_decl(cp, di, ctx_a);
|
||
ccckg_ctx_decl(cp, di, ctx_b);
|
||
|
||
ccckg_init(ctx_a, cp, di, rng);
|
||
ccckg_init(ctx_b, cp, di, rng);
|
||
|
||
uint8_t commitment[ccckg_sizeof_commitment(cp, di)];
|
||
rv = ccckg_contributor_commit(ctx_a, sizeof(commitment), commitment);
|
||
is(rv, 0, "Generated commitment");
|
||
|
||
uint8_t share[ccckg_sizeof_share(cp, di)];
|
||
rv = ccckg_owner_generate_share(ctx_b, sizeof(commitment), commitment, sizeof(share), share);
|
||
is(rv, 0, "Generated share");
|
||
|
||
// Corrupt the share.
|
||
share[ccec_export_pub_size(P_contrib) - 1] ^= 0x55;
|
||
|
||
uint8_t sk_a[32];
|
||
uint8_t opening[ccckg_sizeof_opening(cp, di)];
|
||
rv = ccckg_contributor_finish(ctx_a, sizeof(share), share, sizeof(opening), opening, P_contrib, sizeof(sk_a), sk_a);
|
||
isnt(rv, 0, "Invalid share");
|
||
|
||
ccckg_ctx_clear(cp, di, ctx_a);
|
||
ccckg_ctx_clear(cp, di, ctx_b);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int test_bogus_share_infinity()
|
||
{
|
||
ccec_const_cp_t cp = ccec_cp_256();
|
||
struct ccrng_state *rng = global_test_rng;
|
||
const struct ccdigest_info *di = ccsha256_di();
|
||
|
||
ccec_pub_ctx_decl_cp(cp, P_contrib);
|
||
ccec_ctx_init(cp, P_contrib);
|
||
|
||
int rv;
|
||
|
||
ccckg_ctx_decl(cp, di, ctx_a);
|
||
ccckg_ctx_decl(cp, di, ctx_b);
|
||
|
||
ccckg_init(ctx_a, cp, di, rng);
|
||
ccckg_init(ctx_b, cp, di, rng);
|
||
|
||
uint8_t commitment[ccckg_sizeof_commitment(cp, di)];
|
||
rv = ccckg_contributor_commit(ctx_a, sizeof(commitment), commitment);
|
||
is(rv, 0, "Generated commitment");
|
||
|
||
uint8_t share[ccckg_sizeof_share(cp, di)];
|
||
rv = ccckg_owner_generate_share(ctx_b, sizeof(commitment), commitment, sizeof(share), share);
|
||
is(rv, 0, "Generated share");
|
||
|
||
// Turn the share into the point at infinity.
|
||
cc_clear(ccec_export_pub_size(P_contrib) - 1, share + 1);
|
||
|
||
uint8_t sk_a[32];
|
||
uint8_t opening[ccckg_sizeof_opening(cp, di)];
|
||
rv = ccckg_contributor_finish(ctx_a, sizeof(share), share, sizeof(opening), opening, P_contrib, sizeof(sk_a), sk_a);
|
||
isnt(rv, 0, "Invalid share");
|
||
|
||
ccckg_ctx_clear(cp, di, ctx_a);
|
||
ccckg_ctx_clear(cp, di, ctx_b);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int test_bogus_opening()
|
||
{
|
||
ccec_const_cp_t cp = ccec_cp_256();
|
||
struct ccrng_state *rng = global_test_rng;
|
||
const struct ccdigest_info *di = ccsha256_di();
|
||
|
||
ccec_full_ctx_decl_cp(cp, P_owner);
|
||
ccec_pub_ctx_decl_cp(cp, P_contrib);
|
||
|
||
ccec_ctx_init(cp, P_owner);
|
||
ccec_ctx_init(cp, P_contrib);
|
||
|
||
int rv;
|
||
|
||
ccckg_ctx_decl(cp, di, ctx_a);
|
||
ccckg_ctx_decl(cp, di, ctx_b);
|
||
|
||
ccckg_init(ctx_a, cp, di, rng);
|
||
ccckg_init(ctx_b, cp, di, rng);
|
||
|
||
uint8_t commitment[ccckg_sizeof_commitment(cp, di)];
|
||
rv = ccckg_contributor_commit(ctx_a, sizeof(commitment), commitment);
|
||
is(rv, 0, "Generated commitment");
|
||
|
||
uint8_t share[ccckg_sizeof_share(cp, di)];
|
||
rv = ccckg_owner_generate_share(ctx_b, sizeof(commitment), commitment, sizeof(share), share);
|
||
is(rv, 0, "Generated share");
|
||
|
||
uint8_t sk_a[32];
|
||
uint8_t opening[ccckg_sizeof_opening(cp, di)];
|
||
rv = ccckg_contributor_finish(ctx_a, sizeof(share), share, sizeof(opening), opening, P_contrib, sizeof(sk_a), sk_a);
|
||
is(rv, 0, "Opened commitment");
|
||
|
||
// Corrupt the opening.
|
||
opening[0] ^= 0x01;
|
||
|
||
uint8_t sk_b[32];
|
||
rv = ccckg_owner_finish(ctx_b, sizeof(opening), opening, P_owner, sizeof(sk_b), sk_b);
|
||
is(rv, CCERR_INTEGRITY, "Invalid commitment");
|
||
|
||
ccckg_ctx_clear(cp, di, ctx_a);
|
||
ccckg_ctx_clear(cp, di, ctx_b);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int test_state_machine()
|
||
{
|
||
ccec_const_cp_t cp = ccec_cp_256();
|
||
struct ccrng_state *rng = global_test_rng;
|
||
const struct ccdigest_info *di = ccsha256_di();
|
||
|
||
ccec_full_ctx_decl_cp(cp, P_owner);
|
||
ccec_pub_ctx_decl_cp(cp, P_contrib);
|
||
|
||
ccec_ctx_init(cp, P_owner);
|
||
ccec_ctx_init(cp, P_contrib);
|
||
|
||
int rv;
|
||
|
||
ccckg_ctx_decl(cp, di, ctx_a);
|
||
ccckg_ctx_decl(cp, di, ctx_b);
|
||
|
||
ccckg_init(ctx_a, cp, di, rng);
|
||
ccckg_init(ctx_b, cp, di, rng);
|
||
|
||
uint8_t commitment[ccckg_sizeof_commitment(cp, di)];
|
||
uint8_t share[ccckg_sizeof_share(cp, di)];
|
||
uint8_t opening[ccckg_sizeof_opening(cp, di)];
|
||
|
||
// A=STATE_INIT, B=STATE_INIT
|
||
|
||
uint8_t sk_a[32], sk_b[32];
|
||
rv = ccckg_contributor_finish(ctx_a, sizeof(share), share, sizeof(opening), opening, P_contrib, sizeof(sk_a), sk_a);
|
||
is(rv, CCERR_CALL_SEQUENCE, "Shouldn't be able to finish yet");
|
||
rv = ccckg_owner_finish(ctx_b, sizeof(opening), opening, P_owner, sizeof(sk_b), sk_b);
|
||
is(rv, CCERR_CALL_SEQUENCE, "Shouldn't be able to finish yet");
|
||
|
||
rv = ccckg_contributor_commit(ctx_a, sizeof(commitment), commitment);
|
||
is(rv, 0, "Generated commitment");
|
||
rv = ccckg_owner_generate_share(ctx_b, sizeof(commitment), commitment, sizeof(share), share);
|
||
is(rv, 0, "Generated share");
|
||
|
||
// A=STATE_COMMIT, B=STATE_SHARE
|
||
|
||
rv = ccckg_contributor_commit(ctx_a, sizeof(commitment), commitment);
|
||
is(rv, CCERR_CALL_SEQUENCE, "Shouldn't be able to commit twice");
|
||
rv = ccckg_owner_generate_share(ctx_b, sizeof(commitment), commitment, sizeof(share), share);
|
||
is(rv, CCERR_CALL_SEQUENCE, "Shouldn't be able to share twice");
|
||
|
||
rv = ccckg_contributor_finish(ctx_a, sizeof(share), share, sizeof(opening), opening, P_contrib, sizeof(sk_a), sk_a);
|
||
is(rv, 0, "Opened commitment");
|
||
rv = ccckg_owner_finish(ctx_b, sizeof(opening), opening, P_owner, sizeof(sk_b), sk_b);
|
||
is(rv, 0, "Owner finished");
|
||
|
||
// A=STATE_FINISH, B=STATE_FINISH
|
||
|
||
rv = ccckg_contributor_commit(ctx_a, sizeof(commitment), commitment);
|
||
is(rv, CCERR_CALL_SEQUENCE, "Shouldn't be able to commit twice");
|
||
rv = ccckg_owner_generate_share(ctx_b, sizeof(commitment), commitment, sizeof(share), share);
|
||
is(rv, CCERR_CALL_SEQUENCE, "Shouldn't be able to share twice");
|
||
|
||
rv = ccckg_contributor_finish(ctx_a, sizeof(share), share, sizeof(opening), opening, P_contrib, sizeof(sk_a), sk_a);
|
||
is(rv, CCERR_CALL_SEQUENCE, "Shouldn't be able to finish twice");
|
||
rv = ccckg_owner_finish(ctx_b, sizeof(opening), opening, P_owner, sizeof(sk_b), sk_b);
|
||
is(rv, CCERR_CALL_SEQUENCE, "Shouldn't be able to finish twice");
|
||
|
||
ok_ccn_cmp(ccec_cp_n(cp), ccec_ctx_x(P_contrib), ccec_ctx_x(P_owner), "Ps don't match");
|
||
ok_memcmp_or_fail(sk_a, sk_b, sizeof(sk_a), "SKs don't match");
|
||
|
||
ccckg_ctx_clear(cp, di, ctx_a);
|
||
ccckg_ctx_clear(cp, di, ctx_b);
|
||
|
||
return 0;
|
||
}
|
||
|
||
int ccckg_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 *hashes[] = { ccsha256_di(), ccsha384_di(), ccsha512_di() };
|
||
|
||
int num_tests = 0;
|
||
num_tests += 7; // full run tests
|
||
num_tests *= CC_ARRAY_LEN(curves) * CC_ARRAY_LEN(hashes);
|
||
num_tests += 5; // bogus commitment
|
||
num_tests += 3; // bogus scalar
|
||
num_tests += 8; // bogus shares
|
||
num_tests += 5; // bogus opening
|
||
num_tests += 15; // state machine
|
||
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(hashes); j++) {
|
||
const struct ccdigest_info *di = hashes[j];
|
||
|
||
is(test_full_run(cp, di), 0, "Full run test");
|
||
}
|
||
}
|
||
|
||
is(test_bogus_inputs(), 0, "Bogus input test");
|
||
is(test_bogus_commitment(), 0, "Bogus commitment test");
|
||
is(test_bogus_scalar(), 0, "Bogus scalar test");
|
||
is(test_bogus_share(), 0, "Bogus share test");
|
||
is(test_bogus_share_infinity(), 0, "Bogus share test");
|
||
is(test_bogus_opening(), 0, "Bogus opening test");
|
||
is(test_state_machine(), 0, "State machine test");
|
||
|
||
return 0;
|
||
}
|