]> git.proxmox.com Git - proxmox-backup.git/blame - src/backup/key_derivation.rs
avoid chrono dependency, depend on proxmox 0.3.8
[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
9ea4bce4
WB
5use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions};
6use proxmox::try_block;
e18a6c9e 7
826f309b
DM
8#[derive(Deserialize, Serialize, Debug)]
9pub enum KeyDerivationConfig {
10 Scrypt {
11 n: u64,
12 r: u64,
13 p: u64,
14 #[serde(with = "proxmox::tools::serde::bytes_as_base64")]
15 salt: Vec<u8>,
16 },
17 PBKDF2 {
18 iter: usize,
19 #[serde(with = "proxmox::tools::serde::bytes_as_base64")]
20 salt: Vec<u8>,
21 },
22}
23
24impl KeyDerivationConfig {
25
26 /// Derive a key from provided passphrase
27 pub fn derive_key(&self, passphrase: &[u8]) -> Result<[u8; 32], Error> {
28
29 let mut key = [0u8; 32];
30
31 match self {
32 KeyDerivationConfig::Scrypt { n, r, p, salt } => {
33 // estimated scrypt memory usage is 128*r*n*p
34 openssl::pkcs5::scrypt(
35 passphrase,
36 &salt,
37 *n, *r, *p,
38 1025*1024*1024,
39 &mut key,
40 )?;
41
42 Ok(key)
43 }
44 KeyDerivationConfig::PBKDF2 { iter, salt } => {
45
46 openssl::pkcs5::pbkdf2_hmac(
47 passphrase,
48 &salt,
49 *iter,
50 openssl::hash::MessageDigest::sha256(),
51 &mut key,
52 )?;
53
54 Ok(key)
55 }
56 }
57 }
58}
59
60#[derive(Deserialize, Serialize, Debug)]
61pub struct KeyConfig {
181f097a 62 pub kdf: Option<KeyDerivationConfig>,
6a7be83e
DM
63 #[serde(with = "proxmox::tools::serde::epoch_as_rfc3339")]
64 pub created: i64,
65 #[serde(with = "proxmox::tools::serde::epoch_as_rfc3339")]
66 pub modified: i64,
826f309b 67 #[serde(with = "proxmox::tools::serde::bytes_as_base64")]
181f097a 68 pub data: Vec<u8>,
826f309b
DM
69 }
70
181f097a
DM
71pub fn store_key_config(
72 path: &std::path::Path,
73 replace: bool,
74 key_config: KeyConfig,
75) -> Result<(), Error> {
76
77 let data = serde_json::to_string(&key_config)?;
78
79 use std::io::Write;
80
81 try_block!({
82 if replace {
83 let mode = nix::sys::stat::Mode::S_IRUSR | nix::sys::stat::Mode::S_IWUSR;
feaa1ad3 84 replace_file(&path, data.as_bytes(), CreateOptions::new().perm(mode))?;
181f097a
DM
85 } else {
86 use std::os::unix::fs::OpenOptionsExt;
87
88 let mut file = std::fs::OpenOptions::new()
89 .write(true)
90 .mode(0o0600)
91 .create_new(true)
92 .open(&path)?;
93
94 file.write_all(data.as_bytes())?;
95 }
96
97 Ok(())
98 }).map_err(|err: Error| format_err!("Unable to create file {:?} - {}", path, err))?;
99
100 Ok(())
101}
826f309b 102
ab44acff 103pub fn encrypt_key_with_passphrase(
826f309b
DM
104 raw_key: &[u8],
105 passphrase: &[u8],
ab44acff 106) -> Result<KeyConfig, Error> {
826f309b
DM
107
108 let salt = proxmox::sys::linux::random_data(32)?;
109
110 let kdf = KeyDerivationConfig::Scrypt {
111 n: 65536,
112 r: 8,
113 p: 1,
114 salt,
115 };
116
117 let derived_key = kdf.derive_key(passphrase)?;
118
119 let cipher = openssl::symm::Cipher::aes_256_gcm();
120
121 let iv = proxmox::sys::linux::random_data(16)?;
122 let mut tag = [0u8; 16];
123
124 let encrypted_key = openssl::symm::encrypt_aead(
125 cipher,
126 &derived_key,
127 Some(&iv),
128 b"",
129 &raw_key,
130 &mut tag,
131 )?;
132
133 let mut enc_data = vec![];
134 enc_data.extend_from_slice(&iv);
135 enc_data.extend_from_slice(&tag);
136 enc_data.extend_from_slice(&encrypted_key);
137
6a7be83e 138 let created = proxmox::tools::time::epoch_i64();
826f309b 139
ab44acff 140 Ok(KeyConfig {
826f309b
DM
141 kdf: Some(kdf),
142 created,
ab44acff 143 modified: created,
826f309b 144 data: enc_data,
181f097a 145 })
826f309b
DM
146}
147
b65390eb
WB
148pub fn load_and_decrypt_key(
149 path: &std::path::Path,
150 passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
6a7be83e 151) -> Result<([u8;32], i64), Error> {
b65390eb
WB
152 do_load_and_decrypt_key(path, passphrase)
153 .with_context(|| format!("failed to load decryption key from {:?}", path))
154}
826f309b 155
b65390eb
WB
156fn do_load_and_decrypt_key(
157 path: &std::path::Path,
158 passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
6a7be83e 159) -> Result<([u8;32], i64), Error> {
0351f23b
WB
160 decrypt_key(&file_get_contents(&path)?, passphrase)
161}
826f309b 162
0351f23b
WB
163pub fn decrypt_key(
164 mut keydata: &[u8],
165 passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
6a7be83e 166) -> Result<([u8;32], i64), Error> {
0351f23b 167 let key_config: KeyConfig = serde_json::from_reader(&mut keydata)?;
826f309b
DM
168
169 let raw_data = key_config.data;
ab44acff 170 let created = key_config.created;
826f309b 171
6d0983db 172 let key = if let Some(kdf) = key_config.kdf {
826f309b
DM
173
174 let passphrase = passphrase()?;
175 if passphrase.len() < 5 {
176 bail!("Passphrase is too short!");
177 }
178
179 let derived_key = kdf.derive_key(&passphrase)?;
180
181 if raw_data.len() < 32 {
182 bail!("Unable to encode key - short data");
183 }
184 let iv = &raw_data[0..16];
185 let tag = &raw_data[16..32];
186 let enc_data = &raw_data[32..];
187
188 let cipher = openssl::symm::Cipher::aes_256_gcm();
189
834a2f95 190 openssl::symm::decrypt_aead(
826f309b
DM
191 cipher,
192 &derived_key,
193 Some(&iv),
194 b"", //??
195 &enc_data,
196 &tag,
834a2f95 197 ).map_err(|err| format_err!("Unable to decrypt key - {}", err))?
826f309b 198
826f309b 199 } else {
6d0983db
DM
200 raw_data
201 };
202
203 let mut result = [0u8; 32];
204 result.copy_from_slice(&key);
205
ab44acff 206 Ok((result, created))
826f309b 207}