formats icon indicating copy to clipboard operation
formats copied to clipboard

x509-cert: certificate verification support

Open dayday2019 opened this issue 2 years ago • 8 comments

dayday2019 avatar Jan 04 '23 08:01 dayday2019

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

tarcieri avatar Jan 04 '23 15:01 tarcieri

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

dayday2019 avatar Jan 04 '23 17:01 dayday2019

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

tarcieri avatar Jan 04 '23 17:01 tarcieri

@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)
}

lumag avatar Apr 16 '23 12:04 lumag

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 🙂)

woodruffw avatar Apr 26 '24 20:04 woodruffw

@woodruffw x509-limbo seems interesting for testing!

tarcieri avatar Apr 26 '24 20:04 tarcieri