balloon_hash/
params.rs

1//! Balloon password hash parameters.
2
3use crate::{Error, Result};
4use core::num::NonZeroU32;
5
6#[cfg(feature = "password-hash")]
7use password_hash::{errors::InvalidValue, ParamsString, PasswordHash};
8
9/// Balloon password hash parameters.
10///
11/// These are parameters which can be encoded into a PHC hash string.
12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13pub struct Params {
14    /// Space cost, expressed in number of blocks.
15    pub s_cost: NonZeroU32,
16    /// Time cost, expressed in number of rounds.
17    pub t_cost: NonZeroU32,
18    /// Degree of parallelism, expressed in number of threads.
19    /// Only allowed to be higher than 1 when used in combination
20    /// with [`Algorithm::BalloonM`](crate::Algorithm::BalloonM).
21    pub p_cost: NonZeroU32,
22}
23
24impl Params {
25    /// Default memory cost.
26    pub const DEFAULT_S_COST: u32 = 1024;
27
28    /// Default number of iterations (i.e. "time").
29    pub const DEFAULT_T_COST: u32 = 3;
30
31    /// Default degree of parallelism.
32    pub const DEFAULT_P_COST: u32 = 1;
33
34    /// Create new parameters.
35    pub fn new(s_cost: u32, t_cost: u32, p_cost: u32) -> Result<Self> {
36        Ok(Self {
37            s_cost: NonZeroU32::new(s_cost).ok_or(Error::MemoryTooLittle)?,
38            t_cost: NonZeroU32::new(t_cost).ok_or(Error::TimeTooSmall)?,
39            p_cost: NonZeroU32::new(p_cost).ok_or(Error::ThreadsTooFew)?,
40        })
41    }
42}
43
44impl Default for Params {
45    fn default() -> Self {
46        Self::new(
47            Self::DEFAULT_S_COST,
48            Self::DEFAULT_T_COST,
49            Self::DEFAULT_P_COST,
50        )
51        .unwrap()
52    }
53}
54
55#[cfg(feature = "password-hash")]
56#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
57impl<'a> TryFrom<&'a PasswordHash<'a>> for Params {
58    type Error = password_hash::Error;
59
60    fn try_from(hash: &'a PasswordHash<'a>) -> password_hash::Result<Self> {
61        let mut params = Self::default();
62
63        for (ident, value) in hash.params.iter() {
64            match ident.as_str() {
65                "s" => {
66                    params.s_cost = NonZeroU32::new(value.decimal()?)
67                        .ok_or_else(|| InvalidValue::TooShort.param_error())?;
68                }
69                "t" => {
70                    params.t_cost = NonZeroU32::new(value.decimal()?)
71                        .ok_or_else(|| InvalidValue::TooShort.param_error())?;
72                }
73                "p" => {
74                    params.p_cost = NonZeroU32::new(value.decimal()?)
75                        .ok_or_else(|| InvalidValue::TooShort.param_error())?;
76                }
77                _ => return Err(password_hash::Error::ParamNameInvalid),
78            }
79        }
80
81        Ok(params)
82    }
83}
84
85#[cfg(feature = "password-hash")]
86#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
87impl TryFrom<Params> for ParamsString {
88    type Error = password_hash::Error;
89
90    fn try_from(params: Params) -> password_hash::Result<ParamsString> {
91        ParamsString::try_from(&params)
92    }
93}
94
95#[cfg(feature = "password-hash")]
96#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
97impl TryFrom<&Params> for ParamsString {
98    type Error = password_hash::Error;
99
100    fn try_from(params: &Params) -> password_hash::Result<ParamsString> {
101        let mut output = ParamsString::new();
102        output.add_decimal("s", params.s_cost.get())?;
103        output.add_decimal("t", params.t_cost.get())?;
104        output.add_decimal("p", params.p_cost.get())?;
105
106        Ok(output)
107    }
108}