@@ -32,335 +32,16 @@ extern crate alloc;
3232pub use rand_core;
3333
3434pub mod errors;
35+ pub mod phc;
3536
36- mod ident;
37- mod output;
38- mod params;
39- mod salt;
4037mod traits;
41- mod value;
4238
4339pub use crate :: {
4440 errors:: { Error , Result } ,
45- ident:: Ident ,
46- output:: Output ,
47- params:: ParamsString ,
48- salt:: { Salt , SaltString } ,
4941 traits:: { McfHasher , PasswordHasher , PasswordVerifier } ,
50- value:: { Decimal , Value } ,
5142} ;
5243
53- use core :: fmt :: { self , Debug } ;
44+ pub use phc :: PasswordHash ;
5445
5546#[ cfg( feature = "alloc" ) ]
56- use alloc:: {
57- str:: FromStr ,
58- string:: { String , ToString } ,
59- } ;
60-
61- /// Separator character used in password hashes (e.g. `$6$...`).
62- const PASSWORD_HASH_SEPARATOR : char = '$' ;
63-
64- /// Password hash.
65- ///
66- /// This type corresponds to the parsed representation of a PHC string as
67- /// described in the [PHC string format specification][1].
68- ///
69- /// PHC strings have the following format:
70- ///
71- /// ```text
72- /// $<id>[$v=<version>][$<param>=<value>(,<param>=<value>)*][$<salt>[$<hash>]]
73- /// ```
74- ///
75- /// where:
76- ///
77- /// - `<id>` is the symbolic name for the function
78- /// - `<version>` is the algorithm version
79- /// - `<param>` is a parameter name
80- /// - `<value>` is a parameter value
81- /// - `<salt>` is an encoding of the salt
82- /// - `<hash>` is an encoding of the hash output
83- ///
84- /// The string is then the concatenation, in that order, of:
85- ///
86- /// - a `$` sign;
87- /// - the function symbolic name;
88- /// - optionally, a `$` sign followed by the algorithm version with a `v=version` format;
89- /// - optionally, a `$` sign followed by one or several parameters, each with a `name=value` format;
90- /// the parameters are separated by commas;
91- /// - optionally, a `$` sign followed by the (encoded) salt value;
92- /// - optionally, a `$` sign followed by the (encoded) hash output (the hash output may be present
93- /// only if the salt is present).
94- ///
95- /// [1]: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md#specification
96- #[ derive( Clone , Debug , Eq , PartialEq ) ]
97- pub struct PasswordHash < ' a > {
98- /// Password hashing algorithm identifier.
99- ///
100- /// This corresponds to the `<id>` field in a PHC string, a.k.a. the
101- /// symbolic name for the function.
102- pub algorithm : Ident < ' a > ,
103-
104- /// Optional version field.
105- ///
106- /// This corresponds to the `<version>` field in a PHC string.
107- pub version : Option < Decimal > ,
108-
109- /// Algorithm-specific parameters.
110- ///
111- /// This corresponds to the set of `$<param>=<value>(,<param>=<value>)*`
112- /// name/value pairs in a PHC string.
113- pub params : ParamsString ,
114-
115- /// [`Salt`] string for personalizing a password hash output.
116- ///
117- /// This corresponds to the `<salt>` value in a PHC string.
118- pub salt : Option < Salt < ' a > > ,
119-
120- /// Password hashing function [`Output`], a.k.a. hash/digest.
121- ///
122- /// This corresponds to the `<hash>` output in a PHC string.
123- pub hash : Option < Output > ,
124- }
125-
126- impl < ' a > PasswordHash < ' a > {
127- /// Parse a password hash from a string in the PHC string format.
128- pub fn new ( s : & ' a str ) -> Result < Self > {
129- if s. is_empty ( ) {
130- return Err ( Error :: PhcStringField ) ;
131- }
132-
133- let mut fields = s. split ( PASSWORD_HASH_SEPARATOR ) ;
134- let beginning = fields. next ( ) . expect ( "no first field" ) ;
135-
136- if beginning. chars ( ) . next ( ) . is_some ( ) {
137- return Err ( Error :: PhcStringField ) ;
138- }
139-
140- let algorithm = fields
141- . next ( )
142- . ok_or ( Error :: PhcStringField )
143- . and_then ( Ident :: try_from) ?;
144-
145- let mut version = None ;
146- let mut params = ParamsString :: new ( ) ;
147- let mut salt = None ;
148- let mut hash = None ;
149-
150- let mut next_field = fields. next ( ) ;
151-
152- if let Some ( field) = next_field {
153- // v=<version>
154- if field. starts_with ( "v=" ) && !field. contains ( params:: PARAMS_DELIMITER ) {
155- version = Some ( Value :: new ( & field[ 2 ..] ) . and_then ( |value| value. decimal ( ) ) ?) ;
156- next_field = None ;
157- }
158- }
159-
160- if next_field. is_none ( ) {
161- next_field = fields. next ( ) ;
162- }
163-
164- if let Some ( field) = next_field {
165- // <param>=<value>
166- if field. contains ( params:: PAIR_DELIMITER ) {
167- params = field. parse ( ) ?;
168- next_field = None ;
169- }
170- }
171-
172- if next_field. is_none ( ) {
173- next_field = fields. next ( ) ;
174- }
175-
176- if let Some ( s) = next_field {
177- salt = Some ( s. try_into ( ) ?) ;
178- }
179-
180- if let Some ( field) = fields. next ( ) {
181- hash = Some ( Output :: decode ( field) ?) ;
182- }
183-
184- if fields. next ( ) . is_some ( ) {
185- return Err ( Error :: PhcStringTrailingData ) ;
186- }
187-
188- Ok ( Self {
189- algorithm,
190- version,
191- params,
192- salt,
193- hash,
194- } )
195- }
196-
197- /// Generate a password hash using the supplied algorithm.
198- pub fn generate (
199- phf : impl PasswordHasher ,
200- password : impl AsRef < [ u8 ] > ,
201- salt : impl Into < Salt < ' a > > ,
202- ) -> Result < Self > {
203- phf. hash_password ( password. as_ref ( ) , salt)
204- }
205-
206- /// Verify this password hash using the specified set of supported
207- /// [`PasswordHasher`] trait objects.
208- pub fn verify_password (
209- & self ,
210- phfs : & [ & dyn PasswordVerifier ] ,
211- password : impl AsRef < [ u8 ] > ,
212- ) -> Result < ( ) > {
213- for & phf in phfs {
214- if phf. verify_password ( password. as_ref ( ) , self ) . is_ok ( ) {
215- return Ok ( ( ) ) ;
216- }
217- }
218-
219- Err ( Error :: Password )
220- }
221-
222- /// Serialize this [`PasswordHash`] as a [`PasswordHashString`].
223- #[ cfg( feature = "alloc" ) ]
224- pub fn serialize ( & self ) -> PasswordHashString {
225- self . into ( )
226- }
227- }
228-
229- // Note: this uses `TryFrom` instead of `FromStr` to support a lifetime on
230- // the `str` the value is being parsed from.
231- impl < ' a > TryFrom < & ' a str > for PasswordHash < ' a > {
232- type Error = Error ;
233-
234- fn try_from ( s : & ' a str ) -> Result < Self > {
235- Self :: new ( s)
236- }
237- }
238-
239- impl fmt:: Display for PasswordHash < ' _ > {
240- fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
241- write ! ( f, "{}{}" , PASSWORD_HASH_SEPARATOR , self . algorithm) ?;
242-
243- if let Some ( version) = self . version {
244- write ! ( f, "{PASSWORD_HASH_SEPARATOR}v={version}" ) ?;
245- }
246-
247- if !self . params . is_empty ( ) {
248- write ! ( f, "{}{}" , PASSWORD_HASH_SEPARATOR , self . params) ?;
249- }
250-
251- if let Some ( salt) = & self . salt {
252- write ! ( f, "{PASSWORD_HASH_SEPARATOR}{salt}" ) ?;
253-
254- if let Some ( hash) = & self . hash {
255- write ! ( f, "{PASSWORD_HASH_SEPARATOR}{hash}" ) ?;
256- }
257- }
258-
259- Ok ( ( ) )
260- }
261- }
262-
263- /// Serialized [`PasswordHash`].
264- ///
265- /// This type contains a serialized password hash string which is ensured to
266- /// parse successfully.
267- // TODO(tarcieri): cached parsed representations? or at least structural data
268- #[ cfg( feature = "alloc" ) ]
269- #[ derive( Clone , Debug , Eq , PartialEq ) ]
270- pub struct PasswordHashString {
271- /// String value
272- string : String ,
273- }
274-
275- #[ cfg( feature = "alloc" ) ]
276- #[ allow( clippy:: len_without_is_empty) ]
277- impl PasswordHashString {
278- /// Parse a password hash from a string in the PHC string format.
279- pub fn new ( s : & str ) -> Result < Self > {
280- PasswordHash :: new ( s) . map ( Into :: into)
281- }
282-
283- /// Parse this owned string as a [`PasswordHash`].
284- pub fn password_hash ( & self ) -> PasswordHash < ' _ > {
285- PasswordHash :: new ( & self . string ) . expect ( "malformed password hash" )
286- }
287-
288- /// Borrow this value as a `str`.
289- pub fn as_str ( & self ) -> & str {
290- self . string . as_str ( )
291- }
292-
293- /// Borrow this value as bytes.
294- pub fn as_bytes ( & self ) -> & [ u8 ] {
295- self . as_str ( ) . as_bytes ( )
296- }
297-
298- /// Get the length of this value in ASCII characters.
299- pub fn len ( & self ) -> usize {
300- self . as_str ( ) . len ( )
301- }
302-
303- /// Password hashing algorithm identifier.
304- pub fn algorithm ( & self ) -> Ident < ' _ > {
305- self . password_hash ( ) . algorithm
306- }
307-
308- /// Optional version field.
309- pub fn version ( & self ) -> Option < Decimal > {
310- self . password_hash ( ) . version
311- }
312-
313- /// Algorithm-specific parameters.
314- pub fn params ( & self ) -> ParamsString {
315- self . password_hash ( ) . params
316- }
317-
318- /// [`Salt`] string for personalizing a password hash output.
319- pub fn salt ( & self ) -> Option < Salt < ' _ > > {
320- self . password_hash ( ) . salt
321- }
322-
323- /// Password hashing function [`Output`], a.k.a. hash/digest.
324- pub fn hash ( & self ) -> Option < Output > {
325- self . password_hash ( ) . hash
326- }
327- }
328-
329- #[ cfg( feature = "alloc" ) ]
330- impl AsRef < str > for PasswordHashString {
331- fn as_ref ( & self ) -> & str {
332- self . as_str ( )
333- }
334- }
335-
336- #[ cfg( feature = "alloc" ) ]
337- impl From < PasswordHash < ' _ > > for PasswordHashString {
338- fn from ( hash : PasswordHash < ' _ > ) -> PasswordHashString {
339- PasswordHashString :: from ( & hash)
340- }
341- }
342-
343- #[ cfg( feature = "alloc" ) ]
344- impl From < & PasswordHash < ' _ > > for PasswordHashString {
345- fn from ( hash : & PasswordHash < ' _ > ) -> PasswordHashString {
346- PasswordHashString {
347- string : hash. to_string ( ) ,
348- }
349- }
350- }
351-
352- #[ cfg( feature = "alloc" ) ]
353- impl FromStr for PasswordHashString {
354- type Err = Error ;
355-
356- fn from_str ( s : & str ) -> Result < Self > {
357- Self :: new ( s)
358- }
359- }
360-
361- #[ cfg( feature = "alloc" ) ]
362- impl fmt:: Display for PasswordHashString {
363- fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
364- f. write_str ( self . as_str ( ) )
365- }
366- }
47+ pub use phc:: PasswordHashString ;
0 commit comments