Replace the encrypt and decrypt function by the CipherBox struct

This commit is contained in:
Rodolphe Bréard 2024-03-17 14:23:03 +01:00
parent 47557fe350
commit 749dc03f71
5 changed files with 84 additions and 69 deletions

View file

@ -32,61 +32,71 @@ pub(crate) struct EncryptedData {
pub(crate) ciphertext: Vec<u8>, pub(crate) ciphertext: Vec<u8>,
} }
#[inline] pub struct CipherBox<'a> {
fn generate_aad( ikm_list: &'a InputKeyMaterialList,
ikm_id: IkmId,
nonce: &[u8],
key_context: &KeyContext,
data_context: &DataContext,
time_period: Option<u64>,
) -> String {
let ikm_id_canon = canonicalize(&[ikm_id.to_le_bytes()]);
let nonce_canon = canonicalize(&[nonce]);
let elems = key_context.get_ctx_elems(time_period);
let key_context_canon = canonicalize(&elems);
let data_context_canon = canonicalize(data_context.get_ctx_elems());
join_canonicalized_str(&[
ikm_id_canon,
nonce_canon,
key_context_canon,
data_context_canon,
])
} }
pub fn encrypt( impl<'a> CipherBox<'a> {
ikml: &InputKeyMaterialList, pub fn new(ikm_list: &'a InputKeyMaterialList) -> Self {
key_context: &KeyContext, Self { ikm_list }
data: impl AsRef<[u8]>, }
data_context: &DataContext,
) -> Result<String> {
let tp = if key_context.is_periodic() {
let ts = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
key_context.get_time_period(ts)
} else {
None
};
let ikm = ikml.get_latest_ikm()?;
let key = derive_key(ikm, key_context, tp);
let gen_nonce_function = ikm.scheme.get_gen_nonce();
let nonce = gen_nonce_function()?;
let aad = generate_aad(ikm.id, &nonce, key_context, data_context, tp);
let encryption_function = ikm.scheme.get_encryption();
let encrypted_data = encryption_function(&key, &nonce, data.as_ref(), &aad)?;
Ok(storage::encode_cipher(ikm.id, &encrypted_data, tp))
}
pub fn decrypt( #[inline]
ikml: &InputKeyMaterialList, fn generate_aad(
key_context: &KeyContext, ikm_id: IkmId,
stored_data: &str, nonce: &[u8],
data_context: &DataContext, key_context: &KeyContext,
) -> Result<Vec<u8>> { data_context: &DataContext,
let (ikm_id, encrypted_data, tp) = storage::decode_cipher(stored_data)?; time_period: Option<u64>,
let ikm = ikml.get_ikm_by_id(ikm_id)?; ) -> String {
let key = derive_key(ikm, key_context, tp); let ikm_id_canon = canonicalize(&[ikm_id.to_le_bytes()]);
let aad = generate_aad(ikm.id, &encrypted_data.nonce, key_context, data_context, tp); let nonce_canon = canonicalize(&[nonce]);
let decryption_function = ikm.scheme.get_decryption(); let elems = key_context.get_ctx_elems(time_period);
decryption_function(&key, &encrypted_data, &aad) let key_context_canon = canonicalize(&elems);
let data_context_canon = canonicalize(data_context.get_ctx_elems());
join_canonicalized_str(&[
ikm_id_canon,
nonce_canon,
key_context_canon,
data_context_canon,
])
}
pub fn encrypt(
&self,
key_context: &KeyContext,
data: impl AsRef<[u8]>,
data_context: &DataContext,
) -> Result<String> {
let tp = if key_context.is_periodic() {
let ts = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
key_context.get_time_period(ts)
} else {
None
};
let ikm = self.ikm_list.get_latest_ikm()?;
let key = derive_key(ikm, key_context, tp);
let gen_nonce_function = ikm.scheme.get_gen_nonce();
let nonce = gen_nonce_function()?;
let aad = Self::generate_aad(ikm.id, &nonce, key_context, data_context, tp);
let encryption_function = ikm.scheme.get_encryption();
let encrypted_data = encryption_function(&key, &nonce, data.as_ref(), &aad)?;
Ok(storage::encode_cipher(ikm.id, &encrypted_data, tp))
}
pub fn decrypt(
&self,
key_context: &KeyContext,
stored_data: &str,
data_context: &DataContext,
) -> Result<Vec<u8>> {
let (ikm_id, encrypted_data, tp) = storage::decode_cipher(stored_data)?;
let ikm = self.ikm_list.get_ikm_by_id(ikm_id)?;
let key = derive_key(ikm, key_context, tp);
let aad = Self::generate_aad(ikm.id, &encrypted_data.nonce, key_context, data_context, tp);
let decryption_function = ikm.scheme.get_decryption();
decryption_function(&key, &encrypted_data, &aad)
}
} }
#[cfg(test)] #[cfg(test)]
@ -120,19 +130,20 @@ mod tests {
#[test] #[test]
fn encrypt_decrypt_no_context() { fn encrypt_decrypt_no_context() {
let lst = get_ikm_lst();
let key_ctx = get_static_empty_key_ctx(); let key_ctx = get_static_empty_key_ctx();
let data_ctx = DataContext::from([]); let data_ctx = DataContext::from([]);
let cb = CipherBox::new(&lst);
// Encrypt // Encrypt
let lst = get_ikm_lst(); let res = cb.encrypt(&key_ctx, TEST_DATA, &data_ctx);
let res = encrypt(&lst, &key_ctx, TEST_DATA, &data_ctx);
assert!(res.is_ok(), "res: {res:?}"); assert!(res.is_ok(), "res: {res:?}");
let ciphertext = res.unwrap(); let ciphertext = res.unwrap();
assert!(ciphertext.starts_with("AQAAAA:")); assert!(ciphertext.starts_with("AQAAAA:"));
assert_eq!(ciphertext.len(), 98); assert_eq!(ciphertext.len(), 98);
// Decrypt // Decrypt
let res = decrypt(&lst, &key_ctx, &ciphertext, &data_ctx); let res = cb.decrypt(&key_ctx, &ciphertext, &data_ctx);
assert!(res.is_ok(), "res: {res:?}"); assert!(res.is_ok(), "res: {res:?}");
let plaintext = res.unwrap(); let plaintext = res.unwrap();
assert_eq!(plaintext, TEST_DATA); assert_eq!(plaintext, TEST_DATA);
@ -143,16 +154,17 @@ mod tests {
let lst = get_ikm_lst(); let lst = get_ikm_lst();
let key_ctx = get_static_key_ctx(); let key_ctx = get_static_key_ctx();
let data_ctx = DataContext::from(TEST_DATA_CTX); let data_ctx = DataContext::from(TEST_DATA_CTX);
let cb = CipherBox::new(&lst);
// Encrypt // Encrypt
let res = encrypt(&lst, &key_ctx, TEST_DATA, &data_ctx); let res = cb.encrypt(&key_ctx, TEST_DATA, &data_ctx);
assert!(res.is_ok(), "res: {res:?}"); assert!(res.is_ok(), "res: {res:?}");
let ciphertext = res.unwrap(); let ciphertext = res.unwrap();
assert!(ciphertext.starts_with("AQAAAA:")); assert!(ciphertext.starts_with("AQAAAA:"));
assert_eq!(ciphertext.len(), 98); assert_eq!(ciphertext.len(), 98);
// Decrypt // Decrypt
let res = decrypt(&lst, &key_ctx, &ciphertext, &data_ctx); let res = cb.decrypt(&key_ctx, &ciphertext, &data_ctx);
assert!(res.is_ok(), "res: {res:?}"); assert!(res.is_ok(), "res: {res:?}");
let plaintext = res.unwrap(); let plaintext = res.unwrap();
assert_eq!(plaintext, TEST_DATA); assert_eq!(plaintext, TEST_DATA);
@ -163,16 +175,17 @@ mod tests {
let lst = get_ikm_lst(); let lst = get_ikm_lst();
let key_ctx = KeyContext::from(TEST_KEY_CTX); let key_ctx = KeyContext::from(TEST_KEY_CTX);
let data_ctx = DataContext::from(TEST_DATA_CTX); let data_ctx = DataContext::from(TEST_DATA_CTX);
let cb = CipherBox::new(&lst);
// Encrypt // Encrypt
let res = encrypt(&lst, &key_ctx, TEST_DATA, &data_ctx); let res = cb.encrypt(&key_ctx, TEST_DATA, &data_ctx);
assert!(res.is_ok(), "res: {res:?}"); assert!(res.is_ok(), "res: {res:?}");
let ciphertext = res.unwrap(); let ciphertext = res.unwrap();
assert!(ciphertext.starts_with("AQAAAA:")); assert!(ciphertext.starts_with("AQAAAA:"));
assert_eq!(ciphertext.len(), 110); assert_eq!(ciphertext.len(), 110);
// Decrypt // Decrypt
let res = decrypt(&lst, &key_ctx, &ciphertext, &data_ctx); let res = cb.decrypt(&key_ctx, &ciphertext, &data_ctx);
assert!(res.is_ok(), "res: {res:?}"); assert!(res.is_ok(), "res: {res:?}");
let plaintext = res.unwrap(); let plaintext = res.unwrap();
assert_eq!(plaintext, TEST_DATA); assert_eq!(plaintext, TEST_DATA);
@ -193,14 +206,15 @@ mod tests {
let lst = get_ikm_lst(); let lst = get_ikm_lst();
let key_ctx = KeyContext::from(TEST_KEY_CTX); let key_ctx = KeyContext::from(TEST_KEY_CTX);
let data_ctx = DataContext::from(TEST_DATA_CTX); let data_ctx = DataContext::from(TEST_DATA_CTX);
let cb = CipherBox::new(&lst);
// Test if the reference ciphertext used for the tests is actually valid // Test if the reference ciphertext used for the tests is actually valid
let res = decrypt(&lst, &key_ctx, TEST_CIPHERTEXT, &data_ctx); let res = cb.decrypt(&key_ctx, TEST_CIPHERTEXT, &data_ctx);
assert!(res.is_ok(), "invalid reference ciphertext"); assert!(res.is_ok(), "invalid reference ciphertext");
// Test if altered versions of the reference ciphertext are refused // Test if altered versions of the reference ciphertext are refused
for (ciphertext, error_str) in tests { for (ciphertext, error_str) in tests {
let res = decrypt(&lst, &key_ctx, ciphertext, &data_ctx); let res = cb.decrypt(&key_ctx, ciphertext, &data_ctx);
assert!(res.is_err(), "failed error detection: {error_str}"); assert!(res.is_err(), "failed error detection: {error_str}");
} }
} }
@ -210,16 +224,17 @@ mod tests {
let lst = get_ikm_lst(); let lst = get_ikm_lst();
let key_ctx = KeyContext::from(TEST_KEY_CTX); let key_ctx = KeyContext::from(TEST_KEY_CTX);
let data_ctx = DataContext::from(TEST_DATA_CTX); let data_ctx = DataContext::from(TEST_DATA_CTX);
let cb = CipherBox::new(&lst);
let res = decrypt(&lst, &key_ctx, TEST_CIPHERTEXT, &data_ctx); let res = cb.decrypt(&key_ctx, TEST_CIPHERTEXT, &data_ctx);
assert!(res.is_ok(), "invalid reference ciphertext"); assert!(res.is_ok(), "invalid reference ciphertext");
let invalid_key_ctx = KeyContext::from(["invalid", "key", "context"]); let invalid_key_ctx = KeyContext::from(["invalid", "key", "context"]);
let res = decrypt(&lst, &invalid_key_ctx, TEST_CIPHERTEXT, &data_ctx); let res = cb.decrypt(&invalid_key_ctx, TEST_CIPHERTEXT, &data_ctx);
assert!(res.is_err(), "failed error detection: invalid key context"); assert!(res.is_err(), "failed error detection: invalid key context");
let invalid_data_ctx = DataContext::from(["invalid", "data", "context"]); let invalid_data_ctx = DataContext::from(["invalid", "data", "context"]);
let res = decrypt(&lst, &key_ctx, TEST_CIPHERTEXT, &invalid_data_ctx); let res = cb.decrypt(&key_ctx, TEST_CIPHERTEXT, &invalid_data_ctx);
assert!(res.is_err(), "failed error detection: invalid key context"); assert!(res.is_err(), "failed error detection: invalid key context");
} }
} }

View file

@ -1,7 +1,7 @@
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
mod canonicalization; mod canonicalization;
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
mod encryption; mod cipher_box;
#[cfg(any(feature = "encryption", feature = "ikm-management"))] #[cfg(any(feature = "encryption", feature = "ikm-management"))]
mod error; mod error;
#[cfg(any(feature = "encryption", feature = "ikm-management"))] #[cfg(any(feature = "encryption", feature = "ikm-management"))]
@ -14,7 +14,7 @@ mod scheme;
mod storage; mod storage;
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
pub use encryption::{decrypt, encrypt, DataContext}; pub use cipher_box::{CipherBox, DataContext};
#[cfg(any(feature = "encryption", feature = "ikm-management"))] #[cfg(any(feature = "encryption", feature = "ikm-management"))]
pub use error::Error; pub use error::Error;
#[cfg(any(feature = "encryption", feature = "ikm-management"))] #[cfg(any(feature = "encryption", feature = "ikm-management"))]

View file

@ -1,5 +1,5 @@
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
use crate::encryption::{DecryptionFunction, EncryptionFunction, GenNonceFunction}; use crate::cipher_box::{DecryptionFunction, EncryptionFunction, GenNonceFunction};
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
use crate::kdf::KdfFunction; use crate::kdf::KdfFunction;
use crate::Error; use crate::Error;

View file

@ -1,4 +1,4 @@
use crate::encryption::EncryptedData; use crate::cipher_box::EncryptedData;
use crate::error::Result; use crate::error::Result;
use chacha20poly1305::aead::{Aead, KeyInit, Payload}; use chacha20poly1305::aead::{Aead, KeyInit, Payload};
use chacha20poly1305::{Key, XChaCha20Poly1305, XNonce}; use chacha20poly1305::{Key, XChaCha20Poly1305, XNonce};

View file

@ -1,5 +1,5 @@
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
use crate::encryption::EncryptedData; use crate::cipher_box::EncryptedData;
use crate::error::{Error, Result}; use crate::error::{Error, Result};
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
use crate::ikm::IkmId; use crate::ikm::IkmId;