From 5e17dbf2bbf24883809a7fbe1d7e30e9ace37c80 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Tue, 24 Nov 2020 07:19:15 +0100 Subject: [PATCH] cli: cleanup 'key show' - use format_and_print_result_full We now expose all key derivation functions on the cli, so users can choose between scrypt or pbkdf2. --- src/backup/key_derivation.rs | 43 ++++++++++-- src/bin/proxmox_backup_client/key.rs | 99 ++++++++++++++++------------ 2 files changed, 94 insertions(+), 48 deletions(-) diff --git a/src/backup/key_derivation.rs b/src/backup/key_derivation.rs index e7b266ab..1a5bc250 100644 --- a/src/backup/key_derivation.rs +++ b/src/backup/key_derivation.rs @@ -4,9 +4,32 @@ use serde::{Deserialize, Serialize}; use crate::backup::{CryptConfig, Fingerprint}; +use proxmox::api::api; use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions}; use proxmox::try_block; +#[api(default: "scrypt")] +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +/// Key derivation function for password protected encryption keys. +pub enum Kdf { + /// Do not encrypt the key. + None, + + /// Encrypt they key with a password using SCrypt. + Scrypt, + + /// Encrtypt the Key with a password using PBKDF2 + PBKDF2, +} + +impl Default for Kdf { + #[inline] + fn default() -> Self { + Kdf::Scrypt + } +} + #[derive(Deserialize, Serialize, Debug)] pub enum KeyDerivationConfig { Scrypt { @@ -108,15 +131,25 @@ pub fn store_key_config( pub fn encrypt_key_with_passphrase( raw_key: &[u8], passphrase: &[u8], + kdf: Kdf, ) -> Result { let salt = proxmox::sys::linux::random_data(32)?; - let kdf = KeyDerivationConfig::Scrypt { - n: 65536, - r: 8, - p: 1, - salt, + let kdf = match kdf { + Kdf::Scrypt => KeyDerivationConfig::Scrypt { + n: 65536, + r: 8, + p: 1, + salt, + }, + Kdf::PBKDF2 => KeyDerivationConfig::PBKDF2 { + iter: 65535, + salt, + }, + Kdf::None => { + bail!("No key derivation function specified"); + } }; let derived_key = kdf.derive_key(passphrase)?; diff --git a/src/bin/proxmox_backup_client/key.rs b/src/bin/proxmox_backup_client/key.rs index c7679393..e84f5b6b 100644 --- a/src/bin/proxmox_backup_client/key.rs +++ b/src/bin/proxmox_backup_client/key.rs @@ -8,9 +8,10 @@ use serde_json::Value; use proxmox::api::api; use proxmox::api::cli::{ + ColumnConfig, CliCommand, CliCommandMap, - format_and_print_result, + format_and_print_result_full, get_output_format, OUTPUT_FORMAT, }; @@ -22,6 +23,7 @@ use proxmox_backup::backup::{ load_and_decrypt_key, store_key_config, CryptConfig, + Kdf, KeyConfig, KeyDerivationConfig, }; @@ -83,27 +85,6 @@ pub fn get_encryption_key_password() -> Result, Error> { bail!("no password input mechanism available"); } -#[api( - default: "scrypt", -)] -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case")] -/// Key derivation function for password protected encryption keys. -pub enum Kdf { - /// Do not encrypt the key. - None, - - /// Encrypt they key with a password using SCrypt. - Scrypt, -} - -impl Default for Kdf { - #[inline] - fn default() -> Self { - Kdf::Scrypt - } -} - #[api( input: { properties: { @@ -153,7 +134,7 @@ fn create(kdf: Option, path: Option) -> Result<(), Error> { }, )?; } - Kdf::Scrypt => { + Kdf::Scrypt | Kdf::PBKDF2 => { // always read passphrase from tty if !tty::stdin_isatty() { bail!("unable to read passphrase - no tty"); @@ -161,7 +142,7 @@ fn create(kdf: Option, path: Option) -> Result<(), Error> { let password = tty::read_and_verify_password("Encryption Key Password: ")?; - let mut key_config = encrypt_key_with_passphrase(&key, &password)?; + let mut key_config = encrypt_key_with_passphrase(&key, &password, kdf)?; key_config.fingerprint = Some(crypt_config.fingerprint()); store_key_config(&path, false, key_config)?; @@ -223,10 +204,10 @@ fn change_passphrase(kdf: Option, path: Option) -> Result<(), Error }, )?; } - Kdf::Scrypt => { + Kdf::Scrypt | Kdf::PBKDF2 => { let password = tty::read_and_verify_password("New Password: ")?; - let mut new_key_config = encrypt_key_with_passphrase(&key, &password)?; + let mut new_key_config = encrypt_key_with_passphrase(&key, &password, kdf)?; new_key_config.created = created; // keep original value new_key_config.fingerprint = Some(fingerprint); @@ -237,6 +218,27 @@ fn change_passphrase(kdf: Option, path: Option) -> Result<(), Error Ok(()) } +#[api( + properties: { + kdf: { + type: Kdf, + }, + }, +)] +#[derive(Deserialize, Serialize)] +/// Encryption Key Information +struct KeyInfo { + /// Path to key + path: String, + kdf: Kdf, + /// Key creation time + pub created: i64, + /// Key modification time + pub modified: i64, + /// Key fingerprint + pub fingerprint: Option, +} + #[api( input: { properties: { @@ -267,25 +269,36 @@ fn show_key( } }; - let output_format = get_output_format(¶m); + let config: KeyConfig = serde_json::from_slice(&file_get_contents(path.clone())?)?; - if output_format == "text" { - println!("Path: {:?}", path); - match config.kdf { - Some(KeyDerivationConfig::PBKDF2 { .. }) => println!("KDF: pbkdf2"), - Some(KeyDerivationConfig::Scrypt { .. }) => println!("KDF: scrypt"), - None => println!("KDF: none (plaintext key)"), - }; - println!("Created: {}", proxmox::tools::time::epoch_to_rfc3339_utc(config.created)?); - println!("Modified: {}", proxmox::tools::time::epoch_to_rfc3339_utc(config.modified)?); - match config.fingerprint { - Some(fp) => println!("Fingerprint: {}", fp), - None => println!("Fingerprint: none (legacy key)"), - }; - } else { - format_and_print_result(&serde_json::to_value(config)?, &output_format); - } + let output_format = get_output_format(¶m); + + let info = KeyInfo { + path: format!("{:?}", path), + kdf: match config.kdf { + Some(KeyDerivationConfig::PBKDF2 { .. }) => Kdf::PBKDF2, + Some(KeyDerivationConfig::Scrypt { .. }) => Kdf::Scrypt, + None => Kdf::None, + }, + created: config.created, + modified: config.modified, + fingerprint: match config.fingerprint { + Some(ref fp) => Some(format!("{}", fp)), + None => None, + }, + }; + + let options = proxmox::api::cli::default_table_format_options() + .column(ColumnConfig::new("path")) + .column(ColumnConfig::new("kdf")) + .column(ColumnConfig::new("created").renderer(tools::format::render_epoch)) + .column(ColumnConfig::new("modified").renderer(tools::format::render_epoch)) + .column(ColumnConfig::new("fingerprint")); + + let schema = &KeyInfo::API_SCHEMA; + + format_and_print_result_full(&mut serde_json::to_value(info)?, schema, &output_format, &options); Ok(()) }