argon2_ffi/
lib.rs

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//! Foreign Function Interface (FFI) bindings for the rust-argon2 crate.
10//!
11//! # Return Values
12//!
13//! Most functions return an `int32_t`. On successful completion, the value `0`
14//! is returned; otherwise a negative value is returned. The table below shows
15//! the meaning of the return values.
16//!
17//! | Value | Description |
18//! |:------|:------------|
19//! | 0     | OK          |
20//! | -1    | Output pointer is NULL |
21//! | -2    | Output is too short |
22//! | -3    | Output is too long  |
23//! | -4    | Password is too short |
24//! | -5    | Password is too long  |
25//! | -6    | Salt is too short |
26//! | -7    | Salt is too long  |
27//! | -8    | Associated data is too short |
28//! | -9    | Associated data is too long  |
29//! | -10   | Secret is too short |
30//! | -11   | Secret is too long  |
31//! | -12   | Time cost is too small |
32//! | -13   | Time cost is too large |
33//! | -14   | Memory cost is too small |
34//! | -15   | Memory cost is too large |
35//! | -16   | Too few lanes  |
36//! | -17   | Too many lanes |
37//! | -18   | Password pointer is NULL, but password length is not 0 |
38//! | -19   | Salt pointer is NULL, but salt length is not 0 |
39//! | -20   | Secret pointer is NULL, but secret length is not 0 |
40//! | -21   | Associated data pointer is NULL, bit ad length is not 0 |
41//! | -26   | There is no such version of Argon2 |
42//! | -31   | Encoding failed |
43//! | -32   | Decoding failed |
44//! | -35   | The password does not match the supplied hash |
45//! | -36   | Hash pointer is NULL, but hash length is not 0 |
46
47extern crate argon2;
48extern crate libc;
49
50use argon2::{Config, Error, ThreadMode, Variant, Version};
51use libc::{c_char, size_t, uint8_t, int32_t, uint32_t};
52use std::{ptr, slice};
53use std::ffi::CStr;
54use std::io::{Cursor, Write};
55
56
57/// Argon2d variant.
58#[no_mangle]
59pub static ARGON2D: uint32_t = Variant::Argon2d as uint32_t;
60
61/// Argon2i variant.
62#[no_mangle]
63pub static ARGON2I: uint32_t = Variant::Argon2i as uint32_t;
64
65/// Argon2id variant.
66#[no_mangle]
67pub static ARGON2ID: uint32_t = Variant::Argon2id as uint32_t;
68
69/// Argon version 10.
70#[no_mangle]
71pub static VERSION10: uint32_t = Version::Version10 as uint32_t;
72
73/// Argon version 13.
74#[no_mangle]
75pub static VERSION13: uint32_t = Version::Version13 as uint32_t;
76
77
78const DEF_HASH_LEN: usize = 32;
79const DEF_LANES: u32 = 1;
80const DEF_MEMORY: u32 = 4096;
81const DEF_PARALLELISM: u32 = 1;
82const DEF_THREADS: u32 = 1;
83const DEF_TIME: u32 = 3;
84const DEF_VARIANT: u32 = Variant::Argon2i as u32;
85const DEF_VERSION: u32 = Version::Version13 as u32;
86
87
88#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
89enum ReturnValue {
90    Ok = 0,
91    OutputPtrNull = -1,
92    OutputTooShort = -2,
93    OutputTooLong = -3,
94    PwdTooShort = -4,
95    PwdTooLong = -5,
96    SaltTooShort = -6,
97    SaltTooLong = -7,
98    AdTooShort = -8,
99    AdTooLong = -9,
100    SecretTooShort = -10,
101    SecretTooLong = -11,
102    TimeTooSmall = -12,
103    TimeTooLarge = -13,
104    MemoryTooLittle = -14,
105    MemoryTooMuch = -15,
106    LanesTooFew = -16,
107    LanesTooMany = -17,
108    PwdPtrMismatch = -18,
109    SaltPtrMismatch = -19,
110    SecretPtrMismatch = -20,
111    AdPtrMismatch = -21,
112    IncorrectType = -26,
113    EncodingFail = -31,
114    DecodingFail = -32,
115    VerifyMismatch = -35,
116    HashPtrMismatch = -36,
117}
118
119impl ReturnValue {
120    pub fn from_error(error: Error) -> ReturnValue {
121        match error {
122            Error::OutputTooShort => ReturnValue::OutputTooShort,
123            Error::OutputTooLong => ReturnValue::OutputTooLong,
124            Error::PwdTooShort => ReturnValue::PwdTooShort,
125            Error::PwdTooLong => ReturnValue::PwdTooLong,
126            Error::SaltTooShort => ReturnValue::SaltTooShort,
127            Error::SaltTooLong => ReturnValue::SaltTooLong,
128            Error::AdTooShort => ReturnValue::AdTooShort,
129            Error::AdTooLong => ReturnValue::AdTooLong,
130            Error::SecretTooShort => ReturnValue::SecretTooShort,
131            Error::SecretTooLong => ReturnValue::SecretTooLong,
132            Error::TimeTooSmall => ReturnValue::TimeTooSmall,
133            Error::TimeTooLarge => ReturnValue::TimeTooLarge,
134            Error::MemoryTooLittle => ReturnValue::MemoryTooLittle,
135            Error::MemoryTooMuch => ReturnValue::MemoryTooMuch,
136            Error::LanesTooFew => ReturnValue::LanesTooFew,
137            Error::LanesTooMany => ReturnValue::LanesTooMany,
138            Error::IncorrectType => ReturnValue::IncorrectType,
139            Error::IncorrectVersion => ReturnValue::IncorrectType,
140            Error::DecodingFail => ReturnValue::DecodingFail,
141        }
142    }
143}
144
145/// Returns the length of a null terminated encoded string.
146#[no_mangle]
147pub extern "C" fn encoded_len(
148    variant: uint32_t,
149    mem_cost: uint32_t,
150    time_cost: uint32_t,
151    parallelism: uint32_t,
152    salt_len: uint32_t,
153    hash_len: uint32_t
154) -> uint32_t {
155    let variant = match Variant::from_u32(variant) {
156        Ok(val) => val,
157        Err(_) => Variant::Argon2id,
158    };
159    argon2::encoded_len(variant, mem_cost, time_cost, parallelism, salt_len, hash_len) + 1
160}
161
162/// Returns the length of a null terminated encoded string using default settings.
163#[no_mangle]
164pub extern "C" fn encoded_len_simple(salt_len: uint32_t) -> uint32_t {
165    encoded_len(DEF_VARIANT, DEF_MEMORY, DEF_TIME, DEF_PARALLELISM, salt_len, DEF_HASH_LEN as u32)
166}
167
168/// Hashes the password and writes the encoded string to `encoded`.
169#[no_mangle]
170pub extern "C" fn hash_encoded(
171    variant: uint32_t,
172    version: uint32_t,
173    mem_cost: uint32_t,
174    time_cost: uint32_t,
175    lanes: uint32_t,
176    threads: uint32_t,
177    pwd: *const uint8_t,
178    pwd_len: size_t,
179    salt: *const uint8_t,
180    salt_len: size_t,
181    secret: *const uint8_t,
182    secret_len: size_t,
183    ad: *const uint8_t,
184    ad_len: size_t,
185    hash_len: size_t,
186    encoded: *mut c_char,
187    encoded_len: size_t
188) -> int32_t {
189    let pwd = match mk_slice(pwd, pwd_len, ReturnValue::PwdPtrMismatch) {
190        Ok(val) => val,
191        Err(err) => return err as i32,
192    };
193
194    let salt = match mk_slice(salt, salt_len, ReturnValue::SaltPtrMismatch) {
195        Ok(val) => val,
196        Err(err) => return err as i32,
197    };
198
199    if encoded.is_null() {
200        return ReturnValue::EncodingFail as i32;
201    }
202
203    let config = match mk_config(variant,
204                                 version,
205                                 mem_cost,
206                                 time_cost,
207                                 lanes,
208                                 threads,
209                                 secret,
210                                 secret_len,
211                                 ad,
212                                 ad_len,
213                                 hash_len) {
214        Ok(val) => val,
215        Err(err) => return err as i32,
216    };
217
218    match argon2::hash_encoded(pwd, salt, &config) {
219        Ok(string) => {
220            let bytes = string.into_bytes();
221            let null = [0u8];
222            if bytes.len() + null.len() != encoded_len {
223                return ReturnValue::EncodingFail as i32;
224            }
225
226            let out = unsafe { slice::from_raw_parts_mut(encoded as *mut u8, encoded_len) };
227
228            let mut cursor = Cursor::new(out);
229            if cursor.write(&bytes).is_err() {
230                return ReturnValue::EncodingFail as i32;
231            }
232            if cursor.write(&null).is_err() {
233                return ReturnValue::EncodingFail as i32;
234            }
235            ReturnValue::Ok as i32
236        }
237        Err(err) => ReturnValue::from_error(err) as i32,
238    }
239}
240
241/// Hashes the password using Argon2d and writes the encoded string to `encoded`.
242#[no_mangle]
243pub extern "C" fn hash_encoded_argon2d(
244    mem_cost: uint32_t,
245    time_cost: uint32_t,
246    parallelism: uint32_t,
247    pwd: *const uint8_t,
248    pwd_len: size_t,
249    salt: *const uint8_t,
250    salt_len: size_t,
251    hash_len: size_t,
252    encoded: *mut c_char,
253    encoded_len: size_t
254) -> int32_t {
255    hash_encoded(Variant::Argon2d as u32,
256                 DEF_VERSION,
257                 mem_cost,
258                 time_cost,
259                 parallelism,
260                 parallelism,
261                 pwd,
262                 pwd_len,
263                 salt,
264                 salt_len,
265                 ptr::null(),
266                 0,
267                 ptr::null(),
268                 0,
269                 hash_len,
270                 encoded,
271                 encoded_len)
272}
273
274/// Hashes the password using Argon2i and writes the encoded string to `encoded`.
275#[no_mangle]
276pub extern "C" fn hash_encoded_argon2i(
277    mem_cost: uint32_t,
278    time_cost: uint32_t,
279    parallelism: uint32_t,
280    pwd: *const uint8_t,
281    pwd_len: size_t,
282    salt: *const uint8_t,
283    salt_len: size_t,
284    hash_len: size_t,
285    encoded: *mut c_char,
286    encoded_len: size_t
287) -> int32_t {
288    hash_encoded(Variant::Argon2i as u32,
289                 DEF_VERSION,
290                 mem_cost,
291                 time_cost,
292                 parallelism,
293                 parallelism,
294                 pwd,
295                 pwd_len,
296                 salt,
297                 salt_len,
298                 ptr::null(),
299                 0,
300                 ptr::null(),
301                 0,
302                 hash_len,
303                 encoded,
304                 encoded_len)
305}
306
307/// Hashes the password using Argon2id and writes the encoded string to `encoded`.
308#[no_mangle]
309pub extern "C" fn hash_encoded_argon2id(
310    mem_cost: uint32_t,
311    time_cost: uint32_t,
312    parallelism: uint32_t,
313    pwd: *const uint8_t,
314    pwd_len: size_t,
315    salt: *const uint8_t,
316    salt_len: size_t,
317    hash_len: size_t,
318    encoded: *mut c_char,
319    encoded_len: size_t
320) -> int32_t {
321    hash_encoded(Variant::Argon2id as u32,
322                 DEF_VERSION,
323                 mem_cost,
324                 time_cost,
325                 parallelism,
326                 parallelism,
327                 pwd,
328                 pwd_len,
329                 salt,
330                 salt_len,
331                 ptr::null(),
332                 0,
333                 ptr::null(),
334                 0,
335                 hash_len,
336                 encoded,
337                 encoded_len)
338}
339
340/// Hashes the password using default settings and writes the encoded string to `encoded`.
341#[no_mangle]
342pub extern "C" fn hash_encoded_simple(
343    pwd: *const uint8_t,
344    pwd_len: size_t,
345    salt: *const uint8_t,
346    salt_len: size_t,
347    encoded: *mut c_char,
348    encoded_len: size_t
349) -> int32_t {
350    hash_encoded(DEF_VARIANT,
351                 DEF_VERSION,
352                 DEF_MEMORY,
353                 DEF_TIME,
354                 DEF_LANES,
355                 DEF_THREADS,
356                 pwd,
357                 pwd_len,
358                 salt,
359                 salt_len,
360                 ptr::null(),
361                 0,
362                 ptr::null(),
363                 0,
364                 DEF_HASH_LEN,
365                 encoded,
366                 encoded_len)
367}
368
369/// Hashes the password and writes the hash bytes to `out`.
370#[no_mangle]
371pub extern "C" fn hash_raw(
372    variant: uint32_t,
373    version: uint32_t,
374    mem_cost: uint32_t,
375    time_cost: uint32_t,
376    lanes: uint32_t,
377    threads: uint32_t,
378    pwd: *const uint8_t,
379    pwd_len: size_t,
380    salt: *const uint8_t,
381    salt_len: size_t,
382    secret: *const uint8_t,
383    secret_len: size_t,
384    ad: *const uint8_t,
385    ad_len: size_t,
386    out: *mut uint8_t,
387    out_len: size_t
388) -> int32_t {
389    let pwd = match mk_slice(pwd, pwd_len, ReturnValue::PwdPtrMismatch) {
390        Ok(val) => val,
391        Err(err) => return err as i32,
392    };
393
394    let salt = match mk_slice(salt, salt_len, ReturnValue::SaltPtrMismatch) {
395        Ok(val) => val,
396        Err(err) => return err as i32,
397    };
398
399    if out.is_null() {
400        return ReturnValue::OutputPtrNull as i32;
401    }
402
403    let config = match mk_config(variant,
404                                 version,
405                                 mem_cost,
406                                 time_cost,
407                                 lanes,
408                                 threads,
409                                 secret,
410                                 secret_len,
411                                 ad,
412                                 ad_len,
413                                 out_len) {
414        Ok(val) => val,
415        Err(err) => return err as i32,
416    };
417
418    match argon2::hash_raw(pwd, salt, &config) {
419        Ok(vec) => {
420            if vec.len() != out_len {
421                return ReturnValue::EncodingFail as i32;
422            }
423            unsafe {
424                ptr::copy_nonoverlapping(vec.as_ptr(), out, vec.len());
425            }
426            ReturnValue::Ok as i32
427        }
428        Err(err) => ReturnValue::from_error(err) as i32,
429    }
430}
431
432/// Hashes the password using Argon2d and writes the hash bytes to `out`.
433#[no_mangle]
434pub extern "C" fn hash_raw_argon2d(
435    mem_cost: uint32_t,
436    time_cost: uint32_t,
437    parallelism: uint32_t,
438    pwd: *const uint8_t,
439    pwd_len: size_t,
440    salt: *const uint8_t,
441    salt_len: size_t,
442    out: *mut uint8_t,
443    out_len: size_t
444) -> int32_t {
445    hash_raw(Variant::Argon2d as u32,
446             DEF_VERSION,
447             mem_cost,
448             time_cost,
449             parallelism,
450             parallelism,
451             pwd,
452             pwd_len,
453             salt,
454             salt_len,
455             ptr::null(),
456             0,
457             ptr::null(),
458             0,
459             out,
460             out_len)
461}
462
463/// Hashes the password using Argon2i and writes the hash bytes to `out`.
464#[no_mangle]
465pub extern "C" fn hash_raw_argon2i(
466    mem_cost: uint32_t,
467    time_cost: uint32_t,
468    parallelism: uint32_t,
469    pwd: *const uint8_t,
470    pwd_len: size_t,
471    salt: *const uint8_t,
472    salt_len: size_t,
473    out: *mut uint8_t,
474    out_len: size_t
475) -> int32_t {
476    hash_raw(Variant::Argon2i as u32,
477             DEF_VERSION,
478             mem_cost,
479             time_cost,
480             parallelism,
481             parallelism,
482             pwd,
483             pwd_len,
484             salt,
485             salt_len,
486             ptr::null(),
487             0,
488             ptr::null(),
489             0,
490             out,
491             out_len)
492}
493
494/// Hashes the password using Argon2id and writes the hash bytes to `out`.
495#[no_mangle]
496pub extern "C" fn hash_raw_argon2id(
497    mem_cost: uint32_t,
498    time_cost: uint32_t,
499    parallelism: uint32_t,
500    pwd: *const uint8_t,
501    pwd_len: size_t,
502    salt: *const uint8_t,
503    salt_len: size_t,
504    out: *mut uint8_t,
505    out_len: size_t
506) -> int32_t {
507    hash_raw(Variant::Argon2id as u32,
508             DEF_VERSION,
509             mem_cost,
510             time_cost,
511             parallelism,
512             parallelism,
513             pwd,
514             pwd_len,
515             salt,
516             salt_len,
517             ptr::null(),
518             0,
519             ptr::null(),
520             0,
521             out,
522             out_len)
523}
524
525/// Hashes the password using default settings and writes the hash bytes to `out`.
526#[no_mangle]
527pub extern "C" fn hash_raw_simple(
528    pwd: *const uint8_t,
529    pwd_len: size_t,
530    salt: *const uint8_t,
531    salt_len: size_t,
532    out: *mut uint8_t,
533    out_len: size_t
534) -> int32_t {
535    hash_raw(DEF_VARIANT,
536             DEF_VERSION,
537             DEF_MEMORY,
538             DEF_TIME,
539             DEF_LANES,
540             DEF_THREADS,
541             pwd,
542             pwd_len,
543             salt,
544             salt_len,
545             ptr::null(),
546             0,
547             ptr::null(),
548             0,
549             out,
550             out_len)
551}
552
553/// Verifies the password with the encoded string and returns `0` when correct.
554#[no_mangle]
555pub extern "C" fn verify_encoded(
556    encoded: *const c_char,
557    pwd: *const uint8_t,
558    pwd_len: size_t
559) -> int32_t {
560    let encoded = match mk_str(encoded, ReturnValue::DecodingFail) {
561        Ok(val) => val,
562        Err(err) => return err as i32,
563    };
564
565    let pwd = match mk_slice(pwd, pwd_len, ReturnValue::PwdPtrMismatch) {
566        Ok(val) => val,
567        Err(err) => return err as i32,
568    };
569
570    match argon2::verify_encoded(encoded, pwd) {
571        Ok(true) => ReturnValue::Ok as i32,
572        Ok(false) => ReturnValue::VerifyMismatch as i32,
573        Err(err) => ReturnValue::from_error(err) as i32,
574    }
575}
576
577/// Verifies the password and returns `0` when correct.
578#[no_mangle]
579pub extern "C" fn verify_raw(
580    variant: uint32_t,
581    version: uint32_t,
582    mem_cost: uint32_t,
583    time_cost: uint32_t,
584    lanes: uint32_t,
585    threads: uint32_t,
586    pwd: *const uint8_t,
587    pwd_len: size_t,
588    salt: *const uint8_t,
589    salt_len: size_t,
590    secret: *const uint8_t,
591    secret_len: size_t,
592    ad: *const uint8_t,
593    ad_len: size_t,
594    hash: *const uint8_t,
595    hash_len: size_t
596) -> int32_t {
597    let pwd = match mk_slice(pwd, pwd_len, ReturnValue::PwdPtrMismatch) {
598        Ok(val) => val,
599        Err(err) => return err as i32,
600    };
601
602    let salt = match mk_slice(salt, salt_len, ReturnValue::SaltPtrMismatch) {
603        Ok(val) => val,
604        Err(err) => return err as i32,
605    };
606
607    let hash = match mk_slice(hash, hash_len, ReturnValue::HashPtrMismatch) {
608        Ok(val) => val,
609        Err(err) => return err as i32,
610    };
611
612    let config = match mk_config(variant,
613                                 version,
614                                 mem_cost,
615                                 time_cost,
616                                 lanes,
617                                 threads,
618                                 secret,
619                                 secret_len,
620                                 ad,
621                                 ad_len,
622                                 hash_len) {
623        Ok(val) => val,
624        Err(err) => return err as i32,
625    };
626    match argon2::verify_raw(pwd, salt, hash, &config) {
627        Ok(true) => ReturnValue::Ok as i32,
628        Ok(false) => ReturnValue::VerifyMismatch as i32,
629        Err(err) => ReturnValue::from_error(err) as i32,
630    }
631}
632
633/// Verifies the password using Argon2d and returns `0` when correct.
634#[no_mangle]
635pub extern "C" fn verify_raw_argon2d(
636    mem_cost: uint32_t,
637    time_cost: uint32_t,
638    parallelism: uint32_t,
639    pwd: *const uint8_t,
640    pwd_len: size_t,
641    salt: *const uint8_t,
642    salt_len: size_t,
643    hash: *const uint8_t,
644    hash_len: size_t
645) -> int32_t {
646    verify_raw(Variant::Argon2d as u32,
647               DEF_VERSION,
648               mem_cost,
649               time_cost,
650               parallelism,
651               parallelism,
652               pwd,
653               pwd_len,
654               salt,
655               salt_len,
656               ptr::null(),
657               0,
658               ptr::null(),
659               0,
660               hash,
661               hash_len)
662}
663
664/// Verifies the password using Argon2i and returns `0` when correct.
665#[no_mangle]
666pub extern "C" fn verify_raw_argon2i(
667    mem_cost: uint32_t,
668    time_cost: uint32_t,
669    parallelism: uint32_t,
670    pwd: *const uint8_t,
671    pwd_len: size_t,
672    salt: *const uint8_t,
673    salt_len: size_t,
674    hash: *const uint8_t,
675    hash_len: size_t
676) -> int32_t {
677    verify_raw(Variant::Argon2i as u32,
678               DEF_VERSION,
679               mem_cost,
680               time_cost,
681               parallelism,
682               parallelism,
683               pwd,
684               pwd_len,
685               salt,
686               salt_len,
687               ptr::null(),
688               0,
689               ptr::null(),
690               0,
691               hash,
692               hash_len)
693}
694
695/// Verifies the password using Argon2id and returns `0` when correct.
696#[no_mangle]
697pub extern "C" fn verify_raw_argon2id(
698    mem_cost: uint32_t,
699    time_cost: uint32_t,
700    parallelism: uint32_t,
701    pwd: *const uint8_t,
702    pwd_len: size_t,
703    salt: *const uint8_t,
704    salt_len: size_t,
705    hash: *const uint8_t,
706    hash_len: size_t
707) -> int32_t {
708    verify_raw(Variant::Argon2id as u32,
709               DEF_VERSION,
710               mem_cost,
711               time_cost,
712               parallelism,
713               parallelism,
714               pwd,
715               pwd_len,
716               salt,
717               salt_len,
718               ptr::null(),
719               0,
720               ptr::null(),
721               0,
722               hash,
723               hash_len)
724}
725
726/// Verifies the password using default settings and returns `0` when correct.
727#[no_mangle]
728pub extern "C" fn verify_raw_simple(
729    pwd: *const uint8_t,
730    pwd_len: size_t,
731    salt: *const uint8_t,
732    salt_len: size_t,
733    hash: *const uint8_t,
734    hash_len: size_t
735) -> int32_t {
736    verify_raw(DEF_VARIANT,
737               DEF_VERSION,
738               DEF_MEMORY,
739               DEF_TIME,
740               DEF_LANES,
741               DEF_THREADS,
742               pwd,
743               pwd_len,
744               salt,
745               salt_len,
746               ptr::null(),
747               0,
748               ptr::null(),
749               0,
750               hash,
751               hash_len)
752}
753
754fn mk_config<'a>(
755    variant: u32,
756    version: u32,
757    mem_cost: u32,
758    time_cost: u32,
759    lanes: u32,
760    threads: u32,
761    secret: *const uint8_t,
762    secret_len: size_t,
763    ad: *const uint8_t,
764    ad_len: size_t,
765    hash_len: size_t
766) -> Result<Config<'a>, ReturnValue> {
767    let variant = match Variant::from_u32(variant) {
768        Ok(val) => val,
769        Err(err) => return Err(ReturnValue::from_error(err)),
770    };
771
772    let version = match Version::from_u32(version) {
773        Ok(val) => val,
774        Err(err) => return Err(ReturnValue::from_error(err)),
775    };
776
777    let secret = match mk_slice(secret, secret_len, ReturnValue::SecretPtrMismatch) {
778        Ok(val) => val,
779        Err(err) => return Err(err),
780    };
781
782    let ad = match mk_slice(ad, ad_len, ReturnValue::AdPtrMismatch) {
783        Ok(val) => val,
784        Err(err) => return Err(err),
785    };
786
787    Ok(Config {
788        variant: variant,
789        version: version,
790        secret: secret,
791        ad: ad,
792        mem_cost: mem_cost,
793        time_cost: time_cost,
794        lanes: lanes,
795        thread_mode: ThreadMode::from_threads(threads),
796        hash_length: hash_len as u32,
797    })
798}
799
800fn mk_slice<'a>(p: *const uint8_t, len: size_t, err: ReturnValue) -> Result<&'a [u8], ReturnValue> {
801    if p.is_null() && len != 0 {
802        return Err(err);
803    } else {
804        Ok(unsafe { slice::from_raw_parts(p, len) })
805    }
806}
807
808fn mk_str<'a>(p: *const c_char, err: ReturnValue) -> Result<&'a str, ReturnValue> {
809    if p.is_null() {
810        return Err(err);
811    } else {
812        let c_str = unsafe { CStr::from_ptr(p) };
813        match c_str.to_str() {
814            Ok(str) => Ok(str),
815            Err(_) => Err(err),
816        }
817    }
818}
819
820
821#[cfg(test)]
822mod tests {
823
824    use argon2::{Error, ThreadMode, Variant, Version};
825    use std::ptr;
826    use std::ffi::CString;
827    use super::*;
828
829    const ARGON2D_ENC: &'static [u8] = b"$argon2d$v=19$m=4096,t=3,p=1$c29tZXNhbHQ\
830                                         $2+JCoQtY/2x5F0VB9pEVP3xBNguWP1T25Ui0PtZuk8o";
831
832    const ARGON2I_ENC: &'static [u8] = b"$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ\
833                                         $iWh06vD8Fy27wf9npn6FXWiCX4K6pW6Ue1Bnzz07Z8A";
834
835    const ARGON2ID_ENC: &'static [u8] = b"$argon2id$v=19$m=4096,t=3,p=1$c29tZXNhbHQ\
836                                          $qLml5cbqFAO6YxVHhrSBHP0UWdxrIxkNcM8aMX3blzU";
837
838    const ARGON2D_HASH: [u8; 32] = [219, 226, 66, 161, 11, 88, 255, 108, 121, 23, 69, 65, 246,
839                                    145, 21, 63, 124, 65, 54, 11, 150, 63, 84, 246, 229, 72, 180,
840                                    62, 214, 110, 147, 202];
841
842    const ARGON2I_HASH: [u8; 32] = [137, 104, 116, 234, 240, 252, 23, 45, 187, 193, 255, 103, 166,
843                                    126, 133, 93, 104, 130, 95, 130, 186, 165, 110, 148, 123, 80,
844                                    103, 207, 61, 59, 103, 192];
845
846    const ARGON2ID_HASH: [u8; 32] = [168, 185, 165, 229, 198, 234, 20, 3, 186, 99, 21, 71, 134,
847                                     180, 129, 28, 253, 20, 89, 220, 107, 35, 25, 13, 112, 207,
848                                     26, 49, 125, 219, 151, 53];
849
850    const PWD: &'static [u8] = b"password";
851
852    const PWD_INCORRECT: &'static [u8] = b"wrong";
853
854    const SALT: &'static [u8] = b"somesalt";
855
856    const SALT_SHORT: &'static [u8] = b"salt";
857
858    #[test]
859    fn return_value_from_error_returns_correct_value() {
860        let tuples = vec![(Error::OutputTooShort, ReturnValue::OutputTooShort),
861                          (Error::OutputTooLong, ReturnValue::OutputTooLong),
862                          (Error::PwdTooShort, ReturnValue::PwdTooShort),
863                          (Error::PwdTooLong, ReturnValue::PwdTooLong),
864                          (Error::SaltTooShort, ReturnValue::SaltTooShort),
865                          (Error::SaltTooLong, ReturnValue::SaltTooLong),
866                          (Error::AdTooShort, ReturnValue::AdTooShort),
867                          (Error::AdTooLong, ReturnValue::AdTooLong),
868                          (Error::SecretTooShort, ReturnValue::SecretTooShort),
869                          (Error::SecretTooLong, ReturnValue::SecretTooLong),
870                          (Error::TimeTooSmall, ReturnValue::TimeTooSmall),
871                          (Error::TimeTooLarge, ReturnValue::TimeTooLarge),
872                          (Error::MemoryTooLittle, ReturnValue::MemoryTooLittle),
873                          (Error::MemoryTooMuch, ReturnValue::MemoryTooMuch),
874                          (Error::LanesTooFew, ReturnValue::LanesTooFew),
875                          (Error::LanesTooMany, ReturnValue::LanesTooMany),
876                          (Error::IncorrectType, ReturnValue::IncorrectType),
877                          (Error::IncorrectVersion, ReturnValue::IncorrectType),
878                          (Error::DecodingFail, ReturnValue::DecodingFail)];
879        for tuple in tuples {
880            assert_eq!(ReturnValue::from_error(tuple.0), tuple.1);
881        }
882    }
883
884    #[test]
885    fn encoded_len_returns_correct_length() {
886        let expected = 85;
887        let actual = encoded_len(1, 4096, 3, 1, 8, 32);
888        assert_eq!(actual, expected);
889    }
890
891    #[test]
892    fn encoded_len_simple_returns_correct_length() {
893        let expected = 85;
894        let actual = encoded_len_simple(8);
895        assert_eq!(actual, expected);
896    }
897
898    #[test]
899    fn hash_encoded_with_correct_params_returns_ok() {
900        let encoded = CString::new(vec![1u8; 84]).unwrap().into_raw();
901        let result = hash_encoded(1,
902                                  0x13,
903                                  4096,
904                                  3,
905                                  1,
906                                  1,
907                                  PWD.as_ptr(),
908                                  PWD.len(),
909                                  SALT.as_ptr(),
910                                  SALT.len(),
911                                  ptr::null(),
912                                  0,
913                                  ptr::null(),
914                                  0,
915                                  32,
916                                  encoded,
917                                  85);
918        assert_eq!(result, 0);
919
920        let expected = CString::new(ARGON2I_ENC).unwrap();
921        let actual = unsafe { CString::from_raw(encoded) };
922        assert_eq!(actual, expected);
923    }
924
925    #[test]
926    fn hash_encoded_with_too_short_salt_returns_error() {
927        let encoded = CString::new(vec![1u8; 84]).unwrap().into_raw();
928        let result = hash_encoded(1,
929                                  0x13,
930                                  4096,
931                                  3,
932                                  1,
933                                  1,
934                                  PWD.as_ptr(),
935                                  PWD.len(),
936                                  SALT_SHORT.as_ptr(),
937                                  SALT_SHORT.len(),
938                                  ptr::null(),
939                                  0,
940                                  ptr::null(),
941                                  0,
942                                  32,
943                                  encoded,
944                                  85);
945        assert_eq!(result, -6);
946    }
947
948    #[test]
949    fn hash_encoded_argon2d_with_correct_params_returns_ok() {
950        let encoded = CString::new(vec![1u8; 84]).unwrap().into_raw();
951        let result = hash_encoded_argon2d(4096,
952                                          3,
953                                          1,
954                                          PWD.as_ptr(),
955                                          PWD.len(),
956                                          SALT.as_ptr(),
957                                          SALT.len(),
958                                          32,
959                                          encoded,
960                                          85);
961        assert_eq!(result, 0);
962
963        let expected = CString::new(ARGON2D_ENC).unwrap();
964        let actual = unsafe { CString::from_raw(encoded) };
965        assert_eq!(actual, expected);
966    }
967
968    #[test]
969    fn hash_encoded_argon2i_with_correct_params_returns_ok() {
970        let encoded = CString::new(vec![1u8; 84]).unwrap().into_raw();
971        let result = hash_encoded_argon2i(4096,
972                                          3,
973                                          1,
974                                          PWD.as_ptr(),
975                                          PWD.len(),
976                                          SALT.as_ptr(),
977                                          SALT.len(),
978                                          32,
979                                          encoded,
980                                          85);
981        assert_eq!(result, 0);
982
983        let expected = CString::new(ARGON2I_ENC).unwrap();
984        let actual = unsafe { CString::from_raw(encoded) };
985        assert_eq!(actual, expected);
986    }
987
988    #[test]
989    fn hash_encoded_argon2id_with_correct_params_returns_ok() {
990        let encoded = CString::new(vec![1u8; 85]).unwrap().into_raw();
991        let result = hash_encoded_argon2id(4096,
992                                           3,
993                                           1,
994                                           PWD.as_ptr(),
995                                           PWD.len(),
996                                           SALT.as_ptr(),
997                                           SALT.len(),
998                                           32,
999                                           encoded,
1000                                           86);
1001        assert_eq!(result, 0);
1002
1003        let expected = CString::new(ARGON2ID_ENC).unwrap();
1004        let actual = unsafe { CString::from_raw(encoded) };
1005        assert_eq!(actual, expected);
1006    }
1007
1008    #[test]
1009    fn hash_encoded_simple_with_correct_params_returns_ok() {
1010        let encoded = CString::new(vec![1u8; 84]).unwrap().into_raw();
1011        let result =
1012            hash_encoded_simple(PWD.as_ptr(), PWD.len(), SALT.as_ptr(), SALT.len(), encoded, 85);
1013        assert_eq!(result, 0);
1014
1015        let expected = CString::new(ARGON2I_ENC).unwrap();
1016        let actual = unsafe { CString::from_raw(encoded) };
1017        assert_eq!(actual, expected);
1018    }
1019
1020    #[test]
1021    fn hash_raw_with_correct_params_returns_ok() {
1022        let mut out = [0u8; 32];
1023        let result = hash_raw(1,
1024                              0x13,
1025                              4096,
1026                              3,
1027                              1,
1028                              1,
1029                              PWD.as_ptr(),
1030                              PWD.len(),
1031                              SALT.as_ptr(),
1032                              SALT.len(),
1033                              ptr::null(),
1034                              0,
1035                              ptr::null(),
1036                              0,
1037                              out.as_mut_ptr(),
1038                              out.len());
1039        assert_eq!(result, 0);
1040        assert_eq!(out, ARGON2I_HASH);
1041    }
1042
1043    #[test]
1044    fn hash_raw_with_too_short_salt_returns_error() {
1045        let mut out = [0u8; 32];
1046        let result = hash_raw(1,
1047                              0x13,
1048                              4096,
1049                              3,
1050                              1,
1051                              1,
1052                              PWD.as_ptr(),
1053                              PWD.len(),
1054                              SALT_SHORT.as_ptr(),
1055                              SALT_SHORT.len(),
1056                              ptr::null(),
1057                              0,
1058                              ptr::null(),
1059                              0,
1060                              out.as_mut_ptr(),
1061                              out.len());
1062        assert_eq!(result, -6);
1063    }
1064
1065    #[test]
1066    fn hash_raw_argon2d_with_correct_params_returns_ok() {
1067        let mut out = [0u8; 32];
1068        let result = hash_raw_argon2d(4096,
1069                                      3,
1070                                      1,
1071                                      PWD.as_ptr(),
1072                                      PWD.len(),
1073                                      SALT.as_ptr(),
1074                                      SALT.len(),
1075                                      out.as_mut_ptr(),
1076                                      out.len());
1077        assert_eq!(result, 0);
1078        assert_eq!(out, ARGON2D_HASH);
1079    }
1080
1081    #[test]
1082    fn hash_raw_argon2i_with_correct_params_returns_ok() {
1083        let mut out = [0u8; 32];
1084        let result = hash_raw_argon2i(4096,
1085                                      3,
1086                                      1,
1087                                      PWD.as_ptr(),
1088                                      PWD.len(),
1089                                      SALT.as_ptr(),
1090                                      SALT.len(),
1091                                      out.as_mut_ptr(),
1092                                      out.len());
1093        assert_eq!(result, 0);
1094        assert_eq!(out, ARGON2I_HASH);
1095    }
1096
1097    #[test]
1098    fn hash_raw_argon2id_with_correct_params_returns_ok() {
1099        let mut out = [0u8; 32];
1100        let result = hash_raw_argon2id(4096,
1101                                       3,
1102                                       1,
1103                                       PWD.as_ptr(),
1104                                       PWD.len(),
1105                                       SALT.as_ptr(),
1106                                       SALT.len(),
1107                                       out.as_mut_ptr(),
1108                                       out.len());
1109        assert_eq!(result, 0);
1110        assert_eq!(out, ARGON2ID_HASH);
1111    }
1112
1113    #[test]
1114    fn hash_raw_simple_with_correct_params_returns_ok() {
1115        let mut out = [0u8; 32];
1116        let result = hash_raw_simple(PWD.as_ptr(),
1117                                     PWD.len(),
1118                                     SALT.as_ptr(),
1119                                     SALT.len(),
1120                                     out.as_mut_ptr(),
1121                                     out.len());
1122        assert_eq!(result, 0);
1123        assert_eq!(out, ARGON2I_HASH);
1124    }
1125
1126    #[test]
1127    fn verify_encoded_with_correct_password_returns_ok() {
1128        let encoded = CString::new(ARGON2I_ENC).unwrap();
1129        let result = verify_encoded(encoded.as_ptr(), PWD.as_ptr(), PWD.len());
1130        assert_eq!(result, 0);
1131    }
1132
1133    #[test]
1134    fn verify_encoded_with_incorrect_password_returns_error() {
1135        let encoded = CString::new(ARGON2I_ENC).unwrap();
1136        let result = verify_encoded(encoded.as_ptr(), PWD_INCORRECT.as_ptr(), PWD_INCORRECT.len());
1137        assert_eq!(result, -35);
1138    }
1139
1140    #[test]
1141    fn verify_raw_with_correct_password_returns_ok() {
1142        let result = verify_raw(1,
1143                                0x13,
1144                                4096,
1145                                3,
1146                                1,
1147                                1,
1148                                PWD.as_ptr(),
1149                                PWD.len(),
1150                                SALT.as_ptr(),
1151                                SALT.len(),
1152                                ptr::null(),
1153                                0,
1154                                ptr::null(),
1155                                0,
1156                                ARGON2I_HASH.as_ptr(),
1157                                ARGON2I_HASH.len());
1158        assert_eq!(result, 0);
1159    }
1160
1161    #[test]
1162    fn verify_raw_with_incorrect_password_returns_error() {
1163        let result = verify_raw(1,
1164                                0x13,
1165                                4096,
1166                                3,
1167                                1,
1168                                1,
1169                                PWD_INCORRECT.as_ptr(),
1170                                PWD_INCORRECT.len(),
1171                                SALT.as_ptr(),
1172                                SALT.len(),
1173                                ptr::null(),
1174                                0,
1175                                ptr::null(),
1176                                0,
1177                                ARGON2I_HASH.as_ptr(),
1178                                ARGON2I_HASH.len());
1179        assert_eq!(result, -35);
1180    }
1181
1182    #[test]
1183    fn verify_raw_with_too_sort_salt_returns_error() {
1184        let result = verify_raw(1,
1185                                0x13,
1186                                4096,
1187                                3,
1188                                1,
1189                                1,
1190                                PWD.as_ptr(),
1191                                PWD.len(),
1192                                SALT_SHORT.as_ptr(),
1193                                SALT_SHORT.len(),
1194                                ptr::null(),
1195                                0,
1196                                ptr::null(),
1197                                0,
1198                                ARGON2I_HASH.as_ptr(),
1199                                ARGON2I_HASH.len());
1200        assert_eq!(result, -6);
1201    }
1202
1203    #[test]
1204    fn verify_raw_argon2d_with_correct_password_returns_ok() {
1205        let result = verify_raw_argon2d(4096,
1206                                        3,
1207                                        1,
1208                                        PWD.as_ptr(),
1209                                        PWD.len(),
1210                                        SALT.as_ptr(),
1211                                        SALT.len(),
1212                                        ARGON2D_HASH.as_ptr(),
1213                                        ARGON2D_HASH.len());
1214        assert_eq!(result, 0);
1215    }
1216
1217    #[test]
1218    fn verify_raw_argon2i_with_correct_password_returns_ok() {
1219        let result = verify_raw_argon2i(4096,
1220                                        3,
1221                                        1,
1222                                        PWD.as_ptr(),
1223                                        PWD.len(),
1224                                        SALT.as_ptr(),
1225                                        SALT.len(),
1226                                        ARGON2I_HASH.as_ptr(),
1227                                        ARGON2I_HASH.len());
1228        assert_eq!(result, 0);
1229    }
1230
1231    #[test]
1232    fn verify_raw_argon2id_with_correct_password_returns_ok() {
1233        let result = verify_raw_argon2id(4096,
1234                                         3,
1235                                         1,
1236                                         PWD.as_ptr(),
1237                                         PWD.len(),
1238                                         SALT.as_ptr(),
1239                                         SALT.len(),
1240                                         ARGON2ID_HASH.as_ptr(),
1241                                         ARGON2ID_HASH.len());
1242        assert_eq!(result, 0);
1243    }
1244
1245    #[test]
1246    fn verify_raw_simple_with_correct_password_returns_ok() {
1247        let result = verify_raw_simple(PWD.as_ptr(),
1248                                       PWD.len(),
1249                                       SALT.as_ptr(),
1250                                       SALT.len(),
1251                                       ARGON2I_HASH.as_ptr(),
1252                                       ARGON2I_HASH.len());
1253        assert_eq!(result, 0);
1254    }
1255
1256    #[test]
1257    fn mk_config_with_correct_data_returns_correct_config() {
1258        let variant = 1;
1259        let version = 0x13;
1260        let mem_cost = 1024;
1261        let time_cost = 2;
1262        let lanes = 4;
1263        let threads = 4;
1264        let secret = b"secret";
1265        let ad = b"ad";
1266        let hash_len = 32;
1267        let result = mk_config(variant,
1268                               version,
1269                               mem_cost,
1270                               time_cost,
1271                               lanes,
1272                               threads,
1273                               secret.as_ptr(),
1274                               secret.len(),
1275                               ad.as_ptr(),
1276                               ad.len(),
1277                               hash_len);
1278        assert!(result.is_ok());
1279
1280        let config = result.unwrap();
1281        assert_eq!(config.variant, Variant::Argon2i);
1282        assert_eq!(config.version, Version::Version13);
1283        assert_eq!(config.mem_cost, mem_cost);
1284        assert_eq!(config.time_cost, time_cost);
1285        assert_eq!(config.lanes, lanes);
1286        assert_eq!(config.thread_mode, ThreadMode::Parallel);
1287        assert_eq!(config.secret, secret);
1288        assert_eq!(config.ad, ad);
1289        assert_eq!(config.hash_length, hash_len as u32);
1290    }
1291
1292    #[test]
1293    fn mk_config_with_incorrect_variant_returns_error() {
1294        let variant = 3;
1295        let secret = b"secret";
1296        let ad = b"ad";
1297        let result = mk_config(variant,
1298                               0x13,
1299                               1024,
1300                               2,
1301                               4,
1302                               4,
1303                               secret.as_ptr(),
1304                               secret.len(),
1305                               ad.as_ptr(),
1306                               ad.len(),
1307                               32);
1308        assert_eq!(result, Err(ReturnValue::IncorrectType));
1309    }
1310
1311    #[test]
1312    fn mk_config_with_incorrect_version_returns_error() {
1313        let version = 0;
1314        let secret = b"secret";
1315        let ad = b"ad";
1316        let result = mk_config(1,
1317                               version,
1318                               1024,
1319                               2,
1320                               4,
1321                               4,
1322                               secret.as_ptr(),
1323                               secret.len(),
1324                               ad.as_ptr(),
1325                               ad.len(),
1326                               32);
1327        assert_eq!(result, Err(ReturnValue::IncorrectType));
1328    }
1329
1330    #[test]
1331    fn mk_config_with_incorrect_secret_returns_error() {
1332        let ad = b"ad";
1333        let result = mk_config(1, 0x13, 1024, 2, 4, 4, ptr::null(), 1, ad.as_ptr(), ad.len(), 32);
1334        assert_eq!(result, Err(ReturnValue::SecretPtrMismatch));
1335    }
1336
1337    #[test]
1338    fn mk_config_with_incorrect_ad_returns_error() {
1339        let secret = b"secret";
1340        let result =
1341            mk_config(1, 0x13, 1024, 2, 4, 4, secret.as_ptr(), secret.len(), ptr::null(), 1, 32);
1342        assert_eq!(result, Err(ReturnValue::AdPtrMismatch));
1343    }
1344
1345    #[test]
1346    fn mk_slice_with_null_pointer_returns_empty_slice() {
1347        let expected: Result<&[u8], ReturnValue> = Ok(&[]);
1348        let actual = mk_slice(ptr::null(), 0, ReturnValue::Ok);
1349        assert_eq!(actual, expected);
1350    }
1351
1352    #[test]
1353    fn mk_slice_with_correct_pointer_returns_slice() {
1354        let slice = b"test";
1355        let expected: Result<&[u8], ReturnValue> = Ok(slice);
1356        let actual = mk_slice(slice.as_ptr(), slice.len(), ReturnValue::Ok);
1357        assert_eq!(actual, expected);
1358    }
1359
1360    #[test]
1361    fn mk_slice_with_incorrect_pointer_returns_error() {
1362        let expected = Err(ReturnValue::PwdPtrMismatch);
1363        let actual = mk_slice(ptr::null(), 1, ReturnValue::PwdPtrMismatch);
1364        assert_eq!(actual, expected);
1365    }
1366
1367    #[test]
1368    fn mk_str_with_null_pointer_returns_error() {
1369        let expected = Err(ReturnValue::DecodingFail);
1370        let actual = mk_str(ptr::null(), ReturnValue::DecodingFail);
1371        assert_eq!(actual, expected);
1372    }
1373
1374    #[test]
1375    fn mk_str_with_correct_pointer_returns_str() {
1376        let str = "String";
1377        let c_str = CString::new(str).unwrap();
1378        let expected = Ok(str);
1379        let actual = mk_str(c_str.as_ptr(), ReturnValue::Ok);
1380        assert_eq!(actual, expected);
1381    }
1382}