diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index a64524cbe..9e6eba337 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -50,6 +50,35 @@ pub mod store; #[cfg(test)] mod tests; +#[cfg(any(ossl110, boringssl))] +bitflags::bitflags! { + /// KeyUsage bitset + /// + /// Refer to KeyUsage extension for details and meaning of every flag + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[repr(transparent)] + pub struct X509KeyUsage: u32 { + #[allow(clippy::unnecessary_cast)] + const DIGITAL_SIGNATURE = ffi::X509v3_KU_DIGITAL_SIGNATURE as u32; + #[allow(clippy::unnecessary_cast)] + const NON_REPUDIATION = ffi::X509v3_KU_NON_REPUDIATION as u32; + #[allow(clippy::unnecessary_cast)] + const KEY_ENCIPHERMENT = ffi::X509v3_KU_KEY_ENCIPHERMENT as u32; + #[allow(clippy::unnecessary_cast)] + const DATA_ENCIPHERMENT = ffi::X509v3_KU_DATA_ENCIPHERMENT as u32; + #[allow(clippy::unnecessary_cast)] + const KEY_AGREEMENT = ffi::X509v3_KU_KEY_AGREEMENT as u32; + #[allow(clippy::unnecessary_cast)] + const KEY_CERT_SIGN = ffi::X509v3_KU_KEY_CERT_SIGN as u32; + #[allow(clippy::unnecessary_cast)] + const CRL_SIGN = ffi::X509v3_KU_CRL_SIGN as u32; + #[allow(clippy::unnecessary_cast)] + const ENCIPHER_ONLY = ffi::X509v3_KU_ENCIPHER_ONLY as u32; + #[allow(clippy::unnecessary_cast)] + const DECIPHER_ONLY = ffi::X509v3_KU_DECIPHER_ONLY as u32; + } +} + /// A type of X509 extension. /// /// # Safety @@ -667,6 +696,19 @@ impl X509Ref { } } + #[cfg(any(ossl110, boringssl))] + /// Retrieves set of basic key usage flags within certificate + #[corresponds(X509_get_key_usage)] + pub fn key_usage(&self) -> X509KeyUsage { + let res = unsafe { ffi::X509_get_key_usage(self.as_ptr()) }; + //u32::MAX indicates key usage is not set + if res == u32::MAX { + X509KeyUsage::empty() + } else { + X509KeyUsage::from_bits_retain(res) + } + } + to_pem! { /// Serializes the certificate into a PEM-encoded X509 structure. /// diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index ae61a2ad3..6db5c236a 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -1192,3 +1192,114 @@ fn test_store_all_certificates() { assert_eq!(store.all_certificates().len(), 1); } + +#[cfg(any(ossl110, boringssl))] +#[test] +fn should_get_x509_key_usage() { + use crate::x509::X509KeyUsage; + + let pkey = pkey(); + + let mut name = X509Name::builder().unwrap(); + name.append_entry_by_nid(Nid::COMMONNAME, "key_usage.com") + .unwrap(); + let name = name.build(); + + let mut builder = X509::builder().unwrap(); + builder.set_version(2).unwrap(); + builder.set_subject_name(&name).unwrap(); + builder.set_issuer_name(&name).unwrap(); + builder + .set_not_before(&Asn1Time::days_from_now(0).unwrap()) + .unwrap(); + builder + .set_not_after(&Asn1Time::days_from_now(365).unwrap()) + .unwrap(); + builder.set_pubkey(&pkey).unwrap(); + + let mut serial = BigNum::new().unwrap(); + serial.rand(128, MsbOption::MAYBE_ZERO, false).unwrap(); + builder + .set_serial_number(&serial.to_asn1_integer().unwrap()) + .unwrap(); + + let key_usage = KeyUsage::new() + .digital_signature() + .key_encipherment() + .build() + .unwrap(); + builder.append_extension(key_usage).unwrap(); + builder.sign(&pkey, MessageDigest::sha256()).unwrap(); + + let x509 = builder.build(); + + assert!(pkey.public_eq(&x509.public_key().unwrap())); + assert!(x509.verify(&pkey).unwrap()); + + let cn = x509 + .subject_name() + .entries_by_nid(Nid::COMMONNAME) + .next() + .unwrap(); + assert_eq!(cn.data().as_slice(), b"key_usage.com"); + + let usage = x509.key_usage(); + assert!(usage.contains(X509KeyUsage::DIGITAL_SIGNATURE)); + assert!(usage.contains(X509KeyUsage::KEY_ENCIPHERMENT)); + assert!(!usage.contains(X509KeyUsage::CRL_SIGN)); + assert!(!usage.contains(X509KeyUsage::NON_REPUDIATION)); + assert!(!usage.contains(X509KeyUsage::DATA_ENCIPHERMENT)); + assert!(!usage.contains(X509KeyUsage::KEY_AGREEMENT)); + assert!(!usage.contains(X509KeyUsage::KEY_CERT_SIGN)); + assert!(!usage.contains(X509KeyUsage::CRL_SIGN)); + assert!(!usage.contains(X509KeyUsage::ENCIPHER_ONLY)); + assert!(!usage.contains(X509KeyUsage::DECIPHER_ONLY)); +} + +#[cfg(any(ossl110, boringssl))] +#[test] +fn should_get_x509_key_usage_when_no_set() { + use crate::x509::X509KeyUsage; + + let pkey = pkey(); + + let mut name = X509Name::builder().unwrap(); + name.append_entry_by_nid(Nid::COMMONNAME, "key_usage.com") + .unwrap(); + let name = name.build(); + + let mut builder = X509::builder().unwrap(); + builder.set_version(2).unwrap(); + builder.set_subject_name(&name).unwrap(); + builder.set_issuer_name(&name).unwrap(); + builder + .set_not_before(&Asn1Time::days_from_now(0).unwrap()) + .unwrap(); + builder + .set_not_after(&Asn1Time::days_from_now(365).unwrap()) + .unwrap(); + builder.set_pubkey(&pkey).unwrap(); + + let mut serial = BigNum::new().unwrap(); + serial.rand(128, MsbOption::MAYBE_ZERO, false).unwrap(); + builder + .set_serial_number(&serial.to_asn1_integer().unwrap()) + .unwrap(); + + builder.sign(&pkey, MessageDigest::sha256()).unwrap(); + + let x509 = builder.build(); + + assert!(pkey.public_eq(&x509.public_key().unwrap())); + assert!(x509.verify(&pkey).unwrap()); + + let cn = x509 + .subject_name() + .entries_by_nid(Nid::COMMONNAME) + .next() + .unwrap(); + assert_eq!(cn.data().as_slice(), b"key_usage.com"); + + let usage = x509.key_usage(); + assert_eq!(usage, X509KeyUsage::empty()); +}