rust: bssl-tls: Refactor the stack iterator

As we need to add more stack iterators with different element types,
we need to generalise the current iterator and reuse some of the code.

Signed-off-by: Xiangfei Ding <xfding@google.com>
Change-Id: I44de3afa40fac49bf188c9f71cf0b5f96a6a6964
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/98792
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/rust/bssl-tls/src/credentials.rs b/rust/bssl-tls/src/credentials.rs
index 2fa6181..b2b4db1 100644
--- a/rust/bssl-tls/src/credentials.rs
+++ b/rust/bssl-tls/src/credentials.rs
@@ -65,7 +65,9 @@
     ffi::{
         Alloc,
         Bio,
+        CryptoBufferWrapper,
         Stack,
+        StackIterator,
         sanitize_slice,
         slice_into_ffi_raw_parts, //
     },
@@ -919,67 +921,55 @@
 /// Certificate chain iterator.
 ///
 /// This iterator will supply the peer leaf certificate as the first element in the chain, if any.
+pub type CertificateChainIterator<'a> = CryptoBufferIterator<'a, Certificate>;
+
 #[derive(Clone, Copy)]
-pub struct CertificateChainIterator<'a> {
-    certs: *const bssl_sys::stack_st_CRYPTO_BUFFER,
-    len: usize,
-    curr: usize,
-    _p: PhantomData<&'a ()>,
+#[doc(hidden)]
+pub struct CryptoBufferIterator<'a, T> {
+    inner: StackIterator<'a, bssl_sys::CRYPTO_BUFFER>,
+    _p: PhantomData<fn() -> T>,
 }
 
-impl<'a> CertificateChainIterator<'a> {
-    /// Safety: caller must ensure that `certs` is outlived by,
-    /// or in other words stays alive as long as, `'a`.
-    pub(crate) unsafe fn new(certs: *const bssl_sys::stack_st_CRYPTO_BUFFER) -> Self {
-        let len = if certs.is_null() {
-            0
-        } else {
-            unsafe {
-                // Safety: `certs` is valid now.
-                bssl_sys::sk_CRYPTO_BUFFER_num(certs)
-            }
-        };
+impl<T: CryptoBufferWrapper> CryptoBufferIterator<'_, T> {
+    /// Safety: caller must ensure that `sk` outlives `'a`.
+    pub(crate) unsafe fn new(sk: *const bssl_sys::stack_st_CRYPTO_BUFFER) -> Self {
         Self {
-            certs,
-            len,
-            curr: 0,
+            inner: unsafe {
+                // Safety: `sk` outlives `'a` per pre-condition.
+                StackIterator::new(sk)
+            },
             _p: PhantomData,
         }
     }
 }
 
-impl<'a> Iterator for CertificateChainIterator<'a> {
-    type Item = Certificate;
+impl<T: CryptoBufferWrapper> Iterator for CryptoBufferIterator<'_, T> {
+    type Item = T;
 
     fn next(&mut self) -> Option<Self::Item> {
-        if self.curr >= self.len {
-            return None;
-        }
-        let cert = unsafe {
-            // Safety: `self.certs` is still valid now and `self.curr` is within the bound.
-            bssl_sys::sk_CRYPTO_BUFFER_value(self.certs, self.curr)
-        };
-        self.curr += 1;
-        let Some(cert) = NonNull::new(cert) else {
-            // Fuse the iterator.
-            self.curr = self.len;
-            return None;
-        };
-        unsafe {
-            // Safety: `cert` is valid here.
-            bssl_sys::CRYPTO_BUFFER_up_ref(cert.as_ptr());
-        }
-        Some(Certificate(cert))
+        self.inner
+            .next()
+            .map(|buf| unsafe {
+                // Safety: we are only bumping the ref-count
+                bssl_sys::CRYPTO_BUFFER_dup_ref(buf)
+            })
+            .and_then(|ptr| NonNull::new(ptr as *mut _))
+            .map(|buf| {
+                unsafe {
+                    // Safety: `buf` is now exclusively owned.
+                    T::from_crypto_buffer(buf)
+                }
+            })
     }
 }
 
-impl ExactSizeIterator for CertificateChainIterator<'_> {
+impl<T: CryptoBufferWrapper> ExactSizeIterator for CryptoBufferIterator<'_, T> {
     fn len(&self) -> usize {
-        self.len - self.curr
+        self.inner.len()
     }
 }
 
