blob: 42ae7fc7a26993c290f162702d81aae4127ce3e7 [file] [log] [blame]
/* Copyright (c) 2023, Google Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#![deny(
missing_docs,
unsafe_op_in_unsafe_fn,
clippy::indexing_slicing,
clippy::unwrap_used,
clippy::panic,
clippy::expect_used
)]
#![cfg_attr(not(any(feature = "std", test)), no_std)]
//! Rust BoringSSL bindings
extern crate alloc;
extern crate core;
use alloc::vec::Vec;
use core::ffi::c_void;
#[macro_use]
mod macros;
pub mod aead;
pub mod aes;
/// Ciphers.
pub mod cipher;
pub mod digest;
pub mod ec;
pub mod ecdh;
pub mod ecdsa;
pub mod ed25519;
pub mod hkdf;
pub mod hmac;
pub mod hpke;
pub mod rsa;
pub mod x25519;
mod scoped;
#[cfg(test)]
mod test_helpers;
mod mem;
pub use mem::constant_time_compare;
mod rand;
pub use rand::{rand_array, rand_bytes};
/// Error type for when a "signature" (either a public-key signature or a MAC)
/// is incorrect.
#[derive(Debug)]
pub struct InvalidSignatureError;
/// 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;
}
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> {
fn from(value: &'a [u8]) -> Self {
Self(value)
}
}
impl CSlice<'_> {
/// Returns a raw pointer to the value, which is safe to pass over FFI.
pub fn as_ptr<T>(&self) -> *const T {
if self.0.is_empty() {
core::ptr::null()
} else {
self.0.as_ptr() as *const T
}
}
pub fn len(&self) -> usize {
self.0.len()
}
}
/// 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<'_> {
/// Returns a raw pointer to the value, which is safe to pass over FFI.
pub fn as_mut_ptr<T>(&mut self) -> *mut T {
if self.0.is_empty() {
core::ptr::null_mut()
} else {
self.0.as_mut_ptr() as *mut T
}
}
pub fn len(&self) -> usize {
self.0.len()
}
}
impl<'a> From<&'a mut [u8]> for CSliceMut<'a> {
fn from(value: &'a mut [u8]) -> Self {
Self(value)
}
}
/// A helper trait implemented by types which reference borrowed foreign types.
///
/// # Safety
///
/// Implementations of `ForeignTypeRef` must guarantee the following:
///
/// - `Self::from_ptr(x).as_ptr() == x`
/// - `Self::from_ptr_mut(x).as_ptr() == x`
unsafe trait ForeignTypeRef: Sized {
/// The raw C type.
type CType;
/// Constructs a shared instance of this type from its raw type.
///
/// # Safety
///
/// `ptr` must be a valid, immutable, instance of the type for the `'a` lifetime.
#[inline]
unsafe fn from_ptr<'a>(ptr: *mut Self::CType) -> &'a Self {
debug_assert!(!ptr.is_null());
unsafe { &*(ptr as *mut _) }
}
/// Returns a raw pointer to the wrapped value.
#[inline]
fn as_ptr(&self) -> *mut Self::CType {
self as *const _ as *mut _
}
}
/// Returns a BoringSSL structure that is initialized by some function.
/// Requires that the given function completely initializes the value.
///
/// (Tagged `unsafe` because a no-op argument would otherwise expose
/// uninitialized memory.)
unsafe fn initialized_struct<T, F>(init: F) -> T
where
F: FnOnce(*mut T),
{
let mut out_uninit = core::mem::MaybeUninit::<T>::uninit();
init(out_uninit.as_mut_ptr());
unsafe { out_uninit.assume_init() }
}
/// Returns a BoringSSL structure that is initialized by some function.
/// Requires that the given function completely initializes the value or else
/// returns false.
///
/// (Tagged `unsafe` because a no-op argument would otherwise expose
/// uninitialized memory.)
unsafe fn initialized_struct_fallible<T, F>(init: F) -> Option<T>
where
F: FnOnce(*mut T) -> bool,
{
let mut out_uninit = core::mem::MaybeUninit::<T>::uninit();
if init(out_uninit.as_mut_ptr()) {
Some(unsafe { out_uninit.assume_init() })
} else {
None
}
}
/// 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) -> bool,
{
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) {
// Safety: `func` promises to fill all of `out_uninit` if it returns one.
unsafe { Some(out_uninit.assume_init()) }
} else {
None
}
}
/// Wrap a closure that writes at most `max_output` bytes to fill a vector.
/// It must return the number of bytes written.
#[allow(clippy::unwrap_used)]
unsafe fn with_output_vec<F>(max_output: usize, func: F) -> Vec<u8>
where
F: FnOnce(*mut u8) -> usize,
{
unsafe {
with_output_vec_fallible(max_output, |out_buf| Some(func(out_buf)))
// The closure cannot fail and thus neither can
// `with_output_array_fallible`.
.unwrap()
}
}
/// Wrap a closure that writes at most `max_output` bytes to fill a vector.
/// If successful, it must return the number of bytes written.
unsafe fn with_output_vec_fallible<F>(max_output: usize, func: F) -> Option<Vec<u8>>
where
F: FnOnce(*mut u8) -> Option<usize>,
{
let mut ret = Vec::with_capacity(max_output);
let out = ret.spare_capacity_mut();
let out_buf = out
.get_mut(0)
.map_or(core::ptr::null_mut(), |x| x.as_mut_ptr());
let num_written = func(out_buf)?;
assert!(num_written <= ret.capacity());
unsafe {
// Safety: `num_written` bytes have been written to.
ret.set_len(num_written);
}
Some(ret)
}
/// Buffer represents an owned chunk of memory on the BoringSSL heap.
/// Call `as_ref()` to get a `&[u8]` from it.
pub struct Buffer {
// This pointer is always allocated by BoringSSL and must be freed using
// `OPENSSL_free`.
pub(crate) ptr: *mut u8,
pub(crate) len: usize,
}
impl AsRef<[u8]> for Buffer {
fn as_ref(&self) -> &[u8] {
if self.len == 0 {
return &[];
}
// Safety: `ptr` and `len` describe a valid area of memory and `ptr`
// must be Rust-valid because `len` is non-zero.
unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
}
}
impl Drop for Buffer {
fn drop(&mut self) {
// Safety: `ptr` is owned by this object and is on the BoringSSL heap.
unsafe {
bssl_sys::OPENSSL_free(self.ptr as *mut core::ffi::c_void);
}
}
}
/// Calls `parse_func` with a `CBS` structure pointing at `data`.
/// If that returns a null pointer then it returns [None].
/// Otherwise, if there's still data left in CBS, it calls `free_func` on the
/// pointer and returns [None]. Otherwise it returns the pointer.
fn parse_with_cbs<T, Parse, Free>(data: &[u8], free_func: Free, parse_func: Parse) -> Option<*mut T>
where
Parse: FnOnce(*mut bssl_sys::CBS) -> *mut T,
Free: FnOnce(*mut T),
{
// Safety: type checking ensures that `cbs` is the correct size.
let mut cbs =
unsafe { initialized_struct(|cbs| bssl_sys::CBS_init(cbs, data.as_ffi_ptr(), data.len())) };
let ptr = parse_func(&mut cbs);
if ptr.is_null() {
return None;
}
// Safety: `cbs` is still valid after parsing.
if unsafe { bssl_sys::CBS_len(&cbs) } != 0 {
// Safety: `ptr` is still owned by this function.
free_func(ptr);
return None;
}
Some(ptr)
}
/// Calls `func` with a `CBB` pointer and returns a [Buffer] of the ultimate
/// contents of that CBB.
#[allow(clippy::unwrap_used)]
fn cbb_to_buffer<F: FnOnce(*mut bssl_sys::CBB)>(initial_capacity: usize, func: F) -> Buffer {
// Safety: type checking ensures that `cbb` is the correct size.
let mut cbb = unsafe {
initialized_struct_fallible(|cbb| bssl_sys::CBB_init(cbb, initial_capacity) == 1)
}
// `CBB_init` only fails if out of memory, which isn't something that this crate handles.
.unwrap();
func(&mut cbb);
let mut ptr: *mut u8 = core::ptr::null_mut();
let mut len: usize = 0;
// `CBB_finish` only fails on programming error, which we convert into a
// panic.
assert_eq!(1, unsafe {
bssl_sys::CBB_finish(&mut cbb, &mut ptr, &mut len)
});
// Safety: `ptr` is on the BoringSSL heap and ownership is returned by
// `CBB_finish`.
Buffer { ptr, len }
}
/// Used to prevent external implementations of internal traits.
mod sealed {
pub struct Sealed;
}