blob: 05901db96f7afb2e45c03148d0fe9edfd3e0cd44 [file] [log] [blame]
// Copyright 2026 The BoringSSL Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! X.509 certificate verification parameters.
use alloc::ffi::CString;
use core::{
ffi::{c_int, c_uint, c_ulong},
ptr::NonNull,
};
use bssl_macros::bssl_enum;
use crate::{check_lib_error, errors::PkiError, ffi::slice_into_ffi_raw_parts};
bssl_enum! {
/// Trust settings for certificate verification.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Trust: i8 {
/// Compatibility mode.
Compat = bssl_sys::X509_TRUST_COMPAT as i8,
/// Trust as a client SSL certificate.
SslClient = bssl_sys::X509_TRUST_SSL_CLIENT as i8,
/// Trust as a server SSL certificate.
SslServer = bssl_sys::X509_TRUST_SSL_SERVER as i8,
/// Trust as an email certificate.
Email = bssl_sys::X509_TRUST_EMAIL as i8,
/// Trust as an object signing certificate.
ObjectSign = bssl_sys::X509_TRUST_OBJECT_SIGN as i8,
/// Trust as a Time Stamping Authority.
Tsa = bssl_sys::X509_TRUST_TSA as i8,
}
}
bssl_enum! {
/// Purpose settings for certificate verification.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Purpose: i8 {
/// SSL client.
SslClient = bssl_sys::X509_PURPOSE_SSL_CLIENT as i8,
/// SSL server.
SslServer = bssl_sys::X509_PURPOSE_SSL_SERVER as i8,
/// Netscape SSL server.
NsSslServer = bssl_sys::X509_PURPOSE_NS_SSL_SERVER as i8,
/// S/MIME signing.
SmimeSign = bssl_sys::X509_PURPOSE_SMIME_SIGN as i8,
/// S/MIME encryption.
SmimeEncrypt = bssl_sys::X509_PURPOSE_SMIME_ENCRYPT as i8,
/// CRL signing.
CrlSign = bssl_sys::X509_PURPOSE_CRL_SIGN as i8,
/// Any purpose.
Any = bssl_sys::X509_PURPOSE_ANY as i8,
/// OCSP helper.
OcspHelper = bssl_sys::X509_PURPOSE_OCSP_HELPER as i8,
/// Timestamp signing.
TimestampSign = bssl_sys::X509_PURPOSE_TIMESTAMP_SIGN as i8,
}
}
bitflags::bitflags! {
/// Flags for X.509 certificate verification.
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct VerificationFlags: c_ulong {
/// Send issuer check callback.
const CB_ISSUER_CHECK = bssl_sys::X509_V_FLAG_CB_ISSUER_CHECK as c_ulong;
/// Use check time.
const USE_CHECK_TIME = bssl_sys::X509_V_FLAG_USE_CHECK_TIME as c_ulong;
/// Check CRL.
const CRL_CHECK = bssl_sys::X509_V_FLAG_CRL_CHECK as c_ulong;
/// Check CRL for all certificates in the chain.
const CRL_CHECK_ALL = bssl_sys::X509_V_FLAG_CRL_CHECK_ALL as c_ulong;
/// Ignore critical extensions.
const IGNORE_CRITICAL = bssl_sys::X509_V_FLAG_IGNORE_CRITICAL as c_ulong;
/// Strict X.509 checks.
const X509_STRICT = bssl_sys::X509_V_FLAG_X509_STRICT as c_ulong;
/// Allow proxy certificates.
const ALLOW_PROXY_CERTS = bssl_sys::X509_V_FLAG_ALLOW_PROXY_CERTS as c_ulong;
/// Check policies.
const POLICY_CHECK = bssl_sys::X509_V_FLAG_POLICY_CHECK as c_ulong;
/// Explicit policy.
const EXPLICIT_POLICY = bssl_sys::X509_V_FLAG_EXPLICIT_POLICY as c_ulong;
/// Inhibit any policy.
const INHIBIT_ANY = bssl_sys::X509_V_FLAG_INHIBIT_ANY as c_ulong;
/// Inhibit policy mapping.
const INHIBIT_MAP = bssl_sys::X509_V_FLAG_INHIBIT_MAP as c_ulong;
/// Notify policy.
const NOTIFY_POLICY = bssl_sys::X509_V_FLAG_NOTIFY_POLICY as c_ulong;
/// Extended CRL support.
const EXTENDED_CRL_SUPPORT = bssl_sys::X509_V_FLAG_EXTENDED_CRL_SUPPORT as c_ulong;
/// Use deltas.
const USE_DELTAS = bssl_sys::X509_V_FLAG_USE_DELTAS as c_ulong;
/// Check self-signed signature.
const CHECK_SS_SIGNATURE = bssl_sys::X509_V_FLAG_CHECK_SS_SIGNATURE as c_ulong;
/// Trusted first.
const TRUSTED_FIRST = bssl_sys::X509_V_FLAG_TRUSTED_FIRST as c_ulong;
/// Partial chain.
const PARTIAL_CHAIN = bssl_sys::X509_V_FLAG_PARTIAL_CHAIN as c_ulong;
/// No alternative chains.
const NO_ALT_CHAINS = bssl_sys::X509_V_FLAG_NO_ALT_CHAINS as c_ulong;
/// No check time.
const NO_CHECK_TIME = bssl_sys::X509_V_FLAG_NO_CHECK_TIME as c_ulong;
}
}
bitflags::bitflags! {
/// Flags for X.509 host checking.
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct HostFlags: c_uint {
/// Disable wildcard matching for DNS names.
const NO_WILDCARDS = bssl_sys::X509_CHECK_FLAG_NO_WILDCARDS as c_uint;
/// Disable the subject fallback, normally enabled when subjectAltNames is missing.
const NEVER_CHECK_SUBJECT = bssl_sys::X509_CHECK_FLAG_NEVER_CHECK_SUBJECT as c_uint;
}
}
/// X.509 verification parameters.
pub struct CertificateVerificationParams(NonNull<bssl_sys::X509_VERIFY_PARAM>);
// Safety:
// - `X509_VERIFY_PARAM` is a configuration struct and can be sent.
// - There are no methods that uses shared access, so no data race is possible.
unsafe impl Send for CertificateVerificationParams {}
unsafe impl Sync for CertificateVerificationParams {}
impl Drop for CertificateVerificationParams {
fn drop(&mut self) {
unsafe {
bssl_sys::X509_VERIFY_PARAM_free(self.0.as_ptr());
}
}
}
impl CertificateVerificationParams {
/// Create a new `X509VerifyParam`.
pub fn new() -> Self {
let param = unsafe {
// Safety: this call returns a new X509_VERIFY_PARAM or null on failure.
bssl_sys::X509_VERIFY_PARAM_new()
};
let Some(param) = NonNull::new(param) else {
panic!("allocation error")
};
Self(param)
}
pub(crate) fn ptr(&self) -> *mut bssl_sys::X509_VERIFY_PARAM {
self.0.as_ptr()
}
/// Extract the handle for cross-language interoperability.
///
/// # Safety
///
/// The handle **must** outlive the use sites.
/// Verify the callsite contract to honour the lifetime contracts.
pub unsafe fn as_raw(&self) -> *mut bssl_sys::X509_VERIFY_PARAM {
self.ptr()
}
/// Set the expected DNS hostname.
pub fn set_host(&mut self, host: &str) -> Result<&mut Self, PkiError> {
let c_host = CString::new(host).map_err(|_| PkiError::InternalNullBytes)?;
check_lib_error!(unsafe {
// Safety: the string has been NUL-terminated.
bssl_sys::X509_VERIFY_PARAM_set1_host(self.ptr(), c_host.as_ptr(), host.len())
});
Ok(self)
}
/// Add an expected DNS hostname.
pub fn add_host(&mut self, host: &str) -> Result<&mut Self, PkiError> {
let c_host = CString::new(host).map_err(|_| PkiError::InternalNullBytes)?;
check_lib_error!(unsafe {
// Safety: the string has been NUL-terminated.
bssl_sys::X509_VERIFY_PARAM_add1_host(self.ptr(), c_host.as_ptr(), host.len())
});
Ok(self)
}
/// Set the expected email address.
pub fn set_email(&mut self, email: &str) -> Result<&mut Self, PkiError> {
let c_email = CString::new(email).map_err(|_| PkiError::InternalNullBytes)?;
check_lib_error!(unsafe {
bssl_sys::X509_VERIFY_PARAM_set1_email(self.ptr(), c_email.as_ptr(), email.len())
});
Ok(self)
}
/// Set the expected IP address.
pub fn set_ip(&mut self, ip: &[u8]) -> Result<&mut Self, PkiError> {
if !matches!(ip.len(), 4 | 16) {
return Err(PkiError::InvalidIp);
}
let (ip, len) = slice_into_ffi_raw_parts(ip);
check_lib_error!(unsafe {
// Safety: the slice pointer is not null.
bssl_sys::X509_VERIFY_PARAM_set1_ip(self.ptr(), ip, len)
});
Ok(self)
}
/// Set the expected IP address from an ASCII string, as defined by
pub fn set_ip_asc(&mut self, ip: &str) -> Result<&mut Self, PkiError> {
let c_ip = CString::new(ip).map_err(|_| PkiError::InvalidIp)?;
check_lib_error!(unsafe {
// Safety: the string has been NUL-terminated.
bssl_sys::X509_VERIFY_PARAM_set1_ip_asc(self.ptr(), c_ip.as_ptr())
});
Ok(self)
}
/// Set the verification depth.
pub fn set_depth(&mut self, depth: u16) -> &mut Self {
unsafe {
// Safety: the validity of the handle `self.0` is witnessed by `self`.
bssl_sys::X509_VERIFY_PARAM_set_depth(self.ptr(), depth.into());
}
self
}
/// Set the verification purpose.
pub fn set_purpose(&mut self, purpose: Purpose) -> Result<&mut Self, PkiError> {
check_lib_error!(unsafe {
// Safety:
// - the validity of the handle `self.0` is witnessed by `self`.
// - the `purpose` value is valid per construction.
bssl_sys::X509_VERIFY_PARAM_set_purpose(self.ptr(), purpose as c_int)
});
Ok(self)
}
/// Set the verification trust.
pub fn set_trust(&mut self, trust: Trust) -> Result<&mut Self, PkiError> {
check_lib_error!(unsafe {
// Safety: the validity of the handle `self.0` is witnessed by `self`.
bssl_sys::X509_VERIFY_PARAM_set_trust(self.ptr(), trust as c_int)
});
Ok(self)
}
/// Set time that verification will run against, specified as Unix timestamp.
pub fn set_time_posix(&mut self, time: i64) {
unsafe {
// Safety: the validity of the handle `self.0` is witnessed by `self`.
bssl_sys::X509_VERIFY_PARAM_set_time_posix(self.ptr(), time);
}
}
/// Set verification flags.
pub fn set_flags(&mut self, flags: VerificationFlags) -> Result<&mut Self, PkiError> {
check_lib_error!(unsafe {
// Safety: the flag bits are valid by construction.
bssl_sys::X509_VERIFY_PARAM_set_flags(self.ptr(), flags.bits())
});
Ok(self)
}
/// Clear verification flags.
pub fn clear_flags(&mut self, flags: VerificationFlags) -> Result<&mut Self, PkiError> {
check_lib_error!(unsafe {
// Safety:
// - the validity of the handle `self.0` is witnessed by `self`.
// - the flag bits are valid by construction.
bssl_sys::X509_VERIFY_PARAM_clear_flags(self.ptr(), flags.bits())
});
Ok(self)
}
/// Set the host flags.
pub fn set_hostflags(&mut self, flags: HostFlags) {
unsafe {
// Safety:
// - the validity of the handle `self.0` is witnessed by `self`.
// - the flag bits are valid by construction.
bssl_sys::X509_VERIFY_PARAM_set_hostflags(self.ptr(), flags.bits());
}
}
}