Reworking bssl_crypto: x25519

Change-Id: Ib9fc874e1c5d540dda91a454681dad809e8d6d14
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/65167
Reviewed-by: Bob Beck <bbe@google.com>
Reviewed-by: Maurice Lam <yukl@google.com>
Reviewed-by: Nabil Wadih <nwadih@google.com>
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/rust/bssl-crypto/src/lib.rs b/rust/bssl-crypto/src/lib.rs
index e53469d..022f5a3 100644
--- a/rust/bssl-crypto/src/lib.rs
+++ b/rust/bssl-crypto/src/lib.rs
@@ -28,6 +28,8 @@
 
 extern crate core;
 
+use core::ffi::c_void;
+
 /// Authenticated Encryption with Additional Data algorithms.
 pub mod aead;
 
@@ -52,7 +54,6 @@
 /// Random number generation.
 pub mod rand;
 
-/// X25519 elliptic curve operations.
 pub mod x25519;
 
 /// Memory-manipulation operations.
@@ -68,7 +69,70 @@
 #[cfg(test)]
 mod test_helpers;
 
+/// FfiSlice exists to provide `as_ffi_ptr` on slices. Calling `as_ptr` on an
+/// empty Rust slice may return the alignment of the type, rather than NULL, as
+/// the pointer. When passing pointers into C/C++ code, that is not a valid
+/// pointer. Thus this method should be used whenever passing a pointer to a
+/// slice into BoringSSL code.
+trait FfiSlice {
+    fn as_ffi_ptr(&self) -> *const u8;
+    fn as_ffi_void_ptr(&self) -> *const c_void {
+        self.as_ffi_ptr() as *const c_void
+    }
+}
+
+impl FfiSlice for [u8] {
+    fn as_ffi_ptr(&self) -> *const u8 {
+        if self.is_empty() {
+            core::ptr::null()
+        } else {
+            self.as_ptr()
+        }
+    }
+}
+
+impl<const N: usize> FfiSlice for [u8; N] {
+    fn as_ffi_ptr(&self) -> *const u8 {
+        if N == 0 {
+            core::ptr::null()
+        } else {
+            self.as_ptr()
+        }
+    }
+}
+
+/// See the comment [`FfiSlice`].
+trait FfiMutSlice {
+    fn as_mut_ffi_ptr(&mut self) -> *mut u8;
+    fn as_ffi_void_ptr(&mut self) -> *mut c_void {
+        self.as_mut_ffi_ptr() as *mut c_void
+    }
+}
+
+impl FfiMutSlice for [u8] {
+    fn as_mut_ffi_ptr(&mut self) -> *mut u8 {
+        if self.is_empty() {
+            core::ptr::null_mut()
+        } else {
+            self.as_mut_ptr()
+        }
+    }
+}
+
+impl<const N: usize> FfiMutSlice for [u8; N] {
+    fn as_mut_ffi_ptr(&mut self) -> *mut u8 {
+        if N == 0 {
+            core::ptr::null_mut()
+        } else {
+            self.as_mut_ptr()
+        }
+    }
+}
+
 /// This is a helper struct which provides functions for passing slices over FFI.
+///
+/// Deprecated: use `FfiSlice` which adds less noise and lets one grep for `as_ptr`
+/// as a sign of something to check.
 struct CSlice<'a>(&'a [u8]);
 
 impl<'a> From<&'a [u8]> for CSlice<'a> {
@@ -93,6 +157,9 @@
 }
 
 /// This is a helper struct which provides functions for passing mutable slices over FFI.
+///
+/// Deprecated: use `FfiMutSlice` which adds less noise and lets one grep for
+/// `as_ptr` as a sign of something to check.
 struct CSliceMut<'a>(&'a mut [u8]);
 
 impl CSliceMut<'_> {
@@ -179,3 +246,47 @@
     /// Returns a raw pointer to the wrapped value.
     fn as_ptr(&self) -> *mut Self::CType;
 }
+
+/// Wrap a closure that initializes an output buffer and return that buffer as
+/// an array. Requires that the closure fully initialize the given buffer.
+///
+/// Safety: the closure must fully initialize the array.
+unsafe fn with_output_array<const N: usize, F>(func: F) -> [u8; N]
+where
+    F: FnOnce(*mut u8, usize),
+{
+    let mut out_uninit = core::mem::MaybeUninit::<[u8; N]>::uninit();
+    let out_ptr = if N != 0 {
+        out_uninit.as_mut_ptr() as *mut u8
+    } else {
+        core::ptr::null_mut()
+    };
+    func(out_ptr, N);
+    // Safety: `func` promises to fill all of `out_uninit`.
+    unsafe { out_uninit.assume_init() }
+}
+
+/// Wrap a closure that initializes an output buffer and return that buffer as
+/// an array. The closure returns a [`core::ffi::c_int`] and, if the return value
+/// is not one, then the initialization is assumed to have failed and [None] is
+/// returned. Otherwise, this function requires that the closure fully
+/// initialize the given buffer.
+///
+/// Safety: the closure must fully initialize the array if it returns one.
+unsafe fn with_output_array_fallible<const N: usize, F>(func: F) -> Option<[u8; N]>
+where
+    F: FnOnce(*mut u8, usize) -> core::ffi::c_int,
+{
+    let mut out_uninit = core::mem::MaybeUninit::<[u8; N]>::uninit();
+    let out_ptr = if N != 0 {
+        out_uninit.as_mut_ptr() as *mut u8
+    } else {
+        core::ptr::null_mut()
+    };
+    if func(out_ptr, N) == 1 {
+        // Safety: `func` promises to fill all of `out_uninit` if it returns one.
+        unsafe { Some(out_uninit.assume_init()) }
+    } else {
+        None
+    }
+}