]> git.proxmox.com Git - proxmox-backup.git/blame - pbs-config/src/key_config.rs
update to proxmox-sys 0.2 crate
[proxmox-backup.git] / pbs-config / src / key_config.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
25877d05 7use proxmox_sys::fs::{file_get_contents, replace_file, CreateOptions};
6ef1b649 8use proxmox_lang::try_block;
e18a6c9e 9
bbdda58b 10use pbs_api_types::{Kdf, KeyInfo, Fingerprint};
86fb3877 11
bbdda58b 12use pbs_tools::crypt_config::CryptConfig;
86fb3877 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,
25877d05 21 #[serde(with = "proxmox_serde::bytes_as_base64")]
826f309b
DM
22 salt: Vec<u8>,
23 },
24 PBKDF2 {
25 iter: usize,
25877d05 26 #[serde(with = "proxmox_serde::bytes_as_base64")]
826f309b
DM
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
572e6594 70/// derivation function, the key data is encrypted (AES-GCM), and you
8abe51b7 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>,
25877d05 75 #[serde(with = "proxmox_serde::epoch_as_rfc3339")]
6a7be83e 76 pub created: i64,
25877d05 77 #[serde(with = "proxmox_serde::epoch_as_rfc3339")]
6a7be83e 78 pub modified: i64,
25877d05 79 #[serde(with = "proxmox_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()
ca6e66aa 103 .map(|fp| fp.signature()),
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];
25877d05 114 proxmox_sys::linux::fill_with_random_data(&mut key)?;
9a045790
DM
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())?;
bbdda58b 123 let fingerprint = Some(Fingerprint::new(crypt_config.fingerprint()));
1c86893d 124
6ef1b649 125 let created = proxmox_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
25877d05 147 let salt = proxmox_sys::linux::random_data(32)?;
9a045790
DM
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
25877d05 169 let iv = proxmox_sys::linux::random_data(16)?;
9a045790 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
6ef1b649 186 let created = proxmox_time::epoch_i64();
9a045790 187
1c86893d
DM
188 // always compute fingerprint
189 let crypt_config = CryptConfig::new(raw_key.clone())?;
bbdda58b 190 let fingerprint = Some(Fingerprint::new(crypt_config.fingerprint()));
1c86893d 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())?;
bbdda58b 261 let fingerprint = Fingerprint::new(crypt_config.fingerprint());
9a045790
DM
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;
e0a19d33 284 replace_file(path, data.as_bytes(), CreateOptions::new().perm(mode), true)?;
9a045790
DM
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,
6ef1b649
WB
373 created: proxmox_time::epoch_i64(),
374 modified: proxmox_time::epoch_i64(),
73b50117
FG
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,
6ef1b649
WB
399 created: proxmox_time::epoch_i64(),
400 modified: proxmox_time::epoch_i64(),
e0af222e
FG
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,
6ef1b649
WB
416 created: proxmox_time::epoch_i64(),
417 modified: proxmox_time::epoch_i64(),
c0174285
FG
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}