#[cfg(feature = "legacy-pqclean")]
use crate::core::{CryptographicFunctions, KeyControlVariant};
use crate::{
cryptography::{
hmac_sign::{Operation, Sign, SignType},
*,
},
error::CryptError,
};
use chacha20poly1305::aead::generic_array::GenericArray;
use chacha20poly1305::{
aead::{Aead, KeyInit},
XChaCha20Poly1305, XNonce,
};
use hex;
use hmac::{Hmac, Mac};
use rand::{rngs::OsRng, RngCore};
use sha2::Sha256;
use std::result::Result;
pub fn generate_nonce() -> [u8; 24] {
let mut nonce = [0u8; 24];
OsRng.fill_bytes(&mut nonce);
nonce
}
fn derive_legacy_xchacha_poly_key(
sharedsecret: &[u8],
nonce: &[u8; 24],
) -> Result<[u8; 32], CryptError> {
type HmacSha256 = Hmac<Sha256>;
let mut extract =
<HmacSha256 as Mac>::new_from_slice(nonce).expect("HMAC-SHA256 accepts any key length");
extract.update(sharedsecret);
let prk = extract.finalize().into_bytes();
let mut expand =
<HmacSha256 as Mac>::new_from_slice(&prk).expect("HMAC-SHA256 accepts any key length");
expand.update(b"crypt_guard:legacy:xchacha20poly1305:key");
expand.update(&[1]);
let okm = expand.finalize().into_bytes();
let mut key = [0u8; 32];
key.copy_from_slice(&okm[..32]);
Ok(key)
}
impl CipherChaChaPoly {
pub fn new(infos: CryptographicInformation, nonce: Option<String>) -> Self {
let nonce: [u8; 24] = match nonce {
Some(nonce) => {
let mut array = [0u8; 24];
let decoded = hex::decode(nonce).expect("An error occurred while decoding hex!");
array.copy_from_slice(&decoded);
array
}
None => generate_nonce(),
};
CipherChaChaPoly {
infos,
sharedsecret: Vec::new(),
nonce,
}
}
pub fn get_data(&self) -> Result<Vec<u8>, CryptError> {
let data = &self.infos.content()?;
let data = data.to_vec();
Ok(data)
}
pub fn set_shared_secret(&mut self, sharedsecret: Vec<u8>) -> &Self {
self.sharedsecret = sharedsecret;
self
}
pub fn sharedsecret(&self) -> Result<&[u8], CryptError> {
Ok(&self.sharedsecret)
}
pub fn set_nonce(&mut self, nonce: [u8; 24]) -> &[u8; 24] {
self.nonce = nonce;
&self.nonce
}
pub fn nonce(&self) -> &[u8; 24] {
&self.nonce
}
#[allow(dead_code)]
fn encryption(&self) -> Result<(Vec<u8>, [u8; 24]), CryptError> {
let plaintext = self.infos.content()?;
let passphrase = self.infos.passphrase()?.to_vec();
let derived_key = derive_legacy_xchacha_poly_key(&self.sharedsecret, &self.nonce)?;
let key = GenericArray::from_slice(&derived_key);
let cipher = XChaCha20Poly1305::new(key);
let nonce = XNonce::from_slice(&self.nonce);
let mut hmac = Sign::new(
plaintext.to_vec(),
passphrase,
Operation::Sign,
SignType::Sha512,
);
let data = hmac.hmac();
let encrypted = cipher
.encrypt(nonce, &*data)
.map_err(|e| CryptError::new(e.to_string().as_str()))?;
let nonce = *self.nonce();
Ok((encrypted, nonce))
}
#[allow(dead_code)]
fn decryption(&self) -> Result<(Vec<u8>, [u8; 24]), CryptError> {
let ciphertext = self.infos.content()?;
let passphrase = self.infos.passphrase()?.to_vec();
let nonce = XNonce::from_slice(&self.nonce);
let derived_key = derive_legacy_xchacha_poly_key(&self.sharedsecret, &self.nonce)?;
let cipher = XChaCha20Poly1305::new(GenericArray::from_slice(&derived_key));
let decrypted = match cipher.decrypt(nonce, &*ciphertext.to_vec()) {
Ok(decrypted) => decrypted,
Err(_) => {
let legacy_cipher =
XChaCha20Poly1305::new(GenericArray::from_slice(&self.sharedsecret));
legacy_cipher
.decrypt(nonce, &*ciphertext.to_vec())
.map_err(|e| CryptError::new(e.to_string().as_str()))?
}
};
let mut hmac = Sign::new(
decrypted.to_vec(),
passphrase,
Operation::Verify,
SignType::Sha512,
);
let data = hmac.hmac();
let nonce = *self.nonce();
Ok((data, nonce))
}
}
#[cfg(feature = "legacy-pqclean")]
impl CryptographicFunctions for CipherChaChaPoly {
fn encrypt(&mut self, public_key: Vec<u8>) -> Result<(Vec<u8>, Vec<u8>), CryptError> {
let key = KeyControlVariant::new(self.infos.metadata.key_type()?);
let (sharedsecret, ciphertext) = key.encap(&public_key)?;
let _ = self.set_shared_secret(sharedsecret);
let (encrypted_data, nonce) = self.encryption()?;
println!("Please write down this nonce: {}", hex::encode(nonce));
Ok((encrypted_data, ciphertext))
}
fn decrypt(&mut self, secret_key: Vec<u8>, ciphertext: Vec<u8>) -> Result<Vec<u8>, CryptError> {
let key = KeyControlVariant::new(self.infos.metadata.key_type()?);
let sharedsecret = key.decap(&secret_key, &ciphertext)?;
let _ = self.set_shared_secret(sharedsecret);
let (decrypted_data, _nonce) = self.decryption()?;
Ok(decrypted_data)
}
}