@@ -34,14 +34,108 @@ pub use rand_core;
3434pub mod errors;
3535pub mod phc;
3636
37- mod traits;
38-
39- pub use crate :: {
40- errors:: { Error , Result } ,
41- traits:: { McfHasher , PasswordHasher , PasswordVerifier } ,
42- } ;
43-
37+ pub use crate :: errors:: { Error , Result } ;
4438pub use phc:: PasswordHash ;
4539
4640#[ cfg( feature = "alloc" ) ]
4741pub use phc:: PasswordHashString ;
42+
43+ use crate :: phc:: { Decimal , Ident , ParamsString , Salt } ;
44+ use core:: fmt:: Debug ;
45+
46+ /// Trait for password hashing functions.
47+ pub trait PasswordHasher {
48+ /// Algorithm-specific parameters.
49+ type Params : Clone
50+ + Debug
51+ + Default
52+ + for < ' a > TryFrom < & ' a PasswordHash < ' a > , Error = Error >
53+ + TryInto < ParamsString , Error = Error > ;
54+
55+ /// Compute a [`PasswordHash`] from the provided password using an
56+ /// explicit set of customized algorithm parameters as opposed to the
57+ /// defaults.
58+ ///
59+ /// When in doubt, use [`PasswordHasher::hash_password`] instead.
60+ fn hash_password_customized < ' a > (
61+ & self ,
62+ password : & [ u8 ] ,
63+ algorithm : Option < Ident < ' a > > ,
64+ version : Option < Decimal > ,
65+ params : Self :: Params ,
66+ salt : impl Into < Salt < ' a > > ,
67+ ) -> Result < PasswordHash < ' a > > ;
68+
69+ /// Simple API for computing a [`PasswordHash`] from a password and
70+ /// salt value.
71+ ///
72+ /// Uses the default recommended parameters for a given algorithm.
73+ fn hash_password < ' a > (
74+ & self ,
75+ password : & [ u8 ] ,
76+ salt : impl Into < Salt < ' a > > ,
77+ ) -> Result < PasswordHash < ' a > > {
78+ self . hash_password_customized ( password, None , None , Self :: Params :: default ( ) , salt)
79+ }
80+ }
81+
82+ /// Trait for password verification.
83+ ///
84+ /// Automatically impl'd for any type that impls [`PasswordHasher`].
85+ ///
86+ /// This trait is object safe and can be used to implement abstractions over
87+ /// multiple password hashing algorithms. One such abstraction is provided by
88+ /// the [`PasswordHash::verify_password`] method.
89+ pub trait PasswordVerifier {
90+ /// Compute this password hashing function against the provided password
91+ /// using the parameters from the provided password hash and see if the
92+ /// computed output matches.
93+ fn verify_password ( & self , password : & [ u8 ] , hash : & PasswordHash < ' _ > ) -> Result < ( ) > ;
94+ }
95+
96+ impl < T : PasswordHasher > PasswordVerifier for T {
97+ fn verify_password ( & self , password : & [ u8 ] , hash : & PasswordHash < ' _ > ) -> Result < ( ) > {
98+ if let ( Some ( salt) , Some ( expected_output) ) = ( & hash. salt , & hash. hash ) {
99+ let computed_hash = self . hash_password_customized (
100+ password,
101+ Some ( hash. algorithm ) ,
102+ hash. version ,
103+ T :: Params :: try_from ( hash) ?,
104+ * salt,
105+ ) ?;
106+
107+ if let Some ( computed_output) = & computed_hash. hash {
108+ // See notes on `Output` about the use of a constant-time comparison
109+ if expected_output == computed_output {
110+ return Ok ( ( ) ) ;
111+ }
112+ }
113+ }
114+
115+ Err ( Error :: Password )
116+ }
117+ }
118+
119+ /// Trait for password hashing algorithms which support the legacy
120+ /// [Modular Crypt Format (MCF)][MCF].
121+ ///
122+ /// [MCF]: https://passlib.readthedocs.io/en/stable/modular_crypt_format.html
123+ pub trait McfHasher {
124+ /// Upgrade an MCF hash to a PHC hash. MCF follow this rough format:
125+ ///
126+ /// ```text
127+ /// $<id>$<content>
128+ /// ```
129+ ///
130+ /// MCF hashes are otherwise largely unstructured and parsed according to
131+ /// algorithm-specific rules so hashers must parse a raw string themselves.
132+ fn upgrade_mcf_hash < ' a > ( & self , hash : & ' a str ) -> Result < PasswordHash < ' a > > ;
133+
134+ /// Verify a password hash in MCF format against the provided password.
135+ fn verify_mcf_hash ( & self , password : & [ u8 ] , mcf_hash : & str ) -> Result < ( ) >
136+ where
137+ Self : PasswordVerifier ,
138+ {
139+ self . verify_password ( password, & self . upgrade_mcf_hash ( mcf_hash) ?)
140+ }
141+ }
0 commit comments