#[cfg(any(
feature = "k1-insecure",
feature = "k2",
feature = "k3",
feature = "k4"
))]
use crate::core::error::{PaserkError, PaserkResult};
pub const PIE_NONCE_SIZE: usize = 32;
pub const PIE_TAG_SIZE: usize = 32;
#[cfg(any(
feature = "k1-insecure",
feature = "k2",
feature = "k3",
feature = "k4"
))]
const PIE_ENCRYPTION_KEY_DOMAIN: u8 = 0x80;
#[cfg(any(
feature = "k1-insecure",
feature = "k2",
feature = "k3",
feature = "k4"
))]
const PIE_AUTH_KEY_DOMAIN: u8 = 0x81;
#[cfg(any(feature = "k2", feature = "k4"))]
pub fn pie_wrap_local_k2k4(
wrapping_key: &[u8; 32],
plaintext_key: &[u8; 32],
header: &str,
) -> PaserkResult<([u8; PIE_NONCE_SIZE], [u8; 32], [u8; PIE_TAG_SIZE])> {
use blake2::digest::{FixedOutput, KeyInit, Update};
use blake2::Blake2bMac;
use chacha20::cipher::{KeyIvInit, StreamCipher};
use chacha20::XChaCha20;
use rand_core::{OsRng, TryRngCore};
type Blake2bMac56 = Blake2bMac<blake2::digest::consts::U56>;
type Blake2bMac32 = Blake2bMac<blake2::digest::consts::U32>;
let mut nonce = [0u8; PIE_NONCE_SIZE];
OsRng
.try_fill_bytes(&mut nonce)
.map_err(|_| PaserkError::CryptoError)?;
let mut kdf_mac = <Blake2bMac56 as KeyInit>::new_from_slice(wrapping_key)
.map_err(|_| PaserkError::CryptoError)?;
<Blake2bMac56 as Update>::update(&mut kdf_mac, &[PIE_ENCRYPTION_KEY_DOMAIN]);
<Blake2bMac56 as Update>::update(&mut kdf_mac, &nonce);
let tmp = <Blake2bMac56 as FixedOutput>::finalize_fixed(kdf_mac);
let mut encryption_key = [0u8; 32];
encryption_key.copy_from_slice(&tmp[..32]);
let mut xchacha_nonce = [0u8; 24];
xchacha_nonce.copy_from_slice(&tmp[32..56]);
let mut auth_mac = <Blake2bMac32 as KeyInit>::new_from_slice(wrapping_key)
.map_err(|_| PaserkError::CryptoError)?;
<Blake2bMac32 as Update>::update(&mut auth_mac, &[PIE_AUTH_KEY_DOMAIN]);
<Blake2bMac32 as Update>::update(&mut auth_mac, &nonce);
let mut auth_key: [u8; 32] = <Blake2bMac32 as FixedOutput>::finalize_fixed(auth_mac).into();
let mut ciphertext = *plaintext_key;
let mut cipher = XChaCha20::new(&encryption_key.into(), &xchacha_nonce.into());
cipher.apply_keystream(&mut ciphertext);
let mut tag_mac = <Blake2bMac32 as KeyInit>::new_from_slice(&auth_key)
.map_err(|_| PaserkError::CryptoError)?;
<Blake2bMac32 as Update>::update(&mut tag_mac, header.as_bytes());
<Blake2bMac32 as Update>::update(&mut tag_mac, &nonce);
<Blake2bMac32 as Update>::update(&mut tag_mac, &ciphertext);
let tag: [u8; PIE_TAG_SIZE] = <Blake2bMac32 as FixedOutput>::finalize_fixed(tag_mac).into();
zeroize::Zeroize::zeroize(&mut encryption_key);
zeroize::Zeroize::zeroize(&mut xchacha_nonce);
zeroize::Zeroize::zeroize(&mut auth_key);
Ok((nonce, ciphertext, tag))
}
#[cfg(any(feature = "k2", feature = "k4"))]
pub fn pie_unwrap_local_k2k4(
wrapping_key: &[u8; 32],
nonce: &[u8; PIE_NONCE_SIZE],
ciphertext: &[u8; 32],
tag: &[u8; PIE_TAG_SIZE],
header: &str,
) -> PaserkResult<[u8; 32]> {
use blake2::digest::{FixedOutput, KeyInit, Update};
use blake2::Blake2bMac;
use chacha20::cipher::{KeyIvInit, StreamCipher};
use chacha20::XChaCha20;
use subtle::ConstantTimeEq;
type Blake2bMac56 = Blake2bMac<blake2::digest::consts::U56>;
type Blake2bMac32 = Blake2bMac<blake2::digest::consts::U32>;
let mut kdf_mac = <Blake2bMac56 as KeyInit>::new_from_slice(wrapping_key)
.map_err(|_| PaserkError::CryptoError)?;
<Blake2bMac56 as Update>::update(&mut kdf_mac, &[PIE_ENCRYPTION_KEY_DOMAIN]);
<Blake2bMac56 as Update>::update(&mut kdf_mac, nonce);
let tmp = <Blake2bMac56 as FixedOutput>::finalize_fixed(kdf_mac);
let mut encryption_key = [0u8; 32];
encryption_key.copy_from_slice(&tmp[..32]);
let mut xchacha_nonce = [0u8; 24];
xchacha_nonce.copy_from_slice(&tmp[32..56]);
let mut auth_mac = <Blake2bMac32 as KeyInit>::new_from_slice(wrapping_key)
.map_err(|_| PaserkError::CryptoError)?;
<Blake2bMac32 as Update>::update(&mut auth_mac, &[PIE_AUTH_KEY_DOMAIN]);
<Blake2bMac32 as Update>::update(&mut auth_mac, nonce);
let mut auth_key: [u8; 32] = <Blake2bMac32 as FixedOutput>::finalize_fixed(auth_mac).into();
let mut tag_mac = <Blake2bMac32 as KeyInit>::new_from_slice(&auth_key)
.map_err(|_| PaserkError::CryptoError)?;
<Blake2bMac32 as Update>::update(&mut tag_mac, header.as_bytes());
<Blake2bMac32 as Update>::update(&mut tag_mac, nonce);
<Blake2bMac32 as Update>::update(&mut tag_mac, ciphertext);
let computed_tag: [u8; PIE_TAG_SIZE] =
<Blake2bMac32 as FixedOutput>::finalize_fixed(tag_mac).into();
if computed_tag.ct_eq(tag).into() {
let mut plaintext = *ciphertext;
let mut cipher = XChaCha20::new(&encryption_key.into(), &xchacha_nonce.into());
cipher.apply_keystream(&mut plaintext);
zeroize::Zeroize::zeroize(&mut encryption_key);
zeroize::Zeroize::zeroize(&mut xchacha_nonce);
zeroize::Zeroize::zeroize(&mut auth_key);
Ok(plaintext)
} else {
zeroize::Zeroize::zeroize(&mut encryption_key);
zeroize::Zeroize::zeroize(&mut xchacha_nonce);
zeroize::Zeroize::zeroize(&mut auth_key);
Err(PaserkError::AuthenticationFailed)
}
}
#[cfg(any(feature = "k2", feature = "k4"))]
pub fn pie_wrap_secret_k2k4(
wrapping_key: &[u8; 32],
plaintext_key: &[u8; 64],
header: &str,
) -> PaserkResult<([u8; PIE_NONCE_SIZE], [u8; 64], [u8; PIE_TAG_SIZE])> {
use blake2::digest::{FixedOutput, KeyInit, Update};
use blake2::Blake2bMac;
use chacha20::cipher::{KeyIvInit, StreamCipher};
use chacha20::XChaCha20;
use rand_core::{OsRng, TryRngCore};
type Blake2bMac56 = Blake2bMac<blake2::digest::consts::U56>;
type Blake2bMac32 = Blake2bMac<blake2::digest::consts::U32>;
let mut nonce = [0u8; PIE_NONCE_SIZE];
OsRng
.try_fill_bytes(&mut nonce)
.map_err(|_| PaserkError::CryptoError)?;
let mut kdf_mac = <Blake2bMac56 as KeyInit>::new_from_slice(wrapping_key)
.map_err(|_| PaserkError::CryptoError)?;
<Blake2bMac56 as Update>::update(&mut kdf_mac, &[PIE_ENCRYPTION_KEY_DOMAIN]);
<Blake2bMac56 as Update>::update(&mut kdf_mac, &nonce);
let tmp = <Blake2bMac56 as FixedOutput>::finalize_fixed(kdf_mac);
let mut encryption_key = [0u8; 32];
encryption_key.copy_from_slice(&tmp[..32]);
let mut xchacha_nonce = [0u8; 24];
xchacha_nonce.copy_from_slice(&tmp[32..56]);
let mut auth_mac = <Blake2bMac32 as KeyInit>::new_from_slice(wrapping_key)
.map_err(|_| PaserkError::CryptoError)?;
<Blake2bMac32 as Update>::update(&mut auth_mac, &[PIE_AUTH_KEY_DOMAIN]);
<Blake2bMac32 as Update>::update(&mut auth_mac, &nonce);
let mut auth_key: [u8; 32] = <Blake2bMac32 as FixedOutput>::finalize_fixed(auth_mac).into();
let mut ciphertext = *plaintext_key;
let mut cipher = XChaCha20::new(&encryption_key.into(), &xchacha_nonce.into());
cipher.apply_keystream(&mut ciphertext);
let mut tag_mac = <Blake2bMac32 as KeyInit>::new_from_slice(&auth_key)
.map_err(|_| PaserkError::CryptoError)?;
<Blake2bMac32 as Update>::update(&mut tag_mac, header.as_bytes());
<Blake2bMac32 as Update>::update(&mut tag_mac, &nonce);
<Blake2bMac32 as Update>::update(&mut tag_mac, &ciphertext);
let tag: [u8; PIE_TAG_SIZE] = <Blake2bMac32 as FixedOutput>::finalize_fixed(tag_mac).into();
zeroize::Zeroize::zeroize(&mut encryption_key);
zeroize::Zeroize::zeroize(&mut xchacha_nonce);
zeroize::Zeroize::zeroize(&mut auth_key);
Ok((nonce, ciphertext, tag))
}
#[cfg(any(feature = "k2", feature = "k4"))]
pub fn pie_unwrap_secret_k2k4(
wrapping_key: &[u8; 32],
nonce: &[u8; PIE_NONCE_SIZE],
ciphertext: &[u8; 64],
tag: &[u8; PIE_TAG_SIZE],
header: &str,
) -> PaserkResult<[u8; 64]> {
use blake2::digest::{FixedOutput, KeyInit, Update};
use blake2::Blake2bMac;
use chacha20::cipher::{KeyIvInit, StreamCipher};
use chacha20::XChaCha20;
use subtle::ConstantTimeEq;
type Blake2bMac56 = Blake2bMac<blake2::digest::consts::U56>;
type Blake2bMac32 = Blake2bMac<blake2::digest::consts::U32>;
let mut kdf_mac = <Blake2bMac56 as KeyInit>::new_from_slice(wrapping_key)
.map_err(|_| PaserkError::CryptoError)?;
<Blake2bMac56 as Update>::update(&mut kdf_mac, &[PIE_ENCRYPTION_KEY_DOMAIN]);
<Blake2bMac56 as Update>::update(&mut kdf_mac, nonce);
let tmp = <Blake2bMac56 as FixedOutput>::finalize_fixed(kdf_mac);
let mut encryption_key = [0u8; 32];
encryption_key.copy_from_slice(&tmp[..32]);
let mut xchacha_nonce = [0u8; 24];
xchacha_nonce.copy_from_slice(&tmp[32..56]);
let mut auth_mac = <Blake2bMac32 as KeyInit>::new_from_slice(wrapping_key)
.map_err(|_| PaserkError::CryptoError)?;
<Blake2bMac32 as Update>::update(&mut auth_mac, &[PIE_AUTH_KEY_DOMAIN]);
<Blake2bMac32 as Update>::update(&mut auth_mac, nonce);
let mut auth_key: [u8; 32] = <Blake2bMac32 as FixedOutput>::finalize_fixed(auth_mac).into();
let mut tag_mac = <Blake2bMac32 as KeyInit>::new_from_slice(&auth_key)
.map_err(|_| PaserkError::CryptoError)?;
<Blake2bMac32 as Update>::update(&mut tag_mac, header.as_bytes());
<Blake2bMac32 as Update>::update(&mut tag_mac, nonce);
<Blake2bMac32 as Update>::update(&mut tag_mac, ciphertext);
let computed_tag: [u8; PIE_TAG_SIZE] =
<Blake2bMac32 as FixedOutput>::finalize_fixed(tag_mac).into();
if computed_tag.ct_eq(tag).into() {
let mut plaintext = *ciphertext;
let mut cipher = XChaCha20::new(&encryption_key.into(), &xchacha_nonce.into());
cipher.apply_keystream(&mut plaintext);
zeroize::Zeroize::zeroize(&mut encryption_key);
zeroize::Zeroize::zeroize(&mut xchacha_nonce);
zeroize::Zeroize::zeroize(&mut auth_key);
Ok(plaintext)
} else {
zeroize::Zeroize::zeroize(&mut encryption_key);
zeroize::Zeroize::zeroize(&mut xchacha_nonce);
zeroize::Zeroize::zeroize(&mut auth_key);
Err(PaserkError::AuthenticationFailed)
}
}
#[cfg(any(feature = "k1-insecure", feature = "k3"))]
pub const PIE_K1K3_NONCE_SIZE: usize = 32;
#[cfg(any(feature = "k1-insecure", feature = "k3"))]
pub const PIE_K1K3_TAG_SIZE: usize = 48;
#[cfg(any(feature = "k1-insecure", feature = "k3"))]
const PIE_K1K3_ENCRYPTION_KEY_DOMAIN: u8 = 0x80;
#[cfg(any(feature = "k1-insecure", feature = "k3"))]
const PIE_K1K3_AUTH_KEY_DOMAIN: u8 = 0x81;
#[cfg(any(feature = "k1-insecure", feature = "k3"))]
fn derive_pie_keys_k1k3(
wrapping_key: &[u8; 32],
nonce: &[u8; PIE_K1K3_NONCE_SIZE],
) -> PaserkResult<([u8; 32], [u8; 16], [u8; 48])> {
use hmac::{Hmac, Mac};
use sha2::Sha384;
let mut kdf_mac = <Hmac<Sha384> as Mac>::new_from_slice(wrapping_key)
.map_err(|_| PaserkError::CryptoError)?;
kdf_mac.update(&[PIE_K1K3_ENCRYPTION_KEY_DOMAIN]);
kdf_mac.update(nonce);
let tmp = kdf_mac.finalize().into_bytes();
let mut encryption_key = [0u8; 32];
encryption_key.copy_from_slice(&tmp[..32]);
let mut aes_nonce = [0u8; 16];
aes_nonce.copy_from_slice(&tmp[32..48]);
let mut auth_mac = <Hmac<Sha384> as Mac>::new_from_slice(wrapping_key)
.map_err(|_| PaserkError::CryptoError)?;
auth_mac.update(&[PIE_K1K3_AUTH_KEY_DOMAIN]);
auth_mac.update(nonce);
let auth_key: [u8; 48] = auth_mac.finalize().into_bytes().into();
Ok((encryption_key, aes_nonce, auth_key))
}
#[cfg(any(feature = "k1-insecure", feature = "k3"))]
fn compute_pie_tag_k1k3(
auth_key: &[u8; 48],
header: &str,
nonce: &[u8],
ciphertext: &[u8],
) -> PaserkResult<[u8; PIE_K1K3_TAG_SIZE]> {
use hmac::{Hmac, Mac};
use sha2::Sha384;
let mut tag_mac =
<Hmac<Sha384> as Mac>::new_from_slice(auth_key).map_err(|_| PaserkError::CryptoError)?;
tag_mac.update(header.as_bytes());
tag_mac.update(nonce);
tag_mac.update(ciphertext);
Ok(tag_mac.finalize().into_bytes().into())
}
#[cfg(any(feature = "k1-insecure", feature = "k3"))]
fn aes_ctr_apply(key: &[u8; 32], nonce: &[u8; 16], data: &mut [u8]) {
use aes::cipher::{KeyIvInit, StreamCipher};
use ctr::Ctr128BE;
type Aes256Ctr = Ctr128BE<aes::Aes256>;
let mut cipher = Aes256Ctr::new(key.into(), nonce.into());
cipher.apply_keystream(data);
}
#[cfg(any(feature = "k1-insecure", feature = "k3"))]
pub fn pie_wrap_local_k1k3(
wrapping_key: &[u8; 32],
plaintext_key: &[u8; 32],
header: &str,
) -> PaserkResult<([u8; PIE_K1K3_NONCE_SIZE], [u8; 32], [u8; PIE_K1K3_TAG_SIZE])> {
use rand_core::{OsRng, TryRngCore};
let mut nonce = [0u8; PIE_K1K3_NONCE_SIZE];
OsRng
.try_fill_bytes(&mut nonce)
.map_err(|_| PaserkError::CryptoError)?;
let (mut encryption_key, mut aes_nonce, mut auth_key) =
derive_pie_keys_k1k3(wrapping_key, &nonce)?;
let mut ciphertext = *plaintext_key;
aes_ctr_apply(&encryption_key, &aes_nonce, &mut ciphertext);
let tag = compute_pie_tag_k1k3(&auth_key, header, &nonce, &ciphertext)?;
zeroize::Zeroize::zeroize(&mut encryption_key);
zeroize::Zeroize::zeroize(&mut aes_nonce);
zeroize::Zeroize::zeroize(&mut auth_key);
Ok((nonce, ciphertext, tag))
}
#[cfg(any(feature = "k1-insecure", feature = "k3"))]
pub fn pie_unwrap_local_k1k3(
wrapping_key: &[u8; 32],
nonce: &[u8; PIE_K1K3_NONCE_SIZE],
ciphertext: &[u8; 32],
tag: &[u8; PIE_K1K3_TAG_SIZE],
header: &str,
) -> PaserkResult<[u8; 32]> {
use subtle::ConstantTimeEq;
let (mut encryption_key, mut aes_nonce, mut auth_key) =
derive_pie_keys_k1k3(wrapping_key, nonce)?;
let computed_tag = compute_pie_tag_k1k3(&auth_key, header, nonce, ciphertext)?;
if computed_tag.ct_eq(tag).into() {
let mut plaintext = *ciphertext;
aes_ctr_apply(&encryption_key, &aes_nonce, &mut plaintext);
zeroize::Zeroize::zeroize(&mut encryption_key);
zeroize::Zeroize::zeroize(&mut aes_nonce);
zeroize::Zeroize::zeroize(&mut auth_key);
Ok(plaintext)
} else {
zeroize::Zeroize::zeroize(&mut encryption_key);
zeroize::Zeroize::zeroize(&mut aes_nonce);
zeroize::Zeroize::zeroize(&mut auth_key);
Err(PaserkError::AuthenticationFailed)
}
}
#[cfg(feature = "k3")]
pub fn pie_wrap_secret_k3(
wrapping_key: &[u8; 32],
plaintext_key: &[u8; 48],
header: &str,
) -> PaserkResult<([u8; PIE_K1K3_NONCE_SIZE], [u8; 48], [u8; PIE_K1K3_TAG_SIZE])> {
use rand_core::{OsRng, TryRngCore};
let mut nonce = [0u8; PIE_K1K3_NONCE_SIZE];
OsRng
.try_fill_bytes(&mut nonce)
.map_err(|_| PaserkError::CryptoError)?;
let (mut encryption_key, mut aes_nonce, mut auth_key) =
derive_pie_keys_k1k3(wrapping_key, &nonce)?;
let mut ciphertext = *plaintext_key;
aes_ctr_apply(&encryption_key, &aes_nonce, &mut ciphertext);
let tag = compute_pie_tag_k1k3(&auth_key, header, &nonce, &ciphertext)?;
zeroize::Zeroize::zeroize(&mut encryption_key);
zeroize::Zeroize::zeroize(&mut aes_nonce);
zeroize::Zeroize::zeroize(&mut auth_key);
Ok((nonce, ciphertext, tag))
}
#[cfg(feature = "k3")]
pub fn pie_unwrap_secret_k3(
wrapping_key: &[u8; 32],
nonce: &[u8; PIE_K1K3_NONCE_SIZE],
ciphertext: &[u8; 48],
tag: &[u8; PIE_K1K3_TAG_SIZE],
header: &str,
) -> PaserkResult<[u8; 48]> {
use subtle::ConstantTimeEq;
let (mut encryption_key, mut aes_nonce, mut auth_key) =
derive_pie_keys_k1k3(wrapping_key, nonce)?;
let computed_tag = compute_pie_tag_k1k3(&auth_key, header, nonce, ciphertext)?;
if computed_tag.ct_eq(tag).into() {
let mut plaintext = *ciphertext;
aes_ctr_apply(&encryption_key, &aes_nonce, &mut plaintext);
zeroize::Zeroize::zeroize(&mut encryption_key);
zeroize::Zeroize::zeroize(&mut aes_nonce);
zeroize::Zeroize::zeroize(&mut auth_key);
Ok(plaintext)
} else {
zeroize::Zeroize::zeroize(&mut encryption_key);
zeroize::Zeroize::zeroize(&mut aes_nonce);
zeroize::Zeroize::zeroize(&mut auth_key);
Err(PaserkError::AuthenticationFailed)
}
}
#[cfg(test)]
#[cfg(any(
feature = "k1-insecure",
feature = "k2",
feature = "k3",
feature = "k4"
))]
#[allow(deprecated)]
mod tests {
use super::*;
#[test]
#[cfg(feature = "k4")]
fn test_pie_wrap_unwrap_local_k4_roundtrip() -> PaserkResult<()> {
let wrapping_key = [0x42u8; 32];
let plaintext_key = [0x13u8; 32];
let header = "k4.local-wrap.pie.";
let (nonce, ciphertext, tag) = pie_wrap_local_k2k4(&wrapping_key, &plaintext_key, header)?;
assert_ne!(ciphertext, plaintext_key);
let unwrapped = pie_unwrap_local_k2k4(&wrapping_key, &nonce, &ciphertext, &tag, header)?;
assert_eq!(unwrapped, plaintext_key);
Ok(())
}
#[test]
#[cfg(feature = "k4")]
fn test_pie_wrap_unwrap_secret_k4_roundtrip() -> PaserkResult<()> {
let wrapping_key = [0x42u8; 32];
let plaintext_key = [0x13u8; 64];
let header = "k4.secret-wrap.pie.";
let (nonce, ciphertext, tag) = pie_wrap_secret_k2k4(&wrapping_key, &plaintext_key, header)?;
assert_ne!(ciphertext, plaintext_key);
let unwrapped = pie_unwrap_secret_k2k4(&wrapping_key, &nonce, &ciphertext, &tag, header)?;
assert_eq!(unwrapped, plaintext_key);
Ok(())
}
#[test]
#[cfg(feature = "k4")]
fn test_pie_unwrap_local_wrong_key() -> PaserkResult<()> {
let wrapping_key = [0x42u8; 32];
let wrong_key = [0x43u8; 32];
let plaintext_key = [0x13u8; 32];
let header = "k4.local-wrap.pie.";
let (nonce, ciphertext, tag) = pie_wrap_local_k2k4(&wrapping_key, &plaintext_key, header)?;
let result = pie_unwrap_local_k2k4(&wrong_key, &nonce, &ciphertext, &tag, header);
assert!(matches!(result, Err(PaserkError::AuthenticationFailed)));
Ok(())
}
#[test]
#[cfg(feature = "k4")]
fn test_pie_unwrap_local_modified_ciphertext() -> PaserkResult<()> {
let wrapping_key = [0x42u8; 32];
let plaintext_key = [0x13u8; 32];
let header = "k4.local-wrap.pie.";
let (nonce, mut ciphertext, tag) =
pie_wrap_local_k2k4(&wrapping_key, &plaintext_key, header)?;
ciphertext[0] ^= 0xff;
let result = pie_unwrap_local_k2k4(&wrapping_key, &nonce, &ciphertext, &tag, header);
assert!(matches!(result, Err(PaserkError::AuthenticationFailed)));
Ok(())
}
#[test]
#[cfg(feature = "k4")]
fn test_pie_unwrap_local_modified_tag() -> PaserkResult<()> {
let wrapping_key = [0x42u8; 32];
let plaintext_key = [0x13u8; 32];
let header = "k4.local-wrap.pie.";
let (nonce, ciphertext, mut tag) =
pie_wrap_local_k2k4(&wrapping_key, &plaintext_key, header)?;
tag[0] ^= 0xff;
let result = pie_unwrap_local_k2k4(&wrapping_key, &nonce, &ciphertext, &tag, header);
assert!(matches!(result, Err(PaserkError::AuthenticationFailed)));
Ok(())
}
#[test]
#[cfg(feature = "k4")]
fn test_pie_unwrap_local_wrong_header() -> PaserkResult<()> {
let wrapping_key = [0x42u8; 32];
let plaintext_key = [0x13u8; 32];
let header = "k4.local-wrap.pie.";
let wrong_header = "k4.secret-wrap.pie.";
let (nonce, ciphertext, tag) = pie_wrap_local_k2k4(&wrapping_key, &plaintext_key, header)?;
let result = pie_unwrap_local_k2k4(&wrapping_key, &nonce, &ciphertext, &tag, wrong_header);
assert!(matches!(result, Err(PaserkError::AuthenticationFailed)));
Ok(())
}
#[test]
#[cfg(feature = "k4")]
fn test_pie_wrap_produces_different_nonces() -> PaserkResult<()> {
let wrapping_key = [0x42u8; 32];
let plaintext_key = [0x13u8; 32];
let header = "k4.local-wrap.pie.";
let (nonce1, _, _) = pie_wrap_local_k2k4(&wrapping_key, &plaintext_key, header)?;
let (nonce2, _, _) = pie_wrap_local_k2k4(&wrapping_key, &plaintext_key, header)?;
assert_ne!(nonce1, nonce2);
Ok(())
}
#[test]
#[cfg(feature = "k3")]
fn test_pie_wrap_unwrap_local_k3_roundtrip() -> PaserkResult<()> {
let wrapping_key = [0x42u8; 32];
let plaintext_key = [0x13u8; 32];
let header = "k3.local-wrap.pie.";
let (nonce, ciphertext, tag) = pie_wrap_local_k1k3(&wrapping_key, &plaintext_key, header)?;
assert_ne!(ciphertext, plaintext_key);
let unwrapped = pie_unwrap_local_k1k3(&wrapping_key, &nonce, &ciphertext, &tag, header)?;
assert_eq!(unwrapped, plaintext_key);
Ok(())
}
#[test]
#[cfg(feature = "k1-insecure")]
fn test_pie_wrap_unwrap_local_k1_roundtrip() -> PaserkResult<()> {
let wrapping_key = [0x42u8; 32];
let plaintext_key = [0x13u8; 32];
let header = "k1.local-wrap.pie.";
let (nonce, ciphertext, tag) = pie_wrap_local_k1k3(&wrapping_key, &plaintext_key, header)?;
assert_ne!(ciphertext, plaintext_key);
let unwrapped = pie_unwrap_local_k1k3(&wrapping_key, &nonce, &ciphertext, &tag, header)?;
assert_eq!(unwrapped, plaintext_key);
Ok(())
}
#[test]
#[cfg(feature = "k3")]
fn test_pie_wrap_unwrap_secret_k3_roundtrip() -> PaserkResult<()> {
let wrapping_key = [0x42u8; 32];
let plaintext_key = [0x13u8; 48];
let header = "k3.secret-wrap.pie.";
let (nonce, ciphertext, tag) = pie_wrap_secret_k3(&wrapping_key, &plaintext_key, header)?;
assert_ne!(ciphertext, plaintext_key);
let unwrapped = pie_unwrap_secret_k3(&wrapping_key, &nonce, &ciphertext, &tag, header)?;
assert_eq!(unwrapped, plaintext_key);
Ok(())
}
#[test]
#[cfg(feature = "k3")]
fn test_pie_k1k3_unwrap_wrong_key() -> PaserkResult<()> {
let wrapping_key = [0x42u8; 32];
let wrong_key = [0x43u8; 32];
let plaintext_key = [0x13u8; 32];
let header = "k3.local-wrap.pie.";
let (nonce, ciphertext, tag) = pie_wrap_local_k1k3(&wrapping_key, &plaintext_key, header)?;
let result = pie_unwrap_local_k1k3(&wrong_key, &nonce, &ciphertext, &tag, header);
assert!(matches!(result, Err(PaserkError::AuthenticationFailed)));
Ok(())
}
#[test]
#[cfg(feature = "k3")]
fn test_pie_k1k3_unwrap_modified_tag() -> PaserkResult<()> {
let wrapping_key = [0x42u8; 32];
let plaintext_key = [0x13u8; 32];
let header = "k3.local-wrap.pie.";
let (nonce, ciphertext, mut tag) =
pie_wrap_local_k1k3(&wrapping_key, &plaintext_key, header)?;
tag[0] ^= 0xff;
let result = pie_unwrap_local_k1k3(&wrapping_key, &nonce, &ciphertext, &tag, header);
assert!(matches!(result, Err(PaserkError::AuthenticationFailed)));
Ok(())
}
#[test]
#[cfg(feature = "k3")]
fn test_pie_k1k3_tag_size() {
assert_eq!(PIE_K1K3_TAG_SIZE, 48);
}
#[test]
#[cfg(feature = "k3")]
fn test_pie_k1k3_produces_different_nonces() -> PaserkResult<()> {
let wrapping_key = [0x42u8; 32];
let plaintext_key = [0x13u8; 32];
let header = "k3.local-wrap.pie.";
let (nonce1, _, _) = pie_wrap_local_k1k3(&wrapping_key, &plaintext_key, header)?;
let (nonce2, _, _) = pie_wrap_local_k1k3(&wrapping_key, &plaintext_key, header)?;
assert_ne!(nonce1, nonce2);
Ok(())
}
}