| // 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()); |
| } |
| } |
| } |