#![cfg(any(target_os = "linux", target_os = "macos"))]
use core::mem::size_of;
use core::ops::Deref;
use core::pin::Pin;
use core::ptr;
use std::alloc::{Layout, dealloc};
#[test]
fn rsa() {
use rind::signature::RsaKeyPair;
let rsa = RsaKeyPair::from_pkcs1(include_bytes!("../src/high/rsa/rsa8192.der")).unwrap();
check_zeroed_on_drop(Box::pin(rsa.to_pkcs8()));
check_zeroed_on_drop(Box::pin(rsa));
}
#[test]
fn ecdsa() {
use rind::signature::{EcdsaKeyPair, sign};
for (algorithm, pkcs8) in [
(
&sign::ECDSA_P256_SHA256_ASN1,
&include_bytes!("../src/high/ecdsa/secp256r1.pkcs8.der")[..],
),
(
&sign::ECDSA_P384_SHA384_ASN1,
&include_bytes!("../src/high/ecdsa/secp384r1.pkcs8.der")[..],
),
(
&sign::ECDSA_P521_SHA512_ASN1,
&include_bytes!("../src/high/ecdsa/secp521r1.pkcs8.der")[..],
),
] {
let key = EcdsaKeyPair::from_pkcs8(algorithm, pkcs8).unwrap();
check_zeroed_on_drop(Box::pin(key.to_pkcs8()));
check_zeroed_on_drop(Box::pin(key.to_scalar()));
check_zeroed_on_drop(Box::pin(key));
}
}
#[test]
fn ed25519() {
use rind::signature::Ed25519KeyPair;
let ed25519 =
Ed25519KeyPair::from_pkcs8(include_bytes!("../src/high/asn1/testdata/ed25519-p8v2.bin"))
.unwrap();
check_zeroed_on_drop(Box::pin(ed25519.to_pkcs8()));
check_zeroed_on_drop(Box::pin(ed25519.to_seed()));
check_zeroed_on_drop(Box::pin(ed25519));
}
#[test]
fn ecdh() {
use rind::agreement::*;
for algorithm in [&X25519, &ECDH_P256, &ECDH_P384, &ECDH_P521] {
let key = PrivateKey::generate(algorithm).unwrap();
let peer = PrivateKey::generate(algorithm).unwrap().public_key();
check_zeroed_on_drop(Box::pin(
PrivateKey::generate(algorithm)
.unwrap()
.agree(peer.as_ref())
.unwrap(),
));
check_zeroed_on_drop(Box::pin(key));
}
}
#[test]
fn mlkem768() {
use rind::kem::*;
let key = DecapsulationKey::generate(&ML_KEM_768).unwrap();
let (ciphertext, secret) = key.public_key().encapsulate().unwrap();
check_zeroed_on_drop(Box::pin(secret));
let decapsulated = DecapsulationKey::generate(&ML_KEM_768)
.unwrap()
.decapsulate(ciphertext.as_ref())
.unwrap();
check_zeroed_on_drop(Box::pin(decapsulated));
check_zeroed_on_drop(Box::pin(key));
}
#[test]
fn digest() {
use rind::digest::*;
for algorithm in [&legacy::SHA1, &SHA256, &SHA384, &SHA512, &SHA512_256] {
let mut ctx = Context::new(algorithm);
ctx.update(&[0xffu8; 3]);
check_zeroed_on_drop(Box::pin(ctx));
}
}
#[test]
fn hkdf() {
use rind::hkdf::*;
let salt = Salt::new(&HKDF_SHA256, b"salt");
check_zeroed_on_drop(Box::pin(salt.extract(b"ikm")));
check_zeroed_on_drop(Box::pin(salt));
}
#[test]
fn hmac() {
use rind::hmac::*;
for algorithm in [&legacy::HMAC_SHA1, &HMAC_SHA256, &HMAC_SHA384, &HMAC_SHA512] {
let key = Key::new(algorithm, &[0xffu8; 32]);
let mut ctx = Context::with_key(&key);
ctx.update(&[0xffu8; 3]);
check_zeroed_on_drop(Box::pin(ctx));
check_zeroed_on_drop(Box::pin(key));
}
}
#[test]
fn pbkdf2() {
use rind::pbkdf2::*;
for algorithm in [
&legacy::PBKDF2_HMAC_SHA1,
&PBKDF2_HMAC_SHA256,
&PBKDF2_HMAC_SHA384,
&PBKDF2_HMAC_SHA512,
] {
check_zeroed_on_drop(Box::pin(Secret::new(algorithm, b"password")));
}
}
#[test]
fn aead() {
use rind::aead::{AES_128_GCM, AES_256_GCM, CHACHA20_POLY1305, Key, RandomizedNonceKey};
for algorithm in [&AES_128_GCM, &AES_256_GCM, &CHACHA20_POLY1305] {
let key = Key::new(algorithm, &[0xffu8; 32][..algorithm.key_len()]).unwrap();
check_zeroed_on_drop(Box::pin(key));
}
check_zeroed_on_drop(Box::pin(RandomizedNonceKey::new(&[0xffu8; 32])));
use rind::aead::quic::{AES_128, AES_256, CHACHA20, HeaderProtectionKey};
for algorithm in [&AES_128, &AES_256, &CHACHA20] {
let key = &[0xffu8; 32][..algorithm.key_len()];
check_zeroed_on_drop(Box::pin(HeaderProtectionKey::new(algorithm, key).unwrap()));
}
}
#[test]
fn cipher() {
use rind::cipher::{AES_128, AES_256, Aes, AesCtr, ChaCha20};
for (algorithm, len) in [(&AES_128, 16), (&AES_256, 32)] {
let key = &[0xffu8; 32][..len];
check_zeroed_on_drop(Box::pin(Aes::new(algorithm, key).unwrap()));
check_zeroed_on_drop(Box::pin(AesCtr::new(algorithm, key).unwrap()));
}
check_zeroed_on_drop(Box::pin(ChaCha20::new([0xffu8; 32])));
}
fn check_zeroed_on_drop<T: Unpin>(value: Pin<Box<T>>) {
let ptr = value.deref() as *const T as *const u8;
let len = size_of::<T>();
assert_ne!(len, 0);
println!("this value is {len} bytes in length");
let before_drop = read_into_vec(ptr, len);
let addr = Box::into_raw(unsafe { Pin::into_inner_unchecked(value) });
unsafe {
ptr::drop_in_place(addr);
}
let after_drop = read_into_vec(ptr, len);
unsafe {
dealloc(addr as *mut u8, Layout::new::<T>());
}
for i in HEAP_FREELIST_ZONE.0..len - HEAP_FREELIST_ZONE.1 {
if after_drop[i] != 0x00 {
println!("before_drop: {before_drop:02x?}");
println!("after_drop: {after_drop:02x?}");
panic!(
"byte {i} (0x{:x?}) was not cleared after drop",
after_drop[i]
);
}
}
}
fn read_into_vec(ptr: *const u8, len: usize) -> Vec<u8> {
let mut out = Vec::with_capacity(len);
for i in 0..len {
let byte = unsafe { ptr::read_volatile(ptr.add(i)) };
out.push(byte);
}
out
}
const HEAP_FREELIST_ZONE: (usize, usize) = (16, 0);