├── .gitignore ├── .travis.yml ├── src ├── result.rs ├── decoded.rs ├── thread_mode.rs ├── common.rs ├── version.rs ├── lib.rs ├── error.rs ├── block.rs ├── variant.rs ├── memory.rs ├── context.rs ├── argon2.rs ├── config.rs ├── encoding.rs └── core.rs ├── Cargo.toml ├── LICENSE-MIT ├── README.md ├── CHANGELOG.md ├── LICENSE-APACHE └── tests └── integration_test.rs /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | target 3 | Cargo.lock 4 | .vscode 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - beta 5 | - nightly 6 | matrix: 7 | allow_failures: 8 | - rust: nightly 9 | script: 10 | - cargo build --verbose 11 | - cargo test --verbose 12 | - cargo build --release --verbose 13 | - cargo test --release --test integration_test --verbose -- --test-threads=2 14 | -------------------------------------------------------------------------------- /src/result.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Martijn Rijkeboer 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::error::Error; 10 | use std::result; 11 | 12 | /// A specialized result type for Argon2 operations. 13 | pub type Result = result::Result; 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-argon2" 3 | version = "3.0.0" 4 | authors = ["Martijn Rijkeboer "] 5 | edition = "2021" 6 | license = "MIT/Apache-2.0" 7 | description = "Rust implementation of the Argon2 password hashing function." 8 | documentation = "https://docs.sru-systems.com/rust-argon2/3.0.0/argon2/" 9 | homepage = "https://github.com/sru-systems/rust-argon2" 10 | repository = "https://github.com/sru-systems/rust-argon2" 11 | readme = "README.md" 12 | keywords = ["argon2", "argon2d", "argon2i", "hash", "password"] 13 | 14 | [lib] 15 | name = "argon2" 16 | 17 | [features] 18 | default = ["crossbeam-utils"] 19 | 20 | [dependencies] 21 | base64 = "0.22" 22 | blake2b_simd = "1.0" 23 | constant_time_eq = "0.4.2" 24 | crossbeam-utils = { version = "0.8", optional = true } 25 | serde = { version = "1.0", optional = true, features=["derive"] } 26 | 27 | [dev-dependencies] 28 | hex = "0.4" 29 | -------------------------------------------------------------------------------- /src/decoded.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Martijn Rijkeboer 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::variant::Variant; 10 | use crate::version::Version; 11 | 12 | /// Structure that contains the decoded data. 13 | #[derive(Debug, Eq, PartialEq)] 14 | pub struct Decoded { 15 | /// The variant. 16 | pub variant: Variant, 17 | 18 | /// The version. 19 | pub version: Version, 20 | 21 | /// The amount of memory requested (KiB). 22 | pub mem_cost: u32, 23 | 24 | /// The number of passes. 25 | pub time_cost: u32, 26 | 27 | /// The parallelism. 28 | pub parallelism: u32, 29 | 30 | /// The salt. 31 | pub salt: Vec, 32 | 33 | /// The hash. 34 | pub hash: Vec, 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT license. 2 | 3 | Copyright (c) 2017 Martijn Rijkeboer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/thread_mode.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Xidorn Quan 2 | // Copyright (c) 2017 Martijn Rijkeboer 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | /// The thread mode used to perform the hashing. 11 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 12 | pub enum ThreadMode { 13 | /// Run in one thread. 14 | Sequential, 15 | 16 | #[cfg(feature = "crossbeam-utils")] 17 | /// Run in the same number of threads as the number of lanes. 18 | Parallel, 19 | } 20 | 21 | impl ThreadMode { 22 | #[cfg(feature = "crossbeam-utils")] 23 | /// Create a thread mode from the threads count. 24 | pub fn from_threads(threads: u32) -> ThreadMode { 25 | if threads > 1 { 26 | ThreadMode::Parallel 27 | } else { 28 | ThreadMode::Sequential 29 | } 30 | } 31 | 32 | #[cfg(not(feature = "crossbeam-utils"))] 33 | pub fn from_threads(threads: u32) -> ThreadMode { 34 | assert_eq!(threads, 1); 35 | Self::default() 36 | } 37 | } 38 | 39 | impl Default for ThreadMode { 40 | fn default() -> ThreadMode { 41 | ThreadMode::Sequential 42 | } 43 | } 44 | 45 | #[cfg(test)] 46 | mod tests { 47 | 48 | use crate::thread_mode::ThreadMode; 49 | 50 | #[test] 51 | fn default_returns_correct_thread_mode() { 52 | assert_eq!(ThreadMode::default(), ThreadMode::Sequential); 53 | } 54 | 55 | #[cfg(feature = "crossbeam-utils")] 56 | #[test] 57 | fn from_threads_returns_correct_thread_mode() { 58 | assert_eq!(ThreadMode::from_threads(0), ThreadMode::Sequential); 59 | assert_eq!(ThreadMode::from_threads(1), ThreadMode::Sequential); 60 | assert_eq!(ThreadMode::from_threads(2), ThreadMode::Parallel); 61 | assert_eq!(ThreadMode::from_threads(10), ThreadMode::Parallel); 62 | assert_eq!(ThreadMode::from_threads(100), ThreadMode::Parallel); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust-argon2 2 | 3 | Rust library for hashing passwords using 4 | [Argon2](https://github.com/P-H-C/phc-winner-argon2), the password-hashing 5 | function that won the 6 | [Password Hashing Competition (PHC)](https://password-hashing.net). 7 | 8 | ## Usage 9 | 10 | To use `rust-argon2`, add the following to your Cargo.toml: 11 | 12 | ```toml 13 | [dependencies] 14 | rust-argon2 = "3.0" 15 | ``` 16 | 17 | And the following to your crate root: 18 | 19 | ```rust 20 | extern crate argon2; 21 | ``` 22 | 23 | 24 | ## Examples 25 | 26 | Create a password hash using the defaults and verify it: 27 | 28 | ```rust 29 | use argon2::{self, Config}; 30 | 31 | let password = b"password"; 32 | let salt = b"randomsalt"; 33 | let config = Config::default(); 34 | let hash = argon2::hash_encoded(password, salt, &config).unwrap(); 35 | let matches = argon2::verify_encoded(&hash, password).unwrap(); 36 | assert!(matches); 37 | ``` 38 | 39 | Create a password hash with custom settings and verify it: 40 | 41 | ```rust 42 | use argon2::{self, Config, ThreadMode, Variant, Version}; 43 | 44 | let password = b"password"; 45 | let salt = b"othersalt"; 46 | let config = Config { 47 | variant: Variant::Argon2i, 48 | version: Version::Version13, 49 | mem_cost: 65536, 50 | time_cost: 10, 51 | lanes: 4, 52 | thread_mode: ThreadMode::Parallel, 53 | secret: &[], 54 | ad: &[], 55 | hash_length: 32 56 | }; 57 | let hash = argon2::hash_encoded(password, salt, &config).unwrap(); 58 | let matches = argon2::verify_encoded(&hash, password).unwrap(); 59 | assert!(matches); 60 | ``` 61 | 62 | 63 | ## Limitations 64 | 65 | This crate has the same limitation as the `blake2-rfc` crate that it uses. 66 | It does not attempt to clear potentially sensitive data from its work 67 | memory. To do so correctly without a heavy performance penalty would 68 | require help from the compiler. It's better to not attempt to do so than to 69 | present a false assurance. 70 | 71 | This version uses the standard implementation and does not yet implement 72 | optimizations. Therefore, it is not the fastest implementation available. 73 | 74 | 75 | ## License 76 | 77 | Rust-argon2 is dual licensed under the [MIT](LICENSE-MIT) and 78 | [Apache 2.0](LICENSE-APACHE) licenses, the same licenses as the Rust compiler. 79 | 80 | 81 | ## Contributions 82 | 83 | Contributions are welcome. By submitting a pull request you are agreeing to 84 | make you work available under the license terms of the Rust-argon2 project. 85 | 86 | -------------------------------------------------------------------------------- /src/common.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Martijn Rijkeboer 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | /// Minimum number of lanes (degree of parallelism). 10 | pub const MIN_LANES: u32 = 1; 11 | 12 | /// Maximum number of lanes (degree of parallelism). 13 | pub const MAX_LANES: u32 = 0x00FF_FFFF; 14 | 15 | /// Number of synchronization points between lanes per pass. 16 | pub const SYNC_POINTS: u32 = 4; 17 | 18 | /// Minimum digest size in bytes. 19 | pub const MIN_HASH_LENGTH: u32 = 4; 20 | 21 | /// Maximum digest size in bytes. 22 | pub const MAX_HASH_LENGTH: u32 = 0xFFFF_FFFF; 23 | 24 | /// Minimum number of memory blocks (each of BLOCK_SIZE bytes). 25 | pub const MIN_MEMORY: u32 = 2 * SYNC_POINTS; 26 | 27 | /// Maximum number of memory blocks (each of BLOCK_SIZE bytes). 28 | #[cfg(target_pointer_width = "32")] 29 | pub const MAX_MEMORY: u32 = 0x200000; 30 | #[cfg(target_pointer_width = "64")] 31 | pub const MAX_MEMORY: u32 = 0xFFFF_FFFF; 32 | 33 | /// Minimum number of passes 34 | pub const MIN_TIME: u32 = 1; 35 | 36 | /// Maximum number of passes. 37 | pub const MAX_TIME: u32 = 0xFFFF_FFFF; 38 | 39 | /// Minimum password length in bytes. 40 | pub const MIN_PWD_LENGTH: u32 = 0; 41 | 42 | /// Maximum password length in bytes. 43 | pub const MAX_PWD_LENGTH: u32 = 0xFFFF_FFFF; 44 | 45 | /// Minimum associated data length in bytes. 46 | pub const MIN_AD_LENGTH: u32 = 0; 47 | 48 | /// Maximum associated data length in bytes. 49 | pub const MAX_AD_LENGTH: u32 = 0xFFFF_FFFF; 50 | 51 | /// Minimum salt length in bytes. 52 | pub const MIN_SALT_LENGTH: u32 = 8; 53 | 54 | /// Maximum salt length in bytes. 55 | pub const MAX_SALT_LENGTH: u32 = 0xFFFF_FFFF; 56 | 57 | /// Minimum key length in bytes. 58 | pub const MIN_SECRET_LENGTH: u32 = 0; 59 | 60 | /// Maximum key length in bytes. 61 | pub const MAX_SECRET_LENGTH: u32 = 0xFFFF_FFFF; 62 | 63 | /// Memory block size in bytes. 64 | pub const BLOCK_SIZE: usize = 1024; 65 | 66 | /// Number of quad words in a block. 67 | pub const QWORDS_IN_BLOCK: usize = BLOCK_SIZE / 8; 68 | 69 | /// Number of pseudo-random values generated by one call to Blake in Argon2i 70 | /// to generate reference block positions. 71 | pub const ADDRESSES_IN_BLOCK: u32 = 128; 72 | 73 | /// Pre-hashing digest length. 74 | pub const PREHASH_DIGEST_LENGTH: usize = 64; 75 | 76 | /// Pre-hashing digest length with extension. 77 | pub const PREHASH_SEED_LENGTH: usize = 72; 78 | 79 | /// Blake2b output length in bytes. 80 | pub const BLAKE2B_OUT_LENGTH: usize = 64; 81 | -------------------------------------------------------------------------------- /src/version.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Martijn Rijkeboer 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::error::Error; 10 | use crate::result::Result; 11 | use std::fmt; 12 | 13 | /// The Argon2 version. 14 | #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] 15 | pub enum Version { 16 | /// Version 0x10. 17 | Version10 = 0x10, 18 | 19 | /// Version 0x13 (Recommended). 20 | Version13 = 0x13, 21 | } 22 | 23 | impl Version { 24 | /// Gets the u32 representation of the version. 25 | pub fn as_u32(&self) -> u32 { 26 | *self as u32 27 | } 28 | 29 | /// Attempts to create a version from a string slice. 30 | pub fn from_str(str: &str) -> Result { 31 | match str { 32 | "16" => Ok(Version::Version10), 33 | "19" => Ok(Version::Version13), 34 | _ => Err(Error::DecodingFail), 35 | } 36 | } 37 | 38 | /// Attempts to create a version from an u32. 39 | pub fn from_u32(val: u32) -> Result { 40 | match val { 41 | 0x10 => Ok(Version::Version10), 42 | 0x13 => Ok(Version::Version13), 43 | _ => Err(Error::IncorrectVersion), 44 | } 45 | } 46 | } 47 | 48 | impl Default for Version { 49 | fn default() -> Version { 50 | Version::Version13 51 | } 52 | } 53 | 54 | impl fmt::Display for Version { 55 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 56 | write!(f, "{}", self.as_u32()) 57 | } 58 | } 59 | 60 | #[cfg(test)] 61 | mod tests { 62 | 63 | use crate::error::Error; 64 | use crate::version::Version; 65 | 66 | #[test] 67 | fn as_u32_returns_correct_u32() { 68 | assert_eq!(Version::Version10.as_u32(), 0x10); 69 | assert_eq!(Version::Version13.as_u32(), 0x13); 70 | } 71 | 72 | #[test] 73 | fn default_returns_correct_version() { 74 | assert_eq!(Version::default(), Version::Version13); 75 | } 76 | 77 | #[test] 78 | fn display_returns_correct_string() { 79 | assert_eq!(format!("{}", Version::Version10), "16"); 80 | assert_eq!(format!("{}", Version::Version13), "19"); 81 | } 82 | 83 | #[test] 84 | fn from_str_returns_correct_result() { 85 | assert_eq!(Version::from_str("16"), Ok(Version::Version10)); 86 | assert_eq!(Version::from_str("19"), Ok(Version::Version13)); 87 | assert_eq!(Version::from_str("11"), Err(Error::DecodingFail)); 88 | } 89 | 90 | #[test] 91 | fn from_u32_returns_correct_result() { 92 | assert_eq!(Version::from_u32(0x10), Ok(Version::Version10)); 93 | assert_eq!(Version::from_u32(0x13), Ok(Version::Version13)); 94 | assert_eq!(Version::from_u32(0), Err(Error::IncorrectVersion)); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ChangeLog for rust-argon2 2 | ========================= 3 | 4 | This documents all notable changes to 5 | [rust-argon2](https://github.com/sru-systems/rust-argon2). 6 | 7 | ## 3.0.0 8 | 9 | - Bring back parallelism (by Jonas Malaco). 10 | - Update base64 dependency to 0.22. 11 | - Update constant_time_eq dependency to 0.4.2. 12 | 13 | 14 | ## 2.1.0 15 | 16 | - Add OWASP recommended configurations. 17 | - Change default configuration from RFC9106 to OWASP (t=2, 19 MiB memory). For 18 | the 2.0.0 default configuration use `Config::rfc9106`. For the 1.0.x default 19 | configuration use `Config::original`. 20 | 21 | ## 2.0.0 22 | 23 | - Remove parallel execution to prevent UB. 24 | - Add config settings from RFC9106. 25 | - Use RFC9106 recommended settings as default. 26 | 27 | 28 | ## 1.0.1 29 | 30 | - Update serde dependency to 1.0. 31 | - Update constant_time_eq dependency to 0.3.0. 32 | - Update base64 dependency to 0.21. 33 | - Encoded strings must not use padding, fix tests. 34 | - Update to Rust edition 2021. 35 | 36 | 37 | ## 1.0.0 38 | 39 | - Remove deprecated functions. 40 | - Update constant_time_eq to 0.1.5. 41 | - Update serde to 1.0.133. 42 | - Update blake2b_simd to 1.0. 43 | 44 | 45 | ## 0.8.3 46 | 47 | - Replace transmute with to_le_bytes. 48 | - Update base64 to version 0.13. 49 | - Update crossbeam-utils to version 0.8. 50 | - Update hex to version 0.4. 51 | - Derive Clone for Error struct. 52 | - Add optional serde support for Error struct. 53 | 54 | 55 | ## 0.8.2 56 | 57 | - Change base64 to latest version (0.12). 58 | 59 | 60 | ## 0.8.1 61 | 62 | - Fix issue with verifying multi-lane hashes with parallelism disabled (#27) 63 | 64 | ## 0.8.0 65 | 66 | - Make parallelism optional via feature flag. 67 | 68 | 69 | ## 0.7.0 70 | 71 | - Update crossbeam-utils dependency to 0.7. 72 | 73 | 74 | ## 0.6.1 75 | 76 | - Use constant time equals to compare hashes. 77 | 78 | 79 | ## 0.6.0 80 | 81 | - Use 2018 edition or Rust 82 | - Use &dyn error::Error instead of &error::Error 83 | - Fix clippy lints 84 | - Allow callers of encode_string to pass any &[u8] 85 | - Update base64 dependency. 86 | 87 | 88 | ## 0.5.1 89 | 90 | - Use crossbeam utils 0.6 instead of crossbeam 0.5 91 | 92 | 93 | ## 0.5.0 94 | 95 | - Replace blake2-rfc with blake2b_simd. 96 | 97 | 98 | ## 0.4.0 99 | 100 | - Replace rustc-serialize dependency with base64 and hex. 101 | - Update base64 dependency. 102 | - Update crossbeam dependency. 103 | - Update hex dependency. 104 | - Allow updating to minor versions of blake2-rfc. 105 | 106 | 107 | ## 0.3.0 108 | 109 | - Embed Config struct in Context struct. 110 | 111 | 112 | ## 0.2.0 113 | 114 | - Use ThreadMode enum instead of explicit thread number. 115 | - Use a Config struct instead of explicit configuration arguments. 116 | - Use references instead of vectors for byte data in the Context struct. 117 | - Deprecate the following functions: 118 | - hash_encoded_defaults 119 | - hash_encoded_old 120 | - hash_encoded_std 121 | - hash_raw_defaults 122 | - hash_raw_old 123 | - hash_raw_std 124 | - verify_raw_old 125 | - verify_raw_std 126 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Martijn Rijkeboer 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | //! Library for hashing passwords using 10 | //! [Argon2](https://github.com/P-H-C/phc-winner-argon2), the password-hashing 11 | //! function that won the 12 | //! [Password Hashing Competition (PHC)](https://password-hashing.net). 13 | //! 14 | //! # Usage 15 | //! 16 | //! To use this crate, add the following to your Cargo.toml: 17 | //! 18 | //! ```toml 19 | //! [dependencies] 20 | //! rust-argon2 = "3.0" 21 | //! ``` 22 | //! 23 | //! And the following to your crate root: 24 | //! 25 | //! ```rust 26 | //! extern crate argon2; 27 | //! ``` 28 | //! 29 | //! # Examples 30 | //! 31 | //! Create a password hash using the defaults and verify it: 32 | //! 33 | //! ```rust 34 | //! use argon2::{self, Config}; 35 | //! 36 | //! let password = b"password"; 37 | //! let salt = b"randomsalt"; 38 | //! let config = Config::default(); 39 | //! let hash = argon2::hash_encoded(password, salt, &config).unwrap(); 40 | //! let matches = argon2::verify_encoded(&hash, password).unwrap(); 41 | //! assert!(matches); 42 | //! ``` 43 | //! 44 | //! Create a password hash with custom settings and verify it: 45 | //! 46 | //! ```rust 47 | //! use argon2::{self, Config, ThreadMode, Variant, Version}; 48 | //! 49 | //! let password = b"password"; 50 | //! let salt = b"othersalt"; 51 | //! let config = Config { 52 | //! variant: Variant::Argon2i, 53 | //! version: Version::Version13, 54 | //! mem_cost: 65536, 55 | //! time_cost: 10, 56 | #![cfg_attr(feature = "crossbeam-utils", doc = " lanes: 4,")] 57 | #![cfg_attr( 58 | feature = "crossbeam-utils", 59 | doc = " thread_mode: ThreadMode::Parallel," 60 | )] 61 | #![cfg_attr(not(feature = "crossbeam-utils"), doc = " lanes: 1,")] 62 | #![cfg_attr( 63 | not(feature = "crossbeam-utils"), 64 | doc = " thread_mode: ThreadMode::Sequential," 65 | )] 66 | //! secret: &[], 67 | //! ad: &[], 68 | //! hash_length: 32 69 | //! }; 70 | //! let hash = argon2::hash_encoded(password, salt, &config).unwrap(); 71 | //! let matches = argon2::verify_encoded(&hash, password).unwrap(); 72 | //! assert!(matches); 73 | //! ``` 74 | //! 75 | //! # Limitations 76 | //! 77 | //! This crate has the same limitation as the `blake2-rfc` crate that it uses. 78 | //! It does not attempt to clear potentially sensitive data from its work 79 | //! memory. To do so correctly without a heavy performance penalty would 80 | //! require help from the compiler. It's better to not attempt to do so than to 81 | //! present a false assurance. 82 | //! 83 | //! This version uses the standard implementation and does not yet implement 84 | //! optimizations. Therefore, it is not the fastest implementation available. 85 | 86 | mod argon2; 87 | mod block; 88 | mod common; 89 | mod config; 90 | mod context; 91 | mod core; 92 | mod decoded; 93 | mod encoding; 94 | mod error; 95 | mod memory; 96 | mod result; 97 | mod thread_mode; 98 | mod variant; 99 | mod version; 100 | 101 | pub use crate::argon2::*; 102 | pub use crate::config::Config; 103 | pub use crate::error::Error; 104 | pub use crate::result::Result; 105 | pub use crate::thread_mode::ThreadMode; 106 | pub use crate::variant::Variant; 107 | pub use crate::version::Version; 108 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Martijn Rijkeboer 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use std::{error, fmt}; 10 | 11 | #[cfg(feature = "serde")] 12 | use serde::{Deserialize, Serialize}; 13 | 14 | /// Error type for Argon2 errors. 15 | #[derive(Clone, Debug, PartialEq)] 16 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 17 | #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] 18 | pub enum Error { 19 | /// The output (hash) is too short (minimum is 4). 20 | OutputTooShort, 21 | 22 | /// The output (hash) is too long (maximum is 2^32 - 1). 23 | OutputTooLong, 24 | 25 | /// The password is too short (minimum is 0). 26 | PwdTooShort, 27 | 28 | /// The password is too long (maximum is 2^32 - 1). 29 | PwdTooLong, 30 | 31 | /// The salt is too short (minimum is 8). 32 | SaltTooShort, 33 | 34 | /// The salt is too long (maximum is 2^32 - 1). 35 | SaltTooLong, 36 | 37 | /// The associated data is too short (minimum is 0). 38 | AdTooShort, 39 | 40 | /// The associated data is too long (maximum is 2^32 - 1). 41 | AdTooLong, 42 | 43 | /// The secret value is too short (minimum is 0). 44 | SecretTooShort, 45 | 46 | /// The secret value is too long (maximum is 2^32 - 1). 47 | SecretTooLong, 48 | 49 | /// The time cost (passes) is too small (minimum is 1). 50 | TimeTooSmall, 51 | 52 | /// The time cost (passes) is too large (maximum is 2^32 - 1). 53 | TimeTooLarge, 54 | 55 | /// The memory cost is too small (minimum is 8 x parallelism). 56 | MemoryTooLittle, 57 | 58 | /// The memory cost is too large (maximum 2GiB on 32-bit or 4TiB on 64-bit). 59 | MemoryTooMuch, 60 | 61 | /// The number of lanes (parallelism) is too small (minimum is 1). 62 | LanesTooFew, 63 | 64 | /// The number of lanes (parallelism) is too large (maximum is 2^24 - 1). 65 | LanesTooMany, 66 | 67 | /// Incorrect Argon2 variant. 68 | IncorrectType, 69 | 70 | /// Incorrect Argon2 version. 71 | IncorrectVersion, 72 | 73 | /// The decoding of the encoded data has failed. 74 | DecodingFail, 75 | } 76 | 77 | impl Error { 78 | fn msg(&self) -> &str { 79 | match *self { 80 | Error::OutputTooShort => "Output is too short", 81 | Error::OutputTooLong => "Output is too long", 82 | Error::PwdTooShort => "Password is too short", 83 | Error::PwdTooLong => "Password is too long", 84 | Error::SaltTooShort => "Salt is too short", 85 | Error::SaltTooLong => "Salt is too long", 86 | Error::AdTooShort => "Associated data is too short", 87 | Error::AdTooLong => "Associated data is too long", 88 | Error::SecretTooShort => "Secret is too short", 89 | Error::SecretTooLong => "Secret is too long", 90 | Error::TimeTooSmall => "Time cost is too small", 91 | Error::TimeTooLarge => "Time cost is too large", 92 | Error::MemoryTooLittle => "Memory cost is too small", 93 | Error::MemoryTooMuch => "Memory cost is too large", 94 | Error::LanesTooFew => "Too few lanes", 95 | Error::LanesTooMany => "Too many lanes", 96 | Error::IncorrectType => "There is no such type of Argon2", 97 | Error::IncorrectVersion => "There is no such version of Argon2", 98 | Error::DecodingFail => "Decoding failed", 99 | } 100 | } 101 | } 102 | 103 | impl fmt::Display for Error { 104 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 105 | write!(f, "{}", self.msg()) 106 | } 107 | } 108 | 109 | impl error::Error for Error { 110 | fn description(&self) -> &str { 111 | self.msg() 112 | } 113 | 114 | fn cause(&self) -> Option<&dyn error::Error> { 115 | None 116 | } 117 | } 118 | 119 | impl From for Error { 120 | fn from(_: base64::DecodeError) -> Self { 121 | Error::DecodingFail 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/block.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Martijn Rijkeboer 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::common; 10 | use std::fmt; 11 | use std::fmt::Debug; 12 | use std::ops::{BitXorAssign, Index, IndexMut}; 13 | 14 | /// Structure for the (1 KiB) memory block implemented as 128 64-bit words. 15 | // Blocks are 128-byte aligned to prevent false sharing. This specific alignment value replicates 16 | // what `crossbeam-utils::CachePadded` does on modern architectures, which either use 128-byte 17 | // cache lines (aarch64) or pull 64-byte cache lines in pairs (x86-64), without the need for 18 | // stubbing that type in when `crossbeam-utils` isn't available. As blocks are fairly large (1 19 | // KiB), this simplification shouldn't severy affect the (rarer) targets with smaller cache lines. 20 | #[repr(align(128))] 21 | pub struct Block([u64; common::QWORDS_IN_BLOCK]); 22 | 23 | impl Block { 24 | /// Gets the byte slice representation of the block. 25 | pub fn as_u8(&self) -> &[u8] { 26 | let bytes: &[u8; common::BLOCK_SIZE] = unsafe { 27 | &*(&self.0 as *const [u64; common::QWORDS_IN_BLOCK] as *const [u8; common::BLOCK_SIZE]) 28 | }; 29 | bytes 30 | } 31 | 32 | /// Gets the mutable byte slice representation of the block. 33 | pub fn as_u8_mut(&mut self) -> &mut [u8] { 34 | let bytes: &mut [u8; common::BLOCK_SIZE] = unsafe { 35 | &mut *(&mut self.0 as *mut [u64; common::QWORDS_IN_BLOCK] 36 | as *mut [u8; common::BLOCK_SIZE]) 37 | }; 38 | bytes 39 | } 40 | 41 | /// Copies self to destination. 42 | pub fn copy_to(&self, dst: &mut Block) { 43 | for (d, s) in dst.0.iter_mut().zip(self.0.iter()) { 44 | *d = *s 45 | } 46 | } 47 | 48 | /// Creates a new block filled with zeros. 49 | pub fn zero() -> Block { 50 | Block([0u64; common::QWORDS_IN_BLOCK]) 51 | } 52 | } 53 | 54 | impl<'a> BitXorAssign<&'a Block> for Block { 55 | fn bitxor_assign(&mut self, rhs: &Block) { 56 | for (s, r) in self.0.iter_mut().zip(rhs.0.iter()) { 57 | *s ^= *r 58 | } 59 | } 60 | } 61 | 62 | impl Clone for Block { 63 | fn clone(&self) -> Block { 64 | Block(self.0) 65 | } 66 | } 67 | 68 | impl Debug for Block { 69 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 70 | fmt.debug_list().entries(self.0.iter()).finish() 71 | } 72 | } 73 | 74 | impl Eq for Block {} 75 | 76 | impl Index for Block { 77 | type Output = u64; 78 | fn index(&self, index: usize) -> &u64 { 79 | &self.0[index] 80 | } 81 | } 82 | 83 | impl IndexMut for Block { 84 | fn index_mut(&mut self, index: usize) -> &mut u64 { 85 | &mut self.0[index] 86 | } 87 | } 88 | 89 | impl PartialEq for Block { 90 | fn eq(&self, other: &Block) -> bool { 91 | let mut equal = true; 92 | for (s, o) in self.0.iter().zip(other.0.iter()) { 93 | if s != o { 94 | equal = false; 95 | } 96 | } 97 | equal 98 | } 99 | } 100 | 101 | #[cfg(test)] 102 | mod tests { 103 | 104 | use crate::block::Block; 105 | use crate::common; 106 | 107 | #[test] 108 | fn as_u8_returns_correct_slice() { 109 | let block = Block::zero(); 110 | let expected = vec![0u8; 1024]; 111 | let actual = block.as_u8(); 112 | assert_eq!(actual, expected.as_slice()); 113 | } 114 | 115 | #[test] 116 | fn as_u8_mut_returns_correct_slice() { 117 | let mut block = Block::zero(); 118 | let mut expected = vec![0u8; 1024]; 119 | let actual = block.as_u8_mut(); 120 | assert_eq!(actual, expected.as_mut_slice()); 121 | } 122 | 123 | #[test] 124 | fn bitxor_assign_updates_lhs() { 125 | let mut lhs = Block([0u64; common::QWORDS_IN_BLOCK]); 126 | let rhs = Block([1u64; common::QWORDS_IN_BLOCK]); 127 | lhs ^= &rhs; 128 | assert_eq!(lhs, rhs); 129 | } 130 | 131 | #[test] 132 | fn copy_to_copies_block() { 133 | let src = Block([1u64; common::QWORDS_IN_BLOCK]); 134 | let mut dst = Block([0u64; common::QWORDS_IN_BLOCK]); 135 | src.copy_to(&mut dst); 136 | assert_eq!(dst, src); 137 | } 138 | 139 | #[test] 140 | fn clone_clones_block() { 141 | let orig = Block([1u64; common::QWORDS_IN_BLOCK]); 142 | let copy = orig.clone(); 143 | assert_eq!(copy, orig); 144 | } 145 | 146 | #[test] 147 | fn zero_creates_block_will_all_zeros() { 148 | let expected = Block([0u64; common::QWORDS_IN_BLOCK]); 149 | let actual = Block::zero(); 150 | assert_eq!(actual, expected); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/variant.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Martijn Rijkeboer 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::error::Error; 10 | use crate::result::Result; 11 | use std::fmt; 12 | 13 | /// The Argon2 variant. 14 | #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] 15 | pub enum Variant { 16 | /// Argon2 using data-dependent memory access to thwart tradeoff attacks. 17 | /// Recommended for cryptocurrencies and backend servers. 18 | Argon2d = 0, 19 | 20 | /// Argon2 using data-independent memory access to thwart side-channel 21 | /// attacks. Recommended for password hashing and password-based key 22 | /// derivation. 23 | Argon2i = 1, 24 | 25 | /// Argon2 using hybrid construction. 26 | Argon2id = 2, 27 | } 28 | 29 | impl Variant { 30 | /// Gets the lowercase string slice representation of the variant. 31 | pub fn as_lowercase_str(&self) -> &'static str { 32 | match *self { 33 | Variant::Argon2d => "argon2d", 34 | Variant::Argon2i => "argon2i", 35 | Variant::Argon2id => "argon2id", 36 | } 37 | } 38 | 39 | /// Gets the u32 representation of the variant. 40 | pub fn as_u32(&self) -> u32 { 41 | *self as u32 42 | } 43 | 44 | /// Gets the u64 representation of the variant. 45 | pub fn as_u64(&self) -> u64 { 46 | *self as u64 47 | } 48 | 49 | /// Gets the uppercase string slice representation of the variant. 50 | pub fn as_uppercase_str(&self) -> &'static str { 51 | match *self { 52 | Variant::Argon2d => "Argon2d", 53 | Variant::Argon2i => "Argon2i", 54 | Variant::Argon2id => "Argon2id", 55 | } 56 | } 57 | 58 | /// Attempts to create a variant from a string slice. 59 | pub fn from_str(str: &str) -> Result { 60 | match str { 61 | "Argon2d" => Ok(Variant::Argon2d), 62 | "Argon2i" => Ok(Variant::Argon2i), 63 | "Argon2id" => Ok(Variant::Argon2id), 64 | "argon2d" => Ok(Variant::Argon2d), 65 | "argon2i" => Ok(Variant::Argon2i), 66 | "argon2id" => Ok(Variant::Argon2id), 67 | _ => Err(Error::DecodingFail), 68 | } 69 | } 70 | 71 | /// Attempts to create a variant from an u32. 72 | pub fn from_u32(val: u32) -> Result { 73 | match val { 74 | 0 => Ok(Variant::Argon2d), 75 | 1 => Ok(Variant::Argon2i), 76 | 2 => Ok(Variant::Argon2id), 77 | _ => Err(Error::IncorrectType), 78 | } 79 | } 80 | } 81 | 82 | impl Default for Variant { 83 | fn default() -> Variant { 84 | Variant::Argon2i 85 | } 86 | } 87 | 88 | impl fmt::Display for Variant { 89 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 90 | write!(f, "{}", self.as_lowercase_str()) 91 | } 92 | } 93 | 94 | #[cfg(test)] 95 | mod tests { 96 | 97 | use crate::error::Error; 98 | use crate::variant::Variant; 99 | 100 | #[test] 101 | fn as_lowercase_str_returns_correct_str() { 102 | assert_eq!(Variant::Argon2d.as_lowercase_str(), "argon2d"); 103 | assert_eq!(Variant::Argon2i.as_lowercase_str(), "argon2i"); 104 | assert_eq!(Variant::Argon2id.as_lowercase_str(), "argon2id"); 105 | } 106 | 107 | #[test] 108 | fn as_u32_returns_correct_u32() { 109 | assert_eq!(Variant::Argon2d.as_u32(), 0); 110 | assert_eq!(Variant::Argon2i.as_u32(), 1); 111 | assert_eq!(Variant::Argon2id.as_u32(), 2); 112 | } 113 | 114 | #[test] 115 | fn as_u64_returns_correct_u64() { 116 | assert_eq!(Variant::Argon2d.as_u64(), 0); 117 | assert_eq!(Variant::Argon2i.as_u64(), 1); 118 | assert_eq!(Variant::Argon2id.as_u64(), 2); 119 | } 120 | 121 | #[test] 122 | fn as_uppercase_str_returns_correct_str() { 123 | assert_eq!(Variant::Argon2d.as_uppercase_str(), "Argon2d"); 124 | assert_eq!(Variant::Argon2i.as_uppercase_str(), "Argon2i"); 125 | assert_eq!(Variant::Argon2id.as_uppercase_str(), "Argon2id"); 126 | } 127 | 128 | #[test] 129 | fn default_returns_correct_variant() { 130 | assert_eq!(Variant::default(), Variant::Argon2i); 131 | } 132 | 133 | #[test] 134 | fn display_returns_correct_string() { 135 | assert_eq!(format!("{}", Variant::Argon2d), "argon2d"); 136 | assert_eq!(format!("{}", Variant::Argon2i), "argon2i"); 137 | assert_eq!(format!("{}", Variant::Argon2id), "argon2id"); 138 | } 139 | 140 | #[test] 141 | fn from_str_returns_correct_result() { 142 | assert_eq!(Variant::from_str("Argon2d"), Ok(Variant::Argon2d)); 143 | assert_eq!(Variant::from_str("Argon2i"), Ok(Variant::Argon2i)); 144 | assert_eq!(Variant::from_str("Argon2id"), Ok(Variant::Argon2id)); 145 | assert_eq!(Variant::from_str("argon2d"), Ok(Variant::Argon2d)); 146 | assert_eq!(Variant::from_str("argon2i"), Ok(Variant::Argon2i)); 147 | assert_eq!(Variant::from_str("argon2id"), Ok(Variant::Argon2id)); 148 | assert_eq!(Variant::from_str("foobar"), Err(Error::DecodingFail)); 149 | } 150 | 151 | #[test] 152 | fn from_u32_returns_correct_result() { 153 | assert_eq!(Variant::from_u32(0), Ok(Variant::Argon2d)); 154 | assert_eq!(Variant::from_u32(1), Ok(Variant::Argon2i)); 155 | assert_eq!(Variant::from_u32(2), Ok(Variant::Argon2id)); 156 | assert_eq!(Variant::from_u32(3), Err(Error::IncorrectType)); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/memory.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Martijn Rijkeboer 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | #![warn(unsafe_op_in_unsafe_fn)] 10 | 11 | use crate::block::Block; 12 | use std::fmt; 13 | use std::fmt::Debug; 14 | use std::marker::PhantomData; 15 | use std::ops::{Index, IndexMut}; 16 | use std::ptr::NonNull; 17 | 18 | /// Structure representing the memory matrix. 19 | pub struct Memory { 20 | /// The number of rows. 21 | rows: usize, 22 | 23 | /// The number of columns. 24 | cols: usize, 25 | 26 | /// The flat array of blocks representing the memory matrix. 27 | blocks: Box<[Block]>, 28 | } 29 | 30 | impl Memory { 31 | /// Creates a new memory matrix. 32 | pub fn new(lanes: u32, lane_length: u32) -> Memory { 33 | let rows = lanes as usize; 34 | let cols = lane_length as usize; 35 | let total = rows * cols; 36 | let blocks = vec![Block::zero(); total].into_boxed_slice(); 37 | Memory { rows, cols, blocks } 38 | } 39 | 40 | /// Returns a pointer to the flat array of blocks useful for parallel disjoint access. 41 | pub fn as_unsafe_blocks(&mut self) -> UnsafeBlocks<'_> { 42 | UnsafeBlocks { 43 | blocks: NonNull::new(self.blocks.as_mut_ptr()).unwrap(), 44 | phantom: PhantomData, 45 | } 46 | } 47 | } 48 | 49 | /// A wrapped raw pointer to the flat array of blocks, useful for parallel disjoint access. 50 | /// 51 | /// All operations on this type are unchecked and require `unsafe`. Callers are responsible for 52 | /// accessing the blocks without violating the aliasing rules or resulting in data races. 53 | pub struct UnsafeBlocks<'a> { 54 | blocks: NonNull, 55 | phantom: PhantomData<&'a [Block]>, 56 | } 57 | 58 | impl UnsafeBlocks<'_> { 59 | /// Get a shared reference to the `Block` at `index`. 60 | /// 61 | /// # Safety 62 | /// 63 | /// The caller must ensure that `index` is in bounds, no mutable references exist or are 64 | /// created to the corresponding block, and no data races happen while the returned reference 65 | /// lives. 66 | pub unsafe fn get_unchecked(&self, index: usize) -> &Block { 67 | // SAFETY: the caller promises that the `index` is in bounds; therefore, we're within the 68 | // bounds of the allocated object, and the offset in bytes fits in an `isize`. 69 | let ptr = unsafe { self.blocks.add(index) }; 70 | // SAFETY: the caller promises that there are no mutable references to the requested 71 | // `Block` or data races; and `ptr` points to a valid and aligned `Block`. 72 | unsafe { ptr.as_ref() } 73 | } 74 | 75 | /// Get a mutable reference to the `Block` at `index`. 76 | /// 77 | /// # Safety 78 | /// 79 | /// The caller must ensure that `index` is in bounds, no other references exist or are created 80 | /// to the corresponding block, and no data races happen while the returned reference lives. 81 | #[allow(clippy::mut_from_ref)] 82 | pub unsafe fn get_mut_unchecked(&self, index: usize) -> &mut Block { 83 | // SAFETY: the caller promises that the `index` is in bounds; therefore, we're within 84 | // the bounds of the allocated object, and the offset in bytes fits in an `isize`. 85 | let mut ptr = unsafe { self.blocks.add(index) }; 86 | // SAFETY: the caller promises that there are no other references, accesses, or data races 87 | // affecting the target `Block`; and `ptr` points to a valid and aligned `Block`. 88 | // 89 | // Also note that this isn't interior mutability, as we're going through a `NonNull` 90 | // pointer derived from a mutable reference (and interior mutability is not transitive 91 | // through raw pointers[1][2][3]). 92 | // 93 | // [1]: https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#interior-mutability 94 | // [2]: https://doc.rust-lang.org/stable/reference/behavior-considered-undefined.html?highlight=undefin#r-undefined.immutable 95 | // [3]: https://github.com/rust-lang/reference/issues/1227 96 | unsafe { ptr.as_mut() } 97 | } 98 | } 99 | 100 | // SAFETY: passing or sharing an `UnsafeBlocks` accross threads is, in itself, safe (calling any 101 | // methods on it isn't, but they already require `unsafe`). 102 | unsafe impl Send for UnsafeBlocks<'_> {} 103 | unsafe impl Sync for UnsafeBlocks<'_> {} 104 | 105 | impl Debug for Memory { 106 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 107 | write!(f, "Memory {{ rows: {}, cols: {} }}", self.rows, self.cols) 108 | } 109 | } 110 | 111 | impl Index for Memory { 112 | type Output = Block; 113 | fn index(&self, index: u32) -> &Block { 114 | &self.blocks[index as usize] 115 | } 116 | } 117 | 118 | impl Index for Memory { 119 | type Output = Block; 120 | fn index(&self, index: u64) -> &Block { 121 | &self.blocks[index as usize] 122 | } 123 | } 124 | 125 | impl Index<(u32, u32)> for Memory { 126 | type Output = Block; 127 | fn index(&self, index: (u32, u32)) -> &Block { 128 | let pos = ((index.0 as usize) * self.cols) + (index.1 as usize); 129 | &self.blocks[pos] 130 | } 131 | } 132 | 133 | impl IndexMut for Memory { 134 | fn index_mut(&mut self, index: u32) -> &mut Block { 135 | &mut self.blocks[index as usize] 136 | } 137 | } 138 | 139 | impl IndexMut for Memory { 140 | fn index_mut(&mut self, index: u64) -> &mut Block { 141 | &mut self.blocks[index as usize] 142 | } 143 | } 144 | 145 | impl IndexMut<(u32, u32)> for Memory { 146 | fn index_mut(&mut self, index: (u32, u32)) -> &mut Block { 147 | let pos = ((index.0 as usize) * self.cols) + (index.1 as usize); 148 | &mut self.blocks[pos] 149 | } 150 | } 151 | 152 | #[cfg(test)] 153 | mod tests { 154 | 155 | use crate::memory::Memory; 156 | 157 | #[test] 158 | fn new_returns_correct_instance() { 159 | let lanes = 4; 160 | let lane_length = 128; 161 | let memory = Memory::new(lanes, lane_length); 162 | assert_eq!(memory.rows, lanes as usize); 163 | assert_eq!(memory.cols, lane_length as usize); 164 | assert_eq!(memory.blocks.len(), 512); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/context.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Martijn Rijkeboer 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::common; 10 | use crate::config::Config; 11 | use crate::error::Error; 12 | use crate::result::Result; 13 | 14 | /// Structure containing settings for the Argon2 algorithm. A combination of 15 | /// the original argon2_context and argon2_instance_t. 16 | #[derive(Debug, PartialEq)] 17 | pub struct Context<'a> { 18 | /// The config for this context. 19 | pub config: Config<'a>, 20 | 21 | /// The length of a lane. 22 | pub lane_length: u32, 23 | 24 | /// The number of memory blocks. 25 | pub memory_blocks: u32, 26 | 27 | /// The password. 28 | pub pwd: &'a [u8], 29 | 30 | /// The salt. 31 | pub salt: &'a [u8], 32 | 33 | /// The length of a segment. 34 | pub segment_length: u32, 35 | } 36 | 37 | impl<'a> Context<'a> { 38 | /// Attempts to create a new context. 39 | pub fn new(config: Config<'a>, pwd: &'a [u8], salt: &'a [u8]) -> Result> { 40 | if config.lanes < common::MIN_LANES { 41 | return Err(Error::LanesTooFew); 42 | } else if config.lanes > common::MAX_LANES { 43 | return Err(Error::LanesTooMany); 44 | } 45 | 46 | let lanes = config.lanes; 47 | if config.mem_cost < common::MIN_MEMORY { 48 | return Err(Error::MemoryTooLittle); 49 | } else if config.mem_cost > common::MAX_MEMORY { 50 | return Err(Error::MemoryTooMuch); 51 | } else if config.mem_cost < 8 * lanes { 52 | return Err(Error::MemoryTooLittle); 53 | } 54 | 55 | if config.time_cost < common::MIN_TIME { 56 | return Err(Error::TimeTooSmall); 57 | } else if config.time_cost > common::MAX_TIME { 58 | return Err(Error::TimeTooLarge); 59 | } 60 | 61 | let pwd_len = pwd.len(); 62 | if pwd_len < common::MIN_PWD_LENGTH as usize { 63 | return Err(Error::PwdTooShort); 64 | } else if pwd_len > common::MAX_PWD_LENGTH as usize { 65 | return Err(Error::PwdTooLong); 66 | } 67 | 68 | let salt_len = salt.len(); 69 | if salt_len < common::MIN_SALT_LENGTH as usize { 70 | return Err(Error::SaltTooShort); 71 | } else if salt_len > common::MAX_SALT_LENGTH as usize { 72 | return Err(Error::SaltTooLong); 73 | } 74 | 75 | let secret_len = config.secret.len(); 76 | if secret_len < common::MIN_SECRET_LENGTH as usize { 77 | return Err(Error::SecretTooShort); 78 | } else if secret_len > common::MAX_SECRET_LENGTH as usize { 79 | return Err(Error::SecretTooLong); 80 | } 81 | 82 | let ad_len = config.ad.len(); 83 | if ad_len < common::MIN_AD_LENGTH as usize { 84 | return Err(Error::AdTooShort); 85 | } else if ad_len > common::MAX_AD_LENGTH as usize { 86 | return Err(Error::AdTooLong); 87 | } 88 | 89 | if config.hash_length < common::MIN_HASH_LENGTH { 90 | return Err(Error::OutputTooShort); 91 | } else if config.hash_length > common::MAX_HASH_LENGTH { 92 | return Err(Error::OutputTooLong); 93 | } 94 | 95 | let mut memory_blocks = config.mem_cost; 96 | if memory_blocks < 2 * common::SYNC_POINTS * lanes { 97 | memory_blocks = 2 * common::SYNC_POINTS * lanes; 98 | } 99 | 100 | let segment_length = memory_blocks / (lanes * common::SYNC_POINTS); 101 | let memory_blocks = segment_length * (lanes * common::SYNC_POINTS); 102 | let lane_length = segment_length * common::SYNC_POINTS; 103 | 104 | Ok(Context { 105 | config, 106 | lane_length, 107 | memory_blocks, 108 | pwd, 109 | salt, 110 | segment_length, 111 | }) 112 | } 113 | } 114 | 115 | #[cfg(test)] 116 | mod tests { 117 | 118 | use crate::config::Config; 119 | use crate::context::Context; 120 | use crate::error::Error; 121 | use crate::thread_mode::ThreadMode; 122 | use crate::variant::Variant; 123 | use crate::version::Version; 124 | 125 | #[test] 126 | fn new_returns_correct_instance() { 127 | let config = Config { 128 | ad: b"additionaldata", 129 | hash_length: 32, 130 | lanes: 4, 131 | mem_cost: 4096, 132 | secret: b"secret", 133 | thread_mode: ThreadMode::Sequential, 134 | time_cost: 3, 135 | variant: Variant::Argon2i, 136 | version: Version::Version13, 137 | }; 138 | let pwd = b"password"; 139 | let salt = b"somesalt"; 140 | let result = Context::new(config.clone(), pwd, salt); 141 | assert!(result.is_ok()); 142 | 143 | let context = result.unwrap(); 144 | assert_eq!(context.config, config); 145 | assert_eq!(context.pwd, pwd); 146 | assert_eq!(context.salt, salt); 147 | assert_eq!(context.memory_blocks, 4096); 148 | assert_eq!(context.segment_length, 256); 149 | assert_eq!(context.lane_length, 1024); 150 | } 151 | 152 | #[test] 153 | fn new_with_too_little_mem_cost_returns_correct_error() { 154 | let config = Config { 155 | mem_cost: 7, 156 | ..Default::default() 157 | }; 158 | assert_eq!( 159 | Context::new(config, &[0u8; 8], &[0u8; 8]), 160 | Err(Error::MemoryTooLittle) 161 | ); 162 | } 163 | 164 | #[test] 165 | fn new_with_less_than_8_x_lanes_mem_cost_returns_correct_error() { 166 | let config = Config { 167 | lanes: 4, 168 | mem_cost: 31, 169 | ..Default::default() 170 | }; 171 | assert_eq!( 172 | Context::new(config, &[0u8; 8], &[0u8; 8]), 173 | Err(Error::MemoryTooLittle) 174 | ); 175 | } 176 | 177 | #[test] 178 | fn new_with_too_small_time_cost_returns_correct_error() { 179 | let config = Config { 180 | time_cost: 0, 181 | ..Default::default() 182 | }; 183 | assert_eq!( 184 | Context::new(config, &[0u8; 8], &[0u8; 8]), 185 | Err(Error::TimeTooSmall) 186 | ); 187 | } 188 | 189 | #[test] 190 | fn new_with_too_few_lanes_returns_correct_error() { 191 | let config = Config { 192 | lanes: 0, 193 | ..Default::default() 194 | }; 195 | assert_eq!( 196 | Context::new(config, &[0u8; 8], &[0u8; 8]), 197 | Err(Error::LanesTooFew) 198 | ); 199 | } 200 | 201 | #[test] 202 | fn new_with_too_many_lanes_returns_correct_error() { 203 | let config = Config { 204 | lanes: 1 << 24, 205 | ..Default::default() 206 | }; 207 | assert_eq!( 208 | Context::new(config, &[0u8; 8], &[0u8; 8]), 209 | Err(Error::LanesTooMany) 210 | ); 211 | } 212 | 213 | #[test] 214 | fn new_with_too_short_salt_returns_correct_error() { 215 | let config = Default::default(); 216 | let salt = [0u8; 7]; 217 | assert_eq!( 218 | Context::new(config, &[0u8; 8], &salt), 219 | Err(Error::SaltTooShort) 220 | ); 221 | } 222 | 223 | #[test] 224 | fn new_with_too_short_hash_length_returns_correct_error() { 225 | let config = Config { 226 | hash_length: 3, 227 | ..Default::default() 228 | }; 229 | assert_eq!( 230 | Context::new(config, &[0u8; 8], &[0u8; 8]), 231 | Err(Error::OutputTooShort) 232 | ); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/argon2.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Martijn Rijkeboer 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::config::Config; 10 | use crate::context::Context; 11 | use crate::core; 12 | use crate::encoding; 13 | use crate::memory::Memory; 14 | use crate::result::Result; 15 | use crate::thread_mode::ThreadMode; 16 | use crate::variant::Variant; 17 | use crate::version::Version; 18 | 19 | use 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] 43 | pub 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 | /// ``` 98 | pub fn hash_encoded(pwd: &[u8], salt: &[u8], config: &Config) -> Result { 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 | /// ``` 142 | pub fn hash_raw(pwd: &[u8], salt: &[u8], config: &Config) -> Result> { 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 | /// ``` 161 | pub fn verify_encoded(encoded: &str, pwd: &[u8]) -> Result { 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 | /// ``` 180 | pub fn verify_encoded_ext(encoded: &str, pwd: &[u8], secret: &[u8], ad: &[u8]) -> Result { 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 | /// ``` 217 | pub fn verify_raw(pwd: &[u8], salt: &[u8], hash: &[u8], config: &Config) -> Result { 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 | 227 | fn run(context: &Context) -> Vec { 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)] 236 | mod 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 | } 279 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Martijn Rijkeboer 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::thread_mode::ThreadMode; 10 | use crate::variant::Variant; 11 | use crate::version::Version; 12 | 13 | /// Structure containing configuration settings. 14 | /// 15 | /// # Examples 16 | /// 17 | /// ``` 18 | /// use argon2::{Config, ThreadMode, Variant, Version}; 19 | /// 20 | /// let config = Config::default(); 21 | /// assert_eq!(config.ad, &[]); 22 | /// assert_eq!(config.hash_length, 32); 23 | /// assert_eq!(config.lanes, 1); 24 | /// assert_eq!(config.mem_cost, 19 * 1024); 25 | /// assert_eq!(config.secret, &[]); 26 | /// assert_eq!(config.time_cost, 2); 27 | /// assert_eq!(config.variant, Variant::Argon2id); 28 | /// assert_eq!(config.thread_mode, ThreadMode::Sequential); 29 | /// assert_eq!(config.version, Version::Version13); 30 | /// ``` 31 | #[derive(Clone, Debug, PartialEq)] 32 | pub struct Config<'a> { 33 | /// The associated data. 34 | pub ad: &'a [u8], 35 | 36 | /// The length of the resulting hash. 37 | pub hash_length: u32, 38 | 39 | /// The number of lanes. 40 | pub lanes: u32, 41 | 42 | /// The amount of memory requested (KB). 43 | pub mem_cost: u32, 44 | 45 | /// The key. 46 | pub secret: &'a [u8], 47 | 48 | /// The thread mode. 49 | pub thread_mode: ThreadMode, 50 | 51 | /// The number of passes. 52 | pub time_cost: u32, 53 | 54 | /// The variant. 55 | pub variant: Variant, 56 | 57 | /// The version number. 58 | pub version: Version, 59 | } 60 | 61 | impl<'a> Config<'a> { 62 | /// Default configuration used by the original C implementation. 63 | pub fn original() -> Config<'a> { 64 | Config { 65 | ad: &[], 66 | hash_length: 32, 67 | lanes: 1, 68 | mem_cost: 4096, 69 | secret: &[], 70 | thread_mode: ThreadMode::default(), 71 | time_cost: 3, 72 | variant: Variant::Argon2i, 73 | version: Version::Version13, 74 | } 75 | } 76 | 77 | /// OWASP recommended configuration with t=1 and 46 MiB memory. 78 | pub fn owasp1() -> Config<'a> { 79 | Config { 80 | ad: &[], 81 | hash_length: 32, 82 | lanes: 1, 83 | mem_cost: 47104, 84 | secret: &[], 85 | thread_mode: ThreadMode::default(), 86 | time_cost: 1, 87 | variant: Variant::Argon2id, 88 | version: Version::Version13, 89 | } 90 | } 91 | 92 | /// OWASP recommended configuration with t=2 and 19 MiB memory. 93 | pub fn owasp2() -> Config<'a> { 94 | Config { 95 | ad: &[], 96 | hash_length: 32, 97 | lanes: 1, 98 | mem_cost: 19456, 99 | secret: &[], 100 | thread_mode: ThreadMode::default(), 101 | time_cost: 2, 102 | variant: Variant::Argon2id, 103 | version: Version::Version13, 104 | } 105 | } 106 | 107 | /// OWASP recommended configuration with t=3 and 12 MiB memory. 108 | pub fn owasp3() -> Config<'a> { 109 | Config { 110 | ad: &[], 111 | hash_length: 32, 112 | lanes: 1, 113 | mem_cost: 12288, 114 | secret: &[], 115 | thread_mode: ThreadMode::default(), 116 | time_cost: 3, 117 | variant: Variant::Argon2id, 118 | version: Version::Version13, 119 | } 120 | } 121 | 122 | /// OWASP recommended configuration with t=4 and 9 MiB memory. 123 | pub fn owasp4() -> Config<'a> { 124 | Config { 125 | ad: &[], 126 | hash_length: 32, 127 | lanes: 1, 128 | mem_cost: 9216, 129 | secret: &[], 130 | thread_mode: ThreadMode::default(), 131 | time_cost: 4, 132 | variant: Variant::Argon2id, 133 | version: Version::Version13, 134 | } 135 | } 136 | 137 | /// OWASP recommended configuration with t=5 and 7 MiB memory. 138 | pub fn owasp5() -> Config<'a> { 139 | Config { 140 | ad: &[], 141 | hash_length: 32, 142 | lanes: 1, 143 | mem_cost: 7168, 144 | secret: &[], 145 | thread_mode: ThreadMode::default(), 146 | time_cost: 5, 147 | variant: Variant::Argon2id, 148 | version: Version::Version13, 149 | } 150 | } 151 | 152 | /// RFC9106 recommended configuration with t=1 and 2 GiB memory. 153 | pub fn rfc9106() -> Config<'a> { 154 | Config { 155 | ad: &[], 156 | hash_length: 32, 157 | lanes: 1, 158 | mem_cost: 2097152, 159 | secret: &[], 160 | thread_mode: ThreadMode::default(), 161 | time_cost: 1, 162 | variant: Variant::Argon2id, 163 | version: Version::Version13, 164 | } 165 | } 166 | 167 | /// RFC9106 recommended configuration for memory-constrained environments. 168 | pub fn rfc9106_low_mem() -> Config<'a> { 169 | Config { 170 | ad: &[], 171 | hash_length: 32, 172 | lanes: 1, 173 | mem_cost: 65536, 174 | secret: &[], 175 | thread_mode: ThreadMode::default(), 176 | time_cost: 3, 177 | variant: Variant::Argon2id, 178 | version: Version::Version13, 179 | } 180 | } 181 | 182 | pub fn uses_sequential(&self) -> bool { 183 | self.thread_mode == ThreadMode::Sequential || self.lanes == 1 184 | } 185 | } 186 | 187 | impl<'a> Default for Config<'a> { 188 | /// OWASP recommended configuration with t=2 and 19 MiB memory. 189 | fn default() -> Config<'a> { 190 | Config::owasp2() 191 | } 192 | } 193 | 194 | #[cfg(test)] 195 | mod tests { 196 | 197 | use crate::config::Config; 198 | use crate::thread_mode::ThreadMode; 199 | use crate::variant::Variant; 200 | use crate::version::Version; 201 | 202 | #[test] 203 | fn default_returns_correct_instance() { 204 | let config = Config::default(); 205 | assert_eq!(config.ad, &[]); 206 | assert_eq!(config.hash_length, 32); 207 | assert_eq!(config.lanes, 1); 208 | assert_eq!(config.mem_cost, 19 * 1024); 209 | assert_eq!(config.secret, &[]); 210 | assert_eq!(config.thread_mode, ThreadMode::Sequential); 211 | assert_eq!(config.time_cost, 2); 212 | assert_eq!(config.variant, Variant::Argon2id); 213 | assert_eq!(config.version, Version::Version13); 214 | } 215 | 216 | #[test] 217 | fn original_returns_correct_instance() { 218 | let config = Config::original(); 219 | assert_eq!(config.ad, &[]); 220 | assert_eq!(config.hash_length, 32); 221 | assert_eq!(config.lanes, 1); 222 | assert_eq!(config.mem_cost, 4096); 223 | assert_eq!(config.secret, &[]); 224 | assert_eq!(config.thread_mode, ThreadMode::Sequential); 225 | assert_eq!(config.time_cost, 3); 226 | assert_eq!(config.variant, Variant::Argon2i); 227 | assert_eq!(config.version, Version::Version13); 228 | } 229 | 230 | #[test] 231 | fn owasp1_returns_correct_instance() { 232 | let config = Config::owasp1(); 233 | assert_eq!(config.ad, &[]); 234 | assert_eq!(config.hash_length, 32); 235 | assert_eq!(config.lanes, 1); 236 | assert_eq!(config.mem_cost, 46 * 1024); 237 | assert_eq!(config.secret, &[]); 238 | assert_eq!(config.thread_mode, ThreadMode::Sequential); 239 | assert_eq!(config.time_cost, 1); 240 | assert_eq!(config.variant, Variant::Argon2id); 241 | assert_eq!(config.version, Version::Version13); 242 | } 243 | 244 | #[test] 245 | fn owasp2_returns_correct_instance() { 246 | let config = Config::owasp2(); 247 | assert_eq!(config.ad, &[]); 248 | assert_eq!(config.hash_length, 32); 249 | assert_eq!(config.lanes, 1); 250 | assert_eq!(config.mem_cost, 19 * 1024); 251 | assert_eq!(config.secret, &[]); 252 | assert_eq!(config.thread_mode, ThreadMode::Sequential); 253 | assert_eq!(config.time_cost, 2); 254 | assert_eq!(config.variant, Variant::Argon2id); 255 | assert_eq!(config.version, Version::Version13); 256 | } 257 | 258 | #[test] 259 | fn owasp3_returns_correct_instance() { 260 | let config = Config::owasp3(); 261 | assert_eq!(config.ad, &[]); 262 | assert_eq!(config.hash_length, 32); 263 | assert_eq!(config.lanes, 1); 264 | assert_eq!(config.mem_cost, 12 * 1024); 265 | assert_eq!(config.secret, &[]); 266 | assert_eq!(config.thread_mode, ThreadMode::Sequential); 267 | assert_eq!(config.time_cost, 3); 268 | assert_eq!(config.variant, Variant::Argon2id); 269 | assert_eq!(config.version, Version::Version13); 270 | } 271 | 272 | #[test] 273 | fn owasp4_returns_correct_instance() { 274 | let config = Config::owasp4(); 275 | assert_eq!(config.ad, &[]); 276 | assert_eq!(config.hash_length, 32); 277 | assert_eq!(config.lanes, 1); 278 | assert_eq!(config.mem_cost, 9 * 1024); 279 | assert_eq!(config.secret, &[]); 280 | assert_eq!(config.thread_mode, ThreadMode::Sequential); 281 | assert_eq!(config.time_cost, 4); 282 | assert_eq!(config.variant, Variant::Argon2id); 283 | assert_eq!(config.version, Version::Version13); 284 | } 285 | 286 | #[test] 287 | fn owasp5_returns_correct_instance() { 288 | let config = Config::owasp5(); 289 | assert_eq!(config.ad, &[]); 290 | assert_eq!(config.hash_length, 32); 291 | assert_eq!(config.lanes, 1); 292 | assert_eq!(config.mem_cost, 7 * 1024); 293 | assert_eq!(config.secret, &[]); 294 | assert_eq!(config.thread_mode, ThreadMode::Sequential); 295 | assert_eq!(config.time_cost, 5); 296 | assert_eq!(config.variant, Variant::Argon2id); 297 | assert_eq!(config.version, Version::Version13); 298 | } 299 | 300 | #[test] 301 | fn rfc9106_returns_correct_instance() { 302 | let config = Config::rfc9106(); 303 | assert_eq!(config.ad, &[]); 304 | assert_eq!(config.hash_length, 32); 305 | assert_eq!(config.lanes, 1); 306 | assert_eq!(config.mem_cost, 2 * 1024 * 1024); 307 | assert_eq!(config.secret, &[]); 308 | assert_eq!(config.thread_mode, ThreadMode::Sequential); 309 | assert_eq!(config.time_cost, 1); 310 | assert_eq!(config.variant, Variant::Argon2id); 311 | assert_eq!(config.version, Version::Version13); 312 | } 313 | 314 | #[test] 315 | fn rfc9106_low_mem_returns_correct_instance() { 316 | let config = Config::rfc9106_low_mem(); 317 | assert_eq!(config.ad, &[]); 318 | assert_eq!(config.hash_length, 32); 319 | assert_eq!(config.lanes, 1); 320 | assert_eq!(config.mem_cost, 64 * 1024); 321 | assert_eq!(config.secret, &[]); 322 | assert_eq!(config.thread_mode, ThreadMode::Sequential); 323 | assert_eq!(config.time_cost, 3); 324 | assert_eq!(config.variant, Variant::Argon2id); 325 | assert_eq!(config.version, Version::Version13); 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /src/encoding.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Martijn Rijkeboer 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , 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::{engine::general_purpose, Engine as _}; 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. 25 | 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. 35 | pub fn decode_string(encoded: &str) -> Result { 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 = general_purpose::STANDARD_NO_PAD.decode(items[4])?; 43 | let hash = general_purpose::STANDARD_NO_PAD.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 = general_purpose::STANDARD_NO_PAD.decode(items[3])?; 59 | let hash = general_purpose::STANDARD_NO_PAD.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 | 75 | fn decode_empty(str: &str) -> Result<()> { 76 | if str == "" { 77 | Ok(()) 78 | } else { 79 | Err(Error::DecodingFail) 80 | } 81 | } 82 | 83 | fn decode_options(str: &str) -> Result { 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 | 96 | fn decode_option(str: &str, name: &str) -> Result { 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 | 109 | fn decode_u32(str: &str) -> Result { 110 | match str.parse() { 111 | Ok(i) => Ok(i), 112 | Err(_) => Err(Error::DecodingFail), 113 | } 114 | } 115 | 116 | fn decode_variant(str: &str) -> Result { 117 | Variant::from_str(str) 118 | } 119 | 120 | fn decode_version(str: &str) -> Result { 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. 134 | 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 | general_purpose::STANDARD_NO_PAD.encode(context.salt), 143 | general_purpose::STANDARD_NO_PAD.encode(hash), 144 | ) 145 | } 146 | 147 | /// Gets the string length of the specified number. 148 | 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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 | -------------------------------------------------------------------------------- /src/core.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Martijn Rijkeboer 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | #![warn(unsafe_op_in_unsafe_fn)] 10 | 11 | use crate::block::Block; 12 | use crate::common; 13 | use crate::context::Context; 14 | use crate::memory::{Memory, UnsafeBlocks}; 15 | use crate::variant::Variant; 16 | use crate::version::Version; 17 | use blake2b_simd::Params; 18 | #[cfg(feature = "crossbeam-utils")] 19 | use crossbeam_utils::thread::scope; 20 | 21 | /// Position of the block currently being operated on. 22 | #[derive(Clone, Debug)] 23 | struct Position { 24 | pass: u32, 25 | lane: u32, 26 | slice: u32, 27 | index: u32, 28 | } 29 | 30 | /// Initializes the memory. 31 | pub fn initialize(context: &Context, memory: &mut Memory) { 32 | fill_first_blocks(context, memory, &mut h0(context)); 33 | } 34 | 35 | /// Fills all the memory blocks. 36 | /// 37 | /// # Safety 38 | /// 39 | /// The provided `context` and `memory` arguments must be consistent. 40 | pub unsafe fn fill_memory_blocks(context: &Context, memory: &mut Memory) { 41 | if context.config.uses_sequential() { 42 | // SAFETY: adhering to the safety contract is delegated to the caller. 43 | unsafe { fill_memory_blocks_st(context, memory) }; 44 | } else { 45 | // SAFETY: adhering to the safety contract is delegated to the caller. 46 | unsafe { fill_memory_blocks_mt(context, memory) }; 47 | } 48 | } 49 | 50 | /// Calculates the final hash and returns it. 51 | pub fn finalize(context: &Context, memory: &Memory) -> Vec { 52 | let mut blockhash = memory[context.lane_length - 1].clone(); 53 | for l in 1..context.config.lanes { 54 | let last_block_in_lane = l * context.lane_length + (context.lane_length - 1); 55 | blockhash ^= &memory[last_block_in_lane]; 56 | } 57 | 58 | let mut hash = vec![0u8; context.config.hash_length as usize]; 59 | hprime(hash.as_mut_slice(), blockhash.as_u8()); 60 | hash 61 | } 62 | 63 | fn blake2b(out: &mut [u8], input: &[&[u8]]) { 64 | let mut blake = Params::new().hash_length(out.len()).to_state(); 65 | for slice in input { 66 | blake.update(slice); 67 | } 68 | out.copy_from_slice(blake.finalize().as_bytes()); 69 | } 70 | 71 | fn f_bla_mka(x: u64, y: u64) -> u64 { 72 | let m = 0xFFFF_FFFFu64; 73 | let xy = (x & m) * (y & m); 74 | x.wrapping_add(y.wrapping_add(xy.wrapping_add(xy))) 75 | } 76 | 77 | fn fill_block(prev_block: &Block, ref_block: &Block, next_block: &mut Block, with_xor: bool) { 78 | let mut block_r = ref_block.clone(); 79 | block_r ^= prev_block; 80 | let mut block_tmp = block_r.clone(); 81 | 82 | // Now block_r = ref_block + prev_block and block_tmp = ref_block + prev_block 83 | if with_xor { 84 | // Saving the next block contents for XOR over 85 | block_tmp ^= next_block; 86 | // Now block_r = ref_block + prev_block and 87 | // block_tmp = ref_block + prev_block + next_block 88 | } 89 | 90 | // Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then 91 | // (16,17,..31)... finally (112,113,...127) 92 | for i in 0..8 { 93 | let mut v0 = block_r[16 * i]; 94 | let mut v1 = block_r[16 * i + 1]; 95 | let mut v2 = block_r[16 * i + 2]; 96 | let mut v3 = block_r[16 * i + 3]; 97 | let mut v4 = block_r[16 * i + 4]; 98 | let mut v5 = block_r[16 * i + 5]; 99 | let mut v6 = block_r[16 * i + 6]; 100 | let mut v7 = block_r[16 * i + 7]; 101 | let mut v8 = block_r[16 * i + 8]; 102 | let mut v9 = block_r[16 * i + 9]; 103 | let mut v10 = block_r[16 * i + 10]; 104 | let mut v11 = block_r[16 * i + 11]; 105 | let mut v12 = block_r[16 * i + 12]; 106 | let mut v13 = block_r[16 * i + 13]; 107 | let mut v14 = block_r[16 * i + 14]; 108 | let mut v15 = block_r[16 * i + 15]; 109 | 110 | p( 111 | &mut v0, &mut v1, &mut v2, &mut v3, &mut v4, &mut v5, &mut v6, &mut v7, &mut v8, 112 | &mut v9, &mut v10, &mut v11, &mut v12, &mut v13, &mut v14, &mut v15, 113 | ); 114 | 115 | block_r[16 * i] = v0; 116 | block_r[16 * i + 1] = v1; 117 | block_r[16 * i + 2] = v2; 118 | block_r[16 * i + 3] = v3; 119 | block_r[16 * i + 4] = v4; 120 | block_r[16 * i + 5] = v5; 121 | block_r[16 * i + 6] = v6; 122 | block_r[16 * i + 7] = v7; 123 | block_r[16 * i + 8] = v8; 124 | block_r[16 * i + 9] = v9; 125 | block_r[16 * i + 10] = v10; 126 | block_r[16 * i + 11] = v11; 127 | block_r[16 * i + 12] = v12; 128 | block_r[16 * i + 13] = v13; 129 | block_r[16 * i + 14] = v14; 130 | block_r[16 * i + 15] = v15; 131 | } 132 | 133 | // Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then 134 | // (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) 135 | for i in 0..8 { 136 | let mut v0 = block_r[2 * i]; 137 | let mut v1 = block_r[2 * i + 1]; 138 | let mut v2 = block_r[2 * i + 16]; 139 | let mut v3 = block_r[2 * i + 17]; 140 | let mut v4 = block_r[2 * i + 32]; 141 | let mut v5 = block_r[2 * i + 33]; 142 | let mut v6 = block_r[2 * i + 48]; 143 | let mut v7 = block_r[2 * i + 49]; 144 | let mut v8 = block_r[2 * i + 64]; 145 | let mut v9 = block_r[2 * i + 65]; 146 | let mut v10 = block_r[2 * i + 80]; 147 | let mut v11 = block_r[2 * i + 81]; 148 | let mut v12 = block_r[2 * i + 96]; 149 | let mut v13 = block_r[2 * i + 97]; 150 | let mut v14 = block_r[2 * i + 112]; 151 | let mut v15 = block_r[2 * i + 113]; 152 | 153 | p( 154 | &mut v0, &mut v1, &mut v2, &mut v3, &mut v4, &mut v5, &mut v6, &mut v7, &mut v8, 155 | &mut v9, &mut v10, &mut v11, &mut v12, &mut v13, &mut v14, &mut v15, 156 | ); 157 | 158 | block_r[2 * i] = v0; 159 | block_r[2 * i + 1] = v1; 160 | block_r[2 * i + 16] = v2; 161 | block_r[2 * i + 17] = v3; 162 | block_r[2 * i + 32] = v4; 163 | block_r[2 * i + 33] = v5; 164 | block_r[2 * i + 48] = v6; 165 | block_r[2 * i + 49] = v7; 166 | block_r[2 * i + 64] = v8; 167 | block_r[2 * i + 65] = v9; 168 | block_r[2 * i + 80] = v10; 169 | block_r[2 * i + 81] = v11; 170 | block_r[2 * i + 96] = v12; 171 | block_r[2 * i + 97] = v13; 172 | block_r[2 * i + 112] = v14; 173 | block_r[2 * i + 113] = v15; 174 | } 175 | 176 | block_tmp.copy_to(next_block); 177 | *next_block ^= &block_r; 178 | } 179 | 180 | fn fill_first_blocks(context: &Context, memory: &mut Memory, h0: &mut [u8]) { 181 | for lane in 0..context.config.lanes { 182 | let start = common::PREHASH_DIGEST_LENGTH; 183 | // H'(H0||0||i) 184 | h0[start..(start + 4)].clone_from_slice(&u32::to_le_bytes(0)); 185 | h0[(start + 4)..(start + 8)].clone_from_slice(&u32::to_le_bytes(lane)); 186 | hprime(memory[(lane, 0)].as_u8_mut(), &h0); 187 | 188 | // H'(H0||1||i) 189 | h0[start..(start + 4)].clone_from_slice(&u32::to_le_bytes(1)); 190 | hprime(memory[(lane, 1)].as_u8_mut(), &h0); 191 | } 192 | } 193 | 194 | /// # Safety 195 | /// 196 | /// The provided `context` and `memory` arguments must be consistent. 197 | #[cfg(feature = "crossbeam-utils")] 198 | unsafe fn fill_memory_blocks_mt(context: &Context, memory: &mut Memory) { 199 | let mem = &memory.as_unsafe_blocks(); 200 | for p in 0..context.config.time_cost { 201 | for s in 0..common::SYNC_POINTS { 202 | let _ = scope(|scoped| { 203 | for l in 0..context.config.lanes { 204 | let position = Position { 205 | pass: p, 206 | lane: l, 207 | slice: s, 208 | index: 0, 209 | }; 210 | scoped.spawn(move |_| { 211 | // SAFETY: segments are processed slicewise and then each is handed 212 | // (exactly once) to a worker thread. The threads synchronize when the 213 | // `scope` is dropped, before the next slice is processed. The caller 214 | // promises that `context` and `memory` are consistent, and `position` 215 | // reflects the current lane and slice. 216 | unsafe { fill_segment(context, &position, mem) }; 217 | }); 218 | } 219 | }); 220 | } 221 | } 222 | } 223 | 224 | #[cfg(not(feature = "crossbeam-utils"))] 225 | unsafe fn fill_memory_blocks_mt(_: &Context, _: &mut Memory) { 226 | unimplemented!() 227 | } 228 | 229 | /// # Safety 230 | /// 231 | /// The provided `context` and `memory` arguments must be consistent. 232 | unsafe fn fill_memory_blocks_st(context: &Context, memory: &mut Memory) { 233 | for p in 0..context.config.time_cost { 234 | for s in 0..common::SYNC_POINTS { 235 | for l in 0..context.config.lanes { 236 | let position = Position { 237 | pass: p, 238 | lane: l, 239 | slice: s, 240 | index: 0, 241 | }; 242 | // SAFETY: segments are processed slicewise and then sequentially, the caller 243 | // promises that `context` and `memory` are consistent, and `position` reflects the 244 | // current lane and slice. 245 | unsafe { fill_segment(context, &position, &memory.as_unsafe_blocks()) }; 246 | } 247 | } 248 | } 249 | } 250 | 251 | /// # Safety 252 | /// 253 | /// The memory must be processed slicewise, and this function must be called exactly once per 254 | /// segment, where a segment is the intersection between the slice being processed and a lane. 255 | /// That is, within a slice, this function is called exactly once per lane. 256 | /// 257 | /// If segments are filled in parallel, synchronization points are required between slices. 258 | /// 259 | /// Finally, the provided `context` and `memory` arguments must be consistent, and `position` must 260 | /// reflect the correct current lane and slice. 261 | unsafe fn fill_segment(context: &Context, position: &Position, memory: &UnsafeBlocks) { 262 | let mut position = position.clone(); 263 | let data_independent_addressing = (context.config.variant == Variant::Argon2i) 264 | || (context.config.variant == Variant::Argon2id && position.pass == 0) 265 | && (position.slice < (common::SYNC_POINTS / 2)); 266 | let zero_block = Block::zero(); 267 | let mut input_block = Block::zero(); 268 | let mut address_block = Block::zero(); 269 | 270 | if data_independent_addressing { 271 | input_block[0] = position.pass as u64; 272 | input_block[1] = position.lane as u64; 273 | input_block[2] = position.slice as u64; 274 | input_block[3] = context.memory_blocks as u64; 275 | input_block[4] = context.config.time_cost as u64; 276 | input_block[5] = context.config.variant.as_u64(); 277 | } 278 | 279 | let mut starting_index = 0u32; 280 | 281 | if position.pass == 0 && position.slice == 0 { 282 | starting_index = 2; 283 | 284 | // Don't forget to generate the first block of addresses: 285 | if data_independent_addressing { 286 | next_addresses(&mut address_block, &mut input_block, &zero_block); 287 | } 288 | } 289 | 290 | let mut curr_offset = (position.lane * context.lane_length) 291 | + (position.slice * context.segment_length) 292 | + starting_index; 293 | 294 | let mut prev_offset = if curr_offset % context.lane_length == 0 { 295 | // Last block in this lane 296 | curr_offset + context.lane_length - 1 297 | } else { 298 | curr_offset - 1 299 | }; 300 | 301 | let mut pseudo_rand; 302 | for i in starting_index..context.segment_length { 303 | // 1.1 Rotating prev_offset if needed 304 | if curr_offset % context.lane_length == 1 { 305 | prev_offset = curr_offset - 1; 306 | } 307 | 308 | // 1.2 Computing the index of the reference block 309 | // 1.2.1 Taking pseudo-random value from the previous block 310 | if data_independent_addressing { 311 | if i % common::ADDRESSES_IN_BLOCK == 0 { 312 | next_addresses(&mut address_block, &mut input_block, &zero_block); 313 | } 314 | pseudo_rand = address_block[(i % common::ADDRESSES_IN_BLOCK) as usize]; 315 | } else { 316 | assert!(prev_offset < context.memory_blocks); 317 | assert!(prev_offset / context.lane_length == position.lane); 318 | // SAFETY: `prev_offset` is in bounds and on this lane, so the block isn't mutably 319 | // aliased or mutated from another thread (in the current slice). 320 | pseudo_rand = unsafe { memory.get_unchecked(prev_offset as usize) }[0]; 321 | } 322 | 323 | // 1.2.2 Computing the lane of the reference block 324 | // If (position.pass == 0) && (position.slice == 0): can not reference other lanes yet 325 | let ref_lane = if (position.pass == 0) && (position.slice == 0) { 326 | position.lane as u64 327 | } else { 328 | (pseudo_rand >> 32) % context.config.lanes as u64 329 | }; 330 | 331 | // 1.2.3 Computing the number of possible reference block within the lane. 332 | position.index = i; 333 | let pseudo_rand_u32 = (pseudo_rand & 0xFFFF_FFFF) as u32; 334 | let same_lane = ref_lane == (position.lane as u64); 335 | let ref_index = index_alpha(context, &position, pseudo_rand_u32, same_lane); 336 | 337 | // 2 Creating a new block 338 | let index = context.lane_length as u64 * ref_lane + ref_index as u64; 339 | assert!(curr_offset < context.memory_blocks); 340 | assert!(prev_offset < context.memory_blocks); 341 | assert!(index < context.memory_blocks as u64); 342 | assert!(curr_offset != prev_offset && (curr_offset as u64) != index); 343 | assert!(prev_offset / context.lane_length == position.lane); 344 | assert!( 345 | index / (context.lane_length as u64) == position.lane as u64 346 | || index % (context.lane_length as u64) / (context.segment_length as u64) 347 | != position.slice as u64 348 | ); 349 | // SAFETY: `curr_offset`, `prev_offset` and `index` are in bounds and refer to different 350 | // blocks; and during the processing of the current slice: 351 | // - `curr_offset` is only accessed by this thread, and there are no other references to 352 | // the corresponding block; 353 | // - `prev_offset` is on the same lane as `curr_offset`, and therefore the corresponding 354 | // block isn't mutably aliased or mutated from another thread; 355 | // - `index` is either on the same lane or on a different slice, and in both cases it will 356 | // not be mutably aliased or mutated from another thread. 357 | let curr_block = unsafe { memory.get_mut_unchecked(curr_offset as usize) }; 358 | let prev_block = unsafe { memory.get_unchecked(prev_offset as usize) }; 359 | let ref_block = unsafe { memory.get_unchecked(index as usize) }; 360 | if context.config.version == Version::Version10 || position.pass == 0 { 361 | fill_block(prev_block, ref_block, curr_block, false); 362 | } else { 363 | fill_block(prev_block, ref_block, curr_block, true); 364 | } 365 | 366 | curr_offset += 1; 367 | prev_offset += 1; 368 | } 369 | } 370 | 371 | fn g(a: &mut u64, b: &mut u64, c: &mut u64, d: &mut u64) { 372 | *a = f_bla_mka(*a, *b); 373 | *d = rotr64(*d ^ *a, 32); 374 | *c = f_bla_mka(*c, *d); 375 | *b = rotr64(*b ^ *c, 24); 376 | *a = f_bla_mka(*a, *b); 377 | *d = rotr64(*d ^ *a, 16); 378 | *c = f_bla_mka(*c, *d); 379 | *b = rotr64(*b ^ *c, 63); 380 | } 381 | 382 | fn h0(context: &Context) -> [u8; common::PREHASH_SEED_LENGTH] { 383 | let input = [ 384 | &u32::to_le_bytes(context.config.lanes), 385 | &u32::to_le_bytes(context.config.hash_length), 386 | &u32::to_le_bytes(context.config.mem_cost), 387 | &u32::to_le_bytes(context.config.time_cost), 388 | &u32::to_le_bytes(context.config.version.as_u32()), 389 | &u32::to_le_bytes(context.config.variant.as_u32()), 390 | &len_as_32le(context.pwd), 391 | context.pwd, 392 | &len_as_32le(context.salt), 393 | context.salt, 394 | &len_as_32le(context.config.secret), 395 | context.config.secret, 396 | &len_as_32le(context.config.ad), 397 | context.config.ad, 398 | ]; 399 | let mut out = [0u8; common::PREHASH_SEED_LENGTH]; 400 | blake2b(&mut out[0..common::PREHASH_DIGEST_LENGTH], &input); 401 | out 402 | } 403 | 404 | fn hprime(out: &mut [u8], input: &[u8]) { 405 | let out_len = out.len(); 406 | if out_len <= common::BLAKE2B_OUT_LENGTH { 407 | blake2b(out, &[&u32::to_le_bytes(out_len as u32), input]); 408 | } else { 409 | let ai_len = 32; 410 | let mut out_buffer = [0u8; common::BLAKE2B_OUT_LENGTH]; 411 | let mut in_buffer = [0u8; common::BLAKE2B_OUT_LENGTH]; 412 | blake2b(&mut out_buffer, &[&u32::to_le_bytes(out_len as u32), input]); 413 | out[0..ai_len].clone_from_slice(&out_buffer[0..ai_len]); 414 | let mut out_pos = ai_len; 415 | let mut to_produce = out_len - ai_len; 416 | 417 | while to_produce > common::BLAKE2B_OUT_LENGTH { 418 | in_buffer.clone_from_slice(&out_buffer); 419 | blake2b(&mut out_buffer, &[&in_buffer]); 420 | out[out_pos..out_pos + ai_len].clone_from_slice(&out_buffer[0..ai_len]); 421 | out_pos += ai_len; 422 | to_produce -= ai_len; 423 | } 424 | blake2b(&mut out[out_pos..out_len], &[&out_buffer]); 425 | } 426 | } 427 | 428 | fn index_alpha(context: &Context, position: &Position, pseudo_rand: u32, same_lane: bool) -> u32 { 429 | // Pass 0: 430 | // - This lane: all already finished segments plus already constructed blocks in this segment 431 | // - Other lanes: all already finished segments 432 | // Pass 1+: 433 | // - This lane: (SYNC_POINTS - 1) last segments plus already constructed blocks in this segment 434 | // - Other lanes : (SYNC_POINTS - 1) last segments 435 | let reference_area_size: u32 = if position.pass == 0 { 436 | // First pass 437 | if position.slice == 0 { 438 | // First slice 439 | position.index - 1 440 | } else if same_lane { 441 | // The same lane => add current segment 442 | position.slice * context.segment_length + position.index - 1 443 | } else if position.index == 0 { 444 | position.slice * context.segment_length - 1 445 | } else { 446 | position.slice * context.segment_length 447 | } 448 | } else { 449 | // Second pass 450 | if same_lane { 451 | context.lane_length - context.segment_length + position.index - 1 452 | } else if position.index == 0 { 453 | context.lane_length - context.segment_length - 1 454 | } else { 455 | context.lane_length - context.segment_length 456 | } 457 | }; 458 | let reference_area_size = reference_area_size as u64; 459 | let mut relative_position = pseudo_rand as u64; 460 | relative_position = (relative_position * relative_position) >> 32; 461 | relative_position = reference_area_size - 1 - ((reference_area_size * relative_position) >> 32); 462 | 463 | // 1.2.5 Computing starting position 464 | let start_position: u32 = if position.pass != 0 { 465 | if position.slice == common::SYNC_POINTS - 1 { 466 | 0u32 467 | } else { 468 | (position.slice + 1) * context.segment_length 469 | } 470 | } else { 471 | 0u32 472 | }; 473 | let start_position = start_position as u64; 474 | 475 | // 1.2.6. Computing absolute position 476 | ((start_position + relative_position) % context.lane_length as u64) as u32 477 | } 478 | 479 | fn len_as_32le(slice: &[u8]) -> [u8; 4] { 480 | u32::to_le_bytes(slice.len() as u32) 481 | } 482 | 483 | fn next_addresses(address_block: &mut Block, input_block: &mut Block, zero_block: &Block) { 484 | input_block[6] += 1; 485 | fill_block(zero_block, input_block, address_block, false); 486 | fill_block(zero_block, &address_block.clone(), address_block, false); 487 | } 488 | 489 | fn p( 490 | v0: &mut u64, 491 | v1: &mut u64, 492 | v2: &mut u64, 493 | v3: &mut u64, 494 | v4: &mut u64, 495 | v5: &mut u64, 496 | v6: &mut u64, 497 | v7: &mut u64, 498 | v8: &mut u64, 499 | v9: &mut u64, 500 | v10: &mut u64, 501 | v11: &mut u64, 502 | v12: &mut u64, 503 | v13: &mut u64, 504 | v14: &mut u64, 505 | v15: &mut u64, 506 | ) { 507 | g(v0, v4, v8, v12); 508 | g(v1, v5, v9, v13); 509 | g(v2, v6, v10, v14); 510 | g(v3, v7, v11, v15); 511 | g(v0, v5, v10, v15); 512 | g(v1, v6, v11, v12); 513 | g(v2, v7, v8, v13); 514 | g(v3, v4, v9, v14); 515 | } 516 | 517 | fn rotr64(w: u64, c: u32) -> u64 { 518 | (w >> c) | (w << (64 - c)) 519 | } 520 | -------------------------------------------------------------------------------- /tests/integration_test.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Martijn Rijkeboer 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | // These tests are based on Argon's test.c test suite. 10 | 11 | use argon2::{Config, Error, ThreadMode, Variant, Version}; 12 | use hex::ToHex; 13 | 14 | #[cfg(not(debug_assertions))] 15 | #[test] 16 | fn test_argon2d_version10_1() { 17 | hash_test( 18 | Variant::Argon2d, 19 | Version::Version10, 20 | 2, 21 | 65536, 22 | 1, 23 | b"password", 24 | b"somesalt", 25 | "2ec0d925358f5830caf0c1cc8a3ee58b34505759428b859c79b72415f51f9221", 26 | "$argon2d$m=65536,t=2,p=1$c29tZXNhbHQ\ 27 | $LsDZJTWPWDDK8MHMij7lizRQV1lCi4WcebckFfUfkiE", 28 | ); 29 | } 30 | 31 | #[cfg(not(debug_assertions))] 32 | #[test] 33 | fn test_argon2d_version10_2() { 34 | hash_test( 35 | Variant::Argon2d, 36 | Version::Version10, 37 | 2, 38 | 1048576, 39 | 1, 40 | b"password", 41 | b"somesalt", 42 | "28ae0d1037991b55faec18425274d5f9169b2f44bee19b45b7a561d489d6019a", 43 | "$argon2d$m=1048576,t=2,p=1$c29tZXNhbHQ\ 44 | $KK4NEDeZG1X67BhCUnTV+RabL0S+4ZtFt6Vh1InWAZo", 45 | ); 46 | } 47 | 48 | #[cfg(not(debug_assertions))] 49 | #[test] 50 | fn test_argon2d_version10_3() { 51 | hash_test( 52 | Variant::Argon2d, 53 | Version::Version10, 54 | 2, 55 | 262144, 56 | 1, 57 | b"password", 58 | b"somesalt", 59 | "41c89760d85b80ba1be7e959ebd16390bfb4176db9466d70f670457ccade4ec8", 60 | "$argon2d$m=262144,t=2,p=1$c29tZXNhbHQ\ 61 | $QciXYNhbgLob5+lZ69FjkL+0F225Rm1w9nBFfMreTsg", 62 | ); 63 | } 64 | 65 | #[test] 66 | fn test_argon2d_version10_4() { 67 | hash_test( 68 | Variant::Argon2d, 69 | Version::Version10, 70 | 2, 71 | 256, 72 | 1, 73 | b"password", 74 | b"somesalt", 75 | "bd404868ff00c52e7543c8332e6a772a5724892d7e328d5cf253bbc8e726b371", 76 | "$argon2d$m=256,t=2,p=1$c29tZXNhbHQ\ 77 | $vUBIaP8AxS51Q8gzLmp3KlckiS1+Mo1c8lO7yOcms3E", 78 | ); 79 | } 80 | 81 | #[test] 82 | fn test_argon2d_version10_5() { 83 | hash_test( 84 | Variant::Argon2d, 85 | Version::Version10, 86 | 2, 87 | 256, 88 | 2, 89 | b"password", 90 | b"somesalt", 91 | "6a91d02b9f8854ba0841f04aa6e53c1d3374c0a0c646b8e431b03de805b91ec3", 92 | "$argon2d$m=256,t=2,p=2$c29tZXNhbHQ\ 93 | $apHQK5+IVLoIQfBKpuU8HTN0wKDGRrjkMbA96AW5HsM", 94 | ); 95 | } 96 | 97 | #[cfg(not(debug_assertions))] 98 | #[test] 99 | fn test_argon2d_version10_6() { 100 | hash_test( 101 | Variant::Argon2d, 102 | Version::Version10, 103 | 1, 104 | 65536, 105 | 1, 106 | b"password", 107 | b"somesalt", 108 | "05d1d0a85f499e9397b9c1c936b20f366a7273ccf259e2edfdb44ca8f86dd11f", 109 | "$argon2d$m=65536,t=1,p=1$c29tZXNhbHQ\ 110 | $BdHQqF9JnpOXucHJNrIPNmpyc8zyWeLt/bRMqPht0R8", 111 | ); 112 | } 113 | 114 | #[cfg(not(debug_assertions))] 115 | #[test] 116 | fn test_argon2d_version10_7() { 117 | hash_test( 118 | Variant::Argon2d, 119 | Version::Version10, 120 | 4, 121 | 65536, 122 | 1, 123 | b"password", 124 | b"somesalt", 125 | "99e787faced20949df5a3b9c8620712b45cfea061716adf8b13efb7feee44084", 126 | "$argon2d$m=65536,t=4,p=1$c29tZXNhbHQ\ 127 | $meeH+s7SCUnfWjuchiBxK0XP6gYXFq34sT77f+7kQIQ", 128 | ); 129 | } 130 | 131 | #[cfg(not(debug_assertions))] 132 | #[test] 133 | fn test_argon2d_version10_8() { 134 | hash_test( 135 | Variant::Argon2d, 136 | Version::Version10, 137 | 2, 138 | 65536, 139 | 1, 140 | b"differentpassword", 141 | b"somesalt", 142 | "b9c2e4a4ec9a6db99ca4e2b549dd64256305b95754b0a70ad757cd1ff7eed4a4", 143 | "$argon2d$m=65536,t=2,p=1$c29tZXNhbHQ\ 144 | $ucLkpOyabbmcpOK1Sd1kJWMFuVdUsKcK11fNH/fu1KQ", 145 | ); 146 | } 147 | 148 | #[cfg(not(debug_assertions))] 149 | #[test] 150 | fn test_argon2d_version10_9() { 151 | hash_test( 152 | Variant::Argon2d, 153 | Version::Version10, 154 | 2, 155 | 65536, 156 | 1, 157 | b"password", 158 | b"diffsalt", 159 | "d1da14196c233e0a3cb687aa6e6c465cb50669ffdb891158c2b75722395f14d5", 160 | "$argon2d$m=65536,t=2,p=1$ZGlmZnNhbHQ\ 161 | $0doUGWwjPgo8toeqbmxGXLUGaf/biRFYwrdXIjlfFNU", 162 | ); 163 | } 164 | 165 | #[cfg(not(debug_assertions))] 166 | #[test] 167 | fn test_argon2d_version13_1() { 168 | hash_test( 169 | Variant::Argon2d, 170 | Version::Version13, 171 | 2, 172 | 65536, 173 | 1, 174 | b"password", 175 | b"somesalt", 176 | "955e5d5b163a1b60bba35fc36d0496474fba4f6b59ad53628666f07fb2f93eaf", 177 | "$argon2d$v=19$m=65536,t=2,p=1$c29tZXNhbHQ\ 178 | $lV5dWxY6G2C7o1/DbQSWR0+6T2tZrVNihmbwf7L5Pq8", 179 | ); 180 | } 181 | 182 | #[cfg(not(debug_assertions))] 183 | #[test] 184 | fn test_argon2d_version13_2() { 185 | hash_test( 186 | Variant::Argon2d, 187 | Version::Version13, 188 | 2, 189 | 1048576, 190 | 1, 191 | b"password", 192 | b"somesalt", 193 | "c2d680a6e7ac1b75e2c2b1e5e71b1701584869492efea4a76ccc91fec622d1ae", 194 | "$argon2d$v=19$m=1048576,t=2,p=1$c29tZXNhbHQ\ 195 | $wtaApuesG3XiwrHl5xsXAVhIaUku/qSnbMyR/sYi0a4", 196 | ); 197 | } 198 | 199 | #[cfg(not(debug_assertions))] 200 | #[test] 201 | fn test_argon2d_version13_3() { 202 | hash_test( 203 | Variant::Argon2d, 204 | Version::Version13, 205 | 2, 206 | 262144, 207 | 1, 208 | b"password", 209 | b"somesalt", 210 | "9678b3379ce20ddc96fa38f045c8cdc96282287ff2848efd867582b9528a2155", 211 | "$argon2d$v=19$m=262144,t=2,p=1$c29tZXNhbHQ\ 212 | $lnizN5ziDdyW+jjwRcjNyWKCKH/yhI79hnWCuVKKIVU", 213 | ); 214 | } 215 | 216 | #[test] 217 | fn test_argon2d_version13_4() { 218 | hash_test( 219 | Variant::Argon2d, 220 | Version::Version13, 221 | 2, 222 | 256, 223 | 1, 224 | b"password", 225 | b"somesalt", 226 | "25c4ee8ba448054b49efc804e478b9d823be1f9bd2e99f51d6ec4007a3a1501f", 227 | "$argon2d$v=19$m=256,t=2,p=1$c29tZXNhbHQ\ 228 | $JcTui6RIBUtJ78gE5Hi52CO+H5vS6Z9R1uxAB6OhUB8", 229 | ); 230 | } 231 | 232 | #[test] 233 | fn test_argon2d_version13_5() { 234 | hash_test( 235 | Variant::Argon2d, 236 | Version::Version13, 237 | 2, 238 | 256, 239 | 2, 240 | b"password", 241 | b"somesalt", 242 | "7b69c92d7c3889aad1281dbc8baefc12cc37c80f1c75e33ef2c2d40c28ebc573", 243 | "$argon2d$v=19$m=256,t=2,p=2$c29tZXNhbHQ\ 244 | $e2nJLXw4iarRKB28i678Esw3yA8cdeM+8sLUDCjrxXM", 245 | ); 246 | } 247 | 248 | #[cfg(not(debug_assertions))] 249 | #[test] 250 | fn test_argon2d_version13_6() { 251 | hash_test( 252 | Variant::Argon2d, 253 | Version::Version13, 254 | 1, 255 | 65536, 256 | 1, 257 | b"password", 258 | b"somesalt", 259 | "8193708c030f09b121526b0efdffce738fef0ddb13937a65d43f3f1eab9aa802", 260 | "$argon2d$v=19$m=65536,t=1,p=1$c29tZXNhbHQ\ 261 | $gZNwjAMPCbEhUmsO/f/Oc4/vDdsTk3pl1D8/HquaqAI", 262 | ); 263 | } 264 | 265 | #[cfg(not(debug_assertions))] 266 | #[test] 267 | fn test_argon2d_version13_7() { 268 | hash_test( 269 | Variant::Argon2d, 270 | Version::Version13, 271 | 4, 272 | 65536, 273 | 1, 274 | b"password", 275 | b"somesalt", 276 | "cd1e2816cf8895fffe535b87b7a0aa3612f73c6ce063de83b1e7b4621ca0afe6", 277 | "$argon2d$v=19$m=65536,t=4,p=1$c29tZXNhbHQ\ 278 | $zR4oFs+Ilf/+U1uHt6CqNhL3PGzgY96Dsee0Yhygr+Y", 279 | ); 280 | } 281 | 282 | #[cfg(not(debug_assertions))] 283 | #[test] 284 | fn test_argon2d_version13_8() { 285 | hash_test( 286 | Variant::Argon2d, 287 | Version::Version13, 288 | 2, 289 | 65536, 290 | 1, 291 | b"differentpassword", 292 | b"somesalt", 293 | "a34dafc893182d521ae467bbfdce60f973a1223c4615654efeb0bfef8a930e25", 294 | "$argon2d$v=19$m=65536,t=2,p=1$c29tZXNhbHQ\ 295 | $o02vyJMYLVIa5Ge7/c5g+XOhIjxGFWVO/rC/74qTDiU", 296 | ); 297 | } 298 | 299 | #[cfg(not(debug_assertions))] 300 | #[test] 301 | fn test_argon2d_version13_9() { 302 | hash_test( 303 | Variant::Argon2d, 304 | Version::Version13, 305 | 2, 306 | 65536, 307 | 1, 308 | b"password", 309 | b"diffsalt", 310 | "f00eb8a999a1c6949c61fbbee3c3cece5066e3ff5c79aed421266055e2bf4dd7", 311 | "$argon2d$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ\ 312 | $8A64qZmhxpScYfu+48POzlBm4/9cea7UISZgVeK/Tdc", 313 | ); 314 | } 315 | 316 | #[cfg(not(debug_assertions))] 317 | #[test] 318 | fn test_argon2i_version10_1() { 319 | hash_test( 320 | Variant::Argon2i, 321 | Version::Version10, 322 | 2, 323 | 65536, 324 | 1, 325 | b"password", 326 | b"somesalt", 327 | "f6c4db4a54e2a370627aff3db6176b94a2a209a62c8e36152711802f7b30c694", 328 | "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ\ 329 | $9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ", 330 | ); 331 | } 332 | 333 | #[cfg(not(debug_assertions))] 334 | #[test] 335 | fn test_argon2i_version10_2() { 336 | hash_test( 337 | Variant::Argon2i, 338 | Version::Version10, 339 | 2, 340 | 1048576, 341 | 1, 342 | b"password", 343 | b"somesalt", 344 | "9690ec55d28d3ed32562f2e73ea62b02b018757643a2ae6e79528459de8106e9", 345 | "$argon2i$m=1048576,t=2,p=1$c29tZXNhbHQ\ 346 | $lpDsVdKNPtMlYvLnPqYrArAYdXZDoq5ueVKEWd6BBuk", 347 | ); 348 | } 349 | 350 | #[cfg(not(debug_assertions))] 351 | #[test] 352 | fn test_argon2i_version10_3() { 353 | hash_test( 354 | Variant::Argon2i, 355 | Version::Version10, 356 | 2, 357 | 262144, 358 | 1, 359 | b"password", 360 | b"somesalt", 361 | "3e689aaa3d28a77cf2bc72a51ac53166761751182f1ee292e3f677a7da4c2467", 362 | "$argon2i$m=262144,t=2,p=1$c29tZXNhbHQ\ 363 | $Pmiaqj0op3zyvHKlGsUxZnYXURgvHuKS4/Z3p9pMJGc", 364 | ); 365 | } 366 | 367 | #[test] 368 | fn test_argon2i_version10_4() { 369 | hash_test( 370 | Variant::Argon2i, 371 | Version::Version10, 372 | 2, 373 | 256, 374 | 1, 375 | b"password", 376 | b"somesalt", 377 | "fd4dd83d762c49bdeaf57c47bdcd0c2f1babf863fdeb490df63ede9975fccf06", 378 | "$argon2i$m=256,t=2,p=1$c29tZXNhbHQ\ 379 | $/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY", 380 | ); 381 | } 382 | 383 | #[test] 384 | fn test_argon2i_version10_5() { 385 | hash_test( 386 | Variant::Argon2i, 387 | Version::Version10, 388 | 2, 389 | 256, 390 | 2, 391 | b"password", 392 | b"somesalt", 393 | "b6c11560a6a9d61eac706b79a2f97d68b4463aa3ad87e00c07e2b01e90c564fb", 394 | "$argon2i$m=256,t=2,p=2$c29tZXNhbHQ\ 395 | $tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs", 396 | ); 397 | } 398 | 399 | #[cfg(not(debug_assertions))] 400 | #[test] 401 | fn test_argon2i_version10_6() { 402 | hash_test( 403 | Variant::Argon2i, 404 | Version::Version10, 405 | 1, 406 | 65536, 407 | 1, 408 | b"password", 409 | b"somesalt", 410 | "81630552b8f3b1f48cdb1992c4c678643d490b2b5eb4ff6c4b3438b5621724b2", 411 | "$argon2i$m=65536,t=1,p=1$c29tZXNhbHQ\ 412 | $gWMFUrjzsfSM2xmSxMZ4ZD1JCytetP9sSzQ4tWIXJLI", 413 | ); 414 | } 415 | 416 | #[cfg(not(debug_assertions))] 417 | #[test] 418 | fn test_argon2i_version10_7() { 419 | hash_test( 420 | Variant::Argon2i, 421 | Version::Version10, 422 | 4, 423 | 65536, 424 | 1, 425 | b"password", 426 | b"somesalt", 427 | "f212f01615e6eb5d74734dc3ef40ade2d51d052468d8c69440a3a1f2c1c2847b", 428 | "$argon2i$m=65536,t=4,p=1$c29tZXNhbHQ\ 429 | $8hLwFhXm6110c03D70Ct4tUdBSRo2MaUQKOh8sHChHs", 430 | ); 431 | } 432 | 433 | #[cfg(not(debug_assertions))] 434 | #[test] 435 | fn test_argon2i_version10_8() { 436 | hash_test( 437 | Variant::Argon2i, 438 | Version::Version10, 439 | 2, 440 | 65536, 441 | 1, 442 | b"differentpassword", 443 | b"somesalt", 444 | "e9c902074b6754531a3a0be519e5baf404b30ce69b3f01ac3bf21229960109a3", 445 | "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ\ 446 | $6ckCB0tnVFMaOgvlGeW69ASzDOabPwGsO/ISKZYBCaM", 447 | ); 448 | } 449 | 450 | #[cfg(not(debug_assertions))] 451 | #[test] 452 | fn test_argon2i_version10_9() { 453 | hash_test( 454 | Variant::Argon2i, 455 | Version::Version10, 456 | 2, 457 | 65536, 458 | 1, 459 | b"password", 460 | b"diffsalt", 461 | "79a103b90fe8aef8570cb31fc8b22259778916f8336b7bdac3892569d4f1c497", 462 | "$argon2i$m=65536,t=2,p=1$ZGlmZnNhbHQ\ 463 | $eaEDuQ/orvhXDLMfyLIiWXeJFvgza3vaw4kladTxxJc", 464 | ); 465 | } 466 | 467 | #[cfg(not(debug_assertions))] 468 | #[test] 469 | fn test_argon2i_version13_1() { 470 | hash_test( 471 | Variant::Argon2i, 472 | Version::Version13, 473 | 2, 474 | 65536, 475 | 1, 476 | b"password", 477 | b"somesalt", 478 | "c1628832147d9720c5bd1cfd61367078729f6dfb6f8fea9ff98158e0d7816ed0", 479 | "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ\ 480 | $wWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA", 481 | ); 482 | } 483 | 484 | #[cfg(not(debug_assertions))] 485 | #[test] 486 | fn test_argon2i_version13_2() { 487 | hash_test( 488 | Variant::Argon2i, 489 | Version::Version13, 490 | 2, 491 | 1048576, 492 | 1, 493 | b"password", 494 | b"somesalt", 495 | "d1587aca0922c3b5d6a83edab31bee3c4ebaef342ed6127a55d19b2351ad1f41", 496 | "$argon2i$v=19$m=1048576,t=2,p=1$c29tZXNhbHQ\ 497 | $0Vh6ygkiw7XWqD7asxvuPE667zQu1hJ6VdGbI1GtH0E", 498 | ); 499 | } 500 | 501 | #[cfg(not(debug_assertions))] 502 | #[test] 503 | fn test_argon2i_version13_3() { 504 | hash_test( 505 | Variant::Argon2i, 506 | Version::Version13, 507 | 2, 508 | 262144, 509 | 1, 510 | b"password", 511 | b"somesalt", 512 | "296dbae80b807cdceaad44ae741b506f14db0959267b183b118f9b24229bc7cb", 513 | "$argon2i$v=19$m=262144,t=2,p=1$c29tZXNhbHQ\ 514 | $KW266AuAfNzqrUSudBtQbxTbCVkmexg7EY+bJCKbx8s", 515 | ); 516 | } 517 | 518 | #[test] 519 | fn test_argon2i_version13_4() { 520 | hash_test( 521 | Variant::Argon2i, 522 | Version::Version13, 523 | 2, 524 | 256, 525 | 1, 526 | b"password", 527 | b"somesalt", 528 | "89e9029f4637b295beb027056a7336c414fadd43f6b208645281cb214a56452f", 529 | "$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ\ 530 | $iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8", 531 | ); 532 | } 533 | 534 | #[test] 535 | fn test_argon2i_version13_5() { 536 | hash_test( 537 | Variant::Argon2i, 538 | Version::Version13, 539 | 2, 540 | 256, 541 | 2, 542 | b"password", 543 | b"somesalt", 544 | "4ff5ce2769a1d7f4c8a491df09d41a9fbe90e5eb02155a13e4c01e20cd4eab61", 545 | "$argon2i$v=19$m=256,t=2,p=2$c29tZXNhbHQ\ 546 | $T/XOJ2mh1/TIpJHfCdQan76Q5esCFVoT5MAeIM1Oq2E", 547 | ); 548 | } 549 | 550 | #[cfg(not(debug_assertions))] 551 | #[test] 552 | fn test_argon2i_version13_6() { 553 | hash_test( 554 | Variant::Argon2i, 555 | Version::Version13, 556 | 1, 557 | 65536, 558 | 1, 559 | b"password", 560 | b"somesalt", 561 | "d168075c4d985e13ebeae560cf8b94c3b5d8a16c51916b6f4ac2da3ac11bbecf", 562 | "$argon2i$v=19$m=65536,t=1,p=1$c29tZXNhbHQ\ 563 | $0WgHXE2YXhPr6uVgz4uUw7XYoWxRkWtvSsLaOsEbvs8", 564 | ); 565 | } 566 | 567 | #[cfg(not(debug_assertions))] 568 | #[test] 569 | fn test_argon2i_version13_7() { 570 | hash_test( 571 | Variant::Argon2i, 572 | Version::Version13, 573 | 4, 574 | 65536, 575 | 1, 576 | b"password", 577 | b"somesalt", 578 | "aaa953d58af3706ce3df1aefd4a64a84e31d7f54175231f1285259f88174ce5b", 579 | "$argon2i$v=19$m=65536,t=4,p=1$c29tZXNhbHQ\ 580 | $qqlT1YrzcGzj3xrv1KZKhOMdf1QXUjHxKFJZ+IF0zls", 581 | ); 582 | } 583 | 584 | #[cfg(not(debug_assertions))] 585 | #[test] 586 | fn test_argon2i_version13_8() { 587 | hash_test( 588 | Variant::Argon2i, 589 | Version::Version13, 590 | 2, 591 | 65536, 592 | 1, 593 | b"differentpassword", 594 | b"somesalt", 595 | "14ae8da01afea8700c2358dcef7c5358d9021282bd88663a4562f59fb74d22ee", 596 | "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ\ 597 | $FK6NoBr+qHAMI1jc73xTWNkCEoK9iGY6RWL1n7dNIu4", 598 | ); 599 | } 600 | 601 | #[cfg(not(debug_assertions))] 602 | #[test] 603 | fn test_argon2i_version13_9() { 604 | hash_test( 605 | Variant::Argon2i, 606 | Version::Version13, 607 | 2, 608 | 65536, 609 | 1, 610 | b"password", 611 | b"diffsalt", 612 | "b0357cccfbef91f3860b0dba447b2348cbefecadaf990abfe9cc40726c521271", 613 | "$argon2i$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ\ 614 | $sDV8zPvvkfOGCw26RHsjSMvv7K2vmQq/6cxAcmxSEnE", 615 | ); 616 | } 617 | 618 | #[cfg(not(debug_assertions))] 619 | #[test] 620 | fn test_argon2id_version10_1() { 621 | hash_test( 622 | Variant::Argon2id, 623 | Version::Version10, 624 | 2, 625 | 65536, 626 | 1, 627 | b"password", 628 | b"somesalt", 629 | "980ebd24a4e667f16346f9d4a78b175728783613e0cc6fb17c2ec884b16435df", 630 | "$argon2id$v=16$m=65536,t=2,p=1$c29tZXNhbHQ\ 631 | $mA69JKTmZ/FjRvnUp4sXVyh4NhPgzG+xfC7IhLFkNd8", 632 | ); 633 | } 634 | 635 | #[cfg(not(debug_assertions))] 636 | #[test] 637 | fn test_argon2id_version10_2() { 638 | hash_test( 639 | Variant::Argon2id, 640 | Version::Version10, 641 | 2, 642 | 1048576, 643 | 1, 644 | b"password", 645 | b"somesalt", 646 | "ce622fc2053acd224c06e7be122f9e9554b62b8a414b032056b46123422e32ab", 647 | "$argon2id$v=16$m=1048576,t=2,p=1$c29tZXNhbHQ\ 648 | $zmIvwgU6zSJMBue+Ei+elVS2K4pBSwMgVrRhI0IuMqs", 649 | ); 650 | } 651 | 652 | #[cfg(not(debug_assertions))] 653 | #[test] 654 | fn test_argon2id_version10_3() { 655 | hash_test( 656 | Variant::Argon2id, 657 | Version::Version10, 658 | 2, 659 | 262144, 660 | 1, 661 | b"password", 662 | b"somesalt", 663 | "01cbafb58f2dc764adced1ab9a90fcb62f6ed8a066023b6ba0c204db6c9b90cd", 664 | "$argon2id$v=16$m=262144,t=2,p=1$c29tZXNhbHQ\ 665 | $AcuvtY8tx2StztGrmpD8ti9u2KBmAjtroMIE22ybkM0", 666 | ); 667 | } 668 | 669 | #[test] 670 | fn test_argon2id_version10_4() { 671 | hash_test( 672 | Variant::Argon2id, 673 | Version::Version10, 674 | 2, 675 | 256, 676 | 1, 677 | b"password", 678 | b"somesalt", 679 | "da070e576e50f2f38a3c897cbddc6c7fb4028e870971ff9eae7b4e1879295e6e", 680 | "$argon2id$v=16$m=256,t=2,p=1$c29tZXNhbHQ\ 681 | $2gcOV25Q8vOKPIl8vdxsf7QCjocJcf+erntOGHkpXm4", 682 | ); 683 | } 684 | 685 | #[test] 686 | fn test_argon2id_version10_5() { 687 | hash_test( 688 | Variant::Argon2id, 689 | Version::Version10, 690 | 2, 691 | 256, 692 | 2, 693 | b"password", 694 | b"somesalt", 695 | "f8aabb5315c63cddcdb3b4a021550928e525699da8fcbd1c2b0b1ccd35cc87a7", 696 | "$argon2id$v=16$m=256,t=2,p=2$c29tZXNhbHQ\ 697 | $+Kq7UxXGPN3Ns7SgIVUJKOUlaZ2o/L0cKwsczTXMh6c", 698 | ); 699 | } 700 | 701 | #[cfg(not(debug_assertions))] 702 | #[test] 703 | fn test_argon2id_version10_6() { 704 | hash_test( 705 | Variant::Argon2id, 706 | Version::Version10, 707 | 1, 708 | 65536, 709 | 1, 710 | b"password", 711 | b"somesalt", 712 | "8e0f310407a989013a823defbd6cbae54d86b35d1ac16e4f88eb4645e1357956", 713 | "$argon2id$v=16$m=65536,t=1,p=1$c29tZXNhbHQ\ 714 | $jg8xBAepiQE6gj3vvWy65U2Gs10awW5PiOtGReE1eVY", 715 | ); 716 | } 717 | 718 | #[cfg(not(debug_assertions))] 719 | #[test] 720 | fn test_argon2id_version10_7() { 721 | hash_test( 722 | Variant::Argon2id, 723 | Version::Version10, 724 | 4, 725 | 65536, 726 | 1, 727 | b"password", 728 | b"somesalt", 729 | "16653426e582143da6a0ff75daac9fa94c21ac2c221c96bd3645a5c15c2d962a", 730 | "$argon2id$v=16$m=65536,t=4,p=1$c29tZXNhbHQ\ 731 | $FmU0JuWCFD2moP912qyfqUwhrCwiHJa9NkWlwVwtlio", 732 | ); 733 | } 734 | 735 | #[cfg(not(debug_assertions))] 736 | #[test] 737 | fn test_argon2id_version10_8() { 738 | hash_test( 739 | Variant::Argon2id, 740 | Version::Version10, 741 | 2, 742 | 65536, 743 | 1, 744 | b"differentpassword", 745 | b"somesalt", 746 | "75709d4c96a2c235696a77eefeecf33e64221f470b26177c19e5e27642612dbb", 747 | "$argon2id$v=16$m=65536,t=2,p=1$c29tZXNhbHQ\ 748 | $dXCdTJaiwjVpanfu/uzzPmQiH0cLJhd8GeXidkJhLbs", 749 | ); 750 | } 751 | 752 | #[cfg(not(debug_assertions))] 753 | #[test] 754 | fn test_argon2id_version10_9() { 755 | hash_test( 756 | Variant::Argon2id, 757 | Version::Version10, 758 | 2, 759 | 65536, 760 | 1, 761 | b"password", 762 | b"diffsalt", 763 | "f0b57cfe03a9d78c4700e2fe802002343660a56b1b5d21ead788e6bb44ef48d3", 764 | "$argon2id$v=16$m=65536,t=2,p=1$ZGlmZnNhbHQ\ 765 | $8LV8/gOp14xHAOL+gCACNDZgpWsbXSHq14jmu0TvSNM", 766 | ); 767 | } 768 | 769 | #[cfg(not(debug_assertions))] 770 | #[test] 771 | fn test_argon2id_version13_1() { 772 | hash_test( 773 | Variant::Argon2id, 774 | Version::Version13, 775 | 2, 776 | 65536, 777 | 1, 778 | b"password", 779 | b"somesalt", 780 | "09316115d5cf24ed5a15a31a3ba326e5cf32edc24702987c02b6566f61913cf7", 781 | "$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ\ 782 | $CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc", 783 | ); 784 | } 785 | 786 | #[cfg(not(debug_assertions))] 787 | #[test] 788 | fn test_argon2id_version13_2() { 789 | hash_test( 790 | Variant::Argon2id, 791 | Version::Version13, 792 | 2, 793 | 1048576, 794 | 1, 795 | b"password", 796 | b"somesalt", 797 | "16a1cb8a061cf1f8b16fb195fe559fa6d1435e43dded2f4fb07293176be892fa", 798 | "$argon2id$v=19$m=1048576,t=2,p=1$c29tZXNhbHQ\ 799 | $FqHLigYc8fixb7GV/lWfptFDXkPd7S9PsHKTF2vokvo", 800 | ); 801 | } 802 | 803 | #[cfg(not(debug_assertions))] 804 | #[test] 805 | fn test_argon2id_version13_3() { 806 | hash_test( 807 | Variant::Argon2id, 808 | Version::Version13, 809 | 2, 810 | 262144, 811 | 1, 812 | b"password", 813 | b"somesalt", 814 | "78fe1ec91fb3aa5657d72e710854e4c3d9b9198c742f9616c2f085bed95b2e8c", 815 | "$argon2id$v=19$m=262144,t=2,p=1$c29tZXNhbHQ\ 816 | $eP4eyR+zqlZX1y5xCFTkw9m5GYx0L5YWwvCFvtlbLow", 817 | ); 818 | } 819 | 820 | #[test] 821 | fn test_argon2id_version13_4() { 822 | hash_test( 823 | Variant::Argon2id, 824 | Version::Version13, 825 | 2, 826 | 256, 827 | 1, 828 | b"password", 829 | b"somesalt", 830 | "9dfeb910e80bad0311fee20f9c0e2b12c17987b4cac90c2ef54d5b3021c68bfe", 831 | "$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ\ 832 | $nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4", 833 | ); 834 | } 835 | 836 | #[test] 837 | fn test_argon2id_version13_5() { 838 | hash_test( 839 | Variant::Argon2id, 840 | Version::Version13, 841 | 2, 842 | 256, 843 | 2, 844 | b"password", 845 | b"somesalt", 846 | "6d093c501fd5999645e0ea3bf620d7b8be7fd2db59c20d9fff9539da2bf57037", 847 | "$argon2id$v=19$m=256,t=2,p=2$c29tZXNhbHQ\ 848 | $bQk8UB/VmZZF4Oo79iDXuL5/0ttZwg2f/5U52iv1cDc", 849 | ); 850 | } 851 | 852 | #[cfg(not(debug_assertions))] 853 | #[test] 854 | fn test_argon2id_version13_6() { 855 | hash_test( 856 | Variant::Argon2id, 857 | Version::Version13, 858 | 1, 859 | 65536, 860 | 1, 861 | b"password", 862 | b"somesalt", 863 | "f6a5adc1ba723dddef9b5ac1d464e180fcd9dffc9d1cbf76cca2fed795d9ca98", 864 | "$argon2id$v=19$m=65536,t=1,p=1$c29tZXNhbHQ\ 865 | $9qWtwbpyPd3vm1rB1GThgPzZ3/ydHL92zKL+15XZypg", 866 | ); 867 | } 868 | 869 | #[cfg(not(debug_assertions))] 870 | #[test] 871 | fn test_argon2id_version13_7() { 872 | hash_test( 873 | Variant::Argon2id, 874 | Version::Version13, 875 | 4, 876 | 65536, 877 | 1, 878 | b"password", 879 | b"somesalt", 880 | "9025d48e68ef7395cca9079da4c4ec3affb3c8911fe4f86d1a2520856f63172c", 881 | "$argon2id$v=19$m=65536,t=4,p=1$c29tZXNhbHQ\ 882 | $kCXUjmjvc5XMqQedpMTsOv+zyJEf5PhtGiUghW9jFyw", 883 | ); 884 | } 885 | 886 | #[cfg(not(debug_assertions))] 887 | #[test] 888 | fn test_argon2id_version13_8() { 889 | hash_test( 890 | Variant::Argon2id, 891 | Version::Version13, 892 | 2, 893 | 65536, 894 | 1, 895 | b"differentpassword", 896 | b"somesalt", 897 | "0b84d652cf6b0c4beaef0dfe278ba6a80df6696281d7e0d2891b817d8c458fde", 898 | "$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ\ 899 | $C4TWUs9rDEvq7w3+J4umqA32aWKB1+DSiRuBfYxFj94", 900 | ); 901 | } 902 | 903 | #[cfg(not(debug_assertions))] 904 | #[test] 905 | fn test_argon2id_version13_9() { 906 | hash_test( 907 | Variant::Argon2id, 908 | Version::Version13, 909 | 2, 910 | 65536, 911 | 1, 912 | b"password", 913 | b"diffsalt", 914 | "bdf32b05ccc42eb15d58fd19b1f856b113da1e9a5874fdcc544308565aa8141c", 915 | "$argon2id$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ\ 916 | $vfMrBczELrFdWP0ZsfhWsRPaHppYdP3MVEMIVlqoFBw", 917 | ); 918 | } 919 | 920 | #[test] 921 | fn test_verify_encoded_with_missing_dollar_before_salt_version10() { 922 | let encoded = "$argon2i$m=65536,t=2,p=1c29tZXNhbHQ\ 923 | $9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"; 924 | let password = b"password"; 925 | let res = argon2::verify_encoded(encoded, password); 926 | assert_eq!(res, Err(Error::DecodingFail)); 927 | } 928 | 929 | #[test] 930 | fn test_verify_encoded_with_missing_dollar_before_salt_version13() { 931 | let encoded = "$argon2i$v=19$m=65536,t=2,p=1c29tZXNhbHQ\ 932 | $wWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA"; 933 | let password = b"password"; 934 | let res = argon2::verify_encoded(encoded, password); 935 | assert_eq!(res, Err(Error::DecodingFail)); 936 | } 937 | 938 | #[test] 939 | fn test_verify_encoded_with_missing_dollar_before_hash_version10() { 940 | let encoded = "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ\ 941 | 9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"; 942 | let password = b"password"; 943 | let res = argon2::verify_encoded(encoded, password); 944 | assert_eq!(res, Err(Error::DecodingFail)); 945 | } 946 | 947 | #[test] 948 | fn test_verify_encoded_with_missing_dollar_before_hash_version13() { 949 | let encoded = "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ\ 950 | wWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA"; 951 | let password = b"password"; 952 | let res = argon2::verify_encoded(encoded, password); 953 | assert_eq!(res, Err(Error::DecodingFail)); 954 | } 955 | 956 | #[test] 957 | fn test_verify_encoded_with_too_short_salt_version10() { 958 | let encoded = "$argon2i$m=65536,t=2,p=1$\ 959 | $9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"; 960 | let password = b"password"; 961 | let res = argon2::verify_encoded(encoded, password); 962 | assert_eq!(res, Err(Error::SaltTooShort)); 963 | } 964 | 965 | #[test] 966 | fn test_verify_encoded_with_too_short_salt_version13() { 967 | let encoded = "$argon2i$v=19$m=65536,t=2,p=1$\ 968 | $9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"; 969 | let password = b"password"; 970 | let res = argon2::verify_encoded(encoded, password); 971 | assert_eq!(res, Err(Error::SaltTooShort)); 972 | } 973 | 974 | #[cfg(not(debug_assertions))] 975 | #[test] 976 | fn test_verify_encoded_with_wrong_password_version10() { 977 | let encoded = "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ\ 978 | $b2G3seW+uPzerwQQC+/E1K50CLLO7YXy0JRcaTuswRo"; 979 | let password = b"password"; // should be passwore 980 | let res = argon2::verify_encoded(encoded, password); 981 | assert_eq!(res, Ok(false)); 982 | } 983 | 984 | #[cfg(not(debug_assertions))] 985 | #[test] 986 | fn test_verify_encoded_with_wrong_password_version13() { 987 | let encoded = "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ\ 988 | $8iIuixkI73Js3G1uMbezQXD0b8LG4SXGsOwoQkdAQIM"; 989 | let password = b"password"; // should be passwore 990 | let res = argon2::verify_encoded(encoded, password); 991 | assert_eq!(res, Ok(false)); 992 | } 993 | 994 | #[test] 995 | fn test_encoded_len_returns_correct_length() { 996 | assert_eq!(argon2::encoded_len(Variant::Argon2d, 256, 1, 1, 8, 32), 83); 997 | assert_eq!( 998 | argon2::encoded_len(Variant::Argon2i, 4096, 10, 10, 8, 32), 999 | 86 1000 | ); 1001 | assert_eq!( 1002 | argon2::encoded_len(Variant::Argon2id, 65536, 100, 10, 8, 32), 1003 | 89 1004 | ); 1005 | } 1006 | 1007 | #[test] 1008 | fn test_hash_raw_with_not_enough_memory() { 1009 | let pwd = b"password"; 1010 | let salt = b"diffsalt"; 1011 | let config = Config { 1012 | variant: Variant::Argon2i, 1013 | version: Version::Version13, 1014 | mem_cost: 2, 1015 | time_cost: 2, 1016 | lanes: 1, 1017 | thread_mode: ThreadMode::Sequential, 1018 | secret: &[], 1019 | ad: &[], 1020 | hash_length: 32, 1021 | }; 1022 | let res = argon2::hash_raw(pwd, salt, &config); 1023 | assert_eq!(res, Err(Error::MemoryTooLittle)); 1024 | } 1025 | 1026 | #[test] 1027 | fn test_hash_raw_with_too_short_salt() { 1028 | let pwd = b"password"; 1029 | let salt = b"s"; 1030 | let config = Config { 1031 | variant: Variant::Argon2i, 1032 | version: Version::Version13, 1033 | mem_cost: 2048, 1034 | time_cost: 2, 1035 | lanes: 1, 1036 | thread_mode: ThreadMode::Sequential, 1037 | secret: &[], 1038 | ad: &[], 1039 | hash_length: 32, 1040 | }; 1041 | let res = argon2::hash_raw(pwd, salt, &config); 1042 | assert_eq!(res, Err(Error::SaltTooShort)); 1043 | } 1044 | 1045 | fn hash_test( 1046 | var: Variant, 1047 | ver: Version, 1048 | t: u32, 1049 | m: u32, 1050 | p: u32, 1051 | pwd: &[u8], 1052 | salt: &[u8], 1053 | hex: &str, 1054 | enc: &str, 1055 | ) { 1056 | let threads = if cfg!(feature = "crossbeam-utils") { p } else { 1 }; 1057 | let config = Config { 1058 | variant: var, 1059 | version: ver, 1060 | mem_cost: m, 1061 | time_cost: t, 1062 | lanes: p, 1063 | thread_mode: ThreadMode::from_threads(threads), 1064 | secret: &[], 1065 | ad: &[], 1066 | hash_length: 32, 1067 | }; 1068 | let hash = argon2::hash_raw(pwd, salt, &config).unwrap(); 1069 | let hex_str = hash.encode_hex::(); 1070 | assert_eq!(hex_str.as_str(), hex); 1071 | 1072 | let encoded = argon2::hash_encoded(pwd, salt, &config).unwrap(); 1073 | let result = argon2::verify_encoded(encoded.as_str(), pwd).unwrap(); 1074 | assert!(result); 1075 | 1076 | let result = argon2::verify_encoded(enc, pwd).unwrap(); 1077 | assert!(result); 1078 | } 1079 | --------------------------------------------------------------------------------