diff --git a/src/rust/cryptography-x509-validation/src/certificate.rs b/src/rust/cryptography-x509-validation/src/certificate.rs new file mode 100644 index 000000000000..8aa65a4a8ac8 --- /dev/null +++ b/src/rust/cryptography-x509-validation/src/certificate.rs @@ -0,0 +1,109 @@ +// This file is dual licensed under the terms of the Apache License, Version +// 2.0, and the BSD License. See the LICENSE file in the root of this repository +// for complete details. + +//! Validation-specific certificate functionality. + +use cryptography_x509::certificate::Certificate; + +use crate::ops::CryptoOps; + +// TODO: Remove these attributes once we start using these helpers. +#[allow(dead_code)] +pub(crate) fn cert_is_self_issued(cert: &Certificate<'_>) -> bool { + cert.issuer() == cert.subject() +} + +#[allow(dead_code)] +pub(crate) fn cert_is_self_signed(cert: &Certificate<'_>, ops: &B) -> bool { + match ops.public_key(cert) { + Ok(pk) => cert_is_self_issued(cert) && ops.verify_signed_by(cert, pk).is_ok(), + Err(_) => false, + } +} + +#[cfg(test)] +mod tests { + use crate::certificate::Certificate; + use crate::ops::tests::{cert, v1_cert_pem, NullOps}; + use crate::ops::CryptoOps; + + use super::{cert_is_self_issued, cert_is_self_signed}; + + #[test] + fn test_certificate_v1() { + let cert_pem = v1_cert_pem(); + let cert = cert(&cert_pem); + let ops = NullOps {}; + + assert!(!cert_is_self_issued(&cert)); + assert!(!cert_is_self_signed(&cert, &ops)); + } + + fn ca_pem() -> pem::Pem { + // From vectors/cryptography_vectors/x509/custom/ca/ca.pem + pem::parse( + "-----BEGIN CERTIFICATE----- +MIIBUTCB96ADAgECAgIDCTAKBggqhkjOPQQDAjAnMQswCQYDVQQGEwJVUzEYMBYG +A1UEAwwPY3J5cHRvZ3JhcGh5IENBMB4XDTE3MDEwMTEyMDEwMFoXDTM4MTIzMTA4 +MzAwMFowJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeSBDQTBZ +MBMGByqGSM49AgEGCCqGSM49AwEHA0IABBj/z7v5Obj13cPuwECLBnUGq0/N2CxS +JE4f4BBGZ7VfFblivTvPDG++Gve0oQ+0uctuhrNQ+WxRv8GC177F+QWjEzARMA8G +A1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhANES742XWm64tkGnz8Dn +pG6u2lHkZFQr3oaVvPcemvlbAiEA0WGGzmYx5C9UvfXIK7NEziT4pQtyESE0uRVK +Xw4nMqk= +-----END CERTIFICATE-----", + ) + .unwrap() + } + + #[test] + fn test_certificate_ca() { + let cert_pem = ca_pem(); + let cert = cert(&cert_pem); + let ops = NullOps {}; + + assert!(cert_is_self_issued(&cert)); + assert!(cert_is_self_signed(&cert, &ops)); + } + + struct PublicKeyErrorOps {} + impl CryptoOps for PublicKeyErrorOps { + type Key = (); + type Err = (); + + fn public_key(&self, _cert: &Certificate<'_>) -> Result { + // Simulate failing to retrieve a public key. + Err(()) + } + + fn verify_signed_by( + &self, + _cert: &Certificate<'_>, + _key: Self::Key, + ) -> Result<(), Self::Err> { + Ok(()) + } + } + + #[test] + fn test_certificate_public_key_error() { + let cert_pem = ca_pem(); + let cert = cert(&cert_pem); + let ops = PublicKeyErrorOps {}; + + assert!(cert_is_self_issued(&cert)); + assert!(!cert_is_self_signed(&cert, &ops)); + } + + #[test] + fn test_certificate_public_key_error_ops() { + // Just to get coverage on the `PublicKeyErrorOps` helper. + let cert_pem = ca_pem(); + let cert = cert(&cert_pem); + let ops = PublicKeyErrorOps {}; + + assert!(ops.public_key(&cert).is_err()); + assert!(ops.verify_signed_by(&cert, ()).is_ok()); + } +} diff --git a/src/rust/cryptography-x509-validation/src/lib.rs b/src/rust/cryptography-x509-validation/src/lib.rs index 972f357fd4c2..db654a547540 100644 --- a/src/rust/cryptography-x509-validation/src/lib.rs +++ b/src/rust/cryptography-x509-validation/src/lib.rs @@ -5,6 +5,7 @@ #![forbid(unsafe_code)] #![deny(rust_2018_idioms, clippy::undocumented_unsafe_blocks)] +pub mod certificate; pub mod ops; pub mod policy; pub mod trust_store;