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 base64;
10 use crate::context::Context;
11 use crate::decoded::Decoded;
12 use crate::error::Error;
13 use crate::result::Result;
14 use crate::variant::Variant;
15 use crate::version::Version;
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 
159 #[cfg(test)]
160 mod tests {
161 
162     use crate::config::Config;
163     use crate::context::Context;
164     use crate::decoded::Decoded;
165     use crate::encoding::{base64_len, decode_string, encode_string, num_len};
166     use crate::error::Error;
167     use crate::thread_mode::ThreadMode;
168     use crate::variant::Variant;
169     use crate::version::Version;
170 
171     #[test]
base64_len_returns_correct_length()172     fn base64_len_returns_correct_length() {
173         let tests = vec![
174             (1, 2),
175             (2, 3),
176             (3, 4),
177             (4, 6),
178             (5, 7),
179             (6, 8),
180             (7, 10),
181             (8, 11),
182             (9, 12),
183             (10, 14),
184         ];
185         for (len, expected) in tests {
186             let actual = base64_len(len);
187             assert_eq!(actual, expected);
188         }
189     }
190 
191     #[test]
decode_string_with_version10_returns_correct_result()192     fn decode_string_with_version10_returns_correct_result() {
193         let encoded = "$argon2i$v=16$m=4096,t=3,p=1\
194                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
195         let expected = Decoded {
196             variant: Variant::Argon2i,
197             version: Version::Version10,
198             mem_cost: 4096,
199             time_cost: 3,
200             parallelism: 1,
201             salt: b"salt1234".to_vec(),
202             hash: b"12345678901234567890123456789012".to_vec(),
203         };
204         let actual = decode_string(encoded).unwrap();
205         assert_eq!(actual, expected);
206     }
207 
208     #[test]
decode_string_with_version13_returns_correct_result()209     fn decode_string_with_version13_returns_correct_result() {
210         let encoded = "$argon2i$v=19$m=4096,t=3,p=1\
211                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
212         let expected = Decoded {
213             variant: Variant::Argon2i,
214             version: Version::Version13,
215             mem_cost: 4096,
216             time_cost: 3,
217             parallelism: 1,
218             salt: b"salt1234".to_vec(),
219             hash: b"12345678901234567890123456789012".to_vec(),
220         };
221         let actual = decode_string(encoded).unwrap();
222         assert_eq!(actual, expected);
223     }
224 
225     #[test]
decode_string_without_version_returns_correct_result()226     fn decode_string_without_version_returns_correct_result() {
227         let encoded = "$argon2i$m=4096,t=3,p=1\
228                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
229         let expected = Decoded {
230             variant: Variant::Argon2i,
231             version: Version::Version10,
232             mem_cost: 4096,
233             time_cost: 3,
234             parallelism: 1,
235             salt: b"salt1234".to_vec(),
236             hash: b"12345678901234567890123456789012".to_vec(),
237         };
238         let actual = decode_string(encoded).unwrap();
239         assert_eq!(actual, expected);
240     }
241 
242     #[test]
decode_string_without_variant_returns_error_result()243     fn decode_string_without_variant_returns_error_result() {
244         let encoded = "$m=4096,t=3,p=1\
245                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
246         let result = decode_string(encoded);
247         assert_eq!(result, Err(Error::DecodingFail));
248     }
249 
250     #[test]
decode_string_with_empty_variant_returns_error_result()251     fn decode_string_with_empty_variant_returns_error_result() {
252         let encoded = "$$m=4096,t=3,p=1\
253                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
254         let result = decode_string(encoded);
255         assert_eq!(result, Err(Error::DecodingFail));
256     }
257 
258     #[test]
decode_string_with_invalid_variant_returns_error_result()259     fn decode_string_with_invalid_variant_returns_error_result() {
260         let encoded = "$argon$m=4096,t=3,p=1\
261                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
262         let result = decode_string(encoded);
263         assert_eq!(result, Err(Error::DecodingFail));
264     }
265 
266     #[test]
decode_string_without_mem_cost_returns_error_result()267     fn decode_string_without_mem_cost_returns_error_result() {
268         let encoded = "$argon2i$t=3,p=1\
269                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
270         let result = decode_string(encoded);
271         assert_eq!(result, Err(Error::DecodingFail));
272     }
273 
274     #[test]
decode_string_with_empty_mem_cost_returns_error_result()275     fn decode_string_with_empty_mem_cost_returns_error_result() {
276         let encoded = "$argon2i$m=,t=3,p=1\
277                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
278         let result = decode_string(encoded);
279         assert_eq!(result, Err(Error::DecodingFail));
280     }
281 
282     #[test]
decode_string_with_non_numeric_mem_cost_returns_error_result()283     fn decode_string_with_non_numeric_mem_cost_returns_error_result() {
284         let encoded = "$argon2i$m=a,t=3,p=1\
285                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
286         let result = decode_string(encoded);
287         assert_eq!(result, Err(Error::DecodingFail));
288     }
289 
290     #[test]
decode_string_without_time_cost_returns_error_result()291     fn decode_string_without_time_cost_returns_error_result() {
292         let encoded = "$argon2i$m=4096,p=1\
293                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
294         let result = decode_string(encoded);
295         assert_eq!(result, Err(Error::DecodingFail));
296     }
297 
298     #[test]
decode_string_with_empty_time_cost_returns_error_result()299     fn decode_string_with_empty_time_cost_returns_error_result() {
300         let encoded = "$argon2i$m=4096,t=,p=1\
301                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
302         let result = decode_string(encoded);
303         assert_eq!(result, Err(Error::DecodingFail));
304     }
305 
306     #[test]
decode_string_with_non_numeric_time_cost_returns_error_result()307     fn decode_string_with_non_numeric_time_cost_returns_error_result() {
308         let encoded = "$argon2i$m=4096,t=a,p=1\
309                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
310         let result = decode_string(encoded);
311         assert_eq!(result, Err(Error::DecodingFail));
312     }
313 
314     #[test]
decode_string_without_parallelism_returns_error_result()315     fn decode_string_without_parallelism_returns_error_result() {
316         let encoded = "$argon2i$m=4096,t=3\
317                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
318         let result = decode_string(encoded);
319         assert_eq!(result, Err(Error::DecodingFail));
320     }
321 
322     #[test]
decode_string_with_empty_parallelism_returns_error_result()323     fn decode_string_with_empty_parallelism_returns_error_result() {
324         let encoded = "$argon2i$m=4096,t=3,p=\
325                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
326         let result = decode_string(encoded);
327         assert_eq!(result, Err(Error::DecodingFail));
328     }
329 
330     #[test]
decode_string_with_non_numeric_parallelism_returns_error_result()331     fn decode_string_with_non_numeric_parallelism_returns_error_result() {
332         let encoded = "$argon2i$m=4096,t=3,p=a\
333                        $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
334         let result = decode_string(encoded);
335         assert_eq!(result, Err(Error::DecodingFail));
336     }
337 
338     #[test]
decode_string_without_salt_returns_error_result()339     fn decode_string_without_salt_returns_error_result() {
340         let encoded = "$argon2i$m=4096,t=3,p=1\
341                        $MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
342         let result = decode_string(encoded);
343         assert_eq!(result, Err(Error::DecodingFail));
344     }
345 
346     #[test]
decode_string_without_hash_returns_error_result()347     fn decode_string_without_hash_returns_error_result() {
348         let encoded = "$argon2i$m=4096,t=3,p=a\
349                        $c2FsdDEyMzQ=";
350         let result = decode_string(encoded);
351         assert_eq!(result, Err(Error::DecodingFail));
352     }
353 
354     #[test]
decode_string_with_empty_hash_returns_error_result()355     fn decode_string_with_empty_hash_returns_error_result() {
356         let encoded = "$argon2i$m=4096,t=3,p=a\
357                        $c2FsdDEyMzQ=$";
358         let result = decode_string(encoded);
359         assert_eq!(result, Err(Error::DecodingFail));
360     }
361 
362     #[test]
encode_string_returns_correct_string()363     fn encode_string_returns_correct_string() {
364         let hash = b"12345678901234567890123456789012".to_vec();
365         let config = Config {
366             ad: &[],
367             hash_length: hash.len() as u32,
368             lanes: 1,
369             mem_cost: 4096,
370             secret: &[],
371             thread_mode: ThreadMode::Parallel,
372             time_cost: 3,
373             variant: Variant::Argon2i,
374             version: Version::Version13,
375         };
376         let pwd = b"password".to_vec();
377         let salt = b"salt1234".to_vec();
378         let context = Context::new(config, &pwd, &salt).unwrap();
379         let expected = "$argon2i$v=19$m=4096,t=3,p=1\
380                         $c2FsdDEyMzQ$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI";
381         let actual = encode_string(&context, &hash);
382         assert_eq!(actual, expected);
383     }
384 
385     #[test]
num_len_returns_correct_length()386     fn num_len_returns_correct_length() {
387         let tests = vec![
388             (1, 1),
389             (10, 2),
390             (110, 3),
391             (1230, 4),
392             (12340, 5),
393             (123457, 6),
394         ];
395         for (num, expected) in tests {
396             let actual = num_len(num);
397             assert_eq!(actual, expected);
398         }
399     }
400 }
401