Skip to content

Commit

Permalink
argon2: add alloc feature (#215)
Browse files Browse the repository at this point in the history
Makes linking against liballoc optional, and adds an API which allows
users to stack allocate their own memory blocks array instead of using a
dynamically allocated `vec`.
  • Loading branch information
tarcieri authored Aug 27, 2021
1 parent 2a06f27 commit 4f1f645
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 40 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/argon2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
# target: ${{ matrix.target }}
# override: true
# - run: cargo build --target ${{ matrix.target }} --release --no-default-features
# - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features password-hash
# - run: cargo build --target ${{ matrix.target }} --release
# - run: cargo build --target ${{ matrix.target }} --release --features zeroize

Expand All @@ -55,5 +56,6 @@ jobs:
toolchain: ${{ matrix.rust }}
override: true
- run: cargo test --release --no-default-features
- run: cargo test --release --no-default-features --features password-hash
- run: cargo test --release
- run: cargo test --release --all-features
5 changes: 3 additions & 2 deletions argon2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ hex-literal = "0.3"
password-hash = { version = "=0.3.0-pre.1", features = ["rand_core"] }

[features]
default = ["password-hash", "rand"]
default = ["alloc", "password-hash", "rand"]
alloc = []
parallel = ["rayon", "std"]
rand = ["password-hash/rand_core"]
std = ["password-hash/std"]
std = ["alloc", "password-hash/std"]

[package.metadata.docs.rs]
all-features = true
Expand Down
10 changes: 5 additions & 5 deletions argon2/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use zeroize::Zeroize;

/// Structure for the (1KB) memory block implemented as 128 64-bit words.
#[derive(Copy, Clone, Debug)]
pub(crate) struct Block([u64; Self::SIZE / 8]);
pub struct Block([u64; Self::SIZE / 8]);

impl Default for Block {
fn default() -> Self {
Expand All @@ -25,7 +25,7 @@ impl Block {
pub const SIZE: usize = 1024;

/// Load a block from a block-sized byte slice
pub fn load(&mut self, input: &[u8]) {
pub(crate) fn load(&mut self, input: &[u8]) {
debug_assert_eq!(input.len(), Block::SIZE);

for (i, chunk) in input.chunks(8).enumerate() {
Expand All @@ -34,18 +34,18 @@ impl Block {
}

/// Iterate over the `u64` values contained in this block
pub fn iter(&self) -> slice::Iter<'_, u64> {
pub(crate) fn iter(&self) -> slice::Iter<'_, u64> {
self.0.iter()
}

/// Iterate mutably over the `u64` values contained in this block
pub fn iter_mut(&mut self) -> slice::IterMut<'_, u64> {
pub(crate) fn iter_mut(&mut self) -> slice::IterMut<'_, u64> {
self.0.iter_mut()
}

/// Function fills a new memory block and optionally XORs the old block over the new one.
// TODO(tarcieri): optimized implementation (i.e. from opt.c instead of ref.c)
pub fn fill_block(&mut self, prev_block: Block, ref_block: Block, with_xor: bool) {
pub(crate) fn fill_block(&mut self, prev_block: Block, ref_block: Block, with_xor: bool) {
let mut block_r = ref_block ^ prev_block;
let mut block_tmp = block_r;

Expand Down
2 changes: 1 addition & 1 deletion argon2/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl<'a> Instance<'a> {
memory,
passes: context.params.t_cost(),
lane_length,
lanes: context.lanes(),
lanes: context.params.lanes(),
threads: context.params.t_cost(),
alg,
};
Expand Down
58 changes: 40 additions & 18 deletions argon2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
//! - **Argon2i**: optimized to resist side-channel attacks
//! - **Argon2id**: (default) hybrid version combining both Argon2i and Argon2d
//!
//! Support is provided for embedded (i.e. `no_std`) environments, including
//! ones without `alloc` support.
//!
//! # Usage (simple with default params)
//!
//! Note: this example requires the `rand_core` crate with the `std` feature
Expand Down Expand Up @@ -72,6 +75,7 @@
)]
#![warn(rust_2018_idioms, missing_docs)]

#[cfg(feature = "alloc")]
#[macro_use]
extern crate alloc;

Expand All @@ -88,6 +92,7 @@ mod version;

pub use crate::{
algorithm::Algorithm,
block::Block,
error::{Error, Result},
params::{Params, ParamsBuilder},
version::Version,
Expand All @@ -101,13 +106,12 @@ pub use {
};

use crate::{
block::Block,
instance::Instance,
memory::{Memory, SYNC_POINTS},
};
use blake2::{digest, Blake2b, Digest};

#[cfg(feature = "password-hash")]
#[cfg(all(feature = "alloc", feature = "password-hash"))]
use {
core::convert::{TryFrom, TryInto},
password_hash::{Decimal, Ident, Salt},
Expand Down Expand Up @@ -208,12 +212,35 @@ impl<'key> Argon2<'key> {
}

/// Hash a password and associated parameters into the provided output buffer.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn hash_password_into(
&self,
pwd: &[u8],
salt: &[u8],
ad: &[u8],
out: &mut [u8],
) -> Result<()> {
let mut blocks = vec![Block::default(); self.params.block_count()];
self.hash_password_into_with_memory(pwd, salt, ad, out, &mut blocks)
}

/// Hash a password and associated parameters into the provided output buffer.
///
/// This method takes an explicit `memory_blocks` parameter which allows
/// the caller to provide the backing storage for the algorithm's state:
///
/// - Users with the `alloc` feature enabled can use [`Argon2::hash_password_into`]
/// to have it allocated for them.
/// - `no_std` users on "heapless" targets can use an array of the [`Block`] type
/// to stack allocate this buffer.
pub fn hash_password_into_with_memory(
&self,
pwd: &[u8],
salt: &[u8],
ad: &[u8],
out: &mut [u8],
mut memory_blocks: impl AsMut<[Block]>,
) -> Result<()> {
// Validate output length
if out.len()
Expand Down Expand Up @@ -255,15 +282,14 @@ impl<'key> Argon2<'key> {
// Hashing all inputs
let initial_hash = self.initial_hash(pwd, salt, ad, out);

let segment_length =
Memory::segment_length_for_params(self.params.m_cost(), self.params.p_cost());

let blocks_count = (segment_length * self.params.p_cost() * SYNC_POINTS) as usize;
let segment_length = self.params.segment_length();
let block_count = self.params.block_count();
let memory_blocks = memory_blocks
.as_mut()
.get_mut(..block_count)
.ok_or(Error::MemoryTooLittle)?;

// TODO(tarcieri): support for stack-allocated memory blocks (i.e. no alloc)
let mut blocks = vec![Block::default(); blocks_count];

let memory = Memory::new(&mut blocks, segment_length);
let memory = Memory::new(memory_blocks, segment_length);
Instance::hash(self, self.algorithm, initial_hash, memory, out)
}

Expand All @@ -272,11 +298,6 @@ impl<'key> Argon2<'key> {
&self.params
}

/// Get the number of lanes.
pub(crate) fn lanes(&self) -> u32 {
self.params.p_cost()
}

/// Hashes all the inputs into `blockhash[PREHASH_DIGEST_LENGTH]`.
pub(crate) fn initial_hash(
&self,
Expand All @@ -286,7 +307,7 @@ impl<'key> Argon2<'key> {
out: &[u8],
) -> digest::Output<Blake2b> {
let mut digest = Blake2b::new();
digest.update(&self.lanes().to_le_bytes());
digest.update(&self.params.lanes().to_le_bytes());
digest.update(&(out.len() as u32).to_le_bytes());
digest.update(&self.params.m_cost().to_le_bytes());
digest.update(&self.params.t_cost().to_le_bytes());
Expand All @@ -310,7 +331,8 @@ impl<'key> Argon2<'key> {
}
}

#[cfg(feature = "password-hash")]
#[cfg(all(feature = "alloc", feature = "password-hash"))]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
impl PasswordHasher for Argon2<'_> {
type Params = Params;
Expand Down Expand Up @@ -389,7 +411,7 @@ impl<'key> From<&Params> for Argon2<'key> {
}
}

#[cfg(all(test, feature = "password-hash"))]
#[cfg(all(test, feature = "alloc", feature = "password-hash"))]
mod tests {
use crate::{Algorithm, Argon2, Params, PasswordHasher, Salt, Version};

Expand Down
13 changes: 0 additions & 13 deletions argon2/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,6 @@ pub(crate) struct Memory<'a> {
}

impl<'a> Memory<'a> {
/// Align memory size.
///
/// Minimum memory_blocks = 8*`L` blocks, where `L` is the number of lanes.
pub(crate) fn segment_length_for_params(m_cost: u32, lanes: u32) -> u32 {
let memory_blocks = if m_cost < 2 * SYNC_POINTS * lanes {
2 * SYNC_POINTS * lanes
} else {
m_cost
};

memory_blocks / (lanes * SYNC_POINTS)
}

/// Instantiate a new memory struct
pub(crate) fn new(data: &'a mut [Block], segment_length: u32) -> Self {
Self {
Expand Down
23 changes: 23 additions & 0 deletions argon2/src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,29 @@ impl Params {
pub fn output_len(self) -> Option<usize> {
self.output_len
}

/// Get the number of lanes.
pub(crate) fn lanes(self) -> u32 {
self.p_cost
}

/// Get the segment length given the configured `m_cost` and `p_cost`.
///
/// Minimum memory_blocks = 8*`L` blocks, where `L` is the number of lanes.
pub(crate) fn segment_length(self) -> u32 {
let memory_blocks = if self.m_cost < 2 * SYNC_POINTS * self.lanes() {
2 * SYNC_POINTS * self.lanes()
} else {
self.m_cost
};

memory_blocks / (self.lanes() * SYNC_POINTS)
}

/// Get the number of blocks required given the configured `m_cost` and `p_cost`.
pub(crate) fn block_count(self) -> usize {
(self.segment_length() * self.p_cost * SYNC_POINTS) as usize
}
}

impl Default for Params {
Expand Down
2 changes: 2 additions & 0 deletions argon2/tests/kat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//! `draft-irtf-cfrg-argon2-12` Section 5:
//! <https://datatracker.ietf.org/doc/draft-irtf-cfrg-argon2/>

#![cfg(feature = "alloc")]

// TODO(tarcieri): test full set of vectors from the reference implementation:
// https://github.com/P-H-C/phc-winner-argon2/blob/master/src/test.c

Expand Down
2 changes: 1 addition & 1 deletion argon2/tests/phc_strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! Adapted from: <https://github.com/P-H-C/phc-winner-argon2/blob/master/src/test.c>

#![cfg(feature = "password-hash")]
#![cfg(all(feature = "alloc", feature = "password-hash"))]

use argon2::{Argon2, PasswordHash, PasswordVerifier};

Expand Down

0 comments on commit 4f1f645

Please sign in to comment.