use crate::der::{Reader, parse_oid, tag};
use crate::hash::{Sha1, Sha256, Sha384, Sha512};
use crate::rsa::BoxedRsaPublicKey;
use crate::signature_registry::SignatureAlgorithm;
use crate::x509::{Error, oid};
fn parse_rsa_spki(spki: &[u8]) -> Result<BoxedRsaPublicKey, Error> {
let mut reader = Reader::new(spki);
let mut outer = reader.read_sequence()?;
let mut algid = outer.read_sequence()?;
let alg = parse_oid(algid.read_oid()?)?;
if alg.as_slice() == oid::RSA_ENCRYPTION {
algid.read_null()?;
algid.finish()?;
} else if alg.as_slice() == oid::ID_RSASSA_PSS {
if !algid.is_empty() {
check_rsassa_pss_params(&mut algid)?;
}
algid.finish()?;
} else {
return Err(Error::UnsupportedAlgorithm);
}
let key_bits = outer.read_bit_string()?;
outer.finish()?;
Ok(BoxedRsaPublicKey::from_pkcs1_der(key_bits)?)
}
fn check_rsassa_pss_params(algid: &mut Reader) -> Result<(), Error> {
let mut params = algid.read_sequence()?;
if params.peek_tag() != Some(tag::context(0)) {
return Err(Error::UnsupportedAlgorithm);
}
let body = params.read_tlv(tag::context(0))?;
check_hash_algid(body, oid::ID_SHA256)?;
if params.peek_tag() != Some(tag::context(1)) {
return Err(Error::UnsupportedAlgorithm);
}
let body = params.read_tlv(tag::context(1))?;
let mut r = Reader::new(body);
let mut mgf = r.read_sequence()?;
if parse_oid(mgf.read_oid()?)?.as_slice() != oid::ID_MGF1 {
return Err(Error::UnsupportedAlgorithm);
}
let mgf_hash = mgf.read_element()?;
mgf.finish()?;
r.finish()?;
check_hash_algid(mgf_hash, oid::ID_SHA256)?;
if params.peek_tag() != Some(tag::context(2)) {
return Err(Error::UnsupportedAlgorithm);
}
let body = params.read_tlv(tag::context(2))?;
let mut r = Reader::new(body);
let salt_ok = r.read_integer_bytes()? == [32];
r.finish()?;
if !salt_ok {
return Err(Error::UnsupportedAlgorithm);
}
if !params.is_empty() {
let body = params.read_tlv(tag::context(3))?;
let mut r = Reader::new(body);
let trailer_ok = r.read_integer_bytes()? == [1];
r.finish()?;
if !trailer_ok {
return Err(Error::UnsupportedAlgorithm);
}
}
params.finish()?;
Ok(())
}
fn check_hash_algid(der: &[u8], want: &[u64]) -> Result<(), Error> {
let mut r = Reader::new(der);
let mut h = r.read_sequence()?;
if parse_oid(h.read_oid()?)?.as_slice() != want {
return Err(Error::UnsupportedAlgorithm);
}
if !h.is_empty() {
h.read_null()?;
}
h.finish()?;
r.finish()?;
Ok(())
}
fn rsa_bits(spki: &[u8]) -> Option<u32> {
parse_rsa_spki(spki)
.ok()
.map(|k| k.modulus().bit_len() as u32)
}
macro_rules! rsa_pkcs1_entry {
($(#[$m:meta])* $name:ident, $id:expr, $oid:expr, $tls:expr, $digest:ty) => {
$(#[$m])*
pub(crate) struct $name;
impl SignatureAlgorithm for $name {
fn id(&self) -> &'static str { $id }
fn x509_oids(&self) -> &'static [&'static [u64]] { &[$oid] }
fn tls_schemes(&self) -> &'static [u16] { $tls }
fn verify(&self, spki: &[u8], message: &[u8], signature: &[u8]) -> Result<(), Error> {
let key = parse_rsa_spki(spki)?;
key.verify_pkcs1v15::<$digest>(message, signature).map_err(Error::Rsa)
}
fn rsa_modulus_bits(&self, spki: &[u8]) -> Option<u32> { rsa_bits(spki) }
}
};
}
macro_rules! rsa_pss_entry {
($(#[$m:meta])* $name:ident, $id:expr, $oids:expr, $tls:expr, $digest:ty) => {
$(#[$m])*
pub(crate) struct $name;
impl SignatureAlgorithm for $name {
fn id(&self) -> &'static str { $id }
fn x509_oids(&self) -> &'static [&'static [u64]] { $oids }
fn tls_schemes(&self) -> &'static [u16] { $tls }
fn verify(&self, spki: &[u8], message: &[u8], signature: &[u8]) -> Result<(), Error> {
let key = parse_rsa_spki(spki)?;
key.verify_pss::<$digest>(message, signature).map_err(Error::Rsa)
}
fn rsa_modulus_bits(&self, spki: &[u8]) -> Option<u32> { rsa_bits(spki) }
}
};
}
rsa_pkcs1_entry!(
Pkcs1Sha1,
"rsa-pkcs1-sha1",
oid::SHA1_WITH_RSA,
&[],
Sha1
);
rsa_pkcs1_entry!(
Pkcs1Sha256,
"rsa-pkcs1-sha256",
oid::SHA256_WITH_RSA,
&[0x0401],
Sha256
);
rsa_pkcs1_entry!(
Pkcs1Sha384,
"rsa-pkcs1-sha384",
oid::SHA384_WITH_RSA,
&[0x0501],
Sha384
);
rsa_pkcs1_entry!(
Pkcs1Sha512,
"rsa-pkcs1-sha512",
oid::SHA512_WITH_RSA,
&[],
Sha512
);
rsa_pss_entry!(
PssRsaeSha256,
"rsa-pss-rsae-sha256",
&[],
&[0x0804],
Sha256
);
rsa_pss_entry!(
PssRsaeSha384,
"rsa-pss-rsae-sha384",
&[],
&[0x0805],
Sha384
);
rsa_pss_entry!(
PssRsaeSha512,
"rsa-pss-rsae-sha512",
&[],
&[0x0806],
Sha512
);
rsa_pss_entry!(
PssPssSha256,
"rsa-pss-pss-sha256",
&[oid::ID_RSASSA_PSS],
&[],
Sha256
);
#[cfg(test)]
mod tests {
use super::*;
use crate::signature_registry::{find_by_id, find_by_oid, find_by_tls_scheme};
use crate::test_util::rsa_test_key_a;
use crate::x509::AnyPublicKey;
#[test]
fn ids_and_oids_resolve() {
for (id, scheme) in [
("rsa-pkcs1-sha256", 0x0401u16),
("rsa-pkcs1-sha384", 0x0501),
("rsa-pss-rsae-sha256", 0x0804),
("rsa-pss-rsae-sha384", 0x0805),
("rsa-pss-rsae-sha512", 0x0806),
] {
let by_id = find_by_id(id).expect(id);
assert_eq!(by_id.id(), id);
let by_scheme = find_by_tls_scheme(scheme).expect(id);
assert_eq!(by_scheme.id(), id);
}
assert!(find_by_id("rsa-pkcs1-sha512").is_some());
assert!(find_by_oid(oid::SHA512_WITH_RSA).is_some());
}
#[test]
fn pkcs1_oids_resolve_to_pkcs1_entries_only() {
for (o, id) in [
(oid::SHA256_WITH_RSA, "rsa-pkcs1-sha256"),
(oid::SHA384_WITH_RSA, "rsa-pkcs1-sha384"),
(oid::SHA512_WITH_RSA, "rsa-pkcs1-sha512"),
] {
assert_eq!(find_by_oid(o).expect(id).id(), id);
}
for id in [
"rsa-pss-rsae-sha256",
"rsa-pss-rsae-sha384",
"rsa-pss-rsae-sha512",
] {
assert!(
find_by_id(id).unwrap().x509_oids().is_empty(),
"{id} must not advertise X.509 OIDs"
);
}
assert_eq!(
find_by_oid(oid::ID_RSASSA_PSS).unwrap().id(),
"rsa-pss-pss-sha256"
);
}
fn boxed_pk_from_rsa_test_key() -> BoxedRsaPublicKey {
let pk = rsa_test_key_a().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);
BoxedRsaPublicKey::new(
crate::bignum::BoxedUint::from_be_bytes(&n),
crate::bignum::BoxedUint::from_be_bytes(&e),
)
}
#[test]
fn pkcs1_sha1_verify_via_registry() {
let key = rsa_test_key_a();
let spki = AnyPublicKey::Rsa(boxed_pk_from_rsa_test_key()).to_spki_der();
let sig = key.sign_pkcs1v15::<crate::hash::Sha1>(b"hi").unwrap();
let algo = find_by_id("rsa-pkcs1-sha1").expect("rsa-pkcs1-sha1");
algo.verify(&spki, b"hi", &sig).unwrap();
assert!(algo.verify(&spki, b"other", &sig).is_err());
assert!(algo.tls_schemes().is_empty());
}
#[test]
fn pss_pss_sha256_verify_accepts_rsa_encryption_spki() {
let key = rsa_test_key_a();
let spki = AnyPublicKey::Rsa(boxed_pk_from_rsa_test_key()).to_spki_der();
let mut rng = crate::rng::HmacDrbg::<Sha256>::new(b"reg-pss-pss", b"n", &[]);
let sig = key.sign_pss::<Sha256, _>(b"hi", &mut rng).unwrap();
let algo = find_by_id("rsa-pss-pss-sha256").unwrap();
algo.verify(&spki, b"hi", &sig).unwrap();
}
fn pss_spki(params: Option<alloc::vec::Vec<u8>>) -> alloc::vec::Vec<u8> {
use crate::der::{encode_bit_string, encode_sequence, oid_tlv};
let pkcs1 = boxed_pk_from_rsa_test_key().to_pkcs1_der();
let mut algid = oid_tlv(oid::ID_RSASSA_PSS);
if let Some(p) = params {
algid.extend_from_slice(&p);
}
encode_sequence(&[encode_sequence(&algid), encode_bit_string(&pkcs1)].concat())
}
fn pss_params(hash: &[u64], mgf1_hash: &[u64], salt_len: u8) -> alloc::vec::Vec<u8> {
use crate::der::{encode_context, encode_integer, encode_null, encode_sequence, oid_tlv};
let hash_algid = encode_sequence(&[oid_tlv(hash), encode_null()].concat());
let mgf1_hash_algid = encode_sequence(&[oid_tlv(mgf1_hash), encode_null()].concat());
let mgf_algid = encode_sequence(&[oid_tlv(oid::ID_MGF1), mgf1_hash_algid].concat());
encode_sequence(
&[
encode_context(0, &hash_algid),
encode_context(1, &mgf_algid),
encode_context(2, &encode_integer(&[salt_len])),
]
.concat(),
)
}
#[test]
fn pss_pss_sha256_validates_rsassa_pss_params() {
use crate::der::encode_sequence;
let key = rsa_test_key_a();
let mut rng = crate::rng::HmacDrbg::<Sha256>::new(b"reg-pss-params", b"n", &[]);
let sig = key.sign_pss::<Sha256, _>(b"hi", &mut rng).unwrap();
let algo = find_by_id("rsa-pss-pss-sha256").unwrap();
algo.verify(&pss_spki(None), b"hi", &sig).unwrap();
let good = pss_params(oid::ID_SHA256, oid::ID_SHA256, 32);
algo.verify(&pss_spki(Some(good)), b"hi", &sig).unwrap();
let empty = encode_sequence(&[]);
assert!(algo.verify(&pss_spki(Some(empty)), b"hi", &sig).is_err());
let bad_hash = pss_params(oid::ID_SHA384, oid::ID_SHA256, 32);
assert!(algo.verify(&pss_spki(Some(bad_hash)), b"hi", &sig).is_err());
let bad_mgf = pss_params(oid::ID_SHA256, oid::ID_SHA384, 32);
assert!(algo.verify(&pss_spki(Some(bad_mgf)), b"hi", &sig).is_err());
let bad_salt = pss_params(oid::ID_SHA256, oid::ID_SHA256, 20);
assert!(algo.verify(&pss_spki(Some(bad_salt)), b"hi", &sig).is_err());
assert_eq!(algo.rsa_modulus_bits(&pss_spki(None)), Some(2048));
let bad_hash = pss_params(oid::ID_SHA384, oid::ID_SHA256, 32);
assert_eq!(algo.rsa_modulus_bits(&pss_spki(Some(bad_hash))), None);
}
#[test]
fn pkcs1_sha256_verify_via_registry() {
let key = rsa_test_key_a();
let spki = AnyPublicKey::Rsa(boxed_pk_from_rsa_test_key()).to_spki_der();
let sig = key.sign_pkcs1v15::<Sha256>(b"hi").unwrap();
let algo = find_by_id("rsa-pkcs1-sha256").unwrap();
algo.verify(&spki, b"hi", &sig).unwrap();
assert!(algo.verify(&spki, b"other", &sig).is_err());
assert_eq!(algo.rsa_modulus_bits(&spki), Some(2048));
}
}