| // 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. | |
| use alloc::{ | |
| boxed::Box, | |
| ffi::CString, // | |
| }; | |
| use core::{ | |
| ffi::CStr, | |
| ptr::null, // | |
| }; | |
| use bssl_x509::{ | |
| params::CertificateVerificationParams, | |
| store::X509Store, // | |
| }; | |
| use super::{ | |
| Client, | |
| Server, | |
| TlsConnection, | |
| TlsConnectionBuilder, | |
| lifecycle::{ | |
| EstablishedTlsConnection, | |
| TlsConnectionInHandshake, // | |
| }, | |
| methods::HasPrivateKeyMethods, | |
| methods::HasTlsConnectionMethod, // | |
| }; | |
| use crate::{ | |
| check_lib_error, | |
| config::ConfigurationError, | |
| credentials::{ | |
| CertificateType, | |
| CertificateVerificationMode, | |
| DistinguishedName, | |
| PrivateKeyDelegate, | |
| SignatureAlgorithm, | |
| TlsCredential, | |
| VerifyCertificate, | |
| cert_cb, | |
| get_peer_certificate_type, | |
| get_peer_raw_public_key, | |
| select_cert::{ | |
| ClientCertificateSelector, | |
| ServerCertificateSelector, | |
| select_cert_cb, // | |
| }, // | |
| }, | |
| errors::Error, | |
| ffi::slice_into_ffi_raw_parts, | |
| has_duplicates, // | |
| }; | |
| /// # Asynchronous private key operations | |
| impl<R, M> TlsConnectionBuilder<R, M> | |
| where | |
| M: HasPrivateKeyMethods + HasTlsConnectionMethod, | |
| { | |
| /// Set the private key delegate. | |
| /// | |
| /// This will override the [`crate::context::TlsContext`] private key delegate. | |
| pub fn with_private_key_delegate( | |
| &mut self, | |
| key_method: Option<Box<dyn PrivateKeyDelegate>>, | |
| ) -> &mut Self { | |
| self.in_handshake().set_private_key_delegate(key_method); | |
| self | |
| } | |
| } | |
| /// # Asynchronous private key operations | |
| impl<R, M> TlsConnectionInHandshake<'_, R, M> | |
| where | |
| M: HasPrivateKeyMethods + HasTlsConnectionMethod, | |
| { | |
| /// Set the private key delegate. | |
| /// | |
| /// This will override the [`crate::context::TlsContext`] private key delegate. | |
| pub fn set_private_key_delegate( | |
| &mut self, | |
| key_method: Option<Box<dyn PrivateKeyDelegate>>, | |
| ) -> &mut Self { | |
| if key_method.is_some() { | |
| unsafe { | |
| // Safety: we only install our own vtable. | |
| bssl_sys::SSL_set_private_key_method( | |
| self.ptr(), | |
| <M as HasPrivateKeyMethods>::METHODS, | |
| ); | |
| } | |
| } else { | |
| unsafe { | |
| // Safety: we only uninstall the vtable. | |
| bssl_sys::SSL_set_private_key_method(self.ptr(), core::ptr::null()); | |
| } | |
| } | |
| self.0.get_connection_methods().private_key_delegate = key_method; | |
| self | |
| } | |
| } | |
| /// # Certificate verification | |
| impl<R, M> TlsConnectionBuilder<R, M> | |
| where | |
| M: HasTlsConnectionMethod, | |
| { | |
| /// Configure the certificate verification mode. | |
| pub fn with_certificate_verification_mode( | |
| &mut self, | |
| mode: CertificateVerificationMode, | |
| ) -> &mut Self { | |
| self.as_in_handshake() | |
| .set_certificate_verification_mode(mode); | |
| self | |
| } | |
| } | |
| /// # Custom certificate verification | |
| impl<R, M> TlsConnectionBuilder<R, M> | |
| where | |
| M: HasPrivateKeyMethods + HasTlsConnectionMethod, | |
| { | |
| /// Configure the certificate verifier. | |
| /// | |
| /// See [`VerifyCertificate`] for how to implement a custom verifier. | |
| /// | |
| /// If raw public key authentication, per [RFC 7250], is configured, | |
| /// the authentication through this mechanism will **fail** unless a certificate verifier | |
| /// is configured. | |
| /// | |
| /// [RFC 7250]: <https://datatracker.ietf.org/doc/html/rfc7250> | |
| pub fn with_certificate_verifier<V>( | |
| &mut self, | |
| mode: CertificateVerificationMode, | |
| verifier: V, | |
| ) -> &mut Self | |
| where | |
| V: VerifyCertificate + 'static, | |
| { | |
| self.as_in_handshake() | |
| .set_certificate_verifier(mode, verifier); | |
| self | |
| } | |
| /// Remove custom certificate verifier. | |
| pub fn without_certificate_verifier(&mut self, mode: CertificateVerificationMode) -> &mut Self { | |
| self.as_in_handshake().remove_certificate_verifier(mode); | |
| self | |
| } | |
| } | |
| impl<M> TlsConnectionBuilder<Client, M> | |
| where | |
| M: HasTlsConnectionMethod, | |
| { | |
| /// Set the list of available client certificate types. | |
| pub fn with_available_client_cert_types( | |
| &mut self, | |
| types: &[CertificateType], | |
| ) -> Result<&mut Self, Error> { | |
| let (ptr, len) = slice_into_ffi_raw_parts(types); | |
| check_lib_error!(unsafe { | |
| // Safety: | |
| // - `self.ptr()` is a valid `SSL` handle. | |
| // - `ptr` is a valid pointer to an array of `i32` representing certificate types. | |
| // - `len` is the number of elements in the array. | |
| // - The function copies the data, so the pointer only needs to be valid for the call. | |
| bssl_sys::SSL_set1_available_client_cert_types(self.ptr(), ptr as *const _, len) | |
| }); | |
| Ok(self) | |
| } | |
| } | |
| /// # Select certificate - Server | |
| impl<M> TlsConnectionBuilder<Server, M> | |
| where | |
| M: HasTlsConnectionMethod, | |
| { | |
| /// Set certificate selection callback on **server** side. | |
| pub fn with_server_side_certificate_callback<T: 'static + ServerCertificateSelector<M>>( | |
| &mut self, | |
| cb: T, | |
| ) -> &mut Self { | |
| self.as_in_handshake() | |
| .set_server_side_certificate_callback(cb); | |
| self | |
| } | |
| } | |
| /// # Select certificate - Client | |
| impl<M> TlsConnectionBuilder<Client, M> | |
| where | |
| M: HasTlsConnectionMethod, | |
| { | |
| /// Set certificate selection callback on **client** side. | |
| pub fn with_client_side_certificate_callback<T: 'static + ClientCertificateSelector<M>>( | |
| &mut self, | |
| cb: T, | |
| ) -> &mut Self { | |
| self.as_in_handshake() | |
| .set_client_side_certificate_callback(cb); | |
| self | |
| } | |
| } | |
| /// # Custom certificate verification | |
| impl<R, M> TlsConnectionInHandshake<'_, R, M> | |
| where | |
| M: HasTlsConnectionMethod, | |
| { | |
| /// Configure the certificate verification mode. | |
| pub fn set_certificate_verification_mode( | |
| &mut self, | |
| mode: CertificateVerificationMode, | |
| ) -> &mut Self { | |
| unsafe { | |
| // Safety: this method only updates the mode value. | |
| bssl_sys::SSL_set_verify(self.ptr(), mode as _, None); | |
| } | |
| self | |
| } | |
| /// Configure the certificate verifier. | |
| pub fn set_certificate_verifier<V>( | |
| &mut self, | |
| mode: CertificateVerificationMode, | |
| verifier: V, | |
| ) -> &mut Self | |
| where | |
| V: VerifyCertificate + 'static, | |
| { | |
| unsafe { | |
| // Safety: we only install our own vtable. | |
| bssl_sys::SSL_set_custom_verify( | |
| self.ptr(), | |
| mode as _, | |
| Some(cert_cb::<super::methods::RustConnectionMethods<M>>), | |
| ); | |
| } | |
| self.0.get_connection_methods().verify_certificate_methods = Some(Box::new(verifier) as _); | |
| self | |
| } | |
| /// Remove custom certificate verifier. | |
| pub fn remove_certificate_verifier(&mut self, mode: CertificateVerificationMode) -> &mut Self { | |
| unsafe { | |
| // Safety: we only uninstall the vtable. | |
| bssl_sys::SSL_set_custom_verify(self.ptr(), mode as _, None); | |
| } | |
| self.0.get_connection_methods().verify_certificate_methods = None; | |
| self | |
| } | |
| /// Get the certificate verification mode set by [`Self::set_certificate_verification_mode`]. | |
| pub fn get_certificate_verification_mode(&self) -> Option<CertificateVerificationMode> { | |
| unsafe { | |
| // Safety: the validity of the handle `self.0` is witnessed by `self`. | |
| bssl_sys::SSL_get_verify_mode(self.ptr()) | |
| } | |
| .try_into() | |
| .ok() | |
| } | |
| } | |
| /// # Authenticating with the peer | |
| impl<R, M> TlsConnectionInHandshake<'_, R, M> | |
| where | |
| M: HasTlsConnectionMethod, | |
| { | |
| /// Append `credential` to the list of credentials of this connection. | |
| /// | |
| /// Earlier calls to this method appends a credential that is preferred over those added | |
| /// in the later calls. | |
| pub fn add_credential(&mut self, credential: &TlsCredential) -> Result<&mut Self, Error> { | |
| check_lib_error!(unsafe { | |
| // Safety: `credential` is still valid. | |
| bssl_sys::SSL_add1_credential(self.ptr(), credential.ptr()) | |
| }); | |
| Ok(self) | |
| } | |
| /// Clear all credentials. | |
| pub fn clear_credentials(&mut self) -> &mut Self { | |
| unsafe { | |
| // Safety: `credential` is still valid. | |
| bssl_sys::SSL_certs_clear(self.ptr()); | |
| } | |
| self | |
| } | |
| } | |
| /// # Select certificate - Server | |
| impl<M> TlsConnectionInHandshake<'_, Server, M> | |
| where | |
| M: HasTlsConnectionMethod, | |
| { | |
| /// Set certificate selection callback on **server** side. | |
| pub fn set_server_side_certificate_callback<T: 'static + ServerCertificateSelector<M>>( | |
| &mut self, | |
| cb: T, | |
| ) -> &mut Self { | |
| let conn = self.ptr(); | |
| let methods = self.0.get_connection_methods(); | |
| methods.server_cert_cb = Some(Box::new(cb) as _); | |
| unsafe { | |
| // Safety: we only install our own vtable. | |
| bssl_sys::SSL_set_cert_cb( | |
| conn, | |
| Some(select_cert_cb::<super::methods::RustConnectionMethods<M>, M>), | |
| core::ptr::null_mut(), | |
| ); | |
| } | |
| self | |
| } | |
| } | |
| /// # Select certificate - Client | |
| impl<M> TlsConnectionInHandshake<'_, Client, M> | |
| where | |
| M: HasTlsConnectionMethod, | |
| { | |
| /// Set certificate selection callback on **client** side. | |
| pub fn set_client_side_certificate_callback<T: 'static + ClientCertificateSelector<M>>( | |
| &mut self, | |
| cb: T, | |
| ) -> &mut Self { | |
| let conn = self.ptr(); | |
| let methods = self.0.get_connection_methods(); | |
| methods.client_cert_cb = Some(Box::new(cb) as _); | |
| unsafe { | |
| // Safety: we only install our own vtable. | |
| bssl_sys::SSL_set_cert_cb( | |
| conn, | |
| Some(select_cert_cb::<super::methods::RustConnectionMethods<M>, M>), | |
| core::ptr::null_mut(), | |
| ); | |
| } | |
| self | |
| } | |
| } | |
| /// # Certificate verification - Signed Certificate Timestamps | |
| impl<M> TlsConnectionInHandshake<'_, Client, M> { | |
| /// Enable signed certificate timestamps. | |
| /// | |
| /// This method will instruct the client connections to request Signed Certificate Timestamps. | |
| /// See [RFC 6962] for more information. | |
| /// | |
| /// [RFC 6962]: <https://datatracker.ietf.org/doc/html/rfc6962> | |
| pub fn enable_signed_certificate_timestamps(&mut self) -> &mut Self { | |
| unsafe { | |
| // Safety: the validity of the handle `self.0` is witnessed by `self`. | |
| bssl_sys::SSL_enable_signed_cert_timestamps(self.ptr()); | |
| } | |
| self | |
| } | |
| } | |
| /// # Certificate verification - X.509 Certificate Store | |
| impl<M> TlsConnectionInHandshake<'_, Client, M> { | |
| /// Set certificate verification store. | |
| pub fn set_certificate_store(&mut self, store: &X509Store) -> &mut Self { | |
| unsafe { | |
| // Safety: | |
| // - the validity of the handle `self.0` is witnessed by `self`. | |
| // - when pending handshake, the assignment is always successful. | |
| // - `SSL_set1_verify_cert_store` bumps the ref-count on the store. | |
| bssl_sys::SSL_set1_verify_cert_store(self.ptr(), store.as_mut_ptr()); | |
| } | |
| self | |
| } | |
| /// Set a preference list of signature algorithms. | |
| /// | |
| /// This method returns [`ConfigurationError::InvalidParameters`] if the list of algorithms | |
| /// contains duplicate entries. | |
| pub fn set_certificate_verification_preferences( | |
| &mut self, | |
| algs: &[SignatureAlgorithm], | |
| ) -> Result<&mut Self, Error> { | |
| let algs: &[u16] = unsafe { | |
| // Safety: `SignatureAlgorithm` has a `repr(u16)` and maps to preferences correctly | |
| // by construction. | |
| core::mem::transmute(algs) | |
| }; | |
| if has_duplicates(algs) { | |
| return Err(Error::Configuration(ConfigurationError::InvalidParameters)); | |
| } | |
| let (prefs, prefs_len) = slice_into_ffi_raw_parts(algs); | |
| check_lib_error!(unsafe { | |
| // Safety: the validity of the handle `self.0` is witnessed by `self`. | |
| bssl_sys::SSL_set_verify_algorithm_prefs(self.ptr(), prefs, prefs_len) | |
| }); | |
| Ok(self) | |
| } | |
| } | |
| /// # Certificate verification - Server Host Name | |
| impl<M> TlsConnectionInHandshake<'_, Client, M> { | |
| /// Set host name. | |
| pub fn set_host(&mut self, host_name: &str) -> Result<&mut Self, Error> { | |
| let host_name = CString::new(host_name) | |
| .map_err(|_| Error::Configuration(ConfigurationError::InvalidString))?; | |
| check_lib_error!(unsafe { | |
| // Safety: | |
| // - the validity of the handle `self.0` is witnessed by `self`. | |
| // - the host name string has been sanitised for internal NUL-bytes and NUL-terminated. | |
| bssl_sys::SSL_set1_host(self.ptr(), host_name.as_ptr()) | |
| }); | |
| Ok(self) | |
| } | |
| /// Set SNI extension hostname. | |
| pub fn set_tlsext_host_name(&mut self, host_name: &str) -> Result<&mut Self, Error> { | |
| let host_name = CString::new(host_name) | |
| .map_err(|_| Error::Configuration(ConfigurationError::InvalidString))?; | |
| check_lib_error!(unsafe { | |
| // Safety: | |
| // - the validity of the handle `self.0` is witnessed by `self`. | |
| // - the host name string has been sanitised for internal NUL-bytes and NUL-terminated. | |
| // - BoringSSL copies the string internally, so the pointer does not need to outlive the call. | |
| bssl_sys::SSL_set_tlsext_host_name(self.0.ptr(), host_name.as_ptr()) | |
| }); | |
| Ok(self) | |
| } | |
| } | |
| /// # Certificate verification - Certificate Chain Verification | |
| impl<R, M> TlsConnectionInHandshake<'_, R, M> { | |
| /// Set depth of a potential certificate chain acceptable. | |
| pub fn set_verify_depth(&mut self, depth: u16) -> Result<&mut Self, Error> { | |
| let depth = depth | |
| .try_into() | |
| .map_err(|_| Error::Configuration(ConfigurationError::ValueOutOfRange))?; | |
| unsafe { | |
| // Safety: the validity of the handle `self.0` is witnessed by `self`. | |
| bssl_sys::SSL_set_verify_depth(self.ptr(), depth); | |
| } | |
| Ok(self) | |
| } | |
| /// Get certificate verification depth. | |
| /// | |
| /// This method returns [`None`] if the depth is set but does not fit in a [`u16`]. | |
| pub fn get_verify_depth(&self) -> Option<u16> { | |
| unsafe { | |
| // Safety: the validity and state of the handle `self.0` is witnessed by `self`. | |
| bssl_sys::SSL_get_verify_depth(self.ptr()).try_into().ok() | |
| } | |
| } | |
| /// Set certificate verification parameters. | |
| pub fn set_certificate_verification_params( | |
| &mut self, | |
| params: &CertificateVerificationParams, | |
| ) -> Result<&mut Self, Error> { | |
| check_lib_error!(unsafe { | |
| // Safety: | |
| // - the validity of the handle `self.0` is witnessed by `self`. | |
| // - `SSL_set1_param` claims shared ownership of `params` by bumping ref-count. | |
| bssl_sys::SSL_set1_param(self.ptr(), params.as_ptr()) | |
| }); | |
| Ok(self) | |
| } | |
| } | |
| /// # Raw Public Key | |
| impl<R, M> TlsConnectionInHandshake<'_, R, M> { | |
| /// Set acceptable peer certificate types | |
| pub fn set_accepted_peer_cert_types( | |
| &mut self, | |
| types: &[CertificateType], | |
| ) -> Result<&mut Self, Error> { | |
| let (types, types_len) = slice_into_ffi_raw_parts(types); | |
| check_lib_error!(unsafe { | |
| // Safety: | |
| // - `self.ptr()` is a valid handle. | |
| // - `CertificateType` is a `u8` with acceptable values by construction. | |
| bssl_sys::SSL_set1_accepted_peer_cert_types(self.ptr(), types as *const _, types_len) | |
| }); | |
| Ok(self) | |
| } | |
| } | |
| impl<'a, R, M> EstablishedTlsConnection<'a, R, M> { | |
| /// Export keying material from this connection into a buffer of a chosen length, | |
| /// as per [RFC 5705]. | |
| /// | |
| /// To derive the same value, both sides of a connection must use the same output length, label | |
| /// and context. | |
| /// - In TLS 1.2 and earlier, using a zero-length context and using no context would give | |
| /// different output. | |
| /// - In TLS 1.3 and later, the output length controls the derivation so that a truncated | |
| /// longer export will not match a shorter export. | |
| /// | |
| /// [RFC 5705]: <https://datatracker.ietf.org/doc/html/rfc5705> | |
| pub fn export_keying_material( | |
| &self, | |
| label: &CStr, | |
| context: Option<&[u8]>, | |
| output: &mut [u8], | |
| ) -> Result<(), Error> { | |
| let (context, context_len, use_context) = if let Some(context) = context { | |
| let (context, context_len) = slice_into_ffi_raw_parts(context); | |
| (context, context_len, 1) | |
| } else { | |
| (null(), 0, 0) | |
| }; | |
| let (output, output_len) = crate::ffi::mut_slice_into_ffi_raw_parts(output); | |
| check_lib_error!(unsafe { | |
| // Safety: | |
| // - the validity of the handle `self.0` is witnessed by `self`. | |
| bssl_sys::SSL_export_keying_material( | |
| self.ptr(), | |
| output, | |
| output_len, | |
| label.as_ptr(), | |
| label.count_bytes(), | |
| context, | |
| context_len, | |
| use_context, | |
| ) | |
| }); | |
| Ok(()) | |
| } | |
| } | |
| impl<R, M> TlsConnection<R, M> { | |
| /// Get the peer's [`CertificateType`]. | |
| /// | |
| /// Returns [`None`] if the handshake has not completed. | |
| pub fn get_peer_certificate_type(&self) -> Option<CertificateType> { | |
| if self.is_in_handshake() { | |
| return None; | |
| } | |
| get_peer_certificate_type(self.ptr()) | |
| } | |
| /// Get the peer's raw public key as DER-encoded `SubjectPublicKeyInfo`. | |
| /// | |
| /// Returns [`None`] if the handshake has not completed. | |
| pub fn get_peer_raw_public_key(&self) -> Option<Vec<u8>> { | |
| if self.is_in_handshake() { | |
| return None; | |
| } | |
| get_peer_raw_public_key(self.ptr()) | |
| } | |
| } | |
| /// # Certificate authorities - Client | |
| /// | |
| /// TLS can send a list of supported certificate authorities to guide the peer in certificate | |
| /// selection. | |
| impl<M> TlsConnectionInHandshake<'_, Client, M> { | |
| /// This setting advertises the list of certificate authorities names in the | |
| /// `certificate_authorities` extension to send the client. | |
| pub fn set_ca_acceptable_by_client( | |
| &mut self, | |
| names: impl IntoIterator<Item = DistinguishedName>, | |
| ) -> &mut Self { | |
| unsafe { | |
| // Safety: this call only transfers the ownership of the stack. | |
| bssl_sys::SSL_set0_CA_names( | |
| self.ptr(), | |
| DistinguishedName::into_crypto_buffer_stack(names), | |
| ) | |
| } | |
| self | |
| } | |
| } | |
| /// # Certificate authorities - Server | |
| /// | |
| /// TLS can send a list of supported certificate authorities to guide the peer in certificate | |
| /// selection. | |
| impl<M> TlsConnectionInHandshake<'_, Server, M> { | |
| /// This setting advertises the list of certificate authorities names in the | |
| /// `certificate_authorities` extension to send the server. | |
| pub fn set_ca_acceptable_by_server( | |
| &mut self, | |
| names: impl IntoIterator<Item = DistinguishedName>, | |
| ) -> &mut Self { | |
| unsafe { | |
| // Safety: this call only transfers the ownership of the stack. | |
| bssl_sys::SSL_set0_client_CAs( | |
| self.ptr(), | |
| DistinguishedName::into_crypto_buffer_stack(names), | |
| ) | |
| } | |
| self | |
| } | |
| } |