@@ -304,4 +304,97 @@ 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+ uint64_t claimed = 0 ;
346+ /* Iterate from 0 to 8, setting `offset` to 9 as a side-effect */
347+ for (offset = 1 ; offset < 9 ; offset ++ ) {
348+ claimed = (claimed << 8 ) | proof [offset ];
349+ }
350+ if (value != claimed ) {
351+ return 0 ;
352+ }
353+ }
354+ /* Subtract value from commitment; store modified commitment in xj */
355+ secp256k1_pedersen_ecmult_small (& tmpj , value , & genp );
356+ secp256k1_gej_neg (& tmpj , & tmpj );
357+ secp256k1_gej_add_ge_var (& xj , & tmpj , & commitp , NULL );
358+
359+ /* Now we just have a Schnorr signature in (e, s) form. The verification
360+ * equation is e == H(sG - eX || proof params) */
361+
362+ /* 1. Compute slow/overwrought commitment to proof params */
363+ secp256k1_sha256_initialize (& sha2 );
364+ secp256k1_rangeproof_serialize_point (tmpch , & commitp );
365+ secp256k1_sha256_write (& sha2 , tmpch , 33 );
366+ secp256k1_rangeproof_serialize_point (tmpch , & genp );
367+ secp256k1_sha256_write (& sha2 , tmpch , 33 );
368+ secp256k1_sha256_write (& sha2 , proof , offset ); /* lol we commit to one extra byte here */
369+ secp256k1_sha256_finalize (& sha2 , pp_comm );
370+
371+ /* ... feed this into our hash */
372+ secp256k1_borromean_hash (tmpch , pp_comm , 32 , & proof [offset ], 32 , 0 , 0 );
373+ secp256k1_scalar_set_b32 (& es , tmpch , & overflow );
374+ if (overflow || secp256k1_scalar_is_zero (& es )) {
375+ return 0 ;
376+ }
377+
378+ /* 1. Compute R = sG - eX */
379+ secp256k1_scalar_set_b32 (& ss , & proof [offset + 32 ], & overflow );
380+ if (overflow || secp256k1_scalar_is_zero (& ss )) {
381+ return 0 ;
382+ }
383+ secp256k1_ecmult (& tmpj , & xj , & es , & ss );
384+ if (secp256k1_gej_is_infinity (& tmpj )) {
385+ return 0 ;
386+ }
387+ secp256k1_ge_set_gej (& rp , & tmpj );
388+ secp256k1_eckey_pubkey_serialize (& rp , tmpch , & sz , 1 );
389+
390+ /* 2. Compute e = H(R || proof params) */
391+ secp256k1_sha256_initialize (& sha2 );
392+ secp256k1_sha256_write (& sha2 , tmpch , sz );
393+ secp256k1_sha256_write (& sha2 , pp_comm , sizeof (pp_comm ));
394+ secp256k1_sha256_finalize (& sha2 , tmpch );
395+
396+ /* 3. Check computed e against original e */
397+ return !memcmp (tmpch , & proof [offset ], 32 );
398+ }
399+
307400#endif
0 commit comments