* [pbs-devel] [PATCH proxmox-backup 1/7] master key: store blob name in constant
2020-12-16 13:41 [pbs-devel] [PATCH proxmox-backup 0/7] master key improvements Fabian Grünbichler
@ 2020-12-16 13:41 ` 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
` (6 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Fabian Grünbichler @ 2020-12-16 13:41 UTC (permalink / raw)
To: pbs-devel
since we will use it in more than one place.
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---
src/backup/manifest.rs | 1 +
src/bin/proxmox-backup-client.rs | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/backup/manifest.rs b/src/backup/manifest.rs
index a64cbe15..8742cb0a 100644
--- a/src/backup/manifest.rs
+++ b/src/backup/manifest.rs
@@ -10,6 +10,7 @@ use crate::backup::{BackupDir, CryptMode, CryptConfig, Fingerprint};
pub const MANIFEST_BLOB_NAME: &str = "index.json.blob";
pub const MANIFEST_LOCK_NAME: &str = ".index.json.lck";
pub const CLIENT_LOG_BLOB_NAME: &str = "client.log.blob";
+pub const ENCRYPTED_KEY_BLOB_NAME: &str = "rsa-encrypted.key.blob";
mod hex_csum {
use serde::{self, Deserialize, Serializer, Deserializer};
diff --git a/src/bin/proxmox-backup-client.rs b/src/bin/proxmox-backup-client.rs
index 372ff268..1c456aab 100644
--- a/src/bin/proxmox-backup-client.rs
+++ b/src/bin/proxmox-backup-client.rs
@@ -54,6 +54,7 @@ use proxmox_backup::backup::{
CryptConfig,
CryptMode,
DynamicIndexReader,
+ ENCRYPTED_KEY_BLOB_NAME,
FixedChunkStream,
FixedIndexReader,
IndexFile,
@@ -1064,7 +1065,7 @@ async fn create_backup(
}
if let Some(rsa_encrypted_key) = rsa_encrypted_key {
- let target = "rsa-encrypted.key.blob";
+ let target = ENCRYPTED_KEY_BLOB_NAME;
println!("Upload RSA encoded key to '{:?}' as {}", repo, target);
let stats = client
.upload_blob_from_data(rsa_encrypted_key, target, false, false)
--
2.20.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pbs-devel] [PATCH proxmox-backup 2/7] fix #3197: skip fingerprint check when restoring key
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 ` Fabian Grünbichler
2020-12-16 13:41 ` [pbs-devel] [PATCH proxmox-backup 3/7] key: move RSA-encryption to KeyConfig Fabian Grünbichler
` (5 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Fabian Grünbichler @ 2020-12-16 13:41 UTC (permalink / raw)
To: pbs-devel
when restoring an encrypted key, the original one is obviously not
available to check the fingerprint with.
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---
src/bin/proxmox-backup-client.rs | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/bin/proxmox-backup-client.rs b/src/bin/proxmox-backup-client.rs
index 1c456aab..36da624e 100644
--- a/src/bin/proxmox-backup-client.rs
+++ b/src/bin/proxmox-backup-client.rs
@@ -1273,10 +1273,15 @@ async fn restore(param: Value) -> Result<Value, Error> {
true,
).await?;
+ let (archive_name, archive_type) = parse_archive_type(archive_name);
+
let (manifest, backup_index_data) = client.download_manifest().await?;
- manifest.check_fingerprint(crypt_config.as_ref().map(Arc::as_ref))?;
- let (archive_name, archive_type) = parse_archive_type(archive_name);
+ if archive_name == ENCRYPTED_KEY_BLOB_NAME && crypt_config.is_none() {
+ eprintln!("Restoring encrypted key blob without original key - skipping manifest fingerprint check!")
+ } else {
+ manifest.check_fingerprint(crypt_config.as_ref().map(Arc::as_ref))?;
+ }
if archive_name == MANIFEST_BLOB_NAME {
if let Some(target) = target {
--
2.20.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pbs-devel] [PATCH proxmox-backup 3/7] key: move RSA-encryption to KeyConfig
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 ` Fabian Grünbichler
2020-12-16 13:41 ` [pbs-devel] [PATCH proxmox-backup 4/7] client: add 'import-with-master-key' command Fabian Grünbichler
` (4 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Fabian Grünbichler @ 2020-12-16 13:41 UTC (permalink / raw)
To: pbs-devel
since that is what gets encrypted, and not a CryptConfig.
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---
src/backup/crypt_config.rs | 26 +-------------------------
src/backup/key_derivation.rs | 14 ++++++++++++++
src/bin/proxmox-backup-client.rs | 13 +++++++++++--
3 files changed, 26 insertions(+), 27 deletions(-)
diff --git a/src/backup/crypt_config.rs b/src/backup/crypt_config.rs
index 711f90f6..2b04380a 100644
--- a/src/backup/crypt_config.rs
+++ b/src/backup/crypt_config.rs
@@ -11,7 +11,7 @@ use std::fmt;
use std::fmt::Display;
use std::io::Write;
-use anyhow::{bail, Error};
+use anyhow::{Error};
use openssl::hash::MessageDigest;
use openssl::pkcs5::pbkdf2_hmac;
use openssl::symm::{decrypt_aead, Cipher, Crypter, Mode};
@@ -248,28 +248,4 @@ impl CryptConfig {
Ok(decr_data)
}
-
- pub fn generate_rsa_encoded_key(
- &self,
- rsa: openssl::rsa::Rsa<openssl::pkey::Public>,
- created: i64,
- ) -> Result<Vec<u8>, Error> {
-
- let modified = proxmox::tools::time::epoch_i64();
- let key_config = super::KeyConfig {
- kdf: None,
- created,
- modified,
- data: self.enc_key.to_vec(),
- fingerprint: Some(self.fingerprint()),
- };
- let data = serde_json::to_string(&key_config)?.as_bytes().to_vec();
-
- let mut buffer = vec![0u8; rsa.size() as usize];
- let len = rsa.public_encrypt(&data, &mut buffer, openssl::rsa::Padding::PKCS1)?;
- if len != buffer.len() {
- bail!("got unexpected length from rsa.public_encrypt().");
- }
- Ok(buffer)
- }
}
diff --git a/src/backup/key_derivation.rs b/src/backup/key_derivation.rs
index 046a8c8f..a2aa9469 100644
--- a/src/backup/key_derivation.rs
+++ b/src/backup/key_derivation.rs
@@ -245,3 +245,17 @@ pub fn decrypt_key(
Ok((result, created, fingerprint))
}
+
+pub fn rsa_encrypt_key_config(
+ rsa: openssl::rsa::Rsa<openssl::pkey::Public>,
+ key: &KeyConfig,
+) -> Result<Vec<u8>, Error> {
+ let data = serde_json::to_string(key)?.as_bytes().to_vec();
+
+ let mut buffer = vec![0u8; rsa.size() as usize];
+ let len = rsa.public_encrypt(&data, &mut buffer, openssl::rsa::Padding::PKCS1)?;
+ if len != buffer.len() {
+ bail!("got unexpected length from rsa.public_encrypt().");
+ }
+ Ok(buffer)
+}
diff --git a/src/bin/proxmox-backup-client.rs b/src/bin/proxmox-backup-client.rs
index 36da624e..c40bedc5 100644
--- a/src/bin/proxmox-backup-client.rs
+++ b/src/bin/proxmox-backup-client.rs
@@ -40,6 +40,7 @@ use proxmox_backup::pxar::catalog::*;
use proxmox_backup::backup::{
archive_type,
decrypt_key,
+ rsa_encrypt_key_config,
verify_chunk_size,
ArchiveType,
AsyncReadChunk,
@@ -57,6 +58,7 @@ use proxmox_backup::backup::{
ENCRYPTED_KEY_BLOB_NAME,
FixedChunkStream,
FixedIndexReader,
+ KeyConfig,
IndexFile,
MANIFEST_BLOB_NAME,
Shell,
@@ -914,13 +916,20 @@ async fn create_backup(
let (key, created, fingerprint) = decrypt_key(&key, &key::get_encryption_key_password)?;
println!("Encryption key fingerprint: {}", fingerprint);
- let crypt_config = CryptConfig::new(key)?;
+ let crypt_config = CryptConfig::new(key.clone())?;
match key::find_master_pubkey()? {
Some(ref path) if path.exists() => {
let pem_data = file_get_contents(path)?;
let rsa = openssl::rsa::Rsa::public_key_from_pem(&pem_data)?;
- let enc_key = crypt_config.generate_rsa_encoded_key(rsa, created)?;
+ let key_config = KeyConfig {
+ kdf: None,
+ created,
+ modified: proxmox::tools::time::epoch_i64(),
+ data: key.to_vec(),
+ fingerprint: Some(fingerprint),
+ };
+ let enc_key = rsa_encrypt_key_config(rsa, &key_config)?;
println!("Master key '{:?}'", path);
(Some(Arc::new(crypt_config)), Some(enc_key))
--
2.20.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pbs-devel] [PATCH proxmox-backup 4/7] client: add 'import-with-master-key' command
2020-12-16 13:41 [pbs-devel] [PATCH proxmox-backup 0/7] master key improvements Fabian Grünbichler
` (2 preceding siblings ...)
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
2020-12-16 13:41 ` [pbs-devel] [PATCH proxmox-backup 5/7] docs: replace openssl command with client Fabian Grünbichler
` (3 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Fabian Grünbichler @ 2020-12-16 13:41 UTC (permalink / raw)
To: pbs-devel
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
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pbs-devel] [PATCH proxmox-backup 5/7] docs: replace openssl command with client
2020-12-16 13:41 [pbs-devel] [PATCH proxmox-backup 0/7] master key improvements Fabian Grünbichler
` (3 preceding siblings ...)
2020-12-16 13:41 ` [pbs-devel] [PATCH proxmox-backup 4/7] client: add 'import-with-master-key' command Fabian Grünbichler
@ 2020-12-16 13:41 ` Fabian Grünbichler
2020-12-16 13:41 ` [pbs-devel] [PATCH proxmox-backup 6/7] KeyConfig: add encrypt/decrypt test Fabian Grünbichler
` (2 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Fabian Grünbichler @ 2020-12-16 13:41 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---
docs/backup-client.rst | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/docs/backup-client.rst b/docs/backup-client.rst
index b462f399..1c35818f 100644
--- a/docs/backup-client.rst
+++ b/docs/backup-client.rst
@@ -353,8 +353,10 @@ To set up a master key:
.. code-block:: console
- # openssl rsautl -decrypt -inkey master-private.pem -in rsa-encrypted.key -out /path/to/target
- Enter pass phrase for ./master-private.pem: *********
+ # proxmox-backup-client key import-with-master-key /path/to/target --master-keyfile /path/to/master-private.pem --encrypted-keyfile /path/to/rsa-encrypted.key
+ Master Key Password: ******
+ New Password: ******
+ Verify Password: ******
7. The target file will now contain the encryption key information in plain
text. The success of this can be confirmed by passing the resulting ``json``
--
2.20.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pbs-devel] [PATCH proxmox-backup 6/7] KeyConfig: add encrypt/decrypt test
2020-12-16 13:41 [pbs-devel] [PATCH proxmox-backup 0/7] master key improvements Fabian Grünbichler
` (4 preceding siblings ...)
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 ` 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:53 ` [pbs-devel] applied: [PATCH proxmox-backup 0/7] master key improvements Dietmar Maurer
7 siblings, 0 replies; 11+ messages in thread
From: Fabian Grünbichler @ 2020-12-16 13:41 UTC (permalink / raw)
To: pbs-devel
the RSA key and the encryption key itself are hard-coded to avoid
stalling the test runs because of lack of entropy, they have no special
significance otherwise.
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---
src/backup/key_derivation.rs | 44 ++++++++++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/src/backup/key_derivation.rs b/src/backup/key_derivation.rs
index 8289b86c..a91b21ca 100644
--- a/src/backup/key_derivation.rs
+++ b/src/backup/key_derivation.rs
@@ -271,3 +271,47 @@ pub fn rsa_decrypt_key_config(
.map_err(|err| format_err!("failed to decrypt KeyConfig using RSA - {}", err))?;
decrypt_key(&mut buffer[..decrypted], passphrase)
}
+
+#[test]
+fn encrypt_decrypt_test() -> Result<(), Error> {
+ use openssl::bn::BigNum;
+
+ // hard-coded RSA key to avoid RNG load
+ let n = BigNum::from_dec_str("763297775503047285270249195475255390818773358873206395804367739392073195066702037300664538507287660511788773520960052020490020500131532096848837840341808263208238432840771609456175669299183585737297710099814398628316822920397690811697390531460556770185920171717205255045261184699028939408338685227311360280561223048029934992213591164033485740834987719043448066906674761591422028943934961140687347873900379335604823288576732038392698785999130614670054444889172406669687648738432457362496418067100853836965448514921778954255248154805294695304544857640397043149235605321195443660560591215396460879078079707598866525981810195613239506906019678159905700662365794059211182357495974678620101388841934629146959674859076053348229540838236896752745251734302737503775744293828247434369307467826891918526442390310055226655466835862319406221740752718258752129277114593279326227799698036058425261999904258111333276380007458144919278763944469942242338999234161147188585579934794573969834141472487673642778646170134259790130811461184848743147137198639341697548363179639042991358823669057297753206096865332303845149920379065177826748710006313272747133642274061146677367740923397358666767242901746171920401890395722806446280380164886804469750825832083").expect("converting to bignum failed");
+ let e = BigNum::from_dec_str("65537").expect("converting to bignum failed");
+ let d = BigNum::from_dec_str("19834537920284564853674022001226176519590018312725185651690468898251379391772488358073023011091610629897253174637151053464371346136136825929376853412608136964518211867003891708559549030570664609466682947037305962494828103719078802086159819263581307957743290849968728341884428605863043529798446388179368090663224786773806846388143274064254180335413340334940446739125488182098535411927937482988091512111514808559058456451259207186517021416246081401087976557460070014777577029793101223558164090029643622447657946212243306210181845486266030884899215596710196751196243890657122549917370139613045651724521564033154854414253451612565268626314358200247667906740226693180923631251719053819020017537699856142036238058103150388959616397059243552685604990510867544536282659146915388522812398795915840913802745825670833498941795568293354230962683054249223513028733221781409833526268687556063636480230666207346771664323325175723577540510559973905170578206847160551684632855673373061549848844186260938182413805301541655002820734307939021848604620517318497220269398148326924299176570233223593669359192722153811016413065311904503101005564780859010942238851216519088762587394817890851764597501374473176420295837906296738426781972820833509964922715585").expect("converting to bignum failed");
+ let p = BigNum::from_dec_str("29509637001892646371585718218450720181675215968655693119622290166463846337874978909899277049204111617901784460858811114760264767076166751445502024396748257412446297522757119324882999179307561418697097464139952930737249422485899639568595470472222197161276683797577982497955467948265299386993875583089675892019886767032750524889582030672594405810531152141432362873209548569385820623081973262550874468619670422387868884561012170536839449407663630232422905779693831681822257822783504983493794208329832510955061326579888576047912149807967610736616238778237407615015312695567289456675371922184276823263863231190560557676339").expect("converting to bignum failed");
+ let q = BigNum::from_dec_str("25866050993920799422553175902510303878636288340476152724026122959148470649546748310678170203350410878157245623372422271950639190884394436256045773535202161325882791039345330048364703416719823181485853395688815455066122599160191671526435061804017559815713791273329637690511813515454721229797045837580571003198471014420883727461348135261877384657284061678787895040009197824032371314493780688519536250146270701914875469190776765810821706480996720025323321483843112182646061748043938180130013308823672610860230340094502643614566152670758944502783858455501528490806234504795239898001698524105646533910560293336400403204897").expect("converting to bignum failed");
+ let dmp1 = BigNum::from_dec_str("21607770579166338313924278588690558922108583912962897316392792781303188398339022047518905458553289108745759383366535358272664077428797321640702979183532285223743426240475893650342331272664468275332046219832278884297711602396407401980831582724583041600551528176116883960387063733484217876666037528133838392148714866050744345765006980605100330287254053877398358630385580919903058731105447806937933747350668236714360621211130384969129674812319182867594036995223272269821421615266717078107026511273509659211002684589097654567453625356436054504001404801715927134738465685565147724902539753143706245247513141254140715042985").expect("converting to bignum failed");
+ let dmq1 = BigNum::from_dec_str("294824909477987048059069264677589712640818276551195295555907561384926187881828905626998384758270243160099828809057470393016578048898219996082612765778049262408020582364022789357590879232947921274546172186391582540158896220038500063021605980859684104892476037676079761887366292263067835858498149757735119694054623308549371262243115446856316376077501168409517640338844786525965200908293851935915491689568704919822573134038943559526432621897623477713604851434011395096458613085567264607124524187730342254186063812054159860538030670385536895853938115358646898433438472543479930479076991585011794266310458811393428158049").expect("converting to bignum failed");
+ let iqmp = BigNum::from_dec_str("19428066064824171668277167138275898936765006396600005071379051329779053619544399695639107933588871625444213173194462077344726482973273922001955114108600584475883837715007613468112455972196002915686862701860412263935895363086514864873592142686096117947515832613228762197577036084559813332497101195090727973644165586960538914545531208630624795512138060798977135902359295307626262953373309121954863020224150277262533638440848025788447039555055470985052690506486164836957350781708784380677438638580158751807723730202286612196281022183410822668814233870246463721184575820166925259871133457423401827024362448849298618281053").expect("converting to bignum failed");
+ let public =
+ openssl::rsa::Rsa::from_public_components(n.to_owned().unwrap(), e.to_owned().unwrap())
+ .expect("creating hard-coded RSA public key instance failed");
+ let private = openssl::rsa::Rsa::from_private_components(n, e, d, p, q, dmp1, dmq1, iqmp)
+ .expect("creating hard-coded RSA key instance failed");
+
+ let passphrase = || -> Result<Vec<u8>, Error> { Ok(Vec::new()) };
+
+ let key = KeyConfig {
+ kdf: None,
+ created: proxmox::tools::time::epoch_i64(),
+ modified: proxmox::tools::time::epoch_i64(),
+ data: (0u8..32u8).collect(),
+ fingerprint: Some(Fingerprint::new([
+ 14, 171, 212, 70, 11, 110, 185, 202, 52, 80, 35, 222, 226, 183, 120, 199, 144, 229, 74,
+ 22, 131, 185, 101, 156, 10, 87, 174, 25, 144, 144, 21, 155,
+ ])),
+ };
+
+ let encrypted = rsa_encrypt_key_config(public.clone(), &key).expect("encryption failed");
+ let (decrypted, created, fingerprint) =
+ rsa_decrypt_key_config(private.clone(), &encrypted, &passphrase)
+ .expect("decryption failed");
+
+ assert_eq!(key.created, created);
+ assert_eq!(key.data, decrypted);
+ assert_eq!(key.fingerprint, Some(fingerprint));
+
+ Ok(())
+}
--
2.20.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pbs-devel] [RFC proxmox-backup 7/7] KeyConfig: always calculate fingerprint
2020-12-16 13:41 [pbs-devel] [PATCH proxmox-backup 0/7] master key improvements Fabian Grünbichler
` (5 preceding siblings ...)
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 ` Fabian Grünbichler
2020-12-17 5:55 ` Dietmar Maurer
2020-12-17 5:53 ` [pbs-devel] applied: [PATCH proxmox-backup 0/7] master key improvements Dietmar Maurer
7 siblings, 1 reply; 11+ messages in thread
From: Fabian Grünbichler @ 2020-12-16 13:41 UTC (permalink / raw)
To: pbs-devel
and warn if stored and calculated fingerprint don't match.
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---
Notes:
should not happen in practice, but when it does, it's probably not a good idea
to display/use the wrong fingerprint..
calculating the fingerprint should be cheap anyway:
- derive ID key
- calculate single digest with it
src/backup/key_derivation.rs | 34 +++++++++++++++++++++++++++-------
1 file changed, 27 insertions(+), 7 deletions(-)
diff --git a/src/backup/key_derivation.rs b/src/backup/key_derivation.rs
index a91b21ca..7e8480d3 100644
--- a/src/backup/key_derivation.rs
+++ b/src/backup/key_derivation.rs
@@ -235,13 +235,16 @@ pub fn decrypt_key(
let mut result = [0u8; 32];
result.copy_from_slice(&key);
- let fingerprint = match key_config.fingerprint {
- Some(fingerprint) => fingerprint,
- None => {
- let crypt_config = CryptConfig::new(result.clone())?;
- crypt_config.fingerprint()
- },
- };
+ let crypt_config = CryptConfig::new(result.clone())?;
+ let fingerprint = crypt_config.fingerprint();
+ if let Some(stored_fingerprint) = key_config.fingerprint {
+ if fingerprint != stored_fingerprint {
+ eprintln!(
+ "KeyConfig contains wrong fingerprint {}, contained key has fingerprint {}",
+ stored_fingerprint, fingerprint
+ );
+ }
+ }
Ok((result, created, fingerprint))
}
@@ -313,5 +316,22 @@ fn encrypt_decrypt_test() -> Result<(), Error> {
assert_eq!(key.data, decrypted);
assert_eq!(key.fingerprint, Some(fingerprint));
+ let key = KeyConfig {
+ kdf: None,
+ created: proxmox::tools::time::epoch_i64(),
+ modified: proxmox::tools::time::epoch_i64(),
+ data: (0u8..32u8).collect(),
+ fingerprint: Some(Fingerprint::new([0u8; 32])), // wrong FP
+ };
+ let encrypted = rsa_encrypt_key_config(public.clone(), &key).expect("encryption failed");
+ let (decrypted, created, fingerprint) =
+ rsa_decrypt_key_config(private.clone(), &encrypted, &passphrase)
+ .expect("decryption failed");
+
+ assert_eq!(key.created, created);
+ assert_eq!(key.data, decrypted);
+ // wrong FP update by round-trip through encrypt/decrypt
+ assert_ne!(key.fingerprint, Some(fingerprint));
+
Ok(())
}
--
2.20.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [pbs-devel] [RFC proxmox-backup 7/7] KeyConfig: always calculate fingerprint
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
0 siblings, 1 reply; 11+ messages in thread
From: Dietmar Maurer @ 2020-12-17 5:55 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Fabian Grünbichler
> On 12/16/2020 2:41 PM Fabian Grünbichler <f.gruenbichler@proxmox.com> wrote:
>
>
> and warn if stored and calculated fingerprint don't match.
applied, but I wonder why this is only a warning (should generate an error instead)?
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pbs-devel] applied: [RFC proxmox-backup 7/7] KeyConfig: always calculate fingerprint
2020-12-17 5:55 ` Dietmar Maurer
@ 2020-12-17 10:37 ` Fabian Grünbichler
0 siblings, 0 replies; 11+ messages in thread
From: Fabian Grünbichler @ 2020-12-17 10:37 UTC (permalink / raw)
To: Dietmar Maurer, Proxmox Backup Server development discussion
On December 17, 2020 6:55 am, Dietmar Maurer wrote:
>
>> On 12/16/2020 2:41 PM Fabian Grünbichler <f.gruenbichler@proxmox.com> wrote:
>>
>>
>> and warn if stored and calculated fingerprint don't match.
>
> applied, but I wonder why this is only a warning (should generate an error instead)?
as discussed this morning, switched to bail! and adapted the test cases:
commit c01742855ae95231d0b964bd9434c74d2e9dffd1
Author: Fabian Grünbichler <f.gruenbichler@proxmox.com>
AuthorDate: Thu Dec 17 10:53:21 2020 +0100
Commit: Fabian Grünbichler <f.gruenbichler@proxmox.com>
CommitDate: Thu Dec 17 11:27:06 2020 +0100
KeyConfig: bail on wrong fingerprint
instead of just logging the error. this should never happen in practice
unless someone is messing with the keyfile, in which case, it's better
to abort.
update tests accordingly (wrong fingerprint should fail, no fingerprint
should get the expected one).
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
diff --git a/src/backup/key_derivation.rs b/src/backup/key_derivation.rs
index 7e8480d3..a215670a 100644
--- a/src/backup/key_derivation.rs
+++ b/src/backup/key_derivation.rs
@@ -239,7 +239,7 @@ pub fn decrypt_key(
let fingerprint = crypt_config.fingerprint();
if let Some(stored_fingerprint) = key_config.fingerprint {
if fingerprint != stored_fingerprint {
- eprintln!(
+ bail!(
"KeyConfig contains wrong fingerprint {}, contained key has fingerprint {}",
stored_fingerprint, fingerprint
);
@@ -316,6 +316,11 @@ fn encrypt_decrypt_test() -> Result<(), Error> {
assert_eq!(key.data, decrypted);
assert_eq!(key.fingerprint, Some(fingerprint));
+ Ok(())
+}
+
+#[test]
+fn fingerprint_checks() -> Result<(), Error> {
let key = KeyConfig {
kdf: None,
created: proxmox::tools::time::epoch_i64(),
@@ -323,15 +328,30 @@ fn encrypt_decrypt_test() -> Result<(), Error> {
data: (0u8..32u8).collect(),
fingerprint: Some(Fingerprint::new([0u8; 32])), // wrong FP
};
- let encrypted = rsa_encrypt_key_config(public.clone(), &key).expect("encryption failed");
- let (decrypted, created, fingerprint) =
- rsa_decrypt_key_config(private.clone(), &encrypted, &passphrase)
- .expect("decryption failed");
+ let expected_fingerprint = Fingerprint::new([
+ 14, 171, 212, 70, 11, 110, 185, 202, 52, 80, 35, 222, 226, 183, 120, 199, 144, 229, 74,
+ 22, 131, 185, 101, 156, 10, 87, 174, 25, 144, 144, 21, 155,
+ ]);
+
+ let mut data = serde_json::to_vec(&key).expect("encoding KeyConfig failed");
+ decrypt_key(&mut data, &{ || { Ok(Vec::new()) }}).expect_err("decoding KeyConfig with wrong fingerprint worked");
+
+ let key = KeyConfig {
+ kdf: None,
+ created: proxmox::tools::time::epoch_i64(),
+ modified: proxmox::tools::time::epoch_i64(),
+ data: (0u8..32u8).collect(),
+ fingerprint: None,
+ };
+
+
+ let mut data = serde_json::to_vec(&key).expect("encoding KeyConfig failed");
+ let (key_data, created, fingerprint) = decrypt_key(&mut data, &{ || { Ok(Vec::new()) }}).expect("decoding KeyConfig without fingerprint failed");
+
+ assert_eq!(key.data, key_data);
assert_eq!(key.created, created);
- assert_eq!(key.data, decrypted);
- // wrong FP update by round-trip through encrypt/decrypt
- assert_ne!(key.fingerprint, Some(fingerprint));
+ assert_eq!(expected_fingerprint, fingerprint);
Ok(())
}
^ permalink raw reply [flat|nested] 11+ messages in thread
* [pbs-devel] applied: [PATCH proxmox-backup 0/7] master key improvements
2020-12-16 13:41 [pbs-devel] [PATCH proxmox-backup 0/7] master key improvements Fabian Grünbichler
` (6 preceding siblings ...)
2020-12-16 13:41 ` [pbs-devel] [RFC proxmox-backup 7/7] KeyConfig: always calculate fingerprint Fabian Grünbichler
@ 2020-12-17 5:53 ` Dietmar Maurer
7 siblings, 0 replies; 11+ messages in thread
From: Dietmar Maurer @ 2020-12-17 5:53 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Fabian Grünbichler
applied
> On 12/16/2020 2:41 PM Fabian Grünbichler <f.gruenbichler@proxmox.com> wrote:
>
>
> this series fixes a bug preventing encrypted-key-restore that got
> introduced with fingerprint checks when restoring files, and further
> improves master key handling by providing an import command that takes a
> restored encrypted key and a private master key to create a PBS keyfile.
>
> Fabian Grünbichler (7):
> master key: store blob name in constant
> fix #3197: skip fingerprint check when restoring key
> key: move RSA-encryption to KeyConfig
> client: add 'import-with-master-key' command
> docs: replace openssl command with client
> KeyConfig: add encrypt/decrypt test
> KeyConfig: always calculate fingerprint
>
> docs/backup-client.rst | 6 +-
> src/backup/crypt_config.rs | 26 +------
> src/backup/key_derivation.rs | 104 +++++++++++++++++++++++++--
> src/backup/manifest.rs | 1 +
> src/bin/proxmox-backup-client.rs | 33 +++++----
> src/bin/proxmox_backup_client/key.rs | 95 ++++++++++++++++++++++++
> 6 files changed, 218 insertions(+), 47 deletions(-)
>
> --
> 2.20.1
>
>
>
> _______________________________________________
> pbs-devel mailing list
> pbs-devel@lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 11+ messages in thread