rust: bssl-tls: Add cipher type

Bug: 479599893
Signed-off-by: Xiangfei Ding <xfding@google.com>
Change-Id: I0805b41e484c585a6f917f39ed168f816a6a6964
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/98793
Reviewed-by: Rudolf Polzer <rpolzer@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/rust/bssl-tls/src/ciphers.rs b/rust/bssl-tls/src/ciphers.rs
new file mode 100644
index 0000000..3f53759
--- /dev/null
+++ b/rust/bssl-tls/src/ciphers.rs
@@ -0,0 +1,129 @@
+// 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()
+    }
+}
diff --git a/rust/bssl-tls/src/config.rs b/rust/bssl-tls/src/config.rs
index 7e5c23f..eaebcf1 100644
--- a/rust/bssl-tls/src/config.rs
+++ b/rust/bssl-tls/src/config.rs
@@ -131,22 +131,6 @@
     }
 }
 
-/// Cipher information
-#[derive(Clone)]
-#[non_exhaustive]
-pub struct CipherInfo {
-    /// Protocol ID as assigned by [IANA](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4)
-    pub id: u32,
-    /// IETF Name of the cipher
-    pub ietf_name: String,
-    /// Indicates whether this cipher is an AEAD cipher
-    pub is_aead: bool,
-    /// Indicates whether this cipher is a block cipher
-    pub is_block_cipher: bool,
-    /// Cipher strength in bits
-    pub strength: u16,
-}
-
 /// Supported cipher suites as registered with [IANA].
 ///
 /// The following cipher suite values are assigned by IANA and correspond to
diff --git a/rust/bssl-tls/src/lib.rs b/rust/bssl-tls/src/lib.rs
index bf949c7..8eb233b 100644
--- a/rust/bssl-tls/src/lib.rs
+++ b/rust/bssl-tls/src/lib.rs
@@ -36,6 +36,7 @@
 use core::panic::AssertUnwindSafe;
 
 pub mod alerts;
+pub mod ciphers;
 pub mod config;
 pub mod connection;
 pub mod context;