]> git.proxmox.com Git - proxmox-backup.git/commitdiff
moved key_derivation.rs from pbs_datastore to pbs-config/src/key_config.rs
authorDietmar Maurer <dietmar@proxmox.com>
Tue, 7 Sep 2021 07:22:14 +0000 (09:22 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Tue, 7 Sep 2021 08:12:17 +0000 (10:12 +0200)
Also moved pbs-datastore/src/crypt_config.rs to pbs-tools/src/crypt_config.rs.
We do not want to depend on pbs-api-types there, so I use [u8;32] instead of
Fingerprint.

40 files changed:
pbs-client/src/backup_reader.rs
pbs-client/src/backup_writer.rs
pbs-client/src/remote_chunk_reader.rs
pbs-config/Cargo.toml
pbs-config/src/key_config.rs [new file with mode: 0644]
pbs-config/src/lib.rs
pbs-datastore/Cargo.toml
pbs-datastore/src/checksum_reader.rs
pbs-datastore/src/checksum_writer.rs
pbs-datastore/src/crypt_config.rs [deleted file]
pbs-datastore/src/crypt_reader.rs
pbs-datastore/src/crypt_writer.rs
pbs-datastore/src/data_blob.rs
pbs-datastore/src/data_blob_reader.rs
pbs-datastore/src/data_blob_writer.rs
pbs-datastore/src/key_derivation.rs [deleted file]
pbs-datastore/src/lib.rs
pbs-datastore/src/manifest.rs
pbs-datastore/src/paperkey.rs
pbs-tools/src/crypt_config.rs [new file with mode: 0644]
pbs-tools/src/lib.rs
proxmox-backup-client/Cargo.toml
proxmox-backup-client/src/benchmark.rs
proxmox-backup-client/src/catalog.rs
proxmox-backup-client/src/key.rs
proxmox-backup-client/src/main.rs
proxmox-backup-client/src/mount.rs
proxmox-backup-client/src/snapshot.rs
proxmox-backup-debug/Cargo.toml
proxmox-backup-debug/src/inspect.rs
proxmox-backup-debug/src/recover.rs
proxmox-file-restore/Cargo.toml
proxmox-file-restore/src/main.rs
src/api2/config/tape_encryption_keys.rs
src/backup/read_chunk.rs
src/config/tape_encryption_keys.rs
src/tape/drive/lto/mod.rs
src/tape/drive/mod.rs
src/tape/drive/virtual_tape.rs
tests/blob_writer.rs

index 1702d44156ee6ef4c0b150232b2c5c9b1e533b6f..5da5616c0c4d01cbfa99deb2d4ab3882f07fb501 100644 (file)
@@ -9,14 +9,15 @@ use serde_json::{json, Value};
 
 use proxmox::tools::digest_to_hex;
 
-use pbs_datastore::{PROXMOX_BACKUP_READER_PROTOCOL_ID_V1, CryptConfig, BackupManifest};
+use pbs_tools::crypt_config::CryptConfig;
+use pbs_tools::sha::sha256;
+use pbs_datastore::{PROXMOX_BACKUP_READER_PROTOCOL_ID_V1, BackupManifest};
 use pbs_datastore::data_blob::DataBlob;
 use pbs_datastore::data_blob_reader::DataBlobReader;
 use pbs_datastore::dynamic_index::DynamicIndexReader;
 use pbs_datastore::fixed_index::FixedIndexReader;
 use pbs_datastore::index::IndexFile;
 use pbs_datastore::manifest::MANIFEST_BLOB_NAME;
-use pbs_tools::sha::sha256;
 
 use super::{HttpClient, H2Client};
 
index 5c15f27e1b789a13b1be5647ab93d2b9760c7c96..20ae6e2ce822a999589de6a4936870f19015feab 100644 (file)
@@ -14,13 +14,14 @@ use tokio_stream::wrappers::ReceiverStream;
 
 use proxmox::tools::digest_to_hex;
 
-use pbs_datastore::{CATALOG_NAME, PROXMOX_BACKUP_PROTOCOL_ID_V1, CryptConfig};
+use pbs_tools::crypt_config::CryptConfig;
+use pbs_tools::format::HumanByte;
+use pbs_datastore::{CATALOG_NAME, PROXMOX_BACKUP_PROTOCOL_ID_V1};
 use pbs_datastore::data_blob::{ChunkInfo, DataBlob, DataChunkBuilder};
 use pbs_datastore::dynamic_index::DynamicIndexReader;
 use pbs_datastore::fixed_index::FixedIndexReader;
 use pbs_datastore::index::IndexFile;
 use pbs_datastore::manifest::{ArchiveType, BackupManifest, MANIFEST_BLOB_NAME};
-use pbs_tools::format::HumanByte;
 
 use super::merge_known_chunks::{MergeKnownChunks, MergedChunkInfo};
 
index 61b6fb052232142508e93d5e8634d2e24519b3f7..734cd29fa758e1f09b59c3f6794b58f36a0f05b5 100644 (file)
@@ -5,7 +5,8 @@ use std::sync::{Arc, Mutex};
 
 use anyhow::{bail, Error};
 
-use pbs_datastore::{CryptConfig, CryptMode};
+use pbs_tools::crypt_config::CryptConfig;
+use pbs_api_types::CryptMode;
 use pbs_datastore::data_blob::DataBlob;
 use pbs_datastore::read_chunk::ReadChunk;
 use pbs_datastore::read_chunk::AsyncReadChunk;
index cceb6cc472bd733ab669d2fc59ae43b3bda4fac8..cd65914d217b990e635e6ba4ec6d378eb038e0cf 100644 (file)
@@ -9,6 +9,7 @@ description = "Configuration file management for PBS"
 anyhow = "1.0"
 lazy_static = "1.4"
 serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
 openssl = "0.10"
 nix = "0.19.1"
 
diff --git a/pbs-config/src/key_config.rs b/pbs-config/src/key_config.rs
new file mode 100644 (file)
index 0000000..9e71368
--- /dev/null
@@ -0,0 +1,432 @@
+use std::io::Write;
+use std::path::Path;
+
+use anyhow::{bail, format_err, Context, Error};
+use serde::{Deserialize, Serialize};
+
+use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions};
+use proxmox::try_block;
+
+use pbs_api_types::{Kdf, KeyInfo, Fingerprint};
+
+use pbs_tools::crypt_config::CryptConfig;
+
+/// Key derivation function configuration
+#[derive(Deserialize, Serialize, Clone, Debug)]
+pub enum KeyDerivationConfig {
+    Scrypt {
+        n: u64,
+        r: u64,
+        p: u64,
+        #[serde(with = "proxmox::tools::serde::bytes_as_base64")]
+        salt: Vec<u8>,
+    },
+    PBKDF2 {
+        iter: usize,
+        #[serde(with = "proxmox::tools::serde::bytes_as_base64")]
+        salt: Vec<u8>,
+    },
+}
+
+impl KeyDerivationConfig {
+
+    /// Derive a key from provided passphrase
+    pub fn derive_key(&self, passphrase: &[u8]) -> Result<[u8; 32], Error> {
+
+        let mut key = [0u8; 32];
+
+        match self {
+            KeyDerivationConfig::Scrypt { n, r, p, salt } => {
+                // estimated scrypt memory usage is 128*r*n*p
+                openssl::pkcs5::scrypt(
+                    passphrase,
+                    &salt,
+                    *n, *r, *p,
+                    1025*1024*1024,
+                    &mut key,
+                )?;
+
+                Ok(key)
+            }
+            KeyDerivationConfig::PBKDF2 { iter, salt } => {
+
+                 openssl::pkcs5::pbkdf2_hmac(
+                    passphrase,
+                    &salt,
+                    *iter,
+                    openssl::hash::MessageDigest::sha256(),
+                    &mut key,
+                )?;
+
+                Ok(key)
+            }
+        }
+    }
+}
+
+/// Encryption Key Configuration
+///
+/// We use this struct to store secret keys. When used with a key
+/// derivation function, the key data is encrypted (AES-CGM), and you
+/// need the password to restore the plain key.
+#[derive(Deserialize, Serialize, Clone, Debug)]
+pub struct KeyConfig {
+    pub kdf: Option<KeyDerivationConfig>,
+    #[serde(with = "proxmox::tools::serde::epoch_as_rfc3339")]
+    pub created: i64,
+    #[serde(with = "proxmox::tools::serde::epoch_as_rfc3339")]
+    pub modified: i64,
+    #[serde(with = "proxmox::tools::serde::bytes_as_base64")]
+    pub data: Vec<u8>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    #[serde(default)]
+    pub fingerprint: Option<Fingerprint>,
+    /// Password hint
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub hint: Option<String>,
+}
+
+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| pbs_tools::format::as_fingerprint(fp.bytes())),
+            hint: key_config.hint.clone(),
+        }
+    }
+}
+
+impl KeyConfig  {
+
+    /// Creates a new key using random data, protected by passphrase.
+    pub fn new(passphrase: &[u8], kdf: Kdf) -> Result<([u8;32], Self), Error> {
+        let mut key = [0u8; 32];
+        proxmox::sys::linux::fill_with_random_data(&mut key)?;
+        let key_config = Self::with_key(&key, passphrase, kdf)?;
+        Ok((key, key_config))
+    }
+
+    /// Creates a new, unencrypted key.
+    pub fn without_password(raw_key: [u8; 32]) -> Result<Self, Error> {
+        // always compute fingerprint
+        let crypt_config = CryptConfig::new(raw_key.clone())?;
+        let fingerprint = Some(Fingerprint::new(crypt_config.fingerprint()));
+
+        let created = proxmox::tools::time::epoch_i64();
+        Ok(Self {
+            kdf: None,
+            created,
+            modified: created,
+            data: raw_key.to_vec(),
+            fingerprint,
+            hint: None,
+        })
+    }
+
+    /// Creates a new instance, protect raw_key with passphrase.
+    pub fn with_key(
+        raw_key: &[u8; 32],
+        passphrase: &[u8],
+        kdf: Kdf,
+    ) -> Result<Self, Error> {
+
+        if raw_key.len() != 32 {
+            bail!("got strange key length ({} != 32)", raw_key.len())
+        }
+
+        let salt = proxmox::sys::linux::random_data(32)?;
+
+        let kdf = match kdf {
+            Kdf::Scrypt => KeyDerivationConfig::Scrypt {
+                n: 65536,
+                r: 8,
+                p: 1,
+                salt,
+            },
+            Kdf::PBKDF2 => KeyDerivationConfig::PBKDF2 {
+                iter: 65535,
+                salt,
+            },
+            Kdf::None => {
+                bail!("No key derivation function specified");
+            }
+        };
+
+        let derived_key = kdf.derive_key(passphrase)?;
+
+        let cipher = openssl::symm::Cipher::aes_256_gcm();
+
+        let iv = proxmox::sys::linux::random_data(16)?;
+        let mut tag = [0u8; 16];
+
+        let encrypted_key = openssl::symm::encrypt_aead(
+            cipher,
+            &derived_key,
+            Some(&iv),
+            b"",
+            raw_key,
+            &mut tag,
+        )?;
+
+        let mut enc_data = vec![];
+        enc_data.extend_from_slice(&iv);
+        enc_data.extend_from_slice(&tag);
+        enc_data.extend_from_slice(&encrypted_key);
+
+        let created = proxmox::tools::time::epoch_i64();
+
+        // always compute fingerprint
+        let crypt_config = CryptConfig::new(raw_key.clone())?;
+        let fingerprint = Some(Fingerprint::new(crypt_config.fingerprint()));
+
+        Ok(Self {
+            kdf: Some(kdf),
+            created,
+            modified: created,
+            data: enc_data,
+            fingerprint,
+            hint: None,
+        })
+    }
+
+    /// Loads a KeyConfig from path
+    pub fn load<P: AsRef<Path>>(path: P) -> Result<KeyConfig, Error> {
+        let keydata = file_get_contents(path)?;
+        let key_config: KeyConfig = serde_json::from_reader(&keydata[..])?;
+        Ok(key_config)
+    }
+
+    /// Decrypt key to get raw key data.
+    pub fn decrypt(
+        &self,
+        passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
+    ) -> Result<([u8;32], i64, Fingerprint), Error> {
+
+        let raw_data = &self.data;
+
+        let key = if let Some(ref kdf) = self.kdf {
+
+            let passphrase = passphrase()?;
+            if passphrase.len() < 5 {
+                bail!("Passphrase is too short!");
+            }
+
+            let derived_key = kdf.derive_key(&passphrase)?;
+
+            if raw_data.len() < 32 {
+                bail!("Unable to decrypt key - short data");
+            }
+            let iv = &raw_data[0..16];
+            let tag = &raw_data[16..32];
+            let enc_data = &raw_data[32..];
+
+            let cipher = openssl::symm::Cipher::aes_256_gcm();
+
+            openssl::symm::decrypt_aead(
+                cipher,
+                &derived_key,
+                Some(&iv),
+                b"",
+                &enc_data,
+                &tag,
+            ).map_err(|err| {
+                match self.hint {
+                    Some(ref hint) => {
+                        format_err!("Unable to decrypt key (password hint: {})", hint)
+                    }
+                    None => {
+                        format_err!("Unable to decrypt key (wrong password?) - {}", err)
+                    }
+                }
+            })?
+
+        } else {
+            raw_data.clone()
+        };
+
+        let mut result = [0u8; 32];
+        result.copy_from_slice(&key);
+
+        let crypt_config = CryptConfig::new(result.clone())?;
+        let fingerprint = Fingerprint::new(crypt_config.fingerprint());
+        if let Some(ref stored_fingerprint) = self.fingerprint {
+            if &fingerprint != stored_fingerprint {
+                bail!(
+                    "KeyConfig contains wrong fingerprint {}, contained key has fingerprint {}",
+                    stored_fingerprint, fingerprint
+                );
+            }
+        }
+
+        Ok((result, self.created, fingerprint))
+    }
+
+    /// Store a KeyConfig to path
+    pub fn store<P: AsRef<Path>>(&self, path: P, replace: bool) -> Result<(), Error> {
+
+        let path: &Path = path.as_ref();
+
+        let data = serde_json::to_string(self)?;
+
+        try_block!({
+            if replace {
+                let mode = nix::sys::stat::Mode::S_IRUSR | nix::sys::stat::Mode::S_IWUSR;
+                replace_file(path, data.as_bytes(), CreateOptions::new().perm(mode))?;
+            } else {
+                use std::os::unix::fs::OpenOptionsExt;
+
+                let mut file = std::fs::OpenOptions::new()
+                    .write(true)
+                    .mode(0o0600)
+                    .create_new(true)
+                    .open(&path)?;
+
+                file.write_all(data.as_bytes())?;
+            }
+
+            Ok(())
+        }).map_err(|err: Error| format_err!("Unable to store key file {:?} - {}", path, err))?;
+
+        Ok(())
+    }
+}
+
+/// Loads a KeyConfig from path and decrypt it.
+pub fn load_and_decrypt_key(
+    path: &std::path::Path,
+    passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
+) -> Result<([u8;32], i64, Fingerprint), Error> {
+    decrypt_key(&file_get_contents(&path)?, passphrase)
+        .with_context(|| format!("failed to load decryption key from {:?}", path))
+}
+
+/// Decrypt a KeyConfig from raw keydata.
+pub fn decrypt_key(
+    mut keydata: &[u8],
+    passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
+) -> Result<([u8;32], i64, Fingerprint), Error> {
+    let key_config: KeyConfig = serde_json::from_reader(&mut keydata)?;
+    key_config.decrypt(passphrase)
+}
+
+/// RSA encrypt a KeyConfig using a public key
+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)
+}
+
+/// RSA deccrypt a KeyConfig using a private key
+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(&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,
+        ])),
+        hint: None,
+    };
+
+    let encrypted = rsa_encrypt_key_config(public, &key).expect("encryption failed");
+    let (decrypted, created, fingerprint) =
+        rsa_decrypt_key_config(private, &encrypted, &passphrase)
+            .expect("decryption failed");
+
+    assert_eq!(key.created, created);
+    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(),
+        modified: proxmox::tools::time::epoch_i64(),
+        data: (0u8..32u8).collect(),
+        fingerprint: Some(Fingerprint::new([0u8; 32])), // wrong FP
+        hint: None,
+    };
+
+    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,
+        hint: 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!(expected_fingerprint, fingerprint);
+
+    Ok(())
+}
index bd5430a322125f28441fb80170c5d42f53b9b69b..c73766b7bb3798e8921c9daeb3e556060db8c7c9 100644 (file)
@@ -1,5 +1,6 @@
 pub mod domains;
 pub mod drive;
