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