all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: "Fabian Grünbichler" <f.gruenbichler@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [PATCH proxmox-backup 4/7] client: add 'import-with-master-key' command
Date: Wed, 16 Dec 2020 14:41:08 +0100	[thread overview]
Message-ID: <20201216134111.445581-5-f.gruenbichler@proxmox.com> (raw)
In-Reply-To: <20201216134111.445581-1-f.gruenbichler@proxmox.com>

to import an encrypted encryption key using a master key.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@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





  parent reply	other threads:[~2020-12-16 13:42 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-16 13:41 [pbs-devel] [PATCH proxmox-backup 0/7] master key improvements Fabian Grünbichler
2020-12-16 13:41 ` [pbs-devel] [PATCH proxmox-backup 1/7] master key: store blob name in constant Fabian Grünbichler
2020-12-16 13:41 ` [pbs-devel] [PATCH proxmox-backup 2/7] fix #3197: skip fingerprint check when restoring key Fabian Grünbichler
2020-12-16 13:41 ` [pbs-devel] [PATCH proxmox-backup 3/7] key: move RSA-encryption to KeyConfig Fabian Grünbichler
2020-12-16 13:41 ` Fabian Grünbichler [this message]
2020-12-16 13:41 ` [pbs-devel] [PATCH proxmox-backup 5/7] docs: replace openssl command with client Fabian Grünbichler
2020-12-16 13:41 ` [pbs-devel] [PATCH proxmox-backup 6/7] KeyConfig: add encrypt/decrypt test Fabian Grünbichler
2020-12-16 13:41 ` [pbs-devel] [RFC proxmox-backup 7/7] KeyConfig: always calculate fingerprint Fabian Grünbichler
2020-12-17  5:55   ` Dietmar Maurer
2020-12-17 10:37     ` [pbs-devel] applied: " Fabian Grünbichler
2020-12-17  5:53 ` [pbs-devel] applied: [PATCH proxmox-backup 0/7] master key improvements Dietmar Maurer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201216134111.445581-5-f.gruenbichler@proxmox.com \
    --to=f.gruenbichler@proxmox.com \
    --cc=pbs-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal