pakery_spake2plus/
verifier.rs1use alloc::vec::Vec;
7use rand_core::CryptoRng;
8use subtle::ConstantTimeEq;
9use zeroize::{Zeroize, Zeroizing};
10
11use pakery_core::crypto::CpaceGroup;
12use pakery_core::SharedSecret;
13
14use crate::ciphersuite::Spake2PlusCiphersuite;
15use crate::encoding::build_transcript;
16use crate::error::Spake2PlusError;
17use crate::transcript::{derive_key_schedule, Spake2PlusOutput};
18
19pub struct VerifierState {
21 expected_confirm_p: Vec<u8>,
22 session_key: SharedSecret,
23}
24
25impl Zeroize for VerifierState {
26 fn zeroize(&mut self) {
27 self.expected_confirm_p.zeroize();
28 self.session_key.zeroize();
31 }
32}
33
34impl Drop for VerifierState {
35 fn drop(&mut self) {
36 self.zeroize();
37 }
38}
39
40impl VerifierState {
41 pub fn finish(mut self, confirm_p: &[u8]) -> Result<Spake2PlusOutput, Spake2PlusError> {
43 if !pakery_core::ct::declassify_choice(self.expected_confirm_p.ct_eq(confirm_p)) {
46 return Err(Spake2PlusError::ConfirmationFailed);
47 }
48
49 let session_key =
52 core::mem::replace(&mut self.session_key, SharedSecret::new(alloc::vec![]));
53 Ok(Spake2PlusOutput { session_key })
54 }
55}
56
57pub struct Verifier<C: Spake2PlusCiphersuite>(core::marker::PhantomData<C>);
59
60impl<C: Spake2PlusCiphersuite> Verifier<C> {
61 pub fn start(
69 share_p_bytes: &[u8],
70 w0: &<C::Group as CpaceGroup>::Scalar,
71 l_bytes: &[u8],
72 context: &[u8],
73 id_prover: &[u8],
74 id_verifier: &[u8],
75 rng: &mut impl CryptoRng,
76 ) -> Result<(Vec<u8>, Vec<u8>, VerifierState), Spake2PlusError> {
77 let y = C::Group::random_scalar(rng);
78 Self::start_inner(
79 share_p_bytes,
80 w0,
81 l_bytes,
82 &y,
83 context,
84 id_prover,
85 id_verifier,
86 )
87 }
88
89 #[cfg(feature = "test-utils")]
97 pub fn start_with_scalar(
98 share_p_bytes: &[u8],
99 w0: &<C::Group as CpaceGroup>::Scalar,
100 l_bytes: &[u8],
101 y: &<C::Group as CpaceGroup>::Scalar,
102 context: &[u8],
103 id_prover: &[u8],
104 id_verifier: &[u8],
105 ) -> Result<(Vec<u8>, Vec<u8>, VerifierState), Spake2PlusError> {
106 Self::start_inner(
107 share_p_bytes,
108 w0,
109 l_bytes,
110 y,
111 context,
112 id_prover,
113 id_verifier,
114 )
115 }
116
117 fn start_inner(
118 share_p_bytes: &[u8],
119 w0: &<C::Group as CpaceGroup>::Scalar,
120 l_bytes: &[u8],
121 y: &<C::Group as CpaceGroup>::Scalar,
122 context: &[u8],
123 id_prover: &[u8],
124 id_verifier: &[u8],
125 ) -> Result<(Vec<u8>, Vec<u8>, VerifierState), Spake2PlusError> {
126 let share_p = C::Group::from_bytes(share_p_bytes)?;
128 if share_p.is_identity() {
129 return Err(Spake2PlusError::IdentityPoint);
130 }
131
132 let m = C::Group::from_bytes(C::M_BYTES)?;
134
135 let l = C::Group::from_bytes(l_bytes)?;
137
138 let n = C::Group::from_bytes(C::N_BYTES)?;
140
141 let y_g = C::Group::basepoint_mul(y);
143 let w0_n = n.scalar_mul(w0);
144 let share_v = y_g.add(&w0_n);
145
146 let share_v_bytes = share_v.to_bytes();
147 pakery_core::ct::declassify(&share_v_bytes);
149
150 let w0_m = m.scalar_mul(w0);
152 let tmp = share_p.add(&w0_m.negate());
153
154 let z = tmp.scalar_mul(y);
156
157 let v = l.scalar_mul(y);
159
160 if z.is_identity() {
162 return Err(Spake2PlusError::IdentityPoint);
163 }
164 if v.is_identity() {
165 return Err(Spake2PlusError::IdentityPoint);
166 }
167
168 let z_bytes = Zeroizing::new(z.to_bytes());
169 let v_bytes = Zeroizing::new(v.to_bytes());
170 let w0_bytes = Zeroizing::new(C::Group::scalar_to_bytes(w0));
171 pakery_core::ct::mark_secret(&z_bytes);
175 pakery_core::ct::mark_secret(&v_bytes);
176 pakery_core::ct::mark_secret(&w0_bytes);
177
178 let m_bytes = m.to_bytes();
181 let n_bytes = n.to_bytes();
182
183 let tt = build_transcript(
185 context,
186 id_prover,
187 id_verifier,
188 &m_bytes,
189 &n_bytes,
190 share_p_bytes,
191 &share_v_bytes,
192 &z_bytes,
193 &v_bytes,
194 &w0_bytes,
195 );
196
197 let mut ks = derive_key_schedule::<C>(&tt, share_p_bytes, &share_v_bytes)?;
199
200 let state = VerifierState {
201 expected_confirm_p: core::mem::take(&mut ks.confirm_p),
202 session_key: core::mem::replace(&mut ks.session_key, SharedSecret::new(Vec::new())),
203 };
204
205 let confirm_v = core::mem::take(&mut ks.confirm_v);
206 pakery_core::ct::declassify(&confirm_v);
209 Ok((share_v_bytes, confirm_v, state))
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216
217 #[test]
221 fn zeroize_clears_all_secret_fields() {
222 let mut state = VerifierState {
223 expected_confirm_p: alloc::vec![0xAA; 64],
224 session_key: SharedSecret::new(alloc::vec![0xBB; 32]),
225 };
226 state.zeroize();
227 assert!(state.expected_confirm_p.is_empty());
228 assert!(state.session_key.as_bytes().is_empty());
229 }
230}