+pub mod key_config;
 pub mod media_pool;
 pub mod remote;
 
index 924ba09f1db209c067c60ad78f56dcfd02dd4638..32eae0d763b421082ff74537d2ade8b9fd6a0f0f 100644 (file)
@@ -27,3 +27,4 @@ proxmox = { version = "0.13.0", default-features = false, features = [ "api-macr
 
 pbs-api-types = { path = "../pbs-api-types" }
 pbs-tools = { path = "../pbs-tools" }
+pbs-config = { path = "../pbs-config" }
index dd6c9d299a0686f21f4c5f973823e5d55448ceb8..7bf8f34d0d67d42187a7bce99074c0dae7e4a6c4 100644 (file)
@@ -3,8 +3,7 @@ use std::sync::Arc;
 use std::io::Read;
 
 use pbs_tools::borrow::Tied;
-
-use super::CryptConfig;
+use pbs_tools::crypt_config::CryptConfig;
 
 pub struct ChecksumReader<R> {
     reader: R,
index 14a75503155a72a1a420d45c96c1315a8e535200..3c502dddd6f5456fffb233bbac212b44d2fc15bf 100644 (file)
@@ -4,8 +4,7 @@ use std::io::Write;
 use anyhow::{Error};
 
 use pbs_tools::borrow::Tied;
-
-use super::CryptConfig;
+use pbs_tools::crypt_config::CryptConfig;
 
 pub struct ChecksumWriter<W> {
     writer: W,
diff --git a/pbs-datastore/src/crypt_config.rs b/pbs-datastore/src/crypt_config.rs
deleted file mode 100644 (file)
index c3e803a..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-//! Wrappers for OpenSSL crypto functions
-//!
-//! We use this to encrypt and decrypt data chunks. Cipher is
-//! AES_256_GCM, which is fast and provides authenticated encryption.
-//!
-//! See the Wikipedia Artikel for [Authenticated
-//! encryption](https://en.wikipedia.org/wiki/Authenticated_encryption)
-//! for a short introduction.
-
-use anyhow::{Error};
-use openssl::hash::MessageDigest;
-use openssl::pkcs5::pbkdf2_hmac;
-use openssl::symm::{Cipher, Crypter, Mode};
-
-pub use pbs_api_types::{CryptMode, Fingerprint};
-
-// openssl::sha::sha256(b"Proxmox Backup Encryption Key Fingerprint")
-/// This constant is used to compute fingerprints.
-const FINGERPRINT_INPUT: [u8; 32] = [
-    110, 208, 239, 119,  71,  31, 255,  77,
-    85, 199, 168, 254,  74, 157, 182,  33,
-    97,  64, 127,  19,  76, 114,  93, 223,
-    48, 153,  45,  37, 236,  69, 237,  38,
-];
-
-/// Encryption Configuration with secret key
-///
-/// This structure stores the secret key and provides helpers for
-/// authenticated encryption.
-pub struct CryptConfig {
-    // the Cipher
-    cipher: Cipher,
-    // A secrect key use to provide the chunk digest name space.
-    id_key: [u8; 32],
-    // Openssl hmac PKey of id_key
-    id_pkey: openssl::pkey::PKey<openssl::pkey::Private>,
-    // The private key used by the cipher.
-    enc_key: [u8; 32],
-}
-
-impl CryptConfig {
-
-    /// Create a new instance.
-    ///
-    /// We compute a derived 32 byte key using pbkdf2_hmac. This second
-    /// key is used in compute_digest.
-    pub fn new(enc_key: [u8; 32]) -> Result<Self, Error> {
-
-        let mut id_key = [0u8; 32];
-
-        pbkdf2_hmac(
-            &enc_key,
-            b"_id_key",
-            10,
-            MessageDigest::sha256(),
-            &mut id_key)?;
-
-        let id_pkey = openssl::pkey::PKey::hmac(&id_key).unwrap();
-
-        Ok(Self { id_key, id_pkey, enc_key, cipher: Cipher::aes_256_gcm() })
-    }
-
-    /// Expose Cipher (AES_256_GCM)
-    pub fn cipher(&self) -> &Cipher {
-        &self.cipher
-    }
-
-    /// Expose encryption key
-    pub fn enc_key(&self) -> &[u8; 32] {
-        &self.enc_key
-    }
-
-    /// Compute a chunk digest using a secret name space.
-    ///
-    /// Computes an SHA256 checksum over some secret data (derived
-    /// from the secret key) and the provided data. This ensures that
-    /// chunk digest values do not clash with values computed for
-    /// other sectret keys.
-    pub fn compute_digest(&self, data: &[u8]) -> [u8; 32] {
-        let mut hasher = openssl::sha::Sha256::new();
-        hasher.update(data);
-        hasher.update(&self.id_key); // at the end, to avoid length extensions attacks
-        hasher.finish()
-    }
-
-    /// Returns an openssl Signer using SHA256
-    pub fn data_signer(&self) -> openssl::sign::Signer {
-        openssl::sign::Signer::new(MessageDigest::sha256(), &self.id_pkey).unwrap()
-    }
-
-    /// Compute authentication tag (hmac/sha256)
-    ///
-    /// Computes an SHA256 HMAC using some secret data (derived
-    /// from the secret key) and the provided data.
-    pub fn compute_auth_tag(&self, data: &[u8]) -> [u8; 32] {
-        let mut signer = self.data_signer();
-        signer.update(data).unwrap();
-        let mut tag = [0u8; 32];
-        signer.sign(&mut tag).unwrap();
-        tag
-    }
-
-    /// Computes a fingerprint for the secret key.
-    ///
-    /// This computes a digest using the derived key (id_key) in order
-    /// to hinder brute force attacks.
-    pub fn fingerprint(&self) -> Fingerprint {
-        Fingerprint::new(self.compute_digest(&FINGERPRINT_INPUT))
-    }
-
-    /// Returns an openssl Crypter using AES_256_GCM,
-    pub fn data_crypter(&self, iv: &[u8; 16], mode: Mode) -> Result<Crypter, Error>  {
-        let mut crypter = openssl::symm::Crypter::new(self.cipher, mode, &self.enc_key, Some(iv))?;
-        crypter.aad_update(b"")?; //??
-        Ok(crypter)
-    }
-}
index 20e219b541053f3aad707f77fe58adbf123e0de1..a2d744274930bfb5f172138971791232a708c1e6 100644 (file)
@@ -3,7 +3,7 @@ use std::io::{Read, BufRead};
 
 use anyhow::{bail, Error};
 
-use super::CryptConfig;
+use pbs_tools::crypt_config::CryptConfig;
 
 pub struct CryptReader<R> {
     reader: R,
index f99bca88b2d47e38f9bb7b668d3db42c91da1cc0..eb5f136f27ff3a4aad61663539a0378791c7a961 100644 (file)
@@ -3,7 +3,7 @@ use std::io::Write;
 
 use anyhow::Error;
 
-use super::CryptConfig;
+use pbs_tools::crypt_config::CryptConfig;
 
 pub struct CryptWriter<W> {
     writer: W,
index ef7d74a7261c4b57279937dba354a77c2c15fe8b..7e1e54eb243ada6b405df3f3bf0a85f8997b8c67 100644 (file)
@@ -6,8 +6,10 @@ use openssl::symm::{decrypt_aead, Mode};
 
 use proxmox::tools::io::{ReadExt, WriteExt};
 
+use pbs_tools::crypt_config::CryptConfig;
+use pbs_api_types::CryptMode;
+
 use super::file_formats::*;
-use super::{CryptConfig, CryptMode};
 
 const MAX_BLOB_SIZE: usize = 128*1024*1024;
 
index 8b37dbe71ed62e677c27aaa357407f76809fbc48..9c7a8568e07977fc7e43d8bd07b7e91d1c157068 100644 (file)
@@ -4,8 +4,9 @@ use std::sync::Arc;
 use anyhow::{bail, format_err, Error};
 use proxmox::tools::io::ReadExt;
 
+use pbs_tools::crypt_config::CryptConfig;
+
 use crate::checksum_reader::ChecksumReader;
-use crate::crypt_config::CryptConfig;
 use crate::crypt_reader::CryptReader;
 use crate::file_formats::{self, DataBlobHeader};
 
index 200aac1c8802591c27ca6329ec6e22fb40ccba44..6425cecf52b4d5dab549aa3726aadbb5b930b685 100644 (file)
@@ -3,8 +3,9 @@ use proxmox::tools::io::WriteExt;
 use std::io::{Seek, SeekFrom, Write};
 use std::sync::Arc;
 
+use pbs_tools::crypt_config::CryptConfig;
+
 use crate::checksum_writer::ChecksumWriter;
-use crate::crypt_config::CryptConfig;
 use crate::crypt_writer::CryptWriter;
 use crate::file_formats::{self, DataBlobHeader, EncryptedDataBlobHeader};
 
diff --git a/pbs-datastore/src/key_derivation.rs b/pbs-datastore/src/key_derivation.rs
deleted file mode 100644 (file)
index 92e81ba..0000000
+++ /dev/null
@@ -1,432 +0,0 @@
-use std::io::Write;
-use std::path::Path;
-
-use anyhow::{bail, format_err, Context, Error};
-use serde::{Deserialize, Serialize};
-
-use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions};
-use proxmox::try_block;
-
-use pbs_api_types::{Kdf, KeyInfo};
-
-use crate::crypt_config::{CryptConfig, Fingerprint};
-
-/// Key derivation function configuration
-#[derive(Deserialize, Serialize, Clone, Debug)]
-pub enum KeyDerivationConfig {
-    Scrypt {
-        n: u64,
-        r: u64,
-        p: u64,
-        #[serde(with = "proxmox::tools::serde::bytes_as_base64")]
-        salt: Vec<u8>,
-    },
-    PBKDF2 {
-        iter: usize,
-        #[serde(with = "proxmox::tools::serde::bytes_as_base64")]
-        salt: Vec<u8>,
-    },
-}
-
-impl KeyDerivationConfig {
-
-    /// Derive a key from provided passphrase
-    pub fn derive_key(&self, passphrase: &[u8]) -> Result<[u8; 32], Error> {
-
-        let mut key = [0u8; 32];
-
-        match self {
-            KeyDerivationConfig::Scrypt { n, r, p, salt } => {
-                // estimated scrypt memory usage is 128*r*n*p
-                openssl::pkcs5::scrypt(
-                    passphrase,
-                    &salt,
-                    *n, *r, *p,
-                    1025*1024*1024,
-                    &mut key,
-                )?;
-
-                Ok(key)
-            }
-            KeyDerivationConfig::PBKDF2 { iter, salt } => {
-
-                 openssl::pkcs5::pbkdf2_hmac(
-                    passphrase,
-                    &salt,
-                    *iter,
-                    openssl::hash::MessageDigest::sha256(),
-                    &mut key,
-                )?;
-
-                Ok(key)
-            }
-        }
-    }
-}
-
-/// Encryption Key Configuration
-///
-/// We use this struct to store secret keys. When used with a key
-/// derivation function, the key data is encrypted (AES-CGM), and you
-/// need the password to restore the plain key.
-#[derive(Deserialize, Serialize, Clone, Debug)]
-pub struct KeyConfig {
-    pub kdf: Option<KeyDerivationConfig>,
-    #[serde(with = "proxmox::tools::serde::epoch_as_rfc3339")]
-    pub created: i64,
-    #[serde(with = "proxmox::tools::serde::epoch_as_rfc3339")]
-    pub modified: i64,
-    #[serde(with = "proxmox::tools::serde::bytes_as_base64")]
-    pub data: Vec<u8>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    #[serde(default)]
-    pub fingerprint: Option<Fingerprint>,
-    /// Password hint
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub hint: Option<String>,
-}
-
-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| pbs_tools::format::as_fingerprint(fp.bytes())),
-            hint: key_config.hint.clone(),
-        }
-    }
-}
-
-impl KeyConfig  {
-
-    /// Creates a new key using random data, protected by passphrase.
-    pub fn new(passphrase: &[u8], kdf: Kdf) -> Result<([u8;32], Self), Error> {
-        let mut key = [0u8; 32];
-        proxmox::sys::linux::fill_with_random_data(&mut key)?;
-        let key_config = Self::with_key(&key, passphrase, kdf)?;
-        Ok((key, key_config))
-    }
-
-    /// Creates a new, unencrypted key.
-    pub fn without_password(raw_key: [u8; 32]) -> Result<Self, Error> {
-        // always compute fingerprint
-        let crypt_config = CryptConfig::new(raw_key.clone())?;
-        let fingerprint = Some(crypt_config.fingerprint());
-
-        let created = proxmox::tools::time::epoch_i64();
-        Ok(Self {
-            kdf: None,
-            created,
-            modified: created,
-            data: raw_key.to_vec(),
-            fingerprint,
-            hint: None,
-        })
-    }
-
-    /// Creates a new instance, protect raw_key with passphrase.
-    pub fn with_key(
-        raw_key: &[u8; 32],
-        passphrase: &[u8],
-        kdf: Kdf,
-    ) -> Result<Self, Error> {
-
-        if raw_key.len() != 32 {
-            bail!("got strange key length ({} != 32)", raw_key.len())
-        }
-
-        let salt = proxmox::sys::linux::random_data(32)?;
-
-        let kdf = match kdf {
-            Kdf::Scrypt => KeyDerivationConfig::Scrypt {
-                n: 65536,
-                r: 8,
-                p: 1,
-                salt,
-            },
-            Kdf::PBKDF2 => KeyDerivationConfig::PBKDF2 {
-                iter: 65535,
-                salt,
-            },
-            Kdf::None => {
-                bail!("No key derivation function specified");
-            }
-        };
-
-        let derived_key = kdf.derive_key(passphrase)?;
-
-        let cipher = openssl::symm::Cipher::aes_256_gcm();
-
-        let iv = proxmox::sys::linux::random_data(16)?;
-        let mut tag = [0u8; 16];
-
-        let encrypted_key = openssl::symm::encrypt_aead(
-            cipher,
-            &derived_key,
-            Some(&iv),
-            b"",
-            raw_key,
-            &mut tag,
-        )?;
-
-        let mut enc_data = vec![];
-        enc_data.extend_from_slice(&iv);
-        enc_data.extend_from_slice(&tag);
-        enc_data.extend_from_slice(&encrypted_key);
-
-        let created = proxmox::tools::time::epoch_i64();
-
-        // always compute fingerprint
-        let crypt_config = CryptConfig::new(raw_key.clone())?;
-        let fingerprint = Some(crypt_config.fingerprint());
-
-        Ok(Self {
-            kdf: Some(kdf),
-            created,
-            modified: created,
-            data: enc_data,
-            fingerprint,
-            hint: None,
-        })
-    }
-
-    /// Loads a KeyConfig from path
-    pub fn load<P: AsRef<Path>>(path: P) -> Result<KeyConfig, Error> {
-        let keydata = file_get_contents(path)?;
-        let key_config: KeyConfig = serde_json::from_reader(&keydata[..])?;
-        Ok(key_config)
-    }
-
-    /// Decrypt key to get raw key data.
-    pub fn decrypt(
-        &self,
-        passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
-    ) -> Result<([u8;32], i64, Fingerprint), Error> {
-
-        let raw_data = &self.data;
-
-        let key = if let Some(ref kdf) = self.kdf {
-
-            let passphrase = passphrase()?;
-            if passphrase.len() < 5 {
-                bail!("Passphrase is too short!");
-            }
-
-            let derived_key = kdf.derive_key(&passphrase)?;
-
-            if raw_data.len() < 32 {
-                bail!("Unable to decrypt key - short data");
-            }
-            let iv = &raw_data[0..16];
-            let tag = &raw_data[16..32];
-            let enc_data = &raw_data[32..];
-
-            let cipher = openssl::symm::Cipher::aes_256_gcm();
-
-            openssl::symm::decrypt_aead(
-                cipher,
-                &derived_key,
-                Some(&iv),
-                b"",
-                &enc_data,
-                &tag,
-            ).map_err(|err| {
-                match self.hint {
-                    Some(ref hint) => {
-                        format_err!("Unable to decrypt key (password hint: {})", hint)
-                    }
-                    None => {
-                        format_err!("Unable to decrypt key (wrong password?) - {}", err)
-                    }
-                }
-            })?
-
-        } else {
-            raw_data.clone()
-        };
-
-        let mut result = [0u8; 32];
-        result.copy_from_slice(&key);
-
-        let crypt_config = CryptConfig::new(result.clone())?;
-        let fingerprint = crypt_config.fingerprint();
-        if let Some(ref stored_fingerprint) = self.fingerprint {
-            if &fingerprint != stored_fingerprint {
-                bail!(
-                    "KeyConfig contains wrong fingerprint {}, contained key has fingerprint {}",
-                    stored_fingerprint, fingerprint
-                );
-            }
-        }
-
-        Ok((result, self.created, fingerprint))
-    }
-
-    /// Store a KeyConfig to path
-    pub fn store<P: AsRef<Path>>(&self, path: P, replace: bool) -> Result<(), Error> {
-
-        let path: &Path = path.as_ref();
-
-        let data = serde_json::to_string(self)?;
-
-        try_block!({
-            if replace {
-                let mode = nix::sys::stat::Mode::S_IRUSR | nix::sys::stat::Mode::S_IWUSR;
-                replace_file(path, data.as_bytes(), CreateOptions::new().perm(mode))?;
-            } else {
-                use std::os::unix::fs::OpenOptionsExt;
-
-                let mut file = std::fs::OpenOptions::new()
-                    .write(true)
-                    .mode(0o0600)
-                    .create_new(true)
-                    .open(&path)?;
-
-                file.write_all(data.as_bytes())?;
-            }
-
-            Ok(())
-        }).map_err(|err: Error| format_err!("Unable to store key file {:?} - {}", path, err))?;
-
-        Ok(())
-    }
-}
-
-/// Loads a KeyConfig from path and decrypt it.
-pub fn load_and_decrypt_key(
-    path: &std::path::Path,
-    passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
-) -> Result<([u8;32], i64, Fingerprint), Error> {
-    decrypt_key(&file_get_contents(&path)?, passphrase)
-        .with_context(|| format!("failed to load decryption key from {:?}", path))
-}
-
-/// Decrypt a KeyConfig from raw keydata.
-pub fn decrypt_key(
-    mut keydata: &[u8],
-    passphrase: &dyn Fn() -> Result<Vec<u8>, Error>,
-) -> Result<([u8;32], i64, Fingerprint), Error> {
-    let key_config: KeyConfig = serde_json::from_reader(&mut keydata)?;
-    key_config.decrypt(passphrase)
-}
-
-/// RSA encrypt a KeyConfig using a public key
-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)
-}
-
-/// RSA deccrypt a KeyConfig using a private key
-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(&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,
-        ])),
-        hint: None,
-    };
-
-    let encrypted = rsa_encrypt_key_config(public, &key).expect("encryption failed");
-    let (decrypted, created, fingerprint) =
-        rsa_decrypt_key_config(private, &encrypted, &passphrase)
-            .expect("decryption failed");
-
-    assert_eq!(key.created, created);
-    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(),
-        modified: proxmox::tools::time::epoch_i64(),
-        data: (0u8..32u8).collect(),
-        fingerprint: Some(Fingerprint::new([0u8; 32])), // wrong FP
-        hint: None,
-    };
-
-    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,
-        hint: 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!(expected_fingerprint, fingerprint);
-
-    Ok(())
-}
index ae06686a595017f2c867c35ed942363b222b47c1..b842519eb4f5f20e0aa07710f95c2e4c0755f132 100644 (file)
@@ -186,7 +186,6 @@ pub mod checksum_writer;
 pub mod chunk_stat;
 pub mod chunk_store;
 pub mod chunker;
