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