]> git.proxmox.com Git - proxmox-backup.git/blobdiff - src/backup/crypt_config.rs
switch from failure to anyhow
[proxmox-backup.git] / src / backup / crypt_config.rs
index 49aab44b5fc9e50b583da00e8154c84c1107479d..771d41e01e5156d5afa12c76a8d6c0c2a9d389a8 100644 (file)
@@ -6,12 +6,12 @@
 //! See the Wikipedia Artikel for [Authenticated
 //! encryption](https://en.wikipedia.org/wiki/Authenticated_encryption)
 //! for a short introduction.
-use failure::*;
-use proxmox::tools;
+use anyhow::{bail, Error};
 use openssl::pkcs5::pbkdf2_hmac;
 use openssl::hash::MessageDigest;
 use openssl::symm::{decrypt_aead, Cipher, Crypter, Mode};
 use std::io::Write;
+use chrono::{Local, TimeZone, DateTime};
 
 /// Encryption Configuration with secret key
 ///
@@ -21,9 +21,12 @@ pub struct CryptConfig {
     // the Cipher
     cipher: Cipher,
     // A secrect key use to provide the chunk digest name space.
-    id_key: Vec<u8>,
+    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 {
@@ -34,7 +37,7 @@ impl CryptConfig {
     /// key is used in compute_digest.
     pub fn new(enc_key: [u8; 32]) -> Result<Self, Error> {
 
-        let mut id_key = tools::vec::undefined(32);
+        let mut id_key = [0u8; 32];
 
         pbkdf2_hmac(
             &enc_key,
@@ -43,7 +46,14 @@ impl CryptConfig {
             MessageDigest::sha256(),
             &mut id_key)?;
 
-        Ok(Self { id_key, enc_key, cipher: Cipher::aes_256_gcm() })
+        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
+    pub fn cipher(&self) -> &Cipher {
+        &self.cipher
     }
 
     /// Compute a chunk digest using a secret name space.
@@ -57,8 +67,29 @@ impl CryptConfig {
         let mut hasher = openssl::sha::Sha256::new();
         hasher.update(&self.id_key);
         hasher.update(data);
-        let digest = hasher.finish();
-        digest
+        hasher.finish()
+    }
+
+    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
+    }
+
+    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)
     }
 
     /// Encrypt data using a random 16 byte IV.
@@ -75,8 +106,7 @@ impl CryptConfig {
 
         let mut tag = [0u8; 16];
 
-        let mut c = Crypter::new(self.cipher, Mode::Encrypt, &self.enc_key, Some(&iv))?;
-        c.aad_update(b"")?; //??
+        let mut c = self.data_crypter(&iv, Mode::Encrypt)?;
 
         const BUFFER_SIZE: usize = 32*1024;
 
@@ -106,34 +136,26 @@ impl CryptConfig {
         Ok((iv, tag))
     }
 
-    /// Decompress and decrypt chunk, verify MAC.
-    ///
-    /// Binrary ``data`` is expected to be in format returned by encode_chunk. The magic number
-    /// is not used here.
-    pub fn decode_compressed_chunk(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
-
-        if data.len() < 44 {
-            bail!("Invalid chunk len (<44)");
-        }
-
-        // let magic = &data[0..8];
-        // let crc = &data[8..12];
-        let iv = &data[12..28];
-        let mac = &data[28..44];
+    /// Decompress and decrypt data, verify MAC.
+    pub fn decode_compressed_chunk(
+        &self,
+        data: &[u8],
+        iv: &[u8; 16],
+        tag: &[u8; 16],
+    ) -> Result<Vec<u8>, Error> {
 
         let dec = Vec::with_capacity(1024*1024);
 
         let mut decompressor = zstd::stream::write::Decoder::new(dec)?;
 
-        let mut c = Crypter::new(self.cipher, Mode::Decrypt, &self.enc_key, Some(iv))?;
-        c.aad_update(b"")?; //??
+        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 = 44;
+        let mut start = 0;
         loop {
             let mut end = start + max_decoder_input;
             if end > data.len() { end = data.len(); }
@@ -146,7 +168,7 @@ impl CryptConfig {
             }
         }
 
-        c.set_tag(mac)?;
+        c.set_tag(tag)?;
         let rest = c.finalize(&mut decr_buf)?;
         if rest > 0 { decompressor.write_all(&decr_buf[..rest])?; }
 
@@ -155,30 +177,41 @@ impl CryptConfig {
         Ok(decompressor.into_inner())
     }
 
-    /// Decrypt chunk, verify MAC.
-    ///
-    /// Binrary ``data`` is expected to be in format returned by encode_chunk. The magic number
-    /// is not used here.
-    pub fn decode_uncompressed_chunk(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
-
-        if data.len() < 44 {
-            bail!("Invalid chunk len (<44)");
-        }
-
-        // let magic = &data[0..8];
-        // let crc = &data[8..12];
-        let iv = &data[12..28];
-        let mac = &data[28..44];
+    /// Decrypt data, verify tag.
+    pub fn decode_uncompressed_chunk(
+        &self,
+        data: &[u8],
+        iv: &[u8; 16],
+        tag: &[u8; 16],
+    ) -> Result<Vec<u8>, Error> {
 
         let decr_data = decrypt_aead(
             self.cipher,
             &self.enc_key,
             Some(iv),
             b"", //??
-            &data[44..],
-            mac,
+            data,
+            tag,
         )?;
 
         Ok(decr_data)
     }
+
+    pub fn generate_rsa_encoded_key(
+        &self,
+        rsa: openssl::rsa::Rsa<openssl::pkey::Public>,
+        created: DateTime<Local>,
+    ) -> Result<Vec<u8>, Error> {
+
+         let modified = Local.timestamp(Local::now().timestamp(), 0);
+        let key_config = super::KeyConfig { kdf: None, created, modified, data: self.enc_key.to_vec() };
+        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)
+    }
 }