-pub mod crypt_config;
 pub mod crypt_reader;
 pub mod crypt_writer;
 pub mod data_blob;
@@ -194,7 +193,6 @@ pub mod data_blob_reader;
 pub mod data_blob_writer;
 pub mod file_formats;
 pub mod index;
-pub mod key_derivation;
 pub mod manifest;
 pub mod paperkey;
 pub mod prune;
@@ -210,15 +208,10 @@ pub use checksum_reader::ChecksumReader;
 pub use checksum_writer::ChecksumWriter;
 pub use chunk_store::ChunkStore;
 pub use chunker::Chunker;
-pub use crypt_config::{CryptConfig, CryptMode, Fingerprint};
 pub use crypt_reader::CryptReader;
 pub use crypt_writer::CryptWriter;
 pub use data_blob::DataBlob;
 pub use data_blob_reader::DataBlobReader;
 pub use data_blob_writer::DataBlobWriter;
-pub use key_derivation::{
-    decrypt_key, load_and_decrypt_key, rsa_decrypt_key_config, rsa_encrypt_key_config,
-};
-pub use key_derivation::{KeyConfig, KeyDerivationConfig};
 pub use manifest::BackupManifest;
 pub use store_progress::StoreProgress;
index 7799f906169aaba84d4b12336fe50fa216d7d72f..94e54e765089d4b316302d59849ebfc3cb53f5f7 100644 (file)
@@ -6,7 +6,10 @@ use anyhow::{bail, format_err, Error};
 use serde_json::{json, Value};
 use serde::{Deserialize, Serialize};
 
