| // 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. |
| |
| //! TLS Ciphers |
| |
| use core::{ |
| ffi::CStr, |
| ptr::NonNull, // |
| }; |
| |
| use crate::config::{ |
| CipherSuite, |
| ProtocolVersion, // |
| }; |
| |
| /// A TLS cipher suite. |
| #[derive(Clone, Copy)] |
| #[repr(transparent)] |
| pub struct CipherDescription(pub(crate) NonNull<bssl_sys::SSL_CIPHER>); |
| |
| // Safety: `SSL_CIPHER` is immutable and thread-safe. |
| unsafe impl Send for CipherDescription {} |
| unsafe impl Sync for CipherDescription {} |
| |
| impl CipherDescription { |
| /// Returns the cipher suite corresponding to the given RFC value. |
| pub fn from_value(value: CipherSuite) -> Option<Self> { |
| let ptr = unsafe { |
| // Safety: the cipher code is valid by construction. |
| bssl_sys::SSL_get_cipher_by_value(value.0) |
| }; |
| Self::from_ptr(ptr) |
| } |
| |
| pub(crate) fn from_ptr(ptr: *const bssl_sys::SSL_CIPHER) -> Option<Self> { |
| NonNull::new(ptr as *mut _).map(Self) |
| } |
| |
| pub(crate) fn ptr(self) -> *const bssl_sys::SSL_CIPHER { |
| self.0.as_ptr() |
| } |
| |
| /// Returns the two-byte protocol ID of the cipher suite per [IANA]. |
| /// |
| /// [IANA]: <https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4> |
| pub fn id(&self) -> u16 { |
| unsafe { |
| // Safety: the cipher handle is valid. |
| bssl_sys::SSL_CIPHER_get_protocol_id(self.ptr()) |
| } |
| } |
| |
| /// Returns true if the cipher suite uses an AEAD cipher. |
| pub fn is_aead(&self) -> bool { |
| unsafe { |
| // Safety: the cipher handle is valid. |
| bssl_sys::SSL_CIPHER_is_aead(self.ptr()) != 0 |
| } |
| } |
| |
| /// Returns true if the cipher suite uses a block cipher. |
| pub fn is_block_cipher(&self) -> bool { |
| unsafe { |
| // Safety: the cipher handle is valid. |
| bssl_sys::SSL_CIPHER_is_block_cipher(self.ptr()) != 0 |
| } |
| } |
| |
| /// Returns the minimum protocol version required for the cipher suite. |
| pub fn min_version(&self) -> ProtocolVersion { |
| let version = unsafe { |
| // Safety: the cipher handle is valid. |
| bssl_sys::SSL_CIPHER_get_min_version(self.ptr()) |
| }; |
| let Ok(version) = ProtocolVersion::try_from(version) else { |
| unreachable!("BoringSSL invariant violated") |
| }; |
| version |
| } |
| |
| /// Returns the maximum protocol version that supports the cipher suite. |
| pub fn max_version(&self) -> ProtocolVersion { |
| let version = unsafe { |
| // Safety: the cipher handle is valid. |
| bssl_sys::SSL_CIPHER_get_max_version(self.ptr()) |
| }; |
| let Ok(version) = ProtocolVersion::try_from(version) else { |
| unreachable!("BoringSSL invariant violated") |
| }; |
| version |
| } |
| |
| /// Returns the standard IETF name for the cipher suite. |
| pub fn standard_name(&self) -> &'static str { |
| // Safety: the cipher handle is valid. |
| let ptr = unsafe { bssl_sys::SSL_CIPHER_standard_name(self.ptr()) }; |
| // Safety: BoringSSL returns cipher names as static strings which are valid UTF-8. |
| unsafe { CStr::from_ptr(ptr).to_str().unwrap() } |
| } |
| |
| /// Returns a string that describes the key-exchange method used by the cipher suite. |
| pub fn kx_name(&self) -> &'static str { |
| // Safety: the cipher handle is valid. |
| let ptr = unsafe { bssl_sys::SSL_CIPHER_get_kx_name(self.ptr()) }; |
| // Safety: BoringSSL returns key exchange group names as static strings |
| // which are valid UTF-8. |
| unsafe { CStr::from_ptr(ptr).to_str().unwrap() } |
| } |
| } |
| |
| impl core::fmt::Debug for CipherDescription { |
| fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| f.debug_struct("Cipher") |
| .field("id", &self.id()) |
| .field("name", &self.standard_name()) |
| .finish() |
| } |
| } |