//! 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
///
// 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],
}
/// 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,
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.
/// 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.
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;
Ok((iv, tag))
}
- /// Decompress and decrypt date, verify MAC.
+ /// Decompress and decrypt data, verify MAC.
pub fn decode_compressed_chunk(
&self,
data: &[u8],
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;
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();