| // 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_crypto::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)] | |
| #[non_exhaustive] | |
| 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)] | |
| #[non_exhaustive] | |
| 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. | |
| #[derive(Debug, Copy, Clone, PartialEq, Eq)] | |
| pub struct VerificationFlags: c_ulong { | |
| /// Use check time. | |
| const USE_CHECK_TIME = bssl_sys::X509_V_FLAG_USE_CHECK_TIME as c_ulong; | |
| /// Explicit policy, which mandates assertions of the policy OIDs in the final certificate | |
| /// chain. | |
| /// | |
| /// See [RFC 5280], §6.1.1. | |
| /// | |
| /// [RFC 5280]: <https://datatracker.ietf.org/doc/html/rfc5280#section-6.1.1> | |
| const EXPLICIT_POLICY = bssl_sys::X509_V_FLAG_EXPLICIT_POLICY as c_ulong; | |
| /// Inhibit the `anyPolicy`. | |
| /// | |
| /// See [RFC 5280], §6.1.1. | |
| /// | |
| /// [RFC 5280]: <https://datatracker.ietf.org/doc/html/rfc5280#section-6.1.1> | |
| const INHIBIT_ANY = bssl_sys::X509_V_FLAG_INHIBIT_ANY as c_ulong; | |
| /// Inhibit policy mapping. | |
| /// | |
| /// See [RFC 5280], §6.1.1. | |
| /// | |
| /// [RFC 5280]: <https://datatracker.ietf.org/doc/html/rfc5280#section-6.1.1> | |
| const INHIBIT_MAP = bssl_sys::X509_V_FLAG_INHIBIT_MAP as c_ulong; | |
| /// Treat all trusted certificates as trust anchors regardless of the | |
| /// [`CertificateVerificationParams::set_trust`] setting. | |
| const PARTIAL_CHAIN = bssl_sys::X509_V_FLAG_PARTIAL_CHAIN as c_ulong; | |
| /// Disable all time checks during certificate verification. | |
| const NO_CHECK_TIME = bssl_sys::X509_V_FLAG_NO_CHECK_TIME as c_ulong; | |
| } | |
| } | |
| bitflags::bitflags! { | |
| /// Flags for X.509 host checking. | |
| #[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 `CertificateVerificationParams`. | |
| 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_ptr(&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. | |
| 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()); | |
| } | |
| } | |
| } |