-impl FusedIterator for CertificateChainIterator<'_> {}
+impl<T: CryptoBufferWrapper> FusedIterator for CryptoBufferIterator<'_, T> {}
 
 /// Safety: this callback stub must be installed with a context object allocated
 /// as a `Box<dyn VerifyCertificate>`.
diff --git a/rust/bssl-tls/src/ffi.rs b/rust/bssl-tls/src/ffi.rs
index c462fd9..55d4b02 100644
--- a/rust/bssl-tls/src/ffi.rs
+++ b/rust/bssl-tls/src/ffi.rs
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 use core::{
+    iter::FusedIterator,
     marker::PhantomData,
     mem::{
         MaybeUninit,
@@ -275,6 +276,11 @@
     }
 }
 
+pub(crate) trait CryptoBufferWrapper {
+    /// Safety: `buf` must be exclusively owned.
+    unsafe fn from_crypto_buffer(buf: core::ptr::NonNull<::bssl_sys::CRYPTO_BUFFER>) -> Self;
+}
+
 pub(crate) unsafe trait BsslStack: Sized {
     type Element: StackElement;
 
@@ -283,6 +289,9 @@
     /// Safety: `this` handle must be a live `stack_st_*` handle.
     unsafe fn size(this: *const Self) -> usize;
 
+    /// Safety: `this` handle must be live and `idx` must be in bounds.
+    unsafe fn index(this: *const Self, idx: usize) -> *const Self::Element;
+
     /// Safety: both `this` and `elem` cannot be aliased.
     unsafe fn push(this: *mut Self, elem: *mut Self::Element);
 
@@ -313,6 +322,13 @@
         }
     }
 
+    unsafe fn index(this: *const Self, idx: usize) -> *const Self::Element {
+        unsafe {
+            // Safety: `this` is valid and live
+            bssl_sys::sk_CRYPTO_BUFFER_value(this, idx)
+        }
+    }
+
     unsafe fn push(this: *mut Self, elem: *mut bssl_sys::CRYPTO_BUFFER) {
         let rc = unsafe {
             // Safety: `this` and `elem` are exclusively owned and valid.
@@ -377,3 +393,60 @@
         ptr
     }
 }
+
+#[derive(Clone, Copy)]
+pub(crate) struct StackIterator<'a, T: StackElement> {
+    sk: *const T::Stack,
+    len: usize,
+    curr: usize,
+    _p: PhantomData<&'a fn() -> T>,
+}
+
+impl<'a, T: StackElement> StackIterator<'a, T> {
+    /// Safety: caller must ensure that `sk` outlives `'a`.
+    pub(crate) unsafe fn new(sk: *const T::Stack) -> Self {
+        let len = if sk.is_null() {
+            0
+        } else {
+            unsafe {
+                // Safety: `sk` is valid now.
+                T::Stack::size(sk)
+            }
+        };
+        Self {
+            sk,
+            len,
+            curr: 0,
+            _p: PhantomData,
+        }
+    }
+}
+
+impl<'a, T: StackElement> Iterator for StackIterator<'a, T> {
+    type Item = *const T;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.curr >= self.len {
+            return None;
+        }
+        let elem = unsafe {
+            // Safety: `self.sk` is still valid now and `self.curr` is within the bound.
+            T::Stack::index(self.sk, self.curr)
+        };
+        self.curr += 1;
+        if elem.is_null() {
+            // Fuse the iterator.
+            self.curr = self.len;
+            return None;
+        }
+        Some(elem)
+    }
+}
+
+impl<T: StackElement> ExactSizeIterator for StackIterator<'_, T> {
+    fn len(&self) -> usize {
+        self.len - self.curr
+    }
+}
+
+impl<T: StackElement> FusedIterator for StackIterator<'_, T> {}
diff --git a/rust/bssl-tls/src/macros.rs b/rust/bssl-tls/src/macros.rs
index eac9622..d9fb5ae 100644
--- a/rust/bssl-tls/src/macros.rs
+++ b/rust/bssl-tls/src/macros.rs
@@ -77,6 +77,12 @@
             }
         }
 
+        impl $crate::ffi::CryptoBufferWrapper for $name {
+            unsafe fn from_crypto_buffer(buf: ::core::ptr::NonNull<::bssl_sys::CRYPTO_BUFFER>) -> Self {
+                Self(buf)
+            }
+        }
+
         impl ::core::ops::Deref for $name {
             type Target = [u8];
             fn deref(&self) -> &Self::Target {