impl enc/dec with auth

This commit is contained in:
M Alghazwi 2026-05-01 21:11:44 +03:00
parent 5cb804403e
commit 9ff3fa3b49
No known key found for this signature in database
GPG Key ID: 646E567CAD7DB607
4 changed files with 61 additions and 15 deletions

View File

@ -63,8 +63,8 @@ fn main() -> anyhow::Result<()> {
Some notes: Some notes:
- Encryption and decryption are both in-place for now. - Encryption and decryption are both in-place for now.
- The block length need to be bigger than `64` bytes because Lioness splits the block into two where the left part is 32-byte, and the right part needs at least 16 bytes. might support small blocks in the future, but for Sphinx use-case, this should work. - The block length need to be at least `64` bytes because Lioness splits the block into two where the left part is 32-byte, and the right part needs at least 16 bytes. might support small blocks in the future, but for Sphinx use-case, this should work.
- If you need authenticity, make sure to use `encrypt_in_place_auth` which prepends the plaintext with 128-bits zeros. Also use `decrypt_in_place_auth` to check the zeros after decryption. see [authentication example](./examples/auth) - If you need authenticity, make sure to use `encrypt_auth` which prepends the plaintext with 128-bits zeros. Also use `decrypt_auth` to check the zeros after decryption. see [authentication example](./examples/auth)
### TODO ### TODO
- [x] Add more tests, examples, and benchmarks ... - [x] Add more tests, examples, and benchmarks ...

View File

@ -9,7 +9,7 @@ type TestLioness = Lioness::<
TurboShake128Kdf TurboShake128Kdf
>; >;
fn main() -> Result<()> { fn prepend_before_enc() -> Result<()>{
let mut key: Key256 = Default::default(); let mut key: Key256 = Default::default();
OsRng.fill_bytes(&mut key); OsRng.fill_bytes(&mut key);
let cipher: TestLioness = Lioness::new(&key)?; let cipher: TestLioness = Lioness::new(&key)?;
@ -26,9 +26,37 @@ fn main() -> Result<()> {
cipher.decrypt_in_place(&mut block)?; cipher.decrypt_in_place(&mut block)?;
if block[..SEC_PARAM].iter().all(|&b| b != 0) { for b in block[..SEC_PARAM].iter(){
println!("tampering detected i.e. zero-prefix check failed"); if *b != 0{
println!("tampering detected i.e. zero-prefix check failed");
break
}
} }
Ok(()) Ok(())
} }
fn call_enc_auth() -> Result<()>{
let mut key: Key256 = Default::default();
OsRng.fill_bytes(&mut key);
let cipher: TestLioness = Lioness::new(&key)?;
let mut payload = [0x84u8; 4096];
// let mut block = plaintext.clone();
let mut ciphertext = cipher.encrypt_auth(&mut payload)?;
// tamper with the ciphertext
ciphertext[21] ^= 0x01;
assert!(cipher.decrypt_auth(&mut ciphertext).is_err());
Ok(())
}
fn main() -> Result<()> {
// prepend before calling the lioness encryption
prepend_before_enc()?;
// use built-in functions
call_enc_auth()
}

View File

@ -60,10 +60,10 @@ impl LionessKdf for HkdfSha256{
pub struct DomSepSha256Kdf; pub struct DomSepSha256Kdf;
const LIONESS_ROUND_KEY_DOMAINS: [&[u8]; 4] = [ const LIONESS_ROUND_KEY_DOMAINS: [&[u8]; 4] = [
b"Lioness-key1", b"lioness-key1",
b"Lioness-key2", b"lioness-key2",
b"Lioness-key3", b"lioness-key3",
b"Lionesskey4", b"lionesskey4",
]; ];
impl LionessKdf for DomSepSha256Kdf { impl LionessKdf for DomSepSha256Kdf {
fn derive_keys(master_key: &Key256) -> anyhow::Result<RoundKeys> { fn derive_keys(master_key: &Key256) -> anyhow::Result<RoundKeys> {

View File

@ -121,10 +121,17 @@ impl<
} }
/// Same as `encrypt_in_place` but prepends the plaintext with `SEC_PARAM` bytes of zeros /// Same as `encrypt_in_place` but prepends the plaintext with `SEC_PARAM` bytes of zeros
pub fn encrypt_in_place_auth(&self, _block: &mut [u8]) -> Result<()> { /// WARNING: The output ciphertext is 16 bytes bigger than the input block
let mut plaintext = vec![0u8; SEC_PARAM]; /// Here we can accept 48 bytes since we add 16-bytes zero prefix.
plaintext.extend_from_slice(_block); pub fn encrypt_auth(&self, plaintext: &mut [u8]) -> Result<Vec<u8>> {
todo!() // Here we can accept at least 48 bytes since we add 16-bytes zero prefix.
if plaintext.len() < 2*K_256 - SEC_PARAM {
return Err(anyhow!("block must be at least {} bytes", 2*K_256 - SEC_PARAM));
}
let mut block = vec![0u8; SEC_PARAM + plaintext.len()];
block[SEC_PARAM..].copy_from_slice(plaintext);
self.encrypt_in_place(&mut block)?;
Ok(block)
} }
/// Decrypt a single wide block in place. /// Decrypt a single wide block in place.
@ -150,8 +157,19 @@ impl<
} }
/// Same as `decrypt_in_place` with added check for `SEC_PARAM`-bytes zero prefix /// Same as `decrypt_in_place` with added check for `SEC_PARAM`-bytes zero prefix
pub fn decrypt_in_place_auth(&self, _block: &mut [u8]) -> Result<()> { /// returns either the plaintext without the zero prefix or throws an error
todo!() pub fn decrypt_auth(&self, block: &mut [u8]) -> Result<Vec<u8>> {
if block.len() < 2*K_256 {
return Err(anyhow!("blocks must be at least {} bytes", 2*K_256));
}
self.decrypt_in_place(block)?;
for b in block[..SEC_PARAM].iter(){
if *b != 0{
return Err(anyhow!("ciphertext tampering detected!"))
}
}
Ok(block.to_vec().split_off(SEC_PARAM))
} }
/// apply the steam cipher round /// apply the steam cipher round