Add the IKM id and the nonce to the AAD
This commit is contained in:
parent
bf98245b04
commit
bc3cfe71dc
4 changed files with 66 additions and 24 deletions
|
@ -4,8 +4,8 @@ const CANONICALIZATION_BUFFER_SIZE: usize = 1024;
|
||||||
const CANONICALIZATION_SEPARATOR: &str = ":";
|
const CANONICALIZATION_SEPARATOR: &str = ":";
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn join_canonicalized_str(s1: &str, s2: &str) -> String {
|
pub(crate) fn join_canonicalized_str(elems: &[String]) -> String {
|
||||||
format!("{s1}{CANONICALIZATION_SEPARATOR}{s2}")
|
elems.join(CANONICALIZATION_SEPARATOR)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn canonicalize(context: &[impl AsRef<[u8]>]) -> String {
|
pub(crate) fn canonicalize(context: &[impl AsRef<[u8]>]) -> String {
|
||||||
|
@ -51,13 +51,31 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_join_canonicalized_empty() {
|
fn test_join_canonicalized_empty() {
|
||||||
assert_eq!(join_canonicalized_str("", ""), ":");
|
assert_eq!(join_canonicalized_str(&[]), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_join_canonicalized_one() {
|
||||||
|
assert_eq!(
|
||||||
|
join_canonicalized_str(&["QWO7RGDt".to_string()]),
|
||||||
|
"QWO7RGDt"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_join_canonicalized_one_empty() {
|
||||||
|
assert_eq!(join_canonicalized_str(&[String::new()]), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_join_canonicalized_empty_str() {
|
||||||
|
assert_eq!(join_canonicalized_str(&[String::new(), String::new()]), ":");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_join_canonicalized_with_data() {
|
fn test_join_canonicalized_with_data() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
join_canonicalized_str("QWO7RGDt:f-JmDPvU", "_Sfx61Fp"),
|
join_canonicalized_str(&["QWO7RGDt:f-JmDPvU".into(), "_Sfx61Fp".into()]),
|
||||||
"QWO7RGDt:f-JmDPvU:_Sfx61Fp"
|
"QWO7RGDt:f-JmDPvU:_Sfx61Fp"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use crate::canonicalization::{canonicalize, join_canonicalized_str};
|
use crate::canonicalization::{canonicalize, join_canonicalized_str};
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::kdf::{derive_key, KeyContext};
|
use crate::kdf::{derive_key, KeyContext};
|
||||||
use crate::{storage, InputKeyMaterialList};
|
use crate::{storage, IkmId, InputKeyMaterialList};
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
pub(crate) type DecryptionFunction = dyn Fn(&[u8], &EncryptedData, &str) -> Result<Vec<u8>>;
|
pub(crate) type DecryptionFunction = dyn Fn(&[u8], &EncryptedData, &str) -> Result<Vec<u8>>;
|
||||||
pub(crate) type EncryptionFunction = dyn Fn(&[u8], &[u8], &str) -> Result<EncryptedData>;
|
pub(crate) type EncryptionFunction = dyn Fn(&[u8], &[u8], &[u8], &str) -> Result<EncryptedData>;
|
||||||
|
pub(crate) type GenNonceFunction = dyn Fn() -> Result<Vec<u8>>;
|
||||||
|
|
||||||
pub struct DataContext {
|
pub struct DataContext {
|
||||||
ctx: Vec<String>,
|
ctx: Vec<String>,
|
||||||
|
@ -33,14 +34,23 @@ pub(crate) struct EncryptedData {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn generate_aad(
|
fn generate_aad(
|
||||||
|
ikm_id: IkmId,
|
||||||
|
nonce: &[u8],
|
||||||
key_context: &KeyContext,
|
key_context: &KeyContext,
|
||||||
data_context: &DataContext,
|
data_context: &DataContext,
|
||||||
time_period: Option<u64>,
|
time_period: Option<u64>,
|
||||||
) -> String {
|
) -> 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 elems = key_context.get_ctx_elems(time_period);
|
||||||
let key_context_canon = canonicalize(&elems);
|
let key_context_canon = canonicalize(&elems);
|
||||||
let data_context_canon = canonicalize(data_context.get_ctx_elems());
|
let data_context_canon = canonicalize(data_context.get_ctx_elems());
|
||||||
join_canonicalized_str(&key_context_canon, &data_context_canon)
|
join_canonicalized_str(&[
|
||||||
|
ikm_id_canon,
|
||||||
|
nonce_canon,
|
||||||
|
key_context_canon,
|
||||||
|
data_context_canon,
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encrypt(
|
pub fn encrypt(
|
||||||
|
@ -57,9 +67,11 @@ pub fn encrypt(
|
||||||
};
|
};
|
||||||
let ikm = ikml.get_latest_ikm()?;
|
let ikm = ikml.get_latest_ikm()?;
|
||||||
let key = derive_key(ikm, key_context, tp);
|
let key = derive_key(ikm, key_context, tp);
|
||||||
let aad = generate_aad(key_context, data_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 encryption_function = ikm.scheme.get_encryption();
|
||||||
let encrypted_data = encryption_function(&key, data.as_ref(), &aad)?;
|
let encrypted_data = encryption_function(&key, &nonce, data.as_ref(), &aad)?;
|
||||||
Ok(storage::encode_cipher(ikm.id, &encrypted_data, tp))
|
Ok(storage::encode_cipher(ikm.id, &encrypted_data, tp))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +84,7 @@ pub fn decrypt(
|
||||||
let (ikm_id, encrypted_data, tp) = storage::decode_cipher(stored_data)?;
|
let (ikm_id, encrypted_data, tp) = storage::decode_cipher(stored_data)?;
|
||||||
let ikm = ikml.get_ikm_by_id(ikm_id)?;
|
let ikm = ikml.get_ikm_by_id(ikm_id)?;
|
||||||
let key = derive_key(ikm, key_context, tp);
|
let key = derive_key(ikm, key_context, tp);
|
||||||
let aad = generate_aad(key_context, data_context, tp);
|
let aad = generate_aad(ikm.id, &encrypted_data.nonce, key_context, data_context, tp);
|
||||||
let decryption_function = ikm.scheme.get_decryption();
|
let decryption_function = ikm.scheme.get_decryption();
|
||||||
decryption_function(&key, &encrypted_data, &aad)
|
decryption_function(&key, &encrypted_data, &aad)
|
||||||
}
|
}
|
||||||
|
@ -82,7 +94,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{DataContext, KeyContext};
|
use crate::{DataContext, KeyContext};
|
||||||
|
|
||||||
const TEST_CIPHERTEXT: &str = "AQAAAA:elFanOvp5DNewgq75T5U6wLYNn8zzo1n:9izU-8cw4oSIU4lqcYrfEBzOXluS7lVcUbF_KnEg0HFp2srx6xq3Bir91A:NgAAAAAAAAA";
|
const TEST_CIPHERTEXT: &str = "AQAAAA:W-nzcGkPU6eWj_JjjqLpQk6WSe_CIUPF:we_HR8yD3XnQ9aaJlZFvqPitnDlQHexw4QPaYaOTzpHSWNW86QQrLRRZOg:NgAAAAAAAAA";
|
||||||
const TEST_DATA: &[u8] = b"Lorem ipsum dolor sit amet.";
|
const TEST_DATA: &[u8] = b"Lorem ipsum dolor sit amet.";
|
||||||
const TEST_KEY_CTX: [&str; 3] = ["db_name", "table_name", "column_name"];
|
const TEST_KEY_CTX: [&str; 3] = ["db_name", "table_name", "column_name"];
|
||||||
const TEST_DATA_CTX: [&str; 1] = ["018db876-3d9d-79af-9460-55d17da991d8"];
|
const TEST_DATA_CTX: [&str; 1] = ["018db876-3d9d-79af-9460-55d17da991d8"];
|
||||||
|
@ -170,12 +182,12 @@ mod tests {
|
||||||
fn decrypt_invalid_ciphertext() {
|
fn decrypt_invalid_ciphertext() {
|
||||||
let tests = &[
|
let tests = &[
|
||||||
("", "empty data"),
|
("", "empty data"),
|
||||||
("AQAATA:elFanOvp5DNewgq75T5U6wLYNn8zzo1n:9izU-8cw4oSIU4lqcYrfEBzOXluS7lVcUbF_KnEg0HFp2srx6xq3Bir91A:NgAAAAAAAAA", "unknown ikm id"),
|
("AQAATA:W-nzcGkPU6eWj_JjjqLpQk6WSe_CIUPF:we_HR8yD3XnQ9aaJlZFvqPitnDlQHexw4QPaYaOTzpHSWNW86QQrLRRZOg:NgAAAAAAAAA", "unknown ikm id"),
|
||||||
("AQAAAA:MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0:9izU-8cw4oSIU4lqcYrfEBzOXluS7lVcUbF_KnEg0HFp2srx6xq3Bir91A:NgAAAAAAAAA", "invalid nonce"),
|
("AQAAAA:MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0:we_HR8yD3XnQ9aaJlZFvqPitnDlQHexw4QPaYaOTzpHSWNW86QQrLRRZOg:NgAAAAAAAAA", "invalid nonce"),
|
||||||
("AQAAAA:elFanOvp5DNewgq75T5U6wLYNn8zzo1n:8izU-8cw4oSIU4lqcYrfEBzOXluS7lVcUbF_KnEg0HFp2srx6xq3Bir91A:NgAAAAAAAAA", "invalid ciphertext"),
|
("AQAAAA:W-nzcGkPU6eWj_JjjqLpQk6WSe_CIUPF:8e_HR8yD3XnQ9aaJlZFvqPitnDlQHexw4QPaYaOTzpHSWNW86QQrLRRZOg:NgAAAAAAAAA", "invalid ciphertext"),
|
||||||
("AQAAAA:elFanOvp5DNewgq75T5U6wLYNn8zzo1n:9izU-8cw4oSIU4lqcYrfEBzOXluS7lVcUbF_KnEg0HFp2srx6xq3Bir91A:NaAAAAAAAAA", "invalid time period"),
|
("AQAAAA:W-nzcGkPU6eWj_JjjqLpQk6WSe_CIUPF:we_HR8yD3XnQ9aaJlZFvqPitnDlQHexw4QPaYaOTzpHSWNW86QQrLRRZOg:NaAAAAAAAAA", "invalid time period"),
|
||||||
("AQAAAA:elFanOvp5DNewgq75T5U6wLYNn8zzo1n:9izU-8cw4oSIU4lqcYrfEBzOXluS7lVcUbF_KnEg0HFp2srx6xq3Bir91A:", "empty time period"),
|
("AQAAAA:W-nzcGkPU6eWj_JjjqLpQk6WSe_CIUPF:we_HR8yD3XnQ9aaJlZFvqPitnDlQHexw4QPaYaOTzpHSWNW86QQrLRRZOg:", "empty time period"),
|
||||||
("AQAAAA:elFanOvp5DNewgq75T5U6wLYNn8zzo1n:9izU-8cw4oSIU4lqcYrfEBzOXluS7lVcUbF_KnEg0HFp2srx6xq3Bir91A", "missing time period"),
|
("AQAAAA:W-nzcGkPU6eWj_JjjqLpQk6WSe_CIUPF:we_HR8yD3XnQ9aaJlZFvqPitnDlQHexw4QPaYaOTzpHSWNW86QQrLRRZOg", "missing time period"),
|
||||||
];
|
];
|
||||||
|
|
||||||
let lst = get_ikm_lst();
|
let lst = get_ikm_lst();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
use crate::encryption::{DecryptionFunction, EncryptionFunction};
|
use crate::encryption::{DecryptionFunction, EncryptionFunction, GenNonceFunction};
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
use crate::kdf::KdfFunction;
|
use crate::kdf::KdfFunction;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
@ -45,6 +45,14 @@ impl Scheme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_gen_nonce(&self) -> Box<GenNonceFunction> {
|
||||||
|
match self {
|
||||||
|
Scheme::XChaCha20Poly1305WithBlake3 => {
|
||||||
|
Box::new(xchacha20poly1305::xchacha20poly1305_gen_nonce)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<SchemeSerializeType> for Scheme {
|
impl TryFrom<SchemeSerializeType> for Scheme {
|
||||||
|
|
|
@ -3,18 +3,22 @@ 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};
|
||||||
|
|
||||||
|
pub(crate) fn xchacha20poly1305_gen_nonce() -> Result<Vec<u8>> {
|
||||||
|
// X-variant: the nonce's size is 192 bits (24 bytes)
|
||||||
|
let mut nonce: [u8; 24] = [0; 24];
|
||||||
|
getrandom::getrandom(&mut nonce)?;
|
||||||
|
Ok(nonce.to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn xchacha20poly1305_encrypt(
|
pub(crate) fn xchacha20poly1305_encrypt(
|
||||||
key: &[u8],
|
key: &[u8],
|
||||||
|
nonce: &[u8],
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
aad: &str,
|
aad: &str,
|
||||||
) -> Result<EncryptedData> {
|
) -> Result<EncryptedData> {
|
||||||
// Adapt the key
|
// Adapt the key and nonce
|
||||||
let key = Key::from_slice(key);
|
let key = Key::from_slice(key);
|
||||||
|
let nonce = XNonce::from_slice(nonce);
|
||||||
// Generate a nonce
|
|
||||||
let mut nonce: [u8; 24] = [0; 24];
|
|
||||||
getrandom::getrandom(&mut nonce)?;
|
|
||||||
let nonce = XNonce::from_slice(&nonce);
|
|
||||||
|
|
||||||
// Prepare the payload
|
// Prepare the payload
|
||||||
let payload = Payload {
|
let payload = Payload {
|
||||||
|
|
Loading…
Reference in a new issue