blob: c09bbd21a75f411c4a8050b3078fb350960dc60d [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.
//! Almost-raw bindings to the BoringSSL API.
//!
//! The `raw` module provides bindings to the BoringSSL API which add a little
//! bit of safety beyond the safety provided by completely raw bindings by
//! ensuring that all return values are checked for errors, and converting these
//! C-style return values into Rust `Result`s.
//!
//! This module also directly re-exports any raw bindings which are infallible
//! (e.g., `void` functions).
// infallible functions
pub use boringssl::ffi::{CBB_cleanup, CBB_len, CBS_init, CBS_len, CRYPTO_memcmp,
EC_GROUP_get_curve_name, ED25519_keypair, ED25519_keypair_from_seed,
ERR_print_errors_cb, HMAC_CTX_init, HMAC_size};
use std::num::NonZeroUsize;
use std::os::raw::{c_char, c_int, c_uint, c_void};
use std::ptr::{self, NonNull};
use boringssl::ffi::{self, CBB, CBS, EC_GROUP, EC_KEY, EVP_MD, EVP_PKEY, HMAC_CTX, SHA512_CTX};
use boringssl::wrapper::CInit;
use boringssl::BoringError;
// bytestring.h
impl_traits!(CBB, CDestruct => CBB_cleanup);
impl_traits!(CBS, CDestruct => _);
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn CBB_init(cbb: *mut CBB, initial_capacity: usize) -> Result<(), BoringError> {
one_or_err("CBB_init", ffi::CBB_init(cbb, initial_capacity))
}
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn CBB_data(cbb: *const CBB) -> Result<NonNull<u8>, BoringError> {
ptr_or_err("CBB_init", ffi::CBB_data(cbb) as *mut _)
}
// curve25519.h
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn ED25519_sign(
out: *mut [u8; 64], message: *const u8, message_len: usize, private_key: *const [u8; 64],
) -> Result<(), BoringError> {
one_or_err(
"ED25519_sign",
ffi::ED25519_sign(
out as *mut u8,
message,
message_len,
private_key as *const u8,
),
)
}
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn ED25519_verify(
message: *const u8, message_len: usize, signature: *const [u8; 64], public_key: *const [u8; 32],
) -> bool {
match ffi::ED25519_verify(
message,
message_len,
signature as *const u8,
public_key as *const u8,
) {
0 => false,
1 => true,
// ED25519_verify promises to only return 0 or 1
_ => unreachable_abort!(),
}
}
// digest.h
macro_rules! evp_digest {
($name:ident) => {
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn $name() -> NonNull<EVP_MD> {
// These return pointers to statically-allocated objects, so should
// never fail.
use boringssl::abort::UnwrapAbort;
ptr_or_err(stringify!($name), ffi::$name() as *mut _).unwrap_abort()
}
};
}
evp_digest!(EVP_sha1);
evp_digest!(EVP_sha256);
evp_digest!(EVP_sha384);
evp_digest!(EVP_sha512);
// ec.h
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn EC_GROUP_new_by_curve_name(nid: c_int) -> Result<NonNull<EC_GROUP>, BoringError> {
ptr_or_err(
"EC_GROUP_new_by_curve_name",
ffi::EC_GROUP_new_by_curve_name(nid),
)
}
// ec_key.h
impl_traits!(EC_KEY, CNew => EC_KEY_new, CUpRef => EC_KEY_up_ref, CFree => EC_KEY_free);
impl_traits!(EVP_PKEY, CNew => EVP_PKEY_new, CUpRef => EVP_PKEY_up_ref, CFree => EVP_PKEY_free);
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn EC_curve_nid2nist(nid: c_int) -> Result<NonNull<c_char>, BoringError> {
ptr_or_err("EC_curve_nid2nist", ffi::EC_curve_nid2nist(nid) as *mut _)
}
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn EC_KEY_generate_key(key: *mut EC_KEY) -> Result<(), BoringError> {
one_or_err("EC_KEY_generate_key", ffi::EC_KEY_generate_key(key))
}
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn EC_KEY_get0_group(key: *const EC_KEY) -> Result<NonNull<EC_GROUP>, BoringError> {
ptr_or_err("EC_KEY_get0_group", ffi::EC_KEY_get0_group(key) as *mut _)
}
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn EC_KEY_marshal_private_key(
cbb: *mut CBB, key: *const EC_KEY, enc_flags: c_uint,
) -> Result<(), BoringError> {
one_or_err(
"EC_KEY_marshal_private_key",
ffi::EC_KEY_marshal_private_key(cbb, key, enc_flags),
)
}
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn EC_KEY_parse_private_key(
cbs: *mut CBS, group: *const EC_GROUP,
) -> Result<NonNull<EC_KEY>, BoringError> {
ptr_or_err(
"EC_KEY_parse_private_key",
ffi::EC_KEY_parse_private_key(cbs, group),
)
}
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn EC_KEY_set_group(
key: *mut EC_KEY, group: *const EC_GROUP,
) -> Result<(), BoringError> {
one_or_err("EC_KEY_set_group", ffi::EC_KEY_set_group(key, group))
}
// ecdsa.h
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn ECDSA_sign(
type_: c_int, digest: *const u8, digest_len: usize, sig: *mut u8, sig_len: *mut c_uint,
key: *const EC_KEY,
) -> Result<(), BoringError> {
one_or_err(
"ECDSA_sign",
ffi::ECDSA_sign(type_, digest, digest_len, sig, sig_len, key),
)
}
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn ECDSA_size(key: *const EC_KEY) -> Result<NonZeroUsize, BoringError> {
NonZeroUsize::new(ffi::ECDSA_size(key)).ok_or_else(|| BoringError::consume_stack("ECDSA_size"))
}
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn ECDSA_verify(
type_: c_int, digest: *const u8, digest_len: usize, sig: *const u8, sig_len: usize,
key: *const EC_KEY,
) -> bool {
match ffi::ECDSA_verify(type_, digest, digest_len, sig, sig_len, key) {
1 => true,
0 => false,
// ECDSA_verify promises to only return 0 or 1
_ => unreachable_abort!(),
}
}
// evp.h
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn EVP_marshal_public_key(
cbb: *mut CBB, key: *const EVP_PKEY,
) -> Result<(), BoringError> {
one_or_err(
"EVP_marshal_public_key",
ffi::EVP_marshal_public_key(cbb, key),
)
}
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn EVP_parse_public_key(cbs: *mut CBS) -> Result<NonNull<EVP_PKEY>, BoringError> {
ptr_or_err("EVP_parse_public_key", ffi::EVP_parse_public_key(cbs))
}
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn EVP_PKEY_assign_EC_KEY(
pkey: *mut EVP_PKEY, key: *mut EC_KEY,
) -> Result<(), BoringError> {
one_or_err(
"EVP_PKEY_assign_EC_KEY",
ffi::EVP_PKEY_assign_EC_KEY(pkey, key),
)
}
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn EVP_PKEY_get1_EC_KEY(pkey: *mut EVP_PKEY) -> Result<NonNull<EC_KEY>, BoringError> {
ptr_or_err("EVP_PKEY_get1_EC_KEY", ffi::EVP_PKEY_get1_EC_KEY(pkey))
}
#[allow(non_snake_case)]
// TODO(joshlf): Replace with #[allow(clippy::too_many_arguments)] once the
// tool_lints feature is stable
#[cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))]
#[must_use]
pub unsafe fn EVP_PBE_scrypt(
password: *const c_char, password_len: usize, salt: *const u8, salt_len: usize, N: u64, r: u64,
p: u64, max_mem: usize, out_key: *mut u8, key_len: usize,
) -> Result<(), BoringError> {
one_or_err(
"EVP_PBE_scrypt",
ffi::EVP_PBE_scrypt(
password,
password_len,
salt,
salt_len,
N,
r,
p,
max_mem,
out_key,
key_len,
),
)
}
#[cfg(feature = "kdf")]
#[allow(non_snake_case)]
// TODO(joshlf): Replace with #[allow(clippy::too_many_arguments)] once the
// tool_lints feature is stable
#[cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))]
#[must_use]
pub unsafe fn PKCS5_PBKDF2_HMAC(
password: *const c_char, password_len: usize, salt: *const u8, salt_len: usize,
iterations: c_uint, digest: *const EVP_MD, key_len: usize, out_key: *mut u8,
) -> Result<(), BoringError> {
one_or_err(
"PKCS5_PBKDF2_HMAC",
ffi::PKCS5_PBKDF2_HMAC(
password,
password_len,
salt,
salt_len,
iterations,
digest,
key_len,
out_key,
),
)
}
// hmac.h
// NOTE: We don't implement CInit because some functions that take an HMAC_CTX
// pointer have extra invariants beyond simply having called HMAC_CTX_init. If
// we implemented CInit, then safe code would be able to construct a
// CStackWrapper<HMAC_CTX> using Default::default, and then pass a pointer to
// that object to functions that require extra initialization, leading to
// usoundness.
impl_traits!(HMAC_CTX, CDestruct => HMAC_CTX_cleanup);
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn HMAC_Init_ex(
ctx: *mut HMAC_CTX, key: *const c_void, key_len: usize, md: *const EVP_MD,
) -> Result<(), BoringError> {
one_or_err(
"HMAC_Init_ex",
ffi::HMAC_Init_ex(ctx, key, key_len, md, ptr::null_mut()),
)
}
#[allow(non_snake_case)]
pub unsafe fn HMAC_Update(ctx: *mut HMAC_CTX, data: *const u8, data_len: usize) {
// HMAC_Update promises to return 1.
assert_abort_eq!(ffi::HMAC_Update(ctx, data, data_len), 1);
}
#[allow(non_snake_case)]
#[must_use]
pub unsafe fn HMAC_Final(
ctx: *mut HMAC_CTX, out: *mut u8, out_len: *mut c_uint,
) -> Result<(), BoringError> {
one_or_err("HMAC_Final", ffi::HMAC_Final(ctx, out, out_len))
}
// rand.h
#[allow(non_snake_case)]
pub unsafe fn RAND_bytes(buf: *mut u8, len: usize) {
// RAND_bytes promises to return 1.
assert_abort_eq!(ffi::RAND_bytes(buf, len), 1);
}
// sha.h
#[allow(non_snake_case)]
pub unsafe fn SHA384_Init(ctx: *mut SHA512_CTX) {
// SHA384_Init promises to return 1.
assert_abort_eq!(ffi::SHA384_Init(ctx), 1);
}
// Implemented manually (rather than via impl_traits! or c_init!) so that we can
// assert_abort_eq! that the return value is 1.
unsafe impl CInit for ffi::SHA_CTX {
unsafe fn init(ctx: *mut Self) {
// SHA1_Init promises to return 1.
assert_abort_eq!(ffi::SHA1_Init(ctx), 1);
}
}
unsafe impl CInit for ffi::SHA256_CTX {
unsafe fn init(ctx: *mut Self) {
// SHA256_Init promises to return 1.
assert_abort_eq!(ffi::SHA256_Init(ctx), 1);
}
}
unsafe impl CInit for ffi::SHA512_CTX {
unsafe fn init(ctx: *mut Self) {
// SHA512_Init promises to return 1.
assert_abort_eq!(ffi::SHA512_Init(ctx), 1);
}
}
// implement no-op destructors
impl_traits!(SHA_CTX, CDestruct => _);
impl_traits!(SHA256_CTX, CDestruct => _);
impl_traits!(SHA512_CTX, CDestruct => _);
macro_rules! sha {
($ctx:ident, $update:ident, $final:ident) => {
#[allow(non_snake_case)]
pub unsafe fn $update(ctx: *mut ffi::$ctx, data: *const c_void, len: usize) {
// All XXX_Update functions promise to return 1.
assert_abort_eq!(ffi::$update(ctx, data, len), 1);
}
#[allow(non_snake_case)]
pub unsafe fn $final(md: *mut u8, ctx: *mut ffi::$ctx) -> Result<(), BoringError> {
one_or_err(stringify!($final), ffi::$final(md, ctx))
}
};
}
sha!(SHA_CTX, SHA1_Update, SHA1_Final);
sha!(SHA256_CTX, SHA256_Update, SHA256_Final);
sha!(SHA512_CTX, SHA384_Update, SHA384_Final);
sha!(SHA512_CTX, SHA512_Update, SHA512_Final);
// utility functions
// If code is 1, returns Ok, otherwise returns Err. f should be the name of the
// function that returned this value.
#[must_use]
pub fn one_or_err(f: &str, code: c_int) -> Result<(), BoringError> {
if code == 1 {
Ok(())
} else {
Err(BoringError::consume_stack(f))
}
}
// If ptr is non-NULL, returns Ok, otherwise returns Err. f should be the name
// of the function that returned this value.
fn ptr_or_err<T>(f: &str, ptr: *mut T) -> Result<NonNull<T>, BoringError> {
NonNull::new(ptr).ok_or_else(|| BoringError::consume_stack(f))
}