]> git.proxmox.com Git - proxmox-backup.git/blobdiff - src/backup/crypt_config.rs
avoid chrono dependency, depend on proxmox 0.3.8
[proxmox-backup.git] / src / backup / crypt_config.rs
index f826d65bd2c3da0fd807b29cfa35bd083e058bbe..4be728d9f3cff5235983aaa516150494224b75a5 100644 (file)
@@ -6,13 +6,29 @@
 //! See the Wikipedia Artikel for [Authenticated
 //! encryption](https://en.wikipedia.org/wiki/Authenticated_encryption)
 //! for a short introduction.
-use failure::*;
-use proxmox::tools;
-use openssl::pkcs5::pbkdf2_hmac;
+
+use std::io::Write;
+
+use anyhow::{bail, Error};
 use openssl::hash::MessageDigest;
+use openssl::pkcs5::pbkdf2_hmac;
 use openssl::symm::{decrypt_aead, Cipher, Crypter, Mode};
-use std::io::Write;
-use chrono::{Local, TimeZone, DateTime};
+use serde::{Deserialize, Serialize};
+
+use proxmox::api::api;
+
+#[api(default: "encrypt")]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
+#[serde(rename_all = "kebab-case")]
+/// Defines whether data is encrypted (using an AEAD cipher), only signed, or neither.
+pub enum CryptMode {
+    /// Don't encrypt.
+    None,
+    /// Encrypt.
+    Encrypt,
+    /// Only sign.
+    SignOnly,
+}
 
 /// Encryption Configuration with secret key
 ///
@@ -22,7 +38,9 @@ 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],
 }
@@ -35,7 +53,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,
@@ -44,7 +62,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.
@@ -54,12 +79,32 @@ impl CryptConfig {
     /// chunk digest values do not clash with values computed for
     /// other sectret keys.
     pub fn compute_digest(&self, data: &[u8]) -> [u8; 32] {
-        // FIXME: use HMAC-SHA256 instead??
         let mut hasher = openssl::sha::Sha256::new();
-        hasher.update(&self.id_key);
         hasher.update(data);
-        let digest = hasher.finish();
-        digest
+        hasher.update(&self.id_key); // at the end, to avoid length extensions attacks
+        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.
@@ -76,8 +121,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;
 
@@ -107,7 +151,7 @@ impl CryptConfig {
         Ok((iv, tag))
     }
 
-    /// Decompress and decrypt date, verify MAC.
+    /// Decompress and decrypt data, verify MAC.
     pub fn decode_compressed_chunk(
         &self,
         data: &[u8],
@@ -119,8 +163,7 @@ impl CryptConfig {
 
         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;
 
@@ -172,10 +215,10 @@ impl CryptConfig {
     pub fn generate_rsa_encoded_key(
         &self,
         rsa: openssl::rsa::Rsa<openssl::pkey::Public>,
-        created: DateTime<Local>,
+        created: i64,
     ) -> Result<Vec<u8>, Error> {
 
-         let modified = Local.timestamp(Local::now().timestamp(), 0);
+        let modified = proxmox::tools::time::epoch_i64();
         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();