Skip to content

Bring back parallelism #56

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ keywords = ["argon2", "argon2d", "argon2i", "hash", "password"]
[lib]
name = "argon2"

[features]
default = ["crossbeam-utils"]

[dependencies]
base64 = "0.21"
blake2b_simd = "1.0"
constant_time_eq = "0.3.0"
crossbeam-utils = { version = "0.8", optional = true }
serde = { version = "1.0", optional = true, features=["derive"] }

[dev-dependencies]
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ assert!(matches);
Create a password hash with custom settings and verify it:

```rust
use argon2::{self, Config, Variant, Version};
use argon2::{self, Config, ThreadMode, Variant, Version};

let password = b"password";
let salt = b"othersalt";
Expand All @@ -49,6 +49,7 @@ let config = Config {
mem_cost: 65536,
time_cost: 10,
lanes: 4,
thread_mode: ThreadMode::Parallel,
secret: &[],
ad: &[],
hash_length: 32
Expand Down
79 changes: 69 additions & 10 deletions src/argon2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ 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;

Expand Down Expand Up @@ -73,16 +74,25 @@ pub fn encoded_len(
/// ```
///
///
/// Create an Argon2d encoded hash with 4 lanes:
/// Create an Argon2d encoded hash with 4 lanes and parallel execution:
///
/// ```
/// use argon2::{self, Config, Variant};
/// use argon2::{self, Config, ThreadMode, Variant};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let mut config = Config::default();
/// config.variant = Variant::Argon2d;
/// config.lanes = 4;
#[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<String> {
Expand All @@ -108,16 +118,25 @@ pub fn hash_encoded(pwd: &[u8], salt: &[u8], config: &Config) -> Result<String>
/// ```
///
///
/// Create an Argon2d hash with 4 lanes:
/// Create an Argon2d hash with 4 lanes and parallel execution:
///
/// ```
/// use argon2::{self, Config, Variant};
/// use argon2::{self, Config, ThreadMode, Variant};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let mut config = Config::default();
/// config.variant = Variant::Argon2d;
/// config.lanes = 4;
#[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<Vec<u8>> {
Expand Down Expand Up @@ -160,12 +179,18 @@ pub fn verify_encoded(encoded: &str, pwd: &[u8]) -> Result<bool> {
/// ```
pub fn verify_encoded_ext(encoded: &str, pwd: &[u8], secret: &[u8], ad: &[u8]) -> Result<bool> {
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,
Expand All @@ -177,7 +202,6 @@ pub fn verify_encoded_ext(encoded: &str, pwd: &[u8], secret: &[u8], ad: &[u8]) -
///
/// # Examples
///
///
/// ```
/// use argon2::{self, Config};
///
Expand All @@ -203,7 +227,8 @@ pub fn verify_raw(pwd: &[u8], salt: &[u8], hash: &[u8], config: &Config) -> Resu
fn run(context: &Context) -> Vec<u8> {
let mut memory = Memory::new(context.config.lanes, context.lane_length);
core::initialize(context, &mut memory);
core::fill_memory_blocks(context, &mut memory);
// SAFETY: `memory` is constructed from `context`.
unsafe { core::fill_memory_blocks(context, &mut memory) };
core::finalize(context, &memory)
}

Expand All @@ -213,7 +238,41 @@ mod tests {

#[test]
fn single_thread_verification_multi_lane_hash() {
let hash = "$argon2i$v=19$m=4096,t=3,p=4$YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo$BvBk2OaSofBHfbrUW61nHrWB/43xgfs/QJJ5DkMAd8I";
verify_encoded(hash, b"foo").unwrap();
let hash = "$argon2i$v=19$m=4096,t=3,p=4$YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo$\
BvBk2OaSofBHfbrUW61nHrWB/43xgfs/QJJ5DkMAd8I";
let res = verify_encoded(hash, b"foo").unwrap();
assert!(res);
}

#[test]
fn test_argon2id_for_miri() {
let hash = "$argon2id$v=19$m=256,t=2,p=16$c29tZXNhbHQ$\
0gasyPnKXiBHQ5bft/bd4jrmy2DdtrLTX3JR9co7fRY";
let res = verify_encoded(hash, b"password").unwrap();
assert!(res);
}

#[test]
fn test_argon2id_for_miri_2() {
let hash = "$argon2id$v=19$m=512,t=2,p=8$c29tZXNhbHQ$\
qgW4yz2jO7oklapDpVwzUYgfDLzfwkppGTvhRDDBjkY";
let res = verify_encoded(hash, b"password").unwrap();
assert!(res);
}

#[test]
fn test_argon2d_for_miri() {
let hash = "$argon2d$v=19$m=256,t=2,p=16$c29tZXNhbHQ$\
doW5kZ/0cTwqwbYTwr9JD0wNwy3tMyJMMk9ojGsC8bk";
let res = verify_encoded(hash, b"password").unwrap();
assert!(res);
}

#[test]
fn test_argon2i_for_miri() {
let hash = "$argon2i$v=19$m=256,t=2,p=16$c29tZXNhbHQ$\
c1suSp12ZBNLSuyhD8pJriM2r5jP2kgZ5QdDAk3+HaY";
let res = verify_encoded(hash, b"password").unwrap();
assert!(res);
}
}
8 changes: 7 additions & 1 deletion src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ 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.
/// Structure for the (1 KiB) memory block implemented as 128 64-bit words.
// Blocks are 128-byte aligned to prevent false sharing. This specific alignment value replicates
// what `crossbeam-utils::CachePadded` does on modern architectures, which either use 128-byte
// cache lines (aarch64) or pull 64-byte cache lines in pairs (x86-64), without the need for
// stubbing that type in when `crossbeam-utils` isn't available. As blocks are fairly large (1
// KiB), this simplification shouldn't severy affect the (rarer) targets with smaller cache lines.
#[repr(align(128))]
pub struct Block([u64; common::QWORDS_IN_BLOCK]);

impl Block {
Expand Down
29 changes: 28 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crate::thread_mode::ThreadMode;
use crate::variant::Variant;
use crate::version::Version;

Expand All @@ -14,7 +15,7 @@ use crate::version::Version;
/// # Examples
///
/// ```
/// use argon2::{Config, Variant, Version};
/// use argon2::{Config, ThreadMode, Variant, Version};
///
/// let config = Config::default();
/// assert_eq!(config.ad, &[]);
Expand All @@ -24,6 +25,7 @@ use crate::version::Version;
/// assert_eq!(config.secret, &[]);
/// assert_eq!(config.time_cost, 2);
/// assert_eq!(config.variant, Variant::Argon2id);
/// assert_eq!(config.thread_mode, ThreadMode::Sequential);
/// assert_eq!(config.version, Version::Version13);
/// ```
#[derive(Clone, Debug, PartialEq)]
Expand All @@ -43,6 +45,9 @@ pub struct Config<'a> {
/// The key.
pub secret: &'a [u8],

/// The thread mode.
pub thread_mode: ThreadMode,

/// The number of passes.
pub time_cost: u32,

Expand All @@ -62,6 +67,7 @@ impl<'a> Config<'a> {
lanes: 1,
mem_cost: 4096,
secret: &[],
thread_mode: ThreadMode::default(),
time_cost: 3,
variant: Variant::Argon2i,
version: Version::Version13,
Expand All @@ -76,6 +82,7 @@ impl<'a> Config<'a> {
lanes: 1,
mem_cost: 47104,
secret: &[],
thread_mode: ThreadMode::default(),
time_cost: 1,
variant: Variant::Argon2id,
version: Version::Version13,
Expand All @@ -90,6 +97,7 @@ impl<'a> Config<'a> {
lanes: 1,
mem_cost: 19456,
secret: &[],
thread_mode: ThreadMode::default(),
time_cost: 2,
variant: Variant::Argon2id,
version: Version::Version13,
Expand All @@ -104,6 +112,7 @@ impl<'a> Config<'a> {
lanes: 1,
mem_cost: 12288,
secret: &[],
thread_mode: ThreadMode::default(),
time_cost: 3,
variant: Variant::Argon2id,
version: Version::Version13,
Expand All @@ -118,6 +127,7 @@ impl<'a> Config<'a> {
lanes: 1,
mem_cost: 9216,
secret: &[],
thread_mode: ThreadMode::default(),
time_cost: 4,
variant: Variant::Argon2id,
version: Version::Version13,
Expand All @@ -132,6 +142,7 @@ impl<'a> Config<'a> {
lanes: 1,
mem_cost: 7168,
secret: &[],
thread_mode: ThreadMode::default(),
time_cost: 5,
variant: Variant::Argon2id,
version: Version::Version13,
Expand All @@ -146,6 +157,7 @@ impl<'a> Config<'a> {
lanes: 1,
mem_cost: 2097152,
secret: &[],
thread_mode: ThreadMode::default(),
time_cost: 1,
variant: Variant::Argon2id,
version: Version::Version13,
Expand All @@ -160,11 +172,16 @@ impl<'a> Config<'a> {
lanes: 1,
mem_cost: 65536,
secret: &[],
thread_mode: ThreadMode::default(),
time_cost: 3,
variant: Variant::Argon2id,
version: Version::Version13,
}
}

pub fn uses_sequential(&self) -> bool {
self.thread_mode == ThreadMode::Sequential || self.lanes == 1
}
}

impl<'a> Default for Config<'a> {
Expand All @@ -178,6 +195,7 @@ impl<'a> Default for Config<'a> {
mod tests {

use crate::config::Config;
use crate::thread_mode::ThreadMode;
use crate::variant::Variant;
use crate::version::Version;

Expand All @@ -189,6 +207,7 @@ mod tests {
assert_eq!(config.lanes, 1);
assert_eq!(config.mem_cost, 19 * 1024);
assert_eq!(config.secret, &[]);
assert_eq!(config.thread_mode, ThreadMode::Sequential);
assert_eq!(config.time_cost, 2);
assert_eq!(config.variant, Variant::Argon2id);
assert_eq!(config.version, Version::Version13);
Expand All @@ -202,6 +221,7 @@ mod tests {
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);
Expand All @@ -215,6 +235,7 @@ mod tests {
assert_eq!(config.lanes, 1);
assert_eq!(config.mem_cost, 46 * 1024);
assert_eq!(config.secret, &[]);
assert_eq!(config.thread_mode, ThreadMode::Sequential);
assert_eq!(config.time_cost, 1);
assert_eq!(config.variant, Variant::Argon2id);
assert_eq!(config.version, Version::Version13);
Expand All @@ -228,6 +249,7 @@ mod tests {
assert_eq!(config.lanes, 1);
assert_eq!(config.mem_cost, 19 * 1024);
assert_eq!(config.secret, &[]);
assert_eq!(config.thread_mode, ThreadMode::Sequential);
assert_eq!(config.time_cost, 2);
assert_eq!(config.variant, Variant::Argon2id);
assert_eq!(config.version, Version::Version13);
Expand All @@ -241,6 +263,7 @@ mod tests {
assert_eq!(config.lanes, 1);
assert_eq!(config.mem_cost, 12 * 1024);
assert_eq!(config.secret, &[]);
assert_eq!(config.thread_mode, ThreadMode::Sequential);
assert_eq!(config.time_cost, 3);
assert_eq!(config.variant, Variant::Argon2id);
assert_eq!(config.version, Version::Version13);
Expand All @@ -254,6 +277,7 @@ mod tests {
assert_eq!(config.lanes, 1);
assert_eq!(config.mem_cost, 9 * 1024);
assert_eq!(config.secret, &[]);
assert_eq!(config.thread_mode, ThreadMode::Sequential);
assert_eq!(config.time_cost, 4);
assert_eq!(config.variant, Variant::Argon2id);
assert_eq!(config.version, Version::Version13);
Expand All @@ -267,6 +291,7 @@ mod tests {
assert_eq!(config.lanes, 1);
assert_eq!(config.mem_cost, 7 * 1024);
assert_eq!(config.secret, &[]);
assert_eq!(config.thread_mode, ThreadMode::Sequential);
assert_eq!(config.time_cost, 5);
assert_eq!(config.variant, Variant::Argon2id);
assert_eq!(config.version, Version::Version13);
Expand All @@ -280,6 +305,7 @@ mod tests {
assert_eq!(config.lanes, 1);
assert_eq!(config.mem_cost, 2 * 1024 * 1024);
assert_eq!(config.secret, &[]);
assert_eq!(config.thread_mode, ThreadMode::Sequential);
assert_eq!(config.time_cost, 1);
assert_eq!(config.variant, Variant::Argon2id);
assert_eq!(config.version, Version::Version13);
Expand All @@ -293,6 +319,7 @@ mod tests {
assert_eq!(config.lanes, 1);
assert_eq!(config.mem_cost, 64 * 1024);
assert_eq!(config.secret, &[]);
assert_eq!(config.thread_mode, ThreadMode::Sequential);
assert_eq!(config.time_cost, 3);
assert_eq!(config.variant, Variant::Argon2id);
assert_eq!(config.version, Version::Version13);
Expand Down
2 changes: 2 additions & 0 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ 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;

Expand All @@ -129,6 +130,7 @@ mod tests {
lanes: 4,
mem_cost: 4096,
secret: b"secret",
thread_mode: ThreadMode::Sequential,
time_cost: 3,
variant: Variant::Argon2i,
version: Version::Version13,
Expand Down
Loading