Skip to content

Commit

Permalink
rangeproof: add method to verify single-value proofs
Browse files Browse the repository at this point in the history
  • Loading branch information
apoelstra committed Jul 22, 2022
1 parent 9eea081 commit 3cd525b
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 0 deletions.
94 changes: 94 additions & 0 deletions src/modules/rangeproof/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,4 +304,98 @@ 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_value(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 {
int i;
uint64_t claimed = 0;
for (i = 0; i < 8; i++) {
claimed = (claimed << 8) | proof[1 + i];
}
if (value != claimed) {
return 0;
}
offset = 9;
}
/* 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) */

/* 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 */
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 !memcmp(tmpch, &proof[offset], 32);
}

#endif
2 changes: 2 additions & 0 deletions src/modules/rangeproof/tests_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,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_value(ctx, vector_3, sizeof(vector_3), UINT64_MAX, &pc, secp256k1_generator_h));
}
}

Expand Down

0 comments on commit 3cd525b

Please sign in to comment.