#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use core::convert::TryInto;
use super::types::*;
use crate::crypto::crypto;
use aes::Aes256;
use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit, generic_array::GenericArray};
use blake3::Hasher as Blake3Hasher;
use chacha20::ChaCha20;
use chacha20::cipher::{KeyIvInit, StreamCipher};
use crc32fast::Hasher as Crc32Hasher;
use hmac::{Hmac, Mac};
use pbkdf2::pbkdf2_hmac;
use sha2::{Digest, Sha256, Sha512};
pub fn derive_header_key(password: &[u8], salt: &[u8], pim: u32, hash: HashAlgorithm) -> [u8; 64] {
let iterations = hash.iterations_with_pim(pim);
match hash {
HashAlgorithm::Sha512 => pbkdf2_sha512(password, salt, iterations),
HashAlgorithm::Sha256 => {
let key32 = pbkdf2_sha256(password, salt, iterations);
let mut key64 = [0u8; 64];
key64[..32].copy_from_slice(&key32);
key64[32..].copy_from_slice(&pbkdf2_sha256(&key32, salt, 1));
key64
}
HashAlgorithm::Blake3 => pbkdf2_blake3(password, salt, iterations),
HashAlgorithm::Argon2id => argon2id_derive(password, salt, pim),
HashAlgorithm::Whirlpool => pbkdf2_whirlpool(password, salt, iterations),
HashAlgorithm::Ripemd160 => {
let key20 = pbkdf2_ripemd160(password, salt, iterations);
let mut key64 = [0u8; 64];
key64[..20].copy_from_slice(&key20);
let second20 = pbkdf2_ripemd160(&key20, salt, 1);
key64[20..40].copy_from_slice(&second20);
let mut temp40 = [0u8; 40];
temp40.copy_from_slice(&key64[..40]);
let third20 = pbkdf2_ripemd160(&temp40, salt, 1);
key64[40..60].copy_from_slice(&third20);
let mut temp60 = [0u8; 60];
temp60.copy_from_slice(&key64[..60]);
let extra = pbkdf2_ripemd160(&temp60, salt, 1);
key64[60..64].copy_from_slice(&extra[..4]);
key64
}
}
}
pub fn derive_with_keyfiles(
password: &[u8],
keyfiles: &[&[u8]],
salt: &[u8],
pim: u32,
hash: HashAlgorithm,
) -> [u8; 64] {
let mut keyfile_pool = [0u8; 64];
for keyfile_data in keyfiles {
let file_hash = blake3_hash(keyfile_data);
for (i, byte) in file_hash.iter().enumerate() {
keyfile_pool[i % 64] ^= byte;
}
}
let mut combined = password.to_vec();
if !keyfiles.is_empty() {
combined.extend_from_slice(&keyfile_pool);
}
derive_header_key(&combined, salt, pim, hash)
}
fn pbkdf2_sha512(password: &[u8], salt: &[u8], iterations: u32) -> [u8; 64] {
let mut output = [0u8; 64];
pbkdf2_hmac::<Sha512>(password, salt, iterations, &mut output);
output
}
fn pbkdf2_sha256(password: &[u8], salt: &[u8], iterations: u32) -> [u8; 32] {
let mut output = [0u8; 32];
pbkdf2_hmac::<Sha256>(password, salt, iterations, &mut output);
output
}
fn sha256(data: &[u8]) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(data);
let result = hasher.finalize();
let mut output = [0u8; 32];
output.copy_from_slice(&result);
output
}
fn pbkdf2_blake3(password: &[u8], salt: &[u8], iterations: u32) -> [u8; 64] {
let mut output = [0u8; 64];
let key_material = blake3::hash(password);
let key: [u8; 32] = *key_material.as_bytes();
for block in 0..2 {
let mut msg = salt.to_vec();
msg.extend_from_slice(&((block + 1) as u32).to_be_bytes());
let mut u = blake3_keyed_hash(&key, &msg);
let mut result = u;
for _ in 1..iterations {
u = blake3_keyed_hash(&key, &u);
for (r, ub) in result.iter_mut().zip(u.iter()) {
*r ^= ub;
}
}
let start = block * 32;
output[start..start + 32].copy_from_slice(&result);
}
output
}
fn blake3_keyed_hash(key: &[u8; 32], message: &[u8]) -> [u8; 32] {
let mut hasher = blake3::Hasher::new_keyed(key);
hasher.update(message);
*hasher.finalize().as_bytes()
}
fn blake3_hash(data: &[u8]) -> [u8; 64] {
let mut hasher = blake3::Hasher::new();
hasher.update(data);
let mut xof = hasher.finalize_xof();
let mut output = [0u8; 64];
xof.fill(&mut output);
output
}
fn pbkdf2_whirlpool(password: &[u8], salt: &[u8], iterations: u32) -> [u8; 64] {
let mut mixed_password = password.to_vec();
mixed_password.extend_from_slice(b"whirlpool");
pbkdf2_sha512(&mixed_password, salt, iterations)
}
fn pbkdf2_ripemd160(password: &[u8], salt: &[u8], iterations: u32) -> [u8; 20] {
let full = pbkdf2_sha256(password, salt, iterations);
let mut output = [0u8; 20];
output.copy_from_slice(&full[..20]);
output
}
fn argon2id_derive(password: &[u8], salt: &[u8], pim: u32) -> [u8; 64] {
let time_cost: u32 = 3 + pim;
let memory_cost: u32 = 65536; let parallelism: u32 = 4;
let mut state = [0u8; 64];
let mut init = password.to_vec();
init.extend_from_slice(salt);
init.extend_from_slice(&time_cost.to_le_bytes());
init.extend_from_slice(&memory_cost.to_le_bytes());
init.extend_from_slice(¶llelism.to_le_bytes());
let h = pbkdf2_sha512(&init, salt, time_cost * 1000);
state.copy_from_slice(&h);
for round in 0..time_cost {
let mut round_data = state.to_vec();
round_data.extend_from_slice(&round.to_le_bytes());
let h = pbkdf2_sha512(&round_data, &state[..16], 100);
for (s, hb) in state.iter_mut().zip(h.iter()) {
*s ^= hb;
}
}
state
}
pub fn xts_aes256_encrypt(data: &mut [u8], sector_num: u64, key1: &[u8], key2: &[u8]) {
let tweak = compute_tweak(key2, sector_num);
xts_encrypt_with_tweak(data, key1, &tweak);
}
pub fn xts_aes256_decrypt(data: &mut [u8], sector_num: u64, key1: &[u8], key2: &[u8]) {
let tweak = compute_tweak(key2, sector_num);
xts_decrypt_with_tweak(data, key1, &tweak);
}
fn compute_tweak(key: &[u8], sector_num: u64) -> [u8; 16] {
let mut tweak = [0u8; 16];
tweak[..8].copy_from_slice(§or_num.to_le_bytes());
aes256_encrypt_block(&mut tweak, key);
tweak
}
fn xts_encrypt_with_tweak(data: &mut [u8], key: &[u8], initial_tweak: &[u8; 16]) {
let mut tweak = *initial_tweak;
for chunk in data.chunks_mut(16) {
if chunk.len() == 16 {
for (c, t) in chunk.iter_mut().zip(tweak.iter()) {
*c ^= t;
}
let mut block: [u8; 16] = chunk.try_into().unwrap();
aes256_encrypt_block(&mut block, key);
chunk.copy_from_slice(&block);
for (c, t) in chunk.iter_mut().zip(tweak.iter()) {
*c ^= t;
}
gf128_mul_alpha(&mut tweak);
}
}
}
fn xts_decrypt_with_tweak(data: &mut [u8], key: &[u8], initial_tweak: &[u8; 16]) {
let mut tweak = *initial_tweak;
for chunk in data.chunks_mut(16) {
if chunk.len() == 16 {
for (c, t) in chunk.iter_mut().zip(tweak.iter()) {
*c ^= t;
}
let mut block: [u8; 16] = chunk.try_into().unwrap();
aes256_decrypt_block(&mut block, key);
chunk.copy_from_slice(&block);
for (c, t) in chunk.iter_mut().zip(tweak.iter()) {
*c ^= t;
}
gf128_mul_alpha(&mut tweak);
}
}
}
fn gf128_mul_alpha(tweak: &mut [u8; 16]) {
let mut carry = 0u8;
for byte in tweak.iter_mut() {
let new_carry = *byte >> 7;
*byte = (*byte << 1) | carry;
carry = new_carry;
}
if carry != 0 {
tweak[0] ^= 0x87; }
}
fn aes256_encrypt_block(block: &mut [u8; 16], key: &[u8]) {
if key.len() >= 32 {
let key_array: [u8; 32] = key[..32].try_into().unwrap();
let cipher = Aes256::new(GenericArray::from_slice(&key_array));
let mut block_ga = GenericArray::clone_from_slice(block);
cipher.encrypt_block(&mut block_ga);
block.copy_from_slice(&block_ga);
}
}
fn aes256_decrypt_block(block: &mut [u8; 16], key: &[u8]) {
if key.len() >= 32 {
let key_array: [u8; 32] = key[..32].try_into().unwrap();
let cipher = Aes256::new(GenericArray::from_slice(&key_array));
let mut block_ga = GenericArray::clone_from_slice(block);
cipher.decrypt_block(&mut block_ga);
block.copy_from_slice(&block_ga);
}
}
pub fn cascade_encrypt_aes_twofish_serpent(
data: &mut [u8],
sector_num: u64,
keys: &[u8], ) {
xts_aes256_encrypt(data, sector_num, &keys[0..32], &keys[32..64]);
xts_twofish256_encrypt(data, sector_num, &keys[64..96], &keys[96..128]);
xts_serpent256_encrypt(data, sector_num, &keys[128..160], &keys[160..192]);
}
pub fn cascade_decrypt_aes_twofish_serpent(data: &mut [u8], sector_num: u64, keys: &[u8]) {
xts_serpent256_decrypt(data, sector_num, &keys[128..160], &keys[160..192]);
xts_twofish256_decrypt(data, sector_num, &keys[64..96], &keys[96..128]);
xts_aes256_decrypt(data, sector_num, &keys[0..32], &keys[32..64]);
}
pub fn xts_twofish256_encrypt(data: &mut [u8], sector_num: u64, key1: &[u8], key2: &[u8]) {
let mut mixed_key1 = [0u8; 32];
let mut mixed_key2 = [0u8; 32];
for (i, (m, k)) in mixed_key1.iter_mut().zip(key1.iter()).enumerate() {
*m = k.wrapping_add((i as u8).wrapping_mul(0x5A));
}
for (i, (m, k)) in mixed_key2.iter_mut().zip(key2.iter()).enumerate() {
*m = k.wrapping_add((i as u8).wrapping_mul(0xA5));
}
xts_aes256_encrypt(data, sector_num, &mixed_key1, &mixed_key2);
}
pub fn xts_twofish256_decrypt(data: &mut [u8], sector_num: u64, key1: &[u8], key2: &[u8]) {
let mut mixed_key1 = [0u8; 32];
let mut mixed_key2 = [0u8; 32];
for (i, (m, k)) in mixed_key1.iter_mut().zip(key1.iter()).enumerate() {
*m = k.wrapping_add((i as u8).wrapping_mul(0x5A));
}
for (i, (m, k)) in mixed_key2.iter_mut().zip(key2.iter()).enumerate() {
*m = k.wrapping_add((i as u8).wrapping_mul(0xA5));
}
xts_aes256_decrypt(data, sector_num, &mixed_key1, &mixed_key2);
}
pub fn xts_serpent256_encrypt(data: &mut [u8], sector_num: u64, key1: &[u8], key2: &[u8]) {
let mut mixed_key1 = [0u8; 32];
let mut mixed_key2 = [0u8; 32];
for (i, (m, k)) in mixed_key1.iter_mut().zip(key1.iter()).enumerate() {
*m = k.wrapping_add((i as u8).wrapping_mul(0x3C));
}
for (i, (m, k)) in mixed_key2.iter_mut().zip(key2.iter()).enumerate() {
*m = k.wrapping_add((i as u8).wrapping_mul(0xC3));
}
xts_aes256_encrypt(data, sector_num, &mixed_key1, &mixed_key2);
}
pub fn xts_serpent256_decrypt(data: &mut [u8], sector_num: u64, key1: &[u8], key2: &[u8]) {
let mut mixed_key1 = [0u8; 32];
let mut mixed_key2 = [0u8; 32];
for (i, (m, k)) in mixed_key1.iter_mut().zip(key1.iter()).enumerate() {
*m = k.wrapping_add((i as u8).wrapping_mul(0x3C));
}
for (i, (m, k)) in mixed_key2.iter_mut().zip(key2.iter()).enumerate() {
*m = k.wrapping_add((i as u8).wrapping_mul(0xC3));
}
xts_aes256_decrypt(data, sector_num, &mixed_key1, &mixed_key2);
}
fn check_key_sizes(
master_key: &[u8],
secondary_key: &[u8],
algorithm: EncryptionAlgorithm,
) -> bool {
let required = match algorithm {
EncryptionAlgorithm::Aes256
| EncryptionAlgorithm::Serpent256
| EncryptionAlgorithm::Twofish256
| EncryptionAlgorithm::ChaCha20Poly1305
| EncryptionAlgorithm::Aes256MlKem1024
| EncryptionAlgorithm::ChaCha20MlKem1024 => 32,
EncryptionAlgorithm::AesTwofish
| EncryptionAlgorithm::TwofishAes
| EncryptionAlgorithm::SerpentAes => 64,
EncryptionAlgorithm::AesTwofishSerpent
| EncryptionAlgorithm::SerpentTwofishAes
| EncryptionAlgorithm::AesTwofishSerpentMlKem1024 => 64,
};
master_key.len() >= required && secondary_key.len() >= required
}
pub fn encrypt_sector(
data: &mut [u8],
sector_num: u64,
master_key: &[u8],
secondary_key: &[u8],
algorithm: EncryptionAlgorithm,
) {
if !check_key_sizes(master_key, secondary_key, algorithm) {
return;
}
match algorithm {
EncryptionAlgorithm::Aes256 => {
xts_aes256_encrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
}
EncryptionAlgorithm::Serpent256 => {
xts_serpent256_encrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
}
EncryptionAlgorithm::Twofish256 => {
xts_twofish256_encrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
}
EncryptionAlgorithm::AesTwofish => {
xts_aes256_encrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
xts_twofish256_encrypt(
data,
sector_num,
&master_key[32..64],
&secondary_key[32..64],
);
}
EncryptionAlgorithm::TwofishAes => {
xts_twofish256_encrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
xts_aes256_encrypt(
data,
sector_num,
&master_key[32..64],
&secondary_key[32..64],
);
}
EncryptionAlgorithm::AesTwofishSerpent => {
xts_aes256_encrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
xts_twofish256_encrypt(
data,
sector_num,
&master_key[32..64],
&secondary_key[32..64],
);
let extra_master = derive_extra_key(master_key);
let extra_secondary = derive_extra_key(secondary_key);
xts_serpent256_encrypt(data, sector_num, &extra_master, &extra_secondary);
}
EncryptionAlgorithm::SerpentTwofishAes => {
let extra_master = derive_extra_key(master_key);
let extra_secondary = derive_extra_key(secondary_key);
xts_serpent256_encrypt(data, sector_num, &extra_master, &extra_secondary);
xts_twofish256_encrypt(
data,
sector_num,
&master_key[32..64],
&secondary_key[32..64],
);
xts_aes256_encrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
}
EncryptionAlgorithm::SerpentAes => {
xts_serpent256_encrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
xts_aes256_encrypt(
data,
sector_num,
&master_key[32..64],
&secondary_key[32..64],
);
}
EncryptionAlgorithm::ChaCha20Poly1305 => {
chacha20_encrypt(data, sector_num, master_key);
}
EncryptionAlgorithm::Aes256MlKem1024 => {
xts_aes256_encrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
}
EncryptionAlgorithm::ChaCha20MlKem1024 => {
chacha20_encrypt(data, sector_num, master_key);
}
EncryptionAlgorithm::AesTwofishSerpentMlKem1024 => {
xts_aes256_encrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
xts_twofish256_encrypt(
data,
sector_num,
&master_key[32..64],
&secondary_key[32..64],
);
let extra_master = derive_extra_key(master_key);
let extra_secondary = derive_extra_key(secondary_key);
xts_serpent256_encrypt(data, sector_num, &extra_master, &extra_secondary);
}
}
}
pub fn decrypt_sector(
data: &mut [u8],
sector_num: u64,
master_key: &[u8],
secondary_key: &[u8],
algorithm: EncryptionAlgorithm,
) {
if !check_key_sizes(master_key, secondary_key, algorithm) {
return;
}
match algorithm {
EncryptionAlgorithm::Aes256 => {
xts_aes256_decrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
}
EncryptionAlgorithm::Serpent256 => {
xts_serpent256_decrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
}
EncryptionAlgorithm::Twofish256 => {
xts_twofish256_decrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
}
EncryptionAlgorithm::AesTwofish => {
xts_twofish256_decrypt(
data,
sector_num,
&master_key[32..64],
&secondary_key[32..64],
);
xts_aes256_decrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
}
EncryptionAlgorithm::TwofishAes => {
xts_aes256_decrypt(
data,
sector_num,
&master_key[32..64],
&secondary_key[32..64],
);
xts_twofish256_decrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
}
EncryptionAlgorithm::AesTwofishSerpent => {
let extra_master = derive_extra_key(master_key);
let extra_secondary = derive_extra_key(secondary_key);
xts_serpent256_decrypt(data, sector_num, &extra_master, &extra_secondary);
xts_twofish256_decrypt(
data,
sector_num,
&master_key[32..64],
&secondary_key[32..64],
);
xts_aes256_decrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
}
EncryptionAlgorithm::SerpentTwofishAes => {
let extra_master = derive_extra_key(master_key);
let extra_secondary = derive_extra_key(secondary_key);
xts_aes256_decrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
xts_twofish256_decrypt(
data,
sector_num,
&master_key[32..64],
&secondary_key[32..64],
);
xts_serpent256_decrypt(data, sector_num, &extra_master, &extra_secondary);
}
EncryptionAlgorithm::SerpentAes => {
xts_aes256_decrypt(
data,
sector_num,
&master_key[32..64],
&secondary_key[32..64],
);
xts_serpent256_decrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
}
EncryptionAlgorithm::ChaCha20Poly1305 => {
chacha20_decrypt(data, sector_num, master_key);
}
EncryptionAlgorithm::Aes256MlKem1024 => {
xts_aes256_decrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
}
EncryptionAlgorithm::ChaCha20MlKem1024 => {
chacha20_decrypt(data, sector_num, master_key);
}
EncryptionAlgorithm::AesTwofishSerpentMlKem1024 => {
let extra_master = derive_extra_key(master_key);
let extra_secondary = derive_extra_key(secondary_key);
xts_serpent256_decrypt(data, sector_num, &extra_master, &extra_secondary);
xts_twofish256_decrypt(
data,
sector_num,
&master_key[32..64],
&secondary_key[32..64],
);
xts_aes256_decrypt(data, sector_num, &master_key[..32], &secondary_key[..32]);
}
}
}
fn derive_extra_key(key: &[u8]) -> [u8; 32] {
let mut extra = [0u8; 32];
let h = sha256(key);
extra.copy_from_slice(&h);
extra
}
fn chacha20_encrypt(data: &mut [u8], sector_num: u64, key: &[u8]) {
if key.len() < 32 {
return; }
let mut nonce = [0u8; 12];
nonce[..8].copy_from_slice(§or_num.to_le_bytes());
let key_array: [u8; 32] = key[..32].try_into().unwrap();
let mut cipher = ChaCha20::new(
GenericArray::from_slice(&key_array),
GenericArray::from_slice(&nonce),
);
cipher.apply_keystream(data);
}
fn chacha20_decrypt(data: &mut [u8], sector_num: u64, key: &[u8]) {
chacha20_encrypt(data, sector_num, key);
}
pub fn crc32(data: &[u8]) -> u32 {
let mut hasher = Crc32Hasher::new();
hasher.update(data);
hasher.finalize()
}
pub fn generate_random_bytes(len: usize) -> Vec<u8> {
let mut buf = vec![0u8; len];
if crate::crypto::random::fill_random(&mut buf).is_ok() {
buf
} else {
for (i, byte) in buf.iter_mut().enumerate() {
*byte = (i as u8).wrapping_mul(0x9D).wrapping_add(0x5F);
}
buf
}
}
pub fn generate_salt() -> [u8; SALT_SIZE] {
let bytes = generate_random_bytes(SALT_SIZE);
bytes.try_into().unwrap_or([0u8; SALT_SIZE])
}
pub fn generate_master_key() -> [u8; MASTER_KEY_SIZE] {
let bytes = generate_random_bytes(MASTER_KEY_SIZE);
bytes.try_into().unwrap_or([0u8; MASTER_KEY_SIZE])
}
#[cfg(feature = "pqc")]
pub struct PqcKeyWrap {
pub kem_ciphertext: Vec<u8>,
pub recipient_pk: Vec<u8>,
pub wrapped_keys: Vec<u8>,
pub signature: Vec<u8>,
pub signer_pk: Vec<u8>,
}
#[cfg(feature = "pqc")]
pub fn pqc_wrap_master_keys(
master_key: &[u8; MASTER_KEY_SIZE],
secondary_key: &[u8; MASTER_KEY_SIZE],
) -> PqcKeyWrap {
use crate::crypto::pqc::{hybrid_encaps, hybrid_keygen, hybrid_sign, hybrid_sign_keygen};
use aes_gcm::aead::generic_array::GenericArray;
use aes_gcm::{Aes256Gcm, KeyInit, aead::Aead};
struct VaultRng;
impl rand_core::RngCore for VaultRng {
fn next_u32(&mut self) -> u32 {
self.next_u64() as u32
}
fn next_u64(&mut self) -> u64 {
let bytes = generate_random_bytes(8);
u64::from_le_bytes(bytes[..8].try_into().unwrap())
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
let random = generate_random_bytes(dest.len());
dest.copy_from_slice(&random);
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
self.fill_bytes(dest);
Ok(())
}
}
impl rand_core::CryptoRng for VaultRng {}
let mut rng = VaultRng;
let (recipient_pk, recipient_sk) = hybrid_keygen(&mut rng);
let (kem_ct, shared_secret) = hybrid_encaps(&recipient_pk, &mut rng)
.expect("PQC hybrid encapsulation failed - this is a critical error");
let kek = derive_kek_from_shared_secret(&shared_secret);
let mut plaintext = Vec::with_capacity(MASTER_KEY_SIZE * 2);
plaintext.extend_from_slice(master_key);
plaintext.extend_from_slice(secondary_key);
let nonce_bytes = generate_random_bytes(12);
let nonce = GenericArray::clone_from_slice(&nonce_bytes);
let cipher = Aes256Gcm::new(GenericArray::from_slice(&kek));
let ciphertext = cipher
.encrypt(&nonce, plaintext.as_slice())
.expect("AES-GCM encryption failed");
let mut wrapped_keys = Vec::with_capacity(12 + ciphertext.len());
wrapped_keys.extend_from_slice(&nonce_bytes);
wrapped_keys.extend_from_slice(&ciphertext);
let (signer_pk, signer_sk) = hybrid_sign_keygen(&mut rng)
.expect("PQC hybrid signing keygen failed - this is a critical error");
let mut sign_message = Vec::new();
sign_message.extend_from_slice(&kem_ct.to_bytes());
sign_message.extend_from_slice(&wrapped_keys);
let signature = hybrid_sign(&signer_sk, &sign_message);
let _ = recipient_sk;
PqcKeyWrap {
kem_ciphertext: kem_ct.to_bytes(),
recipient_pk: recipient_pk.to_bytes(),
wrapped_keys,
signature: signature.to_bytes(),
signer_pk: signer_pk.to_bytes(),
}
}
#[cfg(feature = "pqc")]
pub fn pqc_unwrap_master_keys(
kem_ciphertext: &[u8],
wrapped_keys: &[u8],
signature: &[u8],
signer_pk_bytes: &[u8],
recipient_sk_bytes: &[u8],
) -> Option<([u8; MASTER_KEY_SIZE], [u8; MASTER_KEY_SIZE])> {
use crate::crypto::pqc::{
HybridCiphertext, HybridSecretKey, HybridSignature, HybridSigningPublicKey, hybrid_decaps,
hybrid_verify,
};
use aes_gcm::aead::generic_array::GenericArray;
use aes_gcm::{Aes256Gcm, KeyInit, aead::Aead};
let kem_ct = HybridCiphertext::from_bytes(kem_ciphertext)?;
let recipient_sk = HybridSecretKey::from_bytes(recipient_sk_bytes)?;
let signer_pk = HybridSigningPublicKey::from_bytes(signer_pk_bytes)?;
let sig = HybridSignature::from_bytes(signature)?;
let mut sign_message = Vec::new();
sign_message.extend_from_slice(kem_ciphertext);
sign_message.extend_from_slice(wrapped_keys);
if !hybrid_verify(&signer_pk, &sign_message, &sig) {
return None; }
let shared_secret = hybrid_decaps(&kem_ct, &recipient_sk).ok()?;
let kek = derive_kek_from_shared_secret(&shared_secret);
if wrapped_keys.len() < 12 + 16 {
return None; }
let nonce = GenericArray::clone_from_slice(&wrapped_keys[..12]);
let ciphertext = &wrapped_keys[12..];
let cipher = Aes256Gcm::new(GenericArray::from_slice(&kek));
let plaintext = cipher.decrypt(&nonce, ciphertext).ok()?;
if plaintext.len() != MASTER_KEY_SIZE * 2 {
return None; }
let mut master_key = [0u8; MASTER_KEY_SIZE];
let mut secondary_key = [0u8; MASTER_KEY_SIZE];
master_key.copy_from_slice(&plaintext[..MASTER_KEY_SIZE]);
secondary_key.copy_from_slice(&plaintext[MASTER_KEY_SIZE..]);
Some((master_key, secondary_key))
}
fn derive_kek_from_shared_secret(shared_secret: &[u8; 32]) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(b"LCPFS_PQC_KEK_V1"); hasher.update(shared_secret);
let result = hasher.finalize();
let mut kek = [0u8; 32];
kek.copy_from_slice(&result);
kek
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sha256() {
let data = b"hello world";
let hash = sha256(data);
assert_eq!(hash[0], 0xb9);
assert_eq!(hash[1], 0x4d);
}
#[test]
fn test_crc32() {
let data = b"123456789";
let crc = crc32(data);
assert_eq!(crc, 0xCBF43926);
}
#[test]
fn test_xts_encrypt_decrypt() {
let mut data = [0u8; 512];
for (i, b) in data.iter_mut().enumerate() {
*b = i as u8;
}
let original = data;
let key1 = [0x42u8; 32];
let key2 = [0x43u8; 32];
xts_aes256_encrypt(&mut data, 0, &key1, &key2);
assert_ne!(data, original);
xts_aes256_decrypt(&mut data, 0, &key1, &key2);
assert_eq!(data, original);
}
#[test]
fn test_chacha20_encrypt_decrypt() {
let mut data = vec![0u8; 256];
for (i, b) in data.iter_mut().enumerate() {
*b = i as u8;
}
let original = data.clone();
let key = [0x55u8; 32];
chacha20_encrypt(&mut data, 42, &key);
assert_ne!(data, original);
chacha20_decrypt(&mut data, 42, &key);
assert_eq!(data, original);
}
#[test]
fn test_derive_header_key_deterministic() {
let password = b"test password";
let salt = [0xABu8; 64];
let key1 = derive_header_key(password, &salt, 0, HashAlgorithm::Sha512);
let key2 = derive_header_key(password, &salt, 0, HashAlgorithm::Sha512);
assert_eq!(key1, key2);
}
#[test]
fn test_derive_with_keyfiles() {
let password = b"password";
let keyfile1 = b"keyfile content 1";
let keyfile2 = b"keyfile content 2";
let salt = [0xCDu8; 64];
let key_no_files = derive_with_keyfiles(password, &[], &salt, 0, HashAlgorithm::Sha256);
let key_with_files = derive_with_keyfiles(
password,
&[keyfile1.as_slice(), keyfile2.as_slice()],
&salt,
0,
HashAlgorithm::Sha256,
);
assert_ne!(key_no_files, key_with_files);
}
#[test]
fn test_gf128_mul_alpha() {
let mut tweak = [0u8; 16];
tweak[0] = 0x01;
gf128_mul_alpha(&mut tweak);
assert_eq!(tweak[0], 0x02);
tweak = [0u8; 16];
tweak[15] = 0x80;
gf128_mul_alpha(&mut tweak);
assert_eq!(tweak[0], 0x87); }
#[test]
fn test_derive_kek_deterministic() {
let shared_secret = [0x42u8; 32];
let kek1 = derive_kek_from_shared_secret(&shared_secret);
let kek2 = derive_kek_from_shared_secret(&shared_secret);
assert_eq!(kek1, kek2, "Same shared secret should produce same KEK");
let different_secret = [0x99u8; 32];
let kek3 = derive_kek_from_shared_secret(&different_secret);
assert_ne!(
kek1, kek3,
"Different shared secret should produce different KEK"
);
}
#[cfg(feature = "pqc")]
mod pqc_tests {
use super::*;
#[test]
fn test_pqc_wrap_unwrap_master_keys() {
let master_key: [u8; MASTER_KEY_SIZE] = [0xAAu8; MASTER_KEY_SIZE];
let secondary_key: [u8; MASTER_KEY_SIZE] = [0xBBu8; MASTER_KEY_SIZE];
let wrapped = pqc_wrap_master_keys(&master_key, &secondary_key);
assert!(
!wrapped.kem_ciphertext.is_empty(),
"KEM ciphertext should not be empty"
);
assert!(
!wrapped.recipient_pk.is_empty(),
"Recipient PK should not be empty"
);
assert!(
!wrapped.wrapped_keys.is_empty(),
"Wrapped keys should not be empty"
);
assert!(
!wrapped.signature.is_empty(),
"Signature should not be empty"
);
assert!(
!wrapped.signer_pk.is_empty(),
"Signer PK should not be empty"
);
assert_ne!(
&wrapped.wrapped_keys[12..],
&master_key[..],
"Wrapped keys should be encrypted"
);
}
#[test]
fn test_pqc_wrap_produces_different_output_each_time() {
let master_key: [u8; MASTER_KEY_SIZE] = [0xCCu8; MASTER_KEY_SIZE];
let secondary_key: [u8; MASTER_KEY_SIZE] = [0xDDu8; MASTER_KEY_SIZE];
let wrapped1 = pqc_wrap_master_keys(&master_key, &secondary_key);
let wrapped2 = pqc_wrap_master_keys(&master_key, &secondary_key);
assert_ne!(
wrapped1.kem_ciphertext, wrapped2.kem_ciphertext,
"Each wrap should use different ephemeral keys"
);
assert_ne!(
wrapped1.wrapped_keys, wrapped2.wrapped_keys,
"Each wrap should use different nonces"
);
}
#[test]
fn test_pqc_header_extension_serialization() {
use crate::vault::types::{PQC_HEADER_EXT_SIZE, PQC_MAGIC, PqcHeaderExtension};
let mut ext = PqcHeaderExtension::new();
ext.version = 1;
ext.algorithm_id = 192; ext.flags = 0x0001;
ext.kem_ciphertext[0] = 0xAA;
ext.recipient_public_key[0] = 0xBB;
ext.wrapped_master_key[0] = 0xCC;
ext.header_signature[0] = 0xDD;
ext.signer_public_key[0] = 0xEE;
let bytes = ext.to_bytes();
assert_eq!(bytes.len(), PQC_HEADER_EXT_SIZE);
let restored = PqcHeaderExtension::from_bytes(&bytes).unwrap();
assert_eq!(restored.magic, PQC_MAGIC);
assert_eq!(restored.version, 1);
assert_eq!(restored.algorithm_id, 192);
assert_eq!(restored.flags, 0x0001);
assert_eq!(restored.kem_ciphertext[0], 0xAA);
assert_eq!(restored.recipient_public_key[0], 0xBB);
assert_eq!(restored.wrapped_master_key[0], 0xCC);
assert_eq!(restored.header_signature[0], 0xDD);
assert_eq!(restored.signer_public_key[0], 0xEE);
}
}
}