waffles_solana_zk_token_sdk/sigma_proofs/
pubkey_proof.rs1#[cfg(not(target_os = "solana"))]
11use {
12 crate::encryption::{
13 elgamal::{ElGamalKeypair, ElGamalPubkey},
14 pedersen::H,
15 },
16 rand::rngs::OsRng,
17 zeroize::Zeroize,
18};
19use {
20 crate::{
21 errors::ProofVerificationError, sigma_proofs::errors::PubkeyValidityProofError,
22 transcript::TranscriptProtocol,
23 },
24 arrayref::{array_ref, array_refs},
25 curve25519_dalek::{
26 ristretto::{CompressedRistretto, RistrettoPoint},
27 scalar::Scalar,
28 traits::{IsIdentity, VartimeMultiscalarMul},
29 },
30 merlin::Transcript,
31};
32
33#[allow(non_snake_case)]
37#[derive(Clone)]
38pub struct PubkeySigmaProof {
39 Y: CompressedRistretto,
40 z: Scalar,
41}
42
43#[allow(non_snake_case)]
44#[cfg(not(target_os = "solana"))]
45impl PubkeySigmaProof {
46 pub fn new(elgamal_keypair: &ElGamalKeypair, transcript: &mut Transcript) -> Self {
61 transcript.pubkey_proof_domain_sep();
62
63 let s = elgamal_keypair.secret.get_scalar();
65
66 assert!(s != &Scalar::zero());
67 let s_inv = s.invert();
68
69 let mut y = Scalar::random(&mut OsRng);
71 let Y = (&y * &(*H)).compress();
72
73 transcript.append_point(b"Y", &Y);
75 let c = transcript.challenge_scalar(b"c");
76
77 let z = &(&c * s_inv) + &y;
79
80 y.zeroize();
81
82 Self { Y, z }
83 }
84
85 pub fn verify(
90 self,
91 elgamal_pubkey: &ElGamalPubkey,
92 transcript: &mut Transcript,
93 ) -> Result<(), PubkeyValidityProofError> {
94 transcript.pubkey_proof_domain_sep();
95
96 let P = elgamal_pubkey.get_point();
98
99 transcript.validate_and_append_point(b"Y", &self.Y)?;
101 let c = transcript.challenge_scalar(b"c");
102
103 let Y = self
105 .Y
106 .decompress()
107 .ok_or(ProofVerificationError::Deserialization)?;
108
109 let check = RistrettoPoint::vartime_multiscalar_mul(
110 vec![&self.z, &(-&c), &(-&Scalar::one())],
111 vec![&(*H), P, &Y],
112 );
113
114 if check.is_identity() {
115 Ok(())
116 } else {
117 Err(ProofVerificationError::AlgebraicRelation.into())
118 }
119 }
120
121 pub fn to_bytes(&self) -> [u8; 64] {
122 let mut buf = [0_u8; 64];
123 buf[..32].copy_from_slice(self.Y.as_bytes());
124 buf[32..64].copy_from_slice(self.z.as_bytes());
125 buf
126 }
127
128 pub fn from_bytes(bytes: &[u8]) -> Result<Self, PubkeyValidityProofError> {
129 if bytes.len() != 64 {
130 return Err(ProofVerificationError::Deserialization.into());
131 }
132
133 let bytes = array_ref![bytes, 0, 64];
134 let (Y, z) = array_refs![bytes, 32, 32];
135
136 let Y = CompressedRistretto::from_slice(Y);
137 let z = Scalar::from_canonical_bytes(*z).ok_or(ProofVerificationError::Deserialization)?;
138
139 Ok(PubkeySigmaProof { Y, z })
140 }
141}
142
143#[cfg(test)]
144mod test {
145 use {
146 super::*,
147 solana_sdk::{pubkey::Pubkey, signature::Keypair},
148 };
149
150 #[test]
151 fn test_pubkey_proof_correctness() {
152 let keypair = ElGamalKeypair::new_rand();
154
155 let mut prover_transcript = Transcript::new(b"test");
156 let mut verifier_transcript = Transcript::new(b"test");
157
158 let proof = PubkeySigmaProof::new(&keypair, &mut prover_transcript);
159 assert!(proof
160 .verify(&keypair.public, &mut verifier_transcript)
161 .is_ok());
162
163 let keypair = ElGamalKeypair::new(&Keypair::new(), &Pubkey::default()).unwrap();
165
166 let mut prover_transcript = Transcript::new(b"test");
167 let mut verifier_transcript = Transcript::new(b"test");
168
169 let proof = PubkeySigmaProof::new(&keypair, &mut prover_transcript);
170 assert!(proof
171 .verify(&keypair.public, &mut verifier_transcript)
172 .is_ok());
173 }
174}