blob: 18e69b900236ad0f1c158d54f1fb72a072ef097c [file] [log] [blame]
// Copyright 2018 Google LLC
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
//! The BoringSSL API.
//!
//! This module provides a safe access to the BoringSSL API.
//!
//! It accomplishes this using the following structure:
//! - The internal `raw` module provides nearly-raw access to the BoringSSL API.
//! For each function in the BoringSSL API, it exposes an equivalent Rust
//! function which performs error checking. Functions which return pointers
//! return `Result<NonNull<T>, BoringError>`, functions which return status
//! codes return `Result<(), BoringError>`, etc. This API makes it less likely
//! to accidentally forget to check for null pointers or error status codes.
//! - The internal `wrapper` module provides types which wrap C objects and
//! handle many of the details of their lifecycles. These include
//! `CStackWrapper`, which handles initializing and destructing
//! stack-allocated C objects; `CHeapWrapper`, which is analogous to Rust's
//! `Box` or `Rc`, and handles allocation, reference counting, and freeing;
//! and `CRef`, which is analogous to a Rust reference.
//! - This module builds on top of the `raw` and `wrapper` modules to provide a
//! safe API. This allows us to `#![forbid(unsafe_code)]` in the rest of the
//! crate, which in turn means that this is the only module whose memory
//! safety needs to be manually verified.
//!
//! # Usage
//!
//! Each type, `T`, from the BoringSSL API is exposed as either a
//! `CStackWrapper<T>`, a `CHeapWrapper<T>`, or a `CRef<T>`. Each function from
//! the BoringSSL API which operates on a particular type is exposed as a method
//! on the wrapped version of that type. For example, the BoringSSL `CBS_len`
//! function operates on a `CBS`; we provide the `cbs_len` method on the
//! `CStackWrapper<CBS>` type. While BoringSSL functions that operate on a
//! particular type take the form `TYPE_method`, the Rust equivalents are all
//! lower-case - `type_method`.
//!
//! Some functions which do not make sense as methods are exposed as bare
//! functions. For example, the BoringSSL `ECDSA_sign` function is exposed as a
//! bare function as `ecdsa_sign`.
//!
//! Types which can be constructed without arguments implement `Default`. Types
//! which require arguments to be constructed provide associated functions which
//! take those arguments and return a new instance of that type. For example,
//! the `CHeapWrapper<EC_KEY>::ec_key_parse_private_key` function parses a
//! private key from an input stream and returns a new `CHeapWrapper<EC_KEY>`.
//!
//! # API Guidelines
//!
//! This module is meant to be as close as possible to a direct set of FFI
//! bindings while still providing a safe API. While memory safety is handled
//! internally, and certain error conditions which could affect memory safety
//! are checked internally (and cause the process to abort if they fail), most
//! errors are returned from the API, as they are considered business logic,
//! which is outside the scope of this module.
// NOTES on safety requirements of the BoringSSL API:
// - Though it may not be explicitly documented, calling methods on uinitialized
// values is UB. Remember, this is C! Always initialize (usually via XXX_init
// or a similarly-named function) before calling any methods or functions.
// - Any BoringSSL documentation that says "x property must hold" means that, if
// that property doesn't hold, it may cause UB - you are not guaranteed that
// it will be detected and an error will be returned.
// - If a pointer parameter is const, the function does NOT take ownership of
// the object and does NOT retain a reference to the object (in Rust
// terminology, the object need only live as long as the function call). If
// the pointer parameter is not const, it MAY take ownership or hold a
// reference depending on the documentation. Generally, ownership is only
// taken if explicitly documented, but documentation bugs may exist, so be
// careful.
// NOTE(joshlf): It's important to define this module before the abort module,
// or else all of the assertions that are auto-generated by bindgen would result
// in compilation errors.
#[path = "../../boringssl/boringssl.rs"]
mod ffi;
#[macro_use]
mod abort;
#[macro_use]
mod wrapper;
mod raw;
// C types
pub use boringssl::ffi::{
BIGNUM, CBB, CBS, EC_GROUP, EC_KEY, EVP_MD, EVP_PKEY, HMAC_CTX, MD5_CTX, RC4_KEY, RSA, RSA_F4,
SHA256_CTX, SHA512_CTX, SHA_CTX,
};
// C constants
pub use boringssl::ffi::{
NID_X9_62_prime256v1, NID_md5, NID_secp384r1, NID_secp521r1, NID_sha1, NID_sha256, NID_sha384,
NID_sha512, ED25519_PRIVATE_KEY_LEN, ED25519_PUBLIC_KEY_LEN, ED25519_SIGNATURE_LEN,
MD5_DIGEST_LENGTH, SHA256_DIGEST_LENGTH, SHA384_DIGEST_LENGTH, SHA512_DIGEST_LENGTH,
SHA_DIGEST_LENGTH,
};
// wrapper types
pub use boringssl::wrapper::{CHeapWrapper, CRef, CStackWrapper};
use std::convert::TryInto;
use std::ffi::CStr;
use std::fmt::{self, Debug, Display, Formatter};
use std::mem::MaybeUninit;
use std::num::NonZeroUsize;
use std::os::raw::{c_char, c_int, c_uint, c_void};
use std::{cmp, ptr, slice};
use boringssl::abort::UnwrapAbort;
use boringssl::raw::{
size_t, BN_bn2bin_padded, BN_num_bytes, BN_set_u64, CBB_data, CBB_init, CBB_len, CBS_init, CBS_len, CRYPTO_memcmp,
ECDSA_sign, ECDSA_size, ECDSA_verify, EC_GROUP_get_curve_name, EC_GROUP_new_by_curve_name,
EC_KEY_generate_key, EC_KEY_get0_group, EC_KEY_marshal_private_key, EC_KEY_parse_private_key,
EC_KEY_set_group, EC_curve_nid2nist, ED25519_keypair, ED25519_keypair_from_seed, ED25519_sign,
ED25519_verify, ERR_print_errors_cb, EVP_PBE_scrypt, EVP_PKEY_assign_EC_KEY,
EVP_PKEY_assign_RSA, EVP_PKEY_get1_EC_KEY, EVP_PKEY_get1_RSA, EVP_marshal_public_key,
EVP_parse_public_key, HMAC_CTX_copy, HMAC_CTX_init, HMAC_Final, HMAC_Init_ex, HMAC_Update,
HMAC_size, IntoSizeT, IntoUsize, RAND_bytes, RC4_set_key, RSA_bits, RSA_generate_key_ex,
RSA_get0_e, RSA_get0_n, RSA_marshal_private_key, RSA_parse_private_key, RSA_sign_pss_mgf1, RSA_size,
RSA_verify_pss_mgf1, SHA384_Init, RC4,
};
#[cfg(feature = "rsa-pkcs1v15")]
use boringssl::raw::{RSA_sign, RSA_verify};
impl CStackWrapper<BIGNUM> {
/// The `BN_set_u64` function.
#[must_use]
pub fn bn_set_u64(&mut self, value: u64) -> Result<(), BoringError> {
unsafe { BN_set_u64(self.as_mut(), value) }
}
}
impl CRef<'_, BIGNUM> {
/// The `BN_bn2bin_padded` function.
///
/// `bn_bn2bin_padded` can only fail if `out` is shorter than the minimum
/// number of bytes required by [`bn_num_bytes`].
///
/// [`bn_num_bytes`]: CRef::bn_num_bytes
#[must_use]
pub fn bn_bn2bin_padded(&self, out: &mut [u8]) -> Result<(), BoringError> {
unsafe {
BN_bn2bin_padded(
out.as_mut_ptr(), out.len().into_size_t(), self.as_const())
}
}
/// The `BN_num_bytes` function.
///
/// `bn_num_bytes` returns minimum number of bytes required to serialize
/// using [`bn_bn2bin_padded`].
///
/// [`bn_bn2bin_padded`]: CRef::bn_bn2bin_padded
#[must_use]
pub fn bn_num_bytes(&self) -> usize {
unsafe { BN_num_bytes(self.as_const()).into_usize() }
}
}
impl CStackWrapper<CBB> {
/// Creates a new `CBB` and initializes it with `CBB_init`.
///
/// `cbb_new` can only fail due to OOM.
#[must_use]
pub fn cbb_new(initial_capacity: usize) -> Result<CStackWrapper<CBB>, BoringError> {
unsafe {
let mut cbb = MaybeUninit::uninit();
CBB_init(cbb.as_mut_ptr(), initial_capacity.into_size_t())?;
Ok(CStackWrapper::new(cbb.assume_init()))
}
}
/// Invokes a callback on the contents of a `CBB`.
///
/// `cbb_with_data` accepts a callback, and invokes that callback, passing a
/// slice of the current contents of this `CBB`.
#[must_use]
pub fn cbb_with_data<O, F: Fn(&[u8]) -> O>(&self, with_data: F) -> O {
unsafe {
// NOTE: The return value of CBB_data is only valid until the next
// operation on the CBB. This method is safe because the slice
// reference cannot outlive this function body, and thus cannot live
// beyond another method call that could invalidate the buffer.
let len = CBB_len(self.as_const());
if len == 0 {
// If len is 0, then CBB_data could technically return a null
// pointer. Constructing a slice from a null pointer is likely
// invalid, so we do this instead.
with_data(&[])
} else {
// Since the length is non-zero, CBB_data should not return a
// null pointer.
let ptr = CBB_data(self.as_const()).unwrap_abort();
// TODO(joshlf): Can with_data use this to smuggle out the
// reference, outliving the lifetime of self?
with_data(slice::from_raw_parts(ptr.as_ptr(), len.into_usize()))
}
}
}
}
impl CStackWrapper<CBS> {
/// The `CBS_len` function.
#[must_use]
pub fn cbs_len(&self) -> usize {
unsafe { CBS_len(self.as_const()).into_usize() }
}
/// Invokes a callback on a temporary `CBS`.
///
/// `cbs_with_temp_buffer` constructs a `CBS` from the provided byte slice,
/// and invokes a callback on the `CBS`. The `CBS` is destructed before
/// `cbs_with_temp_buffer` returns.
// TODO(joshlf): Holdover until we figure out how to put lifetimes in CStackWrappers.
#[must_use]
pub fn cbs_with_temp_buffer<O, F: Fn(&mut CStackWrapper<CBS>) -> O>(
bytes: &[u8],
with_cbs: F,
) -> O {
unsafe {
let mut cbs = MaybeUninit::uninit();
CBS_init(cbs.as_mut_ptr(), bytes.as_ptr(), bytes.len().into_size_t());
let mut cbs = CStackWrapper::new(cbs.assume_init());
with_cbs(&mut cbs)
}
}
}
impl CRef<'static, EC_GROUP> {
/// The `EC_GROUP_new_by_curve_name` function.
#[must_use]
pub fn ec_group_new_by_curve_name(nid: c_int) -> Result<CRef<'static, EC_GROUP>, BoringError> {
unsafe { Ok(CRef::new(EC_GROUP_new_by_curve_name(nid)?)) }
}
}
impl<'a> CRef<'a, EC_GROUP> {
/// The `EC_GROUP_get_curve_name` function.
#[must_use]
pub fn ec_group_get_curve_name(&self) -> c_int {
unsafe { EC_GROUP_get_curve_name(self.as_const()) }
}
}
/// The `EC_curve_nid2nist` function.
#[must_use]
pub fn ec_curve_nid2nist(nid: c_int) -> Result<&'static CStr, BoringError> {
unsafe { Ok(CStr::from_ptr(EC_curve_nid2nist(nid)?.as_ptr())) }
}
impl CHeapWrapper<EC_KEY> {
/// The `EC_KEY_generate_key` function.
#[must_use]
pub fn ec_key_generate_key(&mut self) -> Result<(), BoringError> {
unsafe { EC_KEY_generate_key(self.as_mut()) }
}
/// The `EC_KEY_parse_private_key` function.
///
/// If `group` is `None`, then the group pointer argument to
/// `EC_KEY_parse_private_key` will be NULL.
#[must_use]
pub fn ec_key_parse_private_key(
cbs: &mut CStackWrapper<CBS>,
group: Option<CRef<'static, EC_GROUP>>,
) -> Result<CHeapWrapper<EC_KEY>, BoringError> {
unsafe {
Ok(CHeapWrapper::new_from(EC_KEY_parse_private_key(
cbs.as_mut(),
group.map(|g| g.as_const()).unwrap_or(ptr::null()),
)?))
}
}
/// The `EC_KEY_get0_group` function.
#[must_use]
#[allow(clippy::needless_lifetimes)] // to be more explicit
pub fn ec_key_get0_group<'a>(&'a self) -> Result<CRef<'a, EC_GROUP>, BoringError> {
// get0 doesn't increment the refcount; the lifetimes ensure that the
// returned CRef can't outlive self
unsafe { Ok(CRef::new(EC_KEY_get0_group(self.as_const())?)) }
}
/// The `EC_KEY_set_group` function.
#[must_use]
pub fn ec_key_set_group(&mut self, group: &CRef<'static, EC_GROUP>) -> Result<(), BoringError> {
unsafe { EC_KEY_set_group(self.as_mut(), group.as_const()) }
}
/// The `EC_KEY_marshal_private_key` function.
#[must_use]
pub fn ec_key_marshal_private_key(
&self,
cbb: &mut CStackWrapper<CBB>,
) -> Result<(), BoringError> {
unsafe { EC_KEY_marshal_private_key(cbb.as_mut(), self.as_const(), 0) }
}
}
/// The `ECDSA_sign` function.
///
/// `ecdsa_sign` returns the number of bytes written to `sig`.
///
/// # Aborts
///
/// `ecdsa_sign` aborts if `sig` is shorter than the minimum required signature
/// size given by `ecdsa_size`, or if `key` doesn't have a group set.
#[must_use]
pub fn ecdsa_sign(
digest: &[u8],
sig: &mut [u8],
key: &CHeapWrapper<EC_KEY>,
) -> Result<usize, BoringError> {
unsafe {
// If we call ECDSA_sign with sig.len() < min_size, it will invoke UB.
// ECDSA_size fails if the key doesn't have a group set.
let min_size = ecdsa_size(key).unwrap_abort();
assert_abort!(sig.len() >= min_size.get());
let mut sig_len: c_uint = 0;
ECDSA_sign(
0,
digest.as_ptr(),
digest.len().into_size_t(),
sig.as_mut_ptr(),
&mut sig_len,
key.as_const(),
)?;
// ECDSA_sign guarantees that it only needs ECDSA_size bytes for the
// signature.
let sig_len = sig_len.try_into().unwrap_abort();
assert_abort!(sig_len <= min_size.get());
Ok(sig_len)
}
}
/// The `ECDSA_verify` function.
#[must_use]
pub fn ecdsa_verify(digest: &[u8], sig: &[u8], key: &CHeapWrapper<EC_KEY>) -> bool {
unsafe {
ECDSA_verify(
0,
digest.as_ptr(),
digest.len().into_size_t(),
sig.as_ptr(),
sig.len().into_size_t(),
key.as_const(),
)
}
}
/// The `ECDSA_size` function.
#[must_use]
pub fn ecdsa_size(key: &CHeapWrapper<EC_KEY>) -> Result<NonZeroUsize, BoringError> {
unsafe { ECDSA_size(key.as_const()) }
}
/// The `ED25519_keypair` function.
#[must_use]
pub fn ed25519_keypair() -> [u8; ED25519_PRIVATE_KEY_LEN as usize] {
let mut public_unused = [0u8; ED25519_PUBLIC_KEY_LEN as usize];
let mut private = [0u8; ED25519_PRIVATE_KEY_LEN as usize];
unsafe {
ED25519_keypair((&mut public_unused[..]).as_mut_ptr(), (&mut private[..]).as_mut_ptr())
};
private
}
/// The `ED25519_sign` function.
#[must_use]
pub fn ed25519_sign(message: &[u8], private_key: &[u8; 64]) -> Result<[u8; 64], BoringError> {
let mut sig = [0u8; 64];
unsafe { ED25519_sign(&mut sig, message.as_ptr(), message.len().into_size_t(), private_key)? };
Ok(sig)
}
/// The `ED25519_keypair_from_seed` function.
#[must_use]
pub fn ed25519_keypair_from_seed(seed: &[u8; 32]) -> ([u8; 32], [u8; 64]) {
let mut public = [0u8; 32];
let mut private = [0u8; 64];
unsafe {
ED25519_keypair_from_seed(
(&mut public[..]).as_mut_ptr(),
(&mut private[..]).as_mut_ptr(),
(&seed[..]).as_ptr(),
)
};
(public, private)
}
/// The `ED25519_verify` function.
#[must_use]
pub fn ed25519_verify(message: &[u8], signature: &[u8; 64], public_key: &[u8; 32]) -> bool {
unsafe { ED25519_verify(message.as_ptr(), message.len().into_size_t(), signature, public_key) }
}
impl CHeapWrapper<EVP_PKEY> {
/// The `EVP_parse_public_key` function.
#[must_use]
pub fn evp_parse_public_key(
cbs: &mut CStackWrapper<CBS>,
) -> Result<CHeapWrapper<EVP_PKEY>, BoringError> {
unsafe { Ok(CHeapWrapper::new_from(EVP_parse_public_key(cbs.as_mut())?)) }
}
/// The `EVP_marshal_public_key` function.
#[must_use]
pub fn evp_marshal_public_key(&self, cbb: &mut CStackWrapper<CBB>) -> Result<(), BoringError> {
unsafe { EVP_marshal_public_key(cbb.as_mut(), self.as_const()) }
}
/// The `EVP_PKEY_assign_EC_KEY` function.
pub fn evp_pkey_assign_ec_key(&mut self, ec_key: CHeapWrapper<EC_KEY>) {
unsafe {
// NOTE: It's very important that we use 'into_mut' here so that
// ec_key's refcount is not decremented. That's because
// EVP_PKEY_assign_EC_KEY doesn't increment the refcount of its
// argument.
let key = ec_key.into_mut();
// EVP_PKEY_assign_EC_KEY only fails if key is NULL.
EVP_PKEY_assign_EC_KEY(self.as_mut(), key).unwrap_abort()
}
}
/// The `EVP_PKEY_assign_RSA` function.
pub fn evp_pkey_assign_rsa(&mut self, rsa: CHeapWrapper<RSA>) {
unsafe {
// NOTE: It's very important that we use 'into_mut' here so that
// rsa's refcount is not decremented. That's because
// EVP_PKEY_assign_RSA doesn't increment the refcount of its
// argument.
let key = rsa.into_mut();
// EVP_PKEY_assign_RSA only fails if key is NULL.
EVP_PKEY_assign_RSA(self.as_mut(), key).unwrap_abort()
}
}
/// The `EVP_PKEY_get1_EC_KEY` function.
#[must_use]
pub fn evp_pkey_get1_ec_key(&mut self) -> Result<CHeapWrapper<EC_KEY>, BoringError> {
// NOTE: It's important that we use get1 here, as it increments the
// refcount of the EC_KEY before returning a pointer to it.
unsafe { Ok(CHeapWrapper::new_from(EVP_PKEY_get1_EC_KEY(self.as_mut())?)) }
}
/// The `EVP_PKEY_get1_RSA` function.
#[must_use]
pub fn evp_pkey_get1_rsa(&mut self) -> Result<CHeapWrapper<RSA>, BoringError> {
// NOTE: It's important that we use get1 here, as it increments the
// refcount of the RSA key before returning a pointer to it.
unsafe { Ok(CHeapWrapper::new_from(EVP_PKEY_get1_RSA(self.as_mut())?)) }
}
}
/// The `EVP_PBE_scrypt` function.
#[allow(non_snake_case)]
#[must_use]
pub fn evp_pbe_scrypt(
password: &[u8],
salt: &[u8],
N: u64,
r: u64,
p: u64,
max_mem: usize,
out_key: &mut [u8],
) -> Result<(), BoringError> {
unsafe {
EVP_PBE_scrypt(
password.as_ptr() as *const c_char,
password.len().into_size_t(),
salt.as_ptr(),
salt.len().into_size_t(),
N,
r,
p,
max_mem.into_size_t(),
out_key.as_mut_ptr(),
out_key.len().into_size_t(),
)
}
}
/// The `PKCS5_PBKDF2_HMAC` function.
#[cfg(feature = "kdf")]
#[must_use]
pub fn pkcs5_pbkdf2_hmac(
password: &[u8],
salt: &[u8],
iterations: c_uint,
digest: &CRef<'static, EVP_MD>,
out_key: &mut [u8],
) -> Result<(), BoringError> {
unsafe {
raw::PKCS5_PBKDF2_HMAC(
password.as_ptr() as *const c_char,
password.len().into_size_t(),
salt.as_ptr(),
salt.len().into_size_t(),
iterations,
digest.as_const(),
out_key.len().into_size_t(),
out_key.as_mut_ptr(),
)
}
}
impl CStackWrapper<SHA512_CTX> {
/// Initializes a new `CStackWrapper<SHA512_CTX>` as a SHA-384 hash.
///
/// The BoringSSL `SHA512_CTX` is used for both the SHA-512 and SHA-384 hash
/// functions. The implementation of `Default` for
/// `CStackWrapper<SHA512_CTX>` produces a context initialized for a SHA-512
/// hash. In order to produce a context for a SHA-384 hash, use this
/// constructor instead.
#[must_use]
pub fn sha384_new() -> CStackWrapper<SHA512_CTX> {
unsafe {
let mut ctx = MaybeUninit::uninit();
SHA384_Init(ctx.as_mut_ptr());
CStackWrapper::new(ctx.assume_init())
}
}
}
macro_rules! impl_evp_digest {
(#[$doc:meta] $name:ident, $raw_name:ident) => {
#[$doc]
#[must_use]
pub fn $name() -> CRef<'static, EVP_MD> {
unsafe { CRef::new(::boringssl::raw::$raw_name()) }
}
};
}
impl CRef<'static, EVP_MD> {
impl_evp_digest!(
/// The `EVP_md5` function.
evp_md5,
EVP_md5
);
impl_evp_digest!(
/// The `EVP_sha1` function.
evp_sha1,
EVP_sha1
);
impl_evp_digest!(
/// The `EVP_sha256` function.
evp_sha256,
EVP_sha256
);
impl_evp_digest!(
/// The `EVP_sha384` function.
evp_sha384,
EVP_sha384
);
impl_evp_digest!(
/// The `EVP_sha512` function.
evp_sha512,
EVP_sha512
);
}
impl CStackWrapper<HMAC_CTX> {
/// Initializes a new `HMAC_CTX`.
///
/// `hmac_ctx_new` initializes a new `HMAC_CTX` using `HMAC_CTX_init` and
/// then further initializes it with `HMAC_CTX_Init_ex`. It can only fail
/// due to OOM.
#[must_use]
pub fn hmac_ctx_new(
key: &[u8],
md: &CRef<'static, EVP_MD>,
) -> Result<CStackWrapper<HMAC_CTX>, BoringError> {
unsafe {
let mut ctx = MaybeUninit::uninit();
HMAC_CTX_init(ctx.as_mut_ptr());
HMAC_Init_ex(
ctx.as_mut_ptr(),
key.as_ptr() as *const c_void,
key.len().into_size_t(),
md.as_const(),
)?;
Ok(CStackWrapper::new(ctx.assume_init()))
}
}
/// The `HMAC_Update` function.
pub fn hmac_update(&mut self, data: &[u8]) {
unsafe { HMAC_Update(self.as_mut(), data.as_ptr(), data.len().into_size_t()) }
}
// NOTE(joshlf): We require exactly the right length (as opposed to just
// long enough) so that we don't have to have hmac_final return a length.
/// The `HMAC_Final` function.
///
/// # Aborts
///
/// `hmac_final` aborts if `out` is not exactly the right length (as defined
/// by `HMAC_size`).
pub fn hmac_final(&mut self, out: &mut [u8]) {
unsafe {
let hmac_size = HMAC_size(self.as_const()).into_usize();
assert_abort_eq!(out.len(), hmac_size);
let mut hmac_final_size: u32 = 0;
// HMAC_Final is documented to fail on allocation failure, but an
// internal comment states that it's infallible. In either case, we
// want to panic. Normally, for allocation failure, we'd put the
// unwrap higher in the stack, but since this is supposed to be
// infallible anyway, we put it here.
//
// TODO(joshlf): Remove this comment once HMAC_Final is documented
// as being infallible.
HMAC_Final(self.as_mut(), out.as_mut_ptr(), &mut hmac_final_size).unwrap_abort();
// `HMAC_Final` guarantees that it will set its out argument to the
// same value returned by `HMAC_size`. If the conversion from `u32`
// to `usize` fails, that means that a) we are on a 16-bit platform
// and b) that `HMAC_Final` failed to uphold its contract.
assert_abort_eq!(
hmac_size,
hmac_final_size
.try_into()
.expect("`HMAC_Final` returned size out of range of `usize`")
);
assert_abort_eq!(out.len(), hmac_size);
}
}
/// The `HMAC_CTX_copy` function.
pub fn hmac_ctx_copy(&self) -> Result<Self, BoringError> {
unsafe {
let mut ctx = MaybeUninit::uninit();
HMAC_CTX_copy(ctx.as_mut_ptr(), self.as_const())?;
Ok(CStackWrapper::new(ctx.assume_init()))
}
}
}
impl CStackWrapper<RC4_KEY> {
/// The `RC4_set_key` function.
///
/// # Aborts
///
/// `RC4_set_key` encodes the key length with `u32`, which may differ from
/// the target platform's word size (`usize`). This function aborts if the
/// length of the `key` slice exceeds `u32::MAX`.
pub fn rc4_set_key(key: &[u8]) -> Self {
let mut rc4 = RC4_KEY { x: 0, y: 0, data: [0; 256] };
unsafe {
// `RC4_set_key` reads `key` and writes into `rc4`. It does not take
// ownership of `key` and `key` need not live as long as `rc4`.
RC4_set_key(&mut rc4, key.len().try_into().unwrap_abort(), key.as_ptr());
CStackWrapper::new(rc4)
}
}
/// The `RC4` function.
pub fn rc4(&mut self, input: &[u8], output: &mut [u8]) {
let len = cmp::min(input.len(), output.len());
let input = &input[..len];
let output = &mut output[..len];
unsafe {
RC4(self.as_mut(), len.into_size_t(), input.as_ptr(), output.as_mut_ptr());
}
}
}
impl CHeapWrapper<RSA> {
/// The `RSA_bits` function.
#[must_use]
pub fn rsa_bits(&self) -> usize {
// RSA_bits does not mutate its argument but, for
// backwards-compatibility reasons, continues to take a normal
// (non-const) pointer.
unsafe { RSA_bits(self.as_const() as *mut _).into_usize() }
}
/// The `RSA_get0_e` function.
///
/// Returns a constant reference to the key public exponent.
#[must_use]
#[allow(clippy::needless_lifetimes)] // to be more explicit
pub fn rsa_get0_e<'a>(&'a self) -> Result<CRef<'a, BIGNUM>, BoringError> {
unsafe { Ok(CRef::new(RSA_get0_e(self.as_const())?)) }
}
/// The `RSA_get0_n` function.
///
/// Returns a constant reference to the key public modulus.
#[allow(clippy::needless_lifetimes)] // to be more explicit
pub fn rsa_get0_n<'a>(&'a self) -> Result<CRef<'a, BIGNUM>, BoringError> {
unsafe { Ok(CRef::new(RSA_get0_n(self.as_const())?)) }
}
/// The `RSA_generate_key_ex` function.
#[must_use]
pub fn rsa_generate_key_ex(
&mut self,
bits: c_int,
e: &CRef<BIGNUM>,
) -> Result<(), BoringError> {
unsafe {
// NOTE: It's very important that we use 'into_mut' here so that e's
// refcount is not decremented. That's because RSA_generate_key_ex
// takes ownership of e, and thus doesn't increment its refcount.
RSA_generate_key_ex(self.as_mut(), bits, e.as_const(), ptr::null_mut())
}
}
/// The `RSA_marshal_private_key` function.
#[must_use]
pub fn rsa_marshal_private_key(&self, cbb: &mut CStackWrapper<CBB>) -> Result<(), BoringError> {
unsafe { RSA_marshal_private_key(cbb.as_mut(), self.as_const()) }
}
/// The `RSA_parse_private_key` function.
#[must_use]
pub fn rsa_parse_private_key(
cbs: &mut CStackWrapper<CBS>,
) -> Result<CHeapWrapper<RSA>, BoringError> {
unsafe { Ok(CHeapWrapper::new_from(RSA_parse_private_key(cbs.as_mut())?)) }
}
/// The `RSA_size` function.
#[must_use]
pub fn rsa_size(&self) -> Result<NonZeroUsize, BoringError> {
unsafe { RSA_size(self.as_const()) }
}
}
/// The `RSA_sign` function.
///
/// # Aborts
///
/// `rsa_sign` aborts if `sig` is shorter than the minimum required signature
/// size given by `rsa_size`.
#[cfg(feature = "rsa-pkcs1v15")]
pub fn rsa_sign(
hash_nid: c_int,
digest: &[u8],
sig: &mut [u8],
key: &CHeapWrapper<RSA>,
) -> Result<usize, BoringError> {
unsafe {
// If we call RSA_sign with sig.len() < min_size, it will invoke UB.
let min_size = key.rsa_size().unwrap_abort();
assert_abort!(sig.len() >= min_size.get());
let mut sig_len: c_uint = 0;
RSA_sign(
hash_nid,
digest.as_ptr(),
digest.len().try_into().unwrap_abort(),
sig.as_mut_ptr(),
&mut sig_len,
// RSA_sign does not mutate its argument but, for
// backwards-compatibility reasons, continues to take a normal
// (non-const) pointer.
key.as_const() as *mut _,
)?;
// RSA_sign guarantees that it only needs RSA_size bytes for the
// signature.
let sig_len = sig_len.try_into().unwrap_abort();
assert_abort!(sig_len <= min_size.get());
Ok(sig_len)
}
}
/// The `rsa_sign_pss_mgf1` function.
#[must_use]
pub fn rsa_sign_pss_mgf1(
key: &CHeapWrapper<RSA>,
sig: &mut [u8],
digest: &[u8],
md: &CRef<'static, EVP_MD>,
mgf1_md: Option<&CRef<'static, EVP_MD>>,
salt_len: c_int,
) -> Result<usize, BoringError> {
unsafe {
let mut sig_len: size_t = 0;
RSA_sign_pss_mgf1(
// RSA_sign_pss_mgf1 does not mutate its argument but, for
// backwards-compatibility reasons, continues to take a normal
// (non-const) pointer.
key.as_const() as *mut _,
&mut sig_len,
sig.as_mut_ptr(),
sig.len().into_size_t(),
digest.as_ptr(),
digest.len().into_size_t(),
md.as_const(),
mgf1_md.map(CRef::as_const).unwrap_or(ptr::null()),
salt_len,
)?;
// RSA_sign_pss_mgf1 guarantees that it only needs RSA_size bytes for
// the signature.
let rsa_size = key.rsa_size().unwrap_abort();
let sig_len = sig_len.into_usize();
assert_abort!(sig_len <= rsa_size.get());
Ok(sig_len)
}
}
/// The `RSA_verify` function.
#[must_use]
#[cfg(feature = "rsa-pkcs1v15")]
pub fn rsa_verify(hash_nid: c_int, digest: &[u8], sig: &[u8], key: &CHeapWrapper<RSA>) -> bool {
unsafe {
RSA_verify(
hash_nid,
digest.as_ptr(),
digest.len().into_size_t(),
sig.as_ptr(),
sig.len().into_size_t(),
// RSA_verify does not mutate its argument but, for
// backwards-compatibility reasons, continues to take a normal
// (non-const) pointer.
key.as_const() as *mut _,
)
}
}
/// The `RSA_verify_pss_mgf1` function.
#[must_use]
pub fn rsa_verify_pss_mgf1(
key: &CHeapWrapper<RSA>,
digest: &[u8],
md: &CRef<'static, EVP_MD>,
mgf1_md: Option<&CRef<'static, EVP_MD>>,
salt_len: c_int,
sig: &[u8],
) -> bool {
unsafe {
RSA_verify_pss_mgf1(
// RSA_verify_pss_mgf1 does not mutate its argument but, for
// backwards-compatibility reasons, continues to take a normal
// (non-const) pointer.
key.as_const() as *mut _,
digest.as_ptr(),
digest.len().into_size_t(),
md.as_const(),
mgf1_md.map(CRef::as_const).unwrap_or(ptr::null()),
salt_len,
sig.as_ptr(),
sig.len().into_size_t(),
)
}
}
/// Implements `CStackWrapper` for a hash context type.
///
/// The caller provides doc comments, a public method name, and a private
/// function name (from the `raw` module) for an update function and a final
/// function (e.g., `SHA256_Update` and `SHA256_Final`). Note that, as multiple
/// impl blocks are allowed for a particular type, the same context type may be
/// used multiple times. This is useful because both SHA-384 and SHA-512 use the
/// `SHA512_CTX` context type.
macro_rules! impl_hash {
($ctx:ident, $digest_len:ident, #[$update_doc:meta] $update:ident, $update_raw:ident, #[$final_doc:meta] $final:ident, $final_raw:ident) => {
impl CStackWrapper<$ctx> {
#[$update_doc]
pub fn $update(&mut self, data: &[u8]) {
unsafe {
::boringssl::raw::$update_raw(
self.as_mut(),
data.as_ptr() as *const c_void,
data.len().into_size_t(),
)
}
}
#[$final_doc]
#[must_use]
pub fn $final(
&mut self,
) -> [u8; ::boringssl::ffi::$digest_len as usize] {
unsafe {
let mut md = MaybeUninit::<[u8; ::boringssl::ffi::$digest_len as usize]>::uninit();
// SHA1_Final promises to return 1. SHA256_Final,
// SHA384_Final, and SHA512_Final all document that they
// only fail due to programmer error. The only input to the
// function which could cause this is the context. I suspect
// that the error condition is that XXX_Final is called
// twice without resetting, but I'm not sure. Until we
// figure it out, let's err on the side of caution and abort
// here.
//
// TODO(joshlf): Figure out how XXX_Final can fail.
::boringssl::raw::$final_raw(md.as_mut_ptr() as _, self.as_mut()).unwrap_abort();
md.assume_init()
}
}
}
};
(@doc_string $s:expr) => (#[doc="The `"] #[doc=$s] #[doc="` function."]);
}
/// Implements `Clone` for a `CStackWrapper<T>`.
///
/// Unsound for types without no-op `CDestruct` impls, or which
/// capture `!Sync` shared state.
macro_rules! impl_clone {
($ty: ty) => {
impl Clone for CStackWrapper<$ty> {
fn clone(&self) -> Self {
unsafe { CStackWrapper::new(*self.as_const()) }
}
}
};
}
impl_hash!(
MD5_CTX,
MD5_DIGEST_LENGTH,
/// The `MD5_Update` function.
md5_update,
MD5_Update,
/// The `MD5_Final` function.
md5_final,
MD5_Final
);
impl_clone!(MD5_CTX);
impl_hash!(
SHA_CTX,
SHA_DIGEST_LENGTH,
/// The `SHA1_Update` function.
sha1_update,
SHA1_Update,
/// The `SHA1_Final` function.
sha1_final,
SHA1_Final
);
impl_clone!(SHA_CTX);
impl_hash!(
SHA256_CTX,
SHA256_DIGEST_LENGTH,
/// The `SHA256_Update` function.
sha256_update,
SHA256_Update,
/// The `SHA256_Final` function.
sha256_final,
SHA256_Final
);
impl_clone!(SHA256_CTX);
impl_hash!(
SHA512_CTX,
SHA384_DIGEST_LENGTH,
/// The `SHA384_Update` function.
sha384_update,
SHA384_Update,
/// The `SHA384_Final` function.
sha384_final,
SHA384_Final
);
impl_hash!(
SHA512_CTX,
SHA512_DIGEST_LENGTH,
/// The `SHA512_Update` function.
sha512_update,
SHA512_Update,
/// The `SHA512_Final` function.
sha512_final,
SHA512_Final
);
impl_clone!(SHA512_CTX);
/// The `CRYPTO_memcmp` function.
///
/// `crypto_memcmp` first verifies that `a.len() == b.len()` before calling
/// `CRYPTO_memcmp`.
#[must_use]
pub fn crypto_memcmp(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
unsafe {
CRYPTO_memcmp(
a.as_ptr() as *const c_void,
b.as_ptr() as *const c_void,
a.len().into_size_t(),
) == 0
}
}
/// The `RAND_bytes` function.
pub fn rand_bytes(buf: &mut [u8]) {
unsafe { RAND_bytes(buf.as_mut_ptr(), buf.len().into_size_t()) }
}
/// An error generated by BoringSSL.
///
/// The `Debug` impl prints a stack trace. Each element of the trace corresponds
/// to a function within BoringSSL which voluntarily pushed itself onto the
/// stack. In this sense, it is not the same as a normal stack trace. Each
/// element of the trace is of the form `[thread id]:error:[error code]:[library
/// name]:OPENSSL_internal:[reason string]:[file]:[line number]:[optional string
/// data]`.
///
/// The `Display` impl prints the first element of the stack trace.
///
/// Some BoringSSL functions do not record any error in the error stack. Errors
/// generated from such functions are printed as `error calling <function name>`
/// for both `Debug` and `Display` impls.
pub struct BoringError {
stack_trace: Vec<String>,
}
impl BoringError {
/// Consumes the error stack.
///
/// `f` is the name of the function that failed. If the error stack is empty
/// (some BoringSSL functions do not push errors onto the stack when
/// returning errors), the returned `BoringError` will simply note that the
/// named function failed; both the `Debug` and `Display` implementations
/// will return `error calling f`, where `f` is the value of the `f`
/// argument.
#[must_use]
fn consume_stack(f: &str) -> BoringError {
let stack_trace = {
let trace = get_error_stack_trace();
if trace.is_empty() {
vec![format!("error calling {}", f)]
} else {
trace
}
};
BoringError { stack_trace }
}
/// The number of frames in the stack trace.
///
/// Guaranteed to be at least 1.
#[must_use]
pub fn stack_depth(&self) -> usize {
self.stack_trace.len()
}
}
fn get_error_stack_trace() -> Vec<String> {
// Credit to agl@google.com for this implementation.
unsafe extern "C" fn error_callback(
s: *const c_char,
s_len: size_t,
ctx: *mut c_void,
) -> c_int {
let stack_trace = ctx as *mut Vec<String>;
let s = ::std::slice::from_raw_parts(s as *const u8, s_len.into_usize() - 1);
(*stack_trace).push(String::from_utf8_lossy(s).to_string());
1
}
let mut stack_trace = Vec::new();
unsafe { ERR_print_errors_cb(Some(error_callback), &mut stack_trace as *mut _ as *mut c_void) };
stack_trace
}
impl Display for BoringError {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "{}", self.stack_trace[0])
}
}
impl Debug for BoringError {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
for elem in &self.stack_trace {
writeln!(f, "{}", elem)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use util::should_fail;
#[test]
fn test_boring_error() {
let _ = CStackWrapper::cbs_with_temp_buffer(&[], |cbs| {
should_fail(
CHeapWrapper::evp_parse_public_key(cbs),
"boringssl::EVP_parse_public_key",
"public key routines:OPENSSL_internal:DECODE_ERROR",
);
});
}
}