1use crate::Database;
2use crate::constants::MIN_SSID_LEN;
3use crate::utils::{
4 H0, compute_authenticator_messages, compute_first_session_key, compute_session_key,
5 compute_ssid, generate_keypair, generate_nonce, generate_server_keypair,
6};
7use crate::{Error, Result};
8use core::marker::PhantomData;
9use curve25519_dalek::traits::IsIdentity;
10use curve25519_dalek::{
11 digest::consts::U64,
12 digest::{Digest, Output},
13 ristretto::RistrettoPoint,
14 scalar::Scalar,
15};
16use password_hash::{ParamsString, SaltString};
17use rand_core::{TryCryptoRng, TryRngCore};
18use subtle::ConstantTimeEq;
19
20#[cfg(feature = "partial_augmentation")]
21use crate::database::PartialAugDatabase;
22
23#[cfg(feature = "strong_aucpace")]
24use crate::database::StrongDatabase;
25
26#[cfg(feature = "serde")]
27use crate::utils::{serde_paramsstring, serde_saltstring};
28
29#[cfg(feature = "serde")]
30use serde::{Deserialize, Serialize};
31
32#[derive(Clone)]
34struct ServerSecret(u64);
35
36impl ServerSecret {
37 fn new<CSPRNG: TryRngCore + TryCryptoRng>(rng: &mut CSPRNG) -> Result<Self> {
38 Ok(Self(rng.try_next_u64().map_err(|_| Error::Rng)?))
39 }
40}
41
42pub struct AuCPaceServer<D, CSPRNG, const K1: usize>
44where
45 D: Digest + Default,
46 CSPRNG: TryRngCore + TryCryptoRng,
47{
48 rng: CSPRNG,
50
51 secret: ServerSecret,
53
54 d: PhantomData<D>,
55}
56
57impl<D, CSPRNG, const K1: usize> AuCPaceServer<D, CSPRNG, K1>
58where
59 D: Digest<OutputSize = U64> + Default,
60 CSPRNG: TryRngCore + TryCryptoRng,
61{
62 pub fn new(mut rng: CSPRNG) -> Result<Self> {
64 let secret = ServerSecret::new(&mut rng)?;
65 Ok(Self {
66 rng,
67 secret,
68 d: PhantomData,
69 })
70 }
71
72 pub fn begin(
80 &mut self,
81 ) -> Result<(
82 AuCPaceServerSsidEstablish<D, K1>,
83 ServerMessage<'static, K1>,
84 )> {
85 let next_step = AuCPaceServerSsidEstablish::new(self.secret.clone(), &mut self.rng)?;
86 let message = ServerMessage::Nonce(next_step.nonce);
87 Ok((next_step, message))
88 }
89
90 pub fn begin_prestablished_ssid<S>(&mut self, ssid: S) -> Result<AuCPaceServerAugLayer<D, K1>>
100 where
101 S: AsRef<[u8]>,
102 {
103 if ssid.as_ref().len() < MIN_SSID_LEN {
105 return Err(Error::InsecureSsid);
106 }
107
108 let mut hasher: D = H0();
110 hasher.update(ssid);
111 let ssid_hash = hasher.finalize();
112 let next_step = AuCPaceServerAugLayer::new(self.secret.clone(), ssid_hash);
113 Ok(next_step)
114 }
115
116 #[cfg(feature = "partial_augmentation")]
127 pub fn generate_long_term_keypair(&mut self) -> Result<(Scalar, RistrettoPoint)> {
128 generate_server_keypair::<D, _>(&mut self.rng)
129 }
130}
131
132#[derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
134pub struct AuCPaceServerSsidEstablish<D, const K1: usize>
135where
136 D: Digest<OutputSize = U64> + Default,
137{
138 #[zeroize(skip)]
139 secret: ServerSecret,
140 nonce: [u8; K1],
141 _d: PhantomData<D>,
142}
143
144impl<D, const K1: usize> AuCPaceServerSsidEstablish<D, K1>
145where
146 D: Digest<OutputSize = U64> + Default,
147{
148 fn new<CSPRNG>(secret: ServerSecret, rng: &mut CSPRNG) -> Result<Self>
149 where
150 CSPRNG: TryRngCore + TryCryptoRng,
151 {
152 Ok(Self {
153 secret,
154 nonce: generate_nonce(rng)?,
155 _d: PhantomData,
156 })
157 }
158
159 #[must_use]
168 pub fn agree_ssid(self, client_nonce: [u8; K1]) -> AuCPaceServerAugLayer<D, K1> {
169 let ssid = compute_ssid::<D, K1>(self.nonce, client_nonce);
170 AuCPaceServerAugLayer::new(self.secret.clone(), ssid)
171 }
172}
173
174#[derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
176pub struct AuCPaceServerAugLayer<D, const K1: usize>
177where
178 D: Digest<OutputSize = U64> + Default,
179{
180 #[zeroize(skip)]
181 secret: ServerSecret,
182 #[zeroize(skip)]
183 ssid: Output<D>,
184}
185
186impl<D, const K1: usize> AuCPaceServerAugLayer<D, K1>
187where
188 D: Digest<OutputSize = U64> + Default,
189{
190 const fn new(secret: ServerSecret, ssid: Output<D>) -> Self {
191 Self { secret, ssid }
192 }
193
194 pub fn generate_client_info<U, DB, CSPRNG>(
207 self,
208 username: U,
209 database: &DB,
210 mut rng: CSPRNG,
211 ) -> Result<(
212 AuCPaceServerCPaceSubstep<D, CSPRNG, K1>,
213 ServerMessage<'static, K1>,
214 )>
215 where
216 U: AsRef<[u8]>,
217 DB: Database<PasswordVerifier = RistrettoPoint>,
218 CSPRNG: TryRngCore + TryCryptoRng,
219 {
220 let (x, x_pub) = generate_server_keypair::<D, _>(&mut rng)?;
221
222 let (prs, message) = self.generate_prs(username.as_ref(), database, &mut rng, x, x_pub)?;
224 let next_step = AuCPaceServerCPaceSubstep::new(self.ssid, prs, rng);
225
226 Ok((next_step, message))
227 }
228
229 #[cfg(feature = "partial_augmentation")]
250 pub fn generate_client_info_partial_aug<U, DB, CSPRNG>(
251 self,
252 username: U,
253 database: &DB,
254 mut rng: CSPRNG,
255 ) -> Result<(
256 AuCPaceServerCPaceSubstep<D, CSPRNG, K1>,
257 ServerMessage<'static, K1>,
258 )>
259 where
260 U: AsRef<[u8]>,
261 DB: Database<PasswordVerifier = RistrettoPoint>
262 + PartialAugDatabase<PrivateKey = Scalar, PublicKey = RistrettoPoint>,
263 CSPRNG: TryRngCore + TryCryptoRng,
264 {
265 let user = username.as_ref();
266 let (prs, message) = if let Some((x, x_pub)) = database.lookup_long_term_keypair(user) {
267 self.generate_prs(user, database, &mut rng, x, x_pub)?
269 } else {
270 let x_pub = {
273 let mut seed = [0u8; 32];
274 rng.try_fill_bytes(&mut seed).map_err(|_| Error::Rng)?;
275 let mut hasher: D = crate::utils::H1();
276 hasher.update(&seed);
277 RistrettoPoint::from_hash(hasher)
278 };
279 self.lookup_failed(user, x_pub, &mut rng)?
280 };
281 let next_step = AuCPaceServerCPaceSubstep::new(self.ssid, prs, rng);
282
283 Ok((next_step, message))
284 }
285
286 #[cfg(feature = "strong_aucpace")]
305 pub fn generate_client_info_strong<U, DB, CSPRNG>(
306 self,
307 username: U,
308 blinded: RistrettoPoint,
309 database: &DB,
310 mut rng: CSPRNG,
311 ) -> Result<(
312 AuCPaceServerCPaceSubstep<D, CSPRNG, K1>,
313 ServerMessage<'static, K1>,
314 )>
315 where
316 U: AsRef<[u8]>,
317 DB: StrongDatabase<PasswordVerifier = RistrettoPoint, Exponent = Scalar>,
318 CSPRNG: TryRngCore + TryCryptoRng,
319 {
320 let (x, x_pub) = generate_server_keypair::<D, _>(&mut rng)?;
321
322 let (prs, message) =
324 self.generate_prs_strong(username.as_ref(), blinded, database, &mut rng, x, x_pub)?;
325 let next_step = AuCPaceServerCPaceSubstep::new(self.ssid, prs, rng);
326
327 Ok((next_step, message))
328 }
329
330 #[cfg(all(feature = "strong_aucpace", feature = "partial_augmentation"))]
350 pub fn generate_client_info_partial_strong<U, DB, CSPRNG>(
351 self,
352 username: U,
353 blinded: RistrettoPoint,
354 database: &DB,
355 mut rng: CSPRNG,
356 ) -> Result<(
357 AuCPaceServerCPaceSubstep<D, CSPRNG, K1>,
358 ServerMessage<'static, K1>,
359 )>
360 where
361 U: AsRef<[u8]>,
362 DB: StrongDatabase<PasswordVerifier = RistrettoPoint, Exponent = Scalar>
363 + PartialAugDatabase<PrivateKey = Scalar, PublicKey = RistrettoPoint>,
364 CSPRNG: TryRngCore + TryCryptoRng,
365 {
366 let user = username.as_ref();
367 let (prs, message) = if let Some((x, x_pub)) = database.lookup_long_term_keypair(user) {
368 self.generate_prs_strong(user, blinded, database, &mut rng, x, x_pub)?
370 } else {
371 let x_pub = {
374 let mut seed = [0u8; 32];
375 rng.try_fill_bytes(&mut seed).map_err(|_| Error::Rng)?;
376 let mut hasher: D = crate::utils::H1();
377 hasher.update(&seed);
378 RistrettoPoint::from_hash(hasher)
379 };
380 self.lookup_failed_strong(user, blinded, x_pub, &mut rng)?
381 };
382 let next_step = AuCPaceServerCPaceSubstep::new(self.ssid, prs, rng);
383
384 Ok((next_step, message))
385 }
386
387 fn generate_prs<DB, CSPRNG>(
389 &self,
390 username: &[u8],
391 database: &DB,
392 rng: &mut CSPRNG,
393 x: Scalar,
394 x_pub: RistrettoPoint,
395 ) -> Result<([u8; 32], ServerMessage<'static, K1>)>
396 where
397 DB: Database<PasswordVerifier = RistrettoPoint>,
398 CSPRNG: TryRngCore + TryCryptoRng,
399 {
400 if let Some((w, salt, sigma)) = database.lookup_verifier(username.as_ref()) {
401 let cofactor = Scalar::ONE;
402 let prs = (w * x * cofactor).compress().to_bytes();
403 let message = ServerMessage::AugmentationInfo {
404 group: "ristretto255",
406 x_pub,
407 salt,
408 pbkdf_params: sigma,
409 };
410 Ok((prs, message))
411 } else {
412 self.lookup_failed(username, x_pub, rng)
414 }
415 }
416
417 #[cfg(feature = "strong_aucpace")]
420 fn generate_prs_strong<DB, CSPRNG>(
421 &self,
422 username: &[u8],
423 blinded: RistrettoPoint,
424 database: &DB,
425 rng: &mut CSPRNG,
426 x: Scalar,
427 x_pub: RistrettoPoint,
428 ) -> Result<([u8; 32], ServerMessage<'static, K1>)>
429 where
430 DB: StrongDatabase<PasswordVerifier = RistrettoPoint, Exponent = Scalar>,
431 CSPRNG: TryRngCore + TryCryptoRng,
432 {
433 if let Some((w, q, sigma)) = database.lookup_verifier_strong(username.as_ref()) {
434 let cofactor = Scalar::ONE;
435 let prs = (w * (x * cofactor)).compress().to_bytes();
436 let uq = blinded * (q * cofactor);
437 if uq.is_identity() {
438 return Err(Error::IllegalPointError);
439 }
440 let message = ServerMessage::StrongAugmentationInfo {
441 group: "ristretto255",
443 x_pub,
444 blinded_salt: uq,
445 pbkdf_params: sigma,
446 };
447 Ok((prs, message))
448 } else {
449 self.lookup_failed_strong(username, blinded, x_pub, rng)
451 }
452 }
453
454 fn lookup_failed<CSPRNG>(
456 &self,
457 username: &[u8],
458 x_pub: RistrettoPoint,
459 rng: &mut CSPRNG,
460 ) -> Result<([u8; 32], ServerMessage<'static, K1>)>
461 where
462 CSPRNG: TryRngCore + TryCryptoRng,
463 {
464 let prs = {
465 let mut tmp = [0u8; 32];
466 rng.try_fill_bytes(&mut tmp).map_err(|_| Error::Rng)?;
467 tmp
468 };
469
470 let mut hasher: D = Default::default();
472 hasher.update(self.secret.0.to_le_bytes());
473 hasher.update(username);
474 let hash = hasher.finalize();
475 let hash_bytes: &[u8] = hash.as_ref();
476
477 let salt = SaltString::encode_b64(&hash_bytes[..48]).map_err(Error::PasswordHashing)?;
481
482 let message = ServerMessage::AugmentationInfo {
483 group: "ristretto255",
484 x_pub,
485 salt,
486 pbkdf_params: ParamsString::default(),
487 };
488
489 Ok((prs, message))
490 }
491
492 #[cfg(feature = "strong_aucpace")]
494 fn lookup_failed_strong<CSPRNG>(
495 &self,
496 username: &[u8],
497 blinded: RistrettoPoint,
498 x_pub: RistrettoPoint,
499 rng: &mut CSPRNG,
500 ) -> Result<([u8; 32], ServerMessage<'static, K1>)>
501 where
502 CSPRNG: TryRngCore + TryCryptoRng,
503 {
504 let prs = {
505 let mut tmp = [0u8; 32];
506 rng.try_fill_bytes(&mut tmp).map_err(|_| Error::Rng)?;
507 tmp
508 };
509
510 let mut hasher: D = Default::default();
512 hasher.update(self.secret.0.to_le_bytes());
513 hasher.update(username);
514 let cofactor = Scalar::ONE;
515 let q = Scalar::from_hash(hasher);
516 let fake_blinded_salt = blinded * (q * cofactor);
517
518 if fake_blinded_salt.is_identity() {
520 return Err(Error::IllegalPointError);
521 }
522
523 let message = ServerMessage::StrongAugmentationInfo {
524 group: "ristretto255",
525 x_pub,
526 blinded_salt: fake_blinded_salt,
527 pbkdf_params: ParamsString::default(),
528 };
529
530 Ok((prs, message))
531 }
532}
533
534#[derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
536pub struct AuCPaceServerCPaceSubstep<D, CSPRNG, const K1: usize>
537where
538 D: Digest<OutputSize = U64> + Default,
539 CSPRNG: TryRngCore + TryCryptoRng,
540{
541 #[zeroize(skip)]
542 ssid: Output<D>,
543 prs: [u8; 32],
544 #[zeroize(skip)]
545 rng: CSPRNG,
546}
547
548impl<D, CSPRNG, const K1: usize> AuCPaceServerCPaceSubstep<D, CSPRNG, K1>
549where
550 D: Digest<OutputSize = U64> + Default,
551 CSPRNG: TryRngCore + TryCryptoRng,
552{
553 const fn new(ssid: Output<D>, prs: [u8; 32], rng: CSPRNG) -> Self {
554 Self { ssid, prs, rng }
555 }
556
557 pub fn generate_public_key<CI: AsRef<[u8]>>(
572 mut self,
573 channel_identifier: CI,
574 ) -> Result<(
575 AuCPaceServerRecvClientKey<D, K1>,
576 ServerMessage<'static, K1>,
577 )> {
578 let (priv_key, pub_key) = generate_keypair::<D, CSPRNG, CI>(
579 &mut self.rng,
580 self.ssid,
581 self.prs,
582 channel_identifier,
583 )?;
584
585 let next_step = AuCPaceServerRecvClientKey::new(self.ssid, priv_key);
586 let message = ServerMessage::PublicKey(pub_key);
587
588 Ok((next_step, message))
589 }
590}
591
592#[derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
594pub struct AuCPaceServerRecvClientKey<D, const K1: usize>
595where
596 D: Digest<OutputSize = U64> + Default,
597{
598 #[zeroize(skip)]
599 ssid: Output<D>,
600 priv_key: Scalar,
601}
602
603impl<D, const K1: usize> AuCPaceServerRecvClientKey<D, K1>
604where
605 D: Digest<OutputSize = U64> + Default,
606{
607 const fn new(ssid: Output<D>, priv_key: Scalar) -> Self {
608 Self { ssid, priv_key }
609 }
610
611 pub fn receive_client_pubkey(
621 self,
622 client_pubkey: RistrettoPoint,
623 ) -> Result<AuCPaceServerExpMutAuth<D, K1>> {
624 if client_pubkey.is_identity() {
626 return Err(Error::IllegalPointError);
627 }
628
629 let sk1 = compute_first_session_key::<D>(self.ssid, self.priv_key, client_pubkey);
630 Ok(AuCPaceServerExpMutAuth::new(self.ssid, sk1))
631 }
632
633 pub fn implicit_auth(
644 self,
645 client_pubkey: RistrettoPoint,
646 ) -> Result<secret_utils::wrappers::SecretKey> {
647 if client_pubkey.is_identity() {
649 return Err(Error::IllegalPointError);
650 }
651
652 let sk1 = compute_first_session_key::<D>(self.ssid, self.priv_key, client_pubkey);
653 Ok(secret_utils::wrappers::SecretKey::from(
654 compute_session_key::<D>(self.ssid, sk1).as_slice().to_vec(),
655 ))
656 }
657}
658
659#[derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
661pub struct AuCPaceServerExpMutAuth<D, const K1: usize>
662where
663 D: Digest<OutputSize = U64> + Default,
664{
665 #[zeroize(skip)]
666 ssid: Output<D>,
667 #[zeroize(skip)]
668 sk1: Output<D>,
669}
670
671impl<D, const K1: usize> AuCPaceServerExpMutAuth<D, K1>
672where
673 D: Digest<OutputSize = U64> + Default,
674{
675 const fn new(ssid: Output<D>, sk1: Output<D>) -> Self {
676 Self { ssid, sk1 }
677 }
678
679 pub fn receive_client_authenticator(
694 self,
695 client_authenticator: [u8; 64],
696 ) -> Result<(
697 secret_utils::wrappers::SecretKey,
698 ServerMessage<'static, K1>,
699 )> {
700 let (ta, tb) = compute_authenticator_messages::<D>(self.ssid, self.sk1);
701 if tb.ct_eq(&client_authenticator).into() {
702 let sk = compute_session_key::<D>(self.ssid, self.sk1);
703 let ta_arr = ta
704 .as_slice()
705 .try_into()
706 .map_err(|_| Error::HashSizeInvalid)?;
707 let message = ServerMessage::Authenticator(ta_arr);
708 Ok((
709 secret_utils::wrappers::SecretKey::from(sk.as_slice().to_vec()),
710 message,
711 ))
712 } else {
713 Err(Error::MutualAuthFail)
714 }
715 }
716}
717
718#[derive(Debug)]
720#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
721pub enum ServerMessage<'a, const K1: usize> {
722 Nonce(#[cfg_attr(feature = "serde", serde(with = "serde_byte_array"))] [u8; K1]),
724
725 AugmentationInfo {
727 group: &'a str,
729
730 x_pub: RistrettoPoint,
732
733 #[cfg_attr(feature = "serde", serde(with = "serde_saltstring"))]
735 salt: SaltString,
736
737 #[cfg_attr(feature = "serde", serde(with = "serde_paramsstring"))]
739 pbkdf_params: ParamsString,
740 },
741
742 #[cfg(feature = "strong_aucpace")]
744 StrongAugmentationInfo {
745 group: &'a str,
747
748 x_pub: RistrettoPoint,
750
751 blinded_salt: RistrettoPoint,
753
754 #[cfg_attr(feature = "serde", serde(with = "serde_paramsstring"))]
756 pbkdf_params: ParamsString,
757 },
758
759 PublicKey(RistrettoPoint),
761
762 Authenticator(#[cfg_attr(feature = "serde", serde(with = "serde_byte_array"))] [u8; 64]),
764}
765
766#[cfg(test)]
767mod tests {
768 #[allow(unused)]
769 use super::*;
770 #[allow(unused)]
771 use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
772
773 #[test]
774 #[cfg(all(feature = "sha2", feature = "getrandom"))]
775 fn test_server_doesnt_accept_insecure_ssid() {
776 use crate::Server;
777 use rand::rngs::OsRng;
778 let mut server = Server::new(OsRng).expect("failed to initialize server RNG");
779 let res = server.begin_prestablished_ssid("bad ssid");
780 assert!(matches!(res, Err(Error::InsecureSsid)));
781 }
782
783 #[test]
784 #[cfg(feature = "sha2")]
785 fn test_server_doesnt_accept_invalid_pubkey() {
786 use crate::utils::H0;
787 use curve25519_dalek::traits::Identity;
788 let ssid = H0::<sha2::Sha512>().finalize();
789 let aug_server: AuCPaceServerRecvClientKey<sha2::Sha512, 16> =
790 AuCPaceServerRecvClientKey::new(ssid, Scalar::from(420u32));
791 let res = aug_server.receive_client_pubkey(RistrettoPoint::identity());
792
793 if let Err(e) = res {
794 assert_eq!(e, Error::IllegalPointError);
795 } else {
796 panic!("Client accepted illegal point.");
797 }
798 }
799
800 #[test]
801 #[cfg(feature = "sha2")]
802 fn test_server_doesnt_accept_invalid_pubkey_implicit_auth() {
803 use crate::utils::H0;
804 use curve25519_dalek::traits::Identity;
805 let ssid = H0::<sha2::Sha512>().finalize();
806 let aug_server: AuCPaceServerRecvClientKey<sha2::Sha512, 16> =
807 AuCPaceServerRecvClientKey::new(ssid, Scalar::from(420u32));
808 let res = aug_server.implicit_auth(RistrettoPoint::identity());
809
810 if let Err(e) = res {
811 assert_eq!(e, Error::IllegalPointError);
812 } else {
813 panic!("Client accepted illegal point.");
814 }
815 }
816
817 #[cfg(all(feature = "sha2", feature = "strong_aucpace"))]
818 struct FakeDatabase();
819
820 #[cfg(all(feature = "sha2", feature = "strong_aucpace"))]
821 impl StrongDatabase for FakeDatabase {
822 type PasswordVerifier = RistrettoPoint;
823 type Exponent = Scalar;
824
825 fn lookup_verifier_strong(
826 &self,
827 _username: &[u8],
828 ) -> Option<(Self::PasswordVerifier, Self::Exponent, ParamsString)> {
829 Some((
830 RISTRETTO_BASEPOINT_POINT,
831 Scalar::ZERO,
832 ParamsString::default(),
833 ))
834 }
835
836 fn store_verifier_strong(
837 &mut self,
838 _username: &[u8],
839 _uad: Option<&[u8]>,
840 _verifier: Self::PasswordVerifier,
841 _secret_exponent: Self::Exponent,
842 _params: ParamsString,
843 ) {
844 unimplemented!()
845 }
846 }
847
848 #[cfg(all(feature = "sha2", feature = "strong_aucpace"))]
849 impl PartialAugDatabase for FakeDatabase {
850 type PrivateKey = Scalar;
851 type PublicKey = RistrettoPoint;
852
853 fn lookup_long_term_keypair(
854 &self,
855 _username: &[u8],
856 ) -> Option<(Self::PrivateKey, Self::PublicKey)> {
857 Some((Scalar::ZERO, RISTRETTO_BASEPOINT_POINT))
858 }
859
860 fn store_long_term_keypair(
861 &mut self,
862 _username: &[u8],
863 _priv_key: Self::PrivateKey,
864 _pub_key: Self::PublicKey,
865 ) -> Result<()> {
866 unimplemented!()
867 }
868 }
869
870 #[test]
871 #[cfg(all(feature = "sha2", feature = "getrandom", feature = "strong_aucpace"))]
872 fn test_server_doesnt_accept_invalid_uq() {
873 use crate::utils::H0;
874 use curve25519_dalek::traits::Identity;
875 use rand::rngs::OsRng;
876
877 let ssid = H0::<sha2::Sha512>().finalize();
878 let aug_server: AuCPaceServerAugLayer<sha2::Sha512, 16> =
879 AuCPaceServerAugLayer::new(ServerSecret(25519), ssid);
880 let res = aug_server.generate_client_info_strong(
881 b"bobbyyyy",
882 RistrettoPoint::identity(),
883 &FakeDatabase(),
884 OsRng,
885 );
886
887 if let Err(e) = res {
888 assert_eq!(e, Error::IllegalPointError);
889 } else {
890 panic!("Client accepted illegal point.");
891 }
892 }
893
894 #[test]
895 #[cfg(all(feature = "sha2", feature = "getrandom", feature = "strong_aucpace"))]
896 fn test_server_doesnt_accept_invalid_uq_partial() {
897 use crate::utils::H0;
898 use curve25519_dalek::traits::Identity;
899 use rand::rngs::OsRng;
900
901 let ssid = H0::<sha2::Sha512>().finalize();
902 let aug_server: AuCPaceServerAugLayer<sha2::Sha512, 16> =
903 AuCPaceServerAugLayer::new(ServerSecret(25519), ssid);
904 let res = aug_server.generate_client_info_partial_strong(
905 b"bobbyyyy",
906 RistrettoPoint::identity(),
907 &FakeDatabase(),
908 OsRng,
909 );
910
911 if let Err(e) = res {
912 assert_eq!(e, Error::IllegalPointError);
913 } else {
914 panic!("Client accepted illegal point.");
915 }
916 }
917
918 #[test]
919 #[cfg(all(feature = "sha2", feature = "getrandom"))]
920 fn test_server_lookup_failed_returns_ok() {
921 use crate::utils::H0;
922 use rand::rngs::OsRng;
923
924 struct NoneDb;
926 impl Database for NoneDb {
927 type PasswordVerifier = RistrettoPoint;
928
929 fn lookup_verifier(
930 &self,
931 _username: &[u8],
932 ) -> Option<(Self::PasswordVerifier, SaltString, ParamsString)> {
933 None
934 }
935
936 fn store_verifier(
937 &mut self,
938 _username: &[u8],
939 _salt: SaltString,
940 _uad: Option<&[u8]>,
941 _verifier: Self::PasswordVerifier,
942 _params: ParamsString,
943 ) {
944 unimplemented!()
945 }
946 }
947
948 let ssid = H0::<sha2::Sha512>().finalize();
949 let aug_server: AuCPaceServerAugLayer<sha2::Sha512, 16> =
950 AuCPaceServerAugLayer::new(ServerSecret(25519), ssid);
951
952 let res = aug_server.generate_client_info(b"missing-user", &NoneDb, OsRng);
954
955 assert!(res.is_ok());
956 if let Ok((_next_step, ServerMessage::AugmentationInfo { .. })) = res {
957 } else {
959 panic!("Expected AugmentationInfo on lookup_failed path");
960 }
961 }
962}