]> git.proxmox.com Git - proxmox-backup.git/blame - pbs-datastore/src/key_derivation.rs
move data_blob encode/decode from crypt_config.rs to data_blob.rs
[proxmox-backup.git] / pbs-datastore / src / key_derivation.rs
CommitLineData
9a045790
DM
1use std::io::Write;
2use std::path::Path;
37e60ddc 3
86fb3877
WB
4use anyhow::{bail, format_err, Context, Error};
5use serde::{Deserialize, Serialize};
6
9ea4bce4
WB
7use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions};
8use proxmox::try_block;
e18a6c9e 9
45d5d873 10use pbs_api_types::{Kdf, KeyInfo};
86fb3877
WB
11
12use crate::crypt_config::{CryptConfig, Fingerprint};
13
8abe51b7 14/// Key derivation function configuration
feb1645f 15#[derive(Deserialize, Serialize, Clone, Debug)]
826f309b
DM
16pub enum KeyDerivationConfig {
17 Scrypt {
18 n: u64,
19 r: u64,
20 p: u64,
21 #[serde(with = "proxmox::tools::serde::bytes_as_base64")]
22 salt: Vec<u8>,
23 },
24 PBKDF2 {
25 iter: usize,
26 #[serde(with = "proxmox::tools::serde::bytes_as_base64")]
27 salt: Vec<u8>,
28 },
29}
30
31impl KeyDerivationConfig {
32
33 /// Derive a key from provided passphrase
34 pub fn derive_key(&self, passphrase: &[u8]) -> Result<[u8; 32], Error> {
35
36 let mut key = [0u8; 32];
37
38 match self {
39 KeyDerivationConfig::Scrypt { n, r, p, salt } => {
40 // estimated scrypt memory usage is 128*r*n*p
41 openssl::pkcs5::scrypt(
42 passphrase,
43 &salt,
44 *n, *r, *p,
45 1025*1024*1024,
46 &mut key,
47 )?;
48
49 Ok(key)
50 }
51 KeyDerivationConfig::PBKDF2 { iter, salt } => {
52
53 openssl::pkcs5::pbkdf2_hmac(
54 passphrase,
55 &salt,
56 *iter,
57 openssl::hash::MessageDigest::sha256(),
58 &mut key,
59 )?;
60
61 Ok(key)
62 }
63 }
64 }
65}
66
8abe51b7
DM
67/// Encryption Key Configuration
68///
69/// We use this struct to store secret keys. When used with a key
70/// derivation function, the key data is encrypted (AES-CGM), and you
71/// need the password to restore the plain key.
feb1645f 72#[derive(Deserialize, Serialize, Clone, Debug)]
826f309b 73pub struct KeyConfig {
181f097a 74 pub kdf: Option<KeyDerivationConfig>,
6a7be83e
DM
75 #[serde(with = "proxmox::tools::serde::epoch_as_rfc3339")]
76 pub created: i64,
77 #[serde(with = "proxmox::tools::serde::epoch_as_rfc3339")]
78 pub modified: i64,
826f309b 79 #[serde(with = "proxmox::tools::serde::bytes_as_base64")]
181f097a 80 pub data: Vec<u8>,
37e60ddc
FG
81 #[serde(skip_serializing_if = "Option::is_none")]
82 #[serde(default)]
83 pub fingerprint: Option<Fingerprint>,
82a103c8
DM
84 /// Password hint
85 #[serde(skip_serializing_if = "Option::is_none")]
86 pub hint: Option<String>,
87}
826f309b 88
69b8bc3b
DM
89impl From<&KeyConfig> for KeyInfo {
90 fn from(key_config: &KeyConfig) -> Self {
91 Self {
92 path: None,
93 kdf: match key_config.kdf {
94 Some(KeyDerivationConfig::PBKDF2 { .. }) => Kdf::PBKDF2,
95 Some(KeyDerivationConfig::Scrypt { .. }) => Kdf::Scrypt,
96 None => Kdf::None,
97 },
98 created: key_config.created,
99 modified: key_config.modified,
100 fingerprint: key_config
101 .fingerprint
102 .as_ref()
770a36e5 103 .map(|fp| pbs_tools::format::as_fingerprint(fp.bytes())),
69b8bc3b
DM
104 hint: key_config.hint.clone(),
105 }
106 }
107}
108
9a045790 109impl KeyConfig {
181f097a 110
8abe51b7 111 /// Creates a new key using random data, protected by passphrase.
9a045790
DM
112 pub fn new(passphrase: &[u8], kdf: Kdf) -> Result<([u8;32], Self), Error> {
113 let mut key = [0u8; 32];
114 proxmox::sys::linux::fill_with_random_data(&mut key)?;
115 let key_config = Self::with_key(&key, passphrase, kdf)?;
116 Ok((key, key_config))
117 }
181f097a 118
8abe51b7 119 /// Creates a new, unencrypted key.
1c86893d
DM
120 pub fn without_password(raw_key: [u8; 32]) -> Result<Self, Error> {
121 // always compute fingerprint
122 let crypt_config = CryptConfig::new(raw_key.clone())?;
123 let fingerprint = Some(crypt_config.fingerprint());
124
9a045790 125 let created = proxmox::tools::time::epoch_i64();
1c86893d 126 Ok(Self {
9a045790
DM
127 kdf: None,
128 created,
129 modified: created,
130 data: raw_key.to_vec(),
1c86893d 131 fingerprint,
9a045790 132 hint: None,
1c86893d 133 })
9a045790 134 }
181f097a 135
8abe51b7 136 /// Creates a new instance, protect raw_key with passphrase.
9a045790 137 pub fn with_key(
1c86893d 138 raw_key: &[u8; 32],
9a045790
DM
139 passphrase: &[u8],
140 kdf: Kdf,
141 ) -> Result<Self, Error> {
181f097a 142
9a045790
DM
143 if raw_key.len() != 32 {
144 bail!("got strange key length ({} != 32)", raw_key.len())
181f097a
DM
145 }
146
9a045790
DM
147 let salt = proxmox::sys::linux::random_data(32)?;
148
149 let kdf = match kdf {
150 Kdf::Scrypt => KeyDerivationConfig::Scrypt {
151 n: 65536,
152 r: 8,
153 p: 1,
154 salt,
155 },
156 Kdf::PBKDF2 => KeyDerivationConfig::PBKDF2 {
157 iter: 65535,
158 salt,
159 },
160 Kdf::None => {
161 bail!("No key derivation function specified");
162 }
163 };
181f097a 164
9a045790 165 let derived_key = kdf.derive_key(passphrase)?;
826f309b 166
9a045790 167 let cipher = openssl::symm::Cipher::aes_256_gcm();
826f309b 168
9a045790
DM
169 let iv = proxmox::sys::linux::random_data(16)?;
170 let mut tag = [0u8; 16];
826f309b 171
9a045790
DM
172 let encrypted_key = openssl::symm::encrypt_aead(
173 cipher,
174 &derived_key,
175 Some(&iv),
176 b"",
1c86893d 177 raw_key,
9a045790
DM
178 &mut tag,
179 )?;
180
181 let mut enc_data = vec![];
182 enc_data.extend_from_slice(&iv);
183 enc_data.extend_from_slice(&tag);
184 enc_data.extend_from_slice(&encrypted_key);
185
186 let created = proxmox::tools::time::epoch_i64();
187
1c86893d
DM
188 // always compute fingerprint
189 let crypt_config = CryptConfig::new(raw_key.clone())?;
190 let fingerprint = Some(crypt_config.fingerprint());
191
9a045790
DM
192 Ok(Self {
193 kdf: Some(kdf),
194 created,
195 modified: created,
196 data: enc_data,
1c86893d 197 fingerprint,
9a045790
DM
198 hint: None,
199 })
200 }
82a103c8 201
9a045790
DM
202 /// Loads a KeyConfig from path
203 pub fn load<P: AsRef<Path>>(path: P) -> Result<KeyConfig, Error> {
204 let keydata = file_get_contents(path)?;
205 let key_config: KeyConfig = serde_json::from_reader(&keydata[..])?;
206 Ok(key_config)
207 }
826f309b 208
8abe51b7 209 /// Decrypt key to get raw key data.
9a045790
DM
210 pub fn decrypt(
211 &self,
212 passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
213 ) -> Result<([u8;32], i64, Fingerprint), Error> {
826f309b 214
9a045790 215 let raw_data = &self.data;
826f309b 216
9a045790 217 let key = if let Some(ref kdf) = self.kdf {
826f309b 218
9a045790
DM
219 let passphrase = passphrase()?;
220 if passphrase.len() < 5 {
221 bail!("Passphrase is too short!");
222 }
826f309b 223
9a045790 224 let derived_key = kdf.derive_key(&passphrase)?;
826f309b 225
9a045790 226 if raw_data.len() < 32 {
8428063d 227 bail!("Unable to decrypt key - short data");
9a045790
DM
228 }
229 let iv = &raw_data[0..16];
230 let tag = &raw_data[16..32];
231 let enc_data = &raw_data[32..];
826f309b 232
9a045790
DM
233 let cipher = openssl::symm::Cipher::aes_256_gcm();
234
235 openssl::symm::decrypt_aead(
236 cipher,
237 &derived_key,
238 Some(&iv),
239 b"",
240 &enc_data,
241 &tag,
8428063d
DM
242 ).map_err(|err| {
243 match self.hint {
244 Some(ref hint) => {
245 format_err!("Unable to decrypt key (password hint: {})", hint)
246 }
247 None => {
248 format_err!("Unable to decrypt key (wrong password?) - {}", err)
249 }
250 }
251 })?
9a045790
DM
252
253 } else {
254 raw_data.clone()
255 };
256
257 let mut result = [0u8; 32];
258 result.copy_from_slice(&key);
259
260 let crypt_config = CryptConfig::new(result.clone())?;
261 let fingerprint = crypt_config.fingerprint();
262 if let Some(ref stored_fingerprint) = self.fingerprint {
263 if &fingerprint != stored_fingerprint {
264 bail!(
265 "KeyConfig contains wrong fingerprint {}, contained key has fingerprint {}",
266 stored_fingerprint, fingerprint
267 );
268 }
e0af222e 269 }
9a045790
DM
270
271 Ok((result, self.created, fingerprint))
272 }
273
8abe51b7 274 /// Store a KeyConfig to path
9a045790
DM
275 pub fn store<P: AsRef<Path>>(&self, path: P, replace: bool) -> Result<(), Error> {
276
277 let path: &Path = path.as_ref();
278
279 let data = serde_json::to_string(self)?;
280
281 try_block!({
282 if replace {
283 let mode = nix::sys::stat::Mode::S_IRUSR | nix::sys::stat::Mode::S_IWUSR;
284 replace_file(path, data.as_bytes(), CreateOptions::new().perm(mode))?;
285 } else {
286 use std::os::unix::fs::OpenOptionsExt;
287
288 let mut file = std::fs::OpenOptions::new()
289 .write(true)
290 .mode(0o0600)
291 .create_new(true)
292 .open(&path)?;
293
294 file.write_all(data.as_bytes())?;
295 }
296
297 Ok(())
298 }).map_err(|err: Error| format_err!("Unable to store key file {:?} - {}", path, err))?;
299
300 Ok(())
e0af222e 301 }
9a045790 302}
82a103c8 303
8abe51b7 304/// Loads a KeyConfig from path and decrypt it.
9a045790
DM
305pub fn load_and_decrypt_key(
306 path: &std::path::Path,
307 passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
308) -> Result<([u8;32], i64, Fingerprint), Error> {
309 decrypt_key(&file_get_contents(&path)?, passphrase)
310 .with_context(|| format!("failed to load decryption key from {:?}", path))
8ca37d6a 311}
37e60ddc 312
8abe51b7 313/// Decrypt a KeyConfig from raw keydata.
8ca37d6a
DM
314pub fn decrypt_key(
315 mut keydata: &[u8],
316 passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
317) -> Result<([u8;32], i64, Fingerprint), Error> {
318 let key_config: KeyConfig = serde_json::from_reader(&mut keydata)?;
9a045790 319 key_config.decrypt(passphrase)
826f309b 320}
8acfd15d 321
8abe51b7 322/// RSA encrypt a KeyConfig using a public key
8acfd15d
FG
323pub fn rsa_encrypt_key_config(
324 rsa: openssl::rsa::Rsa<openssl::pkey::Public>,
325 key: &KeyConfig,
326) -> Result<Vec<u8>, Error> {
327 let data = serde_json::to_string(key)?.as_bytes().to_vec();
328
329 let mut buffer = vec![0u8; rsa.size() as usize];
330 let len = rsa.public_encrypt(&data, &mut buffer, openssl::rsa::Padding::PKCS1)?;
331 if len != buffer.len() {
332 bail!("got unexpected length from rsa.public_encrypt().");
333 }
334 Ok(buffer)
335}
7137630d 336
8abe51b7 337/// RSA deccrypt a KeyConfig using a private key
7137630d
FG
338pub fn rsa_decrypt_key_config(
339 rsa: openssl::rsa::Rsa<openssl::pkey::Private>,
340 key: &[u8],
341 passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
342) -> Result<([u8; 32], i64, Fingerprint), Error> {
343 let mut buffer = vec![0u8; rsa.size() as usize];
344 let decrypted = rsa
345 .private_decrypt(key, &mut buffer, openssl::rsa::Padding::PKCS1)
346 .map_err(|err| format_err!("failed to decrypt KeyConfig using RSA - {}", err))?;
ccec086e 347 decrypt_key(&buffer[..decrypted], passphrase)
7137630d 348}
73b50117
FG
349
350#[test]
351fn encrypt_decrypt_test() -> Result<(), Error> {
352 use openssl::bn::BigNum;
353
354 // hard-coded RSA key to avoid RNG load
355 let n = BigNum::from_dec_str("763297775503047285270249195475255390818773358873206395804367739392073195066702037300664538507287660511788773520960052020490020500131532096848837840341808263208238432840771609456175669299183585737297710099814398628316822920397690811697390531460556770185920171717205255045261184699028939408338685227311360280561223048029934992213591164033485740834987719043448066906674761591422028943934961140687347873900379335604823288576732038392698785999130614670054444889172406669687648738432457362496418067100853836965448514921778954255248154805294695304544857640397043149235605321195443660560591215396460879078079707598866525981810195613239506906019678159905700662365794059211182357495974678620101388841934629146959674859076053348229540838236896752745251734302737503775744293828247434369307467826891918526442390310055226655466835862319406221740752718258752129277114593279326227799698036058425261999904258111333276380007458144919278763944469942242338999234161147188585579934794573969834141472487673642778646170134259790130811461184848743147137198639341697548363179639042991358823669057297753206096865332303845149920379065177826748710006313272747133642274061146677367740923397358666767242901746171920401890395722806446280380164886804469750825832083").expect("converting to bignum failed");
356 let e = BigNum::from_dec_str("65537").expect("converting to bignum failed");
357 let d = BigNum::from_dec_str("19834537920284564853674022001226176519590018312725185651690468898251379391772488358073023011091610629897253174637151053464371346136136825929376853412608136964518211867003891708559549030570664609466682947037305962494828103719078802086159819263581307957743290849968728341884428605863043529798446388179368090663224786773806846388143274064254180335413340334940446739125488182098535411927937482988091512111514808559058456451259207186517021416246081401087976557460070014777577029793101223558164090029643622447657946212243306210181845486266030884899215596710196751196243890657122549917370139613045651724521564033154854414253451612565268626314358200247667906740226693180923631251719053819020017537699856142036238058103150388959616397059243552685604990510867544536282659146915388522812398795915840913802745825670833498941795568293354230962683054249223513028733221781409833526268687556063636480230666207346771664323325175723577540510559973905170578206847160551684632855673373061549848844186260938182413805301541655002820734307939021848604620517318497220269398148326924299176570233223593669359192722153811016413065311904503101005564780859010942238851216519088762587394817890851764597501374473176420295837906296738426781972820833509964922715585").expect("converting to bignum failed");
358 let p = BigNum::from_dec_str("29509637001892646371585718218450720181675215968655693119622290166463846337874978909899277049204111617901784460858811114760264767076166751445502024396748257412446297522757119324882999179307561418697097464139952930737249422485899639568595470472222197161276683797577982497955467948265299386993875583089675892019886767032750524889582030672594405810531152141432362873209548569385820623081973262550874468619670422387868884561012170536839449407663630232422905779693831681822257822783504983493794208329832510955061326579888576047912149807967610736616238778237407615015312695567289456675371922184276823263863231190560557676339").expect("converting to bignum failed");
359 let q = BigNum::from_dec_str("25866050993920799422553175902510303878636288340476152724026122959148470649546748310678170203350410878157245623372422271950639190884394436256045773535202161325882791039345330048364703416719823181485853395688815455066122599160191671526435061804017559815713791273329637690511813515454721229797045837580571003198471014420883727461348135261877384657284061678787895040009197824032371314493780688519536250146270701914875469190776765810821706480996720025323321483843112182646061748043938180130013308823672610860230340094502643614566152670758944502783858455501528490806234504795239898001698524105646533910560293336400403204897").expect("converting to bignum failed");
360 let dmp1 = BigNum::from_dec_str("21607770579166338313924278588690558922108583912962897316392792781303188398339022047518905458553289108745759383366535358272664077428797321640702979183532285223743426240475893650342331272664468275332046219832278884297711602396407401980831582724583041600551528176116883960387063733484217876666037528133838392148714866050744345765006980605100330287254053877398358630385580919903058731105447806937933747350668236714360621211130384969129674812319182867594036995223272269821421615266717078107026511273509659211002684589097654567453625356436054504001404801715927134738465685565147724902539753143706245247513141254140715042985").expect("converting to bignum failed");
361 let dmq1 = BigNum::from_dec_str("294824909477987048059069264677589712640818276551195295555907561384926187881828905626998384758270243160099828809057470393016578048898219996082612765778049262408020582364022789357590879232947921274546172186391582540158896220038500063021605980859684104892476037676079761887366292263067835858498149757735119694054623308549371262243115446856316376077501168409517640338844786525965200908293851935915491689568704919822573134038943559526432621897623477713604851434011395096458613085567264607124524187730342254186063812054159860538030670385536895853938115358646898433438472543479930479076991585011794266310458811393428158049").expect("converting to bignum failed");
362 let iqmp = BigNum::from_dec_str("19428066064824171668277167138275898936765006396600005071379051329779053619544399695639107933588871625444213173194462077344726482973273922001955114108600584475883837715007613468112455972196002915686862701860412263935895363086514864873592142686096117947515832613228762197577036084559813332497101195090727973644165586960538914545531208630624795512138060798977135902359295307626262953373309121954863020224150277262533638440848025788447039555055470985052690506486164836957350781708784380677438638580158751807723730202286612196281022183410822668814233870246463721184575820166925259871133457423401827024362448849298618281053").expect("converting to bignum failed");
363 let public =
364 openssl::rsa::Rsa::from_public_components(n.to_owned().unwrap(), e.to_owned().unwrap())
365 .expect("creating hard-coded RSA public key instance failed");
366 let private = openssl::rsa::Rsa::from_private_components(n, e, d, p, q, dmp1, dmq1, iqmp)
367 .expect("creating hard-coded RSA key instance failed");
368
369 let passphrase = || -> Result<Vec<u8>, Error> { Ok(Vec::new()) };
370
371 let key = KeyConfig {
372 kdf: None,
373 created: proxmox::tools::time::epoch_i64(),
374 modified: proxmox::tools::time::epoch_i64(),
375 data: (0u8..32u8).collect(),
376 fingerprint: Some(Fingerprint::new([
377 14, 171, 212, 70, 11, 110, 185, 202, 52, 80, 35, 222, 226, 183, 120, 199, 144, 229, 74,
378 22, 131, 185, 101, 156, 10, 87, 174, 25, 144, 144, 21, 155,
379 ])),
82a103c8 380 hint: None,
73b50117
FG
381 };
382
44288184 383 let encrypted = rsa_encrypt_key_config(public, &key).expect("encryption failed");
73b50117 384 let (decrypted, created, fingerprint) =
44288184 385 rsa_decrypt_key_config(private, &encrypted, &passphrase)
73b50117
FG
386 .expect("decryption failed");
387
388 assert_eq!(key.created, created);
389 assert_eq!(key.data, decrypted);
390 assert_eq!(key.fingerprint, Some(fingerprint));
391
c0174285
FG
392 Ok(())
393}
394
395#[test]
396fn fingerprint_checks() -> Result<(), Error> {
e0af222e
FG
397 let key = KeyConfig {
398 kdf: None,
399 created: proxmox::tools::time::epoch_i64(),
400 modified: proxmox::tools::time::epoch_i64(),
401 data: (0u8..32u8).collect(),
402 fingerprint: Some(Fingerprint::new([0u8; 32])), // wrong FP
82a103c8 403 hint: None,
e0af222e 404 };
e0af222e 405
c0174285
FG
406 let expected_fingerprint = Fingerprint::new([
407 14, 171, 212, 70, 11, 110, 185, 202, 52, 80, 35, 222, 226, 183, 120, 199, 144, 229, 74,
408 22, 131, 185, 101, 156, 10, 87, 174, 25, 144, 144, 21, 155,
409 ]);
410
411 let mut data = serde_json::to_vec(&key).expect("encoding KeyConfig failed");
412 decrypt_key(&mut data, &{ || { Ok(Vec::new()) }}).expect_err("decoding KeyConfig with wrong fingerprint worked");
413
414 let key = KeyConfig {
415 kdf: None,
416 created: proxmox::tools::time::epoch_i64(),
417 modified: proxmox::tools::time::epoch_i64(),
418 data: (0u8..32u8).collect(),
419 fingerprint: None,
82a103c8 420 hint: None,
c0174285
FG
421 };
422
423
424 let mut data = serde_json::to_vec(&key).expect("encoding KeyConfig failed");
425 let (key_data, created, fingerprint) = decrypt_key(&mut data, &{ || { Ok(Vec::new()) }}).expect("decoding KeyConfig without fingerprint failed");
426
427 assert_eq!(key.data, key_data);
e0af222e 428 assert_eq!(key.created, created);
c0174285 429 assert_eq!(expected_fingerprint, fingerprint);
e0af222e 430
73b50117
FG
431 Ok(())
432}