diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index 97242ff4d8..a04788cc29 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -249,24 +249,14 @@ impl X509Builder { #[corresponds(X509_set_serialNumber)] pub fn set_serial_number(&mut self, serial_number: &Asn1IntegerRef) -> Result<(), ErrorStack> { unsafe { - cvt(ffi::X509_set_serialNumber( - self.0.as_ptr(), - serial_number.as_ptr(), - )) - .map(|_| ()) + cvt(ffi::X509_set_serialNumber(self.0.as_ptr(), serial_number.as_ptr())).map(|_| ()) } } /// Sets the issuer name of the certificate. #[corresponds(X509_set_issuer_name)] pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> { - unsafe { - cvt(ffi::X509_set_issuer_name( - self.0.as_ptr(), - issuer_name.as_ptr(), - )) - .map(|_| ()) - } + unsafe { cvt(ffi::X509_set_issuer_name(self.0.as_ptr(), issuer_name.as_ptr())).map(|_| ()) } } /// Sets the subject name of the certificate. @@ -290,11 +280,7 @@ impl X509Builder { #[corresponds(X509_set_subject_name)] pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> { unsafe { - cvt(ffi::X509_set_subject_name( - self.0.as_ptr(), - subject_name.as_ptr(), - )) - .map(|_| ()) + cvt(ffi::X509_set_subject_name(self.0.as_ptr(), subject_name.as_ptr())).map(|_| ()) } } @@ -429,12 +415,13 @@ impl X509Ref { #[corresponds(X509_get_ext_d2i)] pub fn subject_alt_names(&self) -> Option> { unsafe { - let stack = ffi::X509_get_ext_d2i( - self.as_ptr(), - ffi::NID_subject_alt_name, - ptr::null_mut(), - ptr::null_mut(), - ); + let stack = + ffi::X509_get_ext_d2i( + self.as_ptr(), + ffi::NID_subject_alt_name, + ptr::null_mut(), + ptr::null_mut(), + ); Stack::from_ptr_opt(stack as *mut _) } } @@ -443,12 +430,13 @@ impl X509Ref { #[corresponds(X509_get_ext_d2i)] pub fn crl_distribution_points(&self) -> Option> { unsafe { - let stack = ffi::X509_get_ext_d2i( - self.as_ptr(), - ffi::NID_crl_distribution_points, - ptr::null_mut(), - ptr::null_mut(), - ); + let stack = + ffi::X509_get_ext_d2i( + self.as_ptr(), + ffi::NID_crl_distribution_points, + ptr::null_mut(), + ptr::null_mut(), + ); Stack::from_ptr_opt(stack as *mut _) } } @@ -457,12 +445,13 @@ impl X509Ref { #[corresponds(X509_get_ext_d2i)] pub fn issuer_alt_names(&self) -> Option> { unsafe { - let stack = ffi::X509_get_ext_d2i( - self.as_ptr(), - ffi::NID_issuer_alt_name, - ptr::null_mut(), - ptr::null_mut(), - ); + let stack = + ffi::X509_get_ext_d2i( + self.as_ptr(), + ffi::NID_issuer_alt_name, + ptr::null_mut(), + ptr::null_mut(), + ); Stack::from_ptr_opt(stack as *mut _) } } @@ -788,10 +777,12 @@ impl Clone for X509 { impl fmt::Debug for X509 { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { let serial = match &self.serial_number().to_bn() { - Ok(bn) => match bn.to_hex_str() { - Ok(hex) => hex.to_string(), - Err(_) => "".to_string(), - }, + Ok(bn) => { + match bn.to_hex_str() { + Ok(hex) => hex.to_string(), + Err(_) => "".to_string(), + } + } Err(_) => "".to_string(), }; let mut debug_struct = formatter.debug_struct("X509"); @@ -1375,13 +1366,7 @@ impl X509ReqBuilder { ///[`X509_REQ_set_version`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_set_version.html #[allow(clippy::useless_conversion)] pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> { - unsafe { - cvt(ffi::X509_REQ_set_version( - self.0.as_ptr(), - version as c_long, - )) - .map(|_| ()) - } + unsafe { cvt(ffi::X509_REQ_set_version(self.0.as_ptr(), version as c_long)).map(|_| ()) } } /// Set the issuer name. @@ -1441,11 +1426,7 @@ impl X509ReqBuilder { extensions: &StackRef, ) -> Result<(), ErrorStack> { unsafe { - cvt(ffi::X509_REQ_add_extensions( - self.0.as_ptr(), - extensions.as_ptr(), - )) - .map(|_| ()) + cvt(ffi::X509_REQ_add_extensions(self.0.as_ptr(), extensions.as_ptr())).map(|_| ()) } } @@ -1647,6 +1628,15 @@ impl Stackable for X509Revoked { } impl X509Revoked { + /// Creates a new `X509Revoked` instance.` + #[corresponds(X509_REVOKED_new)] + pub fn new() -> Result { + unsafe { + ffi::init(); + cvt_p(ffi::X509_REVOKED_new()).map(X509Revoked) + } + } + from_der! { /// Deserializes a DER-encoded certificate revocation status #[corresponds(d2i_X509_REVOKED)] @@ -1664,6 +1654,30 @@ impl X509RevokedRef { ffi::i2d_X509_REVOKED } + // Sets the serial number of the revoked certificate. + #[corresponds(X509_REVOKED_set_serialNumber)] + pub fn set_serial_number(&self, serial: &Asn1IntegerRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_REVOKED_set_serialNumber( + self.as_ptr(), + serial.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Set the date of the revocation + #[corresponds(X509_REVOKED_set_revocationDate)] + pub fn set_revocation_date(&self, time: &Asn1TimeRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_REVOKED_set_revocationDate( + self.as_ptr(), + time.as_ptr(), + )) + .map(|_| ()) + } + } + /// Copies the entry to a new `X509Revoked`. #[corresponds(X509_NAME_dup)] #[cfg(any(boringssl, ossl110, libressl270))] @@ -1697,18 +1711,19 @@ impl X509RevokedRef { #[corresponds(X509_REVOKED_get_ext_d2i)] pub fn extension(&self) -> Result, ErrorStack> { let mut critical = -1; - let out = unsafe { - // SAFETY: self.as_ptr() is a valid pointer to an X509_REVOKED. - let ext = ffi::X509_REVOKED_get_ext_d2i( - self.as_ptr(), - T::NID.as_raw(), - &mut critical as *mut _, - ptr::null_mut(), - ); - // SAFETY: Extensions's contract promises that the type returned by - // OpenSSL here is T::Output. - T::Output::from_ptr_opt(ext as *mut _) - }; + let out = + unsafe { + // SAFETY: self.as_ptr() is a valid pointer to an X509_REVOKED. + let ext = ffi::X509_REVOKED_get_ext_d2i( + self.as_ptr(), + T::NID.as_raw(), + &mut critical as *mut _, + ptr::null_mut(), + ); + // SAFETY: Extensions's contract promises that the type returned by + // OpenSSL here is T::Output. + T::Output::from_ptr_opt(ext as *mut _) + }; match (critical, out) { (0, Some(out)) => Ok(Some((false, out))), (1, Some(out)) => Ok(Some((true, out))), @@ -1812,6 +1827,15 @@ impl<'a> CrlStatus<'a> { } impl X509Crl { + /// Creates a new `X509Crl` instance. + #[corresponds(X509_CRL_new)] + pub fn new() -> Result { + unsafe { + ffi::init(); + cvt_p(ffi::X509_CRL_new()).map(X509Crl) + } + } + from_pem! { /// Deserializes a PEM-encoded Certificate Revocation List /// @@ -1848,6 +1872,50 @@ impl X509CrlRef { ffi::i2d_X509_CRL } + /// Adds revocation. + #[corresponds(X509_CRL_add0_revoked)] + pub fn add_revoked(&self, revoked: X509Revoked) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_CRL_add0_revoked(self.as_ptr(), revoked.as_ptr()))?; + std::mem::forget(revoked); + Ok(()) + } + } + + /// Sorts list by serial number. + #[corresponds(X509_CRL_sort)] + pub fn sort(&self) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_CRL_sort(self.as_ptr())).map(|_| ()) } + } + + /// Sets the CRL's `nextUpdate` time. + #[corresponds(X509_CRL_set1_lastUpdate)] + #[cfg(any(ossl110, libressl270))] + pub fn set_last_update(&self, tm: &Asn1TimeRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_CRL_set1_lastUpdate(self.as_ptr(), tm.as_ptr())).map(|_| ()) } + } + + /// Sets the CRL's `issuer`. + #[corresponds(X509_CRL_set_issuer_name)] + pub fn set_issuer(&self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_CRL_set_issuer_name(self.as_ptr(), issuer_name.as_ptr())).map(|_| ()) + } + } + + /// Sign the CRL using the given public key and message digest. + /// + /// Returns `true` if signing succeeded. + #[corresponds(X509_CRL_sign)] + pub fn sign(&self, key: &PKeyRef, md: &MessageDigest) -> Result + where + T: HasPublic, + { + unsafe { + cvt_n(ffi::X509_CRL_sign(self.as_ptr(), key.as_ptr(), md.as_ptr())).map(|n| n > 0) + } + } + /// Get the stack of revocation entries pub fn get_revoked(&self) -> Option<&StackRef> { unsafe { @@ -1935,12 +2003,13 @@ impl X509CrlRef { let mut critical = -1; let out = unsafe { // SAFETY: self.as_ptr() is a valid pointer to an X509_CRL. - let ext = ffi::X509_CRL_get_ext_d2i( - self.as_ptr(), - T::NID.as_raw(), - &mut critical as *mut _, - ptr::null_mut(), - ); + let ext = + ffi::X509_CRL_get_ext_d2i( + self.as_ptr(), + T::NID.as_raw(), + &mut critical as *mut _, + ptr::null_mut(), + ); // SAFETY: Extensions's contract promises that the type returned by // OpenSSL here is T::Output. T::Output::from_ptr_opt(ext as *mut _) diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index 0444a067dd..7b73218fa0 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -763,13 +763,14 @@ fn test_save_subject_der() { #[test] fn test_load_subject_der() { // The subject from ../../test/cert.pem - const SUBJECT_DER: &[u8] = &[ - 48, 90, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 65, 85, 49, 19, 48, 17, 6, 3, 85, 4, 8, 12, - 10, 83, 111, 109, 101, 45, 83, 116, 97, 116, 101, 49, 33, 48, 31, 6, 3, 85, 4, 10, 12, 24, - 73, 110, 116, 101, 114, 110, 101, 116, 32, 87, 105, 100, 103, 105, 116, 115, 32, 80, 116, - 121, 32, 76, 116, 100, 49, 19, 48, 17, 6, 3, 85, 4, 3, 12, 10, 102, 111, 111, 98, 97, 114, - 46, 99, 111, 109, - ]; + const SUBJECT_DER: &[u8] = + &[ + 48, 90, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 65, 85, 49, 19, 48, 17, 6, 3, 85, 4, 8, + 12, 10, 83, 111, 109, 101, 45, 83, 116, 97, 116, 101, 49, 33, 48, 31, 6, 3, 85, 4, 10, + 12, 24, 73, 110, 116, 101, 114, 110, 101, 116, 32, 87, 105, 100, 103, 105, 116, 115, + 32, 80, 116, 121, 32, 76, 116, 100, 49, 19, 48, 17, 6, 3, 85, 4, 3, 12, 10, 102, 111, + 111, 98, 97, 114, 46, 99, 111, 109, + ]; X509Name::from_der(SUBJECT_DER).unwrap(); } @@ -1178,17 +1179,71 @@ fn test_dist_point_null() { assert!(cert.crl_distribution_points().is_none()); } +#[test] +fn test_crl_creation_utils() { + use crate::asn1::Asn1Integer; + use crate::x509::X509Revoked; + + let crl = X509Crl::new().unwrap(); + + let mut name = X509Name::builder().unwrap(); + name.append_entry_by_text("C", "UK").unwrap(); + name.append_entry_by_text("O", "Orga").unwrap(); + crl.set_issuer(&name.build()).unwrap(); + + let date = Asn1Time::from_unix(100_000_000).unwrap(); + crl.set_last_update(&date).unwrap(); + + let pkey = pkey(); + let success = crl.sign(&pkey, &MessageDigest::sha256()).unwrap(); + assert!(success); + + let rvk0 = X509Revoked::new().unwrap(); + let serial0 = Asn1Integer::from_bn(&BigNum::from_u32(1001).unwrap()).unwrap(); + rvk0.set_serial_number(&serial0).unwrap(); + rvk0.set_revocation_date(&date).unwrap(); + crl.add_revoked(rvk0).unwrap(); + + let rvk1 = X509Revoked::new().unwrap(); + let serial1 = Asn1Integer::from_bn(&BigNum::from_u32(300).unwrap()).unwrap(); + rvk1.set_serial_number(&serial1).unwrap(); + rvk1.set_revocation_date(&date).unwrap(); + crl.add_revoked(rvk1).unwrap(); + + let issuer = crl.issuer_name().entries().collect::>(); + assert_eq!(issuer.len(), 2); + assert_eq!(issuer[1].object().nid(), Nid::ORGANIZATIONNAME); + assert_eq!(issuer[1].data().as_slice(), b"Orga"); + + let diff = crl + .last_update() + .diff(&Asn1Time::from_unix(100_000_000).unwrap()) + .unwrap(); + assert_eq!((diff.days, diff.secs), (0, 0)); + + let revoked = crl.get_revoked().unwrap().into_iter().collect::>(); + assert!(revoked[0].serial_number().eq(&serial0)); + assert!(revoked[1].serial_number().eq(&serial1)); + + crl.sort().unwrap(); + + let revoked = crl.get_revoked().unwrap().into_iter().collect::>(); + assert!(revoked[0].serial_number().eq(&serial1)); + assert!(revoked[1].serial_number().eq(&serial0)); +} + #[test] #[cfg(ossl300)] fn test_store_all_certificates() { let cert = include_bytes!("../../test/cert.pem"); let cert = X509::from_pem(cert).unwrap(); - let store = { - let mut b = X509StoreBuilder::new().unwrap(); - b.add_cert(cert).unwrap(); - b.build() - }; + let store = + { + let mut b = X509StoreBuilder::new().unwrap(); + b.add_cert(cert).unwrap(); + b.build() + }; assert_eq!(store.all_certificates().len(), 1); }