use alloc::vec::Vec;
use super::emsa::{self, RawPrivate, RawPublic};
use super::{Error, Pkcs1Digest};
use crate::bignum::{BoxedMontModulus, BoxedUint};
use crate::hash::{Digest, HmacSha256, Sha256};
use crate::rng::{CryptoRng, RngCore};
#[derive(Clone, Debug)]
pub struct BoxedRsaPublicKey {
n: BoxedUint,
e: BoxedUint,
mont: BoxedMontModulus,
k: usize,
}
#[derive(Clone, Debug)]
pub struct BoxedRsaPrivateKey {
n: BoxedUint,
e: BoxedUint,
d: BoxedUint,
p: BoxedUint,
q: BoxedUint,
mont: BoxedMontModulus,
k: usize,
phi_n_minus_1: Option<BoxedUint>,
blinding_seed: [u8; 32],
}
impl Drop for BoxedRsaPrivateKey {
fn drop(&mut self) {
self.d.zeroize();
self.p.zeroize();
self.q.zeroize();
if let Some(phi) = self.phi_n_minus_1.as_mut() {
phi.zeroize();
}
for b in self.blinding_seed.iter_mut() {
*b = 0;
}
let _ = core::hint::black_box(&self.blinding_seed);
}
}
fn derive_blinding_boxed(
p: &BoxedUint,
q: &BoxedUint,
d: &BoxedUint,
) -> (Option<BoxedUint>, [u8; 32]) {
let phi_n_minus_1 = if p.is_zero() || q.is_zero() {
None
} else {
let one = BoxedUint::from_u64(1);
let pm1 = p.sub(&one);
let qm1 = q.sub(&one);
Some(pm1.mul(&qm1).sub(&one))
};
let mut h = Sha256::new();
h.update(b"purecrypto-rsa-blinding-seed-v1");
let d_bytes = d.to_be_bytes(d.bit_len().div_ceil(8).max(1));
h.update(&d_bytes);
let digest = h.finalize();
let mut seed = [0u8; 32];
seed.copy_from_slice(digest.as_ref());
(phi_n_minus_1, seed)
}
fn raw_private_blinded_boxed(
mont: &BoxedMontModulus,
e: &BoxedUint,
d: &BoxedUint,
phi_n_minus_1: Option<&BoxedUint>,
blinding_seed: &[u8; 32],
k_bytes: usize,
c: &BoxedUint,
) -> BoxedUint {
let phi_n_minus_1 = match phi_n_minus_1 {
Some(v) => v,
None => return mont.pow(c, d), };
let c_bytes = c.to_be_bytes(k_bytes);
let mut blinder_bytes = Vec::with_capacity(k_bytes);
let mut counter: u32 = 0;
while blinder_bytes.len() < k_bytes {
let mut m = HmacSha256::new(blinding_seed);
m.update(b"r");
m.update(&counter.to_be_bytes());
m.update(&c_bytes);
let tag = m.finalize();
blinder_bytes.extend_from_slice(tag.as_ref());
counter += 1;
}
blinder_bytes.truncate(k_bytes);
let r_raw = BoxedUint::from_be_bytes(&blinder_bytes);
let r = r_raw.reduce(&mont.modulus());
let r = if r.is_zero() || r == BoxedUint::from_u64(1) {
BoxedUint::from_u64(2)
} else {
r
};
let r_e = mont.pow(&r, e);
let r_inv = mont.pow(&r, phi_n_minus_1);
let c_blind = mont.mul_mod(c, &r_e);
let m_blind = mont.pow(&c_blind, d);
mont.mul_mod(&m_blind, &r_inv)
}
pub(crate) const MIN_RSA_BITS: usize = 1024;
pub(crate) const MAX_RSA_BITS: usize = 16384;
fn validate_public_exponent(n: &BoxedUint, e: &BoxedUint) -> Result<(), Error> {
if !n.is_odd() {
return Err(Error::InvalidKey);
}
let three = BoxedUint::from_u64(3);
if e.lt(&three) || !e.is_odd() || !e.lt(n) {
return Err(Error::InvalidKey);
}
Ok(())
}
fn validate_private_components(n: &BoxedUint, p: &BoxedUint, q: &BoxedUint) -> Result<(), Error> {
let one = BoxedUint::from_u64(1);
if !one.lt(p) || !one.lt(q) {
return Err(Error::InvalidKey);
}
if !p.is_odd() || !q.is_odd() {
return Err(Error::InvalidKey);
}
if p == q {
return Err(Error::InvalidKey);
}
if &p.mul(q) != n {
return Err(Error::InvalidKey);
}
Ok(())
}
impl BoxedRsaPublicKey {
pub fn new(n: BoxedUint, e: BoxedUint) -> Self {
let k = n.bit_len().div_ceil(8);
let mont = BoxedMontModulus::new(&n);
BoxedRsaPublicKey { n, e, mont, k }
}
pub fn try_new(n: BoxedUint, e: BoxedUint) -> Result<Self, Error> {
let bits = n.bit_len();
if !(MIN_RSA_BITS..=MAX_RSA_BITS).contains(&bits) {
return Err(Error::InvalidLength);
}
validate_public_exponent(&n, &e)?;
Ok(Self::new(n, e))
}
pub fn modulus(&self) -> &BoxedUint {
&self.n
}
pub fn exponent(&self) -> &BoxedUint {
&self.e
}
pub fn verify_pkcs1v15<D: Pkcs1Digest>(&self, msg: &[u8], sig: &[u8]) -> Result<(), Error> {
emsa::verify_pkcs1v15::<D, _>(self, msg, sig)
}
pub fn verify_pss<D: Digest>(&self, msg: &[u8], sig: &[u8]) -> Result<(), Error> {
emsa::verify_pss::<D, _>(self, msg, sig)
}
pub fn encrypt_pkcs1v15<R: RngCore + CryptoRng>(
&self,
msg: &[u8],
rng: &mut R,
) -> Result<Vec<u8>, Error> {
emsa::encrypt_pkcs1v15(self, msg, rng)
}
pub fn encrypt_oaep<D: Digest, R: RngCore + CryptoRng>(
&self,
msg: &[u8],
label: &[u8],
rng: &mut R,
) -> Result<Vec<u8>, Error> {
emsa::encrypt_oaep::<D, _, _>(self, msg, label, rng)
}
}
impl BoxedRsaPrivateKey {
pub fn from_components(n: BoxedUint, e: BoxedUint, d: BoxedUint) -> Self {
let k = n.bit_len().div_ceil(8);
let mont = BoxedMontModulus::new(&n);
let p = BoxedUint::zero(1);
let q = BoxedUint::zero(1);
let (phi_n_minus_1, blinding_seed) = derive_blinding_boxed(&p, &q, &d);
BoxedRsaPrivateKey {
n,
e,
d,
p,
q,
mont,
k,
phi_n_minus_1,
blinding_seed,
}
}
pub fn generate<R: RngCore + CryptoRng>(
bits: usize,
e: BoxedUint,
rng: &mut R,
rounds: usize,
) -> Self {
use crate::bignum::inv_mod_boxed;
let one = BoxedUint::from_u64(1);
let half = bits / 2;
loop {
let p = super::prime::random_prime_boxed(rng, half, rounds);
let q = super::prime::random_prime_boxed(rng, half, rounds);
if p == q {
continue;
}
let diff = if p.lt(&q) { q.sub(&p) } else { p.sub(&q) };
if diff.bit_len() <= half.saturating_sub(100) {
continue;
}
let n = p.mul(&q);
let phi = p.sub(&one).mul(&q.sub(&one));
if let Some(d) = inv_mod_boxed(&e, &phi) {
let k = n.bit_len().div_ceil(8);
let mont = BoxedMontModulus::new(&n);
let (phi_n_minus_1, blinding_seed) = derive_blinding_boxed(&p, &q, &d);
return BoxedRsaPrivateKey {
n,
e,
d,
p,
q,
mont,
k,
phi_n_minus_1,
blinding_seed,
};
}
}
}
pub fn public_key(&self) -> BoxedRsaPublicKey {
BoxedRsaPublicKey::new(self.n.clone(), self.e.clone())
}
pub fn modulus(&self) -> &BoxedUint {
&self.n
}
pub fn sign_pkcs1v15<D: Pkcs1Digest>(&self, msg: &[u8]) -> Result<Vec<u8>, Error> {
emsa::sign_pkcs1v15::<D, _>(self, msg)
}
pub fn sign_pss<D: Digest, R: RngCore>(
&self,
msg: &[u8],
rng: &mut R,
) -> Result<Vec<u8>, Error> {
emsa::sign_pss::<D, _, R>(self, msg, rng)
}
pub fn decrypt_pkcs1v15(&self, ct: &[u8]) -> Result<Vec<u8>, Error> {
emsa::decrypt_pkcs1v15(self, ct)
}
pub fn decrypt_pkcs1v15_session(
&self,
ct: &[u8],
expected_len: usize,
) -> Result<Vec<u8>, Error> {
emsa::decrypt_pkcs1v15_session(self, ct, expected_len)
}
pub fn decrypt_oaep<D: Digest>(&self, ct: &[u8], label: &[u8]) -> Result<Vec<u8>, Error> {
emsa::decrypt_oaep::<D, _>(self, ct, label)
}
}
impl RawPublic for BoxedRsaPublicKey {
fn key_size(&self) -> usize {
self.k
}
fn modulus_bits(&self) -> usize {
self.n.bit_len()
}
fn raw_public(&self, m: &[u8]) -> Vec<u8> {
self.mont
.pow(&BoxedUint::from_be_bytes(m), &self.e)
.to_be_bytes(self.k)
}
}
impl emsa::PublicModulus for BoxedRsaPublicKey {
fn modulus_be_bytes(&self) -> Vec<u8> {
self.n.to_be_bytes(self.k)
}
}
impl RawPrivate for BoxedRsaPrivateKey {
fn key_size(&self) -> usize {
self.k
}
fn modulus_bits(&self) -> usize {
self.n.bit_len()
}
fn raw_private(&self, c: &[u8]) -> Vec<u8> {
let c_uint = BoxedUint::from_be_bytes(c);
raw_private_blinded_boxed(
&self.mont,
&self.e,
&self.d,
self.phi_n_minus_1.as_ref(),
&self.blinding_seed,
self.k,
&c_uint,
)
.to_be_bytes(self.k)
}
fn secret_seed(&self) -> [u8; 32] {
self.blinding_seed
}
}
#[cfg(feature = "der")]
impl BoxedRsaPublicKey {
pub fn from_pkcs1_der(der: &[u8]) -> Result<Self, crate::der::Error> {
let mut reader = crate::der::Reader::new(der);
let mut seq = reader.read_sequence()?;
let n = BoxedUint::from_be_bytes(seq.read_integer_bytes()?);
let e = BoxedUint::from_be_bytes(seq.read_integer_bytes()?);
seq.finish()?;
reader.finish()?;
let bits = n.bit_len();
if !(MIN_RSA_BITS..=MAX_RSA_BITS).contains(&bits) {
return Err(crate::der::Error::Malformed);
}
validate_public_exponent(&n, &e).map_err(|_| crate::der::Error::Malformed)?;
Ok(BoxedRsaPublicKey::new(n, e))
}
pub fn to_pkcs1_der(&self) -> Vec<u8> {
use crate::der::{encode_integer, encode_sequence};
let n = self.n.to_be_bytes(self.k);
let e = self.e.to_be_bytes(self.e.bit_len().div_ceil(8).max(1));
encode_sequence(&[encode_integer(&n), encode_integer(&e)].concat())
}
pub fn to_spki_der(&self) -> Vec<u8> {
use crate::der::{encode_bit_string, encode_null, encode_sequence, oid_tlv};
let algid = encode_sequence(&[oid_tlv(&RSA_ENCRYPTION_OID), encode_null()].concat());
encode_sequence(&[algid, encode_bit_string(&self.to_pkcs1_der())].concat())
}
pub fn to_spki_pem(&self) -> alloc::string::String {
crate::der::pem_encode("PUBLIC KEY", &self.to_spki_der())
}
pub fn from_spki_der(der: &[u8]) -> Result<Self, crate::der::Error> {
let mut reader = crate::der::Reader::new(der);
let mut outer = reader.read_sequence()?;
let mut algid = outer.read_sequence()?;
let alg = crate::der::parse_oid(algid.read_oid()?)?;
if alg.as_slice() != RSA_ENCRYPTION_OID {
return Err(crate::der::Error::Malformed);
}
algid.read_null()?;
algid.finish()?;
let key_bits = outer.read_bit_string()?;
outer.finish()?;
reader.finish()?;
Self::from_pkcs1_der(key_bits)
}
pub fn from_spki_pem(pem: &str) -> Result<Self, crate::der::Error> {
Self::from_spki_der(&crate::der::pem_decode(pem, "PUBLIC KEY")?)
}
}
#[cfg(feature = "der")]
const RSA_ENCRYPTION_OID: [u64; 7] = [1, 2, 840, 113549, 1, 1, 1];
#[cfg(feature = "der")]
impl BoxedRsaPrivateKey {
pub fn from_pkcs1_der(der: &[u8]) -> Result<Self, crate::der::Error> {
let mut reader = crate::der::Reader::new(der);
let mut seq = reader.read_sequence()?;
let _version = seq.read_integer_bytes()?;
let n = BoxedUint::from_be_bytes(seq.read_integer_bytes()?);
let e = BoxedUint::from_be_bytes(seq.read_integer_bytes()?);
let d = BoxedUint::from_be_bytes(seq.read_integer_bytes()?);
let p = BoxedUint::from_be_bytes(seq.read_integer_bytes()?);
let q = BoxedUint::from_be_bytes(seq.read_integer_bytes()?);
let _dp = seq.read_integer_bytes()?;
let _dq = seq.read_integer_bytes()?;
let _qinv = seq.read_integer_bytes()?;
seq.finish()?;
reader.finish()?;
let bits = n.bit_len();
if !(MIN_RSA_BITS..=MAX_RSA_BITS).contains(&bits) {
return Err(crate::der::Error::Malformed);
}
validate_public_exponent(&n, &e).map_err(|_| crate::der::Error::Malformed)?;
validate_private_components(&n, &p, &q).map_err(|_| crate::der::Error::Malformed)?;
let k = n.bit_len().div_ceil(8);
let mont = BoxedMontModulus::new(&n);
let (phi_n_minus_1, blinding_seed) = derive_blinding_boxed(&p, &q, &d);
Ok(BoxedRsaPrivateKey {
n,
e,
d,
p,
q,
mont,
k,
phi_n_minus_1,
blinding_seed,
})
}
pub fn from_pkcs1_pem(pem: &str) -> Result<Self, crate::der::Error> {
Self::from_pkcs1_der(&crate::der::pem_decode(pem, "RSA PRIVATE KEY")?)
}
pub fn to_pkcs1_der(&self) -> Vec<u8> {
use crate::bignum::inv_mod_boxed;
use crate::der::{encode_integer, encode_sequence};
assert!(
!self.p.is_zero() && !self.q.is_zero(),
"to_pkcs1_der requires the prime factors (generated keys only)"
);
let one = BoxedUint::from_u64(1);
let dp = self.d.reduce(&self.p.sub(&one));
let dq = self.d.reduce(&self.q.sub(&one));
let qinv = inv_mod_boxed(&self.q, &self.p)
.expect("to_pkcs1_der: gcd(q, p) ≠ 1 — RSA primes are not coprime");
let be = |v: &BoxedUint| v.to_be_bytes(v.bit_len().div_ceil(8).max(1));
encode_sequence(
&[
encode_integer(&[0]),
encode_integer(&be(&self.n)),
encode_integer(&be(&self.e)),
encode_integer(&be(&self.d)),
encode_integer(&be(&self.p)),
encode_integer(&be(&self.q)),
encode_integer(&be(&dp)),
encode_integer(&be(&dq)),
encode_integer(&be(&qinv)),
]
.concat(),
)
}
pub fn to_pkcs1_pem(&self) -> alloc::string::String {
crate::der::pem_encode("RSA PRIVATE KEY", &self.to_pkcs1_der())
}
pub fn to_pkcs8_der(&self) -> Vec<u8> {
use crate::der::{
encode_integer, encode_null, encode_octet_string, encode_sequence, oid_tlv,
};
let algid = encode_sequence(&[oid_tlv(&RSA_ENCRYPTION_OID), encode_null()].concat());
encode_sequence(
&[
encode_integer(&[0]),
algid,
encode_octet_string(&self.to_pkcs1_der()),
]
.concat(),
)
}
pub fn to_pkcs8_pem(&self) -> alloc::string::String {
crate::der::pem_encode("PRIVATE KEY", &self.to_pkcs8_der())
}
pub fn from_pkcs8_der(der: &[u8]) -> Result<Self, crate::der::Error> {
let mut reader = crate::der::Reader::new(der);
let mut outer = reader.read_sequence()?;
let version = outer.read_integer_bytes()?;
if version != [0] {
return Err(crate::der::Error::Malformed);
}
let mut algid = outer.read_sequence()?;
let alg = crate::der::parse_oid(algid.read_oid()?)?;
if alg.as_slice() != RSA_ENCRYPTION_OID {
return Err(crate::der::Error::Malformed);
}
algid.read_null()?;
algid.finish()?;
let inner = outer.read_octet_string()?;
outer.finish()?;
reader.finish()?;
Self::from_pkcs1_der(inner)
}
pub fn from_pkcs8_pem(pem: &str) -> Result<Self, crate::der::Error> {
Self::from_pkcs8_der(&crate::der::pem_decode(pem, "PRIVATE KEY")?)
}
#[cfg(all(feature = "kdf", feature = "der"))]
pub fn to_pkcs8_der_encrypted(
&self,
password: &[u8],
params: &crate::kdf::pbes2::Pbes2Params,
rng: &mut impl crate::rng::RngCore,
) -> Vec<u8> {
crate::kdf::pbes2::encrypt(&self.to_pkcs8_der(), password, params, rng)
}
#[cfg(all(feature = "kdf", feature = "der"))]
pub fn to_pkcs8_pem_encrypted(
&self,
password: &[u8],
params: &crate::kdf::pbes2::Pbes2Params,
rng: &mut impl crate::rng::RngCore,
) -> alloc::string::String {
crate::kdf::pbes2::encrypt_pem(&self.to_pkcs8_der(), password, params, rng)
}
#[cfg(all(feature = "kdf", feature = "der"))]
pub fn from_pkcs8_der_encrypted(
der: &[u8],
password: &[u8],
) -> Result<Self, crate::der::Error> {
let inner =
crate::kdf::pbes2::decrypt(der, password).map_err(|_| crate::der::Error::Malformed)?;
Self::from_pkcs8_der(&inner)
}
#[cfg(all(feature = "kdf", feature = "der"))]
pub fn from_pkcs8_pem_encrypted(pem: &str, password: &[u8]) -> Result<Self, crate::der::Error> {
let inner = crate::kdf::pbes2::decrypt_pem(pem, password)
.map_err(|_| crate::der::Error::Malformed)?;
Self::from_pkcs8_der(&inner)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hash::Sha256;
use crate::rng::HmacDrbg;
use crate::test_util::rsa_test_key_a;
fn boxed_pub() -> (crate::rsa::RsaPrivateKey<32>, BoxedRsaPublicKey) {
let key = rsa_test_key_a();
let pk = key.public_key();
let mut n = [0u8; 256];
pk.modulus().write_be_bytes(&mut n);
let mut e = [0u8; 256];
pk.exponent().write_be_bytes(&mut e);
let boxed =
BoxedRsaPublicKey::new(BoxedUint::from_be_bytes(&n), BoxedUint::from_be_bytes(&e));
(key, boxed)
}
#[test]
fn boxed_oaep_encrypts_const_generic_decrypts() {
let (key, boxed) = boxed_pub();
let mut r = HmacDrbg::<Sha256>::new(b"boxed-oaep", b"nonce", &[]);
let msg = b"OAEP from a runtime-sized public key";
let ct = boxed
.encrypt_oaep::<Sha256, _>(msg, b"label", &mut r)
.unwrap();
assert_eq!(&key.decrypt_oaep::<Sha256>(&ct, b"label").unwrap()[..], msg);
}
#[test]
fn boxed_verifies_const_generic_signatures() {
let (key, boxed) = boxed_pub();
let mut r = HmacDrbg::<Sha256>::new(b"boxed-rsa", b"nonce", &[]);
let s1 = key.sign_pkcs1v15::<Sha256>(b"hello").unwrap();
boxed.verify_pkcs1v15::<Sha256>(b"hello", &s1).unwrap();
assert!(boxed.verify_pkcs1v15::<Sha256>(b"other", &s1).is_err());
let s2 = key.sign_pss::<Sha256, _>(b"hello", &mut r).unwrap();
boxed.verify_pss::<Sha256>(b"hello", &s2).unwrap();
}
#[test]
fn boxed_from_pkcs1_der() {
let key = rsa_test_key_a();
let der = key.public_key().to_pkcs1_der();
let boxed = BoxedRsaPublicKey::from_pkcs1_der(&der).unwrap();
assert_eq!(boxed.modulus().bit_len(), 2048);
let sig = key.sign_pkcs1v15::<Sha256>(b"via der").unwrap();
boxed.verify_pkcs1v15::<Sha256>(b"via der", &sig).unwrap();
}
#[test]
fn generate_runtime_key_signs_and_exports() {
let mut r = HmacDrbg::<Sha256>::new(b"boxed-keygen", b"nonce", &[]);
let key = BoxedRsaPrivateKey::generate(1024, BoxedUint::from_u64(65537), &mut r, 12);
assert_eq!(key.modulus().bit_len(), 1024);
let sig = key.sign_pkcs1v15::<Sha256>(b"runtime keygen").unwrap();
let pk = key.public_key();
pk.verify_pkcs1v15::<Sha256>(b"runtime keygen", &sig)
.unwrap();
assert!(pk.verify_pkcs1v15::<Sha256>(b"other", &sig).is_err());
let parsed = BoxedRsaPrivateKey::from_pkcs1_der(&key.to_pkcs1_der()).unwrap();
let sig2 = parsed.sign_pkcs1v15::<Sha256>(b"via der").unwrap();
pk.verify_pkcs1v15::<Sha256>(b"via der", &sig2).unwrap();
}
#[test]
fn rsa_decrypt_pkcs1v15_rejects_tiny_modulus() {
use crate::der::{encode_integer, encode_sequence};
let n = [0xff];
let e = [0x03];
let der = encode_sequence(&[encode_integer(&n), encode_integer(&e)].concat());
assert!(BoxedRsaPublicKey::from_pkcs1_der(&der).is_err());
}
#[test]
fn rsa_rejects_modulus_above_16384_bits() {
use crate::der::{encode_integer, encode_sequence};
let mut n = alloc::vec![0xffu8; 4096];
n[0] = 0x7f;
let e = [0x01, 0x00, 0x01];
let der = encode_sequence(&[encode_integer(&n), encode_integer(&e)].concat());
assert!(BoxedRsaPublicKey::from_pkcs1_der(&der).is_err());
}
#[test]
fn boxed_private_key_signs() {
let key = rsa_test_key_a();
let mut nb = [0u8; 256];
key.modulus().write_be_bytes(&mut nb);
let mut eb = [0u8; 256];
key.exponent().write_be_bytes(&mut eb);
let mut db = [0u8; 256];
key.private_exponent().write_be_bytes(&mut db);
let boxed = BoxedRsaPrivateKey::from_components(
BoxedUint::from_be_bytes(&nb),
BoxedUint::from_be_bytes(&eb),
BoxedUint::from_be_bytes(&db),
);
let sig = boxed.sign_pkcs1v15::<Sha256>(b"sign me").unwrap();
key.public_key()
.verify_pkcs1v15::<Sha256>(b"sign me", &sig)
.unwrap();
}
fn gen_small_key(seed: &[u8]) -> BoxedRsaPrivateKey {
let mut rng = HmacDrbg::<Sha256>::new(seed, b"n", &[]);
BoxedRsaPrivateKey::generate(1024, BoxedUint::from_u64(65537), &mut rng, 12)
}
#[test]
fn rsa_public_key_spki_der_roundtrip() {
let sk = gen_small_key(b"rsa-spki-der");
let pk = sk.public_key();
let der = pk.to_spki_der();
assert_eq!(der[0], 0x30);
let parsed = BoxedRsaPublicKey::from_spki_der(&der).unwrap();
assert_eq!(parsed.to_pkcs1_der(), pk.to_pkcs1_der());
let any_spki =
crate::x509::AnyPublicKey::Rsa(BoxedRsaPublicKey::new(pk.n.clone(), pk.e.clone()))
.to_spki_der();
assert_eq!(der, any_spki);
}
#[test]
fn rsa_public_key_spki_pem_roundtrip() {
let sk = gen_small_key(b"rsa-spki-pem");
let pk = sk.public_key();
let pem = pk.to_spki_pem();
assert!(pem.starts_with("-----BEGIN PUBLIC KEY-----\n"));
assert!(pem.trim_end().ends_with("-----END PUBLIC KEY-----"));
let parsed = BoxedRsaPublicKey::from_spki_pem(&pem).unwrap();
assert_eq!(parsed.to_pkcs1_der(), pk.to_pkcs1_der());
}
#[test]
fn rsa_private_key_pkcs8_der_roundtrip() {
let sk = gen_small_key(b"rsa-pkcs8-der");
let der = sk.to_pkcs8_der();
assert_eq!(der[0], 0x30);
let parsed = BoxedRsaPrivateKey::from_pkcs8_der(&der).unwrap();
assert_eq!(parsed.to_pkcs1_der(), sk.to_pkcs1_der());
let sig = parsed.sign_pkcs1v15::<Sha256>(b"via pkcs8").unwrap();
sk.public_key()
.verify_pkcs1v15::<Sha256>(b"via pkcs8", &sig)
.unwrap();
}
#[test]
fn rsa_private_key_pkcs8_pem_roundtrip() {
let sk = gen_small_key(b"rsa-pkcs8-pem");
let pem = sk.to_pkcs8_pem();
assert!(pem.starts_with("-----BEGIN PRIVATE KEY-----\n"));
assert!(pem.trim_end().ends_with("-----END PRIVATE KEY-----"));
let parsed = BoxedRsaPrivateKey::from_pkcs8_pem(&pem).unwrap();
assert_eq!(parsed.to_pkcs1_der(), sk.to_pkcs1_der());
}
#[test]
fn rsa_encrypted_pkcs8_pem_roundtrip() {
let sk = gen_small_key(b"rsa-pkcs8-pem-enc");
let mut rng = HmacDrbg::<Sha256>::new(b"pbes2-enc", b"nonce", &[]);
let params = crate::kdf::pbes2::Pbes2Params {
kdf: crate::kdf::pbes2::KdfChoice::Pbkdf2HmacSha256 { iterations: 10_000 },
cipher: crate::kdf::pbes2::CipherChoice::Aes256Gcm,
salt_len: 16,
};
let pem = sk.to_pkcs8_pem_encrypted(b"swordfish", ¶ms, &mut rng);
assert!(pem.starts_with("-----BEGIN ENCRYPTED PRIVATE KEY-----\n"));
assert!(
pem.trim_end()
.ends_with("-----END ENCRYPTED PRIVATE KEY-----")
);
let parsed = BoxedRsaPrivateKey::from_pkcs8_pem_encrypted(&pem, b"swordfish").unwrap();
assert_eq!(parsed.to_pkcs1_der(), sk.to_pkcs1_der());
assert!(BoxedRsaPrivateKey::from_pkcs8_pem_encrypted(&pem, b"wrong").is_err());
let sig = parsed
.sign_pkcs1v15::<Sha256>(b"via encrypted pkcs8")
.unwrap();
sk.public_key()
.verify_pkcs1v15::<Sha256>(b"via encrypted pkcs8", &sig)
.unwrap();
}
#[test]
fn rsa_encrypted_pkcs8_der_roundtrip_cbc() {
let sk = gen_small_key(b"rsa-pkcs8-der-cbc");
let mut rng = HmacDrbg::<Sha256>::new(b"pbes2-cbc", b"nonce", &[]);
let params = crate::kdf::pbes2::Pbes2Params {
kdf: crate::kdf::pbes2::KdfChoice::Pbkdf2HmacSha512 { iterations: 10_000 },
cipher: crate::kdf::pbes2::CipherChoice::Aes256Cbc,
salt_len: 16,
};
let der = sk.to_pkcs8_der_encrypted(b"pass", ¶ms, &mut rng);
let parsed = BoxedRsaPrivateKey::from_pkcs8_der_encrypted(&der, b"pass").unwrap();
assert_eq!(parsed.to_pkcs1_der(), sk.to_pkcs1_der());
}
#[test]
fn rsa_public_key_from_spki_rejects_non_rsa_oid() {
use crate::der::{encode_bit_string, encode_sequence, oid_tlv};
let algid = encode_sequence(&oid_tlv(&[1, 3, 101, 112]));
let dummy_key = [0u8; 32];
let spki = encode_sequence(&[algid, encode_bit_string(&dummy_key)].concat());
assert!(BoxedRsaPublicKey::from_spki_der(&spki).is_err());
}
#[test]
fn rsa_public_key_from_spki_rejects_missing_null_params() {
use crate::der::{encode_bit_string, encode_sequence, oid_tlv};
let algid = encode_sequence(&oid_tlv(&RSA_ENCRYPTION_OID));
let dummy = [0u8; 16];
let spki = encode_sequence(&[algid, encode_bit_string(&dummy)].concat());
assert!(BoxedRsaPublicKey::from_spki_der(&spki).is_err());
}
#[test]
fn rsa_public_key_from_spki_pem_rejects_pkcs1_label() {
let sk = gen_small_key(b"rsa-spki-wrong-label");
let pkcs1_pem = crate::der::pem_encode("RSA PUBLIC KEY", &sk.public_key().to_pkcs1_der());
assert!(BoxedRsaPublicKey::from_spki_pem(&pkcs1_pem).is_err());
}
#[test]
fn rsa_private_key_from_pkcs8_rejects_nonzero_version() {
use crate::der::{
encode_integer, encode_null, encode_octet_string, encode_sequence, oid_tlv,
};
let sk = gen_small_key(b"rsa-pkcs8-v1");
let algid = encode_sequence(&[oid_tlv(&RSA_ENCRYPTION_OID), encode_null()].concat());
let der = encode_sequence(
&[
encode_integer(&[1]), algid,
encode_octet_string(&sk.to_pkcs1_der()),
]
.concat(),
);
assert!(BoxedRsaPrivateKey::from_pkcs8_der(&der).is_err());
}
#[test]
fn rsa_private_key_from_pkcs8_rejects_non_rsa_oid() {
use crate::der::{encode_integer, encode_octet_string, encode_sequence, oid_tlv};
let algid = encode_sequence(&oid_tlv(&[1, 3, 101, 112]));
let dummy = [0u8; 34];
let der =
encode_sequence(&[encode_integer(&[0]), algid, encode_octet_string(&dummy)].concat());
assert!(BoxedRsaPrivateKey::from_pkcs8_der(&der).is_err());
}
#[test]
fn rsa_private_key_from_pkcs8_rejects_missing_null_params() {
use crate::der::{encode_integer, encode_octet_string, encode_sequence, oid_tlv};
let sk = gen_small_key(b"rsa-pkcs8-no-null");
let algid = encode_sequence(&oid_tlv(&RSA_ENCRYPTION_OID));
let der = encode_sequence(
&[
encode_integer(&[0]),
algid,
encode_octet_string(&sk.to_pkcs1_der()),
]
.concat(),
);
assert!(BoxedRsaPrivateKey::from_pkcs8_der(&der).is_err());
}
#[test]
fn try_new_rejects_degenerate_exponents() {
let (_, boxed) = boxed_pub();
let n = boxed.modulus().clone();
let cases: [(BoxedUint, &'static str); 5] = [
(BoxedUint::from_u64(0), "e=0"),
(BoxedUint::from_u64(1), "e=1"),
(BoxedUint::from_u64(2), "e=2 (even)"),
(n.clone(), "e=n"),
(n.add(&BoxedUint::from_u64(1)), "e=n+1"),
];
for (e, why) in cases {
assert!(
matches!(
BoxedRsaPublicKey::try_new(n.clone(), e),
Err(Error::InvalidKey)
),
"{why} should be rejected as InvalidKey"
);
}
assert!(BoxedRsaPublicKey::try_new(n, BoxedUint::from_u64(65537)).is_ok());
}
#[test]
fn from_pkcs1_der_rejects_even_exponent() {
use crate::der::{encode_integer, encode_sequence};
let (_, boxed) = boxed_pub();
let n_bytes = boxed.modulus().to_be_bytes(256);
let der = encode_sequence(&[encode_integer(&n_bytes), encode_integer(&[4])].concat());
assert!(BoxedRsaPublicKey::from_pkcs1_der(&der).is_err());
}
#[test]
fn from_pkcs1_der_rejects_even_modulus() {
use crate::der::{encode_integer, encode_sequence};
let (_, boxed) = boxed_pub();
let one = BoxedUint::from_u64(1);
let mut n = boxed.modulus().clone();
if n.is_odd() {
n = n.sub(&one);
}
assert!(!n.is_odd(), "test modulus must be even");
let n_bytes = n.to_be_bytes(256);
let e_bytes = BoxedUint::from_u64(65537).to_be_bytes(3);
let der = encode_sequence(&[encode_integer(&n_bytes), encode_integer(&e_bytes)].concat());
assert!(BoxedRsaPublicKey::from_pkcs1_der(&der).is_err());
}
#[test]
fn from_pkcs1_der_rejects_mismatched_modulus() {
use crate::der::{encode_integer, encode_sequence};
let sk_a = gen_small_key(b"rsa-pkcs1-pq-a");
let sk_b = gen_small_key(b"rsa-pkcs1-pq-b");
let be = |v: &BoxedUint| v.to_be_bytes(v.bit_len().div_ceil(8).max(1));
let one = BoxedUint::from_u64(1);
let dp = sk_a.d.reduce(&sk_a.p.sub(&one));
let dq = sk_a.d.reduce(&sk_a.q.sub(&one));
let qinv = crate::bignum::inv_mod_boxed(&sk_a.q, &sk_a.p).unwrap();
let der = encode_sequence(
&[
encode_integer(&[0]),
encode_integer(&be(sk_b.modulus())), encode_integer(&be(&sk_a.e)),
encode_integer(&be(&sk_a.d)),
encode_integer(&be(&sk_a.p)),
encode_integer(&be(&sk_a.q)),
encode_integer(&be(&dp)),
encode_integer(&be(&dq)),
encode_integer(&be(&qinv)),
]
.concat(),
);
assert!(matches!(
BoxedRsaPrivateKey::from_pkcs1_der(&der),
Err(crate::der::Error::Malformed)
));
}
#[test]
fn from_pkcs1_der_rejects_equal_primes() {
use crate::der::{encode_integer, encode_sequence};
let sk = gen_small_key(b"rsa-pkcs1-eq-primes");
let be = |v: &BoxedUint| v.to_be_bytes(v.bit_len().div_ceil(8).max(1));
let p_sq = sk.p.mul(&sk.p);
let der = encode_sequence(
&[
encode_integer(&[0]),
encode_integer(&be(&p_sq)),
encode_integer(&be(&sk.e)),
encode_integer(&be(&sk.d)),
encode_integer(&be(&sk.p)),
encode_integer(&be(&sk.p)), encode_integer(&[1]),
encode_integer(&[1]),
encode_integer(&[1]),
]
.concat(),
);
assert!(matches!(
BoxedRsaPrivateKey::from_pkcs1_der(&der),
Err(crate::der::Error::Malformed)
));
}
#[test]
fn boxed_session_decrypt_recovers_message_on_valid_ct() {
let key = rsa_test_key_a();
let mut nb = [0u8; 256];
key.modulus().write_be_bytes(&mut nb);
let mut eb = [0u8; 256];
key.exponent().write_be_bytes(&mut eb);
let mut db = [0u8; 256];
key.private_exponent().write_be_bytes(&mut db);
let boxed_sk = BoxedRsaPrivateKey::from_components(
BoxedUint::from_be_bytes(&nb),
BoxedUint::from_be_bytes(&eb),
BoxedUint::from_be_bytes(&db),
);
let pk = key.public_key();
let mut r = HmacDrbg::<Sha256>::new(b"boxed-session-ok", b"nonce", &[]);
let msg = [0x5au8; 48];
let ct = pk.encrypt_pkcs1v15(&msg, &mut r).unwrap();
let out = boxed_sk.decrypt_pkcs1v15_session(&ct, msg.len()).unwrap();
assert_eq!(out, msg);
}
#[test]
fn boxed_session_decrypt_returns_synthetic_on_bad_padding() {
let mut r = HmacDrbg::<Sha256>::new(b"boxed-session-syn", b"nonce", &[]);
let key = BoxedRsaPrivateKey::generate(1024, BoxedUint::from_u64(65537), &mut r, 12);
let bogus_ct = [0x42u8; 128];
let out = key.decrypt_pkcs1v15_session(&bogus_ct, 48).unwrap();
assert_eq!(out.len(), 48);
}
#[test]
fn boxed_session_decrypt_is_deterministic_under_same_key() {
let mut r = HmacDrbg::<Sha256>::new(b"boxed-session-det", b"nonce", &[]);
let key = BoxedRsaPrivateKey::generate(1024, BoxedUint::from_u64(65537), &mut r, 12);
let bogus_ct = [0xa9u8; 128];
let a = key.decrypt_pkcs1v15_session(&bogus_ct, 48).unwrap();
let b = key.decrypt_pkcs1v15_session(&bogus_ct, 48).unwrap();
assert_eq!(a, b);
}
#[test]
fn boxed_session_decrypt_rejects_wrong_length_ct() {
let mut r = HmacDrbg::<Sha256>::new(b"boxed-session-len", b"nonce", &[]);
let key = BoxedRsaPrivateKey::generate(1024, BoxedUint::from_u64(65537), &mut r, 12);
let short = [0u8; 127];
assert_eq!(
key.decrypt_pkcs1v15_session(&short, 48),
Err(Error::InvalidLength)
);
}
}