#[cfg(not(target_os = "solana"))]
use {
crate::encryption::{
elgamal::{ElGamalKeypair, ElGamalPubkey},
pedersen::H,
},
rand::rngs::OsRng,
zeroize::Zeroize,
};
use {
crate::{
errors::ProofVerificationError, sigma_proofs::errors::PubkeyValidityProofError,
transcript::TranscriptProtocol,
},
arrayref::{array_ref, array_refs},
curve25519_dalek::{
ristretto::{CompressedRistretto, RistrettoPoint},
scalar::Scalar,
traits::{IsIdentity, VartimeMultiscalarMul},
},
merlin::Transcript,
};
#[allow(non_snake_case)]
#[derive(Clone)]
pub struct PubkeySigmaProof {
Y: CompressedRistretto,
z: Scalar,
}
#[allow(non_snake_case)]
#[cfg(not(target_os = "solana"))]
impl PubkeySigmaProof {
pub fn new(elgamal_keypair: &ElGamalKeypair, transcript: &mut Transcript) -> Self {
transcript.pubkey_proof_domain_sep();
let s = elgamal_keypair.secret.get_scalar();
assert!(s != &Scalar::zero());
let s_inv = s.invert();
let mut y = Scalar::random(&mut OsRng);
let Y = (&y * &(*H)).compress();
transcript.append_point(b"Y", &Y);
let c = transcript.challenge_scalar(b"c");
let z = &(&c * s_inv) + &y;
y.zeroize();
Self { Y, z }
}
pub fn verify(
self,
elgamal_pubkey: &ElGamalPubkey,
transcript: &mut Transcript,
) -> Result<(), PubkeyValidityProofError> {
transcript.pubkey_proof_domain_sep();
let P = elgamal_pubkey.get_point();
transcript.validate_and_append_point(b"Y", &self.Y)?;
let c = transcript.challenge_scalar(b"c");
let Y = self
.Y
.decompress()
.ok_or(ProofVerificationError::Deserialization)?;
let check = RistrettoPoint::vartime_multiscalar_mul(
vec![&self.z, &(-&c), &(-&Scalar::one())],
vec![&(*H), P, &Y],
);
if check.is_identity() {
Ok(())
} else {
Err(ProofVerificationError::AlgebraicRelation.into())
}
}
pub fn to_bytes(&self) -> [u8; 64] {
let mut buf = [0_u8; 64];
buf[..32].copy_from_slice(self.Y.as_bytes());
buf[32..64].copy_from_slice(self.z.as_bytes());
buf
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, PubkeyValidityProofError> {
if bytes.len() != 64 {
return Err(ProofVerificationError::Deserialization.into());
}
let bytes = array_ref![bytes, 0, 64];
let (Y, z) = array_refs![bytes, 32, 32];
let Y = CompressedRistretto::from_slice(Y);
let z = Scalar::from_canonical_bytes(*z).ok_or(ProofVerificationError::Deserialization)?;
Ok(PubkeySigmaProof { Y, z })
}
}
#[cfg(test)]
mod test {
use {
super::*,
solana_sdk::{pubkey::Pubkey, signature::Keypair},
};
#[test]
fn test_pubkey_proof_correctness() {
let keypair = ElGamalKeypair::new_rand();
let mut prover_transcript = Transcript::new(b"test");
let mut verifier_transcript = Transcript::new(b"test");
let proof = PubkeySigmaProof::new(&keypair, &mut prover_transcript);
assert!(proof
.verify(&keypair.public, &mut verifier_transcript)
.is_ok());
let keypair = ElGamalKeypair::new(&Keypair::new(), &Pubkey::default()).unwrap();
let mut prover_transcript = Transcript::new(b"test");
let mut verifier_transcript = Transcript::new(b"test");
let proof = PubkeySigmaProof::new(&keypair, &mut prover_transcript);
assert!(proof
.verify(&keypair.public, &mut verifier_transcript)
.is_ok());
}
}