1use core::ops::{Add, Deref};
12
13use derive_where::derive_where;
14use digest::Output;
15use generic_array::sequence::Concat;
16use generic_array::typenum::{Sum, Unsigned};
17use generic_array::{ArrayLength, GenericArray};
18use hkdf::{Hkdf, HkdfExtract};
19use rand::{CryptoRng, RngCore};
20use subtle::{Choice, ConstantTimeEq, CtOption};
21use voprf::{BlindedElement, Group as _, OprfClient, OprfClientLen};
22use zeroize::Zeroizing;
23
24use crate::ciphersuite::{CipherSuite, KeGroup, KeHash, OprfGroup, OprfHash};
25use crate::envelope::{Envelope, EnvelopeLen};
26use crate::errors::{InternalError, ProtocolError};
27use crate::hash::OutputSize;
28use crate::key_exchange::group::Group;
29use crate::key_exchange::shared::NonceLen;
30use crate::key_exchange::{
31 Deserialize, Ke1MessageLen, Ke1StateLen, Ke2StateLen, KeyExchange, Serialize,
32 SerializedContext, SerializedCredentialResponse, SerializedIdentifiers,
33};
34use crate::keypair::{
35 KeyPair, OprfSeed, OprfSeedSerialization, PrivateKey, PrivateKeySerialization, PublicKey,
36};
37use crate::ksf::Ksf;
38use crate::messages::{CredentialRequestLen, RegistrationUploadLen};
39use crate::serialization::{GenericArrayExt, SliceExt};
40use crate::{
41 CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
42 RegistrationResponse, RegistrationUpload, ServerLoginBuilder,
43};
44
45const STR_CREDENTIAL_RESPONSE_PAD: &[u8; 21] = b"CredentialResponsePad";
51const STR_MASKING_KEY: &[u8; 10] = b"MaskingKey";
52const STR_OPRF_KEY: &[u8; 7] = b"OprfKey";
53const STR_OPAQUE_DERIVE_KEY_PAIR: &[u8; 20] = b"OPAQUE-DeriveKeyPair";
54
55#[cfg_attr(
62 feature = "serde",
63 derive(serde::Deserialize, serde::Serialize),
64 serde(bound(
65 deserialize = "<KeGroup<CS> as Group>::Pk: serde::Deserialize<'de>, <KeGroup<CS> as \
66 Group>::Sk: serde::Deserialize<'de>, SK: serde::Deserialize<'de>, OS: \
67 serde::Deserialize<'de>",
68 serialize = "<KeGroup<CS> as Group>::Pk: serde::Serialize, <KeGroup<CS> as Group>::Sk: \
69 serde::Serialize, SK: serde::Serialize, OS: serde::Serialize"
70 ))
71)]
72#[derive_where(Clone)]
73#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <KeGroup<CS> as Group>::Pk, <KeGroup<CS> as Group>::Sk, SK, OS)]
74pub struct ServerSetup<
75 CS: CipherSuite,
76 SK: Clone = PrivateKey<KeGroup<CS>>,
77 OS: Clone = OprfSeed<OprfHash<CS>>,
78> {
79 oprf_seed: OS,
80 keypair: KeyPair<KeGroup<CS>, SK>,
81 pub(crate) dummy_pk: PublicKey<KeGroup<CS>>,
82}
83
84#[cfg_attr(
86 feature = "serde",
87 derive(serde::Deserialize, serde::Serialize),
88 serde(bound = "")
89)]
90#[derive_where(Clone, ZeroizeOnDrop)]
91#[derive_where(
92 Debug, Eq, Hash, PartialEq;
93 voprf::OprfClient<CS::OprfCs>,
94 voprf::BlindedElement<CS::OprfCs>,
95)]
96pub struct ClientRegistration<CS: CipherSuite> {
97 pub(crate) oprf_client: voprf::OprfClient<CS::OprfCs>,
98 pub(crate) blinded_element: voprf::BlindedElement<CS::OprfCs>,
99}
100
101#[cfg_attr(
103 feature = "serde",
104 derive(serde::Deserialize, serde::Serialize),
105 serde(bound(
106 deserialize = "<KeGroup<CS> as Group>::Pk: serde::Deserialize<'de>",
107 serialize = "<KeGroup<CS> as Group>::Pk: serde::Serialize"
108 ))
109)]
110#[derive_where(Clone, ZeroizeOnDrop)]
111#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <KeGroup<CS> as Group>::Pk)]
112pub struct ServerRegistration<CS: CipherSuite>(pub(crate) RegistrationUpload<CS>);
113
114#[cfg_attr(
116 feature = "serde",
117 derive(serde::Deserialize, serde::Serialize),
118 serde(bound(
119 deserialize = "<CS::KeyExchange as KeyExchange>::KE1Message: serde::Deserialize<'de>, \
120 <CS::KeyExchange as KeyExchange>::KE1State: serde::Deserialize<'de>",
121 serialize = "<CS::KeyExchange as KeyExchange>::KE1Message: serde::Serialize, \
122 <CS::KeyExchange as KeyExchange>::KE1State: serde::Serialize"
123 ))
124)]
125#[derive_where(Clone, ZeroizeOnDrop)]
126#[derive_where(
127 Debug, Eq, Hash, PartialEq;
128 voprf::OprfClient<CS::OprfCs>,
129 <CS::KeyExchange as KeyExchange>::KE1State,
130 CredentialRequest<CS>,
131)]
132pub struct ClientLogin<CS: CipherSuite> {
133 pub(crate) oprf_client: voprf::OprfClient<CS::OprfCs>,
134 pub(crate) ke1_state: <CS::KeyExchange as KeyExchange>::KE1State,
135 pub(crate) credential_request: CredentialRequest<CS>,
136}
137
138#[cfg_attr(
140 feature = "serde",
141 derive(serde::Deserialize, serde::Serialize),
142 serde(bound(
143 deserialize = "<CS::KeyExchange as KeyExchange>::KE2State<CS>: serde::Deserialize<'de>",
144 serialize = "<CS::KeyExchange as KeyExchange>::KE2State<CS>: serde::Serialize"
145 ))
146)]
147#[derive_where(Clone, ZeroizeOnDrop)]
148#[derive_where(Debug, Eq, Hash, PartialEq; <CS::KeyExchange as KeyExchange>::KE2State<CS>)]
149pub struct ServerLogin<CS: CipherSuite> {
150 ke2_state: <CS::KeyExchange as KeyExchange>::KE2State<CS>,
151}
152
153impl<CS: CipherSuite> ServerSetup<CS, PrivateKey<KeGroup<CS>>> {
162 pub fn new<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
164 let keypair = KeyPair::random(rng);
165 Self::new_with_key_pair(rng, keypair)
166 }
167}
168
169pub type ServerSetupLen<
171 CS: CipherSuite,
172 SK: PrivateKeySerialization<KeGroup<CS>>,
173 OS: OprfSeedSerialization<OprfHash<CS>, SK::Error>,
174> = Sum<Sum<OS::Len, SK::Len>, <KeGroup<CS> as Group>::PkLen>;
175
176impl<CS: CipherSuite, SK: Clone, OS: Clone> ServerSetup<CS, SK, OS> {
177 pub fn new_with_key_pair_and_seed<R: CryptoRng + RngCore>(
183 rng: &mut R,
184 keypair: KeyPair<KeGroup<CS>, SK>,
185 oprf_seed: OS,
186 ) -> Self {
187 Self {
188 oprf_seed,
189 keypair,
190 dummy_pk: KeyPair::<KeGroup<CS>>::random(rng).public().clone(),
191 }
192 }
193
194 pub fn key_material_info<'ci>(
198 &self,
199 credential_identifier: &'ci [u8],
200 ) -> KeyMaterialInfo<'ci, OS> {
201 KeyMaterialInfo {
202 ikm: self.oprf_seed.clone(),
203 info: [credential_identifier, STR_OPRF_KEY],
204 }
205 }
206
207 pub fn serialize(&self) -> GenericArray<u8, ServerSetupLen<CS, SK, OS>>
209 where
210 SK: PrivateKeySerialization<KeGroup<CS>>,
211 OS: OprfSeedSerialization<OprfHash<CS>, SK::Error>,
212 OS::Len: Add<SK::Len>,
214 Sum<OS::Len, SK::Len>: ArrayLength<u8> + Add<<KeGroup<CS> as Group>::PkLen>,
215 ServerSetupLen<CS, SK, OS>: ArrayLength<u8>,
216 {
217 self.oprf_seed
218 .serialize()
219 .concat(SK::serialize_key_pair(&self.keypair))
220 .concat(self.dummy_pk.serialize())
221 }
222
223 pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError<SK::Error>>
225 where
226 SK: PrivateKeySerialization<KeGroup<CS>>,
227 OS: OprfSeedSerialization<OprfHash<CS>, SK::Error>,
228 {
229 Ok(Self {
230 oprf_seed: OS::deserialize_take(&mut input)?,
231 keypair: SK::deserialize_take_key_pair(&mut input)?,
232 dummy_pk: PublicKey::deserialize_take(&mut input)
233 .map_err(ProtocolError::into_custom)?,
234 })
235 }
236
237 pub fn keypair(&self) -> &KeyPair<KeGroup<CS>, SK> {
239 &self.keypair
240 }
241}
242
243impl<CS: CipherSuite, SK: Clone> ServerSetup<CS, SK> {
244 pub fn new_with_key_pair<R: CryptoRng + RngCore>(
250 rng: &mut R,
251 keypair: KeyPair<KeGroup<CS>, SK>,
252 ) -> Self {
253 let mut oprf_seed = GenericArray::default();
254 rng.fill_bytes(&mut oprf_seed);
255
256 Self::new_with_key_pair_and_seed(rng, keypair, OprfSeed(oprf_seed))
257 }
258}
259
260pub struct KeyMaterialInfo<'ci, OS: Clone> {
268 pub ikm: OS,
270 pub info: [&'ci [u8]; 2],
272}
273
274pub(crate) type ClientRegistrationLen<CS: CipherSuite> =
278 Sum<<OprfGroup<CS> as voprf::Group>::ScalarLen, <OprfGroup<CS> as voprf::Group>::ElemLen>;
279
280impl<CS: CipherSuite> ClientRegistration<CS> {
281 pub fn serialize(&self) -> GenericArray<u8, ClientRegistrationLen<CS>>
283 where
284 <OprfGroup<CS> as voprf::Group>::ScalarLen: Add<<OprfGroup<CS> as voprf::Group>::ElemLen>,
286 ClientRegistrationLen<CS>: ArrayLength<u8>,
287 {
288 self.oprf_client
289 .serialize()
290 .concat(self.blinded_element.serialize())
291 }
292
293 pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError> {
295 let oprf_client = OprfClient::deserialize(input)?;
296 input = &input[OprfClientLen::<CS::OprfCs>::USIZE..];
297
298 let blinded_element = BlindedElement::deserialize(input)?;
299
300 Ok(Self {
301 oprf_client,
302 blinded_element,
303 })
304 }
305
306 pub fn start<R: RngCore + CryptoRng>(
309 blinding_factor_rng: &mut R,
310 password: &[u8],
311 ) -> Result<ClientRegistrationStartResult<CS>, ProtocolError> {
312 let blind_result = blind::<CS, _>(blinding_factor_rng, password)?;
313
314 Ok(ClientRegistrationStartResult {
315 message: RegistrationRequest {
316 blinded_element: blind_result.message.clone(),
317 },
318 state: Self {
319 oprf_client: blind_result.state,
320 blinded_element: blind_result.message,
321 },
322 })
323 }
324
325 pub fn finish<R: CryptoRng + RngCore>(
329 self,
330 rng: &mut R,
331 password: &[u8],
332 registration_response: RegistrationResponse<CS>,
333 params: ClientRegistrationFinishParameters<CS>,
334 ) -> Result<ClientRegistrationFinishResult<CS>, ProtocolError> {
335 if self
337 .blinded_element
338 .value()
339 .ct_eq(®istration_response.evaluation_element.value())
340 .into()
341 {
342 return Err(ProtocolError::ReflectedValueError);
343 }
344
345 #[cfg_attr(not(test), allow(unused_variables))]
346 let (randomized_pwd, randomized_pwd_hasher) = get_password_derived_key::<CS>(
347 password,
348 self.oprf_client.clone(),
349 registration_response.evaluation_element,
350 params.ksf,
351 )?;
352
353 let mut masking_key = Output::<OprfHash<CS>>::default();
354 randomized_pwd_hasher
355 .expand(STR_MASKING_KEY, &mut masking_key)
356 .map_err(|_| InternalError::HkdfError)?;
357
358 let result = Envelope::<CS>::seal(
359 rng,
360 randomized_pwd_hasher,
361 ®istration_response.server_s_pk,
362 params.identifiers,
363 )?;
364
365 Ok(ClientRegistrationFinishResult {
366 message: RegistrationUpload {
367 envelope: result.0,
368 masking_key,
369 client_s_pk: result.1,
370 },
371 export_key: result.2,
372 server_s_pk: registration_response.server_s_pk,
373 #[cfg(test)]
374 state: self,
375 #[cfg(test)]
376 auth_key: result.3,
377 #[cfg(test)]
378 randomized_pwd,
379 })
380 }
381}
382
383pub type ServerRegistrationLen<CS> = RegistrationUploadLen<CS>;
385
386impl<CS: CipherSuite> ServerRegistration<CS> {
387 pub fn serialize(&self) -> GenericArray<u8, ServerRegistrationLen<CS>>
389 where
390 <KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
392 Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
393 ArrayLength<u8> + Add<EnvelopeLen<CS>>,
394 RegistrationUploadLen<CS>: ArrayLength<u8>,
395 {
397 self.0.serialize()
398 }
399
400 pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
402 Ok(Self(RegistrationUpload::deserialize(input)?))
403 }
404
405 pub fn start_with_key_material<SK: Clone, OS: Clone>(
410 server_setup: &ServerSetup<CS, SK, OS>,
411 key_material: GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>,
412 message: RegistrationRequest<CS>,
413 ) -> Result<ServerRegistrationStartResult<CS>, ProtocolError> {
414 let oprf_key = oprf_key_from_key_material::<CS>(key_material)?;
415
416 let server = voprf::OprfServer::new_with_key(&oprf_key)?;
417 let evaluation_element = server.blind_evaluate(&message.blinded_element);
418
419 Ok(ServerRegistrationStartResult {
420 message: RegistrationResponse {
421 evaluation_element,
422 server_s_pk: server_setup.keypair().public().clone(),
423 },
424 #[cfg(test)]
425 oprf_key,
426 })
427 }
428
429 pub fn start<SK: Clone>(
432 server_setup: &ServerSetup<CS, SK>,
433 message: RegistrationRequest<CS>,
434 credential_identifier: &[u8],
435 ) -> Result<ServerRegistrationStartResult<CS>, ProtocolError> {
436 let KeyMaterialInfo {
437 ikm: oprf_seed,
438 info,
439 } = server_setup.key_material_info(credential_identifier);
440 let key_material = oprf_key_material::<CS>(&oprf_seed.0, &info)?;
441
442 Self::start_with_key_material(server_setup, key_material, message)
443 }
444
445 pub fn finish(message: RegistrationUpload<CS>) -> Self {
448 Self(message)
449 }
450
451 pub(crate) fn dummy<R: RngCore + CryptoRng, SK: Clone, S: Clone>(
453 rng: &mut R,
454 server_setup: &ServerSetup<CS, SK, S>,
455 ) -> Self {
456 Self(RegistrationUpload::dummy(rng, server_setup))
457 }
458}
459
460pub(crate) type ClientLoginLen<CS: CipherSuite> =
464 Sum<Sum<<OprfGroup<CS> as voprf::Group>::ScalarLen, CredentialRequestLen<CS>>, Ke1StateLen<CS>>;
465
466impl<CS: CipherSuite> ClientLogin<CS> {
467 pub fn serialize(&self) -> GenericArray<u8, ClientLoginLen<CS>>
469 where
470 <CS::KeyExchange as KeyExchange>::KE1Message: Serialize,
472 <OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>>,
473 CredentialRequestLen<CS>: ArrayLength<u8>,
474 <OprfGroup<CS> as voprf::Group>::ScalarLen: Add<CredentialRequestLen<CS>>,
476 <CS::KeyExchange as KeyExchange>::KE1State: Serialize,
477 Sum<<OprfGroup<CS> as voprf::Group>::ScalarLen, CredentialRequestLen<CS>>:
478 ArrayLength<u8> + Add<Ke1StateLen<CS>>,
479 ClientLoginLen<CS>: ArrayLength<u8>,
480 {
481 self.oprf_client
482 .serialize()
483 .concat(self.credential_request.serialize())
484 .concat(self.ke1_state.serialize())
485 }
486
487 pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError>
489 where
490 <CS::KeyExchange as KeyExchange>::KE1Message: Deserialize + Serialize,
491 <CS::KeyExchange as KeyExchange>::KE1State: Deserialize + Serialize,
492 {
493 let oprf_client = OprfClient::deserialize(input)?;
494 input = &input[OprfClientLen::<CS::OprfCs>::USIZE..];
495
496 Ok(Self {
497 oprf_client,
498 credential_request: CredentialRequest::deserialize_take(&mut input)?,
499 ke1_state: <CS::KeyExchange as KeyExchange>::KE1State::deserialize_take(&mut input)?,
500 })
501 }
502}
503
504impl<CS: CipherSuite> ClientLogin<CS> {
505 pub fn start<R: RngCore + CryptoRng>(
508 rng: &mut R,
509 password: &[u8],
510 ) -> Result<ClientLoginStartResult<CS>, ProtocolError> {
511 let blind_result = blind::<CS, _>(rng, password)?;
512 let ke1_result = CS::KeyExchange::generate_ke1(rng)?;
513
514 let credential_request = CredentialRequest {
515 blinded_element: blind_result.message,
516 ke1_message: ke1_result.message,
517 };
518
519 Ok(ClientLoginStartResult {
520 message: credential_request.clone(),
521 state: Self {
522 oprf_client: blind_result.state,
523 ke1_state: ke1_result.state,
524 credential_request,
525 },
526 })
527 }
528
529 pub fn finish<R: CryptoRng + RngCore>(
532 self,
533 rng: &mut R,
534 password: &[u8],
535 credential_response: CredentialResponse<CS>,
536 params: ClientLoginFinishParameters<CS>,
537 ) -> Result<ClientLoginFinishResult<CS>, ProtocolError> {
538 if self
540 .credential_request
541 .blinded_element
542 .value()
543 .ct_eq(&credential_response.evaluation_element.value())
544 .into()
545 {
546 return Err(ProtocolError::ReflectedValueError);
547 }
548
549 let (_, randomized_pwd_hasher) = get_password_derived_key::<CS>(
550 password,
551 self.oprf_client.clone(),
552 credential_response.evaluation_element.clone(),
553 params.ksf,
554 )?;
555
556 let mut masking_key = Output::<OprfHash<CS>>::default();
557 randomized_pwd_hasher
558 .expand(STR_MASKING_KEY, &mut masking_key)
559 .map_err(|_| InternalError::HkdfError)?;
560
561 let (server_s_pk, envelope) = unmask_response::<CS>(
562 &masking_key,
563 &credential_response.masking_nonce,
564 &credential_response.masked_response,
565 )
566 .map_err(|e| match e {
567 ProtocolError::SerializationError => ProtocolError::InvalidLoginError,
568 err => err,
569 })?;
570
571 let opened_envelope = envelope
572 .open(
573 randomized_pwd_hasher,
574 server_s_pk.clone(),
575 params.identifiers,
576 )
577 .map_err(|e| match e {
578 ProtocolError::LibraryError(InternalError::SealOpenHmacError) => {
579 ProtocolError::InvalidLoginError
580 }
581 err => err,
582 })?;
583
584 let context = SerializedContext::from(params.context)?;
585
586 let result = CS::KeyExchange::generate_ke3(
587 rng,
588 self.credential_request.to_parts(),
589 self.credential_request.ke1_message.clone(),
590 credential_response.to_parts(),
591 &self.ke1_state,
592 credential_response.ke2_message,
593 server_s_pk.clone(),
594 opened_envelope.client_static_keypair.private().clone(),
595 opened_envelope.identifiers,
596 context,
597 )?;
598
599 Ok(ClientLoginFinishResult {
600 message: CredentialFinalization {
601 ke3_message: result.message,
602 },
603 session_key: result.session_key,
604 export_key: opened_envelope.export_key,
605 server_s_pk,
606 #[cfg(test)]
607 state: self,
608 #[cfg(test)]
609 handshake_secret: result.handshake_secret,
610 #[cfg(test)]
611 client_mac_key: result.km3,
612 })
613 }
614}
615
616impl<CS: CipherSuite> ServerLogin<CS> {
617 pub fn serialize(&self) -> GenericArray<u8, Ke2StateLen<CS>>
619 where
620 <CS::KeyExchange as KeyExchange>::KE2State<CS>: Serialize,
621 {
622 self.ke2_state.serialize()
623 }
624
625 pub fn deserialize(mut bytes: &[u8]) -> Result<Self, ProtocolError>
627 where
628 <CS::KeyExchange as KeyExchange>::KE2State<CS>: Deserialize,
629 {
630 Ok(Self {
631 ke2_state:
632 <<CS::KeyExchange as KeyExchange>::KE2State<CS> as Deserialize>::deserialize_take(
633 &mut bytes,
634 )?,
635 })
636 }
637
638 pub fn builder_with_key_material<'a, R: RngCore + CryptoRng, SK: Clone, OS: Clone>(
645 rng: &mut R,
646 server_setup: &ServerSetup<CS, SK, OS>,
647 key_material: GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>,
648 password_file: Option<ServerRegistration<CS>>,
649 credential_request: CredentialRequest<CS>,
650 ServerLoginParameters {
651 context,
652 identifiers,
653 }: ServerLoginParameters<'a, 'a>,
654 ) -> Result<ServerLoginBuilder<'a, CS, SK>, ProtocolError> {
655 let record = CtOption::new(
656 ServerRegistration::dummy(rng, server_setup),
657 Choice::from(password_file.is_none() as u8),
658 )
659 .into_option()
660 .unwrap_or_else(|| password_file.unwrap());
661
662 let client_s_pk = record.0.client_s_pk.clone();
663 let context = SerializedContext::from(context)?;
664 let server_s_pk = server_setup.keypair.public();
665
666 let mut masking_nonce = GenericArray::<_, NonceLen>::default();
667 rng.fill_bytes(&mut masking_nonce);
668
669 let masked_response = mask_response(
670 &record.0.masking_key,
671 masking_nonce.as_slice(),
672 server_s_pk,
673 &record.0.envelope,
674 )?;
675
676 let serialized_client_s_pk = client_s_pk.serialize();
677 let serialized_server_s_pk = server_s_pk.serialize();
678 let identifiers = SerializedIdentifiers::<KeGroup<CS>>::from_identifiers(
679 identifiers,
680 serialized_client_s_pk.clone(),
681 serialized_server_s_pk.clone(),
682 )?;
683
684 let oprf_key = oprf_key_from_key_material::<CS>(key_material)?;
685 let server = voprf::OprfServer::new_with_key(&oprf_key).map_err(ProtocolError::from)?;
686 let evaluation_element = server.blind_evaluate(&credential_request.blinded_element);
687
688 let credential_response = SerializedCredentialResponse::new(
689 &evaluation_element,
690 masking_nonce,
691 masked_response.clone(),
692 );
693
694 let ke2_builder = CS::KeyExchange::ke2_builder(
695 rng,
696 credential_request.to_parts(),
697 credential_request.ke1_message.clone(),
698 credential_response,
699 client_s_pk,
700 identifiers,
701 context,
702 )?;
703
704 Ok(ServerLoginBuilder {
705 server_s_sk: server_setup.keypair().private().clone(),
706 evaluation_element,
707 masking_nonce: Zeroizing::new(masking_nonce),
708 masked_response,
709 #[cfg(test)]
710 oprf_key: Zeroizing::new(oprf_key),
711 ke2_builder,
712 })
713 }
714
715 pub fn builder<'a, R: RngCore + CryptoRng, SK: Clone>(
719 rng: &mut R,
720 server_setup: &ServerSetup<CS, SK>,
721 password_file: Option<ServerRegistration<CS>>,
722 credential_request: CredentialRequest<CS>,
723 credential_identifier: &[u8],
724 params: ServerLoginParameters<'a, 'a>,
725 ) -> Result<ServerLoginBuilder<'a, CS, SK>, ProtocolError> {
726 let KeyMaterialInfo {
727 ikm: oprf_seed,
728 info,
729 } = server_setup.key_material_info(credential_identifier);
730 let key_material = oprf_key_material::<CS>(&oprf_seed.0, &info)?;
731
732 Self::builder_with_key_material(
733 rng,
734 server_setup,
735 key_material,
736 password_file,
737 credential_request,
738 params,
739 )
740 }
741
742 pub(crate) fn build<SK: Clone>(
743 builder: ServerLoginBuilder<CS, SK>,
744 input: <CS::KeyExchange as KeyExchange>::KE2BuilderInput<CS>,
745 ) -> Result<ServerLoginStartResult<CS>, ProtocolError> {
746 let result = CS::KeyExchange::build_ke2(builder.ke2_builder.clone(), input)?;
747
748 let credential_response = CredentialResponse {
749 evaluation_element: builder.evaluation_element.clone(),
750 masking_nonce: *builder.masking_nonce.deref(),
751 masked_response: builder.masked_response.clone(),
752 ke2_message: result.message,
753 };
754
755 Ok(ServerLoginStartResult {
756 message: credential_response,
757 state: Self {
758 ke2_state: result.state,
759 },
760 #[cfg(test)]
761 handshake_secret: result.handshake_secret,
762 #[cfg(test)]
763 server_mac_key: result.km2,
764 #[cfg(test)]
765 oprf_key: builder.oprf_key.deref().clone(),
766 })
767 }
768
769 pub fn start<R: RngCore + CryptoRng>(
772 rng: &mut R,
773 server_setup: &ServerSetup<CS>,
774 password_file: Option<ServerRegistration<CS>>,
775 credential_request: CredentialRequest<CS>,
776 credential_identifier: &[u8],
777 parameters: ServerLoginParameters,
778 ) -> Result<ServerLoginStartResult<CS>, ProtocolError> {
779 let builder = Self::builder(
780 rng,
781 server_setup,
782 password_file,
783 credential_request,
784 credential_identifier,
785 parameters,
786 )?;
787 let input = CS::KeyExchange::generate_ke2_input(
788 &builder.ke2_builder,
789 rng,
790 server_setup.keypair.private(),
791 );
792
793 Self::build(builder, input)
794 }
795
796 pub fn finish(
799 self,
800 message: CredentialFinalization<CS>,
801 parameters: ServerLoginParameters,
802 ) -> Result<ServerLoginFinishResult<CS>, ProtocolError> {
803 let context = SerializedContext::from(parameters.context)?;
804
805 let session_key = <CS::KeyExchange as KeyExchange>::finish_ke(
806 &self.ke2_state,
807 message.ke3_message,
808 parameters.identifiers,
809 context,
810 )?;
811
812 Ok(ServerLoginFinishResult {
813 session_key,
814 #[cfg(test)]
815 state: self,
816 })
817 }
818}
819
820#[derive(Clone, Copy, Debug, Default)]
827pub struct Identifiers<'a> {
828 pub client: Option<&'a [u8]>,
830 pub server: Option<&'a [u8]>,
832}
833
834#[derive_where(Clone, Default)]
836pub struct ClientRegistrationFinishParameters<'i, 'h, CS: CipherSuite> {
837 pub identifiers: Identifiers<'i>,
839 pub ksf: Option<&'h CS::Ksf>,
841}
842
843impl<'i, 'h, CS: CipherSuite> ClientRegistrationFinishParameters<'i, 'h, CS> {
844 pub fn new(identifiers: Identifiers<'i>, ksf: Option<&'h CS::Ksf>) -> Self {
846 Self { identifiers, ksf }
847 }
848}
849
850#[derive_where(Clone)]
852pub struct ClientRegistrationStartResult<CS: CipherSuite> {
853 pub message: RegistrationRequest<CS>,
855 pub state: ClientRegistration<CS>,
858}
859
860#[derive_where(Clone)]
862pub struct ClientRegistrationFinishResult<CS: CipherSuite> {
863 pub message: RegistrationUpload<CS>,
865 pub export_key: Output<OprfHash<CS>>,
867 pub server_s_pk: PublicKey<KeGroup<CS>>,
869 #[cfg(test)]
872 pub state: ClientRegistration<CS>,
873 #[cfg(test)]
875 pub auth_key: Output<OprfHash<CS>>,
876 #[cfg(test)]
878 pub randomized_pwd: Output<OprfHash<CS>>,
879}
880
881#[derive_where(Clone)]
884pub struct ServerRegistrationStartResult<CS: CipherSuite> {
885 pub message: RegistrationResponse<CS>,
887 #[cfg(test)]
889 pub oprf_key: GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>,
890}
891
892#[derive_where(Clone)]
894pub struct ClientLoginStartResult<CS: CipherSuite> {
895 pub message: CredentialRequest<CS>,
897 pub state: ClientLogin<CS>,
899}
900
901#[derive_where(Clone, Default)]
903pub struct ClientLoginFinishParameters<'c, 'i, 'h, CS: CipherSuite> {
904 pub context: Option<&'c [u8]>,
906 pub identifiers: Identifiers<'i>,
909 pub ksf: Option<&'h CS::Ksf>,
911}
912
913impl<'c, 'i, 'h, CS: CipherSuite> ClientLoginFinishParameters<'c, 'i, 'h, CS> {
914 pub fn new(
916 context: Option<&'c [u8]>,
917 identifiers: Identifiers<'i>,
918 ksf: Option<&'h CS::Ksf>,
919 ) -> Self {
920 Self {
921 context,
922 identifiers,
923 ksf,
924 }
925 }
926}
927
928#[derive_where(Clone)]
930pub struct ClientLoginFinishResult<CS: CipherSuite> {
931 pub message: CredentialFinalization<CS>,
933 pub session_key: Output<KeHash<CS>>,
935 pub export_key: Output<OprfHash<CS>>,
937 pub server_s_pk: PublicKey<KeGroup<CS>>,
939 #[cfg(test)]
941 pub state: ClientLogin<CS>,
942 #[cfg(test)]
944 pub handshake_secret: Output<KeHash<CS>>,
945 #[cfg(test)]
947 pub client_mac_key: Output<KeHash<CS>>,
948}
949
950#[derive_where(Clone)]
952#[cfg_attr(not(test), derive_where(Debug))]
953#[cfg_attr(test, derive_where(Debug; ServerLogin<CS>))]
954pub struct ServerLoginFinishResult<CS: CipherSuite> {
955 pub session_key: Output<KeHash<CS>>,
957 #[cfg(test)]
960 pub state: ServerLogin<CS>,
961}
962
963#[derive(Clone, Debug, Default)]
965pub struct ServerLoginParameters<'c, 'i> {
966 pub context: Option<&'c [u8]>,
968 pub identifiers: Identifiers<'i>,
971}
972
973#[derive_where(Clone)]
975#[derive_where(
976 Debug;
977 <KeGroup<CS> as Group>::Pk,
978 voprf::EvaluationElement<CS::OprfCs>,
979 <CS::KeyExchange as KeyExchange>::KE2Message,
980 <CS::KeyExchange as KeyExchange>::KE2State<CS>,
981)]
982pub struct ServerLoginStartResult<CS: CipherSuite> {
983 pub message: CredentialResponse<CS>,
985 pub state: ServerLogin<CS>,
987 #[cfg(test)]
989 pub handshake_secret: Output<KeHash<CS>>,
990 #[cfg(test)]
992 pub server_mac_key: Output<KeHash<CS>>,
993 #[cfg(test)]
995 pub oprf_key: GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>,
996}
997
998#[allow(clippy::type_complexity)]
1005fn get_password_derived_key<CS: CipherSuite>(
1006 input: &[u8],
1007 oprf_client: voprf::OprfClient<CS::OprfCs>,
1008 evaluation_element: voprf::EvaluationElement<CS::OprfCs>,
1009 ksf: Option<&CS::Ksf>,
1010) -> Result<(Output<OprfHash<CS>>, Hkdf<OprfHash<CS>>), ProtocolError> {
1011 let oprf_output = oprf_client.finalize(input, &evaluation_element)?;
1012
1013 let hardened_output = if let Some(ksf) = ksf {
1014 ksf.hash(oprf_output.clone())
1015 } else {
1016 CS::Ksf::default().hash(oprf_output.clone())
1017 }
1018 .map_err(ProtocolError::from)?;
1019
1020 let mut hkdf = HkdfExtract::<OprfHash<CS>>::new(None);
1021 hkdf.input_ikm(&oprf_output);
1022 hkdf.input_ikm(&hardened_output);
1023 Ok(hkdf.finalize())
1024}
1025
1026fn oprf_key_material<CS: CipherSuite>(
1027 oprf_seed: &Output<OprfHash<CS>>,
1028 info: &[&[u8]],
1029) -> Result<GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>, InternalError> {
1030 let mut ikm = GenericArray::<_, <OprfGroup<CS> as voprf::Group>::ScalarLen>::default();
1031 Hkdf::<OprfHash<CS>>::from_prk(oprf_seed)
1032 .ok()
1033 .and_then(|hkdf| hkdf.expand_multi_info(info, &mut ikm).ok())
1034 .ok_or(InternalError::HkdfError)?;
1035
1036 Ok(ikm)
1037}
1038
1039fn oprf_key_from_key_material<CS: CipherSuite>(
1040 input: GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>,
1041) -> Result<GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>, InternalError> {
1042 Ok(OprfGroup::<CS>::serialize_scalar(voprf::derive_key::<
1043 CS::OprfCs,
1044 >(
1045 input.as_slice(),
1046 &GenericArray::from(*STR_OPAQUE_DERIVE_KEY_PAIR),
1047 voprf::Mode::Oprf,
1048 )?))
1049}
1050
1051#[cfg_attr(
1052 feature = "serde",
1053 derive(serde::Deserialize, serde::Serialize),
1054 serde(bound = "")
1055)]
1056#[derive_where(Clone, Zeroize)]
1057#[derive_where(Debug, Eq, Hash, PartialEq)]
1058pub(crate) struct MaskedResponse<CS: CipherSuite> {
1059 pub(crate) nonce: GenericArray<u8, NonceLen>,
1060 pub(crate) hash: Output<OprfHash<CS>>,
1061 pub(crate) pk: GenericArray<u8, <KeGroup<CS> as Group>::PkLen>,
1062}
1063
1064pub(crate) type MaskedResponseLen<CS: CipherSuite> =
1065 Sum<Sum<OutputSize<OprfHash<CS>>, NonceLen>, <KeGroup<CS> as Group>::PkLen>;
1066
1067impl<CS: CipherSuite> MaskedResponse<CS> {
1068 pub(crate) fn serialize(&self) -> GenericArray<u8, MaskedResponseLen<CS>> {
1069 self.nonce.concat_ext(&self.hash).concat(self.pk.clone())
1070 }
1071
1072 pub(crate) fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
1073 Ok(Self {
1074 nonce: bytes.take_array("masked nonce")?,
1075 hash: bytes.take_array("masked hash")?,
1076 pk: bytes.take_array("masked public key")?,
1077 })
1078 }
1079
1080 pub(crate) fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
1081 [self.nonce.as_slice(), &self.hash, &self.pk].into_iter()
1082 }
1083}
1084
1085fn mask_response<CS: CipherSuite>(
1086 masking_key: &[u8],
1087 masking_nonce: &[u8],
1088 server_s_pk: &PublicKey<KeGroup<CS>>,
1089 envelope: &Envelope<CS>,
1090) -> Result<MaskedResponse<CS>, ProtocolError> {
1091 let mut xor_pad = GenericArray::<_, MaskedResponseLen<CS>>::default();
1092
1093 Hkdf::<OprfHash<CS>>::from_prk(masking_key)
1094 .map_err(|_| InternalError::HkdfError)?
1095 .expand_multi_info(&[masking_nonce, STR_CREDENTIAL_RESPONSE_PAD], &mut xor_pad)
1096 .map_err(|_| InternalError::HkdfError)?;
1097
1098 for (x1, x2) in xor_pad.iter_mut().zip(
1099 server_s_pk
1100 .serialize()
1101 .as_slice()
1102 .iter()
1103 .chain(envelope.serialize().iter()),
1104 ) {
1105 *x1 ^= x2
1106 }
1107
1108 MaskedResponse::deserialize_take(&mut (xor_pad.as_slice()))
1109}
1110
1111fn unmask_response<CS: CipherSuite>(
1112 masking_key: &[u8],
1113 masking_nonce: &[u8],
1114 masked_response: &MaskedResponse<CS>,
1115) -> Result<(PublicKey<KeGroup<CS>>, Envelope<CS>), ProtocolError> {
1116 let mut xor_pad = GenericArray::<_, MaskedResponseLen<CS>>::default();
1117
1118 Hkdf::<OprfHash<CS>>::from_prk(masking_key)
1119 .map_err(|_| InternalError::HkdfError)?
1120 .expand_multi_info(&[masking_nonce, STR_CREDENTIAL_RESPONSE_PAD], &mut xor_pad)
1121 .map_err(|_| InternalError::HkdfError)?;
1122
1123 for (x1, x2) in xor_pad.iter_mut().zip(masked_response.iter().flatten()) {
1124 *x1 ^= x2
1125 }
1126
1127 let mut xor_pad = xor_pad.as_slice();
1128 let server_s_pk =
1129 PublicKey::deserialize_take(&mut xor_pad).map_err(|_| ProtocolError::SerializationError)?;
1130 let envelope = Envelope::deserialize_take(&mut xor_pad)?;
1131
1132 Ok((server_s_pk, envelope))
1133}
1134
1135fn blind<CS: CipherSuite, R: RngCore + CryptoRng>(
1139 rng: &mut R,
1140 password: &[u8],
1141) -> Result<voprf::OprfClientBlindResult<CS::OprfCs>, voprf::Error> {
1142 #[cfg(not(test))]
1143 let result = voprf::OprfClient::blind(password, rng)?;
1144
1145 #[cfg(test)]
1146 let result = {
1147 let mut blind_bytes =
1148 GenericArray::<_, <OprfGroup<CS> as voprf::Group>::ScalarLen>::default();
1149 let blind = loop {
1150 rng.fill_bytes(&mut blind_bytes);
1151 if let Ok(scalar) = <OprfGroup<CS> as voprf::Group>::deserialize_scalar(&blind_bytes) {
1152 break scalar;
1153 }
1154 };
1155 voprf::OprfClient::deterministic_blind_unchecked(password, blind)?
1156 };
1157
1158 Ok(result)
1159}