1pub mod client;
2pub mod encoded;
3pub mod server;
4
5use base64::DecodeError;
6use opaque_ke::ciphersuite::CipherSuite;
7pub use opaque_ke::errors::ProtocolError;
8use std::fmt::{Debug, Display, Formatter};
9
10pub struct Cipher;
11impl CipherSuite for Cipher {
12 type OprfCs = opaque_ke::Ristretto255;
13 type KeGroup = opaque_ke::Ristretto255;
14 type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
15 type Ksf = argon2::Argon2<'static>;
16}
17
18#[derive(Debug)]
19pub enum Error {
20 ProtocolError(ProtocolError),
21 DecodeError(DecodeError),
22}
23
24impl Display for Error {
25 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
26 write!(f, "{:?}", &self)
27 }
28}
29
30impl std::error::Error for Error {}
31
32impl From<ProtocolError> for Error {
33 fn from(e: ProtocolError) -> Self {
34 Error::ProtocolError(e)
35 }
36}
37
38impl From<DecodeError> for Error {
39 fn from(e: DecodeError) -> Self {
40 Error::DecodeError(e)
41 }
42}
43
44pub(crate) mod opaque_impl {
45 use opaque_ke::{
46 ClientLogin, ClientLoginFinishParameters, ClientRegistration,
47 ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest,
48 CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload,
49 ServerLogin, ServerLoginStartParameters, ServerRegistration,
50 };
51 use rand::{
52 rngs::{OsRng, ThreadRng},
53 thread_rng,
54 };
55
56 use super::{Cipher, Error, IntoArray};
57
58 #[repr(transparent)]
59 pub struct PasswordFile(ServerRegistration<Cipher>);
60
61 impl PasswordFile {
62 pub fn serialize(&self) -> [u8; PASSWORD_FILE_SERIALIZED_LEN] {
63 self.0.serialize().into_array()
64 }
65
66 pub fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
67 let registration = opaque_ke::ServerRegistration::<Cipher>::deserialize(bytes)?;
68
69 Ok(Self(registration))
70 }
71 }
72
73 pub struct ServerSetup(opaque_ke::ServerSetup<Cipher>);
74
75 impl ServerSetup {
76 pub fn create() -> Self {
77 let mut rng = OsRng;
78 let server_setup = opaque_ke::ServerSetup::<Cipher>::new(&mut rng);
79
80 Self(server_setup)
81 }
82
83 pub fn view(&self) -> ServerSetupView {
84 ServerSetupView {
85 setup: &self.0,
86 rng: thread_rng(),
87 }
88 }
89
90 pub fn serialize(&self) -> [u8; SERVER_SETUP_LEN] {
91 self.0.serialize().into_array()
92 }
93
94 pub fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
95 let setup = opaque_ke::ServerSetup::<Cipher>::deserialize(bytes)?;
96
97 Ok(Self(setup))
98 }
99 }
100
101 #[derive(Clone)]
102 pub struct ServerSetupView<'a> {
103 setup: &'a opaque_ke::ServerSetup<Cipher>,
104 rng: rand::rngs::ThreadRng,
105 }
106
107 pub fn server_login_start(
108 setup: &mut ServerSetupView,
109 password_file: &PasswordFile,
110 login_start_request: &[u8],
111 user_id: &str,
112 ) -> Result<ServerLoginStartResult, Error> {
113 let login_start_request = CredentialRequest::<Cipher>::deserialize(login_start_request)?;
114
115 let result = ServerLogin::<Cipher>::start(
116 &mut setup.rng,
117 setup.setup,
118 Some(password_file.0.clone()),
119 login_start_request,
120 user_id.as_bytes(),
121 ServerLoginStartParameters::default(),
122 )?;
123
124 Ok(ServerLoginStartResult {
125 response: result.message.serialize().into_array(),
126 state: result.state.serialize().into_array(),
127 })
128 }
129
130 pub struct ClientStateLogin {
131 rng: rand::rngs::ThreadRng,
132 state: Option<ClientLogin<Cipher>>,
133 }
134
135 pub fn client_login_start(
136 client_state: &mut ClientStateLogin,
137 password: &[u8],
138 ) -> Result<ClientLoginStartResult, Error> {
139 let result = ClientLogin::<Cipher>::start(&mut client_state.rng, password)?;
140
141 client_state.state = Some(result.state);
142
143 Ok(ClientLoginStartResult {
144 response: result.message.serialize().into_array(),
145 })
146 }
147
148 pub fn client_login_finish(
149 client_state: &mut ClientStateLogin,
150 password: &[u8],
151 server_message: &[u8],
152 ) -> Result<ClientLoginFinishResult, Error> {
153 if client_state.state.is_none() {
154 panic!("Client state not initialized! Run `client_login_start` first!")
155 }
156 let client_state = client_state.state.take().unwrap();
157 let server_message = CredentialResponse::<Cipher>::deserialize(server_message)?;
158 let result = client_state.finish(
159 password,
160 server_message,
161 ClientLoginFinishParameters::default(),
162 )?;
163
164 Ok(ClientLoginFinishResult {
165 response: result.message.serialize().into_array(),
166 shared_secret: result.session_key.into_array(),
167 })
168 }
169
170 pub fn server_login_finish(
171 login_finish_request: &[u8],
172 server_state: &[u8],
173 ) -> Result<ServerLoginFinishResult, Error> {
174 let state = ServerLogin::<Cipher>::deserialize(server_state)?;
175 let login_finish_request =
176 CredentialFinalization::<Cipher>::deserialize(login_finish_request)?;
177 let result = state.finish(login_finish_request)?;
178 Ok(ServerLoginFinishResult {
179 shared_secret: result.session_key.into_array(),
180 })
181 }
182
183 pub struct ClientStateRegistration {
184 rng: ThreadRng,
185 state: Option<ClientRegistration<Cipher>>,
186 }
187
188 impl ClientStateRegistration {
189 pub fn setup() -> Self {
190 Self {
191 rng: thread_rng(),
192 state: None,
193 }
194 }
195
196 pub fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
197 let login = ClientRegistration::<Cipher>::deserialize(bytes)?;
198
199 Ok(Self {
200 rng: thread_rng(),
201 state: Some(login),
202 })
203 }
204
205 pub fn serialize(&self) -> [u8; REGISTER_CLIENT_STATE_LEN] {
206 self.state
207 .as_ref()
208 .expect("Can only serialize after first step is completed!")
209 .serialize()
210 .into_array()
211 }
212 }
213
214 impl ClientStateLogin {
215 pub fn setup() -> Self {
216 Self {
217 rng: thread_rng(),
218 state: None,
219 }
220 }
221
222 pub fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
223 let login = ClientLogin::<Cipher>::deserialize(bytes)?;
224
225 Ok(Self {
226 rng: thread_rng(),
227 state: Some(login),
228 })
229 }
230
231 pub fn serialize(&self) -> [u8; LOGIN_CLIENT_STATE_LEN] {
232 self.state
233 .as_ref()
234 .expect("Can only serialize after first step is completed!")
235 .serialize()
236 .into_array()
237 }
238 }
239
240 pub fn client_register_start(
241 client_state: &mut ClientStateRegistration,
242 password: &[u8],
243 ) -> Result<ClientRegistrationStartResult, Error> {
244 let result = ClientRegistration::start(&mut client_state.rng, password)?;
245
246 client_state.state = Some(result.state);
247
248 Ok(ClientRegistrationStartResult {
249 response: result.message.serialize().into_array(),
250 })
251 }
252
253 pub fn server_register_start(
254 server_setup: &mut ServerSetupView,
255 register_start_request: &[u8],
256 user_id: &[u8],
257 ) -> Result<ServerRegistrationStartResult, Error> {
258 let register_start_request =
259 RegistrationRequest::<Cipher>::deserialize(register_start_request)?;
260
261 let result = ServerRegistration::<Cipher>::start(
262 server_setup.setup,
263 register_start_request,
264 user_id,
265 )?;
266
267 Ok(ServerRegistrationStartResult {
268 response: result.message.serialize().into_array(),
269 })
270 }
271
272 pub fn client_register_finish(
273 client_state: &mut ClientStateRegistration,
274 password: &[u8],
275 server_message: &[u8],
276 ) -> Result<ClientRegistrationFinishResult, Error> {
277 if client_state.state.is_none() {
278 panic!("Client state not initialized! Run `server_register_start` first!")
279 }
280 let state = client_state.state.take().unwrap();
281 let server_message = RegistrationResponse::deserialize(server_message)?;
282
283 let result = state.finish(
284 &mut client_state.rng,
285 password,
286 server_message,
287 ClientRegistrationFinishParameters::default(),
288 )?;
289
290 Ok(ClientRegistrationFinishResult {
291 response: result.message.serialize().into_array(),
292 })
293 }
294
295 pub fn server_register_finish(register_finish_request: &[u8]) -> Result<PasswordFile, Error> {
296 let register_finish_request =
297 RegistrationUpload::<Cipher>::deserialize(register_finish_request)?;
298
299 let result = ServerRegistration::finish(register_finish_request);
300
301 Ok(PasswordFile(result))
302 }
303
304 pub const SERVER_SETUP_LEN: usize = 128;
305
306 pub const LOGIN_SERVER_MESSAGE_LEN: usize = 320;
307 pub const LOGIN_SERVER_STATE_LEN: usize = 192;
308 pub const LOGIN_CLIENT_STATE_LEN: usize = 192;
309 pub const LOGIN_CLIENT_MESSAGE_LEN: usize = 96;
310 pub const LOGIN_FINISH_MESSAGE_LEN: usize = 64;
311
312 pub const REGISTER_SERVER_MESSAGE_LEN: usize = 64;
313 pub const REGISTER_CLIENT_STATE_LEN: usize = 64;
314 pub const REGISTER_CLIENT_MESSAGE_LEN: usize = 32;
315 pub const REGISTER_FINISH_MESSAGE_LEN: usize = 192;
316
317 pub const SHARED_SECRET_LEN: usize = 64;
318
319 pub const PASSWORD_FILE_LEN: usize = 328;
320 pub const PASSWORD_FILE_SERIALIZED_LEN: usize = 192;
321
322 pub struct ServerLoginStartResult {
323 pub response: [u8; LOGIN_SERVER_MESSAGE_LEN],
324 pub state: [u8; LOGIN_SERVER_STATE_LEN],
325 }
326
327 pub struct ClientLoginStartResult {
328 pub response: [u8; LOGIN_CLIENT_MESSAGE_LEN],
329 }
330
331 pub struct ClientLoginFinishResult {
332 pub response: [u8; LOGIN_FINISH_MESSAGE_LEN],
333 pub shared_secret: [u8; SHARED_SECRET_LEN],
334 }
335
336 pub struct ServerLoginFinishResult {
337 pub shared_secret: [u8; SHARED_SECRET_LEN],
338 }
339
340 pub struct ClientRegistrationStartResult {
341 pub response: [u8; REGISTER_CLIENT_MESSAGE_LEN],
342 }
343
344 pub struct ClientRegistrationFinishResult {
345 pub response: [u8; REGISTER_FINISH_MESSAGE_LEN],
346 }
347
348 pub struct ServerRegistrationStartResult {
349 pub response: [u8; REGISTER_SERVER_MESSAGE_LEN],
350 }
351
352 #[cfg(test)]
353 mod test {
354 use opaque_ke::{ClientLogin, ClientRegistration};
355
356 use super::*;
357
358 #[test]
359 fn check_lengths() {
360 let mut rng = OsRng;
361 let server_setup = opaque_ke::ServerSetup::<Cipher>::new(&mut rng);
362
363 assert_eq!(SERVER_SETUP_LEN, server_setup.serialize().len());
364
365 let a = ClientRegistration::start(&mut rng, "my_pass".as_bytes()).unwrap();
366
367 assert_eq!(REGISTER_CLIENT_MESSAGE_LEN, a.message.serialize().len());
368 assert_eq!(
369 a.message.serialize().as_slice(),
370 a.message
371 .serialize()
372 .into_array::<REGISTER_CLIENT_MESSAGE_LEN>()
373 .as_slice()
374 );
375 assert_eq!(REGISTER_CLIENT_STATE_LEN, a.state.serialize().len());
376
377 let b =
378 ServerRegistration::start(&server_setup, a.message, "my_user".as_bytes()).unwrap();
379
380 assert_eq!(REGISTER_SERVER_MESSAGE_LEN, b.message.serialize().len());
381 assert_eq!(
382 b.message.serialize().as_slice(),
383 b.message
384 .serialize()
385 .into_array::<REGISTER_SERVER_MESSAGE_LEN>()
386 .as_slice()
387 );
388
389 let c = a
390 .state
391 .finish(
392 &mut rng,
393 "my_pass".as_bytes(),
394 b.message,
395 opaque_ke::ClientRegistrationFinishParameters::default(),
396 )
397 .unwrap();
398
399 assert_eq!(REGISTER_FINISH_MESSAGE_LEN, c.message.serialize().len());
400 assert_eq!(
401 c.message.serialize().as_slice(),
402 c.message
403 .serialize()
404 .into_array::<REGISTER_FINISH_MESSAGE_LEN>()
405 .as_slice()
406 );
407
408 let pw_file = ServerRegistration::finish(c.message);
409
410 assert_eq!(
411 size_of_val(&PasswordFile(pw_file.clone())),
412 PASSWORD_FILE_LEN
413 );
414 assert_eq!(size_of::<PasswordFile>(), PASSWORD_FILE_LEN);
415
416 let d = ClientLogin::<Cipher>::start(&mut rng, "my_pass".as_bytes()).unwrap();
417
418 assert_eq!(LOGIN_CLIENT_MESSAGE_LEN, d.message.serialize().len());
419 assert_eq!(
420 d.message.serialize().as_slice(),
421 d.message
422 .serialize()
423 .into_array::<LOGIN_CLIENT_MESSAGE_LEN>()
424 .as_slice()
425 );
426 assert_eq!(LOGIN_CLIENT_STATE_LEN, d.state.serialize().len());
427
428 let e = ServerLogin::<Cipher>::start(
429 &mut rng,
430 &server_setup,
431 Some(pw_file.clone()),
432 d.message,
433 "my_user".as_bytes(),
434 opaque_ke::ServerLoginStartParameters::default(),
435 )
436 .unwrap();
437
438 let f: [u8; LOGIN_SERVER_MESSAGE_LEN] = e.message.serialize().into_array();
439 let g: [u8; LOGIN_SERVER_MESSAGE_LEN] =
440 unsafe { core::mem::transmute(e.message.serialize()) };
441
442 assert_eq!(&f, e.message.serialize().as_slice());
443 assert_eq!(&g, &f);
444 assert_eq!(LOGIN_SERVER_MESSAGE_LEN, e.message.serialize().len());
445 assert_eq!(LOGIN_SERVER_STATE_LEN, e.state.serialize().len());
446
447 let h = d
448 .state
449 .finish(
450 "my_pass".as_bytes(),
451 e.message,
452 ClientLoginFinishParameters::default(),
453 )
454 .unwrap();
455
456 assert_eq!(LOGIN_FINISH_MESSAGE_LEN, h.message.serialize().len());
457 assert_eq!(
458 h.message.serialize().as_slice(),
459 h.message
460 .serialize()
461 .into_array::<LOGIN_FINISH_MESSAGE_LEN>()
462 );
463 assert_eq!(64, h.session_key.len());
464 }
465 }
466}
467
468#[cfg(test)]
469pub mod test_util {
470 use super::opaque_impl::*;
471
472 pub fn gen_password_file_with_setup_and_pw(setup: &mut ServerSetupView, user_id: &[u8], password: &[u8]) -> PasswordFile {
473 let mut client_state = ClientStateRegistration::setup();
474
475 let client_start = client_register_start(&mut client_state, password).unwrap();
476
477 let server_start = server_register_start(setup, &client_start.response, user_id).unwrap();
478
479 let client_finish = client_register_finish(&mut client_state, password, &server_start.response).unwrap();
480
481 let server_finish = server_register_finish(&client_finish.response).unwrap();
482
483 server_finish
484 }
485}
486
487mod generic_array_backport {
490 use generic_array::{ArrayLength, GenericArray};
491 use typenum::{Const, ToUInt};
492
493 const unsafe fn const_transmute<A, B>(a: A) -> B {
494 if std::mem::size_of::<A>() != std::mem::size_of::<B>() {
495 panic!("Size mismatch for generic_array::const_transmute");
496 }
497
498 #[repr(C)]
499 union Union<A, B> {
500 a: std::mem::ManuallyDrop<A>,
501 b: std::mem::ManuallyDrop<B>,
502 }
503
504 let a = std::mem::ManuallyDrop::new(a);
505 std::mem::ManuallyDrop::into_inner(Union { a }.b)
506 }
507
508 pub trait IntoArray<N: ArrayLength<u8>> {
509 fn into_array<const U: usize>(self) -> [u8; U]
510 where
511 Const<U>: IntoArrayLength<ArrayLength = N>,
512 Self: Sized;
513 }
514
515 impl<N: ArrayLength<u8>> IntoArray<N> for GenericArray<u8, N> {
516 fn into_array<const U: usize>(self) -> [u8; U]
517 where
518 Const<U>: IntoArrayLength<ArrayLength = N>,
519 Self: Sized,
520 {
521 unsafe { const_transmute(self) }
522 }
523 }
524
525 pub trait IntoArrayLength {
526 type ArrayLength: ArrayLength<u8>;
528 }
529
530 impl<const N: usize> IntoArrayLength for Const<N>
531 where
532 Const<N>: ToUInt,
533 typenum::U<N>: ArrayLength<u8>,
534 {
535 type ArrayLength = typenum::U<N>;
536 }
537}
538
539pub(crate) use generic_array_backport::IntoArray;