From ed2080762c393f67055eabd04fa7e66313f35f88 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Mon, 6 Sep 2021 11:48:08 +0200 Subject: [PATCH] move data_blob encode/decode from crypt_config.rs to data_blob.rs --- pbs-datastore/src/crypt_config.rs | 122 +++--------------------- pbs-datastore/src/data_blob.rs | 124 ++++++++++++++++++++++++- proxmox-backup-client/src/benchmark.rs | 4 +- proxmox-backup-client/src/key.rs | 4 +- 4 files changed, 138 insertions(+), 116 deletions(-) diff --git a/pbs-datastore/src/crypt_config.rs b/pbs-datastore/src/crypt_config.rs index a2f51302..c3e803a4 100644 --- a/pbs-datastore/src/crypt_config.rs +++ b/pbs-datastore/src/crypt_config.rs @@ -7,12 +7,10 @@ //! encryption](https://en.wikipedia.org/wiki/Authenticated_encryption) //! for a short introduction. -use std::io::Write; - use anyhow::{Error}; use openssl::hash::MessageDigest; use openssl::pkcs5::pbkdf2_hmac; -use openssl::symm::{decrypt_aead, Cipher, Crypter, Mode}; +use openssl::symm::{Cipher, Crypter, Mode}; pub use pbs_api_types::{CryptMode, Fingerprint}; @@ -62,11 +60,16 @@ impl CryptConfig { Ok(Self { id_key, id_pkey, enc_key, cipher: Cipher::aes_256_gcm() }) } - /// Expose Cipher + /// 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 @@ -80,6 +83,7 @@ impl CryptConfig { 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() } @@ -96,118 +100,18 @@ impl CryptConfig { 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 { let mut crypter = openssl::symm::Crypter::new(self.cipher, mode, &self.enc_key, Some(iv))?; crypter.aad_update(b"")?; //?? Ok(crypter) } - - /// Encrypt data using a random 16 byte IV. - /// - /// Writes encrypted data to ``output``, Return the used IV and computed MAC. - pub fn encrypt_to( - &self, - data: &[u8], - mut output: W, - ) -> Result<([u8;16], [u8;16]), Error> { - - let mut iv = [0u8; 16]; - proxmox::sys::linux::fill_with_random_data(&mut iv)?; - - let mut tag = [0u8; 16]; - - let mut c = self.data_crypter(&iv, Mode::Encrypt)?; - - const BUFFER_SIZE: usize = 32*1024; - - let mut encr_buf = [0u8; BUFFER_SIZE]; - let max_encoder_input = BUFFER_SIZE - self.cipher.block_size(); - - let mut start = 0; - loop { - let mut end = start + max_encoder_input; - if end > data.len() { end = data.len(); } - if end > start { - let count = c.update(&data[start..end], &mut encr_buf)?; - output.write_all(&encr_buf[..count])?; - start = end; - } else { - break; - } - } - - let rest = c.finalize(&mut encr_buf)?; - if rest > 0 { output.write_all(&encr_buf[..rest])?; } - - output.flush()?; - - c.get_tag(&mut tag)?; - - Ok((iv, tag)) - } - - /// Decompress and decrypt data, verify MAC. - pub fn decode_compressed_chunk( - &self, - data: &[u8], - iv: &[u8; 16], - tag: &[u8; 16], - ) -> Result, Error> { - - let dec = Vec::with_capacity(1024*1024); - - let mut decompressor = zstd::stream::write::Decoder::new(dec)?; - - let mut c = self.data_crypter(iv, Mode::Decrypt)?; - - const BUFFER_SIZE: usize = 32*1024; - - let mut decr_buf = [0u8; BUFFER_SIZE]; - let max_decoder_input = BUFFER_SIZE - self.cipher.block_size(); - - let mut start = 0; - loop { - let mut end = start + max_decoder_input; - if end > data.len() { end = data.len(); } - if end > start { - let count = c.update(&data[start..end], &mut decr_buf)?; - decompressor.write_all(&decr_buf[0..count])?; - start = end; - } else { - break; - } - } - - c.set_tag(tag)?; - let rest = c.finalize(&mut decr_buf)?; - if rest > 0 { decompressor.write_all(&decr_buf[..rest])?; } - - decompressor.flush()?; - - Ok(decompressor.into_inner()) - } - - /// Decrypt data, verify tag. - pub fn decode_uncompressed_chunk( - &self, - data: &[u8], - iv: &[u8; 16], - tag: &[u8; 16], - ) -> Result, Error> { - - let decr_data = decrypt_aead( - self.cipher, - &self.enc_key, - Some(iv), - b"", //?? - data, - tag, - )?; - - Ok(decr_data) - } } diff --git a/pbs-datastore/src/data_blob.rs b/pbs-datastore/src/data_blob.rs index c582e6b7..ef7d74a7 100644 --- a/pbs-datastore/src/data_blob.rs +++ b/pbs-datastore/src/data_blob.rs @@ -1,6 +1,8 @@ use std::convert::TryInto; +use std::io::Write; use anyhow::{bail, Error}; +use openssl::symm::{decrypt_aead, Mode}; use proxmox::tools::io::{ReadExt, WriteExt}; @@ -119,7 +121,7 @@ impl DataBlob { raw_data.write_le_value(dummy_head)?; } - let (iv, tag) = config.encrypt_to(data, &mut raw_data)?; + let (iv, tag) = Self::encrypt_to(&config, data, &mut raw_data)?; let head = EncryptedDataBlobHeader { head: DataBlobHeader { magic, crc: [0; 4] }, iv, tag, @@ -215,9 +217,9 @@ impl DataBlob { if let Some(config) = config { let data = if magic == &ENCR_COMPR_BLOB_MAGIC_1_0 { - config.decode_compressed_chunk(&self.raw_data[header_len..], &head.iv, &head.tag)? + Self::decode_compressed_chunk(config, &self.raw_data[header_len..], &head.iv, &head.tag)? } else { - config.decode_uncompressed_chunk(&self.raw_data[header_len..], &head.iv, &head.tag)? + Self::decode_uncompressed_chunk(config, &self.raw_data[header_len..], &head.iv, &head.tag)? }; if let Some(digest) = digest { Self::verify_digest(&data, Some(config), digest)?; @@ -322,6 +324,122 @@ impl DataBlob { Ok(()) } + + /// Benchmark encryption speed + pub fn encrypt_benchmark( + config: &CryptConfig, + data: &[u8], + output: W, + ) -> Result<(), Error> { + let _ = Self::encrypt_to(config, data, output)?; + Ok(()) + } + + // Encrypt data using a random 16 byte IV. + // + // Writes encrypted data to ``output``, Return the used IV and computed MAC. + fn encrypt_to( + config: &CryptConfig, + data: &[u8], + mut output: W, + ) -> Result<([u8;16], [u8;16]), Error> { + + let mut iv = [0u8; 16]; + proxmox::sys::linux::fill_with_random_data(&mut iv)?; + + let mut tag = [0u8; 16]; + + let mut c = config.data_crypter(&iv, Mode::Encrypt)?; + + const BUFFER_SIZE: usize = 32*1024; + + let mut encr_buf = [0u8; BUFFER_SIZE]; + let max_encoder_input = BUFFER_SIZE - config.cipher().block_size(); + + let mut start = 0; + loop { + let mut end = start + max_encoder_input; + if end > data.len() { end = data.len(); } + if end > start { + let count = c.update(&data[start..end], &mut encr_buf)?; + output.write_all(&encr_buf[..count])?; + start = end; + } else { + break; + } + } + + let rest = c.finalize(&mut encr_buf)?; + if rest > 0 { output.write_all(&encr_buf[..rest])?; } + + output.flush()?; + + c.get_tag(&mut tag)?; + + Ok((iv, tag)) + } + + // Decompress and decrypt data, verify MAC. + fn decode_compressed_chunk( + config: &CryptConfig, + data: &[u8], + iv: &[u8; 16], + tag: &[u8; 16], + ) -> Result, Error> { + + let dec = Vec::with_capacity(1024*1024); + + let mut decompressor = zstd::stream::write::Decoder::new(dec)?; + + let mut c = config.data_crypter(iv, Mode::Decrypt)?; + + const BUFFER_SIZE: usize = 32*1024; + + let mut decr_buf = [0u8; BUFFER_SIZE]; + let max_decoder_input = BUFFER_SIZE - config.cipher().block_size(); + + let mut start = 0; + loop { + let mut end = start + max_decoder_input; + if end > data.len() { end = data.len(); } + if end > start { + let count = c.update(&data[start..end], &mut decr_buf)?; + decompressor.write_all(&decr_buf[0..count])?; + start = end; + } else { + break; + } + } + + c.set_tag(tag)?; + let rest = c.finalize(&mut decr_buf)?; + if rest > 0 { decompressor.write_all(&decr_buf[..rest])?; } + + decompressor.flush()?; + + Ok(decompressor.into_inner()) + } + + // Decrypt data, verify tag. + fn decode_uncompressed_chunk( + config: &CryptConfig, + data: &[u8], + iv: &[u8; 16], + tag: &[u8; 16], + ) -> Result, Error> { + + let decr_data = decrypt_aead( + *config.cipher(), + config.enc_key(), + Some(iv), + b"", //?? + data, + tag, + )?; + + Ok(decr_data) + } + } /// Builder for chunk DataBlobs diff --git a/proxmox-backup-client/src/benchmark.rs b/proxmox-backup-client/src/benchmark.rs index 9f205c27..1d31a7d8 100644 --- a/proxmox-backup-client/src/benchmark.rs +++ b/proxmox-backup-client/src/benchmark.rs @@ -22,7 +22,7 @@ use proxmox::api::{ 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::DataChunkBuilder; +use pbs_datastore::data_blob::{DataBlob, DataChunkBuilder}; use crate::{ KEYFILE_SCHEMA, REPO_URL_SCHEMA, @@ -333,7 +333,7 @@ fn test_crypt_speed( let mut bytes = 0; loop { let mut out = Vec::new(); - crypt_config.encrypt_to(&random_data, &mut out)?; + DataBlob::encrypt_benchmark(&crypt_config, &random_data, &mut out)?; bytes += random_data.len(); if start_time.elapsed().as_micros() > 1_000_000 { break; } } diff --git a/proxmox-backup-client/src/key.rs b/proxmox-backup-client/src/key.rs index c06926e3..aca335f7 100644 --- a/proxmox-backup-client/src/key.rs +++ b/proxmox-backup-client/src/key.rs @@ -14,8 +14,8 @@ use proxmox::api::schema::ApiType; use proxmox::sys::linux::tty; use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions}; -use pbs_api_types::{RsaPubKeyInfo, PASSWORD_HINT_SCHEMA}; -use pbs_datastore::{KeyConfig, KeyInfo, Kdf, rsa_decrypt_key_config}; +use pbs_api_types::{RsaPubKeyInfo, PASSWORD_HINT_SCHEMA, Kdf, KeyInfo}; +use pbs_datastore::{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,