]>
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 | ||
9 | use proxmox::api::schema::*; | |
10 | use proxmox::sys::linux::tty; | |
11 | use proxmox::tools::fs::file_get_contents; | |
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 | ||
346 | use std::env::VarError::*; | |
347 | match std::env::var("PBS_ENCRYPTION_PASSWORD") { | |
348 | Ok(p) => return Ok(p.as_bytes().to_vec()), | |
349 | Err(NotUnicode(_)) => bail!("PBS_ENCRYPTION_PASSWORD contains bad characters"), | |
350 | Err(NotPresent) => { | |
351 | // Try another method | |
352 | } | |
353 | } | |
354 | ||
355 | // If we're on a TTY, query the user for a password | |
356 | if tty::stdin_isatty() { | |
357 | return Ok(tty::read_password("Encryption Key Password: ")?); | |
358 | } | |
359 | ||
360 | bail!("no password input mechanism available"); | |
361 | } | |
362 | ||
a3399f43 WB |
363 | #[cfg(test)] |
364 | fn create_testdir(name: &str) -> Result<String, Error> { | |
4d04cd9a WB |
365 | // FIXME: |
366 | //let mut testdir: PathBuf = format!("{}/testout", env!("CARGO_TARGET_TMPDIR")).into(); | |
367 | let mut testdir: PathBuf = "./target/testout".to_string().into(); | |
a3399f43 WB |
368 | testdir.push(std::module_path!()); |
369 | testdir.push(name); | |
370 | ||
371 | let _ = std::fs::remove_dir_all(&testdir); | |
372 | let _ = std::fs::create_dir_all(&testdir); | |
373 | ||
374 | Ok(testdir.to_str().unwrap().to_string()) | |
375 | } | |
376 | ||
ff8945fd SR |
377 | #[test] |
378 | // WARNING: there must only be one test for crypto_parameters as the default key handling is not | |
379 | // safe w.r.t. concurrency | |
380 | fn test_crypto_parameters_handling() -> Result<(), Error> { | |
381 | use serde_json::json; | |
382 | use proxmox::tools::fs::{replace_file, CreateOptions}; | |
383 | ||
384 | let some_key = vec![1;1]; | |
385 | let default_key = vec![2;1]; | |
386 | ||
387 | let some_master_key = vec![3;1]; | |
388 | let default_master_key = vec![4;1]; | |
389 | ||
a3399f43 WB |
390 | let testdir = create_testdir("key_source")?; |
391 | ||
392 | let keypath = format!("{}/keyfile.test", testdir); | |
393 | let master_keypath = format!("{}/masterkeyfile.test", testdir); | |
394 | let invalid_keypath = format!("{}/invalid_keyfile.test", testdir); | |
ff8945fd SR |
395 | |
396 | let no_key_res = CryptoParams { | |
397 | enc_key: None, | |
398 | master_pubkey: None, | |
399 | mode: CryptMode::None, | |
400 | }; | |
401 | let some_key_res = CryptoParams { | |
402 | enc_key: Some(KeyWithSource::from_path( | |
403 | keypath.to_string(), | |
404 | some_key.clone(), | |
405 | )), | |
406 | master_pubkey: None, | |
407 | mode: CryptMode::Encrypt, | |
408 | }; | |
409 | let some_key_some_master_res = CryptoParams { | |
410 | enc_key: Some(KeyWithSource::from_path( | |
411 | keypath.to_string(), | |
412 | some_key.clone(), | |
413 | )), | |
414 | master_pubkey: Some(KeyWithSource::from_path( | |
415 | master_keypath.to_string(), | |
416 | some_master_key.clone(), | |
417 | )), | |
418 | mode: CryptMode::Encrypt, | |
419 | }; | |
420 | let some_key_default_master_res = CryptoParams { | |
421 | enc_key: Some(KeyWithSource::from_path( | |
422 | keypath.to_string(), | |
423 | some_key.clone(), | |
424 | )), | |
425 | master_pubkey: Some(KeyWithSource::from_default(default_master_key.clone())), | |
426 | mode: CryptMode::Encrypt, | |
427 | }; | |
428 | ||
429 | let some_key_sign_res = CryptoParams { | |
430 | enc_key: Some(KeyWithSource::from_path( | |
431 | keypath.to_string(), | |
432 | some_key.clone(), | |
433 | )), | |
434 | master_pubkey: None, | |
435 | mode: CryptMode::SignOnly, | |
436 | }; | |
437 | let default_key_res = CryptoParams { | |
438 | enc_key: Some(KeyWithSource::from_default(default_key.clone())), | |
439 | master_pubkey: None, | |
440 | mode: CryptMode::Encrypt, | |
441 | }; | |
442 | let default_key_sign_res = CryptoParams { | |
443 | enc_key: Some(KeyWithSource::from_default(default_key.clone())), | |
444 | master_pubkey: None, | |
445 | mode: CryptMode::SignOnly, | |
446 | }; | |
447 | ||
448 | replace_file(&keypath, &some_key, CreateOptions::default())?; | |
449 | replace_file(&master_keypath, &some_master_key, CreateOptions::default())?; | |
450 | ||
451 | // no params, no default key == no key | |
452 | let res = crypto_parameters(&json!({})); | |
453 | assert_eq!(res.unwrap(), no_key_res); | |
454 | ||
455 | // keyfile param == key from keyfile | |
456 | let res = crypto_parameters(&json!({"keyfile": keypath})); | |
457 | assert_eq!(res.unwrap(), some_key_res); | |
458 | ||
459 | // crypt mode none == no key | |
460 | let res = crypto_parameters(&json!({"crypt-mode": "none"})); | |
461 | assert_eq!(res.unwrap(), no_key_res); | |
462 | ||
463 | // crypt mode encrypt/sign-only, no keyfile, no default key == Error | |
464 | assert!(crypto_parameters(&json!({"crypt-mode": "sign-only"})).is_err()); | |
465 | assert!(crypto_parameters(&json!({"crypt-mode": "encrypt"})).is_err()); | |
466 | ||
467 | // crypt mode none with explicit key == Error | |
468 | assert!(crypto_parameters(&json!({"crypt-mode": "none", "keyfile": keypath})).is_err()); | |
469 | ||
470 | // crypt mode sign-only/encrypt with keyfile == key from keyfile with correct mode | |
471 | let res = crypto_parameters(&json!({"crypt-mode": "sign-only", "keyfile": keypath})); | |
472 | assert_eq!(res.unwrap(), some_key_sign_res); | |
473 | let res = crypto_parameters(&json!({"crypt-mode": "encrypt", "keyfile": keypath})); | |
474 | assert_eq!(res.unwrap(), some_key_res); | |
475 | ||
476 | // invalid keyfile parameter always errors | |
477 | assert!(crypto_parameters(&json!({"keyfile": invalid_keypath})).is_err()); | |
478 | assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "none"})).is_err()); | |
479 | assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err()); | |
480 | assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err()); | |
481 | ||
482 | // now set a default key | |
483 | unsafe { set_test_encryption_key(Ok(Some(default_key.clone()))); } | |
484 | ||
485 | // and repeat | |
486 | ||
487 | // no params but default key == default key | |
488 | let res = crypto_parameters(&json!({})); | |
489 | assert_eq!(res.unwrap(), default_key_res); | |
490 | ||
491 | // keyfile param == key from keyfile | |
492 | let res = crypto_parameters(&json!({"keyfile": keypath})); | |
493 | assert_eq!(res.unwrap(), some_key_res); | |
494 | ||
495 | // crypt mode none == no key | |
496 | let res = crypto_parameters(&json!({"crypt-mode": "none"})); | |
497 | assert_eq!(res.unwrap(), no_key_res); | |
498 | ||
499 | // crypt mode encrypt/sign-only, no keyfile, default key == default key with correct mode | |
500 | let res = crypto_parameters(&json!({"crypt-mode": "sign-only"})); | |
501 | assert_eq!(res.unwrap(), default_key_sign_res); | |
502 | let res = crypto_parameters(&json!({"crypt-mode": "encrypt"})); | |
503 | assert_eq!(res.unwrap(), default_key_res); | |
504 | ||
505 | // crypt mode none with explicit key == Error | |
506 | assert!(crypto_parameters(&json!({"crypt-mode": "none", "keyfile": keypath})).is_err()); | |
507 | ||
508 | // crypt mode sign-only/encrypt with keyfile == key from keyfile with correct mode | |
509 | let res = crypto_parameters(&json!({"crypt-mode": "sign-only", "keyfile": keypath})); | |
510 | assert_eq!(res.unwrap(), some_key_sign_res); | |
511 | let res = crypto_parameters(&json!({"crypt-mode": "encrypt", "keyfile": keypath})); | |
512 | assert_eq!(res.unwrap(), some_key_res); | |
513 | ||
514 | // invalid keyfile parameter always errors | |
515 | assert!(crypto_parameters(&json!({"keyfile": invalid_keypath})).is_err()); | |
516 | assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "none"})).is_err()); | |
517 | assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err()); | |
518 | assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err()); | |
519 | ||
520 | // now make default key retrieval error | |
521 | unsafe { set_test_encryption_key(Err(format_err!("test error"))); } | |
522 | ||
523 | // and repeat | |
524 | ||
525 | // no params, default key retrieval errors == Error | |
526 | assert!(crypto_parameters(&json!({})).is_err()); | |
527 | ||
528 | // keyfile param == key from keyfile | |
529 | let res = crypto_parameters(&json!({"keyfile": keypath})); | |
530 | assert_eq!(res.unwrap(), some_key_res); | |
531 | ||
532 | // crypt mode none == no key | |
533 | let res = crypto_parameters(&json!({"crypt-mode": "none"})); | |
534 | assert_eq!(res.unwrap(), no_key_res); | |
535 | ||
536 | // crypt mode encrypt/sign-only, no keyfile, default key error == Error | |
537 | assert!(crypto_parameters(&json!({"crypt-mode": "sign-only"})).is_err()); | |
538 | assert!(crypto_parameters(&json!({"crypt-mode": "encrypt"})).is_err()); | |
539 | ||
540 | // crypt mode none with explicit key == Error | |
541 | assert!(crypto_parameters(&json!({"crypt-mode": "none", "keyfile": keypath})).is_err()); | |
542 | ||
543 | // crypt mode sign-only/encrypt with keyfile == key from keyfile with correct mode | |
544 | let res = crypto_parameters(&json!({"crypt-mode": "sign-only", "keyfile": keypath})); | |
545 | assert_eq!(res.unwrap(), some_key_sign_res); | |
546 | let res = crypto_parameters(&json!({"crypt-mode": "encrypt", "keyfile": keypath})); | |
547 | assert_eq!(res.unwrap(), some_key_res); | |
548 | ||
549 | // invalid keyfile parameter always errors | |
550 | assert!(crypto_parameters(&json!({"keyfile": invalid_keypath})).is_err()); | |
551 | assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "none"})).is_err()); | |
552 | assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "sign-only"})).is_err()); | |
553 | assert!(crypto_parameters(&json!({"keyfile": invalid_keypath, "crypt-mode": "encrypt"})).is_err()); | |
554 | ||
555 | // now remove default key again | |
556 | unsafe { set_test_encryption_key(Ok(None)); } | |
557 | // set a default master key | |
558 | unsafe { set_test_default_master_pubkey(Ok(Some(default_master_key.clone()))); } | |
559 | ||
560 | // and use an explicit master key | |
561 | assert!(crypto_parameters(&json!({"master-pubkey-file": master_keypath})).is_err()); | |
562 | // just a default == no key | |
563 | let res = crypto_parameters(&json!({})); | |
564 | assert_eq!(res.unwrap(), no_key_res); | |
565 | ||
566 | // keyfile param == key from keyfile | |
567 | let res = crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": master_keypath})); | |
568 | assert_eq!(res.unwrap(), some_key_some_master_res); | |
569 | // same with fallback to default master key | |
570 | let res = crypto_parameters(&json!({"keyfile": keypath})); | |
571 | assert_eq!(res.unwrap(), some_key_default_master_res); | |
572 | ||
573 | // crypt mode none == error | |
574 | assert!(crypto_parameters(&json!({"crypt-mode": "none", "master-pubkey-file": master_keypath})).is_err()); | |
575 | // with just default master key == no key | |
576 | let res = crypto_parameters(&json!({"crypt-mode": "none"})); | |
577 | assert_eq!(res.unwrap(), no_key_res); | |
578 | ||
579 | // crypt mode encrypt without enc key == error | |
580 | assert!(crypto_parameters(&json!({"crypt-mode": "encrypt", "master-pubkey-file": master_keypath})).is_err()); | |
581 | assert!(crypto_parameters(&json!({"crypt-mode": "encrypt"})).is_err()); | |
582 | ||
583 | // crypt mode none with explicit key == Error | |
584 | assert!(crypto_parameters(&json!({"crypt-mode": "none", "keyfile": keypath, "master-pubkey-file": master_keypath})).is_err()); | |
585 | assert!(crypto_parameters(&json!({"crypt-mode": "none", "keyfile": keypath})).is_err()); | |
586 | ||
587 | // crypt mode encrypt with keyfile == key from keyfile with correct mode | |
588 | let res = crypto_parameters(&json!({"crypt-mode": "encrypt", "keyfile": keypath, "master-pubkey-file": master_keypath})); | |
589 | assert_eq!(res.unwrap(), some_key_some_master_res); | |
590 | let res = crypto_parameters(&json!({"crypt-mode": "encrypt", "keyfile": keypath})); | |
591 | assert_eq!(res.unwrap(), some_key_default_master_res); | |
592 | ||
593 | // invalid master keyfile parameter always errors when a key is passed, even with a valid | |
594 | // default master key | |
595 | assert!(crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath})).is_err()); | |
596 | assert!(crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath,"crypt-mode": "none"})).is_err()); | |
597 | assert!(crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath,"crypt-mode": "sign-only"})).is_err()); | |
598 | assert!(crypto_parameters(&json!({"keyfile": keypath, "master-pubkey-file": invalid_keypath,"crypt-mode": "encrypt"})).is_err()); | |
599 | ||
600 | Ok(()) | |
601 | } |