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