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