pakery_spake2plus/
verifier.rs1use alloc::vec::Vec;
7use rand_core::CryptoRngCore;
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 Drop for VerifierState {
26 fn drop(&mut self) {
27 self.expected_confirm_p.zeroize();
28 }
30}
31
32impl VerifierState {
33 pub fn finish(mut self, confirm_p: &[u8]) -> Result<Spake2PlusOutput, Spake2PlusError> {
35 if !bool::from(self.expected_confirm_p.ct_eq(confirm_p)) {
36 return Err(Spake2PlusError::ConfirmationFailed);
37 }
38
39 let session_key =
42 core::mem::replace(&mut self.session_key, SharedSecret::new(alloc::vec![]));
43 Ok(Spake2PlusOutput { session_key })
44 }
45}
46
47pub struct Verifier<C: Spake2PlusCiphersuite>(core::marker::PhantomData<C>);
49
50impl<C: Spake2PlusCiphersuite> Verifier<C> {
51 pub fn start(
59 share_p_bytes: &[u8],
60 w0: &<C::Group as CpaceGroup>::Scalar,
61 l_bytes: &[u8],
62 context: &[u8],
63 id_prover: &[u8],
64 id_verifier: &[u8],
65 rng: &mut impl CryptoRngCore,
66 ) -> Result<(Vec<u8>, Vec<u8>, VerifierState), Spake2PlusError> {
67 let y = C::Group::random_scalar(rng);
68 Self::start_inner(
69 share_p_bytes,
70 w0,
71 l_bytes,
72 &y,
73 context,
74 id_prover,
75 id_verifier,
76 )
77 }
78
79 #[cfg(feature = "test-utils")]
87 pub fn start_with_scalar(
88 share_p_bytes: &[u8],
89 w0: &<C::Group as CpaceGroup>::Scalar,
90 l_bytes: &[u8],
91 y: &<C::Group as CpaceGroup>::Scalar,
92 context: &[u8],
93 id_prover: &[u8],
94 id_verifier: &[u8],
95 ) -> Result<(Vec<u8>, Vec<u8>, VerifierState), Spake2PlusError> {
96 Self::start_inner(
97 share_p_bytes,
98 w0,
99 l_bytes,
100 y,
101 context,
102 id_prover,
103 id_verifier,
104 )
105 }
106
107 fn start_inner(
108 share_p_bytes: &[u8],
109 w0: &<C::Group as CpaceGroup>::Scalar,
110 l_bytes: &[u8],
111 y: &<C::Group as CpaceGroup>::Scalar,
112 context: &[u8],
113 id_prover: &[u8],
114 id_verifier: &[u8],
115 ) -> Result<(Vec<u8>, Vec<u8>, VerifierState), Spake2PlusError> {
116 let share_p = C::Group::from_bytes(share_p_bytes)?;
118 if share_p.is_identity() {
119 return Err(Spake2PlusError::IdentityPoint);
120 }
121
122 let m = C::Group::from_bytes(C::M_BYTES)?;
124
125 let l = C::Group::from_bytes(l_bytes)?;
127
128 let n = C::Group::from_bytes(C::N_BYTES)?;
130
131 let y_g = C::Group::basepoint_mul(y);
133 let w0_n = n.scalar_mul(w0);
134 let share_v = y_g.add(&w0_n);
135
136 let share_v_bytes = share_v.to_bytes();
137
138 let w0_m = m.scalar_mul(w0);
140 let tmp = share_p.add(&w0_m.negate());
141
142 let z = tmp.scalar_mul(y);
144
145 let v = l.scalar_mul(y);
147
148 if z.is_identity() {
150 return Err(Spake2PlusError::IdentityPoint);
151 }
152 if v.is_identity() {
153 return Err(Spake2PlusError::IdentityPoint);
154 }
155
156 let z_bytes = Zeroizing::new(z.to_bytes());
157 let v_bytes = Zeroizing::new(v.to_bytes());
158 let w0_bytes = Zeroizing::new(C::Group::scalar_to_bytes(w0));
159
160 let m_bytes = m.to_bytes();
163 let n_bytes = n.to_bytes();
164
165 let tt = build_transcript(
167 context,
168 id_prover,
169 id_verifier,
170 &m_bytes,
171 &n_bytes,
172 share_p_bytes,
173 &share_v_bytes,
174 &z_bytes,
175 &v_bytes,
176 &w0_bytes,
177 );
178
179 let mut ks = derive_key_schedule::<C>(&tt, share_p_bytes, &share_v_bytes)?;
181
182 let state = VerifierState {
183 expected_confirm_p: core::mem::take(&mut ks.confirm_p),
184 session_key: core::mem::replace(&mut ks.session_key, SharedSecret::new(Vec::new())),
185 };
186
187 Ok((share_v_bytes, core::mem::take(&mut ks.confirm_v), state))
188 }
189}