Line data Source code
1 : #include "../fd_zksdk_private.h" 2 : 3 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/zero_ciphertext.rs#L167 */ 4 : static inline void 5 : zero_ciphertext_hash_context( fd_zksdk_transcript_t * transcript, 6 : uchar const pubkey [ 32 ], 7 30 : uchar const ciphertext[ 64 ] ) { 8 30 : fd_zksdk_transcript_append_pubkey ( transcript, FD_TRANSCRIPT_LITERAL("pubkey"), pubkey ); 9 30 : fd_zksdk_transcript_append_ciphertext( transcript, FD_TRANSCRIPT_LITERAL("ciphertext"), ciphertext ); 10 30 : } 11 : 12 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/zero_ciphertext.rs#L98 */ 13 : static inline int 14 : fd_zksdk_verify_proof_zero_ciphertext( 15 : fd_zksdk_zero_ciphertext_proof_t const * proof, 16 : uchar const pubkey [ 32 ], 17 : uchar const ciphertext[ 64 ], 18 32 : fd_zksdk_transcript_t * transcript ) { 19 : /* 20 : We need to verify the 2 following equivalences. 21 : Instead of verifying them one by one, it's more efficient to pack 22 : them up in a single MSM (and to do so we have to mul by 1, w). 23 : 24 : ( z P =?= c H + Y_P ) * 1 25 : ( z D =?= c C + Y_D ) * w 26 : 27 : We store points and scalars in the following arrays: 28 : 29 : points scalars 30 : 0 H -c 31 : 1 P z 32 : 2 C -wc 33 : 3 D wz 34 : 4 Y_D -w 35 : ----------------------- MSM 36 : Y_P 37 : */ 38 : 39 : /* Validate all inputs. */ 40 32 : uchar scalars[ 5 * 32 ]; 41 32 : fd_ristretto255_point_t points[5]; 42 32 : fd_ristretto255_point_t y[1]; 43 32 : fd_ristretto255_point_t res[1]; 44 : 45 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/zero_ciphertext.rs#L104-L109 46 : Reject identity pubkey and ciphertext.commitment. */ 47 32 : if( FD_UNLIKELY( fd_memeq( pubkey, fd_ristretto255_compressed_zero, 32 ) 48 32 : || fd_memeq( &ciphertext[0], fd_ristretto255_compressed_zero, 32 ) 49 32 : || fd_memeq( &ciphertext[32], fd_ristretto255_compressed_zero, 32 ) ) ) { 50 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR; 51 0 : } 52 : 53 : /* Validate scalar and decompress all points. 54 : Note: Agave does this in various places, but any failure simply results in an invalid proof. */ 55 32 : if( FD_UNLIKELY( fd_curve25519_scalar_validate( proof->z )==NULL ) ) { 56 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR; 57 0 : } 58 : 59 32 : fd_ristretto255_point_set( &points[0], fd_zksdk_basepoint_H ); 60 32 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[1], pubkey )==NULL ) ) { 61 2 : return FD_ZKSDK_VERIFY_PROOF_ERROR; 62 2 : } 63 30 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[2], &ciphertext[0] )==NULL ) ) { 64 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR; 65 0 : } 66 30 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[3], &ciphertext[32] )==NULL ) ) { 67 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR; 68 0 : } 69 30 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( &points[4], proof->yd )==NULL ) ) { 70 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR; 71 0 : } 72 30 : if( FD_UNLIKELY( fd_ristretto255_point_decompress( y, proof->yp )==NULL ) ) { 73 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR; 74 0 : } 75 : 76 : /* Finalize transcript and extract challenges */ 77 : 78 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/zero_ciphertext.rs#L111-L112 */ 79 30 : zero_ciphertext_hash_context( transcript, pubkey, ciphertext ); 80 30 : fd_zksdk_transcript_domsep_zero_ciphertext_proof( transcript ); 81 : 82 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/zero_ciphertext.rs#L119-L126 */ 83 30 : int val = FD_TRANSCRIPT_SUCCESS; 84 30 : val |= fd_zksdk_transcript_validate_and_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_P"), proof->yp); 85 30 : if( FD_UNLIKELY( val != FD_TRANSCRIPT_SUCCESS ) ) { 86 0 : return FD_ZKSDK_VERIFY_PROOF_ERROR; 87 0 : } 88 30 : fd_zksdk_transcript_append_point( transcript, FD_TRANSCRIPT_LITERAL("Y_D"), proof->yd); 89 : 90 30 : uchar c[ 32 ]; 91 30 : uchar w[ 32 ]; 92 30 : fd_zksdk_transcript_challenge_scalar( c, transcript, FD_TRANSCRIPT_LITERAL("c") ); 93 : 94 30 : fd_zksdk_transcript_append_scalar( transcript, FD_TRANSCRIPT_LITERAL("z"), proof->z ); 95 : 96 30 : fd_zksdk_transcript_challenge_scalar( w, transcript, FD_TRANSCRIPT_LITERAL("w") ); 97 : 98 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/zero_ciphertext.rs#L141 99 : Note: we use a slightly different MSM but they're equivalent. */ 100 : 101 : /* Compute scalars */ 102 30 : fd_curve25519_scalar_neg( &scalars[ 0*32 ], c ); // -c 103 30 : fd_curve25519_scalar_set( &scalars[ 1*32 ], proof->z ); // z 104 30 : fd_curve25519_scalar_mul( &scalars[ 2*32 ], &scalars[ 0*32 ], w ); // -wc 105 30 : fd_curve25519_scalar_mul( &scalars[ 3*32 ], w, proof->z ); // wz 106 30 : fd_curve25519_scalar_neg( &scalars[ 4*32 ], w ); // -w 107 : 108 : /* Compute the final MSM */ 109 30 : fd_ristretto255_multi_scalar_mul( res, scalars, points, 5 ); 110 : 111 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/sigma_proofs/zero_ciphertext.rs#L160-L164 */ 112 30 : if( FD_LIKELY( fd_ristretto255_point_eq( res, y ) ) ) { 113 24 : return FD_ZKSDK_VERIFY_PROOF_SUCCESS; 114 24 : } 115 6 : return FD_ZKSDK_VERIFY_PROOF_ERROR; 116 30 : } 117 : 118 : /* https://github.com/solana-program/zk-elgamal-proof/blob/zk-sdk%40v5.0.1/zk-sdk/src/zk_elgamal_proof_program/proof_data/zero_ciphertext.rs#L88 */ 119 : int 120 32 : fd_zksdk_instr_verify_proof_zero_ciphertext( void const * _context, void const * _proof ) { 121 32 : fd_zksdk_transcript_t transcript[1]; 122 32 : fd_zksdk_transcript_init( transcript, FD_TRANSCRIPT_LITERAL("zero-ciphertext-instruction") ); 123 : 124 32 : fd_zksdk_zero_ciphertext_context_t const * context = _context; 125 32 : fd_zksdk_zero_ciphertext_proof_t const * proof = _proof; 126 32 : return fd_zksdk_verify_proof_zero_ciphertext( 127 32 : proof, 128 32 : context->pubkey, 129 32 : context->ciphertext, 130 32 : transcript 131 32 : ); 132 32 : }