From 69b8bc3bfa9aba671d83905640fc6befea5e77a0 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Wed, 20 Jan 2021 10:20:41 +0100 Subject: [PATCH] tape: implemenmt show key Moved API types Kdf and KeyInfo to src/api2/types/mod.rs. --- src/api2/config/tape_encryption_keys.rs | 50 +++++++++++++++++++------ src/api2/types/mod.rs | 50 +++++++++++++++++++++++++ src/api2/types/tape/drive.rs | 18 +-------- src/backup/key_derivation.rs | 43 +++++++++++---------- src/bin/proxmox_backup_client/key.rs | 46 ++--------------------- src/bin/proxmox_tape/encryption_key.rs | 48 +++++++++++++++++++++++- src/config/tape_encryption_keys.rs | 3 +- 7 files changed, 163 insertions(+), 95 deletions(-) diff --git a/src/api2/config/tape_encryption_keys.rs b/src/api2/config/tape_encryption_keys.rs index 422481c5..daa8621b 100644 --- a/src/api2/config/tape_encryption_keys.rs +++ b/src/api2/config/tape_encryption_keys.rs @@ -27,14 +27,13 @@ use crate::{ TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA, PROXMOX_CONFIG_DIGEST_SCHEMA, PASSWORD_HINT_SCHEMA, - TapeKeyMetadata, + KeyInfo, + Kdf, }, backup::{ KeyConfig, - Kdf, Fingerprint, }, - tools::format::as_fingerprint, }; #[api( @@ -44,7 +43,7 @@ use crate::{ returns: { description: "The list of tape encryption keys (with config digest).", type: Array, - items: { type: TapeKeyMetadata }, + items: { type: KeyInfo }, }, )] /// List existing keys @@ -52,17 +51,14 @@ pub fn list_keys( _param: Value, _info: &ApiMethod, mut rpcenv: &mut dyn RpcEnvironment, -) -> Result, Error> { +) -> Result, Error> { let (key_map, digest) = load_key_configs()?; let mut list = Vec::new(); - for (fingerprint, item) in key_map { - list.push(TapeKeyMetadata { - hint: item.hint.unwrap_or(String::new()), - fingerprint: as_fingerprint(fingerprint.bytes()), - }); + for (_fingerprint, item) in key_map.iter() { + list.push(item.into()); } rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); @@ -192,6 +188,38 @@ pub fn create_key( } +#[api( + input: { + properties: { + fingerprint: { + schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA, + }, + }, + }, + returns: { + type: KeyInfo, + }, +)] +/// Get key config (public key part) +pub fn read_key( + fingerprint: Fingerprint, + _rpcenv: &mut dyn RpcEnvironment, +) -> Result { + + let (config_map, _digest) = load_key_configs()?; + + let key_config = match config_map.get(&fingerprint) { + Some(key_config) => key_config, + None => bail!("tape encryption key '{}' does not exist.", fingerprint), + }; + + if key_config.kdf.is_none() { + bail!("found unencrypted key - internal error"); + } + + Ok(key_config.into()) +} + #[api( protected: true, input: { @@ -242,7 +270,7 @@ pub fn delete_key( } const ITEM_ROUTER: Router = Router::new() - //.get(&API_METHOD_READ_KEY_METADATA) + .get(&API_METHOD_READ_KEY) .put(&API_METHOD_CHANGE_PASSPHRASE) .delete(&API_METHOD_DELETE_KEY); diff --git a/src/api2/types/mod.rs b/src/api2/types/mod.rs index 07289ca1..52109ef4 100644 --- a/src/api2/types/mod.rs +++ b/src/api2/types/mod.rs @@ -1256,3 +1256,53 @@ pub const PASSWORD_HINT_SCHEMA: Schema = StringSchema::new("Password hint.") .min_length(1) .max_length(64) .schema(); + +#[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 + } +} + +#[api( + properties: { + kdf: { + type: Kdf, + }, + fingerprint: { + schema: CERT_FINGERPRINT_SHA256_SCHEMA, + optional: true, + }, + }, +)] +#[derive(Deserialize, Serialize)] +/// Encryption Key Information +pub struct KeyInfo { + /// Path to key (if stored in a file) + #[serde(skip_serializing_if="Option::is_none")] + pub path: Option, + pub kdf: Kdf, + /// Key creation time + pub created: i64, + /// Key modification time + pub modified: i64, + /// Key fingerprint + #[serde(skip_serializing_if="Option::is_none")] + pub fingerprint: Option, + /// Password hint + #[serde(skip_serializing_if="Option::is_none")] + pub hint: Option, +} diff --git a/src/api2/types/tape/drive.rs b/src/api2/types/tape/drive.rs index f90d22ba..d1d09ed1 100644 --- a/src/api2/types/tape/drive.rs +++ b/src/api2/types/tape/drive.rs @@ -12,8 +12,7 @@ use proxmox::api::{ use crate::api2::types::{ PROXMOX_SAFE_ID_FORMAT, CHANGER_NAME_SCHEMA, - CERT_FINGERPRINT_SHA256_SCHEMA, - }; +}; pub const DRIVE_NAME_SCHEMA: Schema = StringSchema::new("Drive Identifier.") .format(&PROXMOX_SAFE_ID_FORMAT) @@ -206,18 +205,3 @@ pub struct LinuxDriveAndMediaStatus { #[serde(skip_serializing_if="Option::is_none")] pub medium_passes: Option, } - -#[api( - properties: { - fingerprint: { - schema: CERT_FINGERPRINT_SHA256_SCHEMA, - }, - }, -)] -#[derive(Deserialize, Serialize)] -/// Hardware Encryption key Metadata -pub struct TapeKeyMetadata { - /// Password hint - pub hint: String, - pub fingerprint: String, -} diff --git a/src/backup/key_derivation.rs b/src/backup/key_derivation.rs index 34fc4a11..ad4a5b6a 100644 --- a/src/backup/key_derivation.rs +++ b/src/backup/key_derivation.rs @@ -6,31 +6,10 @@ use crate::backup::{CryptConfig, Fingerprint}; use std::io::Write; use std::path::Path; -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 - } -} +use crate::api2::types::{KeyInfo, Kdf}; #[derive(Deserialize, Serialize, Clone, Debug)] pub enum KeyDerivationConfig { @@ -101,6 +80,26 @@ pub struct KeyConfig { pub hint: Option, } +impl From<&KeyConfig> for KeyInfo { + fn from(key_config: &KeyConfig) -> Self { + Self { + path: None, + kdf: match key_config.kdf { + Some(KeyDerivationConfig::PBKDF2 { .. }) => Kdf::PBKDF2, + Some(KeyDerivationConfig::Scrypt { .. }) => Kdf::Scrypt, + None => Kdf::None, + }, + created: key_config.created, + modified: key_config.modified, + fingerprint: key_config + .fingerprint + .as_ref() + .map(|fp| crate::tools::format::as_fingerprint(fp.bytes())), + hint: key_config.hint.clone(), + } + } +} + impl KeyConfig { pub fn new(passphrase: &[u8], kdf: Kdf) -> Result<([u8;32], Self), Error> { diff --git a/src/bin/proxmox_backup_client/key.rs b/src/bin/proxmox_backup_client/key.rs index 8dfedea0..d136309b 100644 --- a/src/bin/proxmox_backup_client/key.rs +++ b/src/bin/proxmox_backup_client/key.rs @@ -22,13 +22,13 @@ use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions}; use proxmox_backup::{ api2::types::{ PASSWORD_HINT_SCHEMA, + KeyInfo, + Kdf, }, backup::{ rsa_decrypt_key_config, CryptConfig, - Kdf, KeyConfig, - KeyDerivationConfig, }, tools, }; @@ -318,31 +318,6 @@ fn change_passphrase( 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 - #[serde(skip_serializing_if="Option::is_none")] - pub fingerprint: Option, - /// Password hint - #[serde(skip_serializing_if="Option::is_none")] - pub hint: Option, -} - #[api( input: { properties: { @@ -378,21 +353,8 @@ fn show_key( 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, - }, - hint: config.hint, - }; + let mut info: KeyInfo = (&config).into(); + info.path = Some(format!("{:?}", path)); let options = proxmox::api::cli::default_table_format_options() .column(ColumnConfig::new("path")) diff --git a/src/bin/proxmox_tape/encryption_key.rs b/src/bin/proxmox_tape/encryption_key.rs index a7f0c38d..e45f2552 100644 --- a/src/bin/proxmox_tape/encryption_key.rs +++ b/src/bin/proxmox_tape/encryption_key.rs @@ -12,6 +12,7 @@ use proxmox::{ }; use proxmox_backup::{ + tools, config, api2::{ self, @@ -19,9 +20,9 @@ use proxmox_backup::{ DRIVE_NAME_SCHEMA, TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA, PASSWORD_HINT_SCHEMA, + Kdf, }, }, - backup::Kdf, config::tape_encryption_keys::complete_key_fingerprint, }; @@ -39,6 +40,12 @@ pub fn encryption_key_commands() -> CommandLineInterface { .arg_param(&["fingerprint"]) .completion_cb("fingerprint", complete_key_fingerprint) ) + .insert( + "show", + CliCommand::new(&API_METHOD_SHOW_KEY) + .arg_param(&["fingerprint"]) + .completion_cb("fingerprint", complete_key_fingerprint) + ) .insert( "restore", CliCommand::new(&API_METHOD_RESTORE_KEY) @@ -54,6 +61,45 @@ pub fn encryption_key_commands() -> CommandLineInterface { cmd_def.into() } +#[api( + input: { + properties: { + fingerprint: { + schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA, + }, + "output-format": { + schema: OUTPUT_FORMAT, + optional: true, + }, + }, + }, +)] +/// Print tthe encryption key's metadata. +fn show_key( + param: Value, + rpcenv: &mut dyn RpcEnvironment, +) -> Result<(), Error> { + + let output_format = get_output_format(¶m); + + let info = &api2::config::tape_encryption_keys::API_METHOD_READ_KEY; + let mut data = match info.handler { + ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?, + _ => unreachable!(), + }; + + let options = proxmox::api::cli::default_table_format_options() + .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")) + .column(ColumnConfig::new("hint")); + + format_and_print_result_full(&mut data, &info.returns, &output_format, &options); + + Ok(()) +} + #[api( input: { properties: { diff --git a/src/config/tape_encryption_keys.rs b/src/config/tape_encryption_keys.rs index 7c1a8460..3e94dfa0 100644 --- a/src/config/tape_encryption_keys.rs +++ b/src/config/tape_encryption_keys.rs @@ -11,9 +11,9 @@ use proxmox::tools::fs::{ }; use crate::{ + api2::types::Kdf, backup::{ Fingerprint, - Kdf, KeyConfig, CryptConfig, }, @@ -204,7 +204,6 @@ pub fn insert_key(key: [u8;32], key_config: KeyConfig) -> Result<(), Error> { save_key_configs(config_map)?; Ok(()) - } // shell completion helper