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: Add const compatibility #438

Merged
merged 23 commits into from
Aug 7, 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
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 @@ -169,7 +169,7 @@ pub struct Argon2<'key> {

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

Expand All @@ -184,8 +184,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 @@ -443,7 +446,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 @@ -503,7 +506,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