31
31
} ;
32
32
33
33
/// Debug message used in panics when invariants aren't properly held.
34
- #[ cfg( feature = "alloc" ) ]
35
34
const INVARIANT_MSG : & str = "should be ensured valid by constructor" ;
36
35
36
+ /// Zero-copy decoder for hashes in the Modular Crypt Format (MCF).
37
+ ///
38
+ /// For more information, see [`McfHash`].
39
+ #[ derive( Clone , Copy , Debug , Eq , PartialEq , PartialOrd , Ord ) ]
40
+ pub struct McfHashRef < ' a > ( & ' a str ) ;
41
+
42
+ impl < ' a > McfHashRef < ' a > {
43
+ /// Parse the given input string, returning an [`McfHashRef`] if valid.
44
+ pub fn new ( s : & ' a str ) -> Result < Self > {
45
+ validate ( s) ?;
46
+ Ok ( Self ( s) )
47
+ }
48
+
49
+ /// Get the contained string as a `str`.
50
+ pub fn as_str ( self ) -> & ' a str {
51
+ self . 0
52
+ }
53
+
54
+ /// Get the algorithm identifier for this MCF hash.
55
+ pub fn id ( self ) -> & ' a str {
56
+ Fields :: new ( self . as_str ( ) )
57
+ . next ( )
58
+ . expect ( INVARIANT_MSG )
59
+ . as_str ( )
60
+ }
61
+
62
+ /// Get an iterator over the parts of the password hash as delimited by `$`, excluding the
63
+ /// initial identifier.
64
+ pub fn fields ( self ) -> Fields < ' a > {
65
+ let mut fields = Fields :: new ( self . as_str ( ) ) ;
66
+
67
+ // Remove the leading identifier
68
+ let id = fields. next ( ) . expect ( INVARIANT_MSG ) ;
69
+ debug_assert_eq ! ( self . id( ) , id. as_str( ) ) ;
70
+
71
+ fields
72
+ }
73
+ }
74
+
37
75
/// Modular Crypt Format (MCF) serialized password hash.
38
76
///
39
77
/// Password hashes in this format take the form `${id}$...`, where `{id}` is a short numeric or
@@ -49,6 +87,7 @@ const INVARIANT_MSG: &str = "should be ensured valid by constructor";
49
87
/// $6$rounds=100000$exn6tVc2j/MZD8uG$BI1Xh8qQSK9J4m14uwy7abn.ctj/TIAzlaVCto0MQrOFIeTXsc1iwzH16XEWo/a7c7Y9eVJvufVzYAs4EsPOy0
50
88
/// ```
51
89
#[ cfg( feature = "alloc" ) ]
90
+ #[ derive( Clone , Debug , Eq , PartialEq , PartialOrd , Ord ) ]
52
91
pub struct McfHash ( String ) ;
53
92
54
93
#[ cfg( feature = "alloc" ) ]
@@ -82,25 +121,20 @@ impl McfHash {
82
121
& self . 0
83
122
}
84
123
124
+ /// Get an [`McfHashRef`] which corresponds to this owned [`McfHash`].
125
+ pub fn as_mcf_hash_ref ( & self ) -> McfHashRef {
126
+ McfHashRef ( self . as_str ( ) )
127
+ }
128
+
85
129
/// Get the algorithm identifier for this MCF hash.
86
130
pub fn id ( & self ) -> & str {
87
- Fields :: new ( self . as_str ( ) )
88
- . expect ( INVARIANT_MSG )
89
- . next ( )
90
- . expect ( INVARIANT_MSG )
91
- . as_str ( )
131
+ self . as_mcf_hash_ref ( ) . id ( )
92
132
}
93
133
94
134
/// Get an iterator over the parts of the password hash as delimited by `$`, excluding the
95
135
/// initial identifier.
96
136
pub fn fields ( & self ) -> Fields {
97
- let mut fields = Fields :: new ( self . as_str ( ) ) . expect ( INVARIANT_MSG ) ;
98
-
99
- // Remove the leading identifier
100
- let id = fields. next ( ) . expect ( INVARIANT_MSG ) ;
101
- debug_assert_eq ! ( self . id( ) , id. as_str( ) ) ;
102
-
103
- fields
137
+ self . as_mcf_hash_ref ( ) . fields ( )
104
138
}
105
139
106
140
/// Push an additional field onto the password hash string.
@@ -116,6 +150,12 @@ impl McfHash {
116
150
}
117
151
}
118
152
153
+ impl < ' a > AsRef < str > for McfHashRef < ' a > {
154
+ fn as_ref ( & self ) -> & str {
155
+ self . as_str ( )
156
+ }
157
+ }
158
+
119
159
#[ cfg( feature = "alloc" ) ]
120
160
impl AsRef < str > for McfHash {
121
161
fn as_ref ( & self ) -> & str {
@@ -140,15 +180,19 @@ impl str::FromStr for McfHash {
140
180
}
141
181
142
182
/// Perform validations that the given string is well-formed MCF.
143
- #[ cfg( feature = "alloc" ) ]
144
183
fn validate ( s : & str ) -> Result < ( ) > {
184
+ // Require leading `$`
185
+ if !s. starts_with ( fields:: DELIMITER ) {
186
+ return Err ( Error { } ) ;
187
+ }
188
+
145
189
// Disallow trailing `$`
146
190
if s. ends_with ( fields:: DELIMITER ) {
147
191
return Err ( Error { } ) ;
148
192
}
149
193
150
194
// Validates the hash begins with a leading `$`
151
- let mut fields = Fields :: new ( s) ? ;
195
+ let mut fields = Fields :: new ( s) ;
152
196
153
197
// Validate characters in the identifier field
154
198
let id = fields. next ( ) . ok_or ( Error { } ) ?;
@@ -166,7 +210,6 @@ fn validate(s: &str) -> Result<()> {
166
210
///
167
211
/// Allowed characters match the regex: `[a-z0-9\-]`, where the first and last characters do NOT
168
212
/// contain a `-`.
169
- #[ cfg( feature = "alloc" ) ]
170
213
fn validate_id ( id : & str ) -> Result < ( ) > {
171
214
let first = id. chars ( ) . next ( ) . ok_or ( Error { } ) ?;
172
215
let last = id. chars ( ) . last ( ) . ok_or ( Error { } ) ?;
0 commit comments