use chacha::KeyStream;
use crate::{util,generate,ArxKW,ArxKwError,AuthTag};
use crate::ConstantTimeEq;
pub struct GX;
impl GX {
#[must_use]
#[cfg(not(tarpaulin_include))]
pub const fn key_length() -> usize {
32
}
}
impl ArxKW for GX {
type Key = [u8; Self::key_length()];
fn encrypt(key: &Self::Key, plaintext: &[u8]) -> Result<(Vec<u8>, AuthTag), ArxKwError> {
let (k1,k2) = generate::subkeys(key)?;
let authentication_tag = util::sip_array_keyed(&k1, plaintext);
let nonce = construct_nonce(&authentication_tag);
let mut stream = util::xchacha8::new(&k2,&nonce);
let mut ciphertext = plaintext.to_vec();
stream.xor_read(&mut ciphertext).map_err(|e| ArxKwError::ChaChaError(format!("{:?}: X",e)))?;
Ok((ciphertext,authentication_tag))
}
fn decrypt(key: &Self::Key, ciphertext: &[u8], authentication_tag: &AuthTag) -> Result<Vec<u8>, ArxKwError> {
let (k1,k2) = generate::subkeys(key)?;
let nonce = construct_nonce(authentication_tag);
let mut stream = util::xchacha8::new(&k2, &nonce);
let mut p_prime = ciphertext.to_vec();
stream.xor_read(&mut p_prime).map_err(|e| ArxKwError::ChaChaError(format!("{:?}: X",e)))?;
let t_prime = util::sip_array_keyed(&k1, &p_prime);
if bool::from(t_prime.ct_eq(authentication_tag)) { return Ok(p_prime);
}
Err(ArxKwError::BadTags(t_prime, *authentication_tag))
}
}
const NONCE_INIT_GX: [u8;24] = [0x61, 0x72, 0x62, 0x69, 0x74, 0x72, 0x47, 0x58,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
#[must_use]
#[inline]
#[cfg(not(tarpaulin_include))]
pub fn construct_nonce(authentication_tag: &AuthTag) -> [u8;24] {
let mut nonce = NONCE_INIT_GX;
nonce[8..24].clone_from_slice(authentication_tag.as_ref());
nonce
}
#[cfg(test)]
mod tests {
extern crate hex;
use super::*;
use crate::assert_ct_eq;
use anyhow::Result;
use hex::FromHex;
#[test]
fn test_encrypt() -> Result<()> {
let k = <[u8; 32]>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")?;
let p = <[u8; 32]>::from_hex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")?;
let t_expected = AuthTag(<[u8; 16]>::from_hex("016325cf6a3c4b2e3b039675e1ccbc65")?);
let c_expected = <[u8; 32]>::from_hex("2f83f391c97f3606ccd5709c6ee15d66cd7e65a2aeb7dc3066636e8f6b0d39c3")?;
let (c,t) = GX::encrypt(&k, &p)?;
assert_eq!(&c.to_vec(), &c_expected);
assert_ct_eq!(t, &t_expected);
Ok(())
}
#[test]
fn test_encrypt_blob() -> Result<()> {
let k = <[u8; 32]>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")?;
let p = <[u8; 32]>::from_hex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")?;
let blob_expected = <[u8; 48]>::from_hex("016325cf6a3c4b2e3b039675e1ccbc652f83f391c97f3606ccd5709c6ee15d66cd7e65a2aeb7dc3066636e8f6b0d39c3")?;
let blob = GX::encrypt_blob(&k, &p)?;
assert_ct_eq!(blob, &blob_expected);
Ok(())
}
#[test]
fn test_decrypt_blob() -> Result<()> {
let k = <[u8; 32]>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")?;
let p_expected = <[u8; 32]>::from_hex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")?;
let blob= <[u8; 48]>::from_hex("016325cf6a3c4b2e3b039675e1ccbc652f83f391c97f3606ccd5709c6ee15d66cd7e65a2aeb7dc3066636e8f6b0d39c3")?;
let p = GX::decrypt_blob(&k, &blob)?;
assert_ct_eq!(&p, &p_expected);
Ok(())
}
#[test]
fn test_decrypt() -> Result<()> {
let k = <[u8; 32]>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")?;
let t = AuthTag(<[u8; 16]>::from_hex("016325cf6a3c4b2e3b039675e1ccbc65")?);
let c = <[u8; 32]>::from_hex("2f83f391c97f3606ccd5709c6ee15d66cd7e65a2aeb7dc3066636e8f6b0d39c3")?;
let p_expected = <[u8; 32]>::from_hex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")?;
let p = GX::decrypt(&k, &c, &t)?;
assert_eq!(p, p_expected);
Ok(())
}
#[test]
fn test_bad_decrypt() -> Result<()> {
let k = <[u8; 32]>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")?;
let c = <[u8; 32]>::from_hex("2283f391c97f3606ccd5709c6ee15d66cd7e65a2aeb7dc3066636e8f6b0d39c3")?; let t_bad = AuthTag(<[u8; 16]>::from_hex("016325cf6a3c4b2e3b039675e1ccbc65")?);
let res = GX::decrypt(&k, &c, &t_bad);
assert!(res.is_err());
Ok(())
}
}