sos_core/crypto/
key_derivation.rs

1//! Constants for supported key derivation functions.
2use crate::{
3    crypto::{csprng, DerivedPrivateKey},
4    Error, Result,
5};
6use argon2::{
7    password_hash::{PasswordHash, PasswordHasher, SaltString},
8    Argon2,
9};
10use balloon_hash::Balloon;
11use rand::Rng;
12use secrecy::{ExposeSecret, SecretString};
13use serde::{Deserialize, Serialize};
14use serde_with::{base64::Base64, serde_as};
15use sha2::{Digest, Sha256};
16use std::{convert::AsRef, fmt, str::FromStr};
17
18/// Argon2 key derivation function.
19pub(crate) const ARGON_2_ID: u8 = 1;
20/// Balloon hash key derivation function.
21pub(crate) const BALLOON_HASH: u8 = 2;
22
23/// Type for additional passphrase seed entropy.
24#[serde_as]
25#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq)]
26pub struct Seed(#[serde_as(as = "Base64")] pub [u8; Seed::SIZE]);
27
28impl Seed {
29    /// Number of bytes for seed entropy.
30    pub const SIZE: usize = 32;
31}
32
33impl AsRef<[u8]> for Seed {
34    fn as_ref(&self) -> &[u8] {
35        &self.0
36    }
37}
38
39/// Supported key derivation functions.
40#[derive(Debug, Hash, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)]
41pub enum KeyDerivation {
42    /// Argon2 key derivation function.
43    Argon2Id,
44    /// Balloon hash key derivation function.
45    BalloonHash,
46}
47
48impl KeyDerivation {
49    /// Get the deriver for this key derivation function.
50    pub fn deriver(&self) -> Box<dyn Deriver<Sha256> + Send + 'static> {
51        match self {
52            KeyDerivation::Argon2Id => Box::new(Argon2IdDeriver),
53            KeyDerivation::BalloonHash => Box::new(BalloonHashDeriver),
54        }
55    }
56
57    /// Generate a new salt string.
58    pub fn generate_salt() -> SaltString {
59        SaltString::generate(&mut csprng())
60    }
61
62    /// Parse a saved salt string.
63    pub fn parse_salt<S: AsRef<str>>(salt: S) -> Result<SaltString> {
64        Ok(SaltString::from_b64(salt.as_ref())?)
65    }
66
67    /// Generate new random seed entropy.
68    #[deprecated]
69    pub fn generate_seed() -> Seed {
70        let bytes: [u8; Seed::SIZE] = csprng().gen();
71        Seed(bytes)
72    }
73}
74
75impl Default for KeyDerivation {
76    fn default() -> Self {
77        Self::Argon2Id
78    }
79}
80
81impl fmt::Display for KeyDerivation {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        write!(f, "{}", {
84            match self {
85                Self::Argon2Id => "argon_2_id",
86                Self::BalloonHash => "balloon_hash",
87            }
88        })
89    }
90}
91
92impl FromStr for KeyDerivation {
93    type Err = Error;
94
95    fn from_str(s: &str) -> Result<Self> {
96        match s {
97            "argon_2_id" => Ok(Self::Argon2Id),
98            "balloon_hash" => Ok(Self::BalloonHash),
99            _ => Err(Error::InvalidKeyDerivation(s.to_string())),
100        }
101    }
102}
103
104impl From<&KeyDerivation> for u8 {
105    fn from(value: &KeyDerivation) -> Self {
106        match value {
107            KeyDerivation::Argon2Id => ARGON_2_ID,
108            KeyDerivation::BalloonHash => BALLOON_HASH,
109        }
110    }
111}
112
113impl TryFrom<u8> for KeyDerivation {
114    type Error = Error;
115    fn try_from(value: u8) -> std::result::Result<Self, Self::Error> {
116        Ok(match value {
117            ARGON_2_ID => KeyDerivation::Argon2Id,
118            BALLOON_HASH => KeyDerivation::BalloonHash,
119            _ => return Err(Error::InvalidKeyDerivation(value.to_string())),
120        })
121    }
122}
123
124/// Trait for types that can derive a private key.
125pub trait Deriver<D: Digest> {
126    /// Hash a password using the given salt.
127    fn hash_password<'a>(
128        &self,
129        password: &[u8],
130        salt: &'a SaltString,
131    ) -> Result<PasswordHash<'a>>;
132
133    /// Derive a private secret key from a passphrase, salt and
134    /// optional seed entropy.
135    fn derive(
136        &self,
137        password: &SecretString,
138        salt: &SaltString,
139        seed: Option<&Seed>,
140    ) -> Result<DerivedPrivateKey> {
141        let buffer = if let Some(seed) = seed {
142            let mut buffer = password.expose_secret().as_bytes().to_vec();
143            buffer.extend_from_slice(seed.as_ref());
144            buffer
145        } else {
146            password.expose_secret().as_bytes().to_vec()
147        };
148
149        let password_hash = self.hash_password(buffer.as_slice(), salt)?;
150        let password_hash_string = password_hash.serialize();
151        let hash = D::digest(password_hash_string.as_bytes());
152        Ok(DerivedPrivateKey::new(secrecy::SecretBox::new(
153            hash.as_slice().to_vec().into(),
154        )))
155    }
156}
157
158/// Derive a private key using the Argon2
159/// key derivation function.
160pub struct Argon2IdDeriver;
161
162impl Deriver<Sha256> for Argon2IdDeriver {
163    fn hash_password<'a>(
164        &self,
165        password: &[u8],
166        salt: &'a SaltString,
167    ) -> Result<PasswordHash<'a>> {
168        let argon2 = Argon2::default();
169        Ok(argon2.hash_password(password, salt)?)
170    }
171}
172
173/// Derive a private key using the Balloon Hash
174/// key derivation function.
175pub struct BalloonHashDeriver;
176
177impl Deriver<Sha256> for BalloonHashDeriver {
178    fn hash_password<'a>(
179        &self,
180        password: &[u8],
181        salt: &'a SaltString,
182    ) -> Result<PasswordHash<'a>> {
183        let balloon = Balloon::<Sha256>::default();
184        Ok(balloon.hash_password(password, salt)?)
185    }
186}