public inbox for pbs-devel@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal