Skip to content

Commit

Permalink
rangeproof: add verify_value function to create single-value proofs
Browse files Browse the repository at this point in the history
  • Loading branch information
apoelstra committed Aug 3, 2022
1 parent 593686c commit 9d28292
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 7 deletions.
30 changes: 26 additions & 4 deletions include/secp256k1_rangeproof.h
Original file line number Diff line number Diff line change
Expand Up @@ -287,10 +287,10 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info(
) 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_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.)
* of a Pedersen commitment. Such proofs can be created with `secp256k1_rangeproof_create_value`,
* 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
Expand All @@ -309,6 +309,28 @@ SECP256K1_API int secp256k1_rangeproof_verify_value(
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_value(
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);

# ifdef __cplusplus
}
# endif
Expand Down
105 changes: 103 additions & 2 deletions src/modules/rangeproof/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ int secp256k1_rangeproof_verify_value(const secp256k1_context* ctx, const unsign
/* Now we just have a Schnorr signature in (e, s) form. The verification
* equation is e == H(sG - eX || proof params) */

/* 1. Compute slow/overwrought commitment to 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);
Expand All @@ -375,7 +375,7 @@ int secp256k1_rangeproof_verify_value(const secp256k1_context* ctx, const unsign
return 0;
}

/* 1. Compute R = sG - eX */
/* 1. Compute R = sG + eX */
secp256k1_scalar_set_b32(&ss, &proof[offset + 32], &overflow);
if (overflow || secp256k1_scalar_is_zero(&ss)) {
return 0;
Expand All @@ -397,4 +397,105 @@ int secp256k1_rangeproof_verify_value(const secp256k1_context* ctx, const unsign
return !memcmp(tmpch, &proof[offset], 32);
}

int secp256k1_rangeproof_create_value(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_scalar es;
secp256k1_scalar tmps;
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 < 73 || (value == 0 && *plen < 65)) {
return 0;
}

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); /* lol we commit to one extra byte here */
secp256k1_sha256_finalize(&sha2, pp_comm);

/* ... feed this into our hash e */
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;
}

/* ... and compute -ex from this */
secp256k1_scalar_set_b32(&tmps, blind, &overflow);
if (overflow || secp256k1_scalar_is_zero(&tmps)) {
secp256k1_scalar_clear(&tmps);
secp256k1_scalar_clear(&es);
return 0;
}
secp256k1_scalar_mul(&es, &es, &tmps);
secp256k1_scalar_negate(&es, &es);

/* 2. Compute random k and set `es` to k - ex */
secp256k1_sha256_initialize(&sha2);
secp256k1_sha256_write(&sha2, blind, 32);
secp256k1_sha256_write(&sha2, pp_comm, 32);
secp256k1_sha256_finalize(&sha2, tmpch);
secp256k1_scalar_set_b32(&tmps, tmpch, &overflow);
if (overflow || secp256k1_scalar_is_zero(&tmps)) {
secp256k1_scalar_clear(&es);
return 0;
}
secp256k1_scalar_add(&es, &es, &tmps);
secp256k1_scalar_get_b32(&proof[offset + 32], &es);

/* Compute R = kG and serialize it*/
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &tmpj, &tmps);
secp256k1_scalar_clear(&tmps);
secp256k1_ge_set_gej(&genp, &tmpj); /* Reuse genp which is no longer used */
secp256k1_eckey_pubkey_serialize(&genp, tmpch, &sz, 1);

/* 3. Compute e0 = H(R || proof params) and serialize it */
secp256k1_sha256_initialize(&sha2);
secp256k1_sha256_write(&sha2, tmpch, sz);
secp256k1_sha256_write(&sha2, pp_comm, sizeof(pp_comm));
secp256k1_sha256_finalize(&sha2, &proof[offset]);

return 1;
}

#endif
49 changes: 48 additions & 1 deletion src/modules/rangeproof/tests_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -225,6 +225,53 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c
CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, 0, NULL) == 0);
CHECK(*ecount == 29);
}

{
*ecount = 0;
len = sizeof(proof);
CHECK(secp256k1_rangeproof_create_value(none, proof, &len, val, blind, &commit, secp256k1_generator_h) == 1);
CHECK(secp256k1_rangeproof_create_value(vrfy, proof, &len, val, blind, &commit, secp256k1_generator_h) == 1);
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, val, blind, &commit, secp256k1_generator_h) == 1);
CHECK(secp256k1_rangeproof_create_value(sign, NULL, &len, val, blind, &commit, secp256k1_generator_h) == 0);
CHECK(*ecount == 1);
CHECK(secp256k1_rangeproof_create_value(sign, proof, NULL, val, blind, &commit, secp256k1_generator_h) == 0);
CHECK(*ecount == 2);
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, val, NULL, &commit, secp256k1_generator_h) == 0);
CHECK(*ecount == 3);
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, val, blind, NULL, secp256k1_generator_h) == 0);
CHECK(*ecount == 4);
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, val, blind, &commit, NULL) == 0);
CHECK(*ecount == 5);
len = 0;
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, 0, blind, &commit, secp256k1_generator_h) == 0);
len = 64;
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, 0, blind, &commit, secp256k1_generator_h) == 0);
len = 65;
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, 0, blind, &commit, secp256k1_generator_h) == 0);
len = 65;
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, 1, blind, &commit, secp256k1_generator_h) == 0);
len = 72;
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, 1, blind, &commit, secp256k1_generator_h) == 0);
len = 73;
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, val, blind, &commit, secp256k1_generator_h) == 1);
CHECK(*ecount == 5);

*ecount = 0;
CHECK(secp256k1_rangeproof_verify_value(none, proof, len, val, &commit, secp256k1_generator_h) == 1);
CHECK(secp256k1_rangeproof_verify_value(sign, proof, len, val, &commit, secp256k1_generator_h) == 1);
CHECK(secp256k1_rangeproof_verify_value(vrfy, proof, len, val, &commit, secp256k1_generator_h) == 1);
CHECK(*ecount == 0);
CHECK(secp256k1_rangeproof_verify_value(vrfy, NULL, len, val, &commit, secp256k1_generator_h) == 0);
CHECK(*ecount == 1);
CHECK(secp256k1_rangeproof_verify_value(vrfy, proof, len, val, NULL, secp256k1_generator_h) == 0);
CHECK(*ecount == 2);
CHECK(secp256k1_rangeproof_verify_value(vrfy, proof, len, val, &commit, NULL) == 0);
CHECK(*ecount == 3);
CHECK(secp256k1_rangeproof_verify_value(vrfy, proof, 0, val, &commit, secp256k1_generator_h) == 0);
CHECK(secp256k1_rangeproof_verify_value(vrfy, proof, len - 1, val, &commit, secp256k1_generator_h) == 0);
CHECK(secp256k1_rangeproof_verify_value(vrfy, proof, len, val ^ 1, &commit, secp256k1_generator_h) == 0);
CHECK(*ecount == 3);
}
}

static void test_api(void) {
Expand Down

0 comments on commit 9d28292

Please sign in to comment.