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