From d25ea610d90c2cfe5cb2294bde96c8892501daa2 Mon Sep 17 00:00:00 2001 From: C0D3 M4513R <28912031+C0D3-M4513R@users.noreply.github.com> Date: Sat, 26 Aug 2023 16:13:45 +0200 Subject: [PATCH] argon2: more const-ness (#450) --- argon2/src/algorithm.rs | 6 +- argon2/src/lib.rs | 4 +- argon2/src/params.rs | 144 ++++++++++++++++++++++------------------ argon2/src/version.rs | 11 +-- 4 files changed, 87 insertions(+), 78 deletions(-) diff --git a/argon2/src/algorithm.rs b/argon2/src/algorithm.rs index e7260d6e..e330e6dd 100644 --- a/argon2/src/algorithm.rs +++ b/argon2/src/algorithm.rs @@ -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", @@ -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, @@ -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() } } diff --git a/argon2/src/lib.rs b/argon2/src/lib.rs index 6a6f04f0..be373c86 100644 --- a/argon2/src/lib.rs +++ b/argon2/src/lib.rs @@ -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 } @@ -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); } diff --git a/argon2/src/params.rs b/argon2/src/params.rs index 81464d5d..9e153600 100644 --- a/argon2/src/params.rs +++ b/argon2/src/params.rs @@ -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) -> Result { - let mut builder = ParamsBuilder::new(); + pub const fn new( + m_cost: u32, + t_cost: u32, + p_cost: u32, + output_len: Option, + ) -> Result { + 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 } @@ -164,25 +202,25 @@ impl Params { } /// Length of the output (in bytes). - pub fn output_len(&self) -> Option { + pub const fn output_len(&self) -> Option { 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() { @@ -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 } } @@ -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"] @@ -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 } } @@ -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. @@ -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 { - 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 { + 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) @@ -483,11 +493,9 @@ impl ParamsBuilder { pub fn context(&self, algorithm: Algorithm, version: Version) -> Result> { 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, @@ -496,6 +504,12 @@ impl Default for ParamsBuilder { data: None, output_len: params.output_len, } + }; +} + +impl Default for ParamsBuilder { + fn default() -> Self { + Self::DEFAULT } } diff --git a/argon2/src/version.rs b/argon2/src/version.rs index a8b0ef5a..2ae8574c 100644 --- a/argon2/src/version.rs +++ b/argon2/src/version.rs @@ -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) @@ -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 for u32 { fn from(version: Version) -> u32 { version as u32