@@ -304,4 +304,98 @@ int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof
304304 proof , plen , min_value , & commitp , blind , nonce , exp , min_bits , value , message , msg_len , extra_commit , extra_commit_len , & genp );
305305}
306306
307+ 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 ) {
308+ secp256k1_ge commitp ;
309+ secp256k1_ge genp ;
310+ secp256k1_gej tmpj ;
311+ secp256k1_gej xj ;
312+ secp256k1_ge rp ;
313+ secp256k1_scalar es ;
314+ secp256k1_scalar ss ;
315+ secp256k1_sha256 sha2 ;
316+ unsigned char tmpch [33 ];
317+ unsigned char pp_comm [32 ];
318+ size_t offset ;
319+ size_t sz ;
320+ int overflow ;
321+
322+ VERIFY_CHECK (ctx != NULL );
323+ ARG_CHECK (proof != NULL );
324+ ARG_CHECK (commit != NULL );
325+ ARG_CHECK (gen != NULL );
326+
327+ if (plen != 73 && plen != 65 ) {
328+ return 0 ;
329+ }
330+ /* 0x80 must be unset for any rangeproof; 0x40 indicates "has nonzero range"
331+ * so must also be unset for single-value proofs */
332+ if ((proof [0 ] & 0xC0 ) != 0x00 ) {
333+ return 0 ;
334+ }
335+
336+ secp256k1_pedersen_commitment_load (& commitp , commit );
337+ secp256k1_generator_load (& genp , gen );
338+ /* Verify that value in the header is what we expect; 0x20 is "has nonzero min-value" */
339+ if ((proof [0 ] & 0x20 ) == 0x00 ) {
340+ if (value != 0 ) {
341+ return 0 ;
342+ }
343+ offset = 1 ;
344+ } else {
345+ int i ;
346+ uint64_t claimed = 0 ;
347+ for (i = 0 ; i < 8 ; i ++ ) {
348+ claimed = (claimed << 8 ) | proof [1 + i ];
349+ }
350+ if (value != claimed ) {
351+ return 0 ;
352+ }
353+ offset = 9 ;
354+ }
355+ /* Subtract value from commitment; store modified commitment in xj */
356+ secp256k1_pedersen_ecmult_small (& tmpj , value , & genp );
357+ secp256k1_gej_neg (& tmpj , & tmpj );
358+ secp256k1_gej_add_ge_var (& xj , & tmpj , & commitp , NULL );
359+
360+ /* Now we just have a Schnorr signature in (e, s) form. The verification
361+ * equation is e == H(sG - eX || proof params) */
362+
363+ /* 1. Compute slow/overwrought commitment to proof params */
364+ secp256k1_sha256_initialize (& sha2 );
365+ secp256k1_rangeproof_serialize_point (tmpch , & commitp );
366+ secp256k1_sha256_write (& sha2 , tmpch , 33 );
367+ secp256k1_rangeproof_serialize_point (tmpch , & genp );
368+ secp256k1_sha256_write (& sha2 , tmpch , 33 );
369+ secp256k1_sha256_write (& sha2 , proof , offset ); /* lol we commit to one extra byte here */
370+ secp256k1_sha256_finalize (& sha2 , pp_comm );
371+
372+ /* ... feed this into our hash */
373+ secp256k1_borromean_hash (tmpch , pp_comm , 32 , & proof [offset ], 32 , 0 , 0 );
374+ secp256k1_scalar_set_b32 (& es , tmpch , & overflow );
375+ if (overflow || secp256k1_scalar_is_zero (& es )) {
376+ return 0 ;
377+ }
378+
379+ /* 1. Compute R = sG - eX */
380+ secp256k1_scalar_set_b32 (& ss , & proof [offset + 32 ], & overflow );
381+ if (overflow || secp256k1_scalar_is_zero (& ss )) {
382+ return 0 ;
383+ }
384+ secp256k1_ecmult (& tmpj , & xj , & es , & ss );
385+ if (secp256k1_gej_is_infinity (& tmpj )) {
386+ return 0 ;
387+ }
388+ secp256k1_ge_set_gej (& rp , & tmpj );
389+ secp256k1_eckey_pubkey_serialize (& rp , tmpch , & sz , 1 );
390+
391+ /* 2. Compute e = H(R || proof params) */
392+ secp256k1_sha256_initialize (& sha2 );
393+ secp256k1_sha256_write (& sha2 , tmpch , sz );
394+ secp256k1_sha256_write (& sha2 , pp_comm , sizeof (pp_comm ));
395+ secp256k1_sha256_finalize (& sha2 , tmpch );
396+
397+ /* 3. Check computed e against original e */
398+ return !memcmp (tmpch , & proof [offset ], 32 );
399+ }
400+
307401#endif
0 commit comments