]>
Commit | Line | Data |
---|---|---|
2711e94e | 1 | use std::io::{IsTerminal, Read}; |
bdfa6370 TL |
2 | use std::os::unix::io::{FromRawFd, RawFd}; |
3 | use std::path::PathBuf; | |
ff8945fd SR |
4 | |
5 | use anyhow::{bail, format_err, Error}; | |
6 | use serde_json::Value; | |
7 | ||
6ef1b649 | 8 | use proxmox_schema::*; |
bdfa6370 TL |
9 | use proxmox_sys::fs::file_get_contents; |
10 | use proxmox_sys::linux::tty; | |
ff8945fd | 11 | |
2b7f8dd5 | 12 | use pbs_api_types::CryptMode; |
ff8945fd SR |
13 | |
14 | pub const DEFAULT_ENCRYPTION_KEY_FILE_NAME: &str = "encryption-key.json"; | |
15 | pub const DEFAULT_MASTER_PUBKEY_FILE_NAME: &str = "master-public.pem"; | |
16 | ||
17 | pub const KEYFILE_SCHEMA: Schema = | |
18 | StringSchema::new("Path to encryption key. All data will be encrypted using this key.") | |
19 | .schema(); | |
20 | ||
21 | pub const KEYFD_SCHEMA: Schema = | |
22 | IntegerSchema::new("Pass an encryption key via an already opened file descriptor.") | |
23 | .minimum(0) | |
24 | .schema(); | |
25 | ||
26 | pub const MASTER_PUBKEY_FILE_SCHEMA: Schema = StringSchema::new( | |
27 | "Path to master public key. The encryption key used for a backup will be encrypted using this key and appended to the backup.") | |
28 | .schema(); | |
29 | ||
30 | pub const MASTER_PUBKEY_FD_SCHEMA: Schema = | |
31 | IntegerSchema::new("Pass a master public key via an already opened file descriptor.") | |
32 | .minimum(0) | |
33 | .schema(); | |
34 | ||
35 | #[derive(Clone, Debug, Eq, PartialEq)] | |
36 | pub enum KeySource { | |
37 | DefaultKey, | |
38 | Fd, | |
39 | Path(String), | |
40 | } | |
41 | ||
42 | pub fn format_key_source(source: &KeySource, key_type: &str) -> String { | |
43 | match source { | |
44 | KeySource::DefaultKey => format!("Using default {} key..", key_type), | |
45 | KeySource::Fd => format!("Using {} key from file descriptor..", key_type), | |
46 | KeySource::Path(path) => format!("Using {} key from '{}'..", key_type, path), | |
47 | } | |
48 | } | |
49 | ||
50 | #[derive(Clone, Debug, Eq, PartialEq)] | |
51 | pub struct KeyWithSource { | |
52 | pub source: KeySource, | |
53 | pub key: Vec<u8>, | |
54 | } | |
55 | ||
56 | impl KeyWithSource { | |
57 | pub fn from_fd(key: Vec<u8>) -> Self { | |
58 | Self { | |
59 | source: KeySource::Fd, | |
60 | key, | |
61 | } | |
62 | } | |
63 | ||
64 | pub fn from_default(key: Vec<u8>) -> Self { | |
65 | Self { | |
66 | source: KeySource::DefaultKey, | |
67 | key, | |
68 | } | |
69 | } | |
70 | ||
71 | pub fn from_path(path: String, key: Vec<u8>) -> Self { | |
72 | Self { | |
73 | source: KeySource::Path(path), | |
74 | key, | |
75 | } | |
76 | } | |
77 | } | |
78 | ||
79 | #[derive(Debug, Eq, PartialEq)] | |
80 | pub struct CryptoParams { | |
81 | pub mode: CryptMode, | |
82 | pub enc_key: Option<KeyWithSource>, | |
83 | // FIXME switch to openssl::rsa::rsa<openssl::pkey::Public> once that is Eq? | |
84 | pub master_pubkey: Option<KeyWithSource>, | |
85 | } | |
86 | ||
87 | pub fn crypto_parameters(param: &Value) -> Result<CryptoParams, Error> { | |
9d8ab627 SR |
88 | do_crypto_parameters(param, false) |
89 | } | |
90 | ||
91 | pub fn crypto_parameters_keep_fd(param: &Value) -> Result<CryptoParams, Error> { | |
92 | do_crypto_parameters(param, true) | |
93 | } | |
94 | ||
95 | fn do_crypto_parameters(param: &Value, keep_keyfd_open: bool) -> Result<CryptoParams, Error> { | |
ff8945fd SR |
96 | let keyfile = match param.get("keyfile") { |
97 | Some(Value::String(keyfile)) => Some(keyfile), | |
98 | Some(_) => bail!("bad --keyfile parameter type"), | |
99 | None => None, | |
100 | }; | |
101 | ||
102 | let key_fd = match param.get("keyfd") { | |
103 | Some(Value::Number(key_fd)) => Some( | |
bdfa6370 TL |
104 | RawFd::try_from( |
105 | key_fd | |
106 | .as_i64() | |
107 | .ok_or_else(|| format_err!("bad key fd: {:?}", key_fd))?, | |
ff8945fd | 108 | ) |
bdfa6370 | 109 | .map_err(|err| format_err!("bad key fd: {:?}: {}", key_fd, err))?, |
ff8945fd SR |
110 | ), |
111 | Some(_) => bail!("bad --keyfd parameter type"), | |
112 | None => None, | |
113 | }; | |
114 | ||
115 | let master_pubkey_file = match param.get("master-pubkey-file") { | |
116 | Some(Value::String(keyfile)) => Some(keyfile), | |
117 | Some(_) => bail!("bad --master-pubkey-file parameter type"), | |
118 | None => None, | |
119 | }; | |
120 | ||
121 | let master_pubkey_fd = match param.get("master-pubkey-fd") { | |
122 | Some(Value::Number(key_fd)) => Some( | |
bdfa6370 TL |
123 | RawFd::try_from( |
124 | key_fd | |
125 | .as_i64() | |
126 | .ok_or_else(|| format_err!("bad master public key fd: {:?}", key_fd))?, | |
ff8945fd | 127 | ) |
bdfa6370 | 128 | .map_err(|err| format_err!("bad public master key fd: {:?}: {}", key_fd, err))?, |
ff8945fd SR |
129 | ), |
130 | Some(_) => bail!("bad --master-pubkey-fd parameter type"), | |
131 | None => None, | |
132 | }; | |
133 | ||
134 | let mode: Option<CryptMode> = match param.get("crypt-mode") { | |
38774184 | 135 | Some(mode) => Some(serde::Deserialize::deserialize(mode)?), |
ff8945fd SR |
136 | None => None, |
137 | }; | |
138 | ||
139 | let key = match (keyfile, key_fd) { | |
140 | (None, None) => None, | |
141 | (Some(_), Some(_)) => bail!("--keyfile and --keyfd are mutually exclusive"), | |
142 | (Some(keyfile), None) => Some(KeyWithSource::from_path( | |
143 | keyfile.clone(), | |
144 | file_get_contents(keyfile)?, | |
145 | )), | |
146 | (None, Some(fd)) => { | |
9d8ab627 | 147 | let mut input = unsafe { std::fs::File::from_raw_fd(fd) }; |
ff8945fd | 148 | let mut data = Vec::new(); |
9d8ab627 | 149 | let _len: usize = input.read_to_end(&mut data).map_err(|err| { |
ff8945fd SR |
150 | format_err!("error reading encryption key from fd {}: {}", fd, err) |
151 | })?; | |
9d8ab627 SR |
152 | if keep_keyfd_open { |
153 | // don't close fd if requested, and try to reset seek position | |
154 | std::mem::forget(input); | |
bdfa6370 TL |
155 | unsafe { |
156 | libc::lseek(fd, 0, libc::SEEK_SET); | |
157 | } | |
9d8ab627 | 158 | } |
ff8945fd SR |
159 | Some(KeyWithSource::from_fd(data)) |
160 | } | |
161 | }; | |
162 | ||
163 | let master_pubkey = match (master_pubkey_file, master_pubkey_fd) { | |
164 | (None, None) => None, | |
165 | (Some(_), Some(_)) => bail!("--keyfile and --keyfd are mutually exclusive"), | |
166 | (Some(keyfile), None) => Some(KeyWithSource::from_path( | |
167 | keyfile.clone(), | |
168 | file_get_contents(keyfile)?, | |
169 | )), | |
170 | (None, Some(fd)) => { | |
171 | let input = unsafe { std::fs::File::from_raw_fd(fd) }; | |
172 | let mut data = Vec::new(); | |
173 | let _len: usize = { input } | |
174 | .read_to_end(&mut data) | |
175 | .map_err(|err| format_err!("error reading master key from fd {}: {}", fd, err))?; | |
176 | Some(KeyWithSource::from_fd(data)) | |
177 | } | |
178 | }; | |
179 | ||
180 | let res = match mode { | |
181 | // no crypt mode, enable encryption if keys are available | |
182 | None => match (key, master_pubkey) { | |
183 | // only default keys if available | |
184 | (None, None) => match read_optional_default_encryption_key()? { | |
185 | None => CryptoParams { mode: CryptMode::None, enc_key: None, master_pubkey: None }, | |
186 | enc_key => { | |
187 | let master_pubkey = read_optional_default_master_pubkey()?; | |
188 | CryptoParams { | |
189 | mode: CryptMode::Encrypt, | |
190 | enc_key, | |
191 | master_pubkey, | |
192 | } | |
193 | }, | |
194 | }, | |
195 | ||
196 | // explicit master key, default enc key needed | |
197 | (None, master_pubkey) => match read_optional_default_encryption_key()? { | |
198 | None => bail!("--master-pubkey-file/--master-pubkey-fd specified, but no key available"), | |
199 | enc_key => { | |
200 | CryptoParams { | |
201 | mode: CryptMode::Encrypt, | |
202 | enc_key, | |
203 | master_pubkey, | |
204 | } | |
205 | }, | |
206 | }, | |
207 | ||
208 | // explicit keyfile, maybe default master key | |
209 | (enc_key, None) => CryptoParams { mode: CryptMode::Encrypt, enc_key, master_pubkey: read_optional_default_master_pubkey()? }, | |
210 | ||
211 | // explicit keyfile and master key | |
212 | (enc_key, master_pubkey) => CryptoParams { mode: CryptMode::Encrypt, enc_key, master_pubkey }, | |
213 | }, | |
214 | ||
215 | // explicitly disabled encryption | |
216 | Some(CryptMode::None) => match (key, master_pubkey) { | |
217 | // no keys => OK, no encryption | |
218 | (None, None) => CryptoParams { mode: CryptMode::None, enc_key: None, master_pubkey: None }, | |
219 | ||
220 | // --keyfile and --crypt-mode=none | |
221 | (Some(_), _) => bail!("--keyfile/--keyfd and --crypt-mode=none are mutually exclusive"), | |
222 | ||
223 | // --master-pubkey-file and --crypt-mode=none | |
224 | (_, Some(_)) => bail!("--master-pubkey-file/--master-pubkey-fd and --crypt-mode=none are mutually exclusive"), | |
225 | }, | |
226 | ||
227 | // explicitly enabled encryption | |
228 | Some(mode) => match (key, master_pubkey) { | |
229 | // no key, maybe master key | |
230 | (None, master_pubkey) => match read_optional_default_encryption_key()? { | |
231 | None => bail!("--crypt-mode without --keyfile and no default key file available"), | |
232 | enc_key => { | |
e10fccf5 | 233 | log::info!("Encrypting with default encryption key!"); |
ff8945fd SR |
234 | let master_pubkey = match master_pubkey { |
235 | None => read_optional_default_master_pubkey()?, | |
236 | master_pubkey => master_pubkey, | |
237 | }; | |
238 | ||
239 | CryptoParams { | |
240 | mode, | |
241 | enc_key, | |
242 | master_pubkey, | |
243 | } | |
244 | }, | |
245 | }, | |
246 | ||
247 | // --keyfile and --crypt-mode other than none | |
248 | (enc_key, master_pubkey) => { | |
249 | let master_pubkey = match master_pubkey { | |
250 | None => read_optional_default_master_pubkey()?, | |
251 | master_pubkey => master_pubkey, | |
252 | }; | |
253 | ||
254 | CryptoParams { mode, enc_key, master_pubkey } | |
255 | }, | |
256 | }, | |
257 | }; | |
258 | ||
259 | Ok(res) | |
260 | } | |
261 | ||
262 | pub fn find_default_master_pubkey() -> Result<Option<PathBuf>, Error> { | |
263 | super::find_xdg_file( | |
264 | DEFAULT_MASTER_PUBKEY_FILE_NAME, | |
265 | "default master public key file", | |
266 | ) | |
267 | } | |
268 | ||
269 | pub fn place_default_master_pubkey() -> Result<PathBuf, Error> { | |
270 | super::place_xdg_file( | |
271 | DEFAULT_MASTER_PUBKEY_FILE_NAME, | |
272 | "default master public key file", | |
273 | ) | |
274 | } | |
275 | ||
276 | pub fn find_default_encryption_key() -> Result<Option<PathBuf>, Error> { | |
277 | super::find_xdg_file( | |
278 | DEFAULT_ENCRYPTION_KEY_FILE_NAME, | |
279 | "default encryption key file", | |
280 | ) | |
281 | } | |
282 | ||
283 | pub fn place_default_encryption_key() -> Result<PathBuf, Error> { | |
284 | super::place_xdg_file( | |
285 | DEFAULT_ENCRYPTION_KEY_FILE_NAME, | |
286 | "default encryption key file", | |
287 | ) | |
288 | } | |
289 | ||
290 | #[cfg(not(test))] | |
291 | pub(crate) fn read_optional_default_encryption_key() -> Result<Option<KeyWithSource>, Error> { | |
292 | find_default_encryption_key()? | |
293 | .map(|path| file_get_contents(path).map(KeyWithSource::from_default)) | |
294 | .transpose() | |
295 | } | |
296 | ||
297 | #[cfg(not(test))] | |
298 | pub(crate) fn read_optional_default_master_pubkey() -> Result<Option<KeyWithSource>, Error> { | |
299 | find_default_master_pubkey()? | |
300 | .map(|path| file_get_contents(path).map(KeyWithSource::from_default)) | |
301 | .transpose() | |
302 | } | |
303 | ||
304 | #[cfg(test)] | |
305 | static mut TEST_DEFAULT_ENCRYPTION_KEY: Result<Option<Vec<u8>>, Error> = Ok(None); | |
306 | ||
307 | #[cfg(test)] | |
308 | pub(crate) fn read_optional_default_encryption_key() -> Result<Option<KeyWithSource>, Error> { | |
309 | // not safe when multiple concurrent test cases end up here! | |
310 | unsafe { | |
311 | match &TEST_DEFAULT_ENCRYPTION_KEY { | |
312 | Ok(Some(key)) => Ok(Some(KeyWithSource::from_default(key.clone()))), | |
313 | Ok(None) => Ok(None), | |
314 | Err(_) => bail!("test error"), | |
315 | } | |
316 | } | |
317 | } | |
318 | ||
319 | #[cfg(test)] | |
320 | // not safe when multiple concurrent test cases end up here! | |
321 | pub(crate) unsafe fn set_test_encryption_key(value: Result<Option<Vec<u8>>, Error>) { | |
322 | TEST_DEFAULT_ENCRYPTION_KEY = value; | |
323 | } | |
324 | ||
325 | #[cfg(test)] | |
326 | static mut TEST_DEFAULT_MASTER_PUBKEY: Result<Option<Vec<u8>>, Error> = Ok(None); | |
327 | ||
328 | #[cfg(test)] | |
329 | pub(crate) fn read_optional_default_master_pubkey() -> Result<Option<KeyWithSource>, Error> { | |
330 | // not safe when multiple concurrent test cases end up here! | |
331 | unsafe { | |
332 | match &TEST_DEFAULT_MASTER_PUBKEY { | |
333 | Ok(Some(key)) => Ok(Some(KeyWithSource::from_default(key.clone()))), | |
334 | Ok(None) => Ok(None), | |
335 | Err(_) => bail!("test error"), | |
336 | } | |
337 | } | |
338 | } | |
339 | ||
340 | #[cfg(test)] | |
341 | // not safe when multiple concurrent test cases end up here! | |
342 | pub(crate) unsafe fn set_test_default_master_pubkey(value: Result<Option<Vec<u8>>, Error>) { | |
343 | TEST_DEFAULT_MASTER_PUBKEY = value; | |
344 | } | |
345 | ||
346 | pub fn get_encryption_key_password() -> Result<Vec<u8>, Error> { | |
347 | // fixme: implement other input methods | |
348 | ||
16a01c19 DM |
349 | if let Some(password) = super::get_secret_from_env("PBS_ENCRYPTION_PASSWORD")? { |
350 | return Ok(password.as_bytes().to_vec()); | |
ff8945fd SR |
351 | } |
352 | ||
353 | // If we're on a TTY, query the user for a password | |
2711e94e | 354 | if std::io::stdin().is_terminal() { |
dcf5a0f6 | 355 | return tty::read_password("Encryption Key Password: "); |
ff8945fd SR |
356 | } |
357 | ||
358 | bail!("no password input mechanism available"); | |
359 | } | |
360 | ||
a3399f43 WB |
361 | #[cfg(test)] |
362 | fn create_testdir(name: &str) -> Result<String, Error> { | |
4d04cd9a WB |
363 | // FIXME: |
364 | //let mut testdir: PathBuf = format!("{}/testout", env!("CARGO_TARGET_TMPDIR")).into(); | |
365 | let mut testdir: PathBuf = "./target/testout".to_string().into(); | |
a3399f43 WB |
366 | testdir.push(std::module_path!()); |
367 | testdir.push(name); | |
368 | ||
369 | let _ = std::fs::remove_dir_all(&testdir); | |
370 | let _ = std::fs::create_dir_all(&testdir); | |
371 | ||
372 | Ok(testdir.to_str().unwrap().to_string()) | |
373 | } | |
374 | ||
ff8945fd SR |
375 | #[test] |
376 | // WARNING: there must only be one test for crypto_parameters as the default key handling is not | |
377 | // safe w.r.t. concurrency | |
378 | fn test_crypto_parameters_handling() -> Result<(), Error> { | |
25877d05 | 379 | use proxmox_sys::fs::{replace_file, CreateOptions}; |
bdfa6370 | 380 | use serde_json::json; |
ff8945fd | 381 | |
bdfa6370 TL |
382 | let some_key = vec![1; 1]; |
383 | let default_key = vec![2; 1]; | |
ff8945fd | 384 | |
bdfa6370 TL |
385 | let some_master_key = vec![3; 1]; |
386 | let default_master_key = vec![4; 1]; | |
ff8945fd | 387 | |
a3399f43 WB |
388 | let testdir = create_testdir("key_source")?; |
389 | ||
390 | let keypath = format!("{}/keyfile.test", testdir); | |
391 | let master_keypath = format!("{}/masterkeyfile.test", testdir); | |
392 | let invalid_keypath = format!("{}/invalid_keyfile.test", testdir); | |
ff8945fd SR |
393 | |
394 | let no_key_res = CryptoParams { | |
395 | enc_key: None, | |
396 | master_pubkey: None, | |
397 | mode: CryptMode::None, | |
398 | }; | |
399 | let some_key_res = CryptoParams { | |
400 | enc_key: Some(KeyWithSource::from_path( | |
401 | keypath.to_string(), | |
402 | some_key.clone(), | |
403 | )), | |
404 | master_pubkey: None, | |
405 | mode: CryptMode::Encrypt, | |
406 | }; | |
407 | let some_key_some_master_res = CryptoParams { | |
408 | enc_key: Some(KeyWithSource::from_path( | |
409 | keypath.to_string(), | |
410 | some_key.clone(), | |
411 | )), | |
412 | master_pubkey: Some(KeyWithSource::from_path( | |
413 | master_keypath.to_string(), | |
414 | some_master_key.clone(), | |
415 | )), | |
416 | mode: CryptMode::Encrypt, | |
417 | }; | |
418 | let some_key_default_master_res = CryptoParams { | |
419 | enc_key: Some(KeyWithSource::from_path( | |
420 | keypath.to_string(), | |
421 | some_key.clone(), | |
422 | )), | |
423 | master_pubkey: Some(KeyWithSource::from_default(default_master_key.clone())), | |
424 | mode: CryptMode::Encrypt, | |
425 | }; | |
426 | ||
427 | let some_key_sign_res = CryptoParams { | |
428 | enc_key: Some(KeyWithSource::from_path( | |
429 | keypath.to_string(), | |
430 | some_key.clone(), | |
431 | )), | |
432 | master_pubkey: None, | |
433 | mode: CryptMode::SignOnly, | |
434 | }; | |
435 | let default_key_res = CryptoParams { | |
436 | enc_key: Some(KeyWithSource::from_default(default_key.clone())), | |
437 | master_pubkey: None, | |
438 | mode: CryptMode::Encrypt, | |
439 | }; | |
440 | let default_key_sign_res = CryptoParams { | |
441 | enc_key: Some(KeyWithSource::from_default(default_key.clone())), | |
442 | master_pubkey: None, | |
443 | mode: CryptMode::SignOnly, | |
444 | }; | |
445 | ||
e0a19d33 | 446 | replace_file(&keypath, &some_key, CreateOptions::default(), false)?; |
bdfa6370 TL |
447 | replace_file( |
448 | &master_keypath, | |
449 | &some_master_key, | |
450 | CreateOptions::default(), | |
451 | false, | |
452 | )?; | |
ff8945fd SR |
453 | |
454 | // no params, no default key == no key | |
455 | let res = crypto_parameters(&json!({})); | |
456 | assert_eq!(res.unwrap(), no_key_res); | |
457 | ||
458 | // keyfile param == key from keyfile | |
bdfa6370 | 459 | let res = crypto_parameters(&json!({ "keyfile": keypath })); |
ff8945fd SR |
460 | assert_eq!(res.unwrap(), some_key_res); |
461 | ||
462 | // crypt mode none == no key | |
463 | let res = crypto_parameters(&json!({"crypt-mode": "none"})); | |
464 | assert_eq!(res.unwrap(), no_key_res); | |
465 | ||
466 | // crypt mode encrypt/sign-only, no keyfile, no default key == Error | |
467 | assert!(crypto_parameters(&json!({"crypt-mode": "sign-only"})).is_err()); | |
468 | assert!(crypto_parameters(&json!({"crypt-mode": "encrypt"})).is_err()); | |
469 | ||
470 | // crypt mode none with explicit key == Error | |
471 | assert!(crypto_parameters(&json!({"crypt-mode": "none", "keyfile": keypath})).is_err()); | |
472 | ||
473 | // crypt mode sign-only/encrypt with keyfile == key from keyfile with correct mode | |
474 | let res = crypto_parameters(&json!({"crypt-mode": "sign-only", "keyfile": keypath})); | |
475 | assert_eq!(res.unwrap(), some_key_sign_res); | |
476 | let res = crypto_parameters(&json!({"crypt-mode": "encrypt", "keyfile": keypath})); | |
477 | assert_eq!(res.unwrap(), some_key_res); | |
478 | ||
479 | // invalid keyfile parameter always errors | |
bdfa6370 | 480 | assert!(crypto_parameters(&json!({ "keyfile": invalid_keypath })).is_err()); |
ff8945fd | 481 | assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "none"})).is_err()); |
bdfa6370 TL |
482 | assert!( |
483 | crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err() | |
484 | ); | |
485 | assert!( | |
486 | crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err() | |
487 | ); | |
ff8945fd SR |
488 | |
489 | // now set a default key | |
bdfa6370 TL |
490 | unsafe { |
491 | set_test_encryption_key(Ok(Some(default_key))); | |
492 | } | |
ff8945fd SR |
493 | |
494 | // and repeat | |
495 | ||
496 | // no params but default key == default key | |
497 | let res = crypto_parameters(&json!({})); | |
498 | assert_eq!(res.unwrap(), default_key_res); | |
499 | ||
500 | // keyfile param == key from keyfile | |
bdfa6370 | 501 | let res = crypto_parameters(&json!({ "keyfile": keypath })); |
ff8945fd SR |
502 | assert_eq!(res.unwrap(), some_key_res); |
503 | ||
504 | // crypt mode none == no key | |
505 | let res = crypto_parameters(&json!({"crypt-mode": "none"})); | |
506 | assert_eq!(res.unwrap(), no_key_res); | |
507 | ||
508 | // crypt mode encrypt/sign-only, no keyfile, default key == default key with correct mode | |
509 | let res = crypto_parameters(&json!({"crypt-mode": "sign-only"})); | |
510 | assert_eq!(res.unwrap(), default_key_sign_res); | |
511 | let res = crypto_parameters(&json!({"crypt-mode": "encrypt"})); | |
512 | assert_eq!(res.unwrap(), default_key_res); | |
513 | ||
514 | // crypt mode none with explicit key == Error | |
515 | assert!(crypto_parameters(&json!({"crypt-mode": "none", "keyfile": keypath})).is_err()); | |
516 | ||
517 | // crypt mode sign-only/encrypt with keyfile == key from keyfile with correct mode | |
518 | let res = crypto_parameters(&json!({"crypt-mode": "sign-only", "keyfile": keypath})); | |
519 | assert_eq!(res.unwrap(), some_key_sign_res); | |
520 | let res = crypto_parameters(&json!({"crypt-mode": "encrypt", "keyfile": keypath})); | |
521 | assert_eq!(res.unwrap(), some_key_res); | |
522 | ||
523 | // invalid keyfile parameter always errors | |
bdfa6370 | 524 | assert!(crypto_parameters(&json!({ "keyfile": invalid_keypath })).is_err()); |
ff8945fd | 525 | assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "none"})).is_err()); |
bdfa6370 TL |
526 | assert!( |
527 | crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err() | |
528 | ); | |
529 | assert!( | |
530 | crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err() | |
531 | ); | |
ff8945fd SR |
532 | |
533 | // now make default key retrieval error | |
bdfa6370 TL |
534 | unsafe { |
535 | set_test_encryption_key(Err(format_err!("test error"))); | |
536 | } | |
ff8945fd SR |
537 | |
538 | // and repeat | |
539 | ||
540 | // no params, default key retrieval errors == Error | |
541 | assert!(crypto_parameters(&json!({})).is_err()); | |
542 | ||
543 | // keyfile param == key from keyfile | |
bdfa6370 | 544 | let res = crypto_parameters(&json!({ "keyfile": keypath })); |
ff8945fd SR |
545 | assert_eq!(res.unwrap(), some_key_res); |
546 | ||
547 | // crypt mode none == no key | |
548 | let res = crypto_parameters(&json!({"crypt-mode": "none"})); | |
549 | assert_eq!(res.unwrap(), no_key_res); | |
550 | ||
551 | // crypt mode encrypt/sign-only, no keyfile, default key error == Error | |
552 | assert!(crypto_parameters(&json!({"crypt-mode": "sign-only"})).is_err()); | |
553 | assert!(crypto_parameters(&json!({"crypt-mode": "encrypt"})).is_err()); | |
554 | ||
555 | // crypt mode none with explicit key == Error | |
556 | assert!(crypto_parameters(&json!({"crypt-mode": "none", "keyfile": keypath})).is_err()); | |
557 | ||
558 | // crypt mode sign-only/encrypt with keyfile == key from keyfile with correct mode | |
559 | let res = crypto_parameters(&json!({"crypt-mode": "sign-only", "keyfile": keypath})); | |
560 | assert_eq!(res.unwrap(), some_key_sign_res); | |
561 | let res = crypto_parameters(&json!({"crypt-mode": "encrypt", "keyfile": keypath})); | |
562 | assert_eq!(res.unwrap(), some_key_res); | |
563 | ||
564 | // invalid keyfile parameter always errors | |
bdfa6370 | 565 | assert!(crypto_parameters(&json!({ "keyfile": invalid_keypath })).is_err()); |
ff8945fd | 566 | assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "none"})).is_err()); |
bdfa6370 TL |
567 | assert!( |
568 | crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err() | |
569 | ); | |
570 | assert!( | |
571 | crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err() | |
572 | ); | |
ff8945fd SR |
573 | |
574 | // now remove default key again | |
bdfa6370 TL |
575 | unsafe { |
576 | set_test_encryption_key(Ok(None)); | |
577 | } | |
ff8945fd | 578 | // set a default master key |
bdfa6370 TL |
579 | unsafe { |
580 | set_test_default_master_pubkey(Ok(Some(default_master_key))); | |
581 | } | |
ff8945fd SR |
582 | |
583 | // and use an explicit master key | |
bdfa6370 | 584 | assert!(crypto_parameters(&json!({ "master-pubkey-file": master_keypath })).is_err()); |
ff8945fd SR |
585 | // just a default == no key |
586 | let res = crypto_parameters(&json!({})); | |
587 | assert_eq!(res.unwrap(), no_key_res); | |
588 | ||
589 | // keyfile param == key from keyfile | |
590 | let res = crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": master_keypath})); | |
591 | assert_eq!(res.unwrap(), some_key_some_master_res); | |
592 | // same with fallback to default master key | |
bdfa6370 | 593 | let res = crypto_parameters(&json!({ "keyfile": keypath })); |
ff8945fd SR |
594 | assert_eq!(res.unwrap(), some_key_default_master_res); |
595 | ||
596 | // crypt mode none == error | |
bdfa6370 TL |
597 | assert!(crypto_parameters( |
598 | &json!({"crypt-mode": "none", "master-pubkey-file": master_keypath}) | |
599 | ) | |
600 | .is_err()); | |
ff8945fd SR |
601 | // with just default master key == no key |
602 | let res = crypto_parameters(&json!({"crypt-mode": "none"})); | |
603 | assert_eq!(res.unwrap(), no_key_res); | |
604 | ||
605 | // crypt mode encrypt without enc key == error | |
bdfa6370 TL |
606 | assert!(crypto_parameters( |
607 | &json!({"crypt-mode": "encrypt", "master-pubkey-file": master_keypath}) | |
608 | ) | |
609 | .is_err()); | |
ff8945fd SR |
610 | assert!(crypto_parameters(&json!({"crypt-mode": "encrypt"})).is_err()); |
611 | ||
612 | // crypt mode none with explicit key == Error | |
bdfa6370 TL |
613 | assert!(crypto_parameters( |
614 | &json!({"crypt-mode": "none", "keyfile": keypath, "master-pubkey-file": master_keypath}) | |
615 | ) | |
616 | .is_err()); | |
ff8945fd SR |
617 | assert!(crypto_parameters(&json!({"crypt-mode": "none", "keyfile": keypath})).is_err()); |
618 | ||
619 | // crypt mode encrypt with keyfile == key from keyfile with correct mode | |
bdfa6370 TL |
620 | let res = crypto_parameters( |
621 | &json!({"crypt-mode": "encrypt", "keyfile": keypath, "master-pubkey-file": master_keypath}), | |
622 | ); | |
ff8945fd SR |
623 | assert_eq!(res.unwrap(), some_key_some_master_res); |
624 | let res = crypto_parameters(&json!({"crypt-mode": "encrypt", "keyfile": keypath})); | |
625 | assert_eq!(res.unwrap(), some_key_default_master_res); | |
626 | ||
627 | // invalid master keyfile parameter always errors when a key is passed, even with a valid | |
628 | // default master key | |
bdfa6370 TL |
629 | assert!( |
630 | crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath})) | |
631 | .is_err() | |
632 | ); | |
633 | assert!(crypto_parameters( | |
634 | &json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath,"crypt-mode": "none"}) | |
635 | ) | |
636 | .is_err()); | |
ff8945fd | 637 | assert!(crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath,"crypt-mode": "sign-only"})).is_err()); |
bdfa6370 TL |
638 | assert!(crypto_parameters( |
639 | &json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath,"crypt-mode": "encrypt"}) | |
640 | ) | |
641 | .is_err()); | |
ff8945fd SR |
642 | |
643 | Ok(()) | |
644 | } |