use rsa::pkcs8::DecodePrivateKey;
use rsa::{Pkcs1v15Sign, RsaPrivateKey, RsaPublicKey};
#[allow(unused_imports)]
use sha2::{Digest, Sha256, Sha384, Sha512};
use crate::error::{HsmError, HsmResult};
use crate::pkcs11_abi::constants::*;
use crate::pkcs11_abi::types::CK_MECHANISM_TYPE;
pub(crate) const fn rsa_private_ops_permitted() -> bool {
cfg!(any(
debug_assertions,
feature = "insecure-rustcrypto-rsa-private-ops"
))
}
pub(crate) fn require_rustcrypto_rsa_private_ops() -> HsmResult<()> {
#[cfg(any(debug_assertions, feature = "insecure-rustcrypto-rsa-private-ops"))]
{
Ok(())
}
#[cfg(not(any(debug_assertions, feature = "insecure-rustcrypto-rsa-private-ops")))]
{
tracing::error!(
"RustCrypto RSA private-key operation refused in release due to RUSTSEC-2023-0071; use a hardened backend"
);
Err(HsmError::MechanismInvalid)
}
}
use std::sync::{Arc, LazyLock};
use zeroize::Zeroize;
const RSA_KEY_CACHE_MAX: usize = 64;
static RSA_PRIV_CACHE: LazyLock<dashmap::DashMap<(u64, u64), Arc<RsaPrivateKey>>> =
LazyLock::new(|| dashmap::DashMap::with_capacity(RSA_KEY_CACHE_MAX));
static RSA_PUB_CACHE: LazyLock<dashmap::DashMap<(u64, u64), Arc<RsaPublicKey>>> =
LazyLock::new(|| dashmap::DashMap::with_capacity(RSA_KEY_CACHE_MAX));
fn evict_one_if_full<K: std::hash::Hash + Eq + Clone, V>(map: &dashmap::DashMap<K, V>) {
if map.len() >= RSA_KEY_CACHE_MAX {
if let Some(entry) = map.iter().next() {
let key = entry.key().clone();
drop(entry); map.remove(&key);
}
}
}
pub fn cache_rsa_private_key(slot_id: u64, handle: u64, key: Arc<RsaPrivateKey>) {
evict_one_if_full(&RSA_PRIV_CACHE);
RSA_PRIV_CACHE.insert((slot_id, handle), key);
}
pub fn get_cached_rsa_private_key(slot_id: u64, handle: u64) -> Option<Arc<RsaPrivateKey>> {
RSA_PRIV_CACHE
.get(&(slot_id, handle))
.map(|entry| Arc::clone(entry.value()))
}
pub fn cache_rsa_public_key(slot_id: u64, handle: u64, key: Arc<RsaPublicKey>) {
evict_one_if_full(&RSA_PUB_CACHE);
RSA_PUB_CACHE.insert((slot_id, handle), key);
}
pub fn get_cached_rsa_public_key(slot_id: u64, handle: u64) -> Option<Arc<RsaPublicKey>> {
RSA_PUB_CACHE
.get(&(slot_id, handle))
.map(|entry| Arc::clone(entry.value()))
}
pub fn evict_cached_keys(slot_id: u64, handle: u64) {
RSA_PRIV_CACHE.remove(&(slot_id, handle));
RSA_PUB_CACHE.remove(&(slot_id, handle));
}
pub fn clear_for_slot(slot_id: u64) {
RSA_PRIV_CACHE.retain(|&(s, _), _| s != slot_id);
RSA_PUB_CACHE.retain(|&(s, _), _| s != slot_id);
}
pub fn clear_all() {
RSA_PRIV_CACHE.clear();
RSA_PUB_CACHE.clear();
}
struct ZeroizingRsaKey {
key: RsaPrivateKey,
}
impl ZeroizingRsaKey {
fn new(key: RsaPrivateKey) -> Self {
Self { key }
}
}
impl Drop for ZeroizingRsaKey {
fn drop(&mut self) {
use rsa::pkcs8::EncodePrivateKey;
if let Ok(der) = self.key.to_pkcs8_der() {
let mut der_bytes = der.as_bytes().to_vec();
der_bytes.zeroize();
}
use rsa::BigUint;
if let Ok(dummy) = RsaPrivateKey::from_components(
BigUint::from(3u32), BigUint::from(3u32), BigUint::from(1u32), vec![BigUint::from(3u32)], ) {
self.key = dummy;
}
}
}
static RSA_KEY_CACHE: LazyLock<dashmap::DashMap<[u8; 32], ZeroizingRsaKey>> =
LazyLock::new(|| dashmap::DashMap::with_capacity(RSA_KEY_CACHE_MAX));
fn parse_rsa_private_key(der: &[u8]) -> HsmResult<RsaPrivateKey> {
let cache_key: [u8; 32] = Sha256::digest(der).into();
if let Some(entry) = RSA_KEY_CACHE.get(&cache_key) {
return Ok(entry.value().key.clone());
}
let private_key = RsaPrivateKey::from_pkcs8_der(der).map_err(|_| HsmError::KeyHandleInvalid)?;
evict_one_if_full_zeroizing(&RSA_KEY_CACHE);
RSA_KEY_CACHE.insert(cache_key, ZeroizingRsaKey::new(private_key.clone()));
Ok(private_key)
}
fn evict_one_if_full_zeroizing(map: &dashmap::DashMap<[u8; 32], ZeroizingRsaKey>) {
if map.len() >= RSA_KEY_CACHE_MAX {
if let Some(entry) = map.iter().next() {
let key = *entry.key();
drop(entry);
map.remove(&key);
}
}
}
pub fn clear_rsa_key_cache() {
RSA_KEY_CACHE.clear();
RSA_PRIV_CACHE.clear();
RSA_PUB_CACHE.clear();
}
const RSA_MIN_MODULUS_BITS: usize = 2048;
const SIGN_MAX_DATA_SIZE: usize = 64 * 1024 * 1024;
fn validate_data_size(data: &[u8]) -> HsmResult<()> {
if data.len() > SIGN_MAX_DATA_SIZE {
tracing::warn!(
"Sign/verify data size {} exceeds maximum {} bytes — rejecting to prevent DoS",
data.len(),
SIGN_MAX_DATA_SIZE
);
return Err(HsmError::DataLenRange);
}
Ok(())
}
fn validate_digest_length(digest: &[u8], hash_alg: HashAlg) -> HsmResult<()> {
let expected_len = match hash_alg {
HashAlg::Sha256 => 32,
HashAlg::Sha384 => 48,
HashAlg::Sha512 => 64,
};
if digest.len() != expected_len {
tracing::error!(
"Prehash digest length mismatch: got {} bytes, expected {} for {:?}",
digest.len(),
expected_len,
hash_alg
);
return Err(HsmError::DataLenRange);
}
Ok(())
}
fn validate_rsa_private_key_size(private_key: &RsaPrivateKey) -> HsmResult<()> {
use rsa::traits::PublicKeyParts;
let modulus_bits = private_key.n().bits();
if modulus_bits < RSA_MIN_MODULUS_BITS {
tracing::warn!(
"Rejecting RSA operation: key size {} bits is below minimum {} bits",
modulus_bits,
RSA_MIN_MODULUS_BITS
);
return Err(HsmError::KeySizeRange);
}
Ok(())
}
fn validate_rsa_public_key_size(modulus: &[u8]) -> HsmResult<()> {
let significant = modulus
.iter()
.position(|&b| b != 0)
.unwrap_or(modulus.len());
let significant_bytes = &modulus[significant..];
let modulus_bits = if significant_bytes.is_empty() {
0
} else {
(significant_bytes.len() - 1) * 8 + (8 - significant_bytes[0].leading_zeros() as usize)
};
if modulus_bits < RSA_MIN_MODULUS_BITS {
tracing::warn!(
"Rejecting RSA operation: key size {} bits is below minimum {} bits",
modulus_bits,
RSA_MIN_MODULUS_BITS
);
return Err(HsmError::KeySizeRange);
}
Ok(())
}
#[derive(Debug, Clone, Copy)]
pub enum HashAlg {
Sha256,
Sha384,
Sha512,
}
pub fn rsa_pkcs1v15_sign(
private_key_der: &[u8],
data: &[u8],
hash_alg: Option<HashAlg>,
) -> HsmResult<Vec<u8>> {
require_rustcrypto_rsa_private_ops()?;
use rsa::signature::SignatureEncoding;
validate_data_size(data)?;
let private_key = parse_rsa_private_key(private_key_der)?;
validate_rsa_private_key_size(&private_key)?;
match hash_alg {
Some(HashAlg::Sha256) => {
use rsa::pkcs1v15::SigningKey;
use rsa::signature::Signer;
let signing_key = SigningKey::<Sha256>::new(private_key);
let signature = signing_key.sign(data);
Ok(signature.to_vec())
}
Some(HashAlg::Sha384) => {
use rsa::pkcs1v15::SigningKey;
use rsa::signature::Signer;
let signing_key = SigningKey::<Sha384>::new(private_key);
let signature = signing_key.sign(data);
Ok(signature.to_vec())
}
Some(HashAlg::Sha512) => {
use rsa::pkcs1v15::SigningKey;
use rsa::signature::Signer;
let signing_key = SigningKey::<Sha512>::new(private_key);
let signature = signing_key.sign(data);
Ok(signature.to_vec())
}
None => {
Err(HsmError::MechanismParamInvalid)
}
}
}
pub fn rsa_pkcs1v15_verify(
modulus: &[u8],
public_exponent: &[u8],
data: &[u8],
signature: &[u8],
hash_alg: Option<HashAlg>,
) -> HsmResult<bool> {
validate_data_size(data)?;
validate_rsa_public_key_size(modulus)?;
let n = rsa::BigUint::from_bytes_be(modulus);
let e = rsa::BigUint::from_bytes_be(public_exponent);
let public_key = RsaPublicKey::new(n, e).map_err(|_| HsmError::KeyHandleInvalid)?;
match hash_alg {
Some(HashAlg::Sha256) => {
use rsa::pkcs1v15::VerifyingKey;
use rsa::signature::Verifier;
let verifying_key = VerifyingKey::<Sha256>::new(public_key);
let sig = rsa::pkcs1v15::Signature::try_from(signature)
.map_err(|_| HsmError::SignatureInvalid)?;
Ok(verifying_key.verify(data, &sig).is_ok())
}
Some(HashAlg::Sha384) => {
use rsa::pkcs1v15::VerifyingKey;
use rsa::signature::Verifier;
let verifying_key = VerifyingKey::<Sha384>::new(public_key);
let sig = rsa::pkcs1v15::Signature::try_from(signature)
.map_err(|_| HsmError::SignatureInvalid)?;
Ok(verifying_key.verify(data, &sig).is_ok())
}
Some(HashAlg::Sha512) => {
use rsa::pkcs1v15::VerifyingKey;
use rsa::signature::Verifier;
let verifying_key = VerifyingKey::<Sha512>::new(public_key);
let sig = rsa::pkcs1v15::Signature::try_from(signature)
.map_err(|_| HsmError::SignatureInvalid)?;
Ok(verifying_key.verify(data, &sig).is_ok())
}
None => {
Err(HsmError::MechanismParamInvalid)
}
}
}
pub fn rsa_pss_sign(private_key_der: &[u8], data: &[u8], hash_alg: HashAlg) -> HsmResult<Vec<u8>> {
require_rustcrypto_rsa_private_ops()?;
use crate::crypto::drbg::DrbgRng;
use rsa::pss::SigningKey;
use rsa::signature::{RandomizedSigner, SignatureEncoding};
validate_data_size(data)?;
let private_key = parse_rsa_private_key(private_key_der)?;
validate_rsa_private_key_size(&private_key)?;
let mut rng = DrbgRng::new()?;
match hash_alg {
HashAlg::Sha256 => {
let signing_key = SigningKey::<Sha256>::new(private_key);
let signature = signing_key.sign_with_rng(&mut rng, data);
Ok(signature.to_vec())
}
HashAlg::Sha384 => {
let signing_key = SigningKey::<Sha384>::new(private_key);
let signature = signing_key.sign_with_rng(&mut rng, data);
Ok(signature.to_vec())
}
HashAlg::Sha512 => {
let signing_key = SigningKey::<Sha512>::new(private_key);
let signature = signing_key.sign_with_rng(&mut rng, data);
Ok(signature.to_vec())
}
}
}
pub fn rsa_pss_verify(
modulus: &[u8],
public_exponent: &[u8],
data: &[u8],
signature: &[u8],
hash_alg: HashAlg,
) -> HsmResult<bool> {
use rsa::pss::VerifyingKey;
use rsa::signature::Verifier;
validate_data_size(data)?;
validate_rsa_public_key_size(modulus)?;
let n = rsa::BigUint::from_bytes_be(modulus);
let e = rsa::BigUint::from_bytes_be(public_exponent);
let public_key = RsaPublicKey::new(n, e).map_err(|_| HsmError::KeyHandleInvalid)?;
match hash_alg {
HashAlg::Sha256 => {
let verifying_key = VerifyingKey::<Sha256>::new(public_key);
let sig =
rsa::pss::Signature::try_from(signature).map_err(|_| HsmError::SignatureInvalid)?;
Ok(verifying_key.verify(data, &sig).is_ok())
}
HashAlg::Sha384 => {
let verifying_key = VerifyingKey::<Sha384>::new(public_key);
let sig =
rsa::pss::Signature::try_from(signature).map_err(|_| HsmError::SignatureInvalid)?;
Ok(verifying_key.verify(data, &sig).is_ok())
}
HashAlg::Sha512 => {
let verifying_key = VerifyingKey::<Sha512>::new(public_key);
let sig =
rsa::pss::Signature::try_from(signature).map_err(|_| HsmError::SignatureInvalid)?;
Ok(verifying_key.verify(data, &sig).is_ok())
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OaepHash {
Sha256,
Sha384,
Sha512,
}
fn oaep_padding(hash_alg: OaepHash, label: &[u8]) -> HsmResult<rsa::Oaep> {
use rsa::Oaep;
let label_str = if label.is_empty() {
None
} else {
Some(
std::str::from_utf8(label)
.map_err(|_| HsmError::MechanismParamInvalid)?
.to_owned(),
)
};
Ok(match (hash_alg, label_str) {
(OaepHash::Sha256, None) => Oaep::new::<Sha256>(),
(OaepHash::Sha384, None) => Oaep::new::<Sha384>(),
(OaepHash::Sha512, None) => Oaep::new::<Sha512>(),
(OaepHash::Sha256, Some(l)) => Oaep::new_with_label::<Sha256, _>(l),
(OaepHash::Sha384, Some(l)) => Oaep::new_with_label::<Sha384, _>(l),
(OaepHash::Sha512, Some(l)) => Oaep::new_with_label::<Sha512, _>(l),
})
}
pub fn rsa_oaep_encrypt(
modulus: &[u8],
public_exponent: &[u8],
plaintext: &[u8],
hash_alg: OaepHash,
label: &[u8],
) -> HsmResult<Vec<u8>> {
use crate::crypto::drbg::DrbgRng;
let n = rsa::BigUint::from_bytes_be(modulus);
let e = rsa::BigUint::from_bytes_be(public_exponent);
let public_key = RsaPublicKey::new(n, e).map_err(|_| HsmError::KeyHandleInvalid)?;
let mut rng = DrbgRng::new()?;
let padding = oaep_padding(hash_alg, label)?;
public_key
.encrypt(&mut rng, padding, plaintext)
.map_err(|_| HsmError::GeneralError)
}
pub fn rsa_oaep_decrypt(
private_key_der: &[u8],
ciphertext: &[u8],
hash_alg: OaepHash,
label: &[u8],
) -> HsmResult<Vec<u8>> {
require_rustcrypto_rsa_private_ops()?;
let private_key = parse_rsa_private_key(private_key_der)?;
validate_rsa_private_key_size(&private_key)?;
let padding = oaep_padding(hash_alg, label)?;
private_key
.decrypt(padding, ciphertext)
.map_err(|_| HsmError::EncryptedDataInvalid)
}
pub fn rsa_pkcs1v15_sign_cached(
slot_id: u64,
handle: u64,
private_key_der: &[u8],
data: &[u8],
hash_alg: Option<HashAlg>,
) -> HsmResult<Vec<u8>> {
require_rustcrypto_rsa_private_ops()?;
use rsa::signature::SignatureEncoding;
validate_data_size(data)?;
let key = get_or_parse_private_key(slot_id, handle, private_key_der)?;
validate_rsa_private_key_size(&key)?;
match hash_alg {
Some(HashAlg::Sha256) => {
use rsa::pkcs1v15::SigningKey;
use rsa::signature::Signer;
let signing_key = SigningKey::<Sha256>::new((*key).clone());
let signature = signing_key.sign(data);
Ok(signature.to_vec())
}
Some(HashAlg::Sha384) => {
use rsa::pkcs1v15::SigningKey;
use rsa::signature::Signer;
let signing_key = SigningKey::<Sha384>::new((*key).clone());
let signature = signing_key.sign(data);
Ok(signature.to_vec())
}
Some(HashAlg::Sha512) => {
use rsa::pkcs1v15::SigningKey;
use rsa::signature::Signer;
let signing_key = SigningKey::<Sha512>::new((*key).clone());
let signature = signing_key.sign(data);
Ok(signature.to_vec())
}
None => Err(HsmError::MechanismParamInvalid),
}
}
pub fn rsa_pkcs1v15_verify_cached(
slot_id: u64,
handle: u64,
modulus: &[u8],
public_exponent: &[u8],
data: &[u8],
signature: &[u8],
hash_alg: Option<HashAlg>,
) -> HsmResult<bool> {
validate_data_size(data)?;
validate_rsa_public_key_size(modulus)?;
let key = get_or_build_public_key(slot_id, handle, modulus, public_exponent)?;
match hash_alg {
Some(HashAlg::Sha256) => {
use rsa::pkcs1v15::VerifyingKey;
use rsa::signature::Verifier;
let verifying_key = VerifyingKey::<Sha256>::new((*key).clone());
let sig = rsa::pkcs1v15::Signature::try_from(signature)
.map_err(|_| HsmError::SignatureInvalid)?;
Ok(verifying_key.verify(data, &sig).is_ok())
}
Some(HashAlg::Sha384) => {
use rsa::pkcs1v15::VerifyingKey;
use rsa::signature::Verifier;
let verifying_key = VerifyingKey::<Sha384>::new((*key).clone());
let sig = rsa::pkcs1v15::Signature::try_from(signature)
.map_err(|_| HsmError::SignatureInvalid)?;
Ok(verifying_key.verify(data, &sig).is_ok())
}
Some(HashAlg::Sha512) => {
use rsa::pkcs1v15::VerifyingKey;
use rsa::signature::Verifier;
let verifying_key = VerifyingKey::<Sha512>::new((*key).clone());
let sig = rsa::pkcs1v15::Signature::try_from(signature)
.map_err(|_| HsmError::SignatureInvalid)?;
Ok(verifying_key.verify(data, &sig).is_ok())
}
None => Err(HsmError::MechanismParamInvalid),
}
}
pub fn rsa_pss_sign_cached(
slot_id: u64,
handle: u64,
private_key_der: &[u8],
data: &[u8],
hash_alg: HashAlg,
) -> HsmResult<Vec<u8>> {
require_rustcrypto_rsa_private_ops()?;
use crate::crypto::drbg::DrbgRng;
use rsa::pss::SigningKey;
use rsa::signature::{RandomizedSigner, SignatureEncoding};
validate_data_size(data)?;
let key = get_or_parse_private_key(slot_id, handle, private_key_der)?;
validate_rsa_private_key_size(&key)?;
let mut rng = DrbgRng::new()?;
match hash_alg {
HashAlg::Sha256 => {
let signing_key = SigningKey::<Sha256>::new((*key).clone());
let signature = signing_key.sign_with_rng(&mut rng, data);
Ok(signature.to_vec())
}
HashAlg::Sha384 => {
let signing_key = SigningKey::<Sha384>::new((*key).clone());
let signature = signing_key.sign_with_rng(&mut rng, data);
Ok(signature.to_vec())
}
HashAlg::Sha512 => {
let signing_key = SigningKey::<Sha512>::new((*key).clone());
let signature = signing_key.sign_with_rng(&mut rng, data);
Ok(signature.to_vec())
}
}
}
pub fn rsa_pss_verify_cached(
slot_id: u64,
handle: u64,
modulus: &[u8],
public_exponent: &[u8],
data: &[u8],
signature: &[u8],
hash_alg: HashAlg,
) -> HsmResult<bool> {
use rsa::pss::VerifyingKey;
use rsa::signature::Verifier;
validate_data_size(data)?;
validate_rsa_public_key_size(modulus)?;
let key = get_or_build_public_key(slot_id, handle, modulus, public_exponent)?;
match hash_alg {
HashAlg::Sha256 => {
let verifying_key = VerifyingKey::<Sha256>::new((*key).clone());
let sig =
rsa::pss::Signature::try_from(signature).map_err(|_| HsmError::SignatureInvalid)?;
Ok(verifying_key.verify(data, &sig).is_ok())
}
HashAlg::Sha384 => {
let verifying_key = VerifyingKey::<Sha384>::new((*key).clone());
let sig =
rsa::pss::Signature::try_from(signature).map_err(|_| HsmError::SignatureInvalid)?;
Ok(verifying_key.verify(data, &sig).is_ok())
}
HashAlg::Sha512 => {
let verifying_key = VerifyingKey::<Sha512>::new((*key).clone());
let sig =
rsa::pss::Signature::try_from(signature).map_err(|_| HsmError::SignatureInvalid)?;
Ok(verifying_key.verify(data, &sig).is_ok())
}
}
}
pub fn rsa_oaep_encrypt_cached(
slot_id: u64,
handle: u64,
modulus: &[u8],
public_exponent: &[u8],
plaintext: &[u8],
hash_alg: OaepHash,
label: &[u8],
) -> HsmResult<Vec<u8>> {
use crate::crypto::drbg::DrbgRng;
validate_rsa_public_key_size(modulus)?;
let key = get_or_build_public_key(slot_id, handle, modulus, public_exponent)?;
let mut rng = DrbgRng::new()?;
let padding = oaep_padding(hash_alg, label)?;
key.encrypt(&mut rng, padding, plaintext)
.map_err(|_| HsmError::GeneralError)
}
pub fn rsa_oaep_decrypt_cached(
slot_id: u64,
handle: u64,
private_key_der: &[u8],
ciphertext: &[u8],
hash_alg: OaepHash,
label: &[u8],
) -> HsmResult<Vec<u8>> {
require_rustcrypto_rsa_private_ops()?;
let key = get_or_parse_private_key(slot_id, handle, private_key_der)?;
validate_rsa_private_key_size(&key)?;
let padding = oaep_padding(hash_alg, label)?;
key.decrypt(padding, ciphertext)
.map_err(|_| HsmError::EncryptedDataInvalid)
}
fn get_or_parse_private_key(
slot_id: u64,
handle: u64,
der: &[u8],
) -> HsmResult<Arc<RsaPrivateKey>> {
if let Some(k) = get_cached_rsa_private_key(slot_id, handle) {
return Ok(k);
}
let k = Arc::new(RsaPrivateKey::from_pkcs8_der(der).map_err(|_| HsmError::KeyHandleInvalid)?);
cache_rsa_private_key(slot_id, handle, Arc::clone(&k));
Ok(k)
}
fn get_or_build_public_key(
slot_id: u64,
handle: u64,
modulus: &[u8],
public_exponent: &[u8],
) -> HsmResult<Arc<RsaPublicKey>> {
if let Some(k) = get_cached_rsa_public_key(slot_id, handle) {
return Ok(k);
}
let n = rsa::BigUint::from_bytes_be(modulus);
let e = rsa::BigUint::from_bytes_be(public_exponent);
let k = Arc::new(RsaPublicKey::new(n, e).map_err(|_| HsmError::KeyHandleInvalid)?);
cache_rsa_public_key(slot_id, handle, Arc::clone(&k));
Ok(k)
}
pub fn ecdsa_p256_sign(private_key_bytes: &[u8], data: &[u8]) -> HsmResult<Vec<u8>> {
use crate::crypto::drbg::DrbgRng;
use p256::ecdsa::signature::RandomizedSigner;
use p256::ecdsa::SigningKey;
validate_data_size(data)?;
let signing_key =
SigningKey::from_slice(private_key_bytes).map_err(|_| HsmError::KeyHandleInvalid)?;
let mut rng = DrbgRng::new()?;
let signature: p256::ecdsa::Signature = signing_key.sign_with_rng(&mut rng, data);
Ok(signature.to_bytes().to_vec())
}
pub fn ecdsa_p256_verify(public_key_sec1: &[u8], data: &[u8], signature: &[u8]) -> HsmResult<bool> {
use p256::ecdsa::signature::Verifier;
use p256::ecdsa::VerifyingKey;
validate_data_size(data)?;
if signature.len() != 64 {
return Err(HsmError::SignatureInvalid);
}
let verifying_key =
VerifyingKey::from_sec1_bytes(public_key_sec1).map_err(|_| HsmError::KeyHandleInvalid)?;
let sig =
p256::ecdsa::Signature::from_slice(signature).map_err(|_| HsmError::SignatureInvalid)?;
Ok(verifying_key.verify(data, &sig).is_ok())
}
pub fn ecdsa_p384_sign(private_key_bytes: &[u8], data: &[u8]) -> HsmResult<Vec<u8>> {
use crate::crypto::drbg::DrbgRng;
use p384::ecdsa::signature::RandomizedSigner;
use p384::ecdsa::SigningKey;
validate_data_size(data)?;
let signing_key =
SigningKey::from_slice(private_key_bytes).map_err(|_| HsmError::KeyHandleInvalid)?;
let mut rng = DrbgRng::new()?;
let signature: p384::ecdsa::Signature = signing_key.sign_with_rng(&mut rng, data);
Ok(signature.to_bytes().to_vec())
}
pub fn ecdsa_p384_verify(public_key_sec1: &[u8], data: &[u8], signature: &[u8]) -> HsmResult<bool> {
use p384::ecdsa::signature::Verifier;
use p384::ecdsa::VerifyingKey;
validate_data_size(data)?;
if signature.len() != 96 {
return Err(HsmError::SignatureInvalid);
}
let verifying_key =
VerifyingKey::from_sec1_bytes(public_key_sec1).map_err(|_| HsmError::KeyHandleInvalid)?;
let sig =
p384::ecdsa::Signature::from_slice(signature).map_err(|_| HsmError::SignatureInvalid)?;
Ok(verifying_key.verify(data, &sig).is_ok())
}
pub fn ed25519_sign(private_key_bytes: &[u8], data: &[u8]) -> HsmResult<Vec<u8>> {
use ed25519_dalek::Signer;
use ed25519_dalek::SigningKey;
use zeroize::Zeroizing;
validate_data_size(data)?;
if private_key_bytes.len() != 32 {
return Err(HsmError::KeyHandleInvalid);
}
let mut key_array = Zeroizing::new([0u8; 32]);
key_array.copy_from_slice(private_key_bytes);
let signing_key = SigningKey::from_bytes(&key_array);
let signature = signing_key.sign(data);
Ok(signature.to_bytes().to_vec())
}
pub fn ed25519_verify(
public_key_bytes: &[u8],
data: &[u8],
signature_bytes: &[u8],
) -> HsmResult<bool> {
use ed25519_dalek::{Signature, Verifier, VerifyingKey};
validate_data_size(data)?;
if public_key_bytes.len() != 32 {
return Err(HsmError::KeyHandleInvalid);
}
let key_array: [u8; 32] = public_key_bytes
.try_into()
.map_err(|_| HsmError::KeyHandleInvalid)?;
let verifying_key =
VerifyingKey::from_bytes(&key_array).map_err(|_| HsmError::KeyHandleInvalid)?;
if signature_bytes.len() != 64 {
return Err(HsmError::SignatureInvalid);
}
let sig_array: [u8; 64] = signature_bytes
.try_into()
.map_err(|_| HsmError::SignatureInvalid)?;
let signature = Signature::from_bytes(&sig_array);
Ok(verifying_key.verify(data, &signature).is_ok())
}
pub fn mechanism_to_hash(mechanism: CK_MECHANISM_TYPE) -> Option<HashAlg> {
match mechanism {
CKM_SHA256_RSA_PKCS => Some(HashAlg::Sha256),
CKM_SHA384_RSA_PKCS => Some(HashAlg::Sha384),
CKM_SHA512_RSA_PKCS => Some(HashAlg::Sha512),
CKM_RSA_PKCS => None,
_ => None,
}
}
pub fn pss_mechanism_to_hash(mechanism: CK_MECHANISM_TYPE) -> HsmResult<HashAlg> {
match mechanism {
CKM_SHA256_RSA_PKCS_PSS => Ok(HashAlg::Sha256),
CKM_SHA384_RSA_PKCS_PSS => Ok(HashAlg::Sha384),
CKM_SHA512_RSA_PKCS_PSS => Ok(HashAlg::Sha512),
CKM_RSA_PKCS_PSS => Err(HsmError::MechanismParamInvalid), _ => Err(HsmError::MechanismInvalid),
}
}
pub fn is_pss_mechanism(mechanism: CK_MECHANISM_TYPE) -> bool {
matches!(
mechanism,
CKM_RSA_PKCS_PSS
| CKM_SHA256_RSA_PKCS_PSS
| CKM_SHA384_RSA_PKCS_PSS
| CKM_SHA512_RSA_PKCS_PSS
)
}
pub fn is_ecdsa_mechanism(mechanism: CK_MECHANISM_TYPE) -> bool {
matches!(
mechanism,
CKM_ECDSA | CKM_ECDSA_SHA256 | CKM_ECDSA_SHA384 | CKM_ECDSA_SHA512
)
}
pub fn ecdsa_mechanism_to_hash(mechanism: CK_MECHANISM_TYPE) -> HsmResult<Option<HashAlg>> {
match mechanism {
CKM_ECDSA => Ok(None),
CKM_ECDSA_SHA256 => Ok(Some(HashAlg::Sha256)),
CKM_ECDSA_SHA384 => Ok(Some(HashAlg::Sha384)),
CKM_ECDSA_SHA512 => Ok(Some(HashAlg::Sha512)),
_ => Err(HsmError::MechanismInvalid),
}
}
pub fn is_eddsa_mechanism(mechanism: CK_MECHANISM_TYPE) -> bool {
mechanism == CKM_EDDSA
}
pub fn sign_mechanism_supports_multipart(mechanism: CK_MECHANISM_TYPE) -> bool {
matches!(
mechanism,
CKM_SHA256_RSA_PKCS
| CKM_SHA384_RSA_PKCS
| CKM_SHA512_RSA_PKCS
| CKM_SHA256_RSA_PKCS_PSS
| CKM_SHA384_RSA_PKCS_PSS
| CKM_SHA512_RSA_PKCS_PSS
| CKM_ECDSA_SHA256
| CKM_ECDSA_SHA384
| CKM_ECDSA_SHA512
)
}
pub fn sign_mechanism_to_digest_mechanism(
mechanism: CK_MECHANISM_TYPE,
) -> Option<CK_MECHANISM_TYPE> {
match mechanism {
CKM_SHA256_RSA_PKCS | CKM_SHA256_RSA_PKCS_PSS | CKM_ECDSA_SHA256 => Some(CKM_SHA256),
CKM_SHA384_RSA_PKCS | CKM_SHA384_RSA_PKCS_PSS | CKM_ECDSA_SHA384 => Some(CKM_SHA384),
CKM_SHA512_RSA_PKCS | CKM_SHA512_RSA_PKCS_PSS | CKM_ECDSA_SHA512 => Some(CKM_SHA512),
_ => None,
}
}
pub(crate) fn rsa_pkcs1v15_sign_prehashed(
private_key_der: &[u8],
digest: &[u8],
hash_alg: HashAlg,
) -> HsmResult<Vec<u8>> {
require_rustcrypto_rsa_private_ops()?;
validate_digest_length(digest, hash_alg)?;
let private_key = parse_rsa_private_key(private_key_der)?;
validate_rsa_private_key_size(&private_key)?;
let scheme = match hash_alg {
HashAlg::Sha256 => Pkcs1v15Sign::new::<Sha256>(),
HashAlg::Sha384 => Pkcs1v15Sign::new::<Sha384>(),
HashAlg::Sha512 => Pkcs1v15Sign::new::<Sha512>(),
};
private_key
.sign(scheme, digest)
.map_err(|_| HsmError::GeneralError)
}
pub(crate) fn rsa_pkcs1v15_verify_prehashed(
modulus: &[u8],
public_exponent: &[u8],
digest: &[u8],
signature: &[u8],
hash_alg: HashAlg,
) -> HsmResult<bool> {
validate_digest_length(digest, hash_alg)?;
validate_rsa_public_key_size(modulus)?;
let n = rsa::BigUint::from_bytes_be(modulus);
let e = rsa::BigUint::from_bytes_be(public_exponent);
let public_key = RsaPublicKey::new(n, e).map_err(|_| HsmError::KeyHandleInvalid)?;
let scheme = match hash_alg {
HashAlg::Sha256 => Pkcs1v15Sign::new::<Sha256>(),
HashAlg::Sha384 => Pkcs1v15Sign::new::<Sha384>(),
HashAlg::Sha512 => Pkcs1v15Sign::new::<Sha512>(),
};
Ok(public_key.verify(scheme, digest, signature).is_ok())
}
pub(crate) fn rsa_pss_sign_prehashed(
private_key_der: &[u8],
digest: &[u8],
hash_alg: HashAlg,
) -> HsmResult<Vec<u8>> {
require_rustcrypto_rsa_private_ops()?;
use crate::crypto::drbg::DrbgRng;
use rsa::pss::SigningKey;
use rsa::signature::hazmat::RandomizedPrehashSigner;
use rsa::signature::SignatureEncoding;
validate_digest_length(digest, hash_alg)?;
let private_key = parse_rsa_private_key(private_key_der)?;
validate_rsa_private_key_size(&private_key)?;
let mut rng = DrbgRng::new()?;
match hash_alg {
HashAlg::Sha256 => {
let signing_key = SigningKey::<Sha256>::new(private_key);
let signature = signing_key
.sign_prehash_with_rng(&mut rng, digest)
.map_err(|_| HsmError::GeneralError)?;
Ok(signature.to_vec())
}
HashAlg::Sha384 => {
let signing_key = SigningKey::<Sha384>::new(private_key);
let signature = signing_key
.sign_prehash_with_rng(&mut rng, digest)
.map_err(|_| HsmError::GeneralError)?;
Ok(signature.to_vec())
}
HashAlg::Sha512 => {
let signing_key = SigningKey::<Sha512>::new(private_key);
let signature = signing_key
.sign_prehash_with_rng(&mut rng, digest)
.map_err(|_| HsmError::GeneralError)?;
Ok(signature.to_vec())
}
}
}
pub(crate) fn rsa_pss_verify_prehashed(
modulus: &[u8],
public_exponent: &[u8],
digest: &[u8],
signature: &[u8],
hash_alg: HashAlg,
) -> HsmResult<bool> {
use rsa::pss::VerifyingKey;
use rsa::signature::hazmat::PrehashVerifier;
validate_digest_length(digest, hash_alg)?;
validate_rsa_public_key_size(modulus)?;
let n = rsa::BigUint::from_bytes_be(modulus);
let e = rsa::BigUint::from_bytes_be(public_exponent);
let public_key = RsaPublicKey::new(n, e).map_err(|_| HsmError::KeyHandleInvalid)?;
let sig = rsa::pss::Signature::try_from(signature).map_err(|_| HsmError::SignatureInvalid)?;
match hash_alg {
HashAlg::Sha256 => {
let verifying_key = VerifyingKey::<Sha256>::new(public_key);
Ok(verifying_key.verify_prehash(digest, &sig).is_ok())
}
HashAlg::Sha384 => {
let verifying_key = VerifyingKey::<Sha384>::new(public_key);
Ok(verifying_key.verify_prehash(digest, &sig).is_ok())
}
HashAlg::Sha512 => {
let verifying_key = VerifyingKey::<Sha512>::new(public_key);
Ok(verifying_key.verify_prehash(digest, &sig).is_ok())
}
}
}
pub(crate) fn rsa_pkcs1v15_sign_prehashed_cached(
slot_id: u64,
handle: u64,
private_key_der: &[u8],
digest: &[u8],
hash_alg: HashAlg,
) -> HsmResult<Vec<u8>> {
require_rustcrypto_rsa_private_ops()?;
validate_digest_length(digest, hash_alg)?;
let key = get_or_parse_private_key(slot_id, handle, private_key_der)?;
validate_rsa_private_key_size(&key)?;
let scheme = match hash_alg {
HashAlg::Sha256 => Pkcs1v15Sign::new::<Sha256>(),
HashAlg::Sha384 => Pkcs1v15Sign::new::<Sha384>(),
HashAlg::Sha512 => Pkcs1v15Sign::new::<Sha512>(),
};
key.sign(scheme, digest).map_err(|_| HsmError::GeneralError)
}
pub(crate) fn rsa_pkcs1v15_verify_prehashed_cached(
slot_id: u64,
handle: u64,
modulus: &[u8],
public_exponent: &[u8],
digest: &[u8],
signature: &[u8],
hash_alg: HashAlg,
) -> HsmResult<bool> {
validate_digest_length(digest, hash_alg)?;
validate_rsa_public_key_size(modulus)?;
let key = get_or_build_public_key(slot_id, handle, modulus, public_exponent)?;
let scheme = match hash_alg {
HashAlg::Sha256 => Pkcs1v15Sign::new::<Sha256>(),
HashAlg::Sha384 => Pkcs1v15Sign::new::<Sha384>(),
HashAlg::Sha512 => Pkcs1v15Sign::new::<Sha512>(),
};
Ok(key.verify(scheme, digest, signature).is_ok())
}
pub(crate) fn rsa_pss_sign_prehashed_cached(
slot_id: u64,
handle: u64,
private_key_der: &[u8],
digest: &[u8],
hash_alg: HashAlg,
) -> HsmResult<Vec<u8>> {
require_rustcrypto_rsa_private_ops()?;
use crate::crypto::drbg::DrbgRng;
use rsa::pss::SigningKey;
use rsa::signature::hazmat::RandomizedPrehashSigner;
use rsa::signature::SignatureEncoding;
validate_digest_length(digest, hash_alg)?;
let key = get_or_parse_private_key(slot_id, handle, private_key_der)?;
validate_rsa_private_key_size(&key)?;
let mut rng = DrbgRng::new()?;
match hash_alg {
HashAlg::Sha256 => {
let signing_key = SigningKey::<Sha256>::new((*key).clone());
let signature = signing_key
.sign_prehash_with_rng(&mut rng, digest)
.map_err(|_| HsmError::GeneralError)?;
Ok(signature.to_vec())
}
HashAlg::Sha384 => {
let signing_key = SigningKey::<Sha384>::new((*key).clone());
let signature = signing_key
.sign_prehash_with_rng(&mut rng, digest)
.map_err(|_| HsmError::GeneralError)?;
Ok(signature.to_vec())
}
HashAlg::Sha512 => {
let signing_key = SigningKey::<Sha512>::new((*key).clone());
let signature = signing_key
.sign_prehash_with_rng(&mut rng, digest)
.map_err(|_| HsmError::GeneralError)?;
Ok(signature.to_vec())
}
}
}
pub(crate) fn rsa_pss_verify_prehashed_cached(
slot_id: u64,
handle: u64,
modulus: &[u8],
public_exponent: &[u8],
digest: &[u8],
signature: &[u8],
hash_alg: HashAlg,
) -> HsmResult<bool> {
use rsa::pss::VerifyingKey;
use rsa::signature::hazmat::PrehashVerifier;
validate_digest_length(digest, hash_alg)?;
validate_rsa_public_key_size(modulus)?;
let key = get_or_build_public_key(slot_id, handle, modulus, public_exponent)?;
let sig = rsa::pss::Signature::try_from(signature).map_err(|_| HsmError::SignatureInvalid)?;
match hash_alg {
HashAlg::Sha256 => {
let verifying_key = VerifyingKey::<Sha256>::new((*key).clone());
Ok(verifying_key.verify_prehash(digest, &sig).is_ok())
}
HashAlg::Sha384 => {
let verifying_key = VerifyingKey::<Sha384>::new((*key).clone());
Ok(verifying_key.verify_prehash(digest, &sig).is_ok())
}
HashAlg::Sha512 => {
let verifying_key = VerifyingKey::<Sha512>::new((*key).clone());
Ok(verifying_key.verify_prehash(digest, &sig).is_ok())
}
}
}
pub(crate) fn ecdsa_p256_sign_prehashed(
private_key_bytes: &[u8],
digest: &[u8],
) -> HsmResult<Vec<u8>> {
use crate::crypto::drbg::DrbgRng;
use p256::ecdsa::signature::hazmat::RandomizedPrehashSigner;
use p256::ecdsa::SigningKey;
let signing_key =
SigningKey::from_slice(private_key_bytes).map_err(|_| HsmError::KeyHandleInvalid)?;
let mut rng = DrbgRng::new()?;
let signature: p256::ecdsa::Signature = signing_key
.sign_prehash_with_rng(&mut rng, digest)
.map_err(|_| HsmError::DataLenRange)?;
Ok(signature.to_bytes().to_vec())
}
pub(crate) fn ecdsa_p256_verify_prehashed(
public_key_sec1: &[u8],
digest: &[u8],
signature: &[u8],
) -> HsmResult<bool> {
use p256::ecdsa::signature::hazmat::PrehashVerifier;
use p256::ecdsa::VerifyingKey;
if digest.len() < 16 {
return Err(HsmError::DataLenRange);
}
if signature.len() != 64 {
return Err(HsmError::SignatureInvalid);
}
let verifying_key =
VerifyingKey::from_sec1_bytes(public_key_sec1).map_err(|_| HsmError::KeyHandleInvalid)?;
let sig =
p256::ecdsa::Signature::from_slice(signature).map_err(|_| HsmError::SignatureInvalid)?;
Ok(verifying_key.verify_prehash(digest, &sig).is_ok())
}
pub(crate) fn ecdsa_p384_sign_prehashed(
private_key_bytes: &[u8],
digest: &[u8],
) -> HsmResult<Vec<u8>> {
use crate::crypto::drbg::DrbgRng;
use p384::ecdsa::signature::hazmat::RandomizedPrehashSigner;
use p384::ecdsa::SigningKey;
let signing_key =
SigningKey::from_slice(private_key_bytes).map_err(|_| HsmError::KeyHandleInvalid)?;
let mut rng = DrbgRng::new()?;
let signature: p384::ecdsa::Signature = signing_key
.sign_prehash_with_rng(&mut rng, digest)
.map_err(|_| HsmError::DataLenRange)?;
Ok(signature.to_bytes().to_vec())
}
pub(crate) fn ecdsa_p384_verify_prehashed(
public_key_sec1: &[u8],
digest: &[u8],
signature: &[u8],
) -> HsmResult<bool> {
use p384::ecdsa::signature::hazmat::PrehashVerifier;
use p384::ecdsa::VerifyingKey;
if digest.len() < 24 {
return Err(HsmError::DataLenRange);
}
if signature.len() != 96 {
return Err(HsmError::SignatureInvalid);
}
let verifying_key =
VerifyingKey::from_sec1_bytes(public_key_sec1).map_err(|_| HsmError::KeyHandleInvalid)?;
let sig =
p384::ecdsa::Signature::from_slice(signature).map_err(|_| HsmError::SignatureInvalid)?;
Ok(verifying_key.verify_prehash(digest, &sig).is_ok())
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(any(debug_assertions, feature = "insecure-rustcrypto-rsa-private-ops")))]
#[test]
fn release_build_refuses_rustcrypto_rsa_private_operations() {
assert!(require_rustcrypto_rsa_private_ops().is_err());
}
fn unique_ids() -> (u64, u64) {
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(1);
let n = COUNTER.fetch_add(1, Ordering::Relaxed);
(0xC0DE_0000 | n, 0xBEEF_0000 | n)
}
fn fresh_rsa_keypair() -> (Vec<u8>, Vec<u8>, Vec<u8>) {
static KEYPAIR: std::sync::OnceLock<(Vec<u8>, Vec<u8>, Vec<u8>)> =
std::sync::OnceLock::new();
KEYPAIR
.get_or_init(|| {
let (priv_der, modulus, pub_exp) =
crate::crypto::keygen::generate_rsa_key_pair(2048, false).unwrap();
(priv_der.as_bytes().to_vec(), modulus, pub_exp)
})
.clone()
}
static CACHE_TEST_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
#[test]
#[cfg_attr(
not(any(debug_assertions, feature = "insecure-rustcrypto-rsa-private-ops")),
ignore = "RustCrypto RSA private-key operations are refused in release builds \
(RUSTSEC-2023-0071); run in debug or with \
--features insecure-rustcrypto-rsa-private-ops"
)]
fn rsa_pkcs1v15_verify_cached_populates_pub_cache() {
let _lock = CACHE_TEST_LOCK.lock().unwrap();
let (slot, handle) = unique_ids();
let (priv_der, modulus, pub_exp) = fresh_rsa_keypair();
let msg = b"verify hot path";
let sig = rsa_pkcs1v15_sign(&priv_der, msg, Some(HashAlg::Sha256)).unwrap();
assert!(get_cached_rsa_public_key(slot, handle).is_none());
let ok = rsa_pkcs1v15_verify_cached(
slot,
handle,
&modulus,
&pub_exp,
msg,
&sig,
Some(HashAlg::Sha256),
)
.expect("first verify");
assert!(ok, "valid signature must verify");
let arc1 = get_cached_rsa_public_key(slot, handle)
.expect("rsa_pkcs1v15_verify_cached should populate the public-key handle cache");
let _ok = rsa_pkcs1v15_verify_cached(
slot,
handle,
&modulus,
&pub_exp,
msg,
&sig,
Some(HashAlg::Sha256),
)
.expect("second verify");
let arc2 = get_cached_rsa_public_key(slot, handle).unwrap();
assert!(
Arc::ptr_eq(&arc1, &arc2),
"second verify should reuse the cached Arc<RsaPublicKey>, not rebuild BigUint"
);
evict_cached_keys(slot, handle);
}
#[test]
#[cfg_attr(
not(any(debug_assertions, feature = "insecure-rustcrypto-rsa-private-ops")),
ignore = "RustCrypto RSA private-key operations are refused in release builds \
(RUSTSEC-2023-0071); run in debug or with \
--features insecure-rustcrypto-rsa-private-ops"
)]
fn evict_cached_keys_removes_entries() {
let _lock = CACHE_TEST_LOCK.lock().unwrap();
let (slot, handle) = unique_ids();
let (der, _, _) = fresh_rsa_keypair();
let _ = rsa_pkcs1v15_sign_cached(slot, handle, &der, b"x", Some(HashAlg::Sha256)).unwrap();
assert!(get_cached_rsa_private_key(slot, handle).is_some());
evict_cached_keys(slot, handle);
assert!(
get_cached_rsa_private_key(slot, handle).is_none(),
"evict_cached_keys must drop the entry"
);
}
#[test]
#[cfg_attr(
not(any(debug_assertions, feature = "insecure-rustcrypto-rsa-private-ops")),
ignore = "RustCrypto RSA private-key operations are refused in release builds \
(RUSTSEC-2023-0071); run in debug or with \
--features insecure-rustcrypto-rsa-private-ops"
)]
fn rsa_oaep_decrypt_cached_uses_handle_cache() {
let _lock = CACHE_TEST_LOCK.lock().unwrap();
let (slot, handle) = unique_ids();
let (priv_der, modulus, pub_exp) = fresh_rsa_keypair();
let ct = rsa_oaep_encrypt_cached(
slot,
handle,
&modulus,
&pub_exp,
b"test",
OaepHash::Sha256,
&[],
)
.expect("encrypt");
assert!(get_cached_rsa_private_key(slot, handle).is_none());
let pt = rsa_oaep_decrypt_cached(slot, handle, &priv_der, &ct, OaepHash::Sha256, &[])
.expect("decrypt");
assert_eq!(pt, b"test");
let arc1 = get_cached_rsa_private_key(slot, handle)
.expect("rsa_oaep_decrypt_cached should populate the private-key handle cache");
let _pt = rsa_oaep_decrypt_cached(slot, handle, &priv_der, &ct, OaepHash::Sha256, &[])
.expect("second decrypt");
let arc2 = get_cached_rsa_private_key(slot, handle).unwrap();
assert!(
Arc::ptr_eq(&arc1, &arc2),
"second decrypt should reuse the cached Arc<RsaPrivateKey>, not rebuild BigUint"
);
evict_cached_keys(slot, handle);
}
#[test]
#[cfg_attr(
not(any(debug_assertions, feature = "insecure-rustcrypto-rsa-private-ops")),
ignore = "RustCrypto RSA private-key operations are refused in release builds \
(RUSTSEC-2023-0071); run in debug or with \
--features insecure-rustcrypto-rsa-private-ops"
)]
fn rsa_pss_verify_cached_populates_pub_cache() {
let _lock = CACHE_TEST_LOCK.lock().unwrap();
let (slot, handle) = unique_ids();
let (priv_der, modulus, pub_exp) = fresh_rsa_keypair();
let msg = b"pss verify path";
let sig = rsa_pss_sign(&priv_der, msg, HashAlg::Sha256).unwrap();
assert!(get_cached_rsa_public_key(slot, handle).is_none());
let ok =
rsa_pss_verify_cached(slot, handle, &modulus, &pub_exp, msg, &sig, HashAlg::Sha256)
.expect("pss verify");
assert!(ok);
assert!(
get_cached_rsa_public_key(slot, handle).is_some(),
"rsa_pss_verify_cached should populate the public-key handle cache"
);
evict_cached_keys(slot, handle);
}
#[test]
#[cfg_attr(
not(any(debug_assertions, feature = "insecure-rustcrypto-rsa-private-ops")),
ignore = "RustCrypto RSA private-key operations are refused in release builds \
(RUSTSEC-2023-0071); run in debug or with \
--features insecure-rustcrypto-rsa-private-ops"
)]
fn rsa_oaep_encrypt_cached_populates_pub_cache_and_round_trips() {
let _lock = CACHE_TEST_LOCK.lock().unwrap();
let (slot, handle) = unique_ids();
let (priv_der, modulus, pub_exp) = fresh_rsa_keypair();
assert!(get_cached_rsa_public_key(slot, handle).is_none());
let ct = rsa_oaep_encrypt_cached(
slot,
handle,
&modulus,
&pub_exp,
b"hello",
OaepHash::Sha256,
&[],
)
.expect("encrypt");
assert!(
get_cached_rsa_public_key(slot, handle).is_some(),
"rsa_oaep_encrypt_cached should populate the public-key handle cache"
);
let pt = rsa_oaep_decrypt(&priv_der, &ct, OaepHash::Sha256, &[]).unwrap();
assert_eq!(pt, b"hello");
evict_cached_keys(slot, handle);
}
#[test]
#[cfg_attr(
not(any(debug_assertions, feature = "insecure-rustcrypto-rsa-private-ops")),
ignore = "RustCrypto RSA private-key operations are refused in release builds \
(RUSTSEC-2023-0071); run in debug or with \
--features insecure-rustcrypto-rsa-private-ops"
)]
fn evict_cached_keys_clears_public_entry() {
let _lock = CACHE_TEST_LOCK.lock().unwrap();
let (slot, handle) = unique_ids();
let (priv_der, modulus, pub_exp) = fresh_rsa_keypair();
let msg = b"x";
let sig = rsa_pkcs1v15_sign(&priv_der, msg, Some(HashAlg::Sha256)).unwrap();
let _ = rsa_pkcs1v15_verify_cached(
slot,
handle,
&modulus,
&pub_exp,
msg,
&sig,
Some(HashAlg::Sha256),
)
.unwrap();
assert!(get_cached_rsa_public_key(slot, handle).is_some());
evict_cached_keys(slot, handle);
assert!(
get_cached_rsa_public_key(slot, handle).is_none(),
"evict_cached_keys must drop the public-key entry"
);
}
#[test]
#[cfg_attr(
not(any(debug_assertions, feature = "insecure-rustcrypto-rsa-private-ops")),
ignore = "RustCrypto RSA private-key operations are refused in release builds \
(RUSTSEC-2023-0071); run in debug or with \
--features insecure-rustcrypto-rsa-private-ops"
)]
fn clear_for_slot_drops_only_targeted_slot() {
let _lock = CACHE_TEST_LOCK.lock().unwrap();
let (slot_a, handle_a) = unique_ids();
let (slot_b, handle_b) = unique_ids();
let (der_a, mod_a, exp_a) = fresh_rsa_keypair();
let (der_b, mod_b, exp_b) = fresh_rsa_keypair();
let _ = rsa_pkcs1v15_sign_cached(slot_a, handle_a, &der_a, b"x", Some(HashAlg::Sha256))
.unwrap();
let _ = rsa_pkcs1v15_sign_cached(slot_b, handle_b, &der_b, b"x", Some(HashAlg::Sha256))
.unwrap();
let _ = rsa_oaep_encrypt_cached(
slot_a,
handle_a,
&mod_a,
&exp_a,
b"x",
OaepHash::Sha256,
&[],
)
.unwrap();
let _ = rsa_oaep_encrypt_cached(
slot_b,
handle_b,
&mod_b,
&exp_b,
b"x",
OaepHash::Sha256,
&[],
)
.unwrap();
assert!(get_cached_rsa_private_key(slot_a, handle_a).is_some());
assert!(get_cached_rsa_public_key(slot_a, handle_a).is_some());
assert!(get_cached_rsa_private_key(slot_b, handle_b).is_some());
assert!(get_cached_rsa_public_key(slot_b, handle_b).is_some());
clear_for_slot(slot_a);
assert!(
get_cached_rsa_private_key(slot_a, handle_a).is_none(),
"clear_for_slot must drop private entries in the targeted slot"
);
assert!(
get_cached_rsa_public_key(slot_a, handle_a).is_none(),
"clear_for_slot must drop public entries in the targeted slot"
);
assert!(
get_cached_rsa_private_key(slot_b, handle_b).is_some(),
"clear_for_slot must NOT touch entries in other slots"
);
assert!(
get_cached_rsa_public_key(slot_b, handle_b).is_some(),
"clear_for_slot must NOT touch public entries in other slots"
);
evict_cached_keys(slot_b, handle_b);
}
#[test]
#[cfg_attr(
not(any(debug_assertions, feature = "insecure-rustcrypto-rsa-private-ops")),
ignore = "RustCrypto RSA private-key operations are refused in release builds \
(RUSTSEC-2023-0071); run in debug or with \
--features insecure-rustcrypto-rsa-private-ops"
)]
fn clear_all_drops_every_entry() {
let _lock = CACHE_TEST_LOCK.lock().unwrap();
let (slot_a, handle_a) = unique_ids();
let (slot_b, handle_b) = unique_ids();
let (der_a, mod_a, exp_a) = fresh_rsa_keypair();
let (der_b, _mod_b, _exp_b) = fresh_rsa_keypair();
let _ = rsa_pkcs1v15_sign_cached(slot_a, handle_a, &der_a, b"x", Some(HashAlg::Sha256))
.unwrap();
let _ = rsa_pkcs1v15_sign_cached(slot_b, handle_b, &der_b, b"x", Some(HashAlg::Sha256))
.unwrap();
let _ = rsa_oaep_encrypt_cached(
slot_a,
handle_a,
&mod_a,
&exp_a,
b"x",
OaepHash::Sha256,
&[],
)
.unwrap();
assert!(get_cached_rsa_private_key(slot_a, handle_a).is_some());
assert!(get_cached_rsa_private_key(slot_b, handle_b).is_some());
assert!(get_cached_rsa_public_key(slot_a, handle_a).is_some());
clear_all();
assert!(
get_cached_rsa_private_key(slot_a, handle_a).is_none(),
"clear_all must drop the private cache"
);
assert!(
get_cached_rsa_private_key(slot_b, handle_b).is_none(),
"clear_all must drop the private cache across slots"
);
assert!(
get_cached_rsa_public_key(slot_a, handle_a).is_none(),
"clear_all must drop the public cache"
);
}
}