-use crate::{BackupDir, CryptMode, CryptConfig, Fingerprint};
+use pbs_tools::crypt_config::CryptConfig;
+use pbs_api_types::{CryptMode, Fingerprint};
+
+use crate::BackupDir;
 
 pub const MANIFEST_BLOB_NAME: &str = "index.json.blob";
 pub const MANIFEST_LOCK_NAME: &str = ".index.json.lck";
@@ -188,7 +191,7 @@ impl BackupManifest {
         if let Some(crypt_config) = crypt_config {
             let sig = self.signature(crypt_config)?;
             manifest["signature"] = proxmox::tools::digest_to_hex(&sig).into();
-            let fingerprint = &crypt_config.fingerprint();
+            let fingerprint = &Fingerprint::new(crypt_config.fingerprint());
             manifest["unprotected"]["key-fingerprint"] = serde_json::to_value(fingerprint)?;
         }
 
@@ -215,7 +218,7 @@ impl BackupManifest {
                     fingerprint,
                 ),
                 Some(crypt_config) => {
-                    let config_fp = crypt_config.fingerprint();
+                    let config_fp = Fingerprint::new(crypt_config.fingerprint());
                     if config_fp != fingerprint {
                         bail!(
                             "wrong key - manifest's key {} does not match provided key {}",
@@ -242,7 +245,7 @@ impl BackupManifest {
                 let fingerprint = &json["unprotected"]["key-fingerprint"];
                 if fingerprint != &Value::Null {
                     let fingerprint = serde_json::from_value(fingerprint.clone())?;
-                    let config_fp = crypt_config.fingerprint();
+                    let config_fp = Fingerprint::new(crypt_config.fingerprint());
                     if config_fp != fingerprint {
                         bail!(
                             "wrong key - unable to verify signature since manifest's key {} does not match provided key {}",
@@ -283,7 +286,7 @@ impl TryFrom<super::DataBlob> for BackupManifest {
 #[test]
 fn test_manifest_signature() -> Result<(), Error> {
 
-    use crate::{KeyDerivationConfig};
+    use pbs_config::key_config::KeyDerivationConfig;
 
     let pw = b"test";
 
index d90fd83a9dc35881697ce0e79472cf9e12e684bb..eb5896fc56ea1a8bc3a28665eae641c8ea2c7953 100644 (file)
@@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
 
 use proxmox::api::api;
 
-use crate::KeyConfig;
+use pbs_config::key_config::KeyConfig;
 
 #[api()]
 #[derive(Debug, Serialize, Deserialize)]
diff --git a/pbs-tools/src/crypt_config.rs b/pbs-tools/src/crypt_config.rs
new file mode 100644 (file)
index 0000000..604abae
--- /dev/null
@@ -0,0 +1,115 @@
+//! Wrappers for OpenSSL crypto functions
+//!
+//! We use this to encrypt and decrypt data chunks. Cipher is
+//! AES_256_GCM, which is fast and provides authenticated encryption.
+//!
+//! See the Wikipedia Artikel for [Authenticated
+//! encryption](https://en.wikipedia.org/wiki/Authenticated_encryption)
+//! for a short introduction.
+
+use anyhow::{Error};
+use openssl::hash::MessageDigest;
+use openssl::pkcs5::pbkdf2_hmac;
+use openssl::symm::{Cipher, Crypter, Mode};
+
+// openssl::sha::sha256(b"Proxmox Backup Encryption Key Fingerprint")
+/// This constant is used to compute fingerprints.
+const FINGERPRINT_INPUT: [u8; 32] = [
+    110, 208, 239, 119,  71,  31, 255,  77,
+    85, 199, 168, 254,  74, 157, 182,  33,
+    97,  64, 127,  19,  76, 114,  93, 223,
+    48, 153,  45,  37, 236,  69, 237,  38,
+];
+
+/// Encryption Configuration with secret key
+///
+/// This structure stores the secret key and provides helpers for
+/// authenticated encryption.
+pub struct CryptConfig {
+    // the Cipher
+    cipher: Cipher,
+    // A secrect key use to provide the chunk digest name space.
+    id_key: [u8; 32],
+    // Openssl hmac PKey of id_key
+    id_pkey: openssl::pkey::PKey<openssl::pkey::Private>,
+    // The private key used by the cipher.
+    enc_key: [u8; 32],
+}
+
+impl CryptConfig {
+
+    /// Create a new instance.
+    ///
+    /// We compute a derived 32 byte key using pbkdf2_hmac. This second
+    /// key is used in compute_digest.
+    pub fn new(enc_key: [u8; 32]) -> Result<Self, Error> {
+
+        let mut id_key = [0u8; 32];
+
+        pbkdf2_hmac(
+            &enc_key,
+            b"_id_key",
+            10,
+            MessageDigest::sha256(),
+            &mut id_key)?;
+
+        let id_pkey = openssl::pkey::PKey::hmac(&id_key).unwrap();
+
+        Ok(Self { id_key, id_pkey, enc_key, cipher: Cipher::aes_256_gcm() })
+    }
+
+    /// Expose Cipher (AES_256_GCM)
+    pub fn cipher(&self) -> &Cipher {
+        &self.cipher
+    }
+
+    /// Expose encryption key
+    pub fn enc_key(&self) -> &[u8; 32] {
+        &self.enc_key
+    }
+
+    /// Compute a chunk digest using a secret name space.
+    ///
+    /// Computes an SHA256 checksum over some secret data (derived
+    /// from the secret key) and the provided data. This ensures that
+    /// chunk digest values do not clash with values computed for
+    /// other sectret keys.
+    pub fn compute_digest(&self, data: &[u8]) -> [u8; 32] {
+        let mut hasher = openssl::sha::Sha256::new();
+        hasher.update(data);
+        hasher.update(&self.id_key); // at the end, to avoid length extensions attacks
+        hasher.finish()
+    }
+
+    /// Returns an openssl Signer using SHA256
+    pub fn data_signer(&self) -> openssl::sign::Signer {
+        openssl::sign::Signer::new(MessageDigest::sha256(), &self.id_pkey).unwrap()
+    }
+
+    /// Compute authentication tag (hmac/sha256)
+    ///
+    /// Computes an SHA256 HMAC using some secret data (derived
+    /// from the secret key) and the provided data.
+    pub fn compute_auth_tag(&self, data: &[u8]) -> [u8; 32] {
+        let mut signer = self.data_signer();
+        signer.update(data).unwrap();
+        let mut tag = [0u8; 32];
+        signer.sign(&mut tag).unwrap();
+        tag
+    }
+
+    /// Computes a fingerprint for the secret key.
+    ///
+    /// This computes a digest using the derived key (id_key) in order
+    /// to hinder brute force attacks.
+    pub fn fingerprint(&self) -> [u8; 32] {
+        self.compute_digest(&FINGERPRINT_INPUT)
+    }
+
+    /// Returns an openssl Crypter using AES_256_GCM,
+    pub fn data_crypter(&self, iv: &[u8; 16], mode: Mode) -> Result<Crypter, Error>  {
+        let mut crypter = openssl::symm::Crypter::new(self.cipher, mode, &self.enc_key, Some(iv))?;
+        crypter.aad_update(b"")?; //??
+        Ok(crypter)
+    }
+}
index ad05a55cbc7a2586998e8d97ccc4e1ac76372263..bb82f7d4454ed14a8131a70fd5f7ae6773e5919e 100644 (file)
@@ -6,6 +6,7 @@ pub mod broadcast_future;
 pub mod cert;
 pub mod cli;
 pub mod compression;
+pub mod crypt_config;
 pub mod format;
 pub mod fd;
 pub mod fs;
index b6e138072281e74857fdea8c5754a7d756efeb42..b1ecf3e4592271f8d1178e2a0970dabf17fadb3f 100644 (file)
@@ -26,6 +26,7 @@ proxmox = { version = "0.13.0", features = [ "sortable-macro", "api-macro", "cli
 
 pbs-api-types = { path = "../pbs-api-types" }
 pbs-buildcfg = { path = "../pbs-buildcfg" }
+pbs-config = { path = "../pbs-config" }
 pbs-client = { path = "../pbs-client" }
 pbs-datastore = { path = "../pbs-datastore" }
 pbs-fuse-loop = { path = "../pbs-fuse-loop" }
index 1d31a7d8f051ee6eac9d73fb64d79ae585099162..e951b3050cd058f844e1c7e1210cf5eed856724f 100644 (file)
@@ -19,9 +19,10 @@ use proxmox::api::{
     schema::ApiType,
 };
 
+use pbs_tools::crypt_config::CryptConfig;
+use pbs_config::key_config::{KeyDerivationConfig, load_and_decrypt_key};
 use pbs_client::tools::key_source::get_encryption_key_password;
 use pbs_client::{BackupRepository, BackupWriter};
-use pbs_datastore::{CryptConfig, KeyDerivationConfig, load_and_decrypt_key};
 use pbs_datastore::data_blob::{DataBlob, DataChunkBuilder};
 
 use crate::{
index adb8fcdc36f5eb24baa68b9d9072c770b4d6fd0e..186de913f54389b2aa5eb54f7fc862ecd6a9fde4 100644 (file)
@@ -10,6 +10,7 @@ use proxmox::api::{api, cli::*};
 use pbs_client::tools::key_source::get_encryption_key_password;
 use pbs_client::{BackupReader, RemoteChunkReader};
 use pbs_tools::json::required_string_param;
+use pbs_tools::crypt_config::CryptConfig;
 
 use crate::{
     REPO_URL_SCHEMA,
@@ -31,7 +32,6 @@ use crate::{
     BufferedDynamicReadAt,
     CatalogReader,
     CATALOG_NAME,
-    CryptConfig,
     DynamicIndexReader,
     IndexFile,
     Shell,
index aca335f79ede82c6017e8cb3c269da2ae473bac4..79b70faef62d6bd556c2020af902949d0c1636cb 100644 (file)
@@ -15,7 +15,7 @@ use proxmox::sys::linux::tty;
 use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions};
 
 use pbs_api_types::{RsaPubKeyInfo, PASSWORD_HINT_SCHEMA, Kdf, KeyInfo};
-use pbs_datastore::{KeyConfig, rsa_decrypt_key_config};
+use pbs_config::key_config::{KeyConfig, rsa_decrypt_key_config};
 use pbs_datastore::paperkey::{generate_paper_key, PaperkeyFormat};
 use pbs_client::tools::key_source::{
     find_default_encryption_key, find_default_master_pubkey, get_encryption_key_password,
index d8dd04456a99b617c5fd46dcb32c5abfaef2c29f..857fdb6291af0cc76cab82346b809dab31340177 100644 (file)
@@ -29,7 +29,7 @@ use pxar::accessor::{MaybeReady, ReadAt, ReadAtOperation};
 
 use pbs_api_types::{
     BACKUP_ID_SCHEMA, BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA, Authid, CryptMode, GroupListItem,
-    PruneListItem, SnapshotListItem, StorageStatus,
+    PruneListItem, SnapshotListItem, StorageStatus, Fingerprint,
 };
 use pbs_client::{
     BACKUP_SOURCE_SCHEMA,
@@ -60,7 +60,8 @@ use pbs_client::tools::{
     },
     CHUNK_SIZE_SCHEMA, REPO_URL_SCHEMA,
 };
-use pbs_datastore::{CATALOG_NAME, CryptConfig, KeyConfig, decrypt_key, rsa_encrypt_key_config};
+use pbs_config::key_config::{KeyConfig, decrypt_key, rsa_encrypt_key_config};
+use pbs_datastore::CATALOG_NAME;
 use pbs_datastore::backup_info::{BackupDir, BackupGroup};
 use pbs_datastore::catalog::{BackupCatalogWriter, CatalogReader, CatalogWriter};
 use pbs_datastore::chunk_store::verify_chunk_size;
@@ -75,6 +76,7 @@ use pbs_datastore::prune::PruneOptions;
 use pbs_tools::sync::StdChannelWriter;
 use pbs_tools::tokio::TokioWriterAdapter;
 use pbs_tools::json;
+use pbs_tools::crypt_config::CryptConfig;
 
 mod benchmark;
 pub use benchmark::*;
@@ -1131,7 +1133,7 @@ async fn restore(param: Value) -> Result<Value, Error> {
                 eprintln!("{}", format_key_source(&key.source, "encryption"));
             }
             if let Some(config) = &crypt_config {
-                eprintln!("Fingerprint: {}", config.fingerprint());
+                eprintln!("Fingerprint: {}", Fingerprint::new(config.fingerprint()));
             }
         }
         manifest.check_fingerprint(crypt_config.as_ref().map(Arc::as_ref))?;
index 70123a36757003b9f503a5f8b5087bd95328dae2..e4544c076584e5bd061b91410276283f392d240c 100644 (file)
@@ -17,7 +17,9 @@ use proxmox::{sortable, identity};
 use proxmox::api::{ApiHandler, ApiMethod, RpcEnvironment, schema::*, cli::*};
 use proxmox::tools::fd::Fd;
 
-use pbs_datastore::{BackupDir, BackupGroup, CryptConfig, load_and_decrypt_key};
+use pbs_tools::crypt_config::CryptConfig;
+use pbs_config::key_config::load_and_decrypt_key;
+use pbs_datastore::{BackupDir, BackupGroup, };
 use pbs_datastore::index::IndexFile;
 use pbs_datastore::dynamic_index::BufferedDynamicReader;
 use pbs_datastore::cached_chunk_reader::CachedChunkReader;
index b63e84a6cda78105f5171da86fa94694c14ecc8a..4465f69ac447e561ea32ffeaaaa207dd21284944 100644 (file)
@@ -8,10 +8,11 @@ use proxmox::{
     tools::fs::file_get_contents,
 };
 
-use pbs_api_types::SnapshotListItem;
+use pbs_tools::crypt_config::CryptConfig;
+use pbs_config::key_config::decrypt_key;
+use pbs_api_types::{SnapshotListItem, CryptMode};
 use pbs_client::tools::key_source::get_encryption_key_password;
-use pbs_datastore::{BackupGroup, CryptMode, CryptConfig, decrypt_key};
-use pbs_datastore::data_blob::DataBlob;
+use pbs_datastore::{DataBlob, BackupGroup};
 use pbs_tools::json::required_string_param;
 
 use crate::{
index cde0f33ffeb9566ba1bad061b313ba7e88e4a67c..7f1f596d1f936bb36932e8e8e790ceb34f50611d 100644 (file)
@@ -11,6 +11,7 @@ serde_json = "1.0"
 
 proxmox = { version = "0.13.0", features = [ "api-macro", "cli" ] }
 
+pbs-config = { path = "../pbs-config" }
 pbs-client = { path = "../pbs-client" }
 pbs-datastore = { path = "../pbs-datastore" }
 pbs-runtime = { path = "../pbs-runtime" }
index ee05e8b583c52ff7f8b061738169270efa83a52a..9fe2ac1a61924afd5acf0b03b95d841c5fe39858 100644 (file)
@@ -12,6 +12,8 @@ use proxmox::api::cli::{
 };
 use proxmox::api::{api, cli::*};
 
+use pbs_tools::cli::outfile_or_stdout;
+use pbs_tools::crypt_config::CryptConfig;
 use pbs_datastore::dynamic_index::DynamicIndexReader;
 use pbs_datastore::file_formats::{
     COMPRESSED_BLOB_MAGIC_1_0, DYNAMIC_SIZED_CHUNK_INDEX_1_0, ENCRYPTED_BLOB_MAGIC_1_0,
@@ -19,11 +21,10 @@ use pbs_datastore::file_formats::{
 };
 use pbs_datastore::fixed_index::FixedIndexReader;
 use pbs_datastore::index::IndexFile;
-use pbs_datastore::{load_and_decrypt_key, CryptConfig, DataBlob};
-
+use pbs_datastore::DataBlob;
+use pbs_config::key_config::load_and_decrypt_key;
 use pbs_client::tools::key_source::get_encryption_key_password;
 
-use pbs_tools::cli::outfile_or_stdout;
 
 /// Decodes a blob and writes its content either to stdout or into a file
 fn decode_blob(
index 7e890e73274830f2e72f57e2148148921022d4c0..098dea09638db2aa36a7b75acfab0eed36ec6a5a 100644 (file)
@@ -7,17 +7,17 @@ use serde_json::Value;
 
 use proxmox::api::api;
 use proxmox::api::cli::{CliCommand, CliCommandMap, CommandLineInterface};
+use proxmox::tools::digest_to_hex;
 
+use pbs_tools::crypt_config::CryptConfig;
 use pbs_datastore::dynamic_index::DynamicIndexReader;
 use pbs_datastore::file_formats::{DYNAMIC_SIZED_CHUNK_INDEX_1_0, FIXED_SIZED_CHUNK_INDEX_1_0};
 use pbs_datastore::fixed_index::FixedIndexReader;
 use pbs_datastore::index::IndexFile;
-use pbs_datastore::{load_and_decrypt_key, CryptConfig, DataBlob};
-
+use pbs_datastore::DataBlob;
+use pbs_config::key_config::load_and_decrypt_key;
 use pbs_client::tools::key_source::get_encryption_key_password;
 
-use proxmox::tools::digest_to_hex;
-
 #[api(
     input: {
         properties: {
index 9890656c1ec925037ed218f7bebe92080099230e..127397b660880d2c8f0a12b8fc76d4c2dbfdeba8 100644 (file)
@@ -20,6 +20,7 @@ proxmox = { version = "0.13.0", features = [ "api-macro", "cli" ] }
 
 pbs-api-types = { path = "../pbs-api-types" }
 pbs-buildcfg = { path = "../pbs-buildcfg" }
+pbs-config = { path = "../pbs-config" }
 pbs-client = { path = "../pbs-client" }
 pbs-datastore = { path = "../pbs-datastore" }
 pbs-runtime = { path = "../pbs-runtime" }
index 48963d2c42772d0eda19457633c0d7ca6dd20467..957ce3f08ba63314225bdce1d91803d0d67374f3 100644 (file)
@@ -17,13 +17,14 @@ use proxmox::tools::fs::{create_path, CreateOptions};
 use pxar::accessor::aio::Accessor;
 use pxar::decoder::aio::Decoder;
 
+use pbs_tools::crypt_config::CryptConfig;
 use pbs_api_types::CryptMode;
-use pbs_datastore::{CryptConfig, CATALOG_NAME};
+use pbs_datastore::CATALOG_NAME;
 use pbs_datastore::backup_info::BackupDir;
 use pbs_datastore::catalog::{ArchiveEntry, CatalogReader, DirEntryAttribute};
 use pbs_datastore::dynamic_index::{BufferedDynamicReader, LocalDynamicReadAt};
 use pbs_datastore::index::IndexFile;
-use pbs_datastore::key_derivation::decrypt_key;
+use pbs_config::key_config::decrypt_key;
 use pbs_client::{BackupReader, RemoteChunkReader};
 use pbs_client::pxar::{create_zip, extract_sub_dir, extract_sub_dir_seq};
 use pbs_client::tools::{
index 9c4b5e058e961651108d05a35ac29f8802d5158f..355efdcca24ab52b2ccddcacdaeeed36b0b33c65 100644 (file)
@@ -12,7 +12,7 @@ use proxmox::{
 };
 
 use pbs_api_types::{Fingerprint, KeyInfo, Kdf};
-use pbs_datastore::key_derivation::KeyConfig;
+use pbs_config::key_config::KeyConfig;
 use pbs_config::open_backup_lockfile;
 
 use crate::{
index 588563c5d46b2f42f0efbe5bfba7c17a46443d43..1e67b5611f0476cb8f09c4ef469619a093d7fe13 100644 (file)
@@ -4,7 +4,8 @@ use std::sync::Arc;
 
 use anyhow::{bail, Error};
 
-use pbs_datastore::crypt_config::{CryptConfig, CryptMode};
+use pbs_tools::crypt_config::CryptConfig;
+use pbs_api_types::CryptMode;
 use pbs_datastore::data_blob::DataBlob;
 use pbs_datastore::read_chunk::{ReadChunk, AsyncReadChunk};
 
index 72dda1f59ca5ffc025713c7c824e9873df4fa3d9..6386150860b2397321867ad16ca5c999c807064a 100644 (file)
@@ -17,8 +17,7 @@ use serde::{Deserialize, Serialize};
 
 use proxmox::tools::fs::file_read_optional_string;
 use pbs_api_types::Fingerprint;
-use pbs_datastore::key_derivation::KeyConfig;
-
+use pbs_config::key_config::KeyConfig;
 use pbs_config::{open_backup_lockfile, replace_secret_config};
 
 mod hex_key {
index 0f44c75097040ad4ee20ed327ac630fbb9cde993..e5040613f1e60dc595a14418c735f0dde6f26d30 100644 (file)
@@ -28,7 +28,7 @@ use proxmox::{
 };
 
 use pbs_api_types::Fingerprint;
-use pbs_datastore::key_derivation::KeyConfig;
+use pbs_config::key_config::KeyConfig;
 use pbs_tools::run_command;
 
 use crate::{
index cc8553039b9725f49c41352bdf60fe4f81344d76..a42ebafeea3219e8315eec9b22d63e91d1ed6117 100644 (file)
@@ -28,11 +28,10 @@ use proxmox::{
     api::section_config::SectionConfigData,
 };
 
-use pbs_api_types::Fingerprint;
-use pbs_datastore::key_derivation::KeyConfig;
+use pbs_api_types::{VirtualTapeDrive, LtoTapeDrive, Fingerprint};
+use pbs_config::key_config::KeyConfig;
 use pbs_datastore::task::TaskState;
 use pbs_datastore::task_log;
-use pbs_api_types::{VirtualTapeDrive, LtoTapeDrive};
 
 use crate::{
     server::{
index 5d7a10ad9aac0a5eb506d007785b1895751b6256..0dadeedee7a2abd6b9d95e79a30ef43270a5c2ac 100644 (file)
@@ -10,7 +10,7 @@ use proxmox::tools::{
     fs::{replace_file, CreateOptions},
 };
 
-use pbs_datastore::key_derivation::KeyConfig;
+use pbs_config::key_config::KeyConfig;
 
 use crate::{
     tape::{
index 9f50ba01ccc0ebe07b512399a7f5999a04a7c230..a20c097243da58db7753296f5b460d31e193258b 100644 (file)
@@ -4,7 +4,8 @@ use std::io::Cursor;
 use std::io::{Read, Write, Seek, SeekFrom };
 use lazy_static::lazy_static;
 
-use pbs_datastore::{CryptConfig, DataBlob, DataBlobReader, DataBlobWriter};
+use pbs_tools::crypt_config::CryptConfig;
+use pbs_datastore::{DataBlob, DataBlobReader, DataBlobWriter};
 
 lazy_static! {
     static ref TEST_DATA: Vec<u8> = {