secured_cipher_key/
lib.rs1use hmac::Hmac;
2use pbkdf2::pbkdf2;
3use rand_core::{OsRng, RngCore};
4use sha2::Sha256;
5
6pub struct Key<const P: usize, const S: usize> {
10 pub pubk: [u8; P],
12
13 pub salt: [u8; S],
15
16 pub strategy: KeyDerivationStrategy,
18}
19
20impl<const P: usize, const S: usize> Key<P, S> {
21 pub fn new(password: &[u8], strategy: KeyDerivationStrategy) -> Self {
34 let salt = random_bytes::<S>();
36
37 let mut pubk = [0; P];
39 match strategy {
40 KeyDerivationStrategy::PBKDF2(rounds) => {
41 if pbkdf2::<Hmac<Sha256>>(password, &salt, rounds as u32, &mut pubk).is_err() {
42 panic!("Key derivation failed")
43 }
44 }
45 }
46
47 Self {
48 pubk,
49 salt,
50 strategy,
51 }
52 }
53
54 pub fn with_salt(password: &[u8], salt: [u8; S], strategy: KeyDerivationStrategy) -> Self {
67 let mut pubk = [0; P];
69
70 match strategy {
71 KeyDerivationStrategy::PBKDF2(rounds) => {
72 if pbkdf2::<Hmac<Sha256>>(password, &salt, rounds as u32, &mut pubk).is_err() {
73 panic!("Key derivation failed")
74 }
75 }
76 }
77
78 Self {
79 pubk,
80 salt,
81 strategy,
82 }
83 }
84}
85
86pub fn random_bytes<const S: usize>() -> [u8; S] {
88 let mut bytes = [0; S];
89 OsRng.fill_bytes(&mut bytes);
90 bytes
91}
92
93#[derive(Clone, Debug)]
94pub enum KeyDerivationStrategy {
95 PBKDF2(usize),
96}
97
98impl Default for KeyDerivationStrategy {
99 fn default() -> Self {
100 KeyDerivationStrategy::PBKDF2(900_000)
101 }
102}
103
104impl TryFrom<Vec<u8>> for KeyDerivationStrategy {
105 type Error = String;
106
107 fn try_from(bytes: Vec<u8>) -> Result<Self, String> {
108 match bytes[0] {
109 0 => {
110 let rounds_bytes = &bytes[1..];
111 let rounds = usize::from_be_bytes(rounds_bytes.try_into().or(Err("Invalid rounds bytes"))?);
112 Ok(KeyDerivationStrategy::PBKDF2(rounds))
113 }
114 _ => Err("Invalid key derivation strategy".to_string()),
115 }
116 }
117}
118
119impl From<KeyDerivationStrategy> for Vec<u8> {
120 fn from(strategy: KeyDerivationStrategy) -> Self {
121 match strategy {
122 KeyDerivationStrategy::PBKDF2(rounds) => [vec![0u8], rounds.to_be_bytes().to_vec()].concat(),
123 }
124 }
125}
126
127impl PartialEq for KeyDerivationStrategy {
128 fn eq(&self, other: &Self) -> bool {
129 match (self, other) {
130 (KeyDerivationStrategy::PBKDF2(rounds), KeyDerivationStrategy::PBKDF2(rounds2)) => {
131 rounds == rounds2
132 }
133 }
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn test_key_derivation() {
143 let password = "password".as_bytes();
144
145 let key = Key::<32, 32>::new(password, KeyDerivationStrategy::PBKDF2(10_000));
146 let key2 = Key::<32, 32>::with_salt(password, key.salt, KeyDerivationStrategy::PBKDF2(10_000));
147
148 assert_eq!(key.pubk, key2.pubk);
149 }
150
151 #[test]
152 fn test_key_derivation_with_different_salt() {
153 let password = "password".as_bytes();
154
155 let key = Key::<32, 32>::new(password, KeyDerivationStrategy::PBKDF2(10_000));
156 let key2 = Key::<32, 32>::new(password, KeyDerivationStrategy::PBKDF2(10_000));
157
158 assert_ne!(key.pubk, key2.pubk);
159 }
160
161 #[test]
162 fn test_key_derivation_with_different_rounds() {
163 let password = "password".as_bytes();
164
165 let key = Key::<32, 32>::new(password, KeyDerivationStrategy::PBKDF2(10_000));
166 let key2 = Key::<32, 32>::new(password, KeyDerivationStrategy::PBKDF2(11_000));
167
168 assert_ne!(key.pubk, key2.pubk);
169 }
170
171 #[test]
172 fn test_key_strategy_serialization_deserialization() {
173 let strategy = KeyDerivationStrategy::PBKDF2(10_000);
174
175 let serialized: Vec<u8> = strategy.clone().into();
176 let deserialized = KeyDerivationStrategy::try_from(serialized).unwrap();
177
178 assert_eq!(strategy, deserialized);
179 }
180}