RustCrypto / formats

Cryptography-related format encoders/decoders: DER, PEM, PKCS, PKIX
236 stars 126 forks source link

x509-cert: certificate verification support #838

Open dayday2019 opened 1 year ago

tarcieri commented 1 year ago

We donโ€™t implement this yet, sorry. The x509-cert crate is still a work in progress

dayday2019 commented 1 year ago

Thank you for your reply! Will you implement this in a near future? I am looking forward to it

tarcieri commented 1 year ago

You can try out @carl-wallace's crate which implements certificate verification:

https://github.com/carl-wallace/rust-pki/tree/main/certval

Hopefully we can get such work upstream soon

lumag commented 1 year ago

@dayday2019 a very limited implementation. Hope it can be helpful. License: Apache-2 + MIT

Note: it doesn't perform path validation, etc. Just signature checks.

pub fn verify_signature(
    cert: &Certificate,
    signed_data: &[u8],
    signature: &[u8],
    algo: &AlgorithmIdentifierOwned,
) -> Result<(), Box<dyn Error>> {
    let spki = cert.tbs_certificate.subject_public_key_info.owned_to_ref();

    match algo.oid {
        OIDdb::rfc5912::SHA_1_WITH_RSA_ENCRYPTION => {
            println!("PKCS#1 v1.5 / SHA1 signature");
            rsa::pkcs1v15::VerifyingKey::<Sha1>::new(RsaPublicKey::try_from(spki)?)
                .verify(signed_data, &signature.try_into()?)?;
        }

        OIDdb::rfc5912::SHA_256_WITH_RSA_ENCRYPTION => {
            println!("PKCS#1 v1.5 / SHA256 signature");
            rsa::pkcs1v15::VerifyingKey::<Sha256>::new(RsaPublicKey::try_from(spki)?)
                .verify(signed_data, &signature.try_into()?)?;
        }

        OIDdb::rfc5912::ID_RSASSA_PSS => {
            let params = algo
                .parameters
                .as_ref()
                .ok_or("Empty PSS parameters")?
                .decode_as::<RsaPssParams>()?;

            match params.hash.oid {
                OIDdb::rfc5912::ID_SHA_256 => {
                    println!("PSS / SHA256 signature");

                    rsa::pss::VerifyingKey::<Sha256>::new(RsaPublicKey::try_from(spki)?)
                        .verify(signed_data, &signature.try_into()?)?
                }
                OIDdb::rfc5912::ID_SHA_1 => {
                    println!("PSS / SHA1 signature");

                    rsa::pss::VerifyingKey::<Sha1>::new(RsaPublicKey::try_from(spki)?)
                        .verify(signed_data, &signature.try_into()?)?
                }
                _ => return Err(format!("Unknown PSS hash algo {}", params.hash.oid).into()),
            }
        }

        OIDdb::rfc5912::ECDSA_WITH_SHA_256 => {
            println!("ECDSA P256 signature");

            let signature = p256::ecdsa::DerSignature::try_from(signature)?;

            p256::ecdsa::VerifyingKey::try_from(spki)?.verify(signed_data, &signature)?;
        }

        OIDdb::rfc5912::ECDSA_WITH_SHA_384 => {
            println!("ECDSA P384 signature");

            let signature = p384::ecdsa::DerSignature::try_from(signature)?;

            p384::ecdsa::VerifyingKey::try_from(spki)?.verify(signed_data, &signature)?;
        }
        _ => {
            return Err(format!(
                "Unknown signature algo {}",
                cert.tbs_certificate.signature.oid
            )
            .into())
        }
    }

    Ok(())
}

pub fn verify_cert_signature(
    cert: &Certificate,
    signed: &Certificate,
) -> Result<(), Box<dyn Error>> {
    if cert.tbs_certificate.subject != signed.tbs_certificate.issuer {
        return Err("Certificate issuer does not match".into());
    }

    let signed_data = signed.tbs_certificate.to_der()?;
    let signature = signed
        .signature
        .as_bytes()
        .ok_or("Could not get cert signature")?;

    verify_signature(cert, &signed_data, signature, &signed.signature_algorithm)
}
woodruffw commented 4 months ago

Tying a few related threads together: PyCA Cryptography (i.e. pip install cryptography) recently merged a pure-Rust X.509 path validator, which we've tested pretty extensively against both other implementations and well-known pathological cases.

The code of that implementation is 100% pure Rust (with a trait abstraction for any backend to provide the crypto), but is tied to PyCA Cryptography's own ASN.1/DER and X.509 libraries. Still, the core approach may be instructive/valuable/reusable for any other Rust implementation ๐Ÿ™‚

The code is here: https://github.com/pyca/cryptography/tree/main/src/rust/cryptography-x509-verification/src, and can be followed top-down from verify in lib.rs.

(I'm happy to answer questions about the implementation as well! Feel free to ping me here or on the RustCrypto Zulip ๐Ÿ™‚)

tarcieri commented 4 months ago

@woodruffw x509-limbo seems interesting for testing!