diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 9bb014545..a3d286eb9 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -296,6 +296,51 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info( size_t plen ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); +/** Verify a rangeproof with a single-value range. Useful as a "proof of value" + * of a Pedersen commitment. Such proofs can be created with `secp256k1_rangeproof_create_exact`, + * or with `secp256k1_rangeproof_sign` by passing an `exp` parameter of -1 and the + * target value as both `value` and `min_value`. (In this case `min_bits` is ignored + * and may take any value, but for clarity it's best to pass zero.) + * Returns 1: Proof was valid and proved the given value + * 0: Otherwise + * In: ctx: pointer to a context object + * proof: pointer to character array with the proof. + * plen: length of proof in bytes. + * value: value being claimed for the Pedersen commitment + * commit: the Pedersen commitment whose value is being verified + * gen: additional generator 'h' + */ +SECP256K1_API int secp256k1_rangeproof_verify_exact( + const secp256k1_context* ctx, + const unsigned char* proof, + size_t plen, + uint64_t value, + const secp256k1_pedersen_commitment* commit, + const secp256k1_generator* gen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); + +/** Create a rangeproof with a single-value range. + * Returns 1: Proof was successfully generated + * 0: Otherwise. The contents of `proof` are unspecified in this case. + * Args: ctx: pointer to a context object + * Out: proof: pointer to character array to populate the proof with. Must be at least 73 + * bytes unless `value` is 0, in which case it must be at least 65 bytes + * In/Out: plen: length of the `proof` buffer; will be overwritten with the actual length + * In: value: value being claimed for the Pedersen commitment + * blind: the blinding factor for the Pedersen commitment `commit` + * commit: the Pedersen commitment whose value is being proven + * gen: additional generator 'h' + */ +SECP256K1_API int secp256k1_rangeproof_create_exact( + const secp256k1_context* ctx, + unsigned char* proof, + size_t* plen, + uint64_t value, + const unsigned char* blind, + const secp256k1_pedersen_commitment* commit, + const secp256k1_generator* gen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7); + /** Returns an upper bound on the size of a rangeproof with the given parameters * * An actual rangeproof may be smaller, for example if the actual value diff --git a/include/secp256k1_surjectionproof.h b/include/secp256k1_surjectionproof.h index ab7a4a9ec..cf08c7fcc 100644 --- a/include/secp256k1_surjectionproof.h +++ b/include/secp256k1_surjectionproof.h @@ -263,6 +263,24 @@ SECP256K1_API int secp256k1_surjectionproof_verify( ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); #endif +/** Verify a single-input surjectionproof. Such proofs are sometimes useful to + * prove in zero knowledge that a given commitment commits to a specific asset. + * They can be verified with much less memory than general proofs. + * Returns 0: proof was invalid + * 1: proof was valid + * + * In: ctx: pointer to a context object, initialized for signing and verification + * proof: proof to be verified + * input_tag: the ephemeral asset tag of the sole input + * output_tag: the ephemeral asset tag of the output + */ +SECP256K1_API int secp256k1_surjectionproof_verify_single( + const secp256k1_context* ctx, + const secp256k1_surjectionproof* proof, + const secp256k1_generator* input_tag, + const secp256k1_generator* output_tag +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + #ifdef __cplusplus } #endif diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index 432f4b959..8dca5b176 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -304,6 +304,209 @@ int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof proof, plen, min_value, &commitp, blind, nonce, exp, min_bits, value, message, msg_len, extra_commit, extra_commit_len, &genp); } +int secp256k1_rangeproof_verify_exact(const secp256k1_context* ctx, const unsigned char* proof, size_t plen, uint64_t value, const secp256k1_pedersen_commitment* commit, const secp256k1_generator* gen) { + secp256k1_ge commitp; + secp256k1_ge genp; + secp256k1_gej tmpj; + secp256k1_gej xj; + secp256k1_ge rp; + secp256k1_scalar es; + secp256k1_scalar ss; + secp256k1_sha256 sha2; + unsigned char tmpch[33]; + unsigned char pp_comm[32]; + size_t offset; + size_t sz; + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(gen != NULL); + + if (plen != 73 && plen != 65) { + return 0; + } + /* 0x80 must be unset for any rangeproof; 0x40 indicates "has nonzero range" + * so must also be unset for single-value proofs */ + if ((proof[0] & 0xC0) != 0x00) { + return 0; + } + + secp256k1_pedersen_commitment_load(&commitp, commit); + secp256k1_generator_load(&genp, gen); + /* Verify that value in the header is what we expect; 0x20 is "has nonzero min-value" */ + if ((proof[0] & 0x20) == 0x00) { + if (value != 0) { + return 0; + } + offset = 1; + } else { + uint64_t claimed = 0; + /* Iterate from 0 to 8, setting `offset` to 9 as a side-effect */ + for (offset = 1; offset < 9; offset++) { + claimed = (claimed << 8) | proof[offset]; + } + if (value != claimed) { + return 0; + } + } + /* Subtract value from commitment; store modified commitment in xj */ + secp256k1_pedersen_ecmult_small(&tmpj, value, &genp); + secp256k1_gej_neg(&tmpj, &tmpj); + secp256k1_gej_add_ge_var(&xj, &tmpj, &commitp, NULL); + + /* Now we just have a Schnorr signature in (e, s) form. The verification + * equation is e == H(sG - eX || proof params) */ + + /* 0. Compute slow/overwrought commitment to proof params */ + secp256k1_sha256_initialize(&sha2); + secp256k1_rangeproof_serialize_point(tmpch, &commitp); + secp256k1_sha256_write(&sha2, tmpch, 33); + secp256k1_rangeproof_serialize_point(tmpch, &genp); + secp256k1_sha256_write(&sha2, tmpch, 33); + secp256k1_sha256_write(&sha2, proof, offset); + secp256k1_sha256_finalize(&sha2, pp_comm); + + /* ... feed this into our hash */ + secp256k1_borromean_hash(tmpch, pp_comm, 32, &proof[offset], 32, 0, 0); + secp256k1_scalar_set_b32(&es, tmpch, &overflow); + if (overflow || secp256k1_scalar_is_zero(&es)) { + return 0; + } + + /* 1. Compute R = sG + eX */ + secp256k1_scalar_set_b32(&ss, &proof[offset + 32], &overflow); + if (overflow || secp256k1_scalar_is_zero(&ss)) { + return 0; + } + secp256k1_ecmult(&tmpj, &xj, &es, &ss); + if (secp256k1_gej_is_infinity(&tmpj)) { + return 0; + } + secp256k1_ge_set_gej(&rp, &tmpj); + secp256k1_eckey_pubkey_serialize(&rp, tmpch, &sz, 1); + + /* 2. Compute e = H(R || proof params) */ + secp256k1_sha256_initialize(&sha2); + secp256k1_sha256_write(&sha2, tmpch, sz); + secp256k1_sha256_write(&sha2, pp_comm, sizeof(pp_comm)); + secp256k1_sha256_finalize(&sha2, tmpch); + + /* 3. Check computed e against original e */ + return !secp256k1_memcmp_var(tmpch, &proof[offset], 32); +} + +int secp256k1_rangeproof_create_exact(const secp256k1_context* ctx, unsigned char* proof, size_t* plen, uint64_t value, const unsigned char* blind, const secp256k1_pedersen_commitment* commit, const secp256k1_generator* gen) { + secp256k1_ge commitp; + secp256k1_ge genp; + secp256k1_gej tmpj; + secp256k1_ge tmpp; + secp256k1_scalar es; + secp256k1_scalar ks; + secp256k1_scalar xs; + secp256k1_sha256 sha2; + unsigned char tmpch[33]; + unsigned char pp_comm[32]; + size_t offset; + size_t sz; + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(proof != NULL); + ARG_CHECK(plen != NULL); + ARG_CHECK(blind != NULL); + ARG_CHECK(commit != NULL); + ARG_CHECK(gen != NULL); + + if (*plen < (value == 0 ? 65 : 73)) { + return 0; + } + *plen = value == 0 ? 65 : 73; + + secp256k1_pedersen_commitment_load(&commitp, commit); + secp256k1_generator_load(&genp, gen); + + /* Encode header */ + if (value > 0) { + proof[0] = 0x20; + proof[1] = value >> 56; + proof[2] = value >> 48; + proof[3] = value >> 40; + proof[4] = value >> 32; + proof[5] = value >> 24; + proof[6] = value >> 16; + proof[7] = value >> 8; + proof[8] = value; + offset = 9; + } else { + proof[0] = 0x00; + offset = 1; + } + + /* Now we have to make a Schnorr signature in (e, s) form. */ + + /* 1. Compute slow/overwrought commitment to proof params */ + secp256k1_sha256_initialize(&sha2); + secp256k1_rangeproof_serialize_point(tmpch, &commitp); + secp256k1_sha256_write(&sha2, tmpch, 33); + secp256k1_rangeproof_serialize_point(tmpch, &genp); + secp256k1_sha256_write(&sha2, tmpch, 33); + secp256k1_sha256_write(&sha2, proof, offset); + secp256k1_sha256_finalize(&sha2, pp_comm); + + /* 2. Compute random k */ + secp256k1_sha256_initialize(&sha2); + secp256k1_sha256_write(&sha2, blind, 32); + secp256k1_sha256_write(&sha2, proof, offset); + secp256k1_sha256_write(&sha2, pp_comm, 32); + secp256k1_sha256_finalize(&sha2, tmpch); + secp256k1_scalar_set_b32(&ks, tmpch, &overflow); + if (overflow || secp256k1_scalar_is_zero(&ks)) { + secp256k1_scalar_clear(&ks); + memset(tmpch, 0, sizeof(tmpch)); + return 0; + } + + /* 3. Compute R = kG */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &tmpj, &ks); + secp256k1_ge_set_gej(&tmpp, &tmpj); + + /* 4. Compute e0 = H(R || proof params) and serialize it into the proof */ + secp256k1_sha256_initialize(&sha2); + secp256k1_eckey_pubkey_serialize(&tmpp, tmpch, &sz, 1); + secp256k1_sha256_write(&sha2, tmpch, sz); + secp256k1_sha256_write(&sha2, pp_comm, sizeof(pp_comm)); + secp256k1_sha256_finalize(&sha2, &proof[offset]); + + /* ... feed this into our hash e, along with e0 */ + secp256k1_borromean_hash(tmpch, pp_comm, 32, &proof[offset], 32, 0, 0); + secp256k1_scalar_set_b32(&es, tmpch, &overflow); + if (overflow || secp256k1_scalar_is_zero(&es)) { + secp256k1_scalar_clear(&ks); + secp256k1_scalar_clear(&es); + return 0; + } + + /* 5. Compute k - ex from this, and serialize it */ + secp256k1_scalar_set_b32(&xs, blind, &overflow); + if (overflow || secp256k1_scalar_is_zero(&xs)) { + secp256k1_scalar_clear(&ks); + secp256k1_scalar_clear(&xs); + secp256k1_scalar_clear(&es); + return 0; + } + secp256k1_scalar_mul(&es, &es, &xs); + secp256k1_scalar_negate(&es, &es); + secp256k1_scalar_add(&es, &es, &ks); + secp256k1_scalar_get_b32(&proof[offset + 32], &es); + + secp256k1_scalar_clear(&ks); + secp256k1_scalar_clear(&xs); + return 1; +} + size_t secp256k1_rangeproof_max_size(const secp256k1_context* ctx, uint64_t max_value, int min_bits) { const int val_mantissa = max_value > 0 ? 64 - secp256k1_clz64_var(max_value) : 1; const int mantissa = min_bits > val_mantissa ? min_bits : val_mantissa; diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 9c9207343..6dcadb67d 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -76,7 +76,7 @@ static void test_pedersen_api(const secp256k1_context *none, const secp256k1_con CHECK(*ecount == 14); } -static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const secp256k1_context *both, const secp256k1_context *sttc, const int32_t *ecount) { +static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const secp256k1_context *both, const secp256k1_context *sttc, int32_t *ecount) { unsigned char proof[5134]; unsigned char blind[32]; secp256k1_pedersen_commitment commit; @@ -226,6 +226,49 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c CHECK(*ecount == 29); } + { + *ecount = 0; + len = sizeof(proof); + CHECK(secp256k1_rangeproof_create_exact(none, proof, &len, val, blind, &commit, secp256k1_generator_h) == 1); + CHECK(secp256k1_rangeproof_create_exact(none, NULL, &len, val, blind, &commit, secp256k1_generator_h) == 0); + CHECK(*ecount == 1); + CHECK(secp256k1_rangeproof_create_exact(none, proof, NULL, val, blind, &commit, secp256k1_generator_h) == 0); + CHECK(*ecount == 2); + CHECK(secp256k1_rangeproof_create_exact(none, proof, &len, val, NULL, &commit, secp256k1_generator_h) == 0); + CHECK(*ecount == 3); + CHECK(secp256k1_rangeproof_create_exact(none, proof, &len, val, blind, NULL, secp256k1_generator_h) == 0); + CHECK(*ecount == 4); + CHECK(secp256k1_rangeproof_create_exact(none, proof, &len, val, blind, &commit, NULL) == 0); + CHECK(*ecount == 5); + len = 0; + CHECK(secp256k1_rangeproof_create_exact(none, proof, &len, 0, blind, &commit, secp256k1_generator_h) == 0); + len = 64; + CHECK(secp256k1_rangeproof_create_exact(none, proof, &len, 0, blind, &commit, secp256k1_generator_h) == 0); + len = 65; + CHECK(secp256k1_rangeproof_create_exact(none, proof, &len, 0, blind, &commit, secp256k1_generator_h) == 1); + len = 65; + CHECK(secp256k1_rangeproof_create_exact(none, proof, &len, 1, blind, &commit, secp256k1_generator_h) == 0); + len = 72; + CHECK(secp256k1_rangeproof_create_exact(none, proof, &len, 1, blind, &commit, secp256k1_generator_h) == 0); + len = 73; + CHECK(secp256k1_rangeproof_create_exact(none, proof, &len, val, blind, &commit, secp256k1_generator_h) == 1); + CHECK(*ecount == 5); + + *ecount = 0; + CHECK(secp256k1_rangeproof_verify_exact(none, proof, len, val, &commit, secp256k1_generator_h) == 1); + CHECK(*ecount == 0); + CHECK(secp256k1_rangeproof_verify_exact(none, NULL, len, val, &commit, secp256k1_generator_h) == 0); + CHECK(*ecount == 1); + CHECK(secp256k1_rangeproof_verify_exact(none, proof, len, val, NULL, secp256k1_generator_h) == 0); + CHECK(*ecount == 2); + CHECK(secp256k1_rangeproof_verify_exact(none, proof, len, val, &commit, NULL) == 0); + CHECK(*ecount == 3); + CHECK(secp256k1_rangeproof_verify_exact(none, proof, 0, val, &commit, secp256k1_generator_h) == 0); + CHECK(secp256k1_rangeproof_verify_exact(none, proof, len - 1, val, &commit, secp256k1_generator_h) == 0); + CHECK(secp256k1_rangeproof_verify_exact(none, proof, len, val ^ 1, &commit, secp256k1_generator_h) == 0); + CHECK(*ecount == 3); + } + /* This constant is hardcoded in these tests and elsewhere, so we * consider it to be part of the API and test it here. */ CHECK(secp256k1_rangeproof_max_size(none, 0, 64) == 5134); @@ -624,6 +667,7 @@ static void test_single_value_proof(uint64_t val) { uint64_t val_out = 0; size_t m_len_out = 0; + const int using_exact_value = secp256k1_testrand32() & 1; secp256k1_testrand256(blind); secp256k1_testrand256(nonce); @@ -642,19 +686,27 @@ static void test_single_value_proof(uint64_t val) { secp256k1_generator_h ) == 0); - plen = sizeof(proof); - CHECK(secp256k1_rangeproof_sign( - ctx, - proof, &plen, - val, /* min_val */ - &commit, blind, nonce, - -1, /* exp: -1 is magic value to indicate a single-value proof */ - 0, /* min_bits */ - val, /* val */ - NULL, 0, - NULL, 0, - secp256k1_generator_h - ) == 1); + if (using_exact_value && val == 0) { + plen = 70; /* sanity-check that plen can be <73 for 0-value proofs */ + } else { + plen = sizeof(proof); + } + if (using_exact_value) { + CHECK(secp256k1_rangeproof_create_exact(ctx, proof, &plen, val, blind, &commit, secp256k1_generator_h) == 1); + } else { + CHECK(secp256k1_rangeproof_sign( + ctx, + proof, &plen, + val, /* min_val */ + &commit, blind, nonce, + -1, /* exp: -1 is magic value to indicate a single-value proof */ + 0, /* min_bits */ + val, /* val */ + NULL, 0, + NULL, 0, + secp256k1_generator_h + ) == 1); + } CHECK(plen <= secp256k1_rangeproof_max_size(ctx, val, 0)); /* Different proof sizes are unfortunate but is caused by `min_value` of @@ -678,25 +730,29 @@ static void test_single_value_proof(uint64_t val) { memset(message_out, 0, sizeof(message_out)); m_len_out = sizeof(message_out); - CHECK(secp256k1_rangeproof_rewind( - ctx, - blind_out, &val_out, - message_out, &m_len_out, - nonce, - &min_val_out, &max_val_out, - &commit, - proof, plen, - NULL, 0, - secp256k1_generator_h - )); - CHECK(val_out == val); - CHECK(min_val_out == val); - CHECK(max_val_out == val); - CHECK(m_len_out == 0); - CHECK(secp256k1_memcmp_var(blind, blind_out, 32) == 0); - for (m_len_out = 0; m_len_out < sizeof(message_out); m_len_out++) { - CHECK(message_out[m_len_out] == 0); + /* exact-value proofs cannot be rewound */ + if (!using_exact_value) { + CHECK(secp256k1_rangeproof_rewind( + ctx, + blind_out, &val_out, + message_out, &m_len_out, + nonce, + &min_val_out, &max_val_out, + &commit, + proof, plen, + NULL, 0, + secp256k1_generator_h + ) == 1); + CHECK(val_out == val); + CHECK(min_val_out == val); + CHECK(max_val_out == val); + CHECK(m_len_out == 0); + CHECK(secp256k1_memcmp_var(blind, blind_out, 32) == 0); + for (m_len_out = 0; m_len_out < sizeof(message_out); m_len_out++) { + CHECK(message_out[m_len_out] == 0); + } } + CHECK(secp256k1_rangeproof_verify_exact(ctx, proof, plen, val, &commit, secp256k1_generator_h)); } #define MAX_N_GENS 30 @@ -983,6 +1039,8 @@ void test_rangeproof_fixed_vectors(void) { CHECK(min_value == UINT64_MAX); CHECK(max_value == UINT64_MAX); CHECK(m_len == 0); + + CHECK(secp256k1_rangeproof_verify_exact(ctx, vector_3, sizeof(vector_3), UINT64_MAX, &pc, secp256k1_generator_h)); } } @@ -1549,6 +1607,10 @@ void run_rangeproof_tests(void) { test_single_value_proof(0); test_single_value_proof(12345678); test_single_value_proof(UINT64_MAX); + for (i = 0; i < count / 2; i++) { + test_single_value_proof(secp256k1_testrand32()); + test_single_value_proof(((uint64_t) secp256k1_testrand32() << 32) + secp256k1_testrand32()); + } test_rangeproof_fixed_vectors(); test_rangeproof_fixed_vectors_reproducible(); diff --git a/src/modules/surjection/main_impl.h b/src/modules/surjection/main_impl.h index a214f90b3..d9966765f 100644 --- a/src/modules/surjection/main_impl.h +++ b/src/modules/surjection/main_impl.h @@ -401,4 +401,74 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256 return secp256k1_borromean_verify(NULL, &proof->data[0], borromean_s, ring_pubkeys, rsizes, 1, msg32, 32); } +int secp256k1_surjectionproof_verify_single(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof, const secp256k1_generator* input_tag, const secp256k1_generator* output_tag) { + secp256k1_ge inputp; + secp256k1_ge outputp; + secp256k1_gej tmpj; + secp256k1_gej xj; + secp256k1_ge rp; + secp256k1_scalar es; + secp256k1_scalar ss; + secp256k1_sha256 sha2; + unsigned char tmpch[33]; + unsigned char pp_comm[32]; + size_t sz; + int overflow; + + /* Validate and decode surjectionproof data */ + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(proof != NULL); + ARG_CHECK(input_tag != NULL); + ARG_CHECK(output_tag != NULL); +#ifdef VERIFY + CHECK(proof->initialized == 1); +#endif + + if (proof->n_inputs != 1 || proof->used_inputs[0] != 1) { + return 0; + } + + secp256k1_generator_load(&inputp, input_tag); + secp256k1_generator_load(&outputp, output_tag); + secp256k1_ge_neg(&inputp, &inputp); + secp256k1_gej_set_ge(&xj, &inputp); + secp256k1_gej_add_ge(&xj, &xj, &outputp); + + /* Now we just have a Schnorr signature in (e, s) form. The verification + * equation is e == H(sG - eX || proof params), where X is the difference + * between the output and input. */ + + /* 1. Compute slow/overwrought commitment to proof params */ + secp256k1_surjection_genmessage(pp_comm, input_tag, 1, output_tag); + /* (past this point the code is identical to rangeproof_verify_value) */ + + /* ... feed this into our hash */ + secp256k1_borromean_hash(tmpch, pp_comm, 32, &proof->data[0], 32, 0, 0); + secp256k1_scalar_set_b32(&es, tmpch, &overflow); + if (overflow || secp256k1_scalar_is_zero(&es)) { + return 0; + } + + /* 1. Compute R = sG - eX */ + secp256k1_scalar_set_b32(&ss, &proof->data[32], &overflow); + if (overflow || secp256k1_scalar_is_zero(&ss)) { + return 0; + } + secp256k1_ecmult(&tmpj, &xj, &es, &ss); + if (secp256k1_gej_is_infinity(&tmpj)) { + return 0; + } + secp256k1_ge_set_gej(&rp, &tmpj); + secp256k1_eckey_pubkey_serialize(&rp, tmpch, &sz, 1); + + /* 2. Compute e = H(R || proof params) */ + secp256k1_sha256_initialize(&sha2); + secp256k1_sha256_write(&sha2, tmpch, sz); + secp256k1_sha256_write(&sha2, pp_comm, sizeof(pp_comm)); + secp256k1_sha256_finalize(&sha2, tmpch); + + /* 3. Check computed e against original e */ + return !secp256k1_memcmp_var(tmpch, &proof->data[0], 32); +} + #endif diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index a792bb5f6..cb873798b 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -445,6 +445,10 @@ static void test_gen_verify(size_t n_inputs, size_t n_used) { CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof, serialized_len)); result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]); CHECK(result == 1); + if (n_inputs == 1) { + result = secp256k1_surjectionproof_verify_single(ctx, &proof, ephemeral_input_tags, &ephemeral_input_tags[n_inputs]); + CHECK(result == 1); + } /* various fail cases */ if (n_inputs > 1) { @@ -720,6 +724,7 @@ void run_surjection_tests(void) { test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); test_input_selection_distribution(); + test_gen_verify(1, 1); test_gen_verify(10, 3); test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); test_no_used_inputs_verify();