elastic_elgamal/proofs/
possession.rs1use merlin::Transcript;
4use rand_core::{CryptoRng, RngCore};
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8#[cfg(feature = "serde")]
9use crate::serde::{ScalarHelper, VecHelper};
10use crate::{
11 alloc::Vec,
12 group::Group,
13 proofs::{TranscriptForGroup, VerificationError},
14 Keypair, PublicKey, SecretKey,
15};
16
17#[derive(Debug, Clone)]
70#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
71#[cfg_attr(feature = "serde", serde(bound = ""))]
72pub struct ProofOfPossession<G: Group> {
73 #[cfg_attr(feature = "serde", serde(with = "ScalarHelper::<G>"))]
74 challenge: G::Scalar,
75 #[cfg_attr(feature = "serde", serde(with = "VecHelper::<ScalarHelper<G>, 1>"))]
76 responses: Vec<G::Scalar>,
77}
78
79impl<G: Group> ProofOfPossession<G> {
80 pub fn new<R: CryptoRng + RngCore>(
82 keypairs: &[Keypair<G>],
83 transcript: &mut Transcript,
84 rng: &mut R,
85 ) -> Self {
86 Self::from_keys(
87 keypairs.iter().map(Keypair::secret),
88 keypairs.iter().map(Keypair::public),
89 transcript,
90 rng,
91 )
92 }
93
94 pub(crate) fn from_keys<'a, R: CryptoRng + RngCore>(
95 secrets: impl Iterator<Item = &'a SecretKey<G>>,
96 public_keys: impl Iterator<Item = &'a PublicKey<G>>,
97 transcript: &mut Transcript,
98 rng: &mut R,
99 ) -> Self {
100 transcript.start_proof(b"multi_pop");
101 let mut key_count = 0;
102 for public_key in public_keys {
103 transcript.append_element_bytes(b"K", public_key.as_bytes());
104 key_count += 1;
105 }
106
107 let random_scalars: Vec<_> = (0..key_count)
108 .map(|_| {
109 let randomness = SecretKey::<G>::generate(rng);
110 let random_element = G::mul_generator(randomness.expose_scalar());
111 transcript.append_element::<G>(b"R", &random_element);
112 randomness
113 })
114 .collect();
115
116 let challenge = transcript.challenge_scalar::<G>(b"c");
117 let responses = secrets
118 .zip(random_scalars)
119 .map(|(log, mut randomness)| {
120 randomness += log * &challenge;
121 *randomness.expose_scalar()
122 })
123 .collect();
124
125 Self {
126 challenge,
127 responses,
128 }
129 }
130
131 pub fn verify<'a>(
137 &self,
138 public_keys: impl Iterator<Item = &'a PublicKey<G>> + Clone,
139 transcript: &mut Transcript,
140 ) -> Result<(), VerificationError> {
141 let mut key_count = 0;
142 transcript.start_proof(b"multi_pop");
143 for public_key in public_keys.clone() {
144 transcript.append_element_bytes(b"K", public_key.as_bytes());
145 key_count += 1;
146 }
147 VerificationError::check_lengths("public keys", self.responses.len(), key_count)?;
148
149 for (public_key, response) in public_keys.zip(&self.responses) {
150 let random_element = G::vartime_double_mul_generator(
151 &-self.challenge,
152 public_key.as_element(),
153 response,
154 );
155 transcript.append_element::<G>(b"R", &random_element);
156 }
157
158 let expected_challenge = transcript.challenge_scalar::<G>(b"c");
159 if expected_challenge == self.challenge {
160 Ok(())
161 } else {
162 Err(VerificationError::ChallengeMismatch)
163 }
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use rand::thread_rng;
170
171 use super::*;
172 use crate::group::Ristretto;
173
174 type Keypair = crate::Keypair<Ristretto>;
175
176 #[test]
177 fn proof_of_possession_basics() {
178 let mut rng = thread_rng();
179 let poly: Vec<_> = (0..5).map(|_| Keypair::generate(&mut rng)).collect();
180
181 ProofOfPossession::new(&poly, &mut Transcript::new(b"test_multi_PoP"), &mut rng)
182 .verify(
183 poly.iter().map(Keypair::public),
184 &mut Transcript::new(b"test_multi_PoP"),
185 )
186 .unwrap();
187 }
188}