1 // Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8 
9 use crate::context::Context;
10 use crate::decoded::Decoded;
11 use crate::error::Error;
12 use crate::result::Result;
13 use crate::variant::Variant;
14 use crate::version::Version;
15 use base64;
16 
17 /// Structure containing the options.
18 struct Options {
19     mem_cost: u32,
20     time_cost: u32,
21     parallelism: u32,
22 }
23 
24 /// Gets the base64 encoded length of a byte slice with the specified length.
base64_len(length: u32) -> u3225 pub fn base64_len(length: u32) -> u32 {
26     let olen = (length / 3) << 2;
27     match length % 3 {
28         2 => olen + 3,
29         1 => olen + 2,
30         _ => olen,
31     }
32 }
33 
34 /// Attempts to decode the encoded string slice.
decode_string(encoded: &str) -> Result<Decoded>35 pub fn decode_string(encoded: &str) -> Result<Decoded> {
36     let items: Vec<&str> = encoded.split('$').collect();
37     if items.len() == 6 {
38         decode_empty(items[0])?;
39         let variant = decode_variant(items[1])?;
40         let version = decode_version(items[2])?;
41         let options = decode_options(items[3])?;
42         let salt = base64::decode(items[4])?;
43         let hash = base64::decode(items[5])?;
44 
45         Ok(Decoded {
46             variant,
47             version,
48             mem_cost: options.mem_cost,
49             time_cost: options.time_cost,
50             parallelism: options.parallelism,
51             salt,
52             hash,
53         })
54     } else if items.len() == 5 {
55         decode_empty(items[0])?;
56         let variant = decode_variant(items[1])?;
57         let options = decode_options(items[2])?;
58         let salt = base64::decode(items[3])?;
59         let hash = base64::decode(items[4])?;
60 
61         Ok(Decoded {
62             variant,
63             version: Version::Version10,
64             mem_cost: options.mem_cost,
65             time_cost: options.time_cost,
66             parallelism: options.parallelism,
67             salt,
68             hash,
69         })
70     } else {
71         Err(Error::DecodingFail)
72     }
73 }
74 
decode_empty(str: &str) -> Result<()>75 fn decode_empty(str: &str) -> Result<()> {
76     if str == "" {
77         Ok(())
78     } else {
79         Err(Error::DecodingFail)
80     }
81 }
82 
decode_options(str: &str) -> Result<Options>83 fn decode_options(str: &str) -> Result<Options> {
84     let items: Vec<&str> = str.split(',').collect();
85     if items.len() == 3 {
86         Ok(Options {
87             mem_cost: decode_option(items[0], "m")?,
88             time_cost: decode_option(items[1], "t")?,
89             parallelism: decode_option(items[2], "p")?,
90         })
91     } else {
92         Err(Error::DecodingFail)
93     }
94 }
95 
decode_option(str: &str, name: &str) -> Result<u32>96 fn decode_option(str: &str, name: &str) -> Result<u32> {
97     let items: Vec<&str> = str.split('=').collect();
98     if items.len() == 2 {
99         if items[0] == name {
100             decode_u32(items[1])
101         } else {
102             Err(Error::DecodingFail)
103         }
104     } else {
105         Err(Error::DecodingFail)
106     }
107 }
108 
decode_u32(str: &str) -> Result<u32>109 fn decode_u32(str: &str) -> Result<u32> {
110     match str.parse() {
111         Ok(i) => Ok(i),
112         Err(_) => Err(Error::DecodingFail),
113     }
114 }
115 
decode_variant(str: &str) -> Result<Variant>116 fn decode_variant(str: &str) -> Result<Variant> {
117     Variant::from_str(str)
118 }
119 
decode_version(str: &str) -> Result<Version>120 fn decode_version(str: &str) -> Result<Version> {
121     let items: Vec<&str> = str.split('=').collect();
122     if items.len() == 2 {
123         if items[0] == "v" {
124             Version::from_str(items[1])
125         } else {
126             Err(Error::DecodingFail)
127         }
128     } else {
129         Err(Error::DecodingFail)
130     }
131 }
132 
133 /// Encodes the hash and context.
encode_string(context: &Context, hash: &[u8]) -> String134 pub fn encode_string(context: &Context, hash: &[u8]) -> String {
135     format!(
136         "${}$v={}$m={},t={},p={}${}${}",
137         context.config.variant,
138         context.config.version,
139         context.config.mem_cost,
140         context.config.time_cost,
141         context.config.lanes,
142         base64::encode_config(context.salt, base64::STANDARD_NO_PAD),
143         base64::encode_config(hash, base64::STANDARD_NO_PAD),
144     )
145 }
146 
147 /// Gets the string length of the specified number.
num_len(number: u32) -> u32148 pub fn num_len(number: u32) -> u32 {
149     let mut len = 1;
150     let mut num = number;
151     while num >= 10 {
152         len += 1;
153         num /= 10;
154     }
155     len
156 }
157 
158 #[cfg(test)]
159 mod tests {
160 
161     #[cfg(feature = "crossbeam-utils")]
162     use crate::config::Config;
163     #[cfg(feature = "crossbeam-utils")]
164     use crate::context::Context;
165     use crate::decoded::Decoded;
166     #[cfg(feature = "crossbeam-utils")]
167     use crate::encoding::encode_string;
168     use crate::encoding::{base64_len, decode_string, num_len};
169     use crate::error::Error;
170     #[cfg(feature = "crossbeam-utils")]
171     use crate::thread_mode::ThreadMode;
172     use crate::variant::Variant;
173     use crate::version::Version;
174 
175     #[test]
base64_len_returns_correct_length()176     fn base64_len_returns_correct_length() {
177         let tests = vec![
178             (1, 2),
179             (2, 3),
180             (3, 4),
181             (4, 6),
182             (5, 7),
183             (6, 8),
184             (7, 10),
185             (8, 11),
186             (9, 12),
187             (10, 14),
188         ];
189         for (len, expected) in tests {
190             let actual = base64_len(len);
191             assert_eq!(actual, expected);
192         }
193     }
194 
195     #[test]
decode_string_with_version10_returns_correct_result()196     fn decode_string_with_version10_returns_correct_result() {
197         let encoded = "$argon2i$v=16$m=4096,t=3,p=1\
198                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
199         let expected = Decoded {
200             variant: Variant::Argon2i,
201             version: Version::Version10,
202             mem_cost: 4096,
203             time_cost: 3,
204             parallelism: 1,
205             salt: b"salt1234".to_vec(),
206             hash: b"12345678901234567890123456789012".to_vec(),
207         };
208         let actual = decode_string(encoded).unwrap();
209         assert_eq!(actual, expected);
210     }
211 
212     #[test]
decode_string_with_version13_returns_correct_result()213     fn decode_string_with_version13_returns_correct_result() {
214         let encoded = "$argon2i$v=19$m=4096,t=3,p=1\
215                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
216         let expected = Decoded {
217             variant: Variant::Argon2i,
218             version: Version::Version13,
219             mem_cost: 4096,
220             time_cost: 3,
221             parallelism: 1,
222             salt: b"salt1234".to_vec(),
223             hash: b"12345678901234567890123456789012".to_vec(),
224         };
225         let actual = decode_string(encoded).unwrap();
226         assert_eq!(actual, expected);
227     }
228 
229     #[test]
decode_string_without_version_returns_correct_result()230     fn decode_string_without_version_returns_correct_result() {
231         let encoded = "$argon2i$m=4096,t=3,p=1\
232                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
233         let expected = Decoded {
234             variant: Variant::Argon2i,
235             version: Version::Version10,
236             mem_cost: 4096,
237             time_cost: 3,
238             parallelism: 1,
239             salt: b"salt1234".to_vec(),
240             hash: b"12345678901234567890123456789012".to_vec(),
241         };
242         let actual = decode_string(encoded).unwrap();
243         assert_eq!(actual, expected);
244     }
245 
246     #[test]
decode_string_without_variant_returns_error_result()247     fn decode_string_without_variant_returns_error_result() {
248         let encoded = "$m=4096,t=3,p=1\
249                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
250         let result = decode_string(encoded);
251         assert_eq!(result, Err(Error::DecodingFail));
252     }
253 
254     #[test]
decode_string_with_empty_variant_returns_error_result()255     fn decode_string_with_empty_variant_returns_error_result() {
256         let encoded = "$$m=4096,t=3,p=1\
257                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
258         let result = decode_string(encoded);
259         assert_eq!(result, Err(Error::DecodingFail));
260     }
261 
262     #[test]
decode_string_with_invalid_variant_returns_error_result()263     fn decode_string_with_invalid_variant_returns_error_result() {
264         let encoded = "$argon$m=4096,t=3,p=1\
265                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
266         let result = decode_string(encoded);
267         assert_eq!(result, Err(Error::DecodingFail));
268     }
269 
270     #[test]
decode_string_without_mem_cost_returns_error_result()271     fn decode_string_without_mem_cost_returns_error_result() {
272         let encoded = "$argon2i$t=3,p=1\
273                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
274         let result = decode_string(encoded);
275         assert_eq!(result, Err(Error::DecodingFail));
276     }
277 
278     #[test]
decode_string_with_empty_mem_cost_returns_error_result()279     fn decode_string_with_empty_mem_cost_returns_error_result() {
280         let encoded = "$argon2i$m=,t=3,p=1\
281                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
282         let result = decode_string(encoded);
283         assert_eq!(result, Err(Error::DecodingFail));
284     }
285 
286     #[test]
decode_string_with_non_numeric_mem_cost_returns_error_result()287     fn decode_string_with_non_numeric_mem_cost_returns_error_result() {
288         let encoded = "$argon2i$m=a,t=3,p=1\
289                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
290         let result = decode_string(encoded);
291         assert_eq!(result, Err(Error::DecodingFail));
292     }
293 
294     #[test]
decode_string_without_time_cost_returns_error_result()295     fn decode_string_without_time_cost_returns_error_result() {
296         let encoded = "$argon2i$m=4096,p=1\
297                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
298         let result = decode_string(encoded);
299         assert_eq!(result, Err(Error::DecodingFail));
300     }
301 
302     #[test]
decode_string_with_empty_time_cost_returns_error_result()303     fn decode_string_with_empty_time_cost_returns_error_result() {
304         let encoded = "$argon2i$m=4096,t=,p=1\
305                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
306         let result = decode_string(encoded);
307         assert_eq!(result, Err(Error::DecodingFail));
308     }
309 
310     #[test]
decode_string_with_non_numeric_time_cost_returns_error_result()311     fn decode_string_with_non_numeric_time_cost_returns_error_result() {
312         let encoded = "$argon2i$m=4096,t=a,p=1\
313                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
314         let result = decode_string(encoded);
315         assert_eq!(result, Err(Error::DecodingFail));
316     }
317 
318     #[test]
decode_string_without_parallelism_returns_error_result()319     fn decode_string_without_parallelism_returns_error_result() {
320         let encoded = "$argon2i$m=4096,t=3\
321                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
322         let result = decode_string(encoded);
323         assert_eq!(result, Err(Error::DecodingFail));
324     }
325 
326     #[test]
decode_string_with_empty_parallelism_returns_error_result()327     fn decode_string_with_empty_parallelism_returns_error_result() {
328         let encoded = "$argon2i$m=4096,t=3,p=\
329                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
330         let result = decode_string(encoded);
331         assert_eq!(result, Err(Error::DecodingFail));
332     }
333 
334     #[test]
decode_string_with_non_numeric_parallelism_returns_error_result()335     fn decode_string_with_non_numeric_parallelism_returns_error_result() {
336         let encoded = "$argon2i$m=4096,t=3,p=a\
337                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
338         let result = decode_string(encoded);
339         assert_eq!(result, Err(Error::DecodingFail));
340     }
341 
342     #[test]
decode_string_without_salt_returns_error_result()343     fn decode_string_without_salt_returns_error_result() {
344         let encoded = "$argon2i$m=4096,t=3,p=1\
345                        $MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
346         let result = decode_string(encoded);
347         assert_eq!(result, Err(Error::DecodingFail));
348     }
349 
350     #[test]
decode_string_without_hash_returns_error_result()351     fn decode_string_without_hash_returns_error_result() {
352         let encoded = "$argon2i$m=4096,t=3,p=a\
353                        $c2FsdDEyMzQ=";
354         let result = decode_string(encoded);
355         assert_eq!(result, Err(Error::DecodingFail));
356     }
357 
358     #[test]
decode_string_with_empty_hash_returns_error_result()359     fn decode_string_with_empty_hash_returns_error_result() {
360         let encoded = "$argon2i$m=4096,t=3,p=a\
361                        $c2FsdDEyMzQ=$";
362         let result = decode_string(encoded);
363         assert_eq!(result, Err(Error::DecodingFail));
364     }
365 
366     #[cfg(feature = "crossbeam-utils")]
367     #[test]
encode_string_returns_correct_string()368     fn encode_string_returns_correct_string() {
369         let hash = b"12345678901234567890123456789012".to_vec();
370         let config = Config {
371             ad: &[],
372             hash_length: hash.len() as u32,
373             lanes: 1,
374             mem_cost: 4096,
375             secret: &[],
376             thread_mode: ThreadMode::Parallel,
377             time_cost: 3,
378             variant: Variant::Argon2i,
379             version: Version::Version13,
380         };
381         let pwd = b"password".to_vec();
382         let salt = b"salt1234".to_vec();
383         let context = Context::new(config, &pwd, &salt).unwrap();
384         let expected = "$argon2i$v=19$m=4096,t=3,p=1\
385                         $c2FsdDEyMzQ$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI";
386         let actual = encode_string(&context, &hash);
387         assert_eq!(actual, expected);
388     }
389 
390     #[test]
num_len_returns_correct_length()391     fn num_len_returns_correct_length() {
392         let tests = vec![
393             (1, 1),
394             (10, 2),
395             (110, 3),
396             (1230, 4),
397             (12340, 5),
398             (123457, 6),
399         ];
400         for (num, expected) in tests {
401             let actual = num_len(num);
402             assert_eq!(actual, expected);
403         }
404     }
405 }
406