rust-argon2-1.0.0/.cargo_vcs_info.json0000644000000001360000000000100132150ustar { "git": { "sha1": "8f6b42fa9df6d9c44438bb21fc0aee7f38521140" }, "path_in_vcs": "" }rust-argon2-1.0.0/.gitignore000064400000000000000000000000220072674642500140170ustar 00000000000000target Cargo.lock rust-argon2-1.0.0/.travis.yml000064400000000000000000000004210072674642500141430ustar 00000000000000language: rust rust: - stable - beta - nightly matrix: allow_failures: - rust: nightly script: - cargo build --verbose - cargo test --verbose - cargo build --release --verbose - cargo test --release --test integration_test --verbose -- --test-threads=2 rust-argon2-1.0.0/CHANGELOG.md000064400000000000000000000033500072674642500136470ustar 00000000000000ChangeLog for rust-argon2 ========================= This documents all notable changes to [rust-argon2](https://github.com/sru-systems/rust-argon2). ## 1.0.0 - Update constant_time_eq to 0.1.5. - Update serde to 1.0.133. - Update blake2b_simd to 1.0. ## 0.8.3 - Replace transmute with to_le_bytes. - Update base64 to version 0.13. - Update crossbeam-utils to version 0.8. - Update hex to version 0.4. - Derive Clone for Error struct. - Add optional serde support for Error struct. ## 0.8.2 - Change base64 to latest version (0.12). ## 0.8.1 - Fix issue with verifying multi-lane hashes with parallelism disabled (#27) ## 0.8.0 - Make parallelism optional via feature flag. ## 0.7.0 - Update crossbeam-utils dependency to 0.7. ## 0.6.1 - Use constant time equals to compare hashes. ## 0.6.0 - Use 2018 edition or Rust - Use &dyn error::Error instead of &error::Error - Fix clippy lints - Allow callers of encode_string to pass any &[u8] - Update base64 dependency. ## 0.5.1 - Use crossbeam utils 0.6 instead of crossbeam 0.5 ## 0.5.0 - Replace blake2-rfc with blake2b_simd. ## 0.4.0 - Replace rustc-serialize dependency with base64 and hex. - Update base64 dependency. - Update crossbeam dependency. - Update hex dependency. - Allow updating to minor versions of blake2-rfc. ## 0.3.0 - Embed Config struct in Context struct. ## 0.2.0 - Use ThreadMode enum instead of explicit thread number. - Use a Config struct instead of explicit configuration arguments. - Use references instead of vectors for byte data in the Context struct. - Deprecate the following functions: - hash_encoded_defaults - hash_encoded_old - hash_encoded_std - hash_raw_defaults - hash_raw_old - hash_raw_std - verify_raw_old - verify_raw_std rust-argon2-1.0.0/Cargo.toml0000644000000025020000000000100112120ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "rust-argon2" version = "1.0.0" authors = ["Martijn Rijkeboer "] description = "Rust implementation of the Argon2 password hashing function." homepage = "https://github.com/sru-systems/rust-argon2" documentation = "https://docs.sru-systems.com/rust-argon2/1.0.0/argon2/" readme = "README.md" keywords = ["argon2", "argon2d", "argon2i", "hash", "password"] license = "MIT/Apache-2.0" repository = "https://github.com/sru-systems/rust-argon2" [lib] name = "argon2" [dependencies.base64] version = "0.13" [dependencies.blake2b_simd] version = "1.0" [dependencies.constant_time_eq] version = "0.1.5" [dependencies.crossbeam-utils] version = "0.8" optional = true [dependencies.serde] version = "1.0.133" features = ["derive"] optional = true [dev-dependencies.hex] version = "0.4" [features] default = ["crossbeam-utils"] rust-argon2-1.0.0/Cargo.toml.orig000064400000000000000000000014360072674642500147300ustar 00000000000000[package] name = "rust-argon2" version = "1.0.0" authors = ["Martijn Rijkeboer "] edition = "2018" license = "MIT/Apache-2.0" description = "Rust implementation of the Argon2 password hashing function." documentation = "https://docs.sru-systems.com/rust-argon2/1.0.0/argon2/" homepage = "https://github.com/sru-systems/rust-argon2" repository = "https://github.com/sru-systems/rust-argon2" readme = "README.md" keywords = ["argon2", "argon2d", "argon2i", "hash", "password"] [lib] name = "argon2" [features] default = ["crossbeam-utils"] [dependencies] base64 = "0.13" blake2b_simd = "1.0" constant_time_eq = "0.1.5" crossbeam-utils = { version = "0.8", optional = true } serde = { version = "1.0.133", optional = true, features=["derive"] } [dev-dependencies] hex = "0.4" rust-argon2-1.0.0/LICENSE-APACHE000064400000000000000000000251370072674642500137710ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. rust-argon2-1.0.0/LICENSE-MIT000064400000000000000000000021150072674642500134700ustar 00000000000000The MIT license. Copyright (c) 2017 Martijn Rijkeboer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rust-argon2-1.0.0/README.md000064400000000000000000000041330072674642500133150ustar 00000000000000# Rust-argon2 Rust library for hashing passwords using [Argon2](https://github.com/P-H-C/phc-winner-argon2), the password-hashing function that won the [Password Hashing Competition (PHC)](https://password-hashing.net). ## Usage To use `rust-argon2`, add the following to your Cargo.toml: ```toml [dependencies] rust-argon2 = "1.0" ``` And the following to your crate root: ```rust extern crate argon2; ``` ## Examples Create a password hash using the defaults and verify it: ```rust use argon2::{self, Config}; let password = b"password"; let salt = b"randomsalt"; let config = Config::default(); let hash = argon2::hash_encoded(password, salt, &config).unwrap(); let matches = argon2::verify_encoded(&hash, password).unwrap(); assert!(matches); ``` Create a password hash with custom settings and verify it: ```rust use argon2::{self, Config, ThreadMode, Variant, Version}; let password = b"password"; let salt = b"othersalt"; let config = Config { variant: Variant::Argon2i, version: Version::Version13, mem_cost: 65536, time_cost: 10, lanes: 4, thread_mode: ThreadMode::Parallel, secret: &[], ad: &[], hash_length: 32 }; let hash = argon2::hash_encoded(password, salt, &config).unwrap(); let matches = argon2::verify_encoded(&hash, password).unwrap(); assert!(matches); ``` ## Limitations This crate has the same limitation as the `blake2-rfc` crate that it uses. It does not attempt to clear potentially sensitive data from its work memory. To do so correctly without a heavy performance penalty would require help from the compiler. It's better to not attempt to do so than to present a false assurance. This version uses the standard implementation and does not yet implement optimizations. Therefore, it is not the fastest implementation available. ## License Rust-argon2 is dual licensed under the [MIT](LICENSE-MIT) and [Apache 2.0](LICENSE-APACHE) licenses, the same licenses as the Rust compiler. ## Contributions Contributions are welcome. By submitting a pull request you are agreeing to make you work available under the license terms of the Rust-argon2 project. rust-argon2-1.0.0/src/argon2.rs000064400000000000000000000162210072674642500143640ustar 00000000000000// Copyright (c) 2017 Martijn Rijkeboer // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::config::Config; use crate::context::Context; use crate::core; use crate::encoding; use crate::memory::Memory; use crate::result::Result; use crate::thread_mode::ThreadMode; use crate::variant::Variant; use crate::version::Version; use constant_time_eq::constant_time_eq; /// Returns the length of the encoded string. /// /// # Remarks /// /// The length is **one** less that the original C version, since no null /// terminator is used. /// /// # Examples /// /// ```rust /// use argon2::{self, Variant}; /// /// let variant = Variant::Argon2i; /// let mem = 4096; /// let time = 10; /// let parallelism = 10; /// let salt_len = 8; /// let hash_len = 32; /// let enc_len = argon2::encoded_len(variant, mem, time, parallelism, salt_len, hash_len); /// assert_eq!(enc_len, 86); /// ``` #[rustfmt::skip] pub fn encoded_len( variant: Variant, mem_cost: u32, time_cost: u32, parallelism: u32, salt_len: u32, hash_len: u32 ) -> u32 { ("$$v=$m=,t=,p=$$".len() as u32) + (variant.as_lowercase_str().len() as u32) + encoding::num_len(Version::default().as_u32()) + encoding::num_len(mem_cost) + encoding::num_len(time_cost) + encoding::num_len(parallelism) + encoding::base64_len(salt_len) + encoding::base64_len(hash_len) } /// Hashes the password and returns the encoded hash. /// /// # Examples /// /// Create an encoded hash with the default configuration: /// /// ``` /// use argon2::{self, Config}; /// /// let pwd = b"password"; /// let salt = b"somesalt"; /// let config = Config::default(); /// let encoded = argon2::hash_encoded(pwd, salt, &config).unwrap(); /// ``` /// /// /// Create an Argon2d encoded hash with 4 lanes and parallel execution: /// /// ``` /// use argon2::{self, Config, ThreadMode, Variant}; /// /// let pwd = b"password"; /// let salt = b"somesalt"; /// let mut config = Config::default(); /// config.variant = Variant::Argon2d; #[cfg_attr(feature = "crossbeam-utils", doc = "config.lanes = 4;")] #[cfg_attr( feature = "crossbeam-utils", doc = "config.thread_mode = ThreadMode::Parallel;" )] #[cfg_attr(not(feature = "crossbeam-utils"), doc = "config.lanes = 1;")] #[cfg_attr( not(feature = "crossbeam-utils"), doc = "config.thread_mode = ThreadMode::Sequential;" )] /// let encoded = argon2::hash_encoded(pwd, salt, &config).unwrap(); /// ``` pub fn hash_encoded(pwd: &[u8], salt: &[u8], config: &Config) -> Result { let context = Context::new(config.clone(), pwd, salt)?; let hash = run(&context); let encoded = encoding::encode_string(&context, &hash); Ok(encoded) } /// Hashes the password and returns the hash as a vector. /// /// # Examples /// /// Create a hash with the default configuration: /// /// ``` /// use argon2::{self, Config}; /// /// let pwd = b"password"; /// let salt = b"somesalt"; /// let config = Config::default(); /// let vec = argon2::hash_raw(pwd, salt, &config).unwrap(); /// ``` /// /// /// Create an Argon2d hash with 4 lanes and parallel execution: /// /// ``` /// use argon2::{self, Config, ThreadMode, Variant}; /// /// let pwd = b"password"; /// let salt = b"somesalt"; /// let mut config = Config::default(); /// config.variant = Variant::Argon2d; #[cfg_attr(feature = "crossbeam-utils", doc = "config.lanes = 4;")] #[cfg_attr( feature = "crossbeam-utils", doc = "config.thread_mode = ThreadMode::Parallel;" )] #[cfg_attr(not(feature = "crossbeam-utils"), doc = "config.lanes = 1;")] #[cfg_attr( not(feature = "crossbeam-utils"), doc = "config.thread_mode = ThreadMode::Sequential;" )] /// let vec = argon2::hash_raw(pwd, salt, &config).unwrap(); /// ``` pub fn hash_raw(pwd: &[u8], salt: &[u8], config: &Config) -> Result> { let context = Context::new(config.clone(), pwd, salt)?; let hash = run(&context); Ok(hash) } /// Verifies the password with the encoded hash. /// /// # Examples /// /// ``` /// use argon2; /// /// let enc = "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ\ /// $iWh06vD8Fy27wf9npn6FXWiCX4K6pW6Ue1Bnzz07Z8A"; /// let pwd = b"password"; /// let res = argon2::verify_encoded(enc, pwd).unwrap(); /// assert!(res); /// ``` pub fn verify_encoded(encoded: &str, pwd: &[u8]) -> Result { verify_encoded_ext(encoded, pwd, &[], &[]) } /// Verifies the password with the encoded hash, secret and associated data. /// /// # Examples /// /// ``` /// use argon2; /// /// let enc = "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ\ /// $OlcSvlN20Lz43sK3jhCJ9K04oejhiY0AmI+ck6nuETo"; /// let pwd = b"password"; /// let secret = b"secret"; /// let ad = b"ad"; /// let res = argon2::verify_encoded_ext(enc, pwd, secret, ad).unwrap(); /// assert!(res); /// ``` pub fn verify_encoded_ext(encoded: &str, pwd: &[u8], secret: &[u8], ad: &[u8]) -> Result { let decoded = encoding::decode_string(encoded)?; let threads = if cfg!(feature = "crossbeam-utils") { decoded.parallelism } else { 1 }; let config = Config { variant: decoded.variant, version: decoded.version, mem_cost: decoded.mem_cost, time_cost: decoded.time_cost, lanes: decoded.parallelism, thread_mode: ThreadMode::from_threads(threads), secret, ad, hash_length: decoded.hash.len() as u32, }; verify_raw(pwd, &decoded.salt, &decoded.hash, &config) } /// Verifies the password with the supplied configuration. /// /// # Examples /// /// /// ``` /// use argon2::{self, Config}; /// /// let pwd = b"password"; /// let salt = b"somesalt"; /// let hash = &[137, 104, 116, 234, 240, 252, 23, 45, 187, 193, 255, 103, 166, /// 126, 133, 93, 104, 130, 95, 130, 186, 165, 110, 148, 123, 80, /// 103, 207, 61, 59, 103, 192]; /// let config = Config::default(); /// let res = argon2::verify_raw(pwd, salt, hash, &config).unwrap(); /// assert!(res); /// ``` pub fn verify_raw(pwd: &[u8], salt: &[u8], hash: &[u8], config: &Config) -> Result { let config = Config { hash_length: hash.len() as u32, ..config.clone() }; let context = Context::new(config, pwd, salt)?; let calculated_hash = run(&context); Ok(constant_time_eq(hash, &calculated_hash)) } fn run(context: &Context) -> Vec { let mut memory = Memory::new(context.config.lanes, context.lane_length); core::initialize(context, &mut memory); core::fill_memory_blocks(context, &mut memory); core::finalize(context, &memory) } #[cfg(test)] mod tests { use super::*; #[test] fn single_thread_verification_multi_lane_hash() { /* let hash = hash_encoded(b"foo", b"abcdefghijklmnopqrstuvwxyz", &Config { lanes: 4, thread_mode: ThreadMode::Parallel, ..Config::default() }); */ let hash = "$argon2i$v=19$m=4096,t=3,p=4$YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo$BvBk2OaSofBHfbrUW61nHrWB/43xgfs/QJJ5DkMAd8I"; verify_encoded(hash, b"foo").unwrap(); } } rust-argon2-1.0.0/src/block.rs000064400000000000000000000074130072674642500142710ustar 00000000000000// Copyright (c) 2017 Martijn Rijkeboer // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::common; use std::fmt; use std::fmt::Debug; use std::ops::{BitXorAssign, Index, IndexMut}; /// Structure for the (1KB) memory block implemented as 128 64-bit words. pub struct Block([u64; common::QWORDS_IN_BLOCK]); impl Block { /// Gets the byte slice representation of the block. pub fn as_u8(&self) -> &[u8] { let bytes: &[u8; common::BLOCK_SIZE] = unsafe { &*(&self.0 as *const [u64; common::QWORDS_IN_BLOCK] as *const [u8; common::BLOCK_SIZE]) }; bytes } /// Gets the mutable byte slice representation of the block. pub fn as_u8_mut(&mut self) -> &mut [u8] { let bytes: &mut [u8; common::BLOCK_SIZE] = unsafe { &mut *(&mut self.0 as *mut [u64; common::QWORDS_IN_BLOCK] as *mut [u8; common::BLOCK_SIZE]) }; bytes } /// Copies self to destination. pub fn copy_to(&self, dst: &mut Block) { for (d, s) in dst.0.iter_mut().zip(self.0.iter()) { *d = *s } } /// Creates a new block filled with zeros. pub fn zero() -> Block { Block([0u64; common::QWORDS_IN_BLOCK]) } } impl<'a> BitXorAssign<&'a Block> for Block { fn bitxor_assign(&mut self, rhs: &Block) { for (s, r) in self.0.iter_mut().zip(rhs.0.iter()) { *s ^= *r } } } impl Clone for Block { fn clone(&self) -> Block { Block(self.0) } } impl Debug for Block { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_list().entries(self.0.iter()).finish() } } impl Eq for Block {} impl Index for Block { type Output = u64; fn index(&self, index: usize) -> &u64 { &self.0[index] } } impl IndexMut for Block { fn index_mut(&mut self, index: usize) -> &mut u64 { &mut self.0[index] } } impl PartialEq for Block { fn eq(&self, other: &Block) -> bool { let mut equal = true; for (s, o) in self.0.iter().zip(other.0.iter()) { if s != o { equal = false; } } equal } } #[cfg(test)] mod tests { use crate::block::Block; use crate::common; #[test] fn as_u8_returns_correct_slice() { let block = Block::zero(); let expected = vec![0u8; 1024]; let actual = block.as_u8(); assert_eq!(actual, expected.as_slice()); } #[test] fn as_u8_mut_returns_correct_slice() { let mut block = Block::zero(); let mut expected = vec![0u8; 1024]; let actual = block.as_u8_mut(); assert_eq!(actual, expected.as_mut_slice()); } #[test] fn bitxor_assign_updates_lhs() { let mut lhs = Block([0u64; common::QWORDS_IN_BLOCK]); let rhs = Block([1u64; common::QWORDS_IN_BLOCK]); lhs ^= &rhs; assert_eq!(lhs, rhs); } #[test] fn copy_to_copies_block() { let src = Block([1u64; common::QWORDS_IN_BLOCK]); let mut dst = Block([0u64; common::QWORDS_IN_BLOCK]); src.copy_to(&mut dst); assert_eq!(dst, src); } #[test] fn clone_clones_block() { let orig = Block([1u64; common::QWORDS_IN_BLOCK]); let copy = orig.clone(); assert_eq!(copy, orig); } #[test] fn zero_creates_block_will_all_zeros() { let expected = Block([0u64; common::QWORDS_IN_BLOCK]); let actual = Block::zero(); assert_eq!(actual, expected); } } rust-argon2-1.0.0/src/common.rs000064400000000000000000000052630072674642500144700ustar 00000000000000// Copyright (c) 2017 Martijn Rijkeboer // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. /// Default number of lanes (degree of parallelism). pub const DEF_LANES: u32 = 1; /// Minimum number of lanes (degree of parallelism). pub const MIN_LANES: u32 = 1; /// Maximum number of lanes (degree of parallelism). pub const MAX_LANES: u32 = 0x00FF_FFFF; /// Number of synchronization points between lanes per pass. pub const SYNC_POINTS: u32 = 4; /// Default digest size in bytes. pub const DEF_HASH_LENGTH: u32 = 32; /// Minimum digest size in bytes. pub const MIN_HASH_LENGTH: u32 = 4; /// Maximum digest size in bytes. pub const MAX_HASH_LENGTH: u32 = 0xFFFF_FFFF; /// Default number of memory blocks (2^12). pub const DEF_MEMORY: u32 = 4096; /// Minimum number of memory blocks (each of BLOCK_SIZE bytes). pub const MIN_MEMORY: u32 = 2 * SYNC_POINTS; /// Maximum number of memory blocks (each of BLOCK_SIZE bytes). #[cfg(target_pointer_width = "32")] pub const MAX_MEMORY: u32 = 0x200000; #[cfg(target_pointer_width = "64")] pub const MAX_MEMORY: u32 = 0xFFFF_FFFF; /// Default number of passes. pub const DEF_TIME: u32 = 3; /// Minimum number of passes pub const MIN_TIME: u32 = 1; /// Maximum number of passes. pub const MAX_TIME: u32 = 0xFFFF_FFFF; /// Minimum password length in bytes. pub const MIN_PWD_LENGTH: u32 = 0; /// Maximum password length in bytes. pub const MAX_PWD_LENGTH: u32 = 0xFFFF_FFFF; /// Minimum associated data length in bytes. pub const MIN_AD_LENGTH: u32 = 0; /// Maximum associated data length in bytes. pub const MAX_AD_LENGTH: u32 = 0xFFFF_FFFF; /// Minimum salt length in bytes. pub const MIN_SALT_LENGTH: u32 = 8; /// Maximum salt length in bytes. pub const MAX_SALT_LENGTH: u32 = 0xFFFF_FFFF; /// Minimum key length in bytes. pub const MIN_SECRET_LENGTH: u32 = 0; /// Maximum key length in bytes. pub const MAX_SECRET_LENGTH: u32 = 0xFFFF_FFFF; /// Memory block size in bytes. pub const BLOCK_SIZE: usize = 1024; /// Number of quad words in a block. pub const QWORDS_IN_BLOCK: usize = BLOCK_SIZE / 8; /// Number of pseudo-random values generated by one call to Blake in Argon2i /// to generate reference block positions. pub const ADDRESSES_IN_BLOCK: u32 = 128; /// Pre-hashing digest length. pub const PREHASH_DIGEST_LENGTH: usize = 64; /// Pre-hashing digest length with extension. pub const PREHASH_SEED_LENGTH: usize = 72; /// Blake2b output length in bytes. pub const BLAKE2B_OUT_LENGTH: usize = 64; rust-argon2-1.0.0/src/config.rs000064400000000000000000000055030072674642500144420ustar 00000000000000// Copyright (c) 2017 Martijn Rijkeboer // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::common; use crate::thread_mode::ThreadMode; use crate::variant::Variant; use crate::version::Version; /// Structure containing configuration settings. /// /// # Examples /// /// ``` /// use argon2::{Config, ThreadMode, Variant, Version}; /// /// let config = Config::default(); /// assert_eq!(config.ad, &[]); /// assert_eq!(config.hash_length, 32); /// assert_eq!(config.lanes, 1); /// assert_eq!(config.mem_cost, 4096); /// assert_eq!(config.secret, &[]); /// assert_eq!(config.thread_mode, ThreadMode::Sequential); /// assert_eq!(config.time_cost, 3); /// assert_eq!(config.variant, Variant::Argon2i); /// assert_eq!(config.version, Version::Version13); /// ``` #[derive(Clone, Debug, PartialEq)] pub struct Config<'a> { /// The associated data. pub ad: &'a [u8], /// The length of the resulting hash. pub hash_length: u32, /// The number of lanes. pub lanes: u32, /// The amount of memory requested (KB). pub mem_cost: u32, /// The key. pub secret: &'a [u8], /// The thread mode. pub thread_mode: ThreadMode, /// The number of passes. pub time_cost: u32, /// The variant. pub variant: Variant, /// The version number. pub version: Version, } impl<'a> Config<'a> { pub fn uses_sequential(&self) -> bool { self.thread_mode == ThreadMode::Sequential || self.lanes == 1 } } impl<'a> Default for Config<'a> { fn default() -> Config<'a> { Config { ad: &[], hash_length: common::DEF_HASH_LENGTH, lanes: common::DEF_LANES, mem_cost: common::DEF_MEMORY, secret: &[], thread_mode: ThreadMode::default(), time_cost: common::DEF_TIME, variant: Variant::default(), version: Version::default(), } } } #[cfg(test)] mod tests { use crate::config::Config; use crate::thread_mode::ThreadMode; use crate::variant::Variant; use crate::version::Version; #[test] fn default_returns_correct_instance() { let config = Config::default(); assert_eq!(config.ad, &[]); assert_eq!(config.hash_length, 32); assert_eq!(config.lanes, 1); assert_eq!(config.mem_cost, 4096); assert_eq!(config.secret, &[]); assert_eq!(config.thread_mode, ThreadMode::Sequential); assert_eq!(config.time_cost, 3); assert_eq!(config.variant, Variant::Argon2i); assert_eq!(config.version, Version::Version13); } } rust-argon2-1.0.0/src/context.rs000064400000000000000000000154400072674642500146620ustar 00000000000000// Copyright (c) 2017 Martijn Rijkeboer // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::common; use crate::config::Config; use crate::error::Error; use crate::result::Result; /// Structure containing settings for the Argon2 algorithm. A combination of /// the original argon2_context and argon2_instance_t. #[derive(Debug, PartialEq)] pub struct Context<'a> { /// The config for this context. pub config: Config<'a>, /// The length of a lane. pub lane_length: u32, /// The number of memory blocks. pub memory_blocks: u32, /// The password. pub pwd: &'a [u8], /// The salt. pub salt: &'a [u8], /// The length of a segment. pub segment_length: u32, } impl<'a> Context<'a> { /// Attempts to create a new context. pub fn new(config: Config<'a>, pwd: &'a [u8], salt: &'a [u8]) -> Result> { if config.lanes < common::MIN_LANES { return Err(Error::LanesTooFew); } else if config.lanes > common::MAX_LANES { return Err(Error::LanesTooMany); } let lanes = config.lanes; if config.mem_cost < common::MIN_MEMORY { return Err(Error::MemoryTooLittle); } else if config.mem_cost > common::MAX_MEMORY { return Err(Error::MemoryTooMuch); } else if config.mem_cost < 8 * lanes { return Err(Error::MemoryTooLittle); } if config.time_cost < common::MIN_TIME { return Err(Error::TimeTooSmall); } else if config.time_cost > common::MAX_TIME { return Err(Error::TimeTooLarge); } let pwd_len = pwd.len(); if pwd_len < common::MIN_PWD_LENGTH as usize { return Err(Error::PwdTooShort); } else if pwd_len > common::MAX_PWD_LENGTH as usize { return Err(Error::PwdTooLong); } let salt_len = salt.len(); if salt_len < common::MIN_SALT_LENGTH as usize { return Err(Error::SaltTooShort); } else if salt_len > common::MAX_SALT_LENGTH as usize { return Err(Error::SaltTooLong); } let secret_len = config.secret.len(); if secret_len < common::MIN_SECRET_LENGTH as usize { return Err(Error::SecretTooShort); } else if secret_len > common::MAX_SECRET_LENGTH as usize { return Err(Error::SecretTooLong); } let ad_len = config.ad.len(); if ad_len < common::MIN_AD_LENGTH as usize { return Err(Error::AdTooShort); } else if ad_len > common::MAX_AD_LENGTH as usize { return Err(Error::AdTooLong); } if config.hash_length < common::MIN_HASH_LENGTH { return Err(Error::OutputTooShort); } else if config.hash_length > common::MAX_HASH_LENGTH { return Err(Error::OutputTooLong); } let mut memory_blocks = config.mem_cost; if memory_blocks < 2 * common::SYNC_POINTS * lanes { memory_blocks = 2 * common::SYNC_POINTS * lanes; } let segment_length = memory_blocks / (lanes * common::SYNC_POINTS); let memory_blocks = segment_length * (lanes * common::SYNC_POINTS); let lane_length = segment_length * common::SYNC_POINTS; Ok(Context { config, lane_length, memory_blocks, pwd, salt, segment_length, }) } } #[cfg(test)] mod tests { use crate::config::Config; use crate::context::Context; use crate::error::Error; use crate::thread_mode::ThreadMode; use crate::variant::Variant; use crate::version::Version; #[test] fn new_returns_correct_instance() { let config = Config { ad: b"additionaldata", hash_length: 32, lanes: 4, mem_cost: 4096, secret: b"secret", thread_mode: ThreadMode::Sequential, time_cost: 3, variant: Variant::Argon2i, version: Version::Version13, }; let pwd = b"password"; let salt = b"somesalt"; let result = Context::new(config.clone(), pwd, salt); assert!(result.is_ok()); let context = result.unwrap(); assert_eq!(context.config, config); assert_eq!(context.pwd, pwd); assert_eq!(context.salt, salt); assert_eq!(context.memory_blocks, 4096); assert_eq!(context.segment_length, 256); assert_eq!(context.lane_length, 1024); } #[test] fn new_with_too_little_mem_cost_returns_correct_error() { let config = Config { mem_cost: 7, ..Default::default() }; assert_eq!( Context::new(config, &[0u8; 8], &[0u8; 8]), Err(Error::MemoryTooLittle) ); } #[test] fn new_with_less_than_8_x_lanes_mem_cost_returns_correct_error() { let config = Config { lanes: 4, mem_cost: 31, ..Default::default() }; assert_eq!( Context::new(config, &[0u8; 8], &[0u8; 8]), Err(Error::MemoryTooLittle) ); } #[test] fn new_with_too_small_time_cost_returns_correct_error() { let config = Config { time_cost: 0, ..Default::default() }; assert_eq!( Context::new(config, &[0u8; 8], &[0u8; 8]), Err(Error::TimeTooSmall) ); } #[test] fn new_with_too_few_lanes_returns_correct_error() { let config = Config { lanes: 0, ..Default::default() }; assert_eq!( Context::new(config, &[0u8; 8], &[0u8; 8]), Err(Error::LanesTooFew) ); } #[test] fn new_with_too_many_lanes_returns_correct_error() { let config = Config { lanes: 1 << 24, ..Default::default() }; assert_eq!( Context::new(config, &[0u8; 8], &[0u8; 8]), Err(Error::LanesTooMany) ); } #[test] fn new_with_too_short_salt_returns_correct_error() { let config = Default::default(); let salt = [0u8; 7]; assert_eq!( Context::new(config, &[0u8; 8], &salt), Err(Error::SaltTooShort) ); } #[test] fn new_with_too_short_hash_length_returns_correct_error() { let config = Config { hash_length: 3, ..Default::default() }; assert_eq!( Context::new(config, &[0u8; 8], &[0u8; 8]), Err(Error::OutputTooShort) ); } } rust-argon2-1.0.0/src/core.rs000064400000000000000000000374470072674642500141410ustar 00000000000000// Copyright (c) 2017 Martijn Rijkeboer // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::block::Block; use crate::common; use crate::context::Context; use crate::memory::Memory; use crate::variant::Variant; use crate::version::Version; use blake2b_simd::Params; #[cfg(feature = "crossbeam-utils")] use crossbeam_utils::thread::scope; /// Position of the block currently being operated on. #[derive(Clone, Debug)] struct Position { pass: u32, lane: u32, slice: u32, index: u32, } /// Initializes the memory. pub fn initialize(context: &Context, memory: &mut Memory) { fill_first_blocks(context, memory, &mut h0(context)); } /// Fills all the memory blocks. pub fn fill_memory_blocks(context: &Context, memory: &mut Memory) { if context.config.uses_sequential() { fill_memory_blocks_st(context, memory); } else { fill_memory_blocks_mt(context, memory); } } /// Calculates the final hash and returns it. pub fn finalize(context: &Context, memory: &Memory) -> Vec { let mut blockhash = memory[context.lane_length - 1].clone(); for l in 1..context.config.lanes { let last_block_in_lane = l * context.lane_length + (context.lane_length - 1); blockhash ^= &memory[last_block_in_lane]; } let mut hash = vec![0u8; context.config.hash_length as usize]; hprime(hash.as_mut_slice(), blockhash.as_u8()); hash } fn blake2b(out: &mut [u8], input: &[&[u8]]) { let mut blake = Params::new().hash_length(out.len()).to_state(); for slice in input { blake.update(slice); } out.copy_from_slice(blake.finalize().as_bytes()); } fn f_bla_mka(x: u64, y: u64) -> u64 { let m = 0xFFFF_FFFFu64; let xy = (x & m) * (y & m); x.wrapping_add(y.wrapping_add(xy.wrapping_add(xy))) } fn fill_block(prev_block: &Block, ref_block: &Block, next_block: &mut Block, with_xor: bool) { let mut block_r = ref_block.clone(); block_r ^= prev_block; let mut block_tmp = block_r.clone(); // Now block_r = ref_block + prev_block and block_tmp = ref_block + prev_block if with_xor { // Saving the next block contents for XOR over block_tmp ^= next_block; // Now block_r = ref_block + prev_block and // block_tmp = ref_block + prev_block + next_block } // Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then // (16,17,..31)... finally (112,113,...127) for i in 0..8 { let mut v0 = block_r[16 * i]; let mut v1 = block_r[16 * i + 1]; let mut v2 = block_r[16 * i + 2]; let mut v3 = block_r[16 * i + 3]; let mut v4 = block_r[16 * i + 4]; let mut v5 = block_r[16 * i + 5]; let mut v6 = block_r[16 * i + 6]; let mut v7 = block_r[16 * i + 7]; let mut v8 = block_r[16 * i + 8]; let mut v9 = block_r[16 * i + 9]; let mut v10 = block_r[16 * i + 10]; let mut v11 = block_r[16 * i + 11]; let mut v12 = block_r[16 * i + 12]; let mut v13 = block_r[16 * i + 13]; let mut v14 = block_r[16 * i + 14]; let mut v15 = block_r[16 * i + 15]; p( &mut v0, &mut v1, &mut v2, &mut v3, &mut v4, &mut v5, &mut v6, &mut v7, &mut v8, &mut v9, &mut v10, &mut v11, &mut v12, &mut v13, &mut v14, &mut v15, ); block_r[16 * i] = v0; block_r[16 * i + 1] = v1; block_r[16 * i + 2] = v2; block_r[16 * i + 3] = v3; block_r[16 * i + 4] = v4; block_r[16 * i + 5] = v5; block_r[16 * i + 6] = v6; block_r[16 * i + 7] = v7; block_r[16 * i + 8] = v8; block_r[16 * i + 9] = v9; block_r[16 * i + 10] = v10; block_r[16 * i + 11] = v11; block_r[16 * i + 12] = v12; block_r[16 * i + 13] = v13; block_r[16 * i + 14] = v14; block_r[16 * i + 15] = v15; } // Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then // (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) for i in 0..8 { let mut v0 = block_r[2 * i]; let mut v1 = block_r[2 * i + 1]; let mut v2 = block_r[2 * i + 16]; let mut v3 = block_r[2 * i + 17]; let mut v4 = block_r[2 * i + 32]; let mut v5 = block_r[2 * i + 33]; let mut v6 = block_r[2 * i + 48]; let mut v7 = block_r[2 * i + 49]; let mut v8 = block_r[2 * i + 64]; let mut v9 = block_r[2 * i + 65]; let mut v10 = block_r[2 * i + 80]; let mut v11 = block_r[2 * i + 81]; let mut v12 = block_r[2 * i + 96]; let mut v13 = block_r[2 * i + 97]; let mut v14 = block_r[2 * i + 112]; let mut v15 = block_r[2 * i + 113]; p( &mut v0, &mut v1, &mut v2, &mut v3, &mut v4, &mut v5, &mut v6, &mut v7, &mut v8, &mut v9, &mut v10, &mut v11, &mut v12, &mut v13, &mut v14, &mut v15, ); block_r[2 * i] = v0; block_r[2 * i + 1] = v1; block_r[2 * i + 16] = v2; block_r[2 * i + 17] = v3; block_r[2 * i + 32] = v4; block_r[2 * i + 33] = v5; block_r[2 * i + 48] = v6; block_r[2 * i + 49] = v7; block_r[2 * i + 64] = v8; block_r[2 * i + 65] = v9; block_r[2 * i + 80] = v10; block_r[2 * i + 81] = v11; block_r[2 * i + 96] = v12; block_r[2 * i + 97] = v13; block_r[2 * i + 112] = v14; block_r[2 * i + 113] = v15; } block_tmp.copy_to(next_block); *next_block ^= &block_r; } fn fill_first_blocks(context: &Context, memory: &mut Memory, h0: &mut [u8]) { for lane in 0..context.config.lanes { let start = common::PREHASH_DIGEST_LENGTH; // H'(H0||0||i) h0[start..(start + 4)].clone_from_slice(&u32::to_le_bytes(0)); h0[(start + 4)..(start + 8)].clone_from_slice(&u32::to_le_bytes(lane)); hprime(memory[(lane, 0)].as_u8_mut(), &h0); // H'(H0||1||i) h0[start..(start + 4)].clone_from_slice(&u32::to_le_bytes(1)); hprime(memory[(lane, 1)].as_u8_mut(), &h0); } } #[cfg(feature = "crossbeam-utils")] fn fill_memory_blocks_mt(context: &Context, memory: &mut Memory) { for p in 0..context.config.time_cost { for s in 0..common::SYNC_POINTS { let _ = scope(|scoped| { for (l, mem) in (0..context.config.lanes).zip(memory.as_lanes_mut()) { let position = Position { pass: p, lane: l, slice: s, index: 0, }; scoped.spawn(move |_| { fill_segment(context, &position, mem); }); } }); } } } #[cfg(not(feature = "crossbeam-utils"))] fn fill_memory_blocks_mt(_: &Context, _: &mut Memory) { unimplemented!() } fn fill_memory_blocks_st(context: &Context, memory: &mut Memory) { for p in 0..context.config.time_cost { for s in 0..common::SYNC_POINTS { for l in 0..context.config.lanes { let position = Position { pass: p, lane: l, slice: s, index: 0, }; fill_segment(context, &position, memory); } } } } fn fill_segment(context: &Context, position: &Position, memory: &mut Memory) { let mut position = position.clone(); let data_independent_addressing = (context.config.variant == Variant::Argon2i) || (context.config.variant == Variant::Argon2id && position.pass == 0) && (position.slice < (common::SYNC_POINTS / 2)); let zero_block = Block::zero(); let mut input_block = Block::zero(); let mut address_block = Block::zero(); if data_independent_addressing { input_block[0] = position.pass as u64; input_block[1] = position.lane as u64; input_block[2] = position.slice as u64; input_block[3] = context.memory_blocks as u64; input_block[4] = context.config.time_cost as u64; input_block[5] = context.config.variant.as_u64(); } let mut starting_index = 0u32; if position.pass == 0 && position.slice == 0 { starting_index = 2; // Don't forget to generate the first block of addresses: if data_independent_addressing { next_addresses(&mut address_block, &mut input_block, &zero_block); } } let mut curr_offset = (position.lane * context.lane_length) + (position.slice * context.segment_length) + starting_index; let mut prev_offset = if curr_offset % context.lane_length == 0 { // Last block in this lane curr_offset + context.lane_length - 1 } else { curr_offset - 1 }; let mut pseudo_rand; for i in starting_index..context.segment_length { // 1.1 Rotating prev_offset if needed if curr_offset % context.lane_length == 1 { prev_offset = curr_offset - 1; } // 1.2 Computing the index of the reference block // 1.2.1 Taking pseudo-random value from the previous block if data_independent_addressing { if i % common::ADDRESSES_IN_BLOCK == 0 { next_addresses(&mut address_block, &mut input_block, &zero_block); } pseudo_rand = address_block[(i % common::ADDRESSES_IN_BLOCK) as usize]; } else { pseudo_rand = memory[(prev_offset)][0]; } // 1.2.2 Computing the lane of the reference block // If (position.pass == 0) && (position.slice == 0): can not reference other lanes yet let ref_lane = if (position.pass == 0) && (position.slice == 0) { position.lane as u64 } else { (pseudo_rand >> 32) % context.config.lanes as u64 }; // 1.2.3 Computing the number of possible reference block within the lane. position.index = i; let pseudo_rand_u32 = (pseudo_rand & 0xFFFF_FFFF) as u32; let same_lane = ref_lane == (position.lane as u64); let ref_index = index_alpha(context, &position, pseudo_rand_u32, same_lane); // 2 Creating a new block let index = context.lane_length as u64 * ref_lane + ref_index as u64; let mut curr_block = memory[curr_offset].clone(); { let prev_block = &memory[prev_offset]; let ref_block = &memory[index]; if context.config.version == Version::Version10 || position.pass == 0 { fill_block(prev_block, ref_block, &mut curr_block, false); } else { fill_block(prev_block, ref_block, &mut curr_block, true); } } memory[curr_offset] = curr_block; curr_offset += 1; prev_offset += 1; } } fn g(a: &mut u64, b: &mut u64, c: &mut u64, d: &mut u64) { *a = f_bla_mka(*a, *b); *d = rotr64(*d ^ *a, 32); *c = f_bla_mka(*c, *d); *b = rotr64(*b ^ *c, 24); *a = f_bla_mka(*a, *b); *d = rotr64(*d ^ *a, 16); *c = f_bla_mka(*c, *d); *b = rotr64(*b ^ *c, 63); } fn h0(context: &Context) -> [u8; common::PREHASH_SEED_LENGTH] { let input = [ &u32::to_le_bytes(context.config.lanes), &u32::to_le_bytes(context.config.hash_length), &u32::to_le_bytes(context.config.mem_cost), &u32::to_le_bytes(context.config.time_cost), &u32::to_le_bytes(context.config.version.as_u32()), &u32::to_le_bytes(context.config.variant.as_u32()), &len_as_32le(context.pwd), context.pwd, &len_as_32le(context.salt), context.salt, &len_as_32le(context.config.secret), context.config.secret, &len_as_32le(context.config.ad), context.config.ad, ]; let mut out = [0u8; common::PREHASH_SEED_LENGTH]; blake2b(&mut out[0..common::PREHASH_DIGEST_LENGTH], &input); out } fn hprime(out: &mut [u8], input: &[u8]) { let out_len = out.len(); if out_len <= common::BLAKE2B_OUT_LENGTH { blake2b(out, &[&u32::to_le_bytes(out_len as u32), input]); } else { let ai_len = 32; let mut out_buffer = [0u8; common::BLAKE2B_OUT_LENGTH]; let mut in_buffer = [0u8; common::BLAKE2B_OUT_LENGTH]; blake2b(&mut out_buffer, &[&u32::to_le_bytes(out_len as u32), input]); out[0..ai_len].clone_from_slice(&out_buffer[0..ai_len]); let mut out_pos = ai_len; let mut to_produce = out_len - ai_len; while to_produce > common::BLAKE2B_OUT_LENGTH { in_buffer.clone_from_slice(&out_buffer); blake2b(&mut out_buffer, &[&in_buffer]); out[out_pos..out_pos + ai_len].clone_from_slice(&out_buffer[0..ai_len]); out_pos += ai_len; to_produce -= ai_len; } blake2b(&mut out[out_pos..out_len], &[&out_buffer]); } } fn index_alpha(context: &Context, position: &Position, pseudo_rand: u32, same_lane: bool) -> u32 { // Pass 0: // - This lane: all already finished segments plus already constructed blocks in this segment // - Other lanes: all already finished segments // Pass 1+: // - This lane: (SYNC_POINTS - 1) last segments plus already constructed blocks in this segment // - Other lanes : (SYNC_POINTS - 1) last segments let reference_area_size: u32 = if position.pass == 0 { // First pass if position.slice == 0 { // First slice position.index - 1 } else if same_lane { // The same lane => add current segment position.slice * context.segment_length + position.index - 1 } else if position.index == 0 { position.slice * context.segment_length - 1 } else { position.slice * context.segment_length } } else { // Second pass if same_lane { context.lane_length - context.segment_length + position.index - 1 } else if position.index == 0 { context.lane_length - context.segment_length - 1 } else { context.lane_length - context.segment_length } }; let reference_area_size = reference_area_size as u64; let mut relative_position = pseudo_rand as u64; relative_position = (relative_position * relative_position) >> 32; relative_position = reference_area_size - 1 - ((reference_area_size * relative_position) >> 32); // 1.2.5 Computing starting position let start_position: u32 = if position.pass != 0 { if position.slice == common::SYNC_POINTS - 1 { 0u32 } else { (position.slice + 1) * context.segment_length } } else { 0u32 }; let start_position = start_position as u64; // 1.2.6. Computing absolute position ((start_position + relative_position) % context.lane_length as u64) as u32 } fn len_as_32le(slice: &[u8]) -> [u8; 4] { u32::to_le_bytes(slice.len() as u32) } fn next_addresses(address_block: &mut Block, input_block: &mut Block, zero_block: &Block) { input_block[6] += 1; fill_block(zero_block, input_block, address_block, false); fill_block(zero_block, &address_block.clone(), address_block, false); } fn p( v0: &mut u64, v1: &mut u64, v2: &mut u64, v3: &mut u64, v4: &mut u64, v5: &mut u64, v6: &mut u64, v7: &mut u64, v8: &mut u64, v9: &mut u64, v10: &mut u64, v11: &mut u64, v12: &mut u64, v13: &mut u64, v14: &mut u64, v15: &mut u64, ) { g(v0, v4, v8, v12); g(v1, v5, v9, v13); g(v2, v6, v10, v14); g(v3, v7, v11, v15); g(v0, v5, v10, v15); g(v1, v6, v11, v12); g(v2, v7, v8, v13); g(v3, v4, v9, v14); } fn rotr64(w: u64, c: u32) -> u64 { (w >> c) | (w << (64 - c)) } rust-argon2-1.0.0/src/decoded.rs000064400000000000000000000015620072674642500145650ustar 00000000000000// Copyright (c) 2017 Martijn Rijkeboer // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::variant::Variant; use crate::version::Version; /// Structure that contains the decoded data. #[derive(Debug, Eq, PartialEq)] pub struct Decoded { /// The variant. pub variant: Variant, /// The version. pub version: Version, /// The amount of memory requested (KiB). pub mem_cost: u32, /// The number of passes. pub time_cost: u32, /// The parallelism. pub parallelism: u32, /// The salt. pub salt: Vec, /// The hash. pub hash: Vec, } rust-argon2-1.0.0/src/encoding.rs000064400000000000000000000311220072674642500147570ustar 00000000000000// Copyright (c) 2017 Martijn Rijkeboer // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::context::Context; use crate::decoded::Decoded; use crate::error::Error; use crate::result::Result; use crate::variant::Variant; use crate::version::Version; use base64; /// Structure containing the options. struct Options { mem_cost: u32, time_cost: u32, parallelism: u32, } /// Gets the base64 encoded length of a byte slice with the specified length. pub fn base64_len(length: u32) -> u32 { let olen = (length / 3) << 2; match length % 3 { 2 => olen + 3, 1 => olen + 2, _ => olen, } } /// Attempts to decode the encoded string slice. pub fn decode_string(encoded: &str) -> Result { let items: Vec<&str> = encoded.split('$').collect(); if items.len() == 6 { decode_empty(items[0])?; let variant = decode_variant(items[1])?; let version = decode_version(items[2])?; let options = decode_options(items[3])?; let salt = base64::decode(items[4])?; let hash = base64::decode(items[5])?; Ok(Decoded { variant, version, mem_cost: options.mem_cost, time_cost: options.time_cost, parallelism: options.parallelism, salt, hash, }) } else if items.len() == 5 { decode_empty(items[0])?; let variant = decode_variant(items[1])?; let options = decode_options(items[2])?; let salt = base64::decode(items[3])?; let hash = base64::decode(items[4])?; Ok(Decoded { variant, version: Version::Version10, mem_cost: options.mem_cost, time_cost: options.time_cost, parallelism: options.parallelism, salt, hash, }) } else { Err(Error::DecodingFail) } } fn decode_empty(str: &str) -> Result<()> { if str == "" { Ok(()) } else { Err(Error::DecodingFail) } } fn decode_options(str: &str) -> Result { let items: Vec<&str> = str.split(',').collect(); if items.len() == 3 { Ok(Options { mem_cost: decode_option(items[0], "m")?, time_cost: decode_option(items[1], "t")?, parallelism: decode_option(items[2], "p")?, }) } else { Err(Error::DecodingFail) } } fn decode_option(str: &str, name: &str) -> Result { let items: Vec<&str> = str.split('=').collect(); if items.len() == 2 { if items[0] == name { decode_u32(items[1]) } else { Err(Error::DecodingFail) } } else { Err(Error::DecodingFail) } } fn decode_u32(str: &str) -> Result { match str.parse() { Ok(i) => Ok(i), Err(_) => Err(Error::DecodingFail), } } fn decode_variant(str: &str) -> Result { Variant::from_str(str) } fn decode_version(str: &str) -> Result { let items: Vec<&str> = str.split('=').collect(); if items.len() == 2 { if items[0] == "v" { Version::from_str(items[1]) } else { Err(Error::DecodingFail) } } else { Err(Error::DecodingFail) } } /// Encodes the hash and context. pub fn encode_string(context: &Context, hash: &[u8]) -> String { format!( "${}$v={}$m={},t={},p={}${}${}", context.config.variant, context.config.version, context.config.mem_cost, context.config.time_cost, context.config.lanes, base64::encode_config(context.salt, base64::STANDARD_NO_PAD), base64::encode_config(hash, base64::STANDARD_NO_PAD), ) } /// Gets the string length of the specified number. pub fn num_len(number: u32) -> u32 { let mut len = 1; let mut num = number; while num >= 10 { len += 1; num /= 10; } len } #[cfg(test)] mod tests { #[cfg(feature = "crossbeam-utils")] use crate::config::Config; #[cfg(feature = "crossbeam-utils")] use crate::context::Context; use crate::decoded::Decoded; #[cfg(feature = "crossbeam-utils")] use crate::encoding::encode_string; use crate::encoding::{base64_len, decode_string, num_len}; use crate::error::Error; #[cfg(feature = "crossbeam-utils")] use crate::thread_mode::ThreadMode; use crate::variant::Variant; use crate::version::Version; #[test] fn base64_len_returns_correct_length() { let tests = vec![ (1, 2), (2, 3), (3, 4), (4, 6), (5, 7), (6, 8), (7, 10), (8, 11), (9, 12), (10, 14), ]; for (len, expected) in tests { let actual = base64_len(len); assert_eq!(actual, expected); } } #[test] fn decode_string_with_version10_returns_correct_result() { let encoded = "$argon2i$v=16$m=4096,t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; let expected = Decoded { variant: Variant::Argon2i, version: Version::Version10, mem_cost: 4096, time_cost: 3, parallelism: 1, salt: b"salt1234".to_vec(), hash: b"12345678901234567890123456789012".to_vec(), }; let actual = decode_string(encoded).unwrap(); assert_eq!(actual, expected); } #[test] fn decode_string_with_version13_returns_correct_result() { let encoded = "$argon2i$v=19$m=4096,t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; let expected = Decoded { variant: Variant::Argon2i, version: Version::Version13, mem_cost: 4096, time_cost: 3, parallelism: 1, salt: b"salt1234".to_vec(), hash: b"12345678901234567890123456789012".to_vec(), }; let actual = decode_string(encoded).unwrap(); assert_eq!(actual, expected); } #[test] fn decode_string_without_version_returns_correct_result() { let encoded = "$argon2i$m=4096,t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; let expected = Decoded { variant: Variant::Argon2i, version: Version::Version10, mem_cost: 4096, time_cost: 3, parallelism: 1, salt: b"salt1234".to_vec(), hash: b"12345678901234567890123456789012".to_vec(), }; let actual = decode_string(encoded).unwrap(); assert_eq!(actual, expected); } #[test] fn decode_string_without_variant_returns_error_result() { let encoded = "$m=4096,t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; let result = decode_string(encoded); assert_eq!(result, Err(Error::DecodingFail)); } #[test] fn decode_string_with_empty_variant_returns_error_result() { let encoded = "$$m=4096,t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; let result = decode_string(encoded); assert_eq!(result, Err(Error::DecodingFail)); } #[test] fn decode_string_with_invalid_variant_returns_error_result() { let encoded = "$argon$m=4096,t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; let result = decode_string(encoded); assert_eq!(result, Err(Error::DecodingFail)); } #[test] fn decode_string_without_mem_cost_returns_error_result() { let encoded = "$argon2i$t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; let result = decode_string(encoded); assert_eq!(result, Err(Error::DecodingFail)); } #[test] fn decode_string_with_empty_mem_cost_returns_error_result() { let encoded = "$argon2i$m=,t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; let result = decode_string(encoded); assert_eq!(result, Err(Error::DecodingFail)); } #[test] fn decode_string_with_non_numeric_mem_cost_returns_error_result() { let encoded = "$argon2i$m=a,t=3,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; let result = decode_string(encoded); assert_eq!(result, Err(Error::DecodingFail)); } #[test] fn decode_string_without_time_cost_returns_error_result() { let encoded = "$argon2i$m=4096,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; let result = decode_string(encoded); assert_eq!(result, Err(Error::DecodingFail)); } #[test] fn decode_string_with_empty_time_cost_returns_error_result() { let encoded = "$argon2i$m=4096,t=,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; let result = decode_string(encoded); assert_eq!(result, Err(Error::DecodingFail)); } #[test] fn decode_string_with_non_numeric_time_cost_returns_error_result() { let encoded = "$argon2i$m=4096,t=a,p=1\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; let result = decode_string(encoded); assert_eq!(result, Err(Error::DecodingFail)); } #[test] fn decode_string_without_parallelism_returns_error_result() { let encoded = "$argon2i$m=4096,t=3\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; let result = decode_string(encoded); assert_eq!(result, Err(Error::DecodingFail)); } #[test] fn decode_string_with_empty_parallelism_returns_error_result() { let encoded = "$argon2i$m=4096,t=3,p=\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; let result = decode_string(encoded); assert_eq!(result, Err(Error::DecodingFail)); } #[test] fn decode_string_with_non_numeric_parallelism_returns_error_result() { let encoded = "$argon2i$m=4096,t=3,p=a\ $c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; let result = decode_string(encoded); assert_eq!(result, Err(Error::DecodingFail)); } #[test] fn decode_string_without_salt_returns_error_result() { let encoded = "$argon2i$m=4096,t=3,p=1\ $MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="; let result = decode_string(encoded); assert_eq!(result, Err(Error::DecodingFail)); } #[test] fn decode_string_without_hash_returns_error_result() { let encoded = "$argon2i$m=4096,t=3,p=a\ $c2FsdDEyMzQ="; let result = decode_string(encoded); assert_eq!(result, Err(Error::DecodingFail)); } #[test] fn decode_string_with_empty_hash_returns_error_result() { let encoded = "$argon2i$m=4096,t=3,p=a\ $c2FsdDEyMzQ=$"; let result = decode_string(encoded); assert_eq!(result, Err(Error::DecodingFail)); } #[cfg(feature = "crossbeam-utils")] #[test] fn encode_string_returns_correct_string() { let hash = b"12345678901234567890123456789012".to_vec(); let config = Config { ad: &[], hash_length: hash.len() as u32, lanes: 1, mem_cost: 4096, secret: &[], thread_mode: ThreadMode::Parallel, time_cost: 3, variant: Variant::Argon2i, version: Version::Version13, }; let pwd = b"password".to_vec(); let salt = b"salt1234".to_vec(); let context = Context::new(config, &pwd, &salt).unwrap(); let expected = "$argon2i$v=19$m=4096,t=3,p=1\ $c2FsdDEyMzQ$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI"; let actual = encode_string(&context, &hash); assert_eq!(actual, expected); } #[test] fn num_len_returns_correct_length() { let tests = vec![ (1, 1), (10, 2), (110, 3), (1230, 4), (12340, 5), (123457, 6), ]; for (num, expected) in tests { let actual = num_len(num); assert_eq!(actual, expected); } } } rust-argon2-1.0.0/src/error.rs000064400000000000000000000073150072674642500143310ustar 00000000000000// Copyright (c) 2017 Martijn Rijkeboer // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::{error, fmt}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; /// Error type for Argon2 errors. #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] pub enum Error { /// The output (hash) is too short (minimum is 4). OutputTooShort, /// The output (hash) is too long (maximum is 2^32 - 1). OutputTooLong, /// The password is too short (minimum is 0). PwdTooShort, /// The password is too long (maximum is 2^32 - 1). PwdTooLong, /// The salt is too short (minimum is 8). SaltTooShort, /// The salt is too long (maximum is 2^32 - 1). SaltTooLong, /// The associated data is too short (minimum is 0). AdTooShort, /// The associated data is too long (maximum is 2^32 - 1). AdTooLong, /// The secret value is too short (minimum is 0). SecretTooShort, /// The secret value is too long (maximum is 2^32 - 1). SecretTooLong, /// The time cost (passes) is too small (minimum is 1). TimeTooSmall, /// The time cost (passes) is too large (maximum is 2^32 - 1). TimeTooLarge, /// The memory cost is too small (minimum is 8 x parallelism). MemoryTooLittle, /// The memory cost is too large (maximum 2GiB on 32-bit or 4TiB on 64-bit). MemoryTooMuch, /// The number of lanes (parallelism) is too small (minimum is 1). LanesTooFew, /// The number of lanes (parallelism) is too large (maximum is 2^24 - 1). LanesTooMany, /// Incorrect Argon2 variant. IncorrectType, /// Incorrect Argon2 version. IncorrectVersion, /// The decoding of the encoded data has failed. DecodingFail, } impl Error { fn msg(&self) -> &str { match *self { Error::OutputTooShort => "Output is too short", Error::OutputTooLong => "Output is too long", Error::PwdTooShort => "Password is too short", Error::PwdTooLong => "Password is too long", Error::SaltTooShort => "Salt is too short", Error::SaltTooLong => "Salt is too long", Error::AdTooShort => "Associated data is too short", Error::AdTooLong => "Associated data is too long", Error::SecretTooShort => "Secret is too short", Error::SecretTooLong => "Secret is too long", Error::TimeTooSmall => "Time cost is too small", Error::TimeTooLarge => "Time cost is too large", Error::MemoryTooLittle => "Memory cost is too small", Error::MemoryTooMuch => "Memory cost is too large", Error::LanesTooFew => "Too few lanes", Error::LanesTooMany => "Too many lanes", Error::IncorrectType => "There is no such type of Argon2", Error::IncorrectVersion => "There is no such version of Argon2", Error::DecodingFail => "Decoding failed", } } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.msg()) } } impl error::Error for Error { fn description(&self) -> &str { self.msg() } fn cause(&self) -> Option<&dyn error::Error> { None } } impl From for Error { fn from(_: base64::DecodeError) -> Self { Error::DecodingFail } } rust-argon2-1.0.0/src/lib.rs000064400000000000000000000060170072674642500137440ustar 00000000000000// Copyright (c) 2017 Martijn Rijkeboer // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Library for hashing passwords using //! [Argon2](https://github.com/P-H-C/phc-winner-argon2), the password-hashing //! function that won the //! [Password Hashing Competition (PHC)](https://password-hashing.net). //! //! # Usage //! //! To use this crate, add the following to your Cargo.toml: //! //! ```toml //! [dependencies] //! rust-argon2 = "1.0" //! ``` //! //! And the following to your crate root: //! //! ```rust //! extern crate argon2; //! ``` //! //! # Examples //! //! Create a password hash using the defaults and verify it: //! //! ```rust //! use argon2::{self, Config}; //! //! let password = b"password"; //! let salt = b"randomsalt"; //! let config = Config::default(); //! let hash = argon2::hash_encoded(password, salt, &config).unwrap(); //! let matches = argon2::verify_encoded(&hash, password).unwrap(); //! assert!(matches); //! ``` //! //! Create a password hash with custom settings and verify it: //! //! ```rust //! use argon2::{self, Config, ThreadMode, Variant, Version}; //! //! let password = b"password"; //! let salt = b"othersalt"; //! let config = Config { //! variant: Variant::Argon2i, //! version: Version::Version13, //! mem_cost: 65536, //! time_cost: 10, #![cfg_attr(feature = "crossbeam-utils", doc = " lanes: 4,")] #![cfg_attr( feature = "crossbeam-utils", doc = " thread_mode: ThreadMode::Parallel," )] #![cfg_attr(not(feature = "crossbeam-utils"), doc = " lanes: 1,")] #![cfg_attr( not(feature = "crossbeam-utils"), doc = " thread_mode: ThreadMode::Sequential," )] //! secret: &[], //! ad: &[], //! hash_length: 32 //! }; //! let hash = argon2::hash_encoded(password, salt, &config).unwrap(); //! let matches = argon2::verify_encoded(&hash, password).unwrap(); //! assert!(matches); //! ``` //! //! # Limitations //! //! This crate has the same limitation as the `blake2-rfc` crate that it uses. //! It does not attempt to clear potentially sensitive data from its work //! memory. To do so correctly without a heavy performance penalty would //! require help from the compiler. It's better to not attempt to do so than to //! present a false assurance. //! //! This version uses the standard implementation and does not yet implement //! optimizations. Therefore, it is not the fastest implementation available. mod argon2; mod block; mod common; mod config; mod context; mod core; mod decoded; mod encoding; mod error; mod memory; mod result; mod thread_mode; mod variant; mod version; pub use crate::argon2::*; pub use crate::config::Config; pub use crate::error::Error; pub use crate::result::Result; pub use crate::thread_mode::ThreadMode; pub use crate::variant::Variant; pub use crate::version::Version; rust-argon2-1.0.0/src/memory.rs000064400000000000000000000061520072674642500145060ustar 00000000000000// Copyright (c) 2017 Martijn Rijkeboer // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::block::Block; use std::fmt; use std::fmt::Debug; use std::ops::{Index, IndexMut}; /// Structure representing the memory matrix. pub struct Memory { /// The number of rows. rows: usize, /// The number of columns. cols: usize, /// The flat array of blocks representing the memory matrix. blocks: Box<[Block]>, } impl Memory { /// Creates a new memory matrix. pub fn new(lanes: u32, lane_length: u32) -> Memory { let rows = lanes as usize; let cols = lane_length as usize; let total = rows * cols; let blocks = vec![Block::zero(); total].into_boxed_slice(); Memory { rows, cols, blocks } } #[cfg(feature = "crossbeam-utils")] /// Gets the mutable lanes representation of the memory matrix. pub fn as_lanes_mut(&mut self) -> Vec<&mut Memory> { let ptr: *mut Memory = self; let mut vec = Vec::with_capacity(self.rows); for _ in 0..self.rows { vec.push(unsafe { &mut (*ptr) }); } vec } } impl Debug for Memory { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Memory {{ rows: {}, cols: {} }}", self.rows, self.cols) } } impl Index for Memory { type Output = Block; fn index(&self, index: u32) -> &Block { &self.blocks[index as usize] } } impl Index for Memory { type Output = Block; fn index(&self, index: u64) -> &Block { &self.blocks[index as usize] } } impl Index<(u32, u32)> for Memory { type Output = Block; fn index(&self, index: (u32, u32)) -> &Block { let pos = ((index.0 as usize) * self.cols) + (index.1 as usize); &self.blocks[pos] } } impl IndexMut for Memory { fn index_mut(&mut self, index: u32) -> &mut Block { &mut self.blocks[index as usize] } } impl IndexMut for Memory { fn index_mut(&mut self, index: u64) -> &mut Block { &mut self.blocks[index as usize] } } impl IndexMut<(u32, u32)> for Memory { fn index_mut(&mut self, index: (u32, u32)) -> &mut Block { let pos = ((index.0 as usize) * self.cols) + (index.1 as usize); &mut self.blocks[pos] } } #[cfg(test)] mod tests { use crate::memory::Memory; #[test] fn new_returns_correct_instance() { let lanes = 4; let lane_length = 128; let memory = Memory::new(lanes, lane_length); assert_eq!(memory.rows, lanes as usize); assert_eq!(memory.cols, lane_length as usize); assert_eq!(memory.blocks.len(), 512); } #[cfg(feature = "crossbeam-utils")] #[test] fn as_lanes_mut_returns_correct_vec() { let mut memory = Memory::new(4, 128); let lanes = memory.as_lanes_mut(); assert_eq!(lanes.len(), 4); } } rust-argon2-1.0.0/src/result.rs000064400000000000000000000007750072674642500145210ustar 00000000000000// Copyright (c) 2017 Martijn Rijkeboer // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::error::Error; use std::result; /// A specialized result type for Argon2 operations. pub type Result = result::Result; rust-argon2-1.0.0/src/thread_mode.rs000064400000000000000000000036150072674642500154520ustar 00000000000000// Copyright (c) 2017 Xidorn Quan // Copyright (c) 2017 Martijn Rijkeboer // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. /// The thread mode used to perform the hashing. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum ThreadMode { /// Run in one thread. Sequential, #[cfg(feature = "crossbeam-utils")] /// Run in the same number of threads as the number of lanes. Parallel, } impl ThreadMode { #[cfg(feature = "crossbeam-utils")] /// Create a thread mode from the threads count. pub fn from_threads(threads: u32) -> ThreadMode { if threads > 1 { ThreadMode::Parallel } else { ThreadMode::Sequential } } #[cfg(not(feature = "crossbeam-utils"))] pub fn from_threads(threads: u32) -> ThreadMode { assert_eq!(threads, 1); Self::default() } } impl Default for ThreadMode { fn default() -> ThreadMode { ThreadMode::Sequential } } #[cfg(test)] mod tests { use crate::thread_mode::ThreadMode; #[test] fn default_returns_correct_thread_mode() { assert_eq!(ThreadMode::default(), ThreadMode::Sequential); } #[cfg(feature = "crossbeam-utils")] #[test] fn from_threads_returns_correct_thread_mode() { assert_eq!(ThreadMode::from_threads(0), ThreadMode::Sequential); assert_eq!(ThreadMode::from_threads(1), ThreadMode::Sequential); assert_eq!(ThreadMode::from_threads(2), ThreadMode::Parallel); assert_eq!(ThreadMode::from_threads(10), ThreadMode::Parallel); assert_eq!(ThreadMode::from_threads(100), ThreadMode::Parallel); } } rust-argon2-1.0.0/src/variant.rs000064400000000000000000000117760072674642500146520ustar 00000000000000// Copyright (c) 2017 Martijn Rijkeboer // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::error::Error; use crate::result::Result; use std::fmt; /// The Argon2 variant. #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Variant { /// Argon2 using data-dependent memory access to thwart tradeoff attacks. /// Recommended for cryptocurrencies and backend servers. Argon2d = 0, /// Argon2 using data-independent memory access to thwart side-channel /// attacks. Recommended for password hashing and password-based key /// derivation. Argon2i = 1, /// Argon2 using hybrid construction. Argon2id = 2, } impl Variant { /// Gets the lowercase string slice representation of the variant. pub fn as_lowercase_str(&self) -> &'static str { match *self { Variant::Argon2d => "argon2d", Variant::Argon2i => "argon2i", Variant::Argon2id => "argon2id", } } /// Gets the u32 representation of the variant. pub fn as_u32(&self) -> u32 { *self as u32 } /// Gets the u64 representation of the variant. pub fn as_u64(&self) -> u64 { *self as u64 } /// Gets the uppercase string slice representation of the variant. pub fn as_uppercase_str(&self) -> &'static str { match *self { Variant::Argon2d => "Argon2d", Variant::Argon2i => "Argon2i", Variant::Argon2id => "Argon2id", } } /// Attempts to create a variant from a string slice. pub fn from_str(str: &str) -> Result { match str { "Argon2d" => Ok(Variant::Argon2d), "Argon2i" => Ok(Variant::Argon2i), "Argon2id" => Ok(Variant::Argon2id), "argon2d" => Ok(Variant::Argon2d), "argon2i" => Ok(Variant::Argon2i), "argon2id" => Ok(Variant::Argon2id), _ => Err(Error::DecodingFail), } } /// Attempts to create a variant from an u32. pub fn from_u32(val: u32) -> Result { match val { 0 => Ok(Variant::Argon2d), 1 => Ok(Variant::Argon2i), 2 => Ok(Variant::Argon2id), _ => Err(Error::IncorrectType), } } } impl Default for Variant { fn default() -> Variant { Variant::Argon2i } } impl fmt::Display for Variant { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.as_lowercase_str()) } } #[cfg(test)] mod tests { use crate::error::Error; use crate::variant::Variant; #[test] fn as_lowercase_str_returns_correct_str() { assert_eq!(Variant::Argon2d.as_lowercase_str(), "argon2d"); assert_eq!(Variant::Argon2i.as_lowercase_str(), "argon2i"); assert_eq!(Variant::Argon2id.as_lowercase_str(), "argon2id"); } #[test] fn as_u32_returns_correct_u32() { assert_eq!(Variant::Argon2d.as_u32(), 0); assert_eq!(Variant::Argon2i.as_u32(), 1); assert_eq!(Variant::Argon2id.as_u32(), 2); } #[test] fn as_u64_returns_correct_u64() { assert_eq!(Variant::Argon2d.as_u64(), 0); assert_eq!(Variant::Argon2i.as_u64(), 1); assert_eq!(Variant::Argon2id.as_u64(), 2); } #[test] fn as_uppercase_str_returns_correct_str() { assert_eq!(Variant::Argon2d.as_uppercase_str(), "Argon2d"); assert_eq!(Variant::Argon2i.as_uppercase_str(), "Argon2i"); assert_eq!(Variant::Argon2id.as_uppercase_str(), "Argon2id"); } #[test] fn default_returns_correct_variant() { assert_eq!(Variant::default(), Variant::Argon2i); } #[test] fn display_returns_correct_string() { assert_eq!(format!("{}", Variant::Argon2d), "argon2d"); assert_eq!(format!("{}", Variant::Argon2i), "argon2i"); assert_eq!(format!("{}", Variant::Argon2id), "argon2id"); } #[test] fn from_str_returns_correct_result() { assert_eq!(Variant::from_str("Argon2d"), Ok(Variant::Argon2d)); assert_eq!(Variant::from_str("Argon2i"), Ok(Variant::Argon2i)); assert_eq!(Variant::from_str("Argon2id"), Ok(Variant::Argon2id)); assert_eq!(Variant::from_str("argon2d"), Ok(Variant::Argon2d)); assert_eq!(Variant::from_str("argon2i"), Ok(Variant::Argon2i)); assert_eq!(Variant::from_str("argon2id"), Ok(Variant::Argon2id)); assert_eq!(Variant::from_str("foobar"), Err(Error::DecodingFail)); } #[test] fn from_u32_returns_correct_result() { assert_eq!(Variant::from_u32(0), Ok(Variant::Argon2d)); assert_eq!(Variant::from_u32(1), Ok(Variant::Argon2i)); assert_eq!(Variant::from_u32(2), Ok(Variant::Argon2id)); assert_eq!(Variant::from_u32(3), Err(Error::IncorrectType)); } } rust-argon2-1.0.0/src/version.rs000064400000000000000000000051470072674642500146660ustar 00000000000000// Copyright (c) 2017 Martijn Rijkeboer // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::error::Error; use crate::result::Result; use std::fmt; /// The Argon2 version. #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Version { /// Version 0x10. Version10 = 0x10, /// Version 0x13 (Recommended). Version13 = 0x13, } impl Version { /// Gets the u32 representation of the version. pub fn as_u32(&self) -> u32 { *self as u32 } /// Attempts to create a version from a string slice. pub fn from_str(str: &str) -> Result { match str { "16" => Ok(Version::Version10), "19" => Ok(Version::Version13), _ => Err(Error::DecodingFail), } } /// Attempts to create a version from an u32. pub fn from_u32(val: u32) -> Result { match val { 0x10 => Ok(Version::Version10), 0x13 => Ok(Version::Version13), _ => Err(Error::IncorrectVersion), } } } impl Default for Version { fn default() -> Version { Version::Version13 } } impl fmt::Display for Version { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.as_u32()) } } #[cfg(test)] mod tests { use crate::error::Error; use crate::version::Version; #[test] fn as_u32_returns_correct_u32() { assert_eq!(Version::Version10.as_u32(), 0x10); assert_eq!(Version::Version13.as_u32(), 0x13); } #[test] fn default_returns_correct_version() { assert_eq!(Version::default(), Version::Version13); } #[test] fn display_returns_correct_string() { assert_eq!(format!("{}", Version::Version10), "16"); assert_eq!(format!("{}", Version::Version13), "19"); } #[test] fn from_str_returns_correct_result() { assert_eq!(Version::from_str("16"), Ok(Version::Version10)); assert_eq!(Version::from_str("19"), Ok(Version::Version13)); assert_eq!(Version::from_str("11"), Err(Error::DecodingFail)); } #[test] fn from_u32_returns_correct_result() { assert_eq!(Version::from_u32(0x10), Ok(Version::Version10)); assert_eq!(Version::from_u32(0x13), Ok(Version::Version13)); assert_eq!(Version::from_u32(0), Err(Error::IncorrectVersion)); } } rust-argon2-1.0.0/tests/integration_test.rs000064400000000000000000000657570072674642500171530ustar 00000000000000// Copyright (c) 2017 Martijn Rijkeboer // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. // These tests are based on Argon's test.c test suite. use argon2::{Config, Error, ThreadMode, Variant, Version}; use hex::ToHex; #[cfg(not(debug_assertions))] #[test] fn test_argon2d_version10_1() { hash_test( Variant::Argon2d, Version::Version10, 2, 65536, 1, b"password", b"somesalt", "2ec0d925358f5830caf0c1cc8a3ee58b34505759428b859c79b72415f51f9221", "$argon2d$m=65536,t=2,p=1$c29tZXNhbHQ\ $LsDZJTWPWDDK8MHMij7lizRQV1lCi4WcebckFfUfkiE", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2d_version10_2() { hash_test( Variant::Argon2d, Version::Version10, 2, 1048576, 1, b"password", b"somesalt", "28ae0d1037991b55faec18425274d5f9169b2f44bee19b45b7a561d489d6019a", "$argon2d$m=1048576,t=2,p=1$c29tZXNhbHQ\ $KK4NEDeZG1X67BhCUnTV+RabL0S+4ZtFt6Vh1InWAZo", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2d_version10_3() { hash_test( Variant::Argon2d, Version::Version10, 2, 262144, 1, b"password", b"somesalt", "41c89760d85b80ba1be7e959ebd16390bfb4176db9466d70f670457ccade4ec8", "$argon2d$m=262144,t=2,p=1$c29tZXNhbHQ\ $QciXYNhbgLob5+lZ69FjkL+0F225Rm1w9nBFfMreTsg", ); } #[test] fn test_argon2d_version10_4() { hash_test( Variant::Argon2d, Version::Version10, 2, 256, 1, b"password", b"somesalt", "bd404868ff00c52e7543c8332e6a772a5724892d7e328d5cf253bbc8e726b371", "$argon2d$m=256,t=2,p=1$c29tZXNhbHQ\ $vUBIaP8AxS51Q8gzLmp3KlckiS1+Mo1c8lO7yOcms3E", ); } #[test] fn test_argon2d_version10_5() { hash_test( Variant::Argon2d, Version::Version10, 2, 256, 2, b"password", b"somesalt", "6a91d02b9f8854ba0841f04aa6e53c1d3374c0a0c646b8e431b03de805b91ec3", "$argon2d$m=256,t=2,p=2$c29tZXNhbHQ\ $apHQK5+IVLoIQfBKpuU8HTN0wKDGRrjkMbA96AW5HsM", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2d_version10_6() { hash_test( Variant::Argon2d, Version::Version10, 1, 65536, 1, b"password", b"somesalt", "05d1d0a85f499e9397b9c1c936b20f366a7273ccf259e2edfdb44ca8f86dd11f", "$argon2d$m=65536,t=1,p=1$c29tZXNhbHQ\ $BdHQqF9JnpOXucHJNrIPNmpyc8zyWeLt/bRMqPht0R8", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2d_version10_7() { hash_test( Variant::Argon2d, Version::Version10, 4, 65536, 1, b"password", b"somesalt", "99e787faced20949df5a3b9c8620712b45cfea061716adf8b13efb7feee44084", "$argon2d$m=65536,t=4,p=1$c29tZXNhbHQ\ $meeH+s7SCUnfWjuchiBxK0XP6gYXFq34sT77f+7kQIQ", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2d_version10_8() { hash_test( Variant::Argon2d, Version::Version10, 2, 65536, 1, b"differentpassword", b"somesalt", "b9c2e4a4ec9a6db99ca4e2b549dd64256305b95754b0a70ad757cd1ff7eed4a4", "$argon2d$m=65536,t=2,p=1$c29tZXNhbHQ\ $ucLkpOyabbmcpOK1Sd1kJWMFuVdUsKcK11fNH/fu1KQ", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2d_version10_9() { hash_test( Variant::Argon2d, Version::Version10, 2, 65536, 1, b"password", b"diffsalt", "d1da14196c233e0a3cb687aa6e6c465cb50669ffdb891158c2b75722395f14d5", "$argon2d$m=65536,t=2,p=1$ZGlmZnNhbHQ\ $0doUGWwjPgo8toeqbmxGXLUGaf/biRFYwrdXIjlfFNU", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2d_version13_1() { hash_test( Variant::Argon2d, Version::Version13, 2, 65536, 1, b"password", b"somesalt", "955e5d5b163a1b60bba35fc36d0496474fba4f6b59ad53628666f07fb2f93eaf", "$argon2d$v=19$m=65536,t=2,p=1$c29tZXNhbHQ\ $lV5dWxY6G2C7o1/DbQSWR0+6T2tZrVNihmbwf7L5Pq8", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2d_version13_2() { hash_test( Variant::Argon2d, Version::Version13, 2, 1048576, 1, b"password", b"somesalt", "c2d680a6e7ac1b75e2c2b1e5e71b1701584869492efea4a76ccc91fec622d1ae", "$argon2d$v=19$m=1048576,t=2,p=1$c29tZXNhbHQ\ $wtaApuesG3XiwrHl5xsXAVhIaUku/qSnbMyR/sYi0a4", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2d_version13_3() { hash_test( Variant::Argon2d, Version::Version13, 2, 262144, 1, b"password", b"somesalt", "9678b3379ce20ddc96fa38f045c8cdc96282287ff2848efd867582b9528a2155", "$argon2d$v=19$m=262144,t=2,p=1$c29tZXNhbHQ\ $lnizN5ziDdyW+jjwRcjNyWKCKH/yhI79hnWCuVKKIVU", ); } #[test] fn test_argon2d_version13_4() { hash_test( Variant::Argon2d, Version::Version13, 2, 256, 1, b"password", b"somesalt", "25c4ee8ba448054b49efc804e478b9d823be1f9bd2e99f51d6ec4007a3a1501f", "$argon2d$v=19$m=256,t=2,p=1$c29tZXNhbHQ\ $JcTui6RIBUtJ78gE5Hi52CO+H5vS6Z9R1uxAB6OhUB8", ); } #[test] fn test_argon2d_version13_5() { hash_test( Variant::Argon2d, Version::Version13, 2, 256, 2, b"password", b"somesalt", "7b69c92d7c3889aad1281dbc8baefc12cc37c80f1c75e33ef2c2d40c28ebc573", "$argon2d$v=19$m=256,t=2,p=2$c29tZXNhbHQ\ $e2nJLXw4iarRKB28i678Esw3yA8cdeM+8sLUDCjrxXM", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2d_version13_6() { hash_test( Variant::Argon2d, Version::Version13, 1, 65536, 1, b"password", b"somesalt", "8193708c030f09b121526b0efdffce738fef0ddb13937a65d43f3f1eab9aa802", "$argon2d$v=19$m=65536,t=1,p=1$c29tZXNhbHQ\ $gZNwjAMPCbEhUmsO/f/Oc4/vDdsTk3pl1D8/HquaqAI", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2d_version13_7() { hash_test( Variant::Argon2d, Version::Version13, 4, 65536, 1, b"password", b"somesalt", "cd1e2816cf8895fffe535b87b7a0aa3612f73c6ce063de83b1e7b4621ca0afe6", "$argon2d$v=19$m=65536,t=4,p=1$c29tZXNhbHQ\ $zR4oFs+Ilf/+U1uHt6CqNhL3PGzgY96Dsee0Yhygr+Y", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2d_version13_8() { hash_test( Variant::Argon2d, Version::Version13, 2, 65536, 1, b"differentpassword", b"somesalt", "a34dafc893182d521ae467bbfdce60f973a1223c4615654efeb0bfef8a930e25", "$argon2d$v=19$m=65536,t=2,p=1$c29tZXNhbHQ\ $o02vyJMYLVIa5Ge7/c5g+XOhIjxGFWVO/rC/74qTDiU", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2d_version13_9() { hash_test( Variant::Argon2d, Version::Version13, 2, 65536, 1, b"password", b"diffsalt", "f00eb8a999a1c6949c61fbbee3c3cece5066e3ff5c79aed421266055e2bf4dd7", "$argon2d$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ\ $8A64qZmhxpScYfu+48POzlBm4/9cea7UISZgVeK/Tdc", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2i_version10_1() { hash_test( Variant::Argon2i, Version::Version10, 2, 65536, 1, b"password", b"somesalt", "f6c4db4a54e2a370627aff3db6176b94a2a209a62c8e36152711802f7b30c694", "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ\ $9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2i_version10_2() { hash_test( Variant::Argon2i, Version::Version10, 2, 1048576, 1, b"password", b"somesalt", "9690ec55d28d3ed32562f2e73ea62b02b018757643a2ae6e79528459de8106e9", "$argon2i$m=1048576,t=2,p=1$c29tZXNhbHQ\ $lpDsVdKNPtMlYvLnPqYrArAYdXZDoq5ueVKEWd6BBuk", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2i_version10_3() { hash_test( Variant::Argon2i, Version::Version10, 2, 262144, 1, b"password", b"somesalt", "3e689aaa3d28a77cf2bc72a51ac53166761751182f1ee292e3f677a7da4c2467", "$argon2i$m=262144,t=2,p=1$c29tZXNhbHQ\ $Pmiaqj0op3zyvHKlGsUxZnYXURgvHuKS4/Z3p9pMJGc", ); } #[test] fn test_argon2i_version10_4() { hash_test( Variant::Argon2i, Version::Version10, 2, 256, 1, b"password", b"somesalt", "fd4dd83d762c49bdeaf57c47bdcd0c2f1babf863fdeb490df63ede9975fccf06", "$argon2i$m=256,t=2,p=1$c29tZXNhbHQ\ $/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY", ); } #[test] fn test_argon2i_version10_5() { hash_test( Variant::Argon2i, Version::Version10, 2, 256, 2, b"password", b"somesalt", "b6c11560a6a9d61eac706b79a2f97d68b4463aa3ad87e00c07e2b01e90c564fb", "$argon2i$m=256,t=2,p=2$c29tZXNhbHQ\ $tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2i_version10_6() { hash_test( Variant::Argon2i, Version::Version10, 1, 65536, 1, b"password", b"somesalt", "81630552b8f3b1f48cdb1992c4c678643d490b2b5eb4ff6c4b3438b5621724b2", "$argon2i$m=65536,t=1,p=1$c29tZXNhbHQ\ $gWMFUrjzsfSM2xmSxMZ4ZD1JCytetP9sSzQ4tWIXJLI", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2i_version10_7() { hash_test( Variant::Argon2i, Version::Version10, 4, 65536, 1, b"password", b"somesalt", "f212f01615e6eb5d74734dc3ef40ade2d51d052468d8c69440a3a1f2c1c2847b", "$argon2i$m=65536,t=4,p=1$c29tZXNhbHQ\ $8hLwFhXm6110c03D70Ct4tUdBSRo2MaUQKOh8sHChHs", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2i_version10_8() { hash_test( Variant::Argon2i, Version::Version10, 2, 65536, 1, b"differentpassword", b"somesalt", "e9c902074b6754531a3a0be519e5baf404b30ce69b3f01ac3bf21229960109a3", "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ\ $6ckCB0tnVFMaOgvlGeW69ASzDOabPwGsO/ISKZYBCaM", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2i_version10_9() { hash_test( Variant::Argon2i, Version::Version10, 2, 65536, 1, b"password", b"diffsalt", "79a103b90fe8aef8570cb31fc8b22259778916f8336b7bdac3892569d4f1c497", "$argon2i$m=65536,t=2,p=1$ZGlmZnNhbHQ\ $eaEDuQ/orvhXDLMfyLIiWXeJFvgza3vaw4kladTxxJc", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2i_version13_1() { hash_test( Variant::Argon2i, Version::Version13, 2, 65536, 1, b"password", b"somesalt", "c1628832147d9720c5bd1cfd61367078729f6dfb6f8fea9ff98158e0d7816ed0", "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ\ $wWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2i_version13_2() { hash_test( Variant::Argon2i, Version::Version13, 2, 1048576, 1, b"password", b"somesalt", "d1587aca0922c3b5d6a83edab31bee3c4ebaef342ed6127a55d19b2351ad1f41", "$argon2i$v=19$m=1048576,t=2,p=1$c29tZXNhbHQ\ $0Vh6ygkiw7XWqD7asxvuPE667zQu1hJ6VdGbI1GtH0E", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2i_version13_3() { hash_test( Variant::Argon2i, Version::Version13, 2, 262144, 1, b"password", b"somesalt", "296dbae80b807cdceaad44ae741b506f14db0959267b183b118f9b24229bc7cb", "$argon2i$v=19$m=262144,t=2,p=1$c29tZXNhbHQ\ $KW266AuAfNzqrUSudBtQbxTbCVkmexg7EY+bJCKbx8s", ); } #[test] fn test_argon2i_version13_4() { hash_test( Variant::Argon2i, Version::Version13, 2, 256, 1, b"password", b"somesalt", "89e9029f4637b295beb027056a7336c414fadd43f6b208645281cb214a56452f", "$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ\ $iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8", ); } #[test] fn test_argon2i_version13_5() { hash_test( Variant::Argon2i, Version::Version13, 2, 256, 2, b"password", b"somesalt", "4ff5ce2769a1d7f4c8a491df09d41a9fbe90e5eb02155a13e4c01e20cd4eab61", "$argon2i$v=19$m=256,t=2,p=2$c29tZXNhbHQ\ $T/XOJ2mh1/TIpJHfCdQan76Q5esCFVoT5MAeIM1Oq2E", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2i_version13_6() { hash_test( Variant::Argon2i, Version::Version13, 1, 65536, 1, b"password", b"somesalt", "d168075c4d985e13ebeae560cf8b94c3b5d8a16c51916b6f4ac2da3ac11bbecf", "$argon2i$v=19$m=65536,t=1,p=1$c29tZXNhbHQ\ $0WgHXE2YXhPr6uVgz4uUw7XYoWxRkWtvSsLaOsEbvs8", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2i_version13_7() { hash_test( Variant::Argon2i, Version::Version13, 4, 65536, 1, b"password", b"somesalt", "aaa953d58af3706ce3df1aefd4a64a84e31d7f54175231f1285259f88174ce5b", "$argon2i$v=19$m=65536,t=4,p=1$c29tZXNhbHQ\ $qqlT1YrzcGzj3xrv1KZKhOMdf1QXUjHxKFJZ+IF0zls", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2i_version13_8() { hash_test( Variant::Argon2i, Version::Version13, 2, 65536, 1, b"differentpassword", b"somesalt", "14ae8da01afea8700c2358dcef7c5358d9021282bd88663a4562f59fb74d22ee", "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ\ $FK6NoBr+qHAMI1jc73xTWNkCEoK9iGY6RWL1n7dNIu4", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2i_version13_9() { hash_test( Variant::Argon2i, Version::Version13, 2, 65536, 1, b"password", b"diffsalt", "b0357cccfbef91f3860b0dba447b2348cbefecadaf990abfe9cc40726c521271", "$argon2i$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ\ $sDV8zPvvkfOGCw26RHsjSMvv7K2vmQq/6cxAcmxSEnE", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2id_version10_1() { hash_test( Variant::Argon2id, Version::Version10, 2, 65536, 1, b"password", b"somesalt", "980ebd24a4e667f16346f9d4a78b175728783613e0cc6fb17c2ec884b16435df", "$argon2id$v=16$m=65536,t=2,p=1$c29tZXNhbHQ\ $mA69JKTmZ/FjRvnUp4sXVyh4NhPgzG+xfC7IhLFkNd8", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2id_version10_2() { hash_test( Variant::Argon2id, Version::Version10, 2, 1048576, 1, b"password", b"somesalt", "ce622fc2053acd224c06e7be122f9e9554b62b8a414b032056b46123422e32ab", "$argon2id$v=16$m=1048576,t=2,p=1$c29tZXNhbHQ\ $zmIvwgU6zSJMBue+Ei+elVS2K4pBSwMgVrRhI0IuMqs", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2id_version10_3() { hash_test( Variant::Argon2id, Version::Version10, 2, 262144, 1, b"password", b"somesalt", "01cbafb58f2dc764adced1ab9a90fcb62f6ed8a066023b6ba0c204db6c9b90cd", "$argon2id$v=16$m=262144,t=2,p=1$c29tZXNhbHQ\ $AcuvtY8tx2StztGrmpD8ti9u2KBmAjtroMIE22ybkM0", ); } #[test] fn test_argon2id_version10_4() { hash_test( Variant::Argon2id, Version::Version10, 2, 256, 1, b"password", b"somesalt", "da070e576e50f2f38a3c897cbddc6c7fb4028e870971ff9eae7b4e1879295e6e", "$argon2id$v=16$m=256,t=2,p=1$c29tZXNhbHQ\ $2gcOV25Q8vOKPIl8vdxsf7QCjocJcf+erntOGHkpXm4", ); } #[test] fn test_argon2id_version10_5() { hash_test( Variant::Argon2id, Version::Version10, 2, 256, 2, b"password", b"somesalt", "f8aabb5315c63cddcdb3b4a021550928e525699da8fcbd1c2b0b1ccd35cc87a7", "$argon2id$v=16$m=256,t=2,p=2$c29tZXNhbHQ\ $+Kq7UxXGPN3Ns7SgIVUJKOUlaZ2o/L0cKwsczTXMh6c", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2id_version10_6() { hash_test( Variant::Argon2id, Version::Version10, 1, 65536, 1, b"password", b"somesalt", "8e0f310407a989013a823defbd6cbae54d86b35d1ac16e4f88eb4645e1357956", "$argon2id$v=16$m=65536,t=1,p=1$c29tZXNhbHQ\ $jg8xBAepiQE6gj3vvWy65U2Gs10awW5PiOtGReE1eVY", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2id_version10_7() { hash_test( Variant::Argon2id, Version::Version10, 4, 65536, 1, b"password", b"somesalt", "16653426e582143da6a0ff75daac9fa94c21ac2c221c96bd3645a5c15c2d962a", "$argon2id$v=16$m=65536,t=4,p=1$c29tZXNhbHQ\ $FmU0JuWCFD2moP912qyfqUwhrCwiHJa9NkWlwVwtlio", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2id_version10_8() { hash_test( Variant::Argon2id, Version::Version10, 2, 65536, 1, b"differentpassword", b"somesalt", "75709d4c96a2c235696a77eefeecf33e64221f470b26177c19e5e27642612dbb", "$argon2id$v=16$m=65536,t=2,p=1$c29tZXNhbHQ\ $dXCdTJaiwjVpanfu/uzzPmQiH0cLJhd8GeXidkJhLbs", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2id_version10_9() { hash_test( Variant::Argon2id, Version::Version10, 2, 65536, 1, b"password", b"diffsalt", "f0b57cfe03a9d78c4700e2fe802002343660a56b1b5d21ead788e6bb44ef48d3", "$argon2id$v=16$m=65536,t=2,p=1$ZGlmZnNhbHQ\ $8LV8/gOp14xHAOL+gCACNDZgpWsbXSHq14jmu0TvSNM", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2id_version13_1() { hash_test( Variant::Argon2id, Version::Version13, 2, 65536, 1, b"password", b"somesalt", "09316115d5cf24ed5a15a31a3ba326e5cf32edc24702987c02b6566f61913cf7", "$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ\ $CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2id_version13_2() { hash_test( Variant::Argon2id, Version::Version13, 2, 1048576, 1, b"password", b"somesalt", "16a1cb8a061cf1f8b16fb195fe559fa6d1435e43dded2f4fb07293176be892fa", "$argon2id$v=19$m=1048576,t=2,p=1$c29tZXNhbHQ\ $FqHLigYc8fixb7GV/lWfptFDXkPd7S9PsHKTF2vokvo", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2id_version13_3() { hash_test( Variant::Argon2id, Version::Version13, 2, 262144, 1, b"password", b"somesalt", "78fe1ec91fb3aa5657d72e710854e4c3d9b9198c742f9616c2f085bed95b2e8c", "$argon2id$v=19$m=262144,t=2,p=1$c29tZXNhbHQ\ $eP4eyR+zqlZX1y5xCFTkw9m5GYx0L5YWwvCFvtlbLow", ); } #[test] fn test_argon2id_version13_4() { hash_test( Variant::Argon2id, Version::Version13, 2, 256, 1, b"password", b"somesalt", "9dfeb910e80bad0311fee20f9c0e2b12c17987b4cac90c2ef54d5b3021c68bfe", "$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ\ $nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4", ); } #[test] fn test_argon2id_version13_5() { hash_test( Variant::Argon2id, Version::Version13, 2, 256, 2, b"password", b"somesalt", "6d093c501fd5999645e0ea3bf620d7b8be7fd2db59c20d9fff9539da2bf57037", "$argon2id$v=19$m=256,t=2,p=2$c29tZXNhbHQ\ $bQk8UB/VmZZF4Oo79iDXuL5/0ttZwg2f/5U52iv1cDc", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2id_version13_6() { hash_test( Variant::Argon2id, Version::Version13, 1, 65536, 1, b"password", b"somesalt", "f6a5adc1ba723dddef9b5ac1d464e180fcd9dffc9d1cbf76cca2fed795d9ca98", "$argon2id$v=19$m=65536,t=1,p=1$c29tZXNhbHQ\ $9qWtwbpyPd3vm1rB1GThgPzZ3/ydHL92zKL+15XZypg", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2id_version13_7() { hash_test( Variant::Argon2id, Version::Version13, 4, 65536, 1, b"password", b"somesalt", "9025d48e68ef7395cca9079da4c4ec3affb3c8911fe4f86d1a2520856f63172c", "$argon2id$v=19$m=65536,t=4,p=1$c29tZXNhbHQ\ $kCXUjmjvc5XMqQedpMTsOv+zyJEf5PhtGiUghW9jFyw", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2id_version13_8() { hash_test( Variant::Argon2id, Version::Version13, 2, 65536, 1, b"differentpassword", b"somesalt", "0b84d652cf6b0c4beaef0dfe278ba6a80df6696281d7e0d2891b817d8c458fde", "$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ\ $C4TWUs9rDEvq7w3+J4umqA32aWKB1+DSiRuBfYxFj94", ); } #[cfg(not(debug_assertions))] #[test] fn test_argon2id_version13_9() { hash_test( Variant::Argon2id, Version::Version13, 2, 65536, 1, b"password", b"diffsalt", "bdf32b05ccc42eb15d58fd19b1f856b113da1e9a5874fdcc544308565aa8141c", "$argon2id$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ\ $vfMrBczELrFdWP0ZsfhWsRPaHppYdP3MVEMIVlqoFBw", ); } #[test] fn test_verify_encoded_with_missing_dollar_before_salt_version10() { let encoded = "$argon2i$m=65536,t=2,p=1c29tZXNhbHQ\ $9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"; let password = b"password"; let res = argon2::verify_encoded(encoded, password); assert_eq!(res, Err(Error::DecodingFail)); } #[test] fn test_verify_encoded_with_missing_dollar_before_salt_version13() { let encoded = "$argon2i$v=19$m=65536,t=2,p=1c29tZXNhbHQ\ $wWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA"; let password = b"password"; let res = argon2::verify_encoded(encoded, password); assert_eq!(res, Err(Error::DecodingFail)); } #[test] fn test_verify_encoded_with_missing_dollar_before_hash_version10() { let encoded = "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ\ 9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"; let password = b"password"; let res = argon2::verify_encoded(encoded, password); assert_eq!(res, Err(Error::DecodingFail)); } #[test] fn test_verify_encoded_with_missing_dollar_before_hash_version13() { let encoded = "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ\ wWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA"; let password = b"password"; let res = argon2::verify_encoded(encoded, password); assert_eq!(res, Err(Error::DecodingFail)); } #[test] fn test_verify_encoded_with_too_short_salt_version10() { let encoded = "$argon2i$m=65536,t=2,p=1$\ $9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"; let password = b"password"; let res = argon2::verify_encoded(encoded, password); assert_eq!(res, Err(Error::SaltTooShort)); } #[test] fn test_verify_encoded_with_too_short_salt_version13() { let encoded = "$argon2i$v=19$m=65536,t=2,p=1$\ $9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"; let password = b"password"; let res = argon2::verify_encoded(encoded, password); assert_eq!(res, Err(Error::SaltTooShort)); } #[cfg(not(debug_assertions))] #[test] fn test_verify_encoded_with_wrong_password_version10() { let encoded = "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ\ $b2G3seW+uPzerwQQC+/E1K50CLLO7YXy0JRcaTuswRo"; let password = b"password"; // should be passwore let res = argon2::verify_encoded(encoded, password); assert_eq!(res, Ok(false)); } #[cfg(not(debug_assertions))] #[test] fn test_verify_encoded_with_wrong_password_version13() { let encoded = "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ\ $8iIuixkI73Js3G1uMbezQXD0b8LG4SXGsOwoQkdAQIM"; let password = b"password"; // should be passwore let res = argon2::verify_encoded(encoded, password); assert_eq!(res, Ok(false)); } #[test] fn test_encoded_len_returns_correct_length() { assert_eq!(argon2::encoded_len(Variant::Argon2d, 256, 1, 1, 8, 32), 83); assert_eq!( argon2::encoded_len(Variant::Argon2i, 4096, 10, 10, 8, 32), 86 ); assert_eq!( argon2::encoded_len(Variant::Argon2id, 65536, 100, 10, 8, 32), 89 ); } #[test] fn test_hash_raw_with_not_enough_memory() { let pwd = b"password"; let salt = b"diffsalt"; let config = Config { variant: Variant::Argon2i, version: Version::Version13, mem_cost: 2, time_cost: 2, lanes: 1, thread_mode: ThreadMode::Sequential, secret: &[], ad: &[], hash_length: 32, }; let res = argon2::hash_raw(pwd, salt, &config); assert_eq!(res, Err(Error::MemoryTooLittle)); } #[test] fn test_hash_raw_with_too_short_salt() { let pwd = b"password"; let salt = b"s"; let config = Config { variant: Variant::Argon2i, version: Version::Version13, mem_cost: 2048, time_cost: 2, lanes: 1, thread_mode: ThreadMode::Sequential, secret: &[], ad: &[], hash_length: 32, }; let res = argon2::hash_raw(pwd, salt, &config); assert_eq!(res, Err(Error::SaltTooShort)); } fn hash_test( var: Variant, ver: Version, t: u32, m: u32, p: u32, pwd: &[u8], salt: &[u8], hex: &str, enc: &str, ) { let threads = if cfg!(feature = "crossbeam-utils") { p } else { 1 }; let config = Config { variant: var, version: ver, mem_cost: m, time_cost: t, lanes: p, thread_mode: ThreadMode::from_threads(threads), secret: &[], ad: &[], hash_length: 32, }; let hash = argon2::hash_raw(pwd, salt, &config).unwrap(); let hex_str = hash.encode_hex::(); assert_eq!(hex_str.as_str(), hex); let encoded = argon2::hash_encoded(pwd, salt, &config).unwrap(); let result = argon2::verify_encoded(encoded.as_str(), pwd).unwrap(); assert!(result); let result = argon2::verify_encoded(enc, pwd).unwrap(); assert!(result); }