1use crate::{
2 errors::{Error, Result},
3 utils::{
4 compute_authenticator_messages, compute_first_session_key, compute_session_key,
5 compute_ssid, generate_keypair, generate_nonce, scalar_from_hash, H0,
6 },
7};
8
9use crate::constants::MIN_SSID_LEN;
10use core::marker::PhantomData;
11use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
12use curve25519_dalek::traits::IsIdentity;
13use curve25519_dalek::{
14 digest::consts::U64,
15 digest::{Digest, Output},
16 ristretto::RistrettoPoint,
17 scalar::Scalar,
18};
19use password_hash::{ParamsString, PasswordHash, PasswordHasher, Salt, SaltString};
20use rand_core::CryptoRngCore;
21use subtle::ConstantTimeEq;
22
23#[cfg(feature = "strong_aucpace")]
24use crate::utils::H1;
25
26#[cfg(feature = "alloc")]
27extern crate alloc;
28
29#[cfg(feature = "serde")]
30use crate::utils::{serde_paramsstring, serde_saltstring};
31
32#[cfg(feature = "serde")]
33use serde::{Deserialize, Serialize};
34
35pub struct AuCPaceClient<D, H, CSPRNG, const K1: usize>
37where
38 D: Digest<OutputSize = U64> + Default,
39 H: PasswordHasher,
40 CSPRNG: CryptoRngCore,
41{
42 rng: CSPRNG,
43 d: PhantomData<D>,
44 h: PhantomData<H>,
45}
46
47impl<D, H, CSPRNG, const K1: usize> AuCPaceClient<D, H, CSPRNG, K1>
48where
49 D: Digest<OutputSize = U64> + Default,
50 H: PasswordHasher,
51 CSPRNG: CryptoRngCore,
52{
53 pub fn new(rng: CSPRNG) -> Self {
55 Self {
56 rng,
57 d: Default::default(),
58 h: Default::default(),
59 }
60 }
61
62 pub fn begin(&mut self) -> (AuCPaceClientSsidEstablish<D, H, K1>, ClientMessage<'_, K1>) {
70 let next_step = AuCPaceClientSsidEstablish::new(&mut self.rng);
71 let message = ClientMessage::Nonce(next_step.nonce);
72
73 (next_step, message)
74 }
75
76 pub fn begin_prestablished_ssid<S>(&mut self, ssid: S) -> Result<AuCPaceClientPreAug<D, H, K1>>
86 where
87 S: AsRef<[u8]>,
88 {
89 if ssid.as_ref().len() < MIN_SSID_LEN {
91 return Err(Error::InsecureSsid);
92 }
93
94 let mut hasher: D = H0();
96 hasher.update(ssid);
97 let ssid_hash = hasher.finalize();
98 let next_step = AuCPaceClientPreAug::new(ssid_hash);
99 Ok(next_step)
100 }
101
102 pub fn register<'a, P, const BUFSIZ: usize>(
121 &mut self,
122 username: &'a [u8],
123 password: P,
124 params: H::Params,
125 hasher: H,
126 ) -> Result<ClientMessage<'a, K1>>
127 where
128 P: AsRef<[u8]>,
129 {
130 let salt_string = SaltString::generate(&mut self.rng);
131
132 let pw_hash = hash_password::<&[u8], P, &SaltString, H, BUFSIZ>(
134 username,
135 password,
136 &salt_string,
137 params.clone(),
138 hasher,
139 )?;
140
141 let cofactor = Scalar::ONE;
142 let w = scalar_from_hash(pw_hash)?;
143 let verifier = RISTRETTO_BASEPOINT_POINT * (w * cofactor);
144
145 let params_string = params.try_into().map_err(Error::PasswordHashing)?;
147
148 Ok(ClientMessage::Registration {
149 username,
150 salt: salt_string,
151 params: params_string,
152 verifier,
153 })
154 }
155
156 #[cfg(feature = "strong_aucpace")]
175 pub fn register_strong<'a, P, const BUFSIZ: usize>(
176 &mut self,
177 username: &'a [u8],
178 password: P,
179 params: H::Params,
180 hasher: H,
181 ) -> Result<ClientMessage<'a, K1>>
182 where
183 P: AsRef<[u8]>,
184 {
185 let (q, salt_string) =
187 Self::generate_salt_strong(username, password.as_ref(), &mut self.rng)?;
188
189 let pw_hash = hash_password::<&[u8], P, &SaltString, H, BUFSIZ>(
191 username,
192 password,
193 &salt_string,
194 params.clone(),
195 hasher,
196 )?;
197 let cofactor = Scalar::ONE;
198 let w = scalar_from_hash(pw_hash)?;
199 let verifier = RISTRETTO_BASEPOINT_POINT * (w * cofactor);
200
201 let params_string = params.try_into().map_err(Error::PasswordHashing)?;
203
204 Ok(ClientMessage::StrongRegistration {
205 username,
206 secret_exponent: q,
207 params: params_string,
208 verifier,
209 })
210 }
211
212 #[cfg(feature = "alloc")]
228 pub fn register_alloc<'a, P>(
229 &mut self,
230 username: &'a [u8],
231 password: P,
232 params: H::Params,
233 hasher: H,
234 ) -> Result<ClientMessage<'a, K1>>
235 where
236 P: AsRef<[u8]>,
237 {
238 let salt_string = SaltString::generate(&mut self.rng);
240
241 let pw_hash =
243 hash_password_alloc(username, password, &salt_string, params.clone(), hasher)?;
244 let cofactor = Scalar::ONE;
245 let w = scalar_from_hash(pw_hash)?;
246 let verifier = RISTRETTO_BASEPOINT_POINT * (w * cofactor);
247
248 let params_string = params.try_into().map_err(Error::PasswordHashing)?;
250
251 Ok(ClientMessage::Registration {
252 username,
253 salt: salt_string,
254 params: params_string,
255 verifier,
256 })
257 }
258
259 #[cfg(all(feature = "strong_aucpace", feature = "alloc"))]
280 pub fn register_alloc_strong<'a, P>(
281 &mut self,
282 username: &'a [u8],
283 password: P,
284 params: H::Params,
285 hasher: H,
286 ) -> Result<ClientMessage<'a, K1>>
287 where
288 P: AsRef<[u8]>,
289 {
290 let (q, salt_string) =
292 Self::generate_salt_strong(username, password.as_ref(), &mut self.rng)?;
293
294 let pw_hash = hash_password_alloc(
296 username,
297 password,
298 salt_string.as_salt(),
299 params.clone(),
300 hasher,
301 )?;
302 let cofactor = Scalar::ONE;
303 let w = scalar_from_hash(pw_hash)?;
304 let verifier = RISTRETTO_BASEPOINT_POINT * (w * cofactor);
305
306 let params_string = params.try_into().map_err(Error::PasswordHashing)?;
308
309 Ok(ClientMessage::StrongRegistration {
310 username,
311 secret_exponent: q,
312 params: params_string,
313 verifier,
314 })
315 }
316
317 #[cfg(feature = "strong_aucpace")]
319 fn generate_salt_strong(
320 user: &[u8],
321 pass: &[u8],
322 rng: &mut CSPRNG,
323 ) -> Result<(Scalar, SaltString)> {
324 let q = Scalar::random(rng);
326
327 let mut hasher: D = H1();
329 hasher.update(user);
330 hasher.update(pass);
331 let z = RistrettoPoint::from_hash(hasher);
332
333 let cofactor = Scalar::ONE;
335 let salt_point = z * (q * cofactor);
336 let salt = salt_point.compress().to_bytes();
337 let salt_string = SaltString::encode_b64(&salt).map_err(Error::PasswordHashing)?;
338
339 Ok((q, salt_string))
340 }
341}
342
343pub struct AuCPaceClientSsidEstablish<D, H, const K1: usize>
345where
346 D: Digest<OutputSize = U64> + Default,
347 H: PasswordHasher,
348{
349 nonce: [u8; K1],
350 d: PhantomData<D>,
351 h: PhantomData<H>,
352}
353
354impl<D, H, const K1: usize> AuCPaceClientSsidEstablish<D, H, K1>
355where
356 D: Digest<OutputSize = U64> + Default,
357 H: PasswordHasher,
358{
359 fn new<CSPRNG>(rng: &mut CSPRNG) -> Self
360 where
361 CSPRNG: CryptoRngCore,
362 {
363 Self {
364 nonce: generate_nonce(rng),
365 d: Default::default(),
366 h: Default::default(),
367 }
368 }
369
370 pub fn agree_ssid(self, server_nonce: [u8; K1]) -> AuCPaceClientPreAug<D, H, K1> {
379 let ssid = compute_ssid::<D, K1>(server_nonce, self.nonce);
380 AuCPaceClientPreAug::new(ssid)
381 }
382}
383
384pub struct AuCPaceClientPreAug<D, H, const K1: usize>
386where
387 D: Digest<OutputSize = U64> + Default,
388 H: PasswordHasher,
389{
390 ssid: Output<D>,
391 h: PhantomData<H>,
392}
393
394impl<D, H, const K1: usize> AuCPaceClientPreAug<D, H, K1>
395where
396 D: Digest<OutputSize = U64> + Default,
397 H: PasswordHasher,
398{
399 fn new(ssid: Output<D>) -> Self {
400 Self {
401 ssid,
402 h: Default::default(),
403 }
404 }
405
406 pub fn start_augmentation<'a>(
417 self,
418 username: &'a [u8],
419 password: &'a [u8],
420 ) -> (AuCPaceClientAugLayer<'a, D, H, K1>, ClientMessage<'a, K1>) {
421 let next_step = AuCPaceClientAugLayer::new(self.ssid, username, password);
422 let message = ClientMessage::Username(username);
423
424 (next_step, message)
425 }
426
427 #[cfg(feature = "strong_aucpace")]
439 pub fn start_augmentation_strong<'a, CSPRNG>(
440 self,
441 username: &'a [u8],
442 password: &'a [u8],
443 rng: &mut CSPRNG,
444 ) -> (
445 StrongAuCPaceClientAugLayer<'a, D, H, K1>,
446 ClientMessage<'a, K1>,
447 )
448 where
449 CSPRNG: CryptoRngCore,
450 {
451 let blinding_value = loop {
454 let val = Scalar::random(rng);
455 if val != Scalar::ZERO {
456 break val;
457 }
458 };
459 let mut hasher: D = H1();
460 hasher.update(username);
461 hasher.update(password);
462 let z = RistrettoPoint::from_hash(hasher);
463 let cofactor = Scalar::ONE;
464 let blinded = z * (blinding_value * cofactor);
465
466 let next_step =
467 StrongAuCPaceClientAugLayer::new(self.ssid, username, password, blinding_value);
468 let message = ClientMessage::StrongUsername { username, blinded };
469
470 (next_step, message)
471 }
472}
473
474pub struct AuCPaceClientAugLayer<'a, D, H, const K1: usize>
476where
477 D: Digest<OutputSize = U64> + Default,
478 H: PasswordHasher,
479{
480 ssid: Output<D>,
481 username: &'a [u8],
482 password: &'a [u8],
483 h: PhantomData<H>,
484}
485
486impl<'a, D, H, const K1: usize> AuCPaceClientAugLayer<'a, D, H, K1>
487where
488 D: Digest<OutputSize = U64> + Default,
489 H: PasswordHasher,
490{
491 fn new(ssid: Output<D>, username: &'a [u8], password: &'a [u8]) -> Self {
492 Self {
493 ssid,
494 username,
495 password,
496 h: Default::default(),
497 }
498 }
499
500 pub fn generate_cpace<'salt, S, const BUFSIZ: usize>(
523 self,
524 x_pub: RistrettoPoint,
525 salt: S,
526 params: H::Params,
527 hasher: H,
528 ) -> Result<AuCPaceClientCPaceSubstep<D, K1>>
529 where
530 S: Into<Salt<'salt>>,
531 {
532 if x_pub.is_identity() {
534 return Err(Error::IllegalPointError);
535 }
536
537 let cofactor = Scalar::ONE;
538 let pw_hash = hash_password::<&[u8], &[u8], S, H, BUFSIZ>(
539 self.username,
540 self.password,
541 salt,
542 params,
543 hasher,
544 )?;
545 let w = scalar_from_hash(pw_hash)?;
546
547 let prs = (x_pub * (w * cofactor)).compress().to_bytes();
548
549 Ok(AuCPaceClientCPaceSubstep::new(self.ssid, prs))
550 }
551
552 #[cfg(feature = "alloc")]
570 pub fn generate_cpace_alloc<'salt, S>(
571 self,
572 x_pub: RistrettoPoint,
573 salt: S,
574 params: H::Params,
575 hasher: H,
576 ) -> Result<AuCPaceClientCPaceSubstep<D, K1>>
577 where
578 S: Into<Salt<'a>>,
579 {
580 if x_pub.is_identity() {
582 return Err(Error::IllegalPointError);
583 }
584
585 let cofactor = Scalar::ONE;
586 let pw_hash = hash_password_alloc(self.username, self.password, salt, params, hasher)?;
587 let w = scalar_from_hash(pw_hash)?;
588
589 let prs = (x_pub * (w * cofactor)).compress().to_bytes();
590
591 Ok(AuCPaceClientCPaceSubstep::new(self.ssid, prs))
592 }
593}
594
595#[cfg(feature = "strong_aucpace")]
597pub struct StrongAuCPaceClientAugLayer<'a, D, H, const K1: usize>
598where
599 D: Digest<OutputSize = U64> + Default,
600 H: PasswordHasher,
601{
602 ssid: Output<D>,
603 username: &'a [u8],
604 password: &'a [u8],
605 blinding_value: Scalar,
606 h: PhantomData<H>,
607}
608
609#[cfg(feature = "strong_aucpace")]
610impl<'a, D, H, const K1: usize> StrongAuCPaceClientAugLayer<'a, D, H, K1>
611where
612 D: Digest<OutputSize = U64> + Default,
613 H: PasswordHasher,
614{
615 fn new(
616 ssid: Output<D>,
617 username: &'a [u8],
618 password: &'a [u8],
619 blinding_value: Scalar,
620 ) -> Self {
621 Self {
622 ssid,
623 username,
624 password,
625 blinding_value,
626 h: Default::default(),
627 }
628 }
629
630 pub fn generate_cpace<const BUFSIZ: usize>(
653 self,
654 x_pub: RistrettoPoint,
655 blinded_salt: RistrettoPoint,
656 params: H::Params,
657 hasher: H,
658 ) -> Result<AuCPaceClientCPaceSubstep<D, K1>> {
659 if x_pub.is_identity() {
661 return Err(Error::IllegalPointError);
662 }
663
664 let cofactor = Scalar::ONE;
666
667 let exponent = (self.blinding_value * cofactor * cofactor).invert() * cofactor;
671 let salt = (blinded_salt * exponent).compress().to_bytes();
672 let salt_string = SaltString::encode_b64(&salt).map_err(Error::PasswordHashing)?;
673
674 let pw_hash = hash_password::<&[u8], &[u8], &SaltString, H, BUFSIZ>(
676 self.username,
677 self.password,
678 &salt_string,
679 params,
680 hasher,
681 )?;
682 let w = scalar_from_hash(pw_hash)?;
683 let prs = (x_pub * (w * cofactor)).compress().to_bytes();
684
685 Ok(AuCPaceClientCPaceSubstep::new(self.ssid, prs))
686 }
687
688 #[cfg(feature = "alloc")]
707 pub fn generate_cpace_alloc(
708 self,
709 x_pub: RistrettoPoint,
710 blinded_salt: RistrettoPoint,
711 params: H::Params,
712 hasher: H,
713 ) -> Result<AuCPaceClientCPaceSubstep<D, K1>> {
714 if x_pub.is_identity() {
716 return Err(Error::IllegalPointError);
717 }
718
719 let cofactor = Scalar::ONE;
721
722 let exponent = (self.blinding_value * cofactor * cofactor).invert() * cofactor;
726
727 let salt_point = blinded_salt * exponent;
729 if salt_point.is_identity() {
730 return Err(Error::IllegalPointError);
731 }
732 let salt = salt_point.compress().to_bytes();
733 let salt_string = SaltString::encode_b64(&salt).map_err(Error::PasswordHashing)?;
734
735 let pw_hash = hash_password_alloc(
737 self.username,
738 self.password,
739 salt_string.as_salt(),
740 params,
741 hasher,
742 )?;
743 let w = scalar_from_hash(pw_hash)?;
744 let prs = (x_pub * (w * cofactor)).compress().to_bytes();
745
746 Ok(AuCPaceClientCPaceSubstep::new(self.ssid, prs))
747 }
748}
749
750pub struct AuCPaceClientCPaceSubstep<D, const K1: usize>
752where
753 D: Digest<OutputSize = U64> + Default,
754{
755 ssid: Output<D>,
756 prs: [u8; 32],
757}
758
759impl<D, const K1: usize> AuCPaceClientCPaceSubstep<D, K1>
760where
761 D: Digest<OutputSize = U64> + Default,
762{
763 fn new(ssid: Output<D>, prs: [u8; 32]) -> Self {
764 Self { ssid, prs }
765 }
766
767 pub fn generate_public_key<CI, CSPRNG>(
782 self,
783 channel_identifier: CI,
784 rng: &mut CSPRNG,
785 ) -> (
786 AuCPaceClientRecvServerKey<D, K1>,
787 ClientMessage<'static, K1>,
788 )
789 where
790 CI: AsRef<[u8]>,
791 CSPRNG: CryptoRngCore,
792 {
793 let (priv_key, pub_key) =
794 generate_keypair::<D, CSPRNG, CI>(rng, self.ssid, self.prs, channel_identifier);
795
796 let next_step = AuCPaceClientRecvServerKey::new(self.ssid, priv_key);
797 let message = ClientMessage::PublicKey(pub_key);
798
799 (next_step, message)
800 }
801}
802
803pub struct AuCPaceClientRecvServerKey<D, const K1: usize>
805where
806 D: Digest<OutputSize = U64> + Default,
807{
808 ssid: Output<D>,
809 priv_key: Scalar,
810}
811
812impl<D, const K1: usize> AuCPaceClientRecvServerKey<D, K1>
813where
814 D: Digest<OutputSize = U64> + Default,
815{
816 fn new(ssid: Output<D>, priv_key: Scalar) -> Self {
817 Self { ssid, priv_key }
818 }
819
820 pub fn receive_server_pubkey(
832 self,
833 server_pubkey: RistrettoPoint,
834 ) -> Result<(AuCPaceClientExpMutAuth<D, K1>, ClientMessage<'static, K1>)> {
835 if server_pubkey.is_identity() {
836 return Err(Error::IllegalPointError);
837 }
838
839 let sk1 = compute_first_session_key::<D>(self.ssid, self.priv_key, server_pubkey);
840 let (ta, tb) = compute_authenticator_messages::<D>(self.ssid, sk1);
841 let next_step = AuCPaceClientExpMutAuth::new(self.ssid, sk1, ta);
842 let message = ClientMessage::Authenticator(
843 tb.as_slice()
844 .try_into()
845 .expect("array length invariant broken"),
846 );
847 Ok((next_step, message))
848 }
849
850 pub fn implicit_auth(self, server_pubkey: RistrettoPoint) -> Result<Output<D>> {
861 if server_pubkey.is_identity() {
862 return Err(Error::IllegalPointError);
863 }
864
865 let sk1 = compute_first_session_key::<D>(self.ssid, self.priv_key, server_pubkey);
866 Ok(compute_session_key::<D>(self.ssid, sk1))
867 }
868}
869
870pub struct AuCPaceClientExpMutAuth<D, const K1: usize>
872where
873 D: Digest<OutputSize = U64> + Default,
874{
875 ssid: Output<D>,
876 sk1: Output<D>,
877 server_authenticator: Output<D>,
878}
879
880impl<D, const K1: usize> AuCPaceClientExpMutAuth<D, K1>
881where
882 D: Digest<OutputSize = U64> + Default,
883{
884 fn new(ssid: Output<D>, sk1: Output<D>, server_authenticator: Output<D>) -> Self {
885 Self {
886 ssid,
887 sk1,
888 server_authenticator,
889 }
890 }
891
892 pub fn receive_server_authenticator(self, server_authenticator: [u8; 64]) -> Result<Output<D>> {
905 if self
906 .server_authenticator
907 .ct_eq(&server_authenticator)
908 .into()
909 {
910 Ok(compute_session_key::<D>(self.ssid, self.sk1))
911 } else {
912 Err(Error::MutualAuthFail)
913 }
914 }
915}
916
917fn hash_password<'a, U, P, S, H, const BUFSIZ: usize>(
919 username: U,
920 password: P,
921 salt: S,
922 params: H::Params,
923 hasher: H,
924) -> Result<PasswordHash<'a>>
925where
926 H: PasswordHasher,
927 U: AsRef<[u8]>,
928 P: AsRef<[u8]>,
929 S: Into<Salt<'a>>,
930{
931 let user = username.as_ref();
932 let pass = password.as_ref();
933 let u = user.len();
934 let p = pass.len();
935
936 if u + p + 1 > BUFSIZ {
937 return Err(Error::UsernameOrPasswordTooLong);
938 }
939
940 let mut buf = [0u8; BUFSIZ];
941 buf[0..u].copy_from_slice(user);
942 buf[u] = b':';
943 buf[u + 1..u + p + 1].copy_from_slice(pass);
944
945 let hash = hasher
946 .hash_password_customized(&buf[0..u + p + 1], None, None, params, salt)
947 .map_err(Error::PasswordHashing);
948
949 hash
950}
951
952#[cfg(feature = "alloc")]
954fn hash_password_alloc<'a, U, P, S, H>(
955 username: U,
956 password: P,
957 salt: S,
958 params: H::Params,
959 hasher: H,
960) -> Result<PasswordHash<'a>>
961where
962 H: PasswordHasher,
963 U: AsRef<[u8]>,
964 P: AsRef<[u8]>,
965 S: Into<Salt<'a>>,
966{
967 let user = username.as_ref();
968 let pass = password.as_ref();
969
970 let mut v = alloc::vec::Vec::with_capacity(user.len() + pass.len() + 1);
972 v.extend_from_slice(user);
973 v.push(b':');
974 v.extend_from_slice(pass);
975
976 hasher
977 .hash_password_customized(v.as_slice(), None, None, params, salt)
978 .map_err(Error::PasswordHashing)
979}
980
981#[derive(Debug)]
983#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
984pub enum ClientMessage<'a, const K1: usize> {
985 Nonce(#[cfg_attr(feature = "serde", serde(with = "serde_byte_array"))] [u8; K1]),
987
988 Username(&'a [u8]),
990
991 #[cfg(feature = "strong_aucpace")]
994 StrongUsername {
995 username: &'a [u8],
997 blinded: RistrettoPoint,
999 },
1000
1001 PublicKey(RistrettoPoint),
1003
1004 Authenticator(#[cfg_attr(feature = "serde", serde(with = "serde_byte_array"))] [u8; 64]),
1006
1007 Registration {
1010 username: &'a [u8],
1012
1013 #[cfg_attr(feature = "serde", serde(with = "serde_saltstring"))]
1015 salt: SaltString,
1016
1017 #[cfg_attr(feature = "serde", serde(with = "serde_paramsstring"))]
1019 params: ParamsString,
1020
1021 verifier: RistrettoPoint,
1023 },
1024
1025 #[cfg(feature = "strong_aucpace")]
1028 StrongRegistration {
1029 username: &'a [u8],
1031
1032 secret_exponent: Scalar,
1034
1035 #[cfg_attr(feature = "serde", serde(with = "serde_paramsstring"))]
1037 params: ParamsString,
1038
1039 verifier: RistrettoPoint,
1041 },
1042}
1043
1044#[cfg(test)]
1045mod tests {
1046 #[allow(unused)]
1047 use super::*;
1048
1049 #[test]
1050 #[cfg(all(feature = "alloc", feature = "getrandom", feature = "scrypt"))]
1051 fn test_hash_password_no_std_and_alloc_agree() {
1052 use rand_core::{OsRng, RngCore};
1053 use scrypt::{Params, Scrypt};
1054
1055 let username = "worf@starship.enterprise";
1056 let password = "data_x_worf_4ever_<3";
1057 let mut bytes = [0u8; Salt::RECOMMENDED_LENGTH];
1058 OsRng.fill_bytes(&mut bytes);
1059 let salt = SaltString::encode_b64(&bytes).expect("Salt length invariant broken.");
1060 let params: Params = Default::default();
1063
1064 let no_std_res = hash_password::<&str, &str, &SaltString, Scrypt, 100>(
1065 username, password, &salt, params, Scrypt,
1066 )
1067 .unwrap();
1068 let alloc_res = hash_password_alloc(username, password, &salt, params, Scrypt).unwrap();
1069
1070 assert_eq!(alloc_res, no_std_res);
1071 }
1072
1073 #[test]
1074 #[cfg(all(feature = "getrandom", feature = "sha2"))]
1075 fn test_client_doesnt_accept_insecure_ssid() {
1076 use crate::Client;
1077 use rand_core::OsRng;
1078
1079 let mut client = Client::new(OsRng);
1080 let res = client.begin_prestablished_ssid("bad ssid");
1081 assert!(matches!(res, Err(Error::InsecureSsid)));
1082 }
1083
1084 #[test]
1085 #[cfg(all(feature = "sha2", feature = "scrypt"))]
1086 fn test_client_doesnt_accept_invalid_x_pub() {
1087 use crate::utils::H0;
1088 use curve25519_dalek::traits::Identity;
1089 let ssid = H0::<sha2::Sha512>().finalize();
1090 let aug_client: AuCPaceClientAugLayer<'_, sha2::Sha512, scrypt::Scrypt, 16> =
1091 AuCPaceClientAugLayer::new(
1092 ssid,
1093 b"bob",
1094 b"bob's very secure password that nobody knows about, honest",
1095 );
1096 let res = aug_client.generate_cpace::<'_, &SaltString, 100>(
1097 RistrettoPoint::identity(),
1098 &SaltString::encode_b64(b"saltyboi").unwrap(),
1099 scrypt::Params::recommended(),
1100 scrypt::Scrypt,
1101 );
1102
1103 if let Err(e) = res {
1104 assert_eq!(e, Error::IllegalPointError);
1105 } else {
1106 panic!("Client accepted illegal point.");
1107 }
1108 }
1109
1110 #[test]
1111 #[cfg(all(feature = "sha2", feature = "scrypt", feature = "alloc"))]
1112 fn test_alloc_client_doesnt_accept_invalid_x_pub() {
1113 use crate::utils::H0;
1114 use curve25519_dalek::traits::Identity;
1115 let ssid = H0::<sha2::Sha512>().finalize();
1116 let aug_client: AuCPaceClientAugLayer<'_, sha2::Sha512, scrypt::Scrypt, 16> =
1117 AuCPaceClientAugLayer::new(
1118 ssid,
1119 b"bob",
1120 b"bob's very secure password that nobody knows about, honest",
1121 );
1122 let res = aug_client.generate_cpace_alloc(
1123 RistrettoPoint::identity(),
1124 &SaltString::encode_b64(b"saltyboi").unwrap(),
1125 scrypt::Params::recommended(),
1126 scrypt::Scrypt,
1127 );
1128
1129 if let Err(e) = res {
1130 assert_eq!(e, Error::IllegalPointError);
1131 } else {
1132 panic!("Client accepted illegal point.");
1133 }
1134 }
1135
1136 #[test]
1137 #[cfg(all(feature = "sha2", feature = "scrypt", feature = "strong_aucpace"))]
1138 fn test_strong_client_doesnt_accept_invalid_x_pub() {
1139 use crate::utils::H0;
1140 use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
1141 use curve25519_dalek::traits::Identity;
1142
1143 let ssid = H0::<sha2::Sha512>().finalize();
1144 let aug_client: StrongAuCPaceClientAugLayer<'_, sha2::Sha512, scrypt::Scrypt, 16> =
1145 StrongAuCPaceClientAugLayer::new(
1146 ssid,
1147 b"bob",
1148 b"bob's very secure password that nobody knows about, honest",
1149 Scalar::from(69u32),
1150 );
1151 let res = aug_client.generate_cpace::<100>(
1152 RistrettoPoint::identity(),
1153 RISTRETTO_BASEPOINT_POINT,
1154 scrypt::Params::recommended(),
1155 scrypt::Scrypt,
1156 );
1157
1158 if let Err(e) = res {
1159 assert_eq!(e, Error::IllegalPointError);
1160 } else {
1161 panic!("Client accepted illegal point.");
1162 }
1163 }
1164
1165 #[test]
1166 #[cfg(all(
1167 feature = "sha2",
1168 feature = "scrypt",
1169 feature = "alloc",
1170 feature = "strong_aucpace"
1171 ))]
1172 fn test_strong_alloc_client_doesnt_accept_invalid_x_pub() {
1173 use crate::utils::H0;
1174 use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
1175 use curve25519_dalek::traits::Identity;
1176
1177 let ssid = H0::<sha2::Sha512>().finalize();
1178 let aug_client: StrongAuCPaceClientAugLayer<'_, sha2::Sha512, scrypt::Scrypt, 16> =
1179 StrongAuCPaceClientAugLayer::new(
1180 ssid,
1181 b"bob",
1182 b"bob's very secure password that nobody knows about, honest",
1183 Scalar::from(69u32),
1184 );
1185 let res = aug_client.generate_cpace_alloc(
1186 RistrettoPoint::identity(),
1187 RISTRETTO_BASEPOINT_POINT,
1188 scrypt::Params::recommended(),
1189 scrypt::Scrypt,
1190 );
1191
1192 if let Err(e) = res {
1193 assert_eq!(e, Error::IllegalPointError);
1194 } else {
1195 panic!("Client accepted illegal point.");
1196 }
1197 }
1198
1199 #[test]
1200 #[cfg(all(feature = "sha2", feature = "scrypt"))]
1201 fn test_client_doesnt_accept_invalid_pubkey() {
1202 use crate::utils::H0;
1203 use curve25519_dalek::traits::Identity;
1204 let ssid = H0::<sha2::Sha512>().finalize();
1205 let aug_client: AuCPaceClientRecvServerKey<sha2::Sha512, 16> =
1206 AuCPaceClientRecvServerKey::new(ssid, Scalar::from(420u32));
1207 let res = aug_client.receive_server_pubkey(RistrettoPoint::identity());
1208
1209 if let Err(e) = res {
1210 assert_eq!(e, Error::IllegalPointError);
1211 } else {
1212 panic!("Client accepted illegal point.");
1213 }
1214 }
1215
1216 #[test]
1217 #[cfg(all(feature = "sha2", feature = "scrypt"))]
1218 fn test_client_doesnt_accept_invalid_pubkey_implicit_auth() {
1219 use crate::utils::H0;
1220 use curve25519_dalek::traits::Identity;
1221 let ssid = H0::<sha2::Sha512>().finalize();
1222 let aug_client: AuCPaceClientRecvServerKey<sha2::Sha512, 16> =
1223 AuCPaceClientRecvServerKey::new(ssid, Scalar::from(420u32));
1224 let res = aug_client.implicit_auth(RistrettoPoint::identity());
1225
1226 if let Err(e) = res {
1227 assert_eq!(e, Error::IllegalPointError);
1228 } else {
1229 panic!("Client accepted illegal point.");
1230 }
1231 }
1232
1233 #[test]
1234 #[cfg(all(
1235 feature = "sha2",
1236 feature = "scrypt",
1237 feature = "alloc",
1238 feature = "strong_aucpace"
1239 ))]
1240 fn test_strong_alloc_client_doesnt_accept_invalid_salt() {
1241 use crate::utils::H0;
1242 use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
1243 use curve25519_dalek::traits::Identity;
1244
1245 let ssid = H0::<sha2::Sha512>().finalize();
1246 let aug_client: StrongAuCPaceClientAugLayer<'_, sha2::Sha512, scrypt::Scrypt, 16> =
1247 StrongAuCPaceClientAugLayer::new(
1248 ssid,
1249 b"bob",
1250 b"bob's very secure password that nobody knows about, honest",
1251 Scalar::from(69u32),
1252 );
1253 let res = aug_client.generate_cpace_alloc(
1254 RISTRETTO_BASEPOINT_POINT,
1255 RistrettoPoint::identity(),
1256 scrypt::Params::recommended(),
1257 scrypt::Scrypt,
1258 );
1259
1260 if let Err(e) = res {
1261 assert_eq!(e, Error::IllegalPointError);
1262 } else {
1263 panic!("Client accepted illegal point.");
1264 }
1265 }
1266}