/* 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 #include "ccckg_priv.h" #include "cc_priv.h" #include "testmore.h" #include 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; }