Compare commits
63 Commits
main
...
jrose/bori
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f23bb2d478 | ||
|
|
c740bd7715 | ||
|
|
a721d89d08 | ||
|
|
395f57810b | ||
|
|
64b3e14976 | ||
|
|
33b36ab7a1 | ||
|
|
80c3a8911e | ||
|
|
f10414d927 | ||
|
|
98215dbdb0 | ||
|
|
05e3aafd0b | ||
|
|
35774a0116 | ||
|
|
16290eb11b | ||
|
|
97aa4a987e | ||
|
|
5a3d1ae4b2 | ||
|
|
a54b351cc0 | ||
|
|
36a2a1ca44 | ||
|
|
feb62b627b | ||
|
|
7efa9e1dd0 | ||
|
|
ff3d69c075 | ||
|
|
f2f4871a39 | ||
|
|
237c82d394 | ||
|
|
327162be0d | ||
|
|
7c8dd2678a | ||
|
|
81d4aa19ac | ||
|
|
bb42da53b3 | ||
|
|
615af5aafb | ||
|
|
1af143f509 | ||
|
|
f7b6f5dfa1 | ||
|
|
3d4180b232 | ||
|
|
889ad0f41e | ||
|
|
59883d7e23 | ||
|
|
fbea17998a | ||
|
|
6c4a02f131 | ||
|
|
b9a75167dc | ||
|
|
7c88181568 | ||
|
|
e17586365c | ||
|
|
e8e6122545 | ||
|
|
2f63b8affd | ||
|
|
7f4dca3405 | ||
|
|
8245063ae6 | ||
|
|
8d4822be72 | ||
|
|
02e4a3f9aa | ||
|
|
3c50b28a61 | ||
|
|
3809a7e1ca | ||
|
|
9534656269 | ||
|
|
bdf9d2ac06 | ||
|
|
25e1dd8fa5 | ||
|
|
73ee9d7085 | ||
|
|
81eeaab77a | ||
|
|
b95cb545b9 | ||
|
|
e1c719c096 | ||
|
|
8a2d686286 | ||
|
|
8e6f19aa9a | ||
|
|
879ffef1b5 | ||
|
|
a04d27b0c4 | ||
|
|
7451d82bb2 | ||
|
|
b73043e51b | ||
|
|
7afa7798cf | ||
|
|
29b7ed4cbd | ||
|
|
1e2df380d9 | ||
|
|
2dbdb97351 | ||
|
|
ea59de3542 | ||
|
|
ceb140105b |
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@ -3,11 +3,11 @@ name: CI
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- v4.x
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
@ -162,8 +162,8 @@ jobs:
|
||||
apt_packages: gcc-arm-linux-gnueabi g++-arm-linux-gnueabi
|
||||
check_only: true
|
||||
custom_env:
|
||||
CC_arm-unknown-linux-gnueabi: arm-linux-gnueabi-gcc
|
||||
CXX_arm-unknown-linux-gnueabi: arm-linux-gnueabi-g++
|
||||
CC: arm-linux-gnueabi-gcc
|
||||
CXX: arm-linux-gnueabi-g++
|
||||
CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABI_LINKER: arm-linux-gnueabi-g++
|
||||
- thing: aarch64-linux
|
||||
target: aarch64-unknown-linux-gnu
|
||||
@ -172,8 +172,8 @@ jobs:
|
||||
apt_packages: crossbuild-essential-arm64
|
||||
check_only: true
|
||||
custom_env:
|
||||
CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc
|
||||
CXX_aarch64_unknown_linux_gnu: aarch64-linux-gnu-g++
|
||||
CC: aarch64-linux-gnu-gcc
|
||||
CXX: aarch64-linux-gnu-g++
|
||||
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-g++
|
||||
- thing: arm64-macos
|
||||
target: aarch64-apple-darwin
|
||||
|
||||
@ -34,9 +34,6 @@ pub(crate) struct Env {
|
||||
pub(crate) android_ndk_home: Option<PathBuf>,
|
||||
pub(crate) cmake_toolchain_file: Option<PathBuf>,
|
||||
pub(crate) cpp_runtime_lib: Option<OsString>,
|
||||
/// C compiler (ignored if using FIPS)
|
||||
pub(crate) cc: Option<OsString>,
|
||||
pub(crate) cxx: Option<OsString>,
|
||||
pub(crate) docs_rs: bool,
|
||||
}
|
||||
|
||||
@ -126,15 +123,18 @@ impl Features {
|
||||
|
||||
impl Env {
|
||||
fn from_env(host: &str, target: &str, is_fips_like: bool) -> Self {
|
||||
let var_prefix = if host == target { "HOST" } else { "TARGET" };
|
||||
let target_with_underscores = target.replace('-', "_");
|
||||
|
||||
let target_only_var = |name: &str| {
|
||||
// Logic stolen from cmake-rs.
|
||||
let target_var = |name: &str| {
|
||||
let kind = if host == target { "HOST" } else { "TARGET" };
|
||||
|
||||
// TODO(rmehra): look for just `name` first, as most people just set that
|
||||
var(&format!("{name}_{target}"))
|
||||
.or_else(|| var(&format!("{name}_{target_with_underscores}")))
|
||||
.or_else(|| var(&format!("{var_prefix}_{name}")))
|
||||
.or_else(|| var(&format!("{kind}_{name}")))
|
||||
.or_else(|| var(name))
|
||||
};
|
||||
let target_var = |name: &str| target_only_var(name).or_else(|| var(name));
|
||||
|
||||
let boringssl_var = |name: &str| {
|
||||
const BORING_BSSL_PREFIX: &str = "BORING_BSSL_";
|
||||
@ -171,9 +171,6 @@ impl Env {
|
||||
android_ndk_home: target_var("ANDROID_NDK_HOME").map(Into::into),
|
||||
cmake_toolchain_file: target_var("CMAKE_TOOLCHAIN_FILE").map(Into::into),
|
||||
cpp_runtime_lib: target_var("BORING_BSSL_RUST_CPPLIB"),
|
||||
// matches the `cc` crate
|
||||
cc: target_only_var("CC"),
|
||||
cxx: target_only_var("CXX"),
|
||||
docs_rs: var("DOCS_RS").is_some(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,15 +232,6 @@ fn get_boringssl_cmake_config(config: &Config) -> cmake::Config {
|
||||
.define("CMAKE_ASM_COMPILER_TARGET", &config.target);
|
||||
}
|
||||
|
||||
if !config.features.fips {
|
||||
if let Some(cc) = &config.env.cc {
|
||||
boringssl_cmake.define("CMAKE_C_COMPILER", cc);
|
||||
}
|
||||
if let Some(cxx) = &config.env.cxx {
|
||||
boringssl_cmake.define("CMAKE_CXX_COMPILER", cxx);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(sysroot) = &config.env.sysroot {
|
||||
boringssl_cmake.define("CMAKE_SYSROOT", sysroot);
|
||||
}
|
||||
|
||||
@ -603,27 +603,37 @@ impl Asn1ObjectRef {
|
||||
pub fn nid(&self) -> Nid {
|
||||
unsafe { Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr())) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Asn1ObjectRef {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// Returns the numerical string OID of this object.
|
||||
///
|
||||
/// This corresponds to [`OBJ_obj2txt`] with `no_name = 1`.
|
||||
///
|
||||
/// [`OBJ_obj2txt`]: https://www.openssl.org/docs/man1.1.1/man3/OBJ_obj2txt.html
|
||||
pub fn oid_string(&self) -> String {
|
||||
self.to_text(true)
|
||||
}
|
||||
|
||||
// To promote this to `pub`, the call-site parameter meaning ought to be clearer
|
||||
fn to_text(&self, no_name: bool) -> String {
|
||||
unsafe {
|
||||
let mut buf = [0u8; 80];
|
||||
let len = ffi::OBJ_obj2txt(
|
||||
buf.as_mut_ptr().cast(),
|
||||
buf.len() as c_int,
|
||||
self.as_ptr(),
|
||||
0,
|
||||
no_name as c_int,
|
||||
);
|
||||
fmt.write_str(
|
||||
buf.get(..len as usize)
|
||||
.and_then(|s| str::from_utf8(s).ok())
|
||||
.unwrap_or("error"),
|
||||
)
|
||||
String::from_utf8_lossy(&buf[..len as usize]).into_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Asn1ObjectRef {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.write_str(&self.to_text(false))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Asn1ObjectRef {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.write_str(self.to_string().as_str())
|
||||
@ -723,4 +733,13 @@ mod tests {
|
||||
.map(|object| object.to_string())
|
||||
.expect_err("parsing invalid OID should fail");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_to_text() {
|
||||
let oid = "2.16.840.1.101.3.4.2.1";
|
||||
let object = Asn1Object::from_str(oid).unwrap();
|
||||
assert_eq!(object.to_text(false), Nid::SHA256.long_name().unwrap());
|
||||
assert_eq!(object.to_text(true), oid.to_string());
|
||||
assert_eq!(object.oid_string(), oid.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,6 +257,16 @@ impl<T: Stackable> StackRef<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for StackRef<T>
|
||||
where
|
||||
T: Stackable,
|
||||
T::Ref: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_list().entries(self).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Stackable> Index<usize> for StackRef<T> {
|
||||
type Output = T::Ref;
|
||||
|
||||
|
||||
509
boring/src/x509/crl.rs
Normal file
509
boring/src/x509/crl.rs
Normal file
@ -0,0 +1,509 @@
|
||||
//! Certificate revocation lists describe certificates that have been revoked
|
||||
//! by their issuer and should no longer be trusted.
|
||||
//!
|
||||
//! An `X509CRL` can be provided along with an issuing `X509` to verify that
|
||||
//! issued certificates have not been revoked.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! use boring::hash::MessageDigest;
|
||||
//! use boring::pkey::{PKey, Private};
|
||||
//! use boring::x509::crl::{X509CRLBuilder, X509Revoked};
|
||||
//! use boring::x509::extension::BasicConstraints;
|
||||
//! use boring::x509::verify::X509VerifyFlags;
|
||||
//! use boring::x509::X509Extension;
|
||||
//! use boring::x509::X509;
|
||||
//! use boring::x509::store::{X509Store, X509StoreBuilder};
|
||||
//! use boring::asn1::Asn1Time;
|
||||
//! use boring::bn::BigNum;
|
||||
//! use boring::error::ErrorStack;
|
||||
//!
|
||||
//! fn crl_checking_store(issuer: X509, pkey: PKey<Private>) -> Result<X509Store, ErrorStack> {
|
||||
//! let mut builder = X509CRLBuilder::new()?;
|
||||
//! builder.set_issuer_name(issuer.subject_name())?;
|
||||
//! builder.add_revoked(X509Revoked::from_parts(
|
||||
//! &*BigNum::from_u32(1)?.to_asn1_integer()?,
|
||||
//! &*Asn1Time::days_from_now(0)?
|
||||
//! )?)?;
|
||||
//! builder.set_last_update(&*Asn1Time::days_from_now(0)?)?;
|
||||
//! builder.set_next_update(&*Asn1Time::days_from_now(30)?)?;
|
||||
//! builder.sign(&pkey, MessageDigest::sha256())?;
|
||||
//!
|
||||
//! let mut store_builder = X509StoreBuilder::new()?;
|
||||
//! store_builder.add_cert(issuer)?;
|
||||
//! store_builder.add_crl(builder.build())?;
|
||||
//! store_builder
|
||||
//! .param_mut()
|
||||
//! .set_flags(X509VerifyFlags::CRL_CHECK | X509VerifyFlags::CRL_CHECK_ALL);
|
||||
//! Ok(store_builder.build())
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use crate::asn1::{Asn1BitStringRef, Asn1IntegerRef, Asn1TimeRef};
|
||||
use crate::foreign_types::ForeignType;
|
||||
use crate::foreign_types::ForeignTypeRef;
|
||||
use crate::hash::{DigestBytes, MessageDigest};
|
||||
use crate::pkey::{HasPrivate, HasPublic, PKeyRef};
|
||||
use crate::stack::{StackRef, Stackable};
|
||||
use crate::x509::X509ExtensionRef;
|
||||
use crate::x509::{X509AlgorithmRef, X509Extension, X509NameRef};
|
||||
use crate::{cvt, cvt_n, cvt_p, ErrorStack};
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::Formatter;
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
foreign_type_and_impl_send_sync! {
|
||||
type CType = ffi::X509_REVOKED;
|
||||
fn drop = ffi::X509_REVOKED_free;
|
||||
|
||||
/// An `X509_REVOKED` containing information about a revoked certificate
|
||||
pub struct X509Revoked;
|
||||
}
|
||||
|
||||
impl Stackable for X509Revoked {
|
||||
type StackType = ffi::stack_st_X509_REVOKED;
|
||||
}
|
||||
|
||||
impl X509Revoked {
|
||||
/// Create an `X509Revoked`
|
||||
///
|
||||
/// This corresponds to [`X509_REVOKED_new`] followed by calls to
|
||||
/// [`X509_REVOKED_set_serialNumber`] and [`X509_REVOKED_set_revocationDate`]
|
||||
/// with the provided parameters
|
||||
///
|
||||
/// [`X509_REVOKED_new`]: https://www.openssl.org/docs/man1.1.1/man3/X509_REVOKED_new.html
|
||||
/// [`X509_REVOKED_set_serialNumber`]: https://www.openssl.org/docs/man1.1.1/man3/X509_REVOKED_set_serialNumber.html
|
||||
/// [`X509_REVOKED_set_revocationDate`]: https://www.openssl.org/docs/man1.1.1/man3/X509_REVOKED_set_revocationDate.html
|
||||
pub fn from_parts(
|
||||
serial_number: &Asn1IntegerRef,
|
||||
revocation_date: &Asn1TimeRef,
|
||||
) -> Result<X509Revoked, ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
let revoked = cvt_p(ffi::X509_REVOKED_new())?;
|
||||
cvt(ffi::X509_REVOKED_set_serialNumber(
|
||||
revoked,
|
||||
serial_number.as_ptr(),
|
||||
))?;
|
||||
cvt(ffi::X509_REVOKED_set_revocationDate(
|
||||
revoked,
|
||||
revocation_date.as_ptr(),
|
||||
))?;
|
||||
Ok(X509Revoked::from_ptr(revoked))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl X509RevokedRef {
|
||||
/// Returns the serial number of the revoked certificate
|
||||
///
|
||||
/// This corresponds to [`X509_REVOKED_get0_serialNumber`].
|
||||
///
|
||||
/// [`X509_REVOKED_get0_serialNumber`]: https://www.openssl.org/docs/man1.1.1/man3/X509_REVOKED_get0_serialNumber.html
|
||||
pub fn serial_number(&self) -> &Asn1IntegerRef {
|
||||
unsafe {
|
||||
let r = ffi::X509_REVOKED_get0_serialNumber(self.as_ptr());
|
||||
assert!(!r.is_null());
|
||||
Asn1IntegerRef::from_ptr(r as *mut _)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns certificate's revocation date
|
||||
///
|
||||
/// This corresponds to [`X509_REVOKED_get0_revocationDate`].
|
||||
///
|
||||
/// [`X509_REVOKED_get0_revocationDate`]: https://www.openssl.org/docs/man1.1.1/man3/X509_REVOKED_get0_revocationDate
|
||||
pub fn revocation_date(&self) -> &Asn1TimeRef {
|
||||
unsafe {
|
||||
let date = ffi::X509_REVOKED_get0_revocationDate(self.as_ptr());
|
||||
assert!(!date.is_null());
|
||||
Asn1TimeRef::from_ptr(date as *mut _)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for X509RevokedRef {
|
||||
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||
let sn = self.serial_number().to_bn().and_then(|bn| bn.to_hex_str());
|
||||
let sn = sn.as_ref().map(|x| &***x).unwrap_or("");
|
||||
|
||||
fmt.debug_struct("X509Revoked")
|
||||
.field("serial_number", &sn)
|
||||
.field("revocation_date", self.revocation_date())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
foreign_type_and_impl_send_sync! {
|
||||
type CType = ffi::X509_CRL;
|
||||
fn drop = ffi::X509_CRL_free;
|
||||
|
||||
/// An `X509CRL` certificate revocation list
|
||||
pub struct X509CRL;
|
||||
}
|
||||
|
||||
impl Stackable for X509CRL {
|
||||
type StackType = ffi::stack_st_X509_CRL;
|
||||
}
|
||||
|
||||
impl X509CRL {
|
||||
from_pem! {
|
||||
/// Deserializes a PEM-encoded X509CRL structure.
|
||||
///
|
||||
/// The input should have a header of `-----BEGIN X509 CRL-----`
|
||||
///
|
||||
/// This corresponds to [`PEM_read_bio_X509_CRL`].
|
||||
///
|
||||
/// [`PEM_read_bio_X509_CRL`]: https://www.openssl.org/docs/man1.1.1/man3/PEM_read_bio_X509_CRL
|
||||
from_pem,
|
||||
X509CRL,
|
||||
ffi::PEM_read_bio_X509_CRL
|
||||
}
|
||||
|
||||
from_der! {
|
||||
/// Deserializes a DER-encoded X509 structure.
|
||||
///
|
||||
/// This corresponds to [`d2i_X509_CRL`].
|
||||
///
|
||||
/// [`d2i_X509_CRL`]: https://www.openssl.org/docs/man1.1.1/man3/d2i_X509_CRL.html
|
||||
from_der,
|
||||
X509CRL,
|
||||
ffi::d2i_X509_CRL,
|
||||
::libc::c_long
|
||||
}
|
||||
}
|
||||
|
||||
impl X509CRLRef {
|
||||
/// Returns the CRL's last update time
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_get0_lastUpdate`]
|
||||
///
|
||||
/// [`X509_CRL_get0_lastUpdate`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_get0_lastUpdate
|
||||
pub fn last_update(&self) -> Option<&Asn1TimeRef> {
|
||||
unsafe {
|
||||
let date = ffi::X509_CRL_get0_lastUpdate(self.as_ptr());
|
||||
if date.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Asn1TimeRef::from_ptr(date as *mut _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the CRL's next update time
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_get0_nextUpdate`]
|
||||
///
|
||||
/// [`X509_CRL_get0_nextUpdate`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_get0_nextUpdate
|
||||
pub fn next_update(&self) -> Option<&Asn1TimeRef> {
|
||||
unsafe {
|
||||
let date = ffi::X509_CRL_get0_nextUpdate(self.as_ptr());
|
||||
if date.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Asn1TimeRef::from_ptr(date as *mut _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the CRL's issuer name
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_get_issuer`]
|
||||
///
|
||||
/// [`X509_CRL_get_issuer`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_get_issuer
|
||||
pub fn issuer(&self) -> &X509NameRef {
|
||||
unsafe {
|
||||
let name = ffi::X509_CRL_get_issuer(self.as_ptr());
|
||||
|
||||
assert!(!name.is_null());
|
||||
X509NameRef::from_ptr(name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the CRL's extensions
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_get0_extensions`]
|
||||
///
|
||||
/// [`X509_CRL_get0_extensions`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_get0_extensions
|
||||
pub fn extensions(&self) -> Option<&StackRef<X509Extension>> {
|
||||
unsafe {
|
||||
let extensions = ffi::X509_CRL_get0_extensions(self.as_ptr());
|
||||
if extensions.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(StackRef::from_ptr(extensions as *mut _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the revoked certificates in this CRL
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_get_REVOKED`]
|
||||
///
|
||||
/// [`X509_CRL_get_REVOKED`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_get_REVOKED
|
||||
pub fn revoked(&self) -> Option<&StackRef<X509Revoked>> {
|
||||
unsafe {
|
||||
let revoked = ffi::X509_CRL_get_REVOKED(self.as_ptr());
|
||||
if revoked.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(StackRef::from_ptr(revoked))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the CRL's signature and signature algorithm
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_get0_signature`]
|
||||
///
|
||||
/// [`X509_CRL_get0_signature`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_get0_signature
|
||||
pub fn signature(&self) -> (&Asn1BitStringRef, &X509AlgorithmRef) {
|
||||
unsafe {
|
||||
let mut signature = ptr::null();
|
||||
let mut algor = ptr::null();
|
||||
ffi::X509_CRL_get0_signature(self.as_ptr(), &mut signature, &mut algor);
|
||||
assert!(!algor.is_null());
|
||||
assert!(!signature.is_null());
|
||||
(
|
||||
Asn1BitStringRef::from_ptr(signature as *mut _),
|
||||
X509AlgorithmRef::from_ptr(algor as *mut _),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a digest of the DER representation of the CRL
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_digest`]
|
||||
///
|
||||
/// [`X509_CRL_digest`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_digest
|
||||
pub fn digest(&self, hash_type: MessageDigest) -> Result<DigestBytes, ErrorStack> {
|
||||
unsafe {
|
||||
let mut digest = DigestBytes {
|
||||
buf: [0; ffi::EVP_MAX_MD_SIZE as usize],
|
||||
len: ffi::EVP_MAX_MD_SIZE as usize,
|
||||
};
|
||||
let mut len = ffi::EVP_MAX_MD_SIZE.try_into().unwrap();
|
||||
cvt(ffi::X509_CRL_digest(
|
||||
self.as_ptr(),
|
||||
hash_type.as_ptr(),
|
||||
digest.buf.as_mut_ptr() as *mut _,
|
||||
&mut len,
|
||||
))?;
|
||||
digest.len = len as usize;
|
||||
|
||||
Ok(digest)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the CRL is signed using the given public key.
|
||||
///
|
||||
/// Only the signature is checked: no other checks (such as certificate chain validity)
|
||||
/// are performed.
|
||||
///
|
||||
/// Returns `true` if verification succeeds.
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_verify"].
|
||||
///
|
||||
/// [`X509_CRL_verify`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_verify
|
||||
pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
|
||||
where
|
||||
T: HasPublic,
|
||||
{
|
||||
unsafe { cvt_n(ffi::X509_CRL_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
|
||||
}
|
||||
|
||||
to_pem! {
|
||||
/// Serializes the CRL into a PEM-encoded X509 CRL structure.
|
||||
///
|
||||
/// The output will have a header of `-----BEGIN X509 CRL-----`
|
||||
///
|
||||
/// This corresponds to [`PEM_write_bio_X509_CRL`]
|
||||
///
|
||||
/// [`PEM_write_bio_X509_CRL`]: https://www.openssl.org/docs/man1.1.1/man3/PEM_write_bio_X509_CRL
|
||||
to_pem,
|
||||
ffi::PEM_write_bio_X509_CRL
|
||||
}
|
||||
|
||||
to_der! {
|
||||
/// Serializes the CRL into a DER-encoded X509 CRL structure
|
||||
///
|
||||
/// This corresponds to `i2d_X509_CRL`
|
||||
///
|
||||
/// [`i2d_X509_CRL`]: https://www.openssl.org/docs/man1.1.1/man3/i2d_X509_CRL
|
||||
to_der,
|
||||
ffi::i2d_X509_CRL
|
||||
}
|
||||
}
|
||||
impl ToOwned for X509CRLRef {
|
||||
type Owned = X509CRL;
|
||||
|
||||
fn to_owned(&self) -> X509CRL {
|
||||
unsafe {
|
||||
ffi::X509_CRL_up_ref(self.as_ptr());
|
||||
X509CRL::from_ptr(self.as_ptr())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for X509CRL {
|
||||
fn clone(&self) -> X509CRL {
|
||||
X509CRLRef::to_owned(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for X509CRLRef {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut debug_struct = formatter.debug_struct("X509CRL");
|
||||
debug_struct.field("issuer", self.issuer());
|
||||
debug_struct.field("signature_algorithm", &self.signature().1.object());
|
||||
|
||||
if let Some(next_update) = self.next_update() {
|
||||
debug_struct.field("next_update", next_update);
|
||||
}
|
||||
if let Some(last_update) = self.last_update() {
|
||||
debug_struct.field("last_update", last_update);
|
||||
}
|
||||
if let Some(revoked) = self.revoked() {
|
||||
debug_struct.field("revoked", &revoked);
|
||||
}
|
||||
if let Some(extensions) = self.extensions() {
|
||||
debug_struct.field("extensions", &extensions);
|
||||
}
|
||||
debug_struct.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for X509CRL {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
let x: &X509CRLRef = self;
|
||||
x.fmt(formatter)
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder used to construct an `X509CRL`
|
||||
pub struct X509CRLBuilder(X509CRL);
|
||||
|
||||
impl X509CRLBuilder {
|
||||
/// Creates a new builder.
|
||||
pub fn new() -> Result<X509CRLBuilder, ErrorStack> {
|
||||
unsafe {
|
||||
ffi::init();
|
||||
cvt_p(ffi::X509_CRL_new()).map(|p| X509CRLBuilder(X509CRL::from_ptr(p)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Append an `X509Extension` to the certificate revocation list
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_add_ext`]
|
||||
///
|
||||
/// [`X509_CRL_add_ext`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_add_ext
|
||||
pub fn append_extension(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
// -1 indicates append to end
|
||||
cvt(ffi::X509_CRL_add_ext(
|
||||
self.0.as_ptr(),
|
||||
extension.as_ptr(),
|
||||
-1,
|
||||
))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Signs the certificate revocation list with a private key.
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_sign`]
|
||||
///
|
||||
/// [`X509_CRL_sign`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_sign
|
||||
pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
|
||||
where
|
||||
T: HasPrivate,
|
||||
{
|
||||
unsafe {
|
||||
cvt(ffi::X509_CRL_sign(
|
||||
self.0.as_ptr(),
|
||||
key.as_ptr(),
|
||||
hash.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a revoked certificate to the certificate revocation list
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_add0_revoked`]
|
||||
///
|
||||
/// [`X509_CRL_add0_revoked`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_add0_revoked
|
||||
pub fn add_revoked(&mut self, revoked: X509Revoked) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::X509_CRL_add0_revoked(
|
||||
self.0.as_ptr(),
|
||||
revoked.as_ptr(),
|
||||
))?;
|
||||
mem::forget(revoked);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the issuer name of the certificate revocation list.
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_set_issuer_name`]
|
||||
///
|
||||
/// [`X509_CRL_set_issuer_name`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_set_issuer_name
|
||||
pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::X509_CRL_set_issuer_name(
|
||||
self.0.as_ptr(),
|
||||
issuer_name.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the version of the certificate revocation list.
|
||||
///
|
||||
/// Note that the version is zero-indexed; that is, a certificate corresponding to version 3 of
|
||||
/// the X.509 standard should pass `2` to this method.
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_set_version`]
|
||||
///
|
||||
/// [`X509_CRL_set_version`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_set_version
|
||||
pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::X509_CRL_set_version(self.0.as_ptr(), version.into())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Sets the last update time on the certificate revocation list.
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_set1_lastUpdate`]
|
||||
///
|
||||
/// [`X509_CRL_set1_lastUpdate`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_set1_lastUpdate
|
||||
pub fn set_last_update(&mut self, last_update: &Asn1TimeRef) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::X509_CRL_set1_lastUpdate(
|
||||
self.0.as_ptr(),
|
||||
last_update.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the next update time on the certificate revocation list.
|
||||
///
|
||||
/// This corresponds to [`X509_CRL_set1_nextUpdate`]
|
||||
///
|
||||
/// [`X509_CRL_set1_nextUpdate`]: https://www.openssl.org/docs/man1.1.1/man3/X509_CRL_set1_nextUpdate
|
||||
pub fn set_next_update(&mut self, next_update: &Asn1TimeRef) -> Result<(), ErrorStack> {
|
||||
unsafe {
|
||||
cvt(ffi::X509_CRL_set1_nextUpdate(
|
||||
self.0.as_ptr(),
|
||||
next_update.as_ptr(),
|
||||
))
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes the builder, returning the certificate revocation list
|
||||
pub fn build(self) -> X509CRL {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
@ -38,10 +38,12 @@ use crate::stack::{Stack, StackRef, Stackable};
|
||||
use crate::string::OpensslString;
|
||||
use crate::try_int;
|
||||
use crate::util::ForeignTypeRefExt;
|
||||
use crate::x509::crl::X509CRL;
|
||||
use crate::x509::verify::{X509VerifyParam, X509VerifyParamRef};
|
||||
use crate::{cvt, cvt_n, cvt_p};
|
||||
use crate::{ffi, free_data_box};
|
||||
|
||||
pub mod crl;
|
||||
pub mod extension;
|
||||
pub mod store;
|
||||
pub mod verify;
|
||||
@ -127,6 +129,7 @@ impl X509StoreContextRef {
|
||||
/// This can be used to provide data to callbacks registered with the context. Use the
|
||||
/// `Ssl::new_ex_index` method to create an `Index`.
|
||||
#[corresponds(X509_STORE_CTX_set_ex_data)]
|
||||
#[doc(alias = "replace_ex_data")]
|
||||
pub fn set_ex_data<T>(&mut self, index: Index<X509StoreContext, T>, data: T) {
|
||||
if let Some(old) = self.ex_data_mut(index) {
|
||||
*old = data;
|
||||
@ -261,6 +264,32 @@ impl X509StoreContextRef {
|
||||
unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) }
|
||||
}
|
||||
|
||||
/// Verifies the stored certificate with additional untrusted CRLs
|
||||
///
|
||||
/// Returns `true` if verification succeeds. The `error` method will return the specific
|
||||
/// validation error if the certificate was not valid.
|
||||
///
|
||||
/// This will only work inside of a call to `init`.
|
||||
///
|
||||
/// This corresponds to [`X509_STORE_CTX_set0_crls`] followed by [`X509_verify_cert`].
|
||||
///
|
||||
/// [`X509_STORE_CTX_set0_crls`]: https://www.openssl.org/docs/man1.0.2/crypto/X509_STORE_CTX_set0_crls.html
|
||||
/// [`X509_verify_cert`]: https://www.openssl.org/docs/man1.0.2/crypto/X509_verify_cert.html
|
||||
pub fn verify_cert_with_crls(
|
||||
&mut self,
|
||||
untrusted_crls: Stack<X509CRL>,
|
||||
) -> Result<bool, ErrorStack> {
|
||||
unsafe {
|
||||
ffi::X509_STORE_CTX_set0_crls(self.as_ptr(), untrusted_crls.as_ptr());
|
||||
let res = cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0);
|
||||
// set0_crls does not take ownership of the stack, so we'll drop and free
|
||||
// untrusted_crls after this method. null out the crls in ctx to make sure
|
||||
// no one has a reference to it.
|
||||
ffi::X509_STORE_CTX_set0_crls(self.as_ptr(), ptr::null_mut());
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the verify result of the context.
|
||||
#[corresponds(X509_STORE_CTX_set_error)]
|
||||
pub fn set_error(&mut self, result: X509VerifyResult) {
|
||||
@ -716,6 +745,21 @@ impl X509Ref {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the extensions of the certificate.
|
||||
///
|
||||
/// This corresponds to [`X509_get0_extensions`].
|
||||
///
|
||||
/// [`X509_get0_extensions`]: https://www.openssl.org/docs/man1.1.1/man3/X509_get0_extensions.html
|
||||
pub fn extensions(&self) -> Option<&StackRef<X509Extension>> {
|
||||
unsafe {
|
||||
let stack = ffi::X509_get0_extensions(self.as_ptr());
|
||||
if stack.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(StackRef::from_ptr(stack as *mut _))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_host(&self, host: &str) -> Result<bool, ErrorStack> {
|
||||
unsafe {
|
||||
cvt_n(ffi::X509_check_host(
|
||||
@ -999,6 +1043,47 @@ impl X509ExtensionRef {
|
||||
}
|
||||
}
|
||||
|
||||
impl X509ExtensionRef {
|
||||
/// Returns whether this extension is critical
|
||||
///
|
||||
/// This corresponds to [`X509_EXTENSION_get_critical`].
|
||||
///
|
||||
/// [`X509_EXTENSION_get_critical`]: https://www.openssl.org/docs/man1.1.1/man3/X509_EXTENSION_get_critical
|
||||
pub fn critical(&self) -> bool {
|
||||
unsafe { ffi::X509_EXTENSION_get_critical(self.as_ptr()) != 0 }
|
||||
}
|
||||
|
||||
/// Returns the `Asn1Object` of this `X509Extension`
|
||||
///
|
||||
/// This can be used to determine the `Nid` of this extension
|
||||
///
|
||||
/// This corresponds to [`X509_EXTENSION_get_object`].
|
||||
///
|
||||
/// [`X509_EXTENSION_get_object`]: https://www.openssl.org/docs/man1.1.1/man3/X509_EXTENSION_get_object
|
||||
pub fn object(&self) -> &Asn1ObjectRef {
|
||||
unsafe { Asn1ObjectRef::from_ptr(ffi::X509_EXTENSION_get_object(self.as_ptr())) }
|
||||
}
|
||||
|
||||
/// Returns the `data` of this `X509Extension`
|
||||
///
|
||||
/// This corresponds to [`X509_EXTENSION_get_data`].
|
||||
///
|
||||
/// [`X509_EXTENSION_get_data`]: https://www.openssl.org/docs/man1.1.1/man3/X509_EXTENSION_get_data
|
||||
pub fn data(&self) -> &Asn1StringRef {
|
||||
unsafe { Asn1StringRef::from_ptr(ffi::X509_EXTENSION_get_data(self.as_ptr())) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for X509ExtensionRef {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter
|
||||
.debug_struct("X509Extension")
|
||||
.field("critical", &self.critical())
|
||||
.field("object_nid", &self.object())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder used to construct an `X509Name`.
|
||||
pub struct X509NameBuilder(X509Name);
|
||||
|
||||
|
||||
@ -43,6 +43,7 @@
|
||||
use crate::error::ErrorStack;
|
||||
use crate::ffi;
|
||||
use crate::stack::StackRef;
|
||||
use crate::x509::crl::X509CRL;
|
||||
use crate::x509::verify::{X509VerifyFlags, X509VerifyParamRef};
|
||||
use crate::x509::{X509Object, X509Ref};
|
||||
use crate::{cvt, cvt_p};
|
||||
@ -85,6 +86,24 @@ impl X509StoreBuilderRef {
|
||||
unsafe { cvt(ffi::X509_STORE_add_cert(self.as_ptr(), cert.as_ptr())) }
|
||||
}
|
||||
|
||||
/// Adds a CRL to the certificate store.
|
||||
///
|
||||
/// This corresponds to [`X509_STORE_add_crl`].
|
||||
///
|
||||
/// [`X509_STORE_add_crl`]: https://www.openssl.org/docs/man1.1.1/man3/X509_STORE_add_crl
|
||||
pub fn add_crl(&mut self, crl: X509CRL) -> Result<(), ErrorStack> {
|
||||
unsafe { cvt(ffi::X509_STORE_add_crl(self.as_ptr(), crl.as_ptr())).map(|_| ()) }
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the X509 verification configuration.
|
||||
///
|
||||
/// This corresponds to [`X509_STORE_get0_param`].
|
||||
///
|
||||
/// [`X509_STORE_add_crl`]: https://www.openssl.org/docs/man1.1.1/man3/X509_STORE_get0_param
|
||||
pub fn param_mut(&mut self) -> &mut X509VerifyParamRef {
|
||||
unsafe { X509VerifyParamRef::from_ptr_mut(ffi::X509_STORE_get0_param(self.as_ptr())) }
|
||||
}
|
||||
|
||||
/// Load certificates from their default locations.
|
||||
///
|
||||
/// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR`
|
||||
|
||||
@ -6,13 +6,15 @@ use crate::hash::MessageDigest;
|
||||
use crate::nid::Nid;
|
||||
use crate::pkey::{PKey, Private};
|
||||
use crate::rsa::Rsa;
|
||||
use crate::stack::Stack;
|
||||
use crate::stack::{Stack, Stackable};
|
||||
use crate::x509::crl::{X509CRLBuilder, X509Revoked, X509CRL};
|
||||
use crate::x509::extension::{
|
||||
AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName,
|
||||
SubjectKeyIdentifier,
|
||||
};
|
||||
use crate::x509::store::X509StoreBuilder;
|
||||
use crate::x509::{X509Extension, X509Name, X509Req, X509StoreContext, X509};
|
||||
use crate::x509::verify::X509VerifyFlags;
|
||||
use crate::x509::{X509Extension, X509Name, X509Req, X509StoreContext, X509VerifyError, X509};
|
||||
|
||||
mod trusted_first;
|
||||
|
||||
@ -21,6 +23,15 @@ fn pkey() -> PKey<Private> {
|
||||
PKey::from_rsa(rsa).unwrap()
|
||||
}
|
||||
|
||||
fn stack_of<T>(item: T) -> Stack<T>
|
||||
where
|
||||
T: Stackable,
|
||||
{
|
||||
let mut stack = Stack::new().expect("unable to initialize stack");
|
||||
stack.push(item).expect("failed to add to stack");
|
||||
stack
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cert_loading() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
@ -287,6 +298,7 @@ fn x509_builder() {
|
||||
|
||||
assert!(pkey.public_eq(&x509.public_key().unwrap()));
|
||||
assert!(x509.verify(&pkey).unwrap());
|
||||
assert_eq!(x509.extensions().unwrap().len(), 6);
|
||||
|
||||
let cn = x509
|
||||
.subject_name()
|
||||
@ -297,6 +309,48 @@ fn x509_builder() {
|
||||
assert_eq!(serial, x509.serial_number().to_bn().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn x509_crl_builder() {
|
||||
let mut builder = X509CRLBuilder::new().unwrap();
|
||||
|
||||
let mut name = X509Name::builder().unwrap();
|
||||
name.append_entry_by_nid(Nid::COMMONNAME, "foobar.com")
|
||||
.unwrap();
|
||||
let name = name.build();
|
||||
builder.set_issuer_name(&name).unwrap();
|
||||
|
||||
let mut serial = BigNum::new().unwrap();
|
||||
serial.rand(128, MsbOption::MAYBE_ZERO, false).unwrap();
|
||||
let serial_asn = serial.to_asn1_integer().unwrap();
|
||||
let revoked =
|
||||
X509Revoked::from_parts(&serial_asn, &Asn1Time::days_from_now(0).unwrap()).unwrap();
|
||||
builder.add_revoked(revoked).unwrap();
|
||||
|
||||
builder
|
||||
.set_last_update(&Asn1Time::days_from_now(0).unwrap())
|
||||
.unwrap();
|
||||
builder
|
||||
.set_next_update(&Asn1Time::days_from_now(30).unwrap())
|
||||
.unwrap();
|
||||
|
||||
let pkey = pkey();
|
||||
builder.sign(&pkey, MessageDigest::sha256()).unwrap();
|
||||
|
||||
let crl = builder.build();
|
||||
|
||||
let cn = crl.issuer().entries_by_nid(Nid::COMMONNAME).next().unwrap();
|
||||
assert_eq!(cn.data().as_slice(), b"foobar.com");
|
||||
let revoked_sn = crl
|
||||
.revoked()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.next()
|
||||
.unwrap()
|
||||
.serial_number();
|
||||
assert_eq!(serial, revoked_sn.to_bn().unwrap());
|
||||
assert!(crl.verify(&pkey).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn x509_extension_new() {
|
||||
assert!(X509Extension::new(None, None, "crlDistributionPoints", "section").is_err());
|
||||
@ -493,6 +547,278 @@ fn test_verify_fails() {
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_revoked() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let crl = include_bytes!("../../../test/crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(&ca).unwrap();
|
||||
store_bldr.add_crl(crl).unwrap();
|
||||
store_bldr
|
||||
.param_mut()
|
||||
.set_flags(X509VerifyFlags::CRL_CHECK | X509VerifyFlags::CRL_CHECK_ALL);
|
||||
let store = store_bldr.build();
|
||||
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
assert!(!context
|
||||
.init(&store, &cert, &chain, |c| c.verify_cert())
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_crl_signature() {
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
|
||||
let crl = include_bytes!("../../../test/bad_sig.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
assert!(!crl.verify(&ca.public_key().unwrap()).unwrap());
|
||||
|
||||
let crl = include_bytes!("../../../test/crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
assert!(crl.verify(&ca.public_key().unwrap()).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_untrusted_valid_crl() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(&ca).unwrap();
|
||||
store_bldr
|
||||
.param_mut()
|
||||
.set_flags(X509VerifyFlags::CRL_CHECK | X509VerifyFlags::CRL_CHECK_ALL);
|
||||
store_bldr.param_mut().set_time(1656633600); // 2022-07-01, everything is valid
|
||||
let store = store_bldr.build();
|
||||
|
||||
// cert is not revoked
|
||||
let crl = include_bytes!("../../../test/empty_crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
assert!(context
|
||||
.init(&store, &cert, &chain, |c| c
|
||||
.verify_cert_with_crls(stack_of(crl)))
|
||||
.unwrap());
|
||||
|
||||
// cert is revoked
|
||||
let crl = include_bytes!("../../../test/crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
context
|
||||
.init(&store, &cert, &chain, |c| {
|
||||
assert!(!c.verify_cert_with_crls(stack_of(crl)).unwrap());
|
||||
assert_eq!(c.verify_result(), Err(X509VerifyError::CERT_REVOKED));
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_untrusted_invalid_crl() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(&ca).unwrap();
|
||||
store_bldr
|
||||
.param_mut()
|
||||
.set_flags(X509VerifyFlags::CRL_CHECK | X509VerifyFlags::CRL_CHECK_ALL);
|
||||
store_bldr.param_mut().set_time(1656633600); // 2022-07-01, everything is valid
|
||||
let store = store_bldr.build();
|
||||
|
||||
// this CRL was issued by a different CA (not in the trusted store)
|
||||
let crl = include_bytes!("../../../test/invalid_crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
context
|
||||
.init(&store, &cert, &chain, |c| {
|
||||
assert!(!c.verify_cert_with_crls(stack_of(crl)).unwrap());
|
||||
assert_eq!(c.verify_result(), Err(X509VerifyError::UNABLE_TO_GET_CRL));
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// this CRL has an invalid signature
|
||||
let crl = include_bytes!("../../../test/bad_sig.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
context
|
||||
.init(&store, &cert, &chain, |c| {
|
||||
assert!(!c.verify_cert_with_crls(stack_of(crl)).unwrap());
|
||||
assert_eq!(
|
||||
c.verify_result(),
|
||||
Err(X509VerifyError::CRL_SIGNATURE_FAILURE)
|
||||
);
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_revoked_serial_numbers() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let cert_sn = cert.serial_number().to_bn().unwrap();
|
||||
|
||||
let crl = include_bytes!("../../../test/crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
crl.revoked()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|revoked| revoked.serial_number().to_bn().unwrap() == cert_sn)
|
||||
.count(),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_crl() {
|
||||
let crl = include_bytes!("../../../test/crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let digest = hex::encode(crl.digest(MessageDigest::sha1()).unwrap());
|
||||
|
||||
let serialized = crl.to_pem().unwrap();
|
||||
let crl_deserialized = X509CRL::from_pem(&serialized).unwrap();
|
||||
let new_digest = crl_deserialized.digest(MessageDigest::sha1()).unwrap();
|
||||
assert_eq!(digest, hex::encode(new_digest));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_crl_extensions() {
|
||||
let crl = include_bytes!("../../../test/crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let extensions = crl.extensions();
|
||||
assert_eq!(
|
||||
extensions
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|ext| ext.object().nid() == Nid::AUTHORITY_KEY_IDENTIFIER)
|
||||
.count(),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
extensions
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|ext| ext.object().nid() == Nid::CRL_NUMBER)
|
||||
.count(),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug_crl() {
|
||||
let crl = include_bytes!("../../../test/crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let debugged = format!("{:#?}", crl);
|
||||
assert!(debugged.contains(r#"countryName = "AU""#));
|
||||
assert!(debugged.contains(r#"stateOrProvinceName = "Some-State""#));
|
||||
assert!(debugged.contains(r#"organizationName = "Internet Widgits Pty Ltd""#));
|
||||
assert!(debugged.contains(r#"last_update: Jun 21 20:22:02 2022 GMT"#));
|
||||
assert!(debugged.contains(r#"next_update: Jun 18 20:22:02 2032 GMT"#));
|
||||
assert!(debugged.contains(r#"revocation_date: Jun 21 20:21:55 2022 GMT"#));
|
||||
assert!(debugged.contains(r#"serial_number: "8771f7bdee982fa5""#));
|
||||
assert!(debugged.contains(r#"object_nid: X509v3 Authority Key Identifier"#));
|
||||
assert!(debugged.contains(r#"object_nid: X509v3 CRL Number"#));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_time_valid() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(&ca).unwrap();
|
||||
store_bldr.param_mut().set_time(1656633600); // 2022-07-01, everything is valid
|
||||
let store = store_bldr.build();
|
||||
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
assert!(context
|
||||
.init(&store, &cert, &chain, |c| c.verify_cert())
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_time_expired() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(&ca).unwrap();
|
||||
store_bldr.param_mut().set_time(1786838400); // 2026-08-16, after the root and leaf expiration
|
||||
let store = store_bldr.build();
|
||||
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
assert!(!context
|
||||
.init(&store, &cert, &chain, |c| c.verify_cert())
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_time_too_soon() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(&ca).unwrap();
|
||||
store_bldr.param_mut().set_time(1262304000); // 2010-01-01, before the root and leaf issuance
|
||||
let store = store_bldr.build();
|
||||
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
assert!(!context
|
||||
.init(&store, &cert, &chain, |c| c.verify_cert())
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_time_crl() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
let cert = X509::from_pem(cert).unwrap();
|
||||
let ca = include_bytes!("../../../test/root-ca.pem");
|
||||
let ca = X509::from_pem(ca).unwrap();
|
||||
let crl = include_bytes!("../../../test/crl.pem");
|
||||
let crl = X509CRL::from_pem(crl).unwrap();
|
||||
let chain = Stack::new().unwrap();
|
||||
|
||||
let mut store_bldr = X509StoreBuilder::new().unwrap();
|
||||
store_bldr.add_cert(&ca).unwrap();
|
||||
store_bldr
|
||||
.param_mut()
|
||||
.set_flags(X509VerifyFlags::CRL_CHECK | X509VerifyFlags::CRL_CHECK_ALL);
|
||||
store_bldr.param_mut().set_time(1640995200); // 2022-01-01, before the CRL's issue date
|
||||
let store = store_bldr.build();
|
||||
|
||||
let mut context = X509StoreContext::new().unwrap();
|
||||
assert!(!context
|
||||
.init(&store, &cert, &chain, |c| c
|
||||
.verify_cert_with_crls(stack_of(crl)))
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_save_subject_der() {
|
||||
let cert = include_bytes!("../../../test/cert.pem");
|
||||
|
||||
11
boring/test/bad_sig.pem
Normal file
11
boring/test/bad_sig.pem
Normal file
@ -0,0 +1,11 @@
|
||||
-----BEGIN X509 CRL-----
|
||||
MIIBqTCBkjANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwK
|
||||
U29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkFw0y
|
||||
MjA2MTYyMjQ0NDFaFw0yMzA2MTYyMjQ0NDFaMBwwGgIJAIdx973umC+lFw0yMjA2
|
||||
MTYyMjIxMDVaMA0GCSqGSIb3DQEBBQUAA4IBAQApqFdwm46jxkJK8J5kprGm6cp8
|
||||
b7XMKB1epvhJGIkXHjp7O+2rxYGIExcNlM7jPcwqnUE0E50qGrqSMEupmtaBH03a
|
||||
fmmDKyhLema7KD64UaERLqWjaW2DPeX9VX6vL4ECc6zTVLxfmYzxVt6A9hhqCm3b
|
||||
fu8klWczGTa79r/WhTbA7uVf5+OI98da5tlxw+DlAQfqd34L2qq5aFg2dcTGqIdz
|
||||
3pxP6UlTyj0ZPK3tUtTpIURVO2/MX3j5V+QjWz81UeCv0gQcmOiIVSRUGwi9c6JY
|
||||
jDqBIvDY6df0riz5is1SS+D94sp1iovBlluwpq4kB8xyDuwt7vblkzleS2YU
|
||||
-----END X509 CRL-----
|
||||
13
boring/test/crl.pem
Normal file
13
boring/test/crl.pem
Normal file
@ -0,0 +1,13 @@
|
||||
-----BEGIN X509 CRL-----
|
||||
MIIB3jCBxwIBATANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJBVTETMBEGA1UE
|
||||
CAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk
|
||||
Fw0yMjA2MjEyMDIyMDJaFw0zMjA2MTgyMDIyMDJaMBwwGgIJAIdx973umC+lFw0y
|
||||
MjA2MjEyMDIxNTVaoDAwLjAfBgNVHSMEGDAWgBRs06UDqw1fLMmNipyIp4h3uDf9
|
||||
mjALBgNVHRQEBAICEAEwDQYJKoZIhvcNAQELBQADggEBABgsWr/sVZqXm1AzgGCJ
|
||||
JBMJW1oUY18aqroxo4kAIoI4QveLmHxi1Wm2I4dqdc6pM09SJhU5v5CfqpJ2BDc0
|
||||
JBfEk8KKi5O/OYyLcUKa4dEpAlYPgeDyLc6zF8rGLtJoDIYuk4JUeuuByoXt0Sh+
|
||||
7vx6UzuI7EH+mr4ZjnyAkD3f9jZy+mDcTm/+0REuh4iZ1AotE2YuQWQgxc1Y8TlD
|
||||
eK+ks1zBKI23s0hPBxJQunmz2k3Uu9Yf+Sg0KxCiDgJZWFiGSw/6DtnT0oYAFGaD
|
||||
mCyQWtwmS6zGBg+p76wNXkwyJMVvSDgrXSZ55bmImNmA38yKqOLOpB5i+FAS3r4V
|
||||
ApQ=
|
||||
-----END X509 CRL-----
|
||||
11
boring/test/empty_crl.pem
Normal file
11
boring/test/empty_crl.pem
Normal file
@ -0,0 +1,11 @@
|
||||
-----BEGIN X509 CRL-----
|
||||
MIIBijB0MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApT
|
||||
b21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQXDTIy
|
||||
MDYxNjIyNDU1NVoXDTIzMDYxNjIyNDU1NVowDQYJKoZIhvcNAQEFBQADggEBAEtx
|
||||
nBr3aI0qlegMVJsJn3GfkMzaPVTSTHuw76Dzdl9eGDj0hXzAzZW5k4WBHvaInzNT
|
||||
NKkeISoQJHLH981R9sQU2zA8sTESLTJGyCFu05Y6XhdmqX4ywmzVRjL6p/aoHNdZ
|
||||
H1mxgK16wG+Sv0pd+9qNJgC/cNFmNbWzbiEAi5kID4IUxSmId/FZsXsms1EjqDH4
|
||||
DFIwLIQO/kR5zwE5fZ5EjqUBdAxoSVHfD+OPKl4x2t8CHMmao+ih2FOfd70+NLBD
|
||||
2oxaJMjZL/SIf8vYxjpjimMR+7yJ5J5P1j/RBfG3LwwUDP0RtWLIvRQo/dZUyXTg
|
||||
LuC1vNuUoObe12z/NQ4=
|
||||
-----END X509 CRL-----
|
||||
11
boring/test/invalid_crl.pem
Normal file
11
boring/test/invalid_crl.pem
Normal file
@ -0,0 +1,11 @@
|
||||
-----BEGIN X509 CRL-----
|
||||
MIIBmTCBggIBATANBgkqhkiG9w0BAQsFADAhMQ0wCwYDVQQKDARGQUtFMRAwDgYD
|
||||
VQQDDAdGQUtFIENBFw0yMjA2MjcyMTQ3NTBaFw0zMjA2MjQyMTQ3NTBaMBwwGgIJ
|
||||
AIdx973umC+lFw0yMjA2MjcyMTQ2MjhaoA8wDTALBgNVHRQEBAICEAMwDQYJKoZI
|
||||
hvcNAQELBQADggEBAMXZRqTG28rnJSUPnVaqkmePSH15iz5Q/e4MdrM0cipXGuzX
|
||||
z5C8Oh0D2uT3ddawBxTosnbjuzlT7Tanbp3xCRBm9spRPxFbGaFWysBlG1aLTDka
|
||||
e9t9YeErg7wpwU6Qar0dzkLL5IkW3NArgbe8gP9PkYQxz/B0ESdHIPYJP1YBMNG6
|
||||
tLgEhg74Xs9UhOBInNsQB8qMGsEeOnzfiuvfspU/yvKHHzvjAcjeIrONLJaZcu2Y
|
||||
Dsfm5gOXGkHEm5/qJZ/IILoY0GSsSBekCAZda5+v3nvyjfRaBPyhx3Zv+rXh7u5z
|
||||
77bIzPZP60rslomacgEr4p/Y52E4GmKGV+X2+Hc=
|
||||
-----END X509 CRL-----
|
||||
Loading…
Reference in New Issue
Block a user