[pbs-devel] [PATCH proxmox-backup 4/7] client: add 'import-with-master-key' command
Fabian Grünbichler
f.gruenbichler at proxmox.com
Wed Dec 16 14:41:08 CET 2020
to import an encrypted encryption key using a master key.
Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
---
src/backup/key_derivation.rs | 12 ++++
src/bin/proxmox-backup-client.rs | 8 ---
src/bin/proxmox_backup_client/key.rs | 95 ++++++++++++++++++++++++++++
3 files changed, 107 insertions(+), 8 deletions(-)
diff --git a/src/backup/key_derivation.rs b/src/backup/key_derivation.rs
index a2aa9469..8289b86c 100644
--- a/src/backup/key_derivation.rs
+++ b/src/backup/key_derivation.rs
@@ -259,3 +259,15 @@ pub fn rsa_encrypt_key_config(
}
Ok(buffer)
}
+
+pub fn rsa_decrypt_key_config(
+ rsa: openssl::rsa::Rsa<openssl::pkey::Private>,
+ key: &[u8],
+ passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
+) -> Result<([u8; 32], i64, Fingerprint), Error> {
+ let mut buffer = vec![0u8; rsa.size() as usize];
+ let decrypted = rsa
+ .private_decrypt(key, &mut buffer, openssl::rsa::Padding::PKCS1)
+ .map_err(|err| format_err!("failed to decrypt KeyConfig using RSA - {}", err))?;
+ decrypt_key(&mut buffer[..decrypted], passphrase)
+}
diff --git a/src/bin/proxmox-backup-client.rs b/src/bin/proxmox-backup-client.rs
index c40bedc5..6cf81952 100644
--- a/src/bin/proxmox-backup-client.rs
+++ b/src/bin/proxmox-backup-client.rs
@@ -1081,14 +1081,6 @@ async fn create_backup(
.await?;
manifest.add_file(target.to_string(), stats.size, stats.csum, crypt_mode)?;
- // openssl rsautl -decrypt -inkey master-private.pem -in rsa-encrypted.key -out t
- /*
- let mut buffer2 = vec![0u8; rsa.size() as usize];
- let pem_data = file_get_contents("master-private.pem")?;
- let rsa = openssl::rsa::Rsa::private_key_from_pem(&pem_data)?;
- let len = rsa.private_decrypt(&buffer, &mut buffer2, openssl::rsa::Padding::PKCS1)?;
- println!("TEST {} {:?}", len, buffer2);
- */
}
// create manifest (index.json)
// manifests are never encrypted, but include a signature
diff --git a/src/bin/proxmox_backup_client/key.rs b/src/bin/proxmox_backup_client/key.rs
index 88fa5340..e49131c1 100644
--- a/src/bin/proxmox_backup_client/key.rs
+++ b/src/bin/proxmox_backup_client/key.rs
@@ -21,12 +21,14 @@ use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions};
use proxmox_backup::backup::{
encrypt_key_with_passphrase,
load_and_decrypt_key,
+ rsa_decrypt_key_config,
store_key_config,
CryptConfig,
Kdf,
KeyConfig,
KeyDerivationConfig,
};
+
use proxmox_backup::tools;
#[api()]
@@ -152,6 +154,90 @@ fn create(kdf: Option<Kdf>, path: Option<String>) -> Result<(), Error> {
Ok(())
}
+#[api(
+ input: {
+ properties: {
+ "master-keyfile": {
+ description: "(Private) master key to use.",
+ },
+ "encrypted-keyfile": {
+ description: "RSA-encrypted keyfile to import.",
+ },
+ kdf: {
+ type: Kdf,
+ optional: true,
+ },
+ "path": {
+ description:
+ "Output file. Without this the key will become the new default encryption key.",
+ optional: true,
+ }
+ },
+ },
+)]
+/// Import an encrypted backup of an encryption key using a (private) master key.
+async fn import_with_master_key(
+ master_keyfile: String,
+ encrypted_keyfile: String,
+ kdf: Option<Kdf>,
+ path: Option<String>,
+) -> Result<(), Error> {
+ let path = match path {
+ Some(path) => PathBuf::from(path),
+ None => {
+ let path = place_default_encryption_key()?;
+ if path.exists() {
+ bail!("Please remove default encryption key at {:?} before importing to default location (or choose a non-default one).", path);
+ }
+ println!("Importing key to default location at: {:?}", path);
+ path
+ }
+ };
+
+ let encrypted_key = file_get_contents(&encrypted_keyfile)?;
+ let master_key = file_get_contents(&master_keyfile)?;
+ let password = tty::read_password("Master Key Password: ")?;
+
+ let master_key =
+ openssl::pkey::PKey::private_key_from_pem_passphrase(&master_key, &password)
+ .map_err(|err| format_err!("failed to read PEM-formatted private key - {}", err))?
+ .rsa()
+ .map_err(|err| format_err!("not a valid private RSA key - {}", err))?;
+
+ let (key, created, fingerprint) =
+ rsa_decrypt_key_config(master_key, &encrypted_key, &get_encryption_key_password)?;
+
+ let kdf = kdf.unwrap_or_default();
+ match kdf {
+ Kdf::None => {
+ let modified = proxmox::tools::time::epoch_i64();
+
+ store_key_config(
+ &path,
+ true,
+ KeyConfig {
+ kdf: None,
+ created, // keep original value
+ modified,
+ data: key.to_vec(),
+ fingerprint: Some(fingerprint),
+ },
+ )?;
+ }
+ Kdf::Scrypt | Kdf::PBKDF2 => {
+ let password = tty::read_and_verify_password("New 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);
+
+ store_key_config(&path, true, new_key_config)?;
+ }
+ }
+
+ Ok(())
+}
+
#[api(
input: {
properties: {
@@ -446,6 +532,14 @@ pub fn cli() -> CliCommandMap {
.arg_param(&["path"])
.completion_cb("path", tools::complete_file_name);
+ let key_import_with_master_key_cmd_def = CliCommand::new(&API_METHOD_IMPORT_WITH_MASTER_KEY)
+ .arg_param(&["master-keyfile"])
+ .completion_cb("master-keyfile", tools::complete_file_name)
+ .arg_param(&["encrypted-keyfile"])
+ .completion_cb("encrypted-keyfile", tools::complete_file_name)
+ .arg_param(&["path"])
+ .completion_cb("path", tools::complete_file_name);
+
let key_change_passphrase_cmd_def = CliCommand::new(&API_METHOD_CHANGE_PASSPHRASE)
.arg_param(&["path"])
.completion_cb("path", tools::complete_file_name);
@@ -465,6 +559,7 @@ pub fn cli() -> CliCommandMap {
CliCommandMap::new()
.insert("create", key_create_cmd_def)
+ .insert("import-with-master-key", key_import_with_master_key_cmd_def)
.insert("create-master-key", key_create_master_key_cmd_def)
.insert("import-master-pubkey", key_import_master_pubkey_cmd_def)
.insert("change-passphrase", key_change_passphrase_cmd_def)
--
2.20.1
More information about the pbs-devel
mailing list