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