Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

argon2: more const-ness #450

Merged
merged 6 commits into from
Aug 26, 2023
Merged
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
6 changes: 3 additions & 3 deletions argon2/src/algorithm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl Algorithm {
}

/// Get the identifier string for this PBKDF2 [`Algorithm`].
pub fn as_str(&self) -> &'static str {
pub const fn as_str(&self) -> &'static str {
match self {
Algorithm::Argon2d => "argon2d",
Algorithm::Argon2i => "argon2i",
Expand All @@ -69,7 +69,7 @@ impl Algorithm {
/// Get the [`Ident`] that corresponds to this Argon2 [`Algorithm`].
#[cfg(feature = "password-hash")]
#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
pub fn ident(&self) -> Ident<'static> {
pub const fn ident(&self) -> Ident<'static> {
match self {
Algorithm::Argon2d => ARGON2D_IDENT,
Algorithm::Argon2i => ARGON2I_IDENT,
Expand All @@ -78,7 +78,7 @@ impl Algorithm {
}

/// Serialize primitive type as little endian bytes
pub(crate) fn to_le_bytes(self) -> [u8; 4] {
pub(crate) const fn to_le_bytes(self) -> [u8; 4] {
(self as u32).to_le_bytes()
}
}
Expand Down
4 changes: 2 additions & 2 deletions argon2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ impl<'key> Argon2<'key> {
}

/// Get default configured [`Params`].
pub fn params(&self) -> &Params {
pub const fn params(&self) -> &Params {
&self.params
}

Expand Down Expand Up @@ -531,7 +531,7 @@ impl<'key> Argon2<'key> {
digest.finalize()
}

fn verify_inputs(pwd: &[u8], salt: &[u8]) -> Result<()> {
const fn verify_inputs(pwd: &[u8], salt: &[u8]) -> Result<()> {
if pwd.len() > MAX_PWD_LEN {
return Err(Error::PwdTooLong);
}
Expand Down
144 changes: 79 additions & 65 deletions argon2/src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,36 +104,74 @@ impl Params {
/// - `t_cost`: number of iterations. Between 1 and (2^32)-1.
/// - `p_cost`: degree of parallelism. Between 1 and 255.
/// - `output_len`: size of the KDF output in bytes. Default 32.
pub fn new(m_cost: u32, t_cost: u32, p_cost: u32, output_len: Option<usize>) -> Result<Self> {
let mut builder = ParamsBuilder::new();
pub const fn new(
m_cost: u32,
t_cost: u32,
p_cost: u32,
output_len: Option<usize>,
) -> Result<Self> {
if m_cost < Params::MIN_M_COST {
return Err(Error::MemoryTooLittle);
}

builder.m_cost(m_cost).t_cost(t_cost).p_cost(p_cost);
// Note: we don't need to check `MAX_M_COST`, since it's `u32::MAX`

if m_cost < p_cost * 8 {
return Err(Error::MemoryTooLittle);
}

if t_cost < Params::MIN_T_COST {
return Err(Error::TimeTooSmall);
}

// Note: we don't need to check `MAX_T_COST`, since it's `u32::MAX`

if p_cost < Params::MIN_P_COST {
return Err(Error::ThreadsTooFew);
}

if p_cost > Params::MAX_P_COST {
return Err(Error::ThreadsTooMany);
}

if let Some(len) = output_len {
builder.output_len(len);
if len < Params::MIN_OUTPUT_LEN {
return Err(Error::OutputTooShort);
}

if len > Params::MAX_OUTPUT_LEN {
return Err(Error::OutputTooLong);
}
}

builder.build()
Ok(Params {
m_cost,
t_cost,
p_cost,
keyid: KeyId::EMPTY,
data: AssociatedData::EMPTY,
output_len,
})
}

/// Memory size, expressed in kibibytes. Between 1 and (2^32)-1.
///
/// Value is an integer in decimal (1 to 10 digits).
pub fn m_cost(&self) -> u32 {
pub const fn m_cost(&self) -> u32 {
self.m_cost
}

/// Number of iterations. Between 1 and (2^32)-1.
///
/// Value is an integer in decimal (1 to 10 digits).
pub fn t_cost(&self) -> u32 {
pub const fn t_cost(&self) -> u32 {
self.t_cost
}

/// Degree of parallelism. Between 1 and 255.
///
/// Value is an integer in decimal (1 to 3 digits).
pub fn p_cost(&self) -> u32 {
pub const fn p_cost(&self) -> u32 {
self.p_cost
}

Expand Down Expand Up @@ -164,25 +202,25 @@ impl Params {
}

/// Length of the output (in bytes).
pub fn output_len(&self) -> Option<usize> {
pub const fn output_len(&self) -> Option<usize> {
self.output_len
}

/// Get the number of lanes.
#[allow(clippy::cast_possible_truncation)]
pub(crate) fn lanes(&self) -> usize {
pub(crate) const fn lanes(&self) -> usize {
self.p_cost as usize
}

/// Get the number of blocks in a lane.
pub(crate) fn lane_length(&self) -> usize {
pub(crate) const fn lane_length(&self) -> usize {
self.segment_length() * SYNC_POINTS
}

/// 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) -> usize {
pub(crate) const fn segment_length(&self) -> usize {
let m_cost = self.m_cost as usize;

let memory_blocks = if m_cost < 2 * SYNC_POINTS * self.lanes() {
Expand All @@ -195,7 +233,7 @@ impl Params {
}

/// Get the number of blocks required given the configured `m_cost` and `p_cost`.
pub fn block_count(&self) -> usize {
pub const fn block_count(&self) -> usize {
self.segment_length() * self.lanes() * SYNC_POINTS
}
}
Expand Down Expand Up @@ -233,6 +271,12 @@ macro_rules! param_buf {
Ok(Self { bytes, len })
}

/// Empty value.
pub const EMPTY: Self = Self {
bytes: [0u8; Self::MAX_LEN],
len: 0,
};

#[doc = "Decode"]
#[doc = $name]
#[doc = " from a B64 string"]
Expand All @@ -249,12 +293,12 @@ macro_rules! param_buf {
}

/// Get the length in bytes.
pub fn len(&self) -> usize {
pub const fn len(&self) -> usize {
self.len
}

/// Is this value empty?
pub fn is_empty(&self) -> bool {
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
}
Expand Down Expand Up @@ -384,8 +428,8 @@ pub struct ParamsBuilder {

impl ParamsBuilder {
/// Create a new builder with the default parameters.
pub fn new() -> Self {
Self::default()
pub const fn new() -> Self {
Self::DEFAULT
}

/// Set memory size, expressed in kibibytes, between 1 and (2^32)-1.
Expand Down Expand Up @@ -428,52 +472,18 @@ impl ParamsBuilder {
///
/// This performs validations to ensure that the given parameters are valid
/// and compatible with each other, and will return an error if they are not.
pub fn build(&self) -> Result<Params> {
if self.m_cost < Params::MIN_M_COST {
return Err(Error::MemoryTooLittle);
}

// Note: we don't need to check `MAX_M_COST`, since it's `u32::MAX`

if self.m_cost < self.p_cost * 8 {
return Err(Error::MemoryTooLittle);
}

if self.t_cost < Params::MIN_T_COST {
return Err(Error::TimeTooSmall);
}

// Note: we don't need to check `MAX_T_COST`, since it's `u32::MAX`

if self.p_cost < Params::MIN_P_COST {
return Err(Error::ThreadsTooFew);
}

if self.p_cost > Params::MAX_P_COST {
return Err(Error::ThreadsTooMany);
}

if let Some(len) = self.output_len {
if len < Params::MIN_OUTPUT_LEN {
return Err(Error::OutputTooShort);
}
pub const fn build(&self) -> Result<Params> {
let mut params = match Params::new(self.m_cost, self.t_cost, self.p_cost, self.output_len) {
Ok(params) => params,
Err(err) => return Err(err),
};

if len > Params::MAX_OUTPUT_LEN {
return Err(Error::OutputTooLong);
}
if let Some(keyid) = self.keyid {
params.keyid = keyid;
}

let keyid = self.keyid.unwrap_or_default();

let data = self.data.unwrap_or_default();

let params = Params {
m_cost: self.m_cost,
t_cost: self.t_cost,
p_cost: self.p_cost,
keyid,
data,
output_len: self.output_len,
if let Some(data) = self.data {
params.data = data;
};

Ok(params)
Expand All @@ -483,11 +493,9 @@ impl ParamsBuilder {
pub fn context(&self, algorithm: Algorithm, version: Version) -> Result<Argon2<'_>> {
Ok(Argon2::new(algorithm, version, self.build()?))
}
}

impl Default for ParamsBuilder {
fn default() -> Self {
let params = Params::default();
/// Default parameters (recommended).
pub const DEFAULT: ParamsBuilder = {
let params = Params::DEFAULT;
Self {
m_cost: params.m_cost,
t_cost: params.t_cost,
Expand All @@ -496,6 +504,12 @@ impl Default for ParamsBuilder {
data: None,
output_len: params.output_len,
}
};
}

impl Default for ParamsBuilder {
fn default() -> Self {
Self::DEFAULT
}
}

Expand Down
11 changes: 3 additions & 8 deletions argon2/src/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use crate::{Error, Result};

/// Version of the algorithm.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum Version {
/// Version 16 (0x10 in hex)
Expand All @@ -14,22 +14,17 @@ pub enum Version {
/// Version 19 (0x13 in hex, default)
///
/// Performs XOR internally
#[default]
V0x13 = 0x13,
}

impl Version {
/// Serialize version as little endian bytes
pub(crate) fn to_le_bytes(self) -> [u8; 4] {
pub(crate) const fn to_le_bytes(self) -> [u8; 4] {
(self as u32).to_le_bytes()
}
}

impl Default for Version {
fn default() -> Self {
Self::V0x13
}
}

impl From<Version> for u32 {
fn from(version: Version) -> u32 {
version as u32
Expand Down