sos_core/crypto/
key_derivation.rs1use 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
17pub(crate) const ARGON_2_ID: u8 = 1;
19pub(crate) const BALLOON_HASH: u8 = 2;
21
22#[doc(hidden)]
24pub const SEED_SIZE: usize = 32;
25pub type Seed = [u8; SEED_SIZE];
27
28#[derive(Debug, Hash, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)]
30pub enum KeyDerivation {
31 Argon2Id,
33 BalloonHash,
35}
36
37impl KeyDerivation {
38 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 pub fn generate_salt() -> SaltString {
48 SaltString::generate(&mut csprng())
49 }
50
51 pub fn parse_salt<S: AsRef<str>>(salt: S) -> Result<SaltString> {
53 Ok(SaltString::from_b64(salt.as_ref())?)
54 }
55
56 #[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
112pub trait Deriver<D: Digest> {
114 fn hash_password<'a>(
116 &self,
117 password: &[u8],
118 salt: &'a SaltString,
119 ) -> Result<PasswordHash<'a>>;
120
121 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
146pub 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
161pub 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}