@@ -19,17 +19,23 @@ use crate::{Fido2Credential, Field, Login, LoginUri};
19
19
/// Prefix that indicates the URL is an Android app scheme.
20
20
const ANDROID_APP_SCHEME : & str = "androidapp://" ;
21
21
22
- /// Convert CXF TotpCredential to Bitwarden's Totp struct
23
- /// This ensures we use the exact same encoding and formatting as Bitwarden's core implementation
24
- fn totp_credential_to_totp ( cxf_totp : & TotpCredential ) -> Totp {
25
- let algorithm = match cxf_totp . algorithm {
22
+ /// Convert CXF OTPHashAlgorithm to Bitwarden's TotpAlgorithm
23
+ /// Handles standard algorithms and special cases like Steam
24
+ fn convert_otp_algorithm ( algorithm : & OTPHashAlgorithm ) -> TotpAlgorithm {
25
+ match algorithm {
26
26
OTPHashAlgorithm :: Sha1 => TotpAlgorithm :: Sha1 ,
27
27
OTPHashAlgorithm :: Sha256 => TotpAlgorithm :: Sha256 ,
28
28
OTPHashAlgorithm :: Sha512 => TotpAlgorithm :: Sha512 ,
29
29
OTPHashAlgorithm :: Unknown ( ref algo) if algo == "steam" => TotpAlgorithm :: Steam ,
30
30
OTPHashAlgorithm :: Unknown ( _) | _ => TotpAlgorithm :: Sha1 , /* Default to SHA1 for unknown
31
31
* algorithms */
32
- } ;
32
+ }
33
+ }
34
+
35
+ /// Convert CXF TotpCredential to Bitwarden's Totp struct
36
+ /// This ensures we use the exact same encoding and formatting as Bitwarden's core implementation
37
+ fn totp_credential_to_totp ( cxf_totp : & TotpCredential ) -> Totp {
38
+ let algorithm = convert_otp_algorithm ( & cxf_totp. algorithm ) ;
33
39
34
40
let secret_bytes: Vec < u8 > = cxf_totp. secret . clone ( ) . into ( ) ;
35
41
@@ -543,4 +549,56 @@ mod tests {
543
549
assert ! ( otpauth. contains( "&digits=8" ) ) ;
544
550
assert ! ( otpauth. contains( "&algorithm=SHA256" ) ) ;
545
551
}
552
+
553
+ // Algorithm conversion tests
554
+ #[ test]
555
+ fn test_convert_otp_algorithm_sha1 ( ) {
556
+ let result = convert_otp_algorithm ( & OTPHashAlgorithm :: Sha1 ) ;
557
+ assert_eq ! ( result, TotpAlgorithm :: Sha1 ) ;
558
+ }
559
+
560
+ #[ test]
561
+ fn test_convert_otp_algorithm_sha256 ( ) {
562
+ let result = convert_otp_algorithm ( & OTPHashAlgorithm :: Sha256 ) ;
563
+ assert_eq ! ( result, TotpAlgorithm :: Sha256 ) ;
564
+ }
565
+
566
+ #[ test]
567
+ fn test_convert_otp_algorithm_sha512 ( ) {
568
+ let result = convert_otp_algorithm ( & OTPHashAlgorithm :: Sha512 ) ;
569
+ assert_eq ! ( result, TotpAlgorithm :: Sha512 ) ;
570
+ }
571
+
572
+ #[ test]
573
+ fn test_convert_otp_algorithm_steam ( ) {
574
+ let result = convert_otp_algorithm ( & OTPHashAlgorithm :: Unknown ( "steam" . to_string ( ) ) ) ;
575
+ assert_eq ! ( result, TotpAlgorithm :: Steam ) ;
576
+ }
577
+
578
+ #[ test]
579
+ fn test_convert_otp_algorithm_steam_case_sensitive ( ) {
580
+ // Test that "steam" is case-sensitive
581
+ let result = convert_otp_algorithm ( & OTPHashAlgorithm :: Unknown ( "Steam" . to_string ( ) ) ) ;
582
+ assert_eq ! ( result, TotpAlgorithm :: Sha1 ) ; // will default to SHA1
583
+ }
584
+
585
+ #[ test]
586
+ fn test_convert_otp_algorithm_unknown_empty ( ) {
587
+ let result = convert_otp_algorithm ( & OTPHashAlgorithm :: Unknown ( "" . to_string ( ) ) ) ;
588
+ assert_eq ! ( result, TotpAlgorithm :: Sha1 ) ; // will default to SHA1
589
+ }
590
+
591
+ #[ test]
592
+ fn test_convert_otp_algorithm_unknown_md5 ( ) {
593
+ // Test an algorithm that might exist in other systems but isn't supported
594
+ let result = convert_otp_algorithm ( & OTPHashAlgorithm :: Unknown ( "md5" . to_string ( ) ) ) ;
595
+ assert_eq ! ( result, TotpAlgorithm :: Sha1 ) ; // will default to SHA1
596
+ }
597
+
598
+ #[ test]
599
+ fn test_convert_otp_algorithm_unknown_whitespace ( ) {
600
+ // Test steam with whitespace (will not match)
601
+ let result = convert_otp_algorithm ( & OTPHashAlgorithm :: Unknown ( " steam " . to_string ( ) ) ) ;
602
+ assert_eq ! ( result, TotpAlgorithm :: Sha1 ) ; // will default to SHA1
603
+ }
546
604
}
0 commit comments