use rsa::pkcs1v15::Pkcs1v15Sign;
use rsa::pss::Pss;
use rsa::{traits::SignatureScheme, RsaPublicKey};
use sha1::Sha1;
use sha2::{Sha256, Sha384, Sha512};
use crate::error::PdfError;
use super::der::{read_octet_string, read_oid, write_set, write_tlv, Class};
use super::signed_data::{Attribute, SignedData, SignerIdentifier, SignerInfo};
use super::x509::Certificate;
pub const OID_SHA1: [u64; 6] = [1, 3, 14, 3, 2, 26];
pub const OID_SHA256: [u64; 9] = [2, 16, 840, 1, 101, 3, 4, 2, 1];
pub const OID_SHA384: [u64; 9] = [2, 16, 840, 1, 101, 3, 4, 2, 2];
pub const OID_SHA512: [u64; 9] = [2, 16, 840, 1, 101, 3, 4, 2, 3];
pub const OID_RSA_ENCRYPTION: [u64; 7] = [1, 2, 840, 113549, 1, 1, 1];
pub const OID_SHA1_WITH_RSA: [u64; 7] = [1, 2, 840, 113549, 1, 1, 5];
pub const OID_SHA256_WITH_RSA: [u64; 7] = [1, 2, 840, 113549, 1, 1, 11];
pub const OID_SHA384_WITH_RSA: [u64; 7] = [1, 2, 840, 113549, 1, 1, 12];
pub const OID_SHA512_WITH_RSA: [u64; 7] = [1, 2, 840, 113549, 1, 1, 13];
pub const OID_RSA_PSS: [u64; 7] = [1, 2, 840, 113549, 1, 1, 10];
pub const OID_EC_PUBLIC_KEY: [u64; 6] = [1, 2, 840, 10045, 2, 1];
pub const OID_ECDSA_WITH_SHA1: [u64; 6] = [1, 2, 840, 10045, 4, 1];
pub const OID_ECDSA_WITH_SHA256: [u64; 7] = [1, 2, 840, 10045, 4, 3, 2];
pub const OID_ECDSA_WITH_SHA384: [u64; 7] = [1, 2, 840, 10045, 4, 3, 3];
pub const OID_ECDSA_WITH_SHA512: [u64; 7] = [1, 2, 840, 10045, 4, 3, 4];
pub const OID_NAMED_CURVE_P256: [u64; 7] = [1, 2, 840, 10045, 3, 1, 7];
pub const OID_NAMED_CURVE_P384: [u64; 5] = [1, 3, 132, 0, 34];
pub const OID_NAMED_CURVE_P521: [u64; 5] = [1, 3, 132, 0, 35];
pub const OID_ATTR_MESSAGE_DIGEST: [u64; 7] = [1, 2, 840, 113549, 1, 9, 4];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HashAlg {
Sha1,
Sha256,
Sha384,
Sha512,
}
impl HashAlg {
pub fn from_oid(oid: &[u64]) -> Option<Self> {
if oid == OID_SHA1 {
Some(Self::Sha1)
} else if oid == OID_SHA256 {
Some(Self::Sha256)
} else if oid == OID_SHA384 {
Some(Self::Sha384)
} else if oid == OID_SHA512 {
Some(Self::Sha512)
} else {
None
}
}
pub fn hash(self, input: &[u8]) -> Vec<u8> {
use sha2::Digest as _;
match self {
Self::Sha1 => Sha1::digest(input).to_vec(),
Self::Sha256 => Sha256::digest(input).to_vec(),
Self::Sha384 => Sha384::digest(input).to_vec(),
Self::Sha512 => Sha512::digest(input).to_vec(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SignatureAlg {
RsaPkcs1v15,
RsaPss,
Ecdsa,
}
impl SignatureAlg {
pub fn from_oid(oid: &[u64]) -> Option<Self> {
if oid == OID_RSA_ENCRYPTION
|| oid == OID_SHA1_WITH_RSA
|| oid == OID_SHA256_WITH_RSA
|| oid == OID_SHA384_WITH_RSA
|| oid == OID_SHA512_WITH_RSA
{
Some(Self::RsaPkcs1v15)
} else if oid == OID_RSA_PSS {
Some(Self::RsaPss)
} else if oid == OID_EC_PUBLIC_KEY
|| oid == OID_ECDSA_WITH_SHA1
|| oid == OID_ECDSA_WITH_SHA256
|| oid == OID_ECDSA_WITH_SHA384
|| oid == OID_ECDSA_WITH_SHA512
{
Some(Self::Ecdsa)
} else {
None
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EcCurve {
P256,
P384,
P521,
}
impl EcCurve {
pub fn from_named_curve_params(params_der: &[u8]) -> Option<Self> {
let (oid, rest) = read_oid(params_der).ok()?;
if !rest.is_empty() {
return None;
}
if oid == OID_NAMED_CURVE_P256 {
Some(Self::P256)
} else if oid == OID_NAMED_CURVE_P384 {
Some(Self::P384)
} else if oid == OID_NAMED_CURVE_P521 {
Some(Self::P521)
} else {
None
}
}
}
pub fn resolve_signer_cert<'a>(
sid: &SignerIdentifier,
certs: &'a [Certificate],
) -> Option<&'a Certificate> {
for cert in certs {
match sid {
SignerIdentifier::IssuerAndSerial(ias) => {
if cert.issuer_der == ias.issuer_der && cert.serial == ias.serial {
return Some(cert);
}
}
SignerIdentifier::SubjectKeyIdentifier(ski) => {
if let Some(cert_ski) = cert.subject_key_identifier() {
if &cert_ski == ski {
return Some(cert);
}
}
}
}
}
None
}
pub fn signed_attrs_to_be_signed(signed_attrs_der: &[u8]) -> Vec<u8> {
write_set(signed_attrs_der)
}
pub fn message_digest_attr(attrs: &[Attribute]) -> Option<Vec<u8>> {
for a in attrs {
if a.oid == OID_ATTR_MESSAGE_DIGEST {
if let Some(v) = a.values.first() {
if let Ok((bytes, _)) = read_octet_string(v) {
return Some(bytes.to_vec());
}
}
}
}
None
}
#[derive(Debug, Clone, Copy)]
pub enum AttachedContent<'a> {
FromEContent(&'a SignedData),
External(&'a [u8]),
}
pub fn verify_signature(
signer: &SignerInfo,
certs: &[Certificate],
content: AttachedContent<'_>,
) -> Result<bool, PdfError> {
let hash = HashAlg::from_oid(&signer.digest_algorithm_oid).ok_or_else(|| {
PdfError::other(format!(
"CMS verify: unsupported digestAlgorithm OID {:?}",
signer.digest_algorithm_oid
))
})?;
let sig_alg = SignatureAlg::from_oid(&signer.signature_algorithm_oid).ok_or_else(|| {
PdfError::other(format!(
"CMS verify: unsupported signatureAlgorithm OID {:?}",
signer.signature_algorithm_oid
))
})?;
let cert = resolve_signer_cert(&signer.sid, certs).ok_or_else(|| {
PdfError::other("CMS verify: no certificate in pool matches the signer identifier")
})?;
let message_digest_to_verify: Vec<u8> = if let Some(sa_body) = &signer.signed_attrs_der {
let content_bytes = resolve_content_bytes(&content)?;
let content_hash = hash.hash(content_bytes);
let md_attr = message_digest_attr(&signer.signed_attrs).ok_or_else(|| {
PdfError::other(
"CMS verify: signed_attrs present but messageDigest attribute is missing",
)
})?;
if md_attr != content_hash {
return Ok(false);
}
let to_be_signed = signed_attrs_to_be_signed(sa_body);
hash.hash(&to_be_signed)
} else {
let content_bytes = resolve_content_bytes(&content)?;
hash.hash(content_bytes)
};
match sig_alg {
SignatureAlg::RsaPkcs1v15 => {
verify_rsa_pkcs1v15(cert, hash, &message_digest_to_verify, &signer.signature)
}
SignatureAlg::RsaPss => {
verify_rsa_pss(cert, hash, &message_digest_to_verify, &signer.signature)
}
SignatureAlg::Ecdsa => {
verify_ecdsa(cert, hash, &message_digest_to_verify, &signer.signature)
}
}
}
fn resolve_content_bytes<'a>(content: &'a AttachedContent<'a>) -> Result<&'a [u8], PdfError> {
match content {
AttachedContent::FromEContent(sd) => sd.encap_content_octets.as_deref().ok_or_else(|| {
PdfError::other("CMS verify: detached signature requires AttachedContent::External")
}),
AttachedContent::External(bytes) => Ok(bytes),
}
}
fn rsa_pubkey_from_cert(cert: &Certificate) -> Result<RsaPublicKey, PdfError> {
let spki = cert
.spki_pubkey_bits
.as_deref()
.ok_or_else(|| PdfError::other("CMS verify: signer cert SPKI bytes unavailable"))?;
use rsa::pkcs1::DecodeRsaPublicKey;
RsaPublicKey::from_pkcs1_der(spki)
.map_err(|e| PdfError::other(format!("CMS verify: RSA public key parse: {e}")))
}
fn verify_rsa_pkcs1v15(
cert: &Certificate,
hash: HashAlg,
message_digest: &[u8],
signature: &[u8],
) -> Result<bool, PdfError> {
let pubkey = rsa_pubkey_from_cert(cert)?;
let scheme: Pkcs1v15Sign = match hash {
HashAlg::Sha1 => Pkcs1v15Sign::new::<Sha1>(),
HashAlg::Sha256 => Pkcs1v15Sign::new::<Sha256>(),
HashAlg::Sha384 => Pkcs1v15Sign::new::<Sha384>(),
HashAlg::Sha512 => Pkcs1v15Sign::new::<Sha512>(),
};
Ok(scheme.verify(&pubkey, message_digest, signature).is_ok())
}
fn verify_rsa_pss(
cert: &Certificate,
hash: HashAlg,
message_digest: &[u8],
signature: &[u8],
) -> Result<bool, PdfError> {
let pubkey = rsa_pubkey_from_cert(cert)?;
let scheme = match hash {
HashAlg::Sha1 => Pss::new::<Sha1>(),
HashAlg::Sha256 => Pss::new::<Sha256>(),
HashAlg::Sha384 => Pss::new::<Sha384>(),
HashAlg::Sha512 => Pss::new::<Sha512>(),
};
Ok(scheme.verify(&pubkey, message_digest, signature).is_ok())
}
fn verify_ecdsa(
cert: &Certificate,
hash: HashAlg,
message_digest: &[u8],
signature_der: &[u8],
) -> Result<bool, PdfError> {
let params = cert.spki_algorithm_params.as_deref().ok_or_else(|| {
PdfError::other("CMS verify: ECDSA requires named-curve parameters in cert SPKI")
})?;
let curve = EcCurve::from_named_curve_params(params)
.ok_or_else(|| PdfError::other("CMS verify: ECDSA cert names an unsupported curve"))?;
let spki = cert
.spki_pubkey_bits
.as_deref()
.ok_or_else(|| PdfError::other("CMS verify: signer cert SPKI bytes unavailable"))?;
let _ = (hash, message_digest);
match curve {
EcCurve::P256 => verify_ecdsa_p256(spki, message_digest, signature_der),
EcCurve::P384 => verify_ecdsa_p384(spki, message_digest, signature_der),
EcCurve::P521 => verify_ecdsa_p521(spki, message_digest, signature_der),
}
}
fn verify_ecdsa_p256(spki: &[u8], digest: &[u8], sig_der: &[u8]) -> Result<bool, PdfError> {
use p256::ecdsa::{Signature, VerifyingKey};
use rsa::signature::hazmat::PrehashVerifier;
let vk = match VerifyingKey::from_sec1_bytes(spki) {
Ok(v) => v,
Err(_) => return Ok(false),
};
let sig = match Signature::from_der(sig_der) {
Ok(s) => s,
Err(_) => return Ok(false),
};
Ok(vk.verify_prehash(digest, &sig).is_ok())
}
fn verify_ecdsa_p384(spki: &[u8], digest: &[u8], sig_der: &[u8]) -> Result<bool, PdfError> {
use p384::ecdsa::{Signature, VerifyingKey};
use rsa::signature::hazmat::PrehashVerifier;
let vk = match VerifyingKey::from_sec1_bytes(spki) {
Ok(v) => v,
Err(_) => return Ok(false),
};
let sig = match Signature::from_der(sig_der) {
Ok(s) => s,
Err(_) => return Ok(false),
};
Ok(vk.verify_prehash(digest, &sig).is_ok())
}
fn verify_ecdsa_p521(spki: &[u8], digest: &[u8], sig_der: &[u8]) -> Result<bool, PdfError> {
use p521::ecdsa::{Signature, VerifyingKey};
use rsa::signature::hazmat::PrehashVerifier;
let vk = match VerifyingKey::from_sec1_bytes(spki) {
Ok(v) => v,
Err(_) => return Ok(false),
};
let sig = match Signature::from_der(sig_der) {
Ok(s) => s,
Err(_) => return Ok(false),
};
Ok(vk.verify_prehash(digest, &sig).is_ok())
}
pub fn build_message_digest_attribute_der(content_hash: &[u8]) -> Vec<u8> {
use super::der::{write_octet_string, write_oid, write_sequence};
let oid = write_oid(&OID_ATTR_MESSAGE_DIGEST);
let value = write_octet_string(content_hash);
let value_set = write_set(&value);
let mut body = oid;
body.extend_from_slice(&value_set);
write_sequence(&body)
}
pub fn pack_signed_attrs_implicit(attrs_der: &[Vec<u8>]) -> Vec<u8> {
let mut body = Vec::new();
for a in attrs_der {
body.extend_from_slice(a);
}
body
}
pub fn implicit_signed_attrs_tlv(attrs_body: &[u8]) -> Vec<u8> {
write_tlv(Class::ContextSpecific, true, 0, attrs_body)
}
pub fn rsa_pubkey_to_pkcs1_der(pubkey: &RsaPublicKey) -> Vec<u8> {
use rsa::pkcs1::EncodeRsaPublicKey;
pubkey
.to_pkcs1_der()
.expect("RSA public-key PKCS#1 encode infallible")
.as_bytes()
.to_vec()
}
pub fn parse_rsa_pubkey_pkcs1(der: &[u8]) -> Result<RsaPublicKey, PdfError> {
use rsa::pkcs1::DecodeRsaPublicKey;
RsaPublicKey::from_pkcs1_der(der)
.map_err(|e| PdfError::other(format!("RSA pubkey decode: {e}")))
}
pub fn rsa_pubkey_components(pubkey: &RsaPublicKey) -> (Vec<u8>, Vec<u8>) {
use rsa::traits::PublicKeyParts;
let n = pubkey.n();
let e = pubkey.e();
(n.to_bytes_be(), e.to_bytes_be())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pubsec::cms::IssuerAndSerial;
use crate::pubsec::der;
use crate::pubsec::signed_data::SignerIdentifier;
fn fake_rsa_cert(issuer_der: Vec<u8>, serial: Vec<u8>, rsa_pkcs1_der: Vec<u8>) -> Certificate {
Certificate {
issuer_der,
serial,
spki_pubkey_bits: Some(rsa_pkcs1_der),
spki_algorithm_oid: Some(OID_RSA_ENCRYPTION.to_vec()),
spki_algorithm_params: Some(der::write_null()),
..Default::default()
}
}
fn fake_ec_cert(
issuer_der: Vec<u8>,
serial: Vec<u8>,
sec1_pubkey: Vec<u8>,
named_curve_oid: &[u64],
) -> Certificate {
Certificate {
issuer_der,
serial,
spki_pubkey_bits: Some(sec1_pubkey),
spki_algorithm_oid: Some(OID_EC_PUBLIC_KEY.to_vec()),
spki_algorithm_params: Some(der::write_oid(named_curve_oid)),
..Default::default()
}
}
fn ias_signer(issuer_der: &[u8], serial: &[u8]) -> SignerIdentifier {
SignerIdentifier::IssuerAndSerial(IssuerAndSerial {
issuer_der: issuer_der.to_vec(),
serial: serial.to_vec(),
})
}
#[test]
fn hash_alg_dispatch_round_trip() {
assert_eq!(HashAlg::from_oid(&OID_SHA1), Some(HashAlg::Sha1));
assert_eq!(HashAlg::from_oid(&OID_SHA256), Some(HashAlg::Sha256));
assert_eq!(HashAlg::from_oid(&OID_SHA384), Some(HashAlg::Sha384));
assert_eq!(HashAlg::from_oid(&OID_SHA512), Some(HashAlg::Sha512));
assert!(HashAlg::from_oid(&[1, 2, 3, 4]).is_none());
}
#[test]
fn signed_attrs_to_be_signed_replaces_implicit_tag() {
let body = b"--ATTR-SET-BODY--";
let out = signed_attrs_to_be_signed(body);
assert_eq!(out[0], 0x31, "leading byte should be universal SET");
assert!(out.windows(body.len()).any(|w| w == &body[..]));
}
fn rsa_sign_pkcs1v15_sha256(priv_key: &rsa::RsaPrivateKey, msg_digest: &[u8]) -> Vec<u8> {
let scheme: Pkcs1v15Sign = Pkcs1v15Sign::new::<Sha256>();
scheme
.sign(None::<&mut rsa::rand_core::OsRng>, priv_key, msg_digest)
.expect("RSA-PKCS1v15 sign")
}
#[test]
fn rsa_pkcs1v15_sha256_with_signed_attrs_verifies() {
let mut rng = rsa::rand_core::OsRng;
let priv_key = rsa::RsaPrivateKey::new(&mut rng, 2048).expect("RSA");
let pub_key = rsa::RsaPublicKey::from(&priv_key);
let issuer_der = der::write_sequence(b"O=Round-20 RSA Signer");
let serial = vec![0x42, 0x01];
let pubkey_pkcs1 = rsa_pubkey_to_pkcs1_der(&pub_key);
let cert = fake_rsa_cert(issuer_der.clone(), serial.clone(), pubkey_pkcs1);
let payload = b"OXIDEAV-ROUND-20-CONTENT-PAYLOAD";
let content_hash = HashAlg::Sha256.hash(payload);
let md_attr = build_message_digest_attribute_der(&content_hash);
let attrs_body = pack_signed_attrs_implicit(&[md_attr]);
let to_be_signed = signed_attrs_to_be_signed(&attrs_body);
let tbs_hash = HashAlg::Sha256.hash(&to_be_signed);
let signature = rsa_sign_pkcs1v15_sha256(&priv_key, &tbs_hash);
let signer = SignerInfo {
version: 1,
sid: ias_signer(&issuer_der, &serial),
digest_algorithm_oid: OID_SHA256.to_vec(),
digest_algorithm_params: Vec::new(),
signed_attrs: vec![Attribute {
oid: OID_ATTR_MESSAGE_DIGEST.to_vec(),
values: vec![der::write_octet_string(&content_hash)],
}],
signed_attrs_der: Some(attrs_body),
signature_algorithm_oid: OID_RSA_ENCRYPTION.to_vec(),
signature_algorithm_params: Vec::new(),
signature,
unsigned_attrs: Vec::new(),
};
let ok = verify_signature(&signer, &[cert], AttachedContent::External(payload))
.expect("verify dispatch");
assert!(
ok,
"RSA-PKCS1v15 + SHA-256 verifier should accept signature"
);
}
#[test]
fn rsa_pkcs1v15_sha256_no_signed_attrs_verifies() {
let mut rng = rsa::rand_core::OsRng;
let priv_key = rsa::RsaPrivateKey::new(&mut rng, 2048).expect("RSA");
let pub_key = rsa::RsaPublicKey::from(&priv_key);
let issuer_der = der::write_sequence(b"O=Round-20 No-Attr");
let serial = vec![0x77];
let pubkey_pkcs1 = rsa_pubkey_to_pkcs1_der(&pub_key);
let cert = fake_rsa_cert(issuer_der.clone(), serial.clone(), pubkey_pkcs1);
let payload = b"NO-ATTR-PAYLOAD";
let payload_hash = HashAlg::Sha256.hash(payload);
let signature = rsa_sign_pkcs1v15_sha256(&priv_key, &payload_hash);
let signer = SignerInfo {
version: 1,
sid: ias_signer(&issuer_der, &serial),
digest_algorithm_oid: OID_SHA256.to_vec(),
digest_algorithm_params: Vec::new(),
signed_attrs: Vec::new(),
signed_attrs_der: None,
signature_algorithm_oid: OID_RSA_ENCRYPTION.to_vec(),
signature_algorithm_params: Vec::new(),
signature,
unsigned_attrs: Vec::new(),
};
let ok = verify_signature(&signer, &[cert], AttachedContent::External(payload))
.expect("verify dispatch");
assert!(ok);
}
#[test]
fn rsa_pkcs1v15_tampered_content_fails_via_message_digest_check() {
let mut rng = rsa::rand_core::OsRng;
let priv_key = rsa::RsaPrivateKey::new(&mut rng, 2048).expect("RSA");
let pub_key = rsa::RsaPublicKey::from(&priv_key);
let issuer_der = der::write_sequence(b"O=Tamper-RSA");
let serial = vec![0x66];
let cert = fake_rsa_cert(
issuer_der.clone(),
serial.clone(),
rsa_pubkey_to_pkcs1_der(&pub_key),
);
let payload = b"ORIGINAL-CONTENT";
let content_hash = HashAlg::Sha256.hash(payload);
let md_attr = build_message_digest_attribute_der(&content_hash);
let attrs_body = pack_signed_attrs_implicit(&[md_attr]);
let tbs_hash = HashAlg::Sha256.hash(&signed_attrs_to_be_signed(&attrs_body));
let signature = rsa_sign_pkcs1v15_sha256(&priv_key, &tbs_hash);
let signer = SignerInfo {
version: 1,
sid: ias_signer(&issuer_der, &serial),
digest_algorithm_oid: OID_SHA256.to_vec(),
digest_algorithm_params: Vec::new(),
signed_attrs: vec![Attribute {
oid: OID_ATTR_MESSAGE_DIGEST.to_vec(),
values: vec![der::write_octet_string(&content_hash)],
}],
signed_attrs_der: Some(attrs_body),
signature_algorithm_oid: OID_RSA_ENCRYPTION.to_vec(),
signature_algorithm_params: Vec::new(),
signature,
unsigned_attrs: Vec::new(),
};
let tampered = b"TAMPERED-CONTENT";
let ok = verify_signature(&signer, &[cert], AttachedContent::External(tampered))
.expect("verify dispatch");
assert!(!ok, "tampered content must fail messageDigest check");
}
#[test]
fn rsa_pkcs1v15_tampered_signature_byte_fails() {
let mut rng = rsa::rand_core::OsRng;
let priv_key = rsa::RsaPrivateKey::new(&mut rng, 2048).expect("RSA");
let pub_key = rsa::RsaPublicKey::from(&priv_key);
let issuer_der = der::write_sequence(b"O=Bit-Flip");
let serial = vec![0x55];
let cert = fake_rsa_cert(
issuer_der.clone(),
serial.clone(),
rsa_pubkey_to_pkcs1_der(&pub_key),
);
let payload = b"OK-CONTENT";
let payload_hash = HashAlg::Sha256.hash(payload);
let mut signature = rsa_sign_pkcs1v15_sha256(&priv_key, &payload_hash);
signature[0] ^= 0x01;
let signer = SignerInfo {
version: 1,
sid: ias_signer(&issuer_der, &serial),
digest_algorithm_oid: OID_SHA256.to_vec(),
digest_algorithm_params: Vec::new(),
signed_attrs: Vec::new(),
signed_attrs_der: None,
signature_algorithm_oid: OID_RSA_ENCRYPTION.to_vec(),
signature_algorithm_params: Vec::new(),
signature,
unsigned_attrs: Vec::new(),
};
let ok = verify_signature(&signer, &[cert], AttachedContent::External(payload))
.expect("verify dispatch");
assert!(!ok, "single-bit-flipped signature must not verify");
}
#[test]
fn cert_not_in_pool_returns_error() {
let mut rng = rsa::rand_core::OsRng;
let priv_key = rsa::RsaPrivateKey::new(&mut rng, 2048).expect("RSA");
let pub_key = rsa::RsaPublicKey::from(&priv_key);
let payload = b"some-content";
let payload_hash = HashAlg::Sha256.hash(payload);
let signature = rsa_sign_pkcs1v15_sha256(&priv_key, &payload_hash);
let cert = fake_rsa_cert(
der::write_sequence(b"O=NotInPool"),
vec![0xAA],
rsa_pubkey_to_pkcs1_der(&pub_key),
);
let signer = SignerInfo {
version: 1,
sid: ias_signer(b"O=DifferentIssuer", &[0xBB]),
digest_algorithm_oid: OID_SHA256.to_vec(),
digest_algorithm_params: Vec::new(),
signed_attrs: Vec::new(),
signed_attrs_der: None,
signature_algorithm_oid: OID_RSA_ENCRYPTION.to_vec(),
signature_algorithm_params: Vec::new(),
signature,
unsigned_attrs: Vec::new(),
};
let err = verify_signature(&signer, &[cert], AttachedContent::External(payload))
.expect_err("must error when cert not found");
assert!(format!("{err}").contains("no certificate"));
}
#[test]
fn rsa_pss_sha256_verifies() {
let mut rng = rsa::rand_core::OsRng;
let priv_key = rsa::RsaPrivateKey::new(&mut rng, 2048).expect("RSA");
let pub_key = rsa::RsaPublicKey::from(&priv_key);
let issuer_der = der::write_sequence(b"O=Round-20 PSS");
let serial = vec![0x12];
let cert = fake_rsa_cert(
issuer_der.clone(),
serial.clone(),
rsa_pubkey_to_pkcs1_der(&pub_key),
);
let payload = b"PSS-CONTENT";
let payload_hash = HashAlg::Sha256.hash(payload);
let scheme = Pss::new::<Sha256>();
let signature = scheme
.sign(Some(&mut rng), &priv_key, &payload_hash)
.expect("RSA-PSS sign");
let signer = SignerInfo {
version: 1,
sid: ias_signer(&issuer_der, &serial),
digest_algorithm_oid: OID_SHA256.to_vec(),
digest_algorithm_params: Vec::new(),
signed_attrs: Vec::new(),
signed_attrs_der: None,
signature_algorithm_oid: OID_RSA_PSS.to_vec(),
signature_algorithm_params: Vec::new(),
signature,
unsigned_attrs: Vec::new(),
};
let ok = verify_signature(&signer, &[cert], AttachedContent::External(payload))
.expect("dispatch");
assert!(ok);
}
#[test]
fn ecdsa_p256_sha256_verifies() {
use p256::ecdsa::signature::Signer as _;
use p256::ecdsa::{Signature, SigningKey};
let scalar = [0x21u8; 32];
let signing_key = SigningKey::from_slice(&scalar).expect("scalar");
let verifying_key = signing_key.verifying_key();
let pub_sec1 = verifying_key.to_encoded_point(false).as_bytes().to_vec();
let issuer_der = der::write_sequence(b"O=Round-20 P-256");
let serial = vec![0xC0, 0xFE];
let cert = fake_ec_cert(
issuer_der.clone(),
serial.clone(),
pub_sec1,
&OID_NAMED_CURVE_P256,
);
let payload = b"OXIDEAV-ECDSA-P256-PAYLOAD";
let sig: Signature = signing_key.sign(payload);
let sig_der = sig.to_der().as_bytes().to_vec();
let signer = SignerInfo {
version: 1,
sid: ias_signer(&issuer_der, &serial),
digest_algorithm_oid: OID_SHA256.to_vec(),
digest_algorithm_params: Vec::new(),
signed_attrs: Vec::new(),
signed_attrs_der: None,
signature_algorithm_oid: OID_ECDSA_WITH_SHA256.to_vec(),
signature_algorithm_params: Vec::new(),
signature: sig_der,
unsigned_attrs: Vec::new(),
};
let ok = verify_signature(&signer, &[cert], AttachedContent::External(payload))
.expect("dispatch");
assert!(ok);
}
#[test]
fn ecdsa_p384_sha384_verifies() {
use p384::ecdsa::signature::Signer as _;
use p384::ecdsa::{Signature, SigningKey};
let scalar = [0x35u8; 48];
let signing_key = SigningKey::from_slice(&scalar).expect("scalar");
let verifying_key = signing_key.verifying_key();
let pub_sec1 = verifying_key.to_encoded_point(false).as_bytes().to_vec();
let issuer_der = der::write_sequence(b"O=Round-20 P-384");
let serial = vec![0xCA, 0xFE];
let cert = fake_ec_cert(
issuer_der.clone(),
serial.clone(),
pub_sec1,
&OID_NAMED_CURVE_P384,
);
let payload = b"OXIDEAV-ECDSA-P384-PAYLOAD";
let sig: Signature = signing_key.sign(payload);
let sig_der = sig.to_der().as_bytes().to_vec();
let signer = SignerInfo {
version: 1,
sid: ias_signer(&issuer_der, &serial),
digest_algorithm_oid: OID_SHA384.to_vec(),
digest_algorithm_params: Vec::new(),
signed_attrs: Vec::new(),
signed_attrs_der: None,
signature_algorithm_oid: OID_ECDSA_WITH_SHA384.to_vec(),
signature_algorithm_params: Vec::new(),
signature: sig_der,
unsigned_attrs: Vec::new(),
};
let ok = verify_signature(&signer, &[cert], AttachedContent::External(payload))
.expect("dispatch");
assert!(ok);
}
#[test]
fn ecdsa_p521_sha512_verifies() {
use p521::ecdsa::signature::Signer as _;
use p521::ecdsa::{Signature, SigningKey, VerifyingKey};
let mut scalar = [0u8; 66];
scalar[1] = 0x01;
scalar[65] = 0x42;
let signing_key = SigningKey::from_slice(&scalar).expect("scalar");
let verifying_key = VerifyingKey::from(&signing_key);
let pub_sec1 = verifying_key.to_encoded_point(false).as_bytes().to_vec();
let issuer_der = der::write_sequence(b"O=Round-20 P-521");
let serial = vec![0x52, 0x21];
let cert = fake_ec_cert(
issuer_der.clone(),
serial.clone(),
pub_sec1,
&OID_NAMED_CURVE_P521,
);
let payload = b"OXIDEAV-ECDSA-P521-PAYLOAD";
let sig: Signature = signing_key.sign(payload);
let sig_der = sig.to_der().as_bytes().to_vec();
let signer = SignerInfo {
version: 1,
sid: ias_signer(&issuer_der, &serial),
digest_algorithm_oid: OID_SHA512.to_vec(),
digest_algorithm_params: Vec::new(),
signed_attrs: Vec::new(),
signed_attrs_der: None,
signature_algorithm_oid: OID_ECDSA_WITH_SHA512.to_vec(),
signature_algorithm_params: Vec::new(),
signature: sig_der,
unsigned_attrs: Vec::new(),
};
let ok = verify_signature(&signer, &[cert], AttachedContent::External(payload))
.expect("dispatch");
assert!(ok);
}
#[test]
fn ecdsa_p256_tampered_signature_fails() {
use p256::ecdsa::signature::Signer as _;
use p256::ecdsa::{Signature, SigningKey};
let scalar = [0x21u8; 32];
let signing_key = SigningKey::from_slice(&scalar).expect("scalar");
let pub_sec1 = signing_key
.verifying_key()
.to_encoded_point(false)
.as_bytes()
.to_vec();
let issuer_der = der::write_sequence(b"O=Tamper-EC");
let serial = vec![0x99];
let cert = fake_ec_cert(
issuer_der.clone(),
serial.clone(),
pub_sec1,
&OID_NAMED_CURVE_P256,
);
let payload = b"good-content";
let sig: Signature = signing_key.sign(payload);
let mut sig_der = sig.to_der().as_bytes().to_vec();
sig_der[6] ^= 0xFF;
let signer = SignerInfo {
version: 1,
sid: ias_signer(&issuer_der, &serial),
digest_algorithm_oid: OID_SHA256.to_vec(),
digest_algorithm_params: Vec::new(),
signed_attrs: Vec::new(),
signed_attrs_der: None,
signature_algorithm_oid: OID_ECDSA_WITH_SHA256.to_vec(),
signature_algorithm_params: Vec::new(),
signature: sig_der,
unsigned_attrs: Vec::new(),
};
let ok = verify_signature(&signer, &[cert], AttachedContent::External(payload))
.expect("dispatch");
assert!(!ok);
}
#[test]
fn ski_resolution_picks_matching_cert() {
use sha1::Digest;
let pubkey_bits = b"OXIDEAV-PUBSEC-VERIFY-SKI-BITS-OK".to_vec();
let ski = sha1::Sha1::digest(&pubkey_bits).to_vec();
let cert = Certificate {
issuer_der: der::write_sequence(b"O=SKI-Cert"),
serial: vec![0x01],
spki_pubkey_bits: Some(pubkey_bits),
spki_algorithm_oid: Some(OID_RSA_ENCRYPTION.to_vec()),
spki_algorithm_params: Some(der::write_null()),
..Default::default()
};
let sid = SignerIdentifier::SubjectKeyIdentifier(ski.clone());
let pool = vec![cert.clone()];
let resolved = resolve_signer_cert(&sid, &pool).expect("found");
assert_eq!(resolved.serial, cert.serial);
let sid2 = SignerIdentifier::SubjectKeyIdentifier(vec![0u8; 20]);
let pool2 = vec![cert];
assert!(resolve_signer_cert(&sid2, &pool2).is_none());
}
}