pakery_spake2plus/
prover.rs1use alloc::vec::Vec;
6use rand_core::CryptoRng;
7use subtle::ConstantTimeEq;
8use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
9
10use pakery_core::crypto::CpaceGroup;
11use pakery_core::SharedSecret;
12
13use crate::ciphersuite::Spake2PlusCiphersuite;
14use crate::encoding::build_transcript;
15use crate::error::Spake2PlusError;
16use crate::transcript::derive_key_schedule;
17
18#[derive(Zeroize, ZeroizeOnDrop)]
20pub struct ProverState<C: Spake2PlusCiphersuite> {
21 x: <C::Group as CpaceGroup>::Scalar,
22 w0: <C::Group as CpaceGroup>::Scalar,
23 w1: <C::Group as CpaceGroup>::Scalar,
24 share_p_bytes: Vec<u8>,
25 context: Vec<u8>,
26 id_prover: Vec<u8>,
27 id_verifier: Vec<u8>,
28 #[zeroize(skip)]
29 _marker: core::marker::PhantomData<C>,
30}
31
32#[derive(Zeroize, ZeroizeOnDrop)]
34pub struct ProverOutput {
35 #[zeroize(skip)]
37 pub session_key: SharedSecret,
38 pub confirm_p: Vec<u8>,
40}
41
42impl ProverOutput {
43 #[must_use]
58 pub fn into_session_key(mut self) -> SharedSecret {
59 core::mem::replace(&mut self.session_key, SharedSecret::new(Vec::new()))
60 }
61
62 #[must_use]
72 pub fn into_confirm_p(mut self) -> Vec<u8> {
73 core::mem::take(&mut self.confirm_p)
74 }
75}
76
77pub struct Prover<C: Spake2PlusCiphersuite>(core::marker::PhantomData<C>);
79
80impl<C: Spake2PlusCiphersuite> Prover<C> {
81 pub fn start(
88 w0: &<C::Group as CpaceGroup>::Scalar,
89 w1: &<C::Group as CpaceGroup>::Scalar,
90 context: &[u8],
91 id_prover: &[u8],
92 id_verifier: &[u8],
93 rng: &mut impl CryptoRng,
94 ) -> Result<(Vec<u8>, ProverState<C>), Spake2PlusError> {
95 let x = C::Group::random_scalar(rng);
96 Self::start_inner(w0.clone(), w1.clone(), x, context, id_prover, id_verifier)
97 }
98
99 #[cfg(feature = "test-utils")]
107 pub fn start_with_scalar(
108 w0: &<C::Group as CpaceGroup>::Scalar,
109 w1: &<C::Group as CpaceGroup>::Scalar,
110 x: &<C::Group as CpaceGroup>::Scalar,
111 context: &[u8],
112 id_prover: &[u8],
113 id_verifier: &[u8],
114 ) -> Result<(Vec<u8>, ProverState<C>), Spake2PlusError> {
115 Self::start_inner(
116 w0.clone(),
117 w1.clone(),
118 x.clone(),
119 context,
120 id_prover,
121 id_verifier,
122 )
123 }
124
125 fn start_inner(
126 w0: <C::Group as CpaceGroup>::Scalar,
127 w1: <C::Group as CpaceGroup>::Scalar,
128 x: <C::Group as CpaceGroup>::Scalar,
129 context: &[u8],
130 id_prover: &[u8],
131 id_verifier: &[u8],
132 ) -> Result<(Vec<u8>, ProverState<C>), Spake2PlusError> {
133 let m = C::Group::from_bytes(C::M_BYTES)?;
135
136 let x_g = C::Group::basepoint_mul(&x);
138 let w0_m = m.scalar_mul(&w0);
139 let share_p = x_g.add(&w0_m);
140
141 let share_p_bytes = share_p.to_bytes();
142
143 let state = ProverState {
144 x,
145 w0,
146 w1,
147 share_p_bytes: share_p_bytes.clone(),
148 context: context.to_vec(),
149 id_prover: id_prover.to_vec(),
150 id_verifier: id_verifier.to_vec(),
151 _marker: core::marker::PhantomData,
152 };
153
154 Ok((share_p_bytes, state))
155 }
156}
157
158impl<C: Spake2PlusCiphersuite> ProverState<C> {
159 pub fn finish(
165 self,
166 share_v_bytes: &[u8],
167 confirm_v: &[u8],
168 ) -> Result<ProverOutput, Spake2PlusError> {
169 let share_v = C::Group::from_bytes(share_v_bytes)?;
171 if share_v.is_identity() {
172 return Err(Spake2PlusError::IdentityPoint);
173 }
174
175 let n = C::Group::from_bytes(C::N_BYTES)?;
177
178 let w0_n = n.scalar_mul(&self.w0);
180 let tmp = share_v.add(&w0_n.negate());
181
182 let z = tmp.scalar_mul(&self.x);
184
185 let v = tmp.scalar_mul(&self.w1);
187
188 if z.is_identity() {
190 return Err(Spake2PlusError::IdentityPoint);
191 }
192 if v.is_identity() {
193 return Err(Spake2PlusError::IdentityPoint);
194 }
195
196 let z_bytes = Zeroizing::new(z.to_bytes());
197 let v_bytes = Zeroizing::new(v.to_bytes());
198 let w0_bytes = Zeroizing::new(C::Group::scalar_to_bytes(&self.w0));
199
200 let m = C::Group::from_bytes(C::M_BYTES)?;
205 let n_point = C::Group::from_bytes(C::N_BYTES)?;
206 let m_bytes = m.to_bytes();
207 let n_bytes = n_point.to_bytes();
208
209 let tt = build_transcript(
211 &self.context,
212 &self.id_prover,
213 &self.id_verifier,
214 &m_bytes,
215 &n_bytes,
216 &self.share_p_bytes,
217 share_v_bytes,
218 &z_bytes,
219 &v_bytes,
220 &w0_bytes,
221 );
222
223 let mut ks = derive_key_schedule::<C>(&tt, &self.share_p_bytes, share_v_bytes)?;
225
226 if !bool::from(ks.confirm_v.ct_eq(confirm_v)) {
228 return Err(Spake2PlusError::ConfirmationFailed);
229 }
230
231 Ok(ProverOutput {
232 session_key: core::mem::replace(&mut ks.session_key, SharedSecret::new(Vec::new())),
233 confirm_p: core::mem::take(&mut ks.confirm_p),
234 })
235 }
236}