argon2/
argon2.rs

1// Copyright (c) 2017 Martijn Rijkeboer <[email protected]>
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
9use crate::config::Config;
10use crate::context::Context;
11use crate::core;
12use crate::encoding;
13use crate::memory::Memory;
14use crate::result::Result;
15use crate::thread_mode::ThreadMode;
16use crate::variant::Variant;
17use crate::version::Version;
18
19use constant_time_eq::constant_time_eq;
20
21/// Returns the length of the encoded string.
22///
23/// # Remarks
24///
25/// The length is **one** less that the original C version, since no null
26/// terminator is used.
27///
28/// # Examples
29///
30/// ```rust
31/// use argon2::{self, Variant};
32///
33/// let variant = Variant::Argon2i;
34/// let mem = 4096;
35/// let time = 10;
36/// let parallelism = 10;
37/// let salt_len = 8;
38/// let hash_len = 32;
39/// let enc_len = argon2::encoded_len(variant, mem, time, parallelism, salt_len, hash_len);
40/// assert_eq!(enc_len, 86);
41/// ```
42#[rustfmt::skip]
43pub fn encoded_len(
44    variant: Variant,
45    mem_cost: u32,
46    time_cost: u32,
47    parallelism: u32,
48    salt_len: u32,
49    hash_len: u32
50) -> u32 {
51    ("$$v=$m=,t=,p=$$".len() as u32)  +
52    (variant.as_lowercase_str().len() as u32) +
53    encoding::num_len(Version::default().as_u32()) +
54    encoding::num_len(mem_cost) +
55    encoding::num_len(time_cost) +
56    encoding::num_len(parallelism) +
57    encoding::base64_len(salt_len) +
58    encoding::base64_len(hash_len)
59}
60
61/// Hashes the password and returns the encoded hash.
62///
63/// # Examples
64///
65/// Create an encoded hash with the default configuration:
66///
67/// ```
68/// use argon2::{self, Config};
69///
70/// let pwd = b"password";
71/// let salt = b"somesalt";
72/// let config = Config::default();
73/// let encoded = argon2::hash_encoded(pwd, salt, &config).unwrap();
74/// ```
75///
76///
77/// Create an Argon2d encoded hash with 4 lanes and parallel execution:
78///
79/// ```
80/// use argon2::{self, Config, ThreadMode, Variant};
81///
82/// let pwd = b"password";
83/// let salt = b"somesalt";
84/// let mut config = Config::default();
85/// config.variant = Variant::Argon2d;
86#[cfg_attr(feature = "crossbeam-utils", doc = "config.lanes = 4;")]
87#[cfg_attr(
88    feature = "crossbeam-utils",
89    doc = "config.thread_mode = ThreadMode::Parallel;"
90)]
91#[cfg_attr(not(feature = "crossbeam-utils"), doc = "config.lanes = 1;")]
92#[cfg_attr(
93    not(feature = "crossbeam-utils"),
94    doc = "config.thread_mode = ThreadMode::Sequential;"
95)]
96/// let encoded = argon2::hash_encoded(pwd, salt, &config).unwrap();
97/// ```
98pub fn hash_encoded(pwd: &[u8], salt: &[u8], config: &Config) -> Result<String> {
99    let context = Context::new(config.clone(), pwd, salt)?;
100    let hash = run(&context);
101    let encoded = encoding::encode_string(&context, &hash);
102    Ok(encoded)
103}
104
105/// Hashes the password and returns the hash as a vector.
106///
107/// # Examples
108///
109/// Create a hash with the default configuration:
110///
111/// ```
112/// use argon2::{self, Config};
113///
114/// let pwd = b"password";
115/// let salt = b"somesalt";
116/// let config = Config::default();
117/// let vec = argon2::hash_raw(pwd, salt, &config).unwrap();
118/// ```
119///
120///
121/// Create an Argon2d hash with 4 lanes and parallel execution:
122///
123/// ```
124/// use argon2::{self, Config, ThreadMode, Variant};
125///
126/// let pwd = b"password";
127/// let salt = b"somesalt";
128/// let mut config = Config::default();
129/// config.variant = Variant::Argon2d;
130#[cfg_attr(feature = "crossbeam-utils", doc = "config.lanes = 4;")]
131#[cfg_attr(
132    feature = "crossbeam-utils",
133    doc = "config.thread_mode = ThreadMode::Parallel;"
134)]
135#[cfg_attr(not(feature = "crossbeam-utils"), doc = "config.lanes = 1;")]
136#[cfg_attr(
137    not(feature = "crossbeam-utils"),
138    doc = "config.thread_mode = ThreadMode::Sequential;"
139)]
140/// let vec = argon2::hash_raw(pwd, salt, &config).unwrap();
141/// ```
142pub fn hash_raw(pwd: &[u8], salt: &[u8], config: &Config) -> Result<Vec<u8>> {
143    let context = Context::new(config.clone(), pwd, salt)?;
144    let hash = run(&context);
145    Ok(hash)
146}
147
148/// Verifies the password with the encoded hash.
149///
150/// # Examples
151///
152/// ```
153/// use argon2;
154///
155/// let enc = "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ\
156///            $iWh06vD8Fy27wf9npn6FXWiCX4K6pW6Ue1Bnzz07Z8A";
157/// let pwd = b"password";
158/// let res = argon2::verify_encoded(enc, pwd).unwrap();
159/// assert!(res);
160/// ```
161pub fn verify_encoded(encoded: &str, pwd: &[u8]) -> Result<bool> {
162    verify_encoded_ext(encoded, pwd, &[], &[])
163}
164
165/// Verifies the password with the encoded hash, secret and associated data.
166///
167/// # Examples
168///
169/// ```
170/// use argon2;
171///
172/// let enc = "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ\
173///            $OlcSvlN20Lz43sK3jhCJ9K04oejhiY0AmI+ck6nuETo";
174/// let pwd = b"password";
175/// let secret = b"secret";
176/// let ad = b"ad";
177/// let res = argon2::verify_encoded_ext(enc, pwd, secret, ad).unwrap();
178/// assert!(res);
179/// ```
180pub fn verify_encoded_ext(encoded: &str, pwd: &[u8], secret: &[u8], ad: &[u8]) -> Result<bool> {
181    let decoded = encoding::decode_string(encoded)?;
182    let threads = if cfg!(feature = "crossbeam-utils") {
183        decoded.parallelism
184    } else {
185        1
186    };
187    let config = Config {
188        variant: decoded.variant,
189        version: decoded.version,
190        mem_cost: decoded.mem_cost,
191        time_cost: decoded.time_cost,
192        lanes: decoded.parallelism,
193        thread_mode: ThreadMode::from_threads(threads),
194        secret,
195        ad,
196        hash_length: decoded.hash.len() as u32,
197    };
198    verify_raw(pwd, &decoded.salt, &decoded.hash, &config)
199}
200
201/// Verifies the password with the supplied configuration.
202///
203/// # Examples
204///
205/// ```
206/// use argon2::{self, Config};
207///
208/// let pwd = b"password";
209/// let salt = b"somesalt";
210/// let hash = &[158, 135, 137, 200, 180, 40, 52, 34, 10, 252, 0, 8, 90, 199,
211///              58, 204, 48, 134, 81, 33, 105, 148, 171, 191, 221, 214, 155,
212///              37, 146, 3, 46, 253];
213/// let config = Config::rfc9106_low_mem();
214/// let res = argon2::verify_raw(pwd, salt, hash, &config).unwrap();
215/// assert!(res);
216/// ```
217pub fn verify_raw(pwd: &[u8], salt: &[u8], hash: &[u8], config: &Config) -> Result<bool> {
218    let config = Config {
219        hash_length: hash.len() as u32,
220        ..config.clone()
221    };
222    let context = Context::new(config, pwd, salt)?;
223    let calculated_hash = run(&context);
224    Ok(constant_time_eq(hash, &calculated_hash))
225}
226
227fn run(context: &Context) -> Vec<u8> {
228    let mut memory = Memory::new(context.config.lanes, context.lane_length);
229    core::initialize(context, &mut memory);
230    // SAFETY: `memory` is constructed from `context`.
231    unsafe { core::fill_memory_blocks(context, &mut memory) };
232    core::finalize(context, &memory)
233}
234
235#[cfg(test)]
236mod tests {
237    use super::*;
238
239    #[test]
240    fn single_thread_verification_multi_lane_hash() {
241        let hash = "$argon2i$v=19$m=4096,t=3,p=4$YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo$\
242                    BvBk2OaSofBHfbrUW61nHrWB/43xgfs/QJJ5DkMAd8I";
243        let res = verify_encoded(hash, b"foo").unwrap();
244        assert!(res);
245    }
246
247    #[test]
248    fn test_argon2id_for_miri() {
249        let hash = "$argon2id$v=19$m=256,t=2,p=16$c29tZXNhbHQ$\
250                    0gasyPnKXiBHQ5bft/bd4jrmy2DdtrLTX3JR9co7fRY";
251        let res = verify_encoded(hash, b"password").unwrap();
252        assert!(res);
253    }
254
255    #[test]
256    fn test_argon2id_for_miri_2() {
257        let hash = "$argon2id$v=19$m=512,t=2,p=8$c29tZXNhbHQ$\
258                    qgW4yz2jO7oklapDpVwzUYgfDLzfwkppGTvhRDDBjkY";
259        let res = verify_encoded(hash, b"password").unwrap();
260        assert!(res);
261    }
262
263    #[test]
264    fn test_argon2d_for_miri() {
265        let hash = "$argon2d$v=19$m=256,t=2,p=16$c29tZXNhbHQ$\
266                    doW5kZ/0cTwqwbYTwr9JD0wNwy3tMyJMMk9ojGsC8bk";
267        let res = verify_encoded(hash, b"password").unwrap();
268        assert!(res);
269    }
270
271    #[test]
272    fn test_argon2i_for_miri() {
273        let hash = "$argon2i$v=19$m=256,t=2,p=16$c29tZXNhbHQ$\
274                    c1suSp12ZBNLSuyhD8pJriM2r5jP2kgZ5QdDAk3+HaY";
275        let res = verify_encoded(hash, b"password").unwrap();
276        assert!(res);
277    }
278}