pakery_spake2plus/
prover.rs1use alloc::vec::Vec;
6use rand_core::CryptoRngCore;
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
42pub struct Prover<C: Spake2PlusCiphersuite>(core::marker::PhantomData<C>);
44
45impl<C: Spake2PlusCiphersuite> Prover<C> {
46 pub fn start(
53 w0: &<C::Group as CpaceGroup>::Scalar,
54 w1: &<C::Group as CpaceGroup>::Scalar,
55 context: &[u8],
56 id_prover: &[u8],
57 id_verifier: &[u8],
58 rng: &mut impl CryptoRngCore,
59 ) -> Result<(Vec<u8>, ProverState<C>), Spake2PlusError> {
60 let x = C::Group::random_scalar(rng);
61 Self::start_inner(w0.clone(), w1.clone(), x, context, id_prover, id_verifier)
62 }
63
64 #[cfg(feature = "test-utils")]
72 pub fn start_with_scalar(
73 w0: &<C::Group as CpaceGroup>::Scalar,
74 w1: &<C::Group as CpaceGroup>::Scalar,
75 x: &<C::Group as CpaceGroup>::Scalar,
76 context: &[u8],
77 id_prover: &[u8],
78 id_verifier: &[u8],
79 ) -> Result<(Vec<u8>, ProverState<C>), Spake2PlusError> {
80 Self::start_inner(
81 w0.clone(),
82 w1.clone(),
83 x.clone(),
84 context,
85 id_prover,
86 id_verifier,
87 )
88 }
89
90 fn start_inner(
91 w0: <C::Group as CpaceGroup>::Scalar,
92 w1: <C::Group as CpaceGroup>::Scalar,
93 x: <C::Group as CpaceGroup>::Scalar,
94 context: &[u8],
95 id_prover: &[u8],
96 id_verifier: &[u8],
97 ) -> Result<(Vec<u8>, ProverState<C>), Spake2PlusError> {
98 let m = C::Group::from_bytes(C::M_BYTES)?;
100
101 let x_g = C::Group::basepoint_mul(&x);
103 let w0_m = m.scalar_mul(&w0);
104 let share_p = x_g.add(&w0_m);
105
106 let share_p_bytes = share_p.to_bytes();
107
108 let state = ProverState {
109 x,
110 w0,
111 w1,
112 share_p_bytes: share_p_bytes.clone(),
113 context: context.to_vec(),
114 id_prover: id_prover.to_vec(),
115 id_verifier: id_verifier.to_vec(),
116 _marker: core::marker::PhantomData,
117 };
118
119 Ok((share_p_bytes, state))
120 }
121}
122
123impl<C: Spake2PlusCiphersuite> ProverState<C> {
124 pub fn finish(
130 self,
131 share_v_bytes: &[u8],
132 confirm_v: &[u8],
133 ) -> Result<ProverOutput, Spake2PlusError> {
134 let share_v = C::Group::from_bytes(share_v_bytes)?;
136 if share_v.is_identity() {
137 return Err(Spake2PlusError::IdentityPoint);
138 }
139
140 let n = C::Group::from_bytes(C::N_BYTES)?;
142
143 let w0_n = n.scalar_mul(&self.w0);
145 let tmp = share_v.add(&w0_n.negate());
146
147 let z = tmp.scalar_mul(&self.x);
149
150 let v = tmp.scalar_mul(&self.w1);
152
153 if z.is_identity() {
155 return Err(Spake2PlusError::IdentityPoint);
156 }
157 if v.is_identity() {
158 return Err(Spake2PlusError::IdentityPoint);
159 }
160
161 let z_bytes = Zeroizing::new(z.to_bytes());
162 let v_bytes = Zeroizing::new(v.to_bytes());
163 let w0_bytes = Zeroizing::new(C::Group::scalar_to_bytes(&self.w0));
164
165 let m = C::Group::from_bytes(C::M_BYTES)?;
170 let n_point = C::Group::from_bytes(C::N_BYTES)?;
171 let m_bytes = m.to_bytes();
172 let n_bytes = n_point.to_bytes();
173
174 let tt = build_transcript(
176 &self.context,
177 &self.id_prover,
178 &self.id_verifier,
179 &m_bytes,
180 &n_bytes,
181 &self.share_p_bytes,
182 share_v_bytes,
183 &z_bytes,
184 &v_bytes,
185 &w0_bytes,
186 );
187
188 let mut ks = derive_key_schedule::<C>(&tt, &self.share_p_bytes, share_v_bytes)?;
190
191 if !bool::from(ks.confirm_v.ct_eq(confirm_v)) {
193 return Err(Spake2PlusError::ConfirmationFailed);
194 }
195
196 Ok(ProverOutput {
197 session_key: core::mem::replace(&mut ks.session_key, SharedSecret::new(Vec::new())),
198 confirm_p: core::mem::take(&mut ks.confirm_p),
199 })
200 }
201}