Skip to content

Commit

Permalink
argon2: improve const compatibility (#438)
Browse files Browse the repository at this point in the history
  • Loading branch information
C0D3-M4513R committed Aug 7, 2023
1 parent fe0d512 commit 5e3f574
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 43 deletions.
10 changes: 6 additions & 4 deletions argon2/src/algorithm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,20 @@ pub enum Algorithm {

impl Default for Algorithm {
fn default() -> Algorithm {
Algorithm::Argon2id
Algorithm::DEFAULT
}
}

impl Algorithm {
/// Default Algorithm (recommended).
pub const DEFAULT: Algorithm = Algorithm::Argon2id;
/// Parse an [`Algorithm`] from the provided string.
pub fn new(id: impl AsRef<str>) -> Result<Self> {
id.as_ref().parse()
}

/// 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 @@ -74,7 +76,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 @@ -83,7 +85,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
11 changes: 7 additions & 4 deletions argon2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ pub struct Argon2<'key> {

impl Default for Argon2<'_> {
fn default() -> Self {
Self::new(Algorithm::default(), Version::default(), Params::default())
Self::DEFAULT
}
}

Expand All @@ -190,8 +190,11 @@ impl fmt::Debug for Argon2<'_> {
}

impl<'key> Argon2<'key> {
/// Default parameters (recommended).
pub const DEFAULT: Argon2<'static> =
Argon2::new(Algorithm::DEFAULT, Version::DEFAULT, Params::DEFAULT);
/// Create a new Argon2 context.
pub fn new(algorithm: Algorithm, version: Version, params: Params) -> Self {
pub const fn new(algorithm: Algorithm, version: Version, params: Params) -> Self {
Self {
algorithm,
version,
Expand Down Expand Up @@ -470,7 +473,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 +534,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
104 changes: 71 additions & 33 deletions argon2/src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,36 +104,33 @@ 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();

builder.m_cost(m_cost).t_cost(t_cost).p_cost(p_cost);

if let Some(len) = output_len {
builder.output_len(len);
}

builder.build()
pub const fn new(
m_cost: u32,
t_cost: u32,
p_cost: u32,
output_len: Option<usize>,
) -> Result<Self> {
ParamsBuilder::new_params(m_cost, t_cost, p_cost, None, None, output_len).build()
}

/// 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 +161,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 +192,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 All @@ -217,7 +214,6 @@ macro_rules! param_buf {
/// Length of byte array
len: usize,
}

impl $ty {
/// Maximum length in bytes
pub const MAX_LEN: usize = $max_len;
Expand All @@ -233,6 +229,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 +251,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 +386,27 @@ 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
}
/// Create a new builder with the provided parameters.
/// This function exists to allow for const construction of ParamsBuilder with custom parameters.
pub const fn new_params(
m_cost: u32,
t_cost: u32,
p_cost: u32,
keyid: Option<KeyId>,
data: Option<AssociatedData>,
output_len: Option<usize>,
) -> Self {
Self {
m_cost,
t_cost,
p_cost,
keyid,
data,
output_len,
}
}

/// Set memory size, expressed in kibibytes, between 1 and (2^32)-1.
Expand Down Expand Up @@ -428,7 +449,7 @@ 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> {
pub const fn build(&self) -> Result<Params> {
if self.m_cost < Params::MIN_M_COST {
return Err(Error::MemoryTooLittle);
}
Expand Down Expand Up @@ -465,9 +486,15 @@ impl ParamsBuilder {
}
}

let keyid = self.keyid.unwrap_or_default();
let keyid = match self.keyid {
Some(keyid) => keyid,
None => KeyId::EMPTY,
};

let data = self.data.unwrap_or_default();
let data = match self.data {
Some(data) => data,
None => AssociatedData::EMPTY,
};

let params = Params {
m_cost: self.m_cost,
Expand All @@ -482,14 +509,19 @@ impl ParamsBuilder {
}

/// Create a new [`Argon2`] context using the provided algorithm/version.
pub fn context(&self, algorithm: Algorithm, version: Version) -> Result<Argon2<'_>> {
Ok(Argon2::new(algorithm, version, self.build()?))
pub const fn context(&self, algorithm: Algorithm, version: Version) -> Result<Argon2<'_>> {
Ok(Argon2::new(
algorithm,
version,
match self.build() {
Ok(params) => params,
Err(e) => return Err(e),
},
))
}
}

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 @@ -498,6 +530,12 @@ impl Default for ParamsBuilder {
data: None,
output_len: params.output_len,
}
};
}

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

Expand Down
6 changes: 4 additions & 2 deletions argon2/src/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@ pub enum Version {
}

impl Version {
/// Default Version (recommended).
pub const DEFAULT: Version = Version::V0x13;
/// 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
Self::DEFAULT
}
}

Expand Down

0 comments on commit 5e3f574

Please sign in to comment.