initial setup for bssl crate with hmac and sha2 bindings

- update rust folder to split into `bssl` and `bssl-sys`
- add initial bindings for hmac and a subset of sha2

Change-Id: I09e0e778c1590de6818a49e19529ceb011e4d9f6
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/57285
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: Bob Beck <bbe@google.com>
Reviewed-by: Adam Langley <agl@google.com>
fips-20230428
Nabil Wadih 2 years ago committed by Boringssl LUCI CQ
parent 8aa51ddfcf
commit be79283dd8
  1. 1
      .gitignore
  2. 40
      rust/CMakeLists.txt
  3. 14
      rust/bssl-crypto/Cargo.lock
  4. 11
      rust/bssl-crypto/Cargo.toml
  5. 14
      rust/bssl-crypto/README.md
  6. 212
      rust/bssl-crypto/deny.toml
  7. 80
      rust/bssl-crypto/src/digest.rs
  8. 391
      rust/bssl-crypto/src/hmac.rs
  9. 118
      rust/bssl-crypto/src/lib.rs
  10. 40
      rust/bssl-sys/CMakeLists.txt
  11. 2
      rust/bssl-sys/Cargo.toml
  12. 0
      rust/bssl-sys/README.md
  13. 9
      rust/bssl-sys/build.rs
  14. 0
      rust/bssl-sys/rust_wrapper.c
  15. 0
      rust/bssl-sys/rust_wrapper.h
  16. 0
      rust/bssl-sys/src/lib.rs
  17. 79
      rust/bssl-sys/wrapper.h
  18. 79
      rust/wrapper.h

1
.gitignore vendored

@ -26,3 +26,4 @@ util/bot/sde-linux64.tar.xz
util/bot/sde-win32 util/bot/sde-win32
util/bot/sde-win32.tar.xz util/bot/sde-win32.tar.xz
util/bot/win_toolchain.json util/bot/win_toolchain.json
target/

@ -1,39 +1 @@
# Additional interop for things like macros and inlined functions. add_subdirectory(bssl-sys)
add_library(rust_wrapper STATIC rust_wrapper.c)
target_link_libraries(rust_wrapper crypto)
# Generate architecture-specific wrappers.
set(WRAPPER_TARGET ${CMAKE_CURRENT_BINARY_DIR}/src/wrapper_${RUST_BINDINGS}.rs)
set(COMMAND ${BINDGEN_EXECUTABLE} "wrapper.h"
-o ${WRAPPER_TARGET}
--no-derive-default
--enable-function-attribute-detection
--use-core
--size_t-is-usize
--default-macro-constant-type="signed"
--rustified-enum="point_conversion_form_t"
--allowlist-file=".*/include/openssl/.*\\.h"
--allowlist-file=".*/rust_wrapper\\.h"
-- # these are LLVM arg passthroughs
-I../include
# https://doc.rust-lang.org/nightly/rustc/platform-support.html
--target=${RUST_BINDINGS})
set(INCLUDES "include!(\"wrapper_${RUST_BINDINGS}.rs\");\n")
add_custom_target(
bindgen_rust_${RUST_BINDINGS}
ALL
${COMMAND}
BYPRODUCTS ${WRAPPER_TARGET}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
# move files into build directory
configure_file("src/lib.rs" "src/lib.rs")
if(NOT BUILD_SHARED_LIBS)
configure_file("build.rs" "build.rs" COPYONLY)
endif()
configure_file("Cargo.toml" "Cargo.toml" COPYONLY)

@ -0,0 +1,14 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bssl-crypto"
version = "0.1.0"
dependencies = [
"bssl-sys",
]
[[package]]
name = "bssl-sys"
version = "0.1.0"

@ -0,0 +1,11 @@
[package]
name = "bssl-crypto"
version = "0.1.0"
edition = "2021"
publish = false
license = "MIT"
[dependencies]
# the crate will need to be generated at this path by running this command at root
# `mkdir build && cd build && cmake -G Ninja .. -DRUST_BINDINGS="$(gcc -dumpmachine)" && ninja`
bssl-sys = {path = "../../build/rust/bssl-sys"}

@ -0,0 +1,14 @@
bssl-crypto
============
rust bindings to boringssl which wrap bssl-sys, a low level autogenerated binding
Before using this crate, first generate the bssl-sys bindings by running this command from the root of the repo:
```
mkdir build && cd build && cmake -G Ninja .. -DRUST_BINDINGS="$(gcc -dumpmachine)" && ninja
```
Then to run all tests:
```
cd rust/bssl-crypto && cargo clippy && cargo deny check && cargo test
```

@ -0,0 +1,212 @@
# This template contains all of the possible sections and their default values
# Note that all fields that take a lint level have these possible values:
# * deny - An error will be produced and the check will fail
# * warn - A warning will be produced, but the check will not fail
# * allow - No warning or error will be produced, though in some cases a note
# will be
# The values provided in this template are the default values that will be used
# when any section or field is not specified in your own configuration
# If 1 or more target triples (and optionally, target_features) are specified,
# only the specified targets will be checked when running `cargo deny check`.
# This means, if a particular package is only ever used as a target specific
# dependency, such as, for example, the `nix` crate only being used via the
# `target_family = "unix"` configuration, that only having windows targets in
# this list would mean the nix crate, as well as any of its exclusive
# dependencies not shared by any other crates, would be ignored, as the target
# list here is effectively saying which targets you are building for.
targets = [
# The triple can be any string, but only the target triples built in to
# rustc (as of 1.40) can be checked against actual config expressions
#{ triple = "x86_64-unknown-linux-musl" },
# You can also specify which target_features you promise are enabled for a
# particular target. target_features are currently not validated against
# the actual valid features supported by the target architecture.
#{ triple = "wasm32-unknown-unknown", features = ["atomics"] },
]
# This section is considered when running `cargo deny check advisories`
# More documentation for the advisories section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
[advisories]
# The path where the advisory database is cloned/fetched into
db-path = "~/.cargo/advisory-db"
# The url(s) of the advisory databases to use
db-urls = ["https://github.com/rustsec/advisory-db"]
# The lint level for security vulnerabilities
vulnerability = "deny"
# The lint level for unmaintained crates
unmaintained = "warn"
# The lint level for crates that have been yanked from their source registry
yanked = "warn"
# The lint level for crates with security notices. Note that as of
# 2019-12-17 there are no security notice advisories in
# https://github.com/rustsec/advisory-db
notice = "warn"
# A list of advisory IDs to ignore. Note that ignored advisories will still
# output a note when they are encountered.
ignore = [
#"RUSTSEC-0000-0000",
]
# Threshold for security vulnerabilities, any vulnerability with a CVSS score
# lower than the range specified will be ignored. Note that ignored advisories
# will still output a note when they are encountered.
# * None - CVSS Score 0.0
# * Low - CVSS Score 0.1 - 3.9
# * Medium - CVSS Score 4.0 - 6.9
# * High - CVSS Score 7.0 - 8.9
# * Critical - CVSS Score 9.0 - 10.0
#severity-threshold =
# If this is true, then cargo deny will use the git executable to fetch advisory database.
# If this is false, then it uses a built-in git library.
# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support.
# See Git Authentication for more information about setting up git authentication.
#git-fetch-with-cli = true
# This section is considered when running `cargo deny check licenses`
# More documentation for the licenses section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
[licenses]
# The lint level for crates which do not have a detectable license
unlicensed = "deny"
# List of explicitly allowed licenses
# See https://spdx.org/licenses/ for list of possible licenses
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
allow = [
"MIT",
"Apache-2.0",
"Apache-2.0 WITH LLVM-exception",
"Unicode-DFS-2016",
]
# List of explicitly disallowed licenses
# See https://spdx.org/licenses/ for list of possible licenses
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
deny = [
#"Nokia",
]
# Lint level for licenses considered copyleft
copyleft = "warn"
# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses
# * both - The license will be approved if it is both OSI-approved *AND* FSF
# * either - The license will be approved if it is either OSI-approved *OR* FSF
# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF
# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved
# * neither - This predicate is ignored and the default lint level is used
allow-osi-fsf-free = "neither"
# Lint level used when no other predicates are matched
# 1. License isn't in the allow or deny lists
# 2. License isn't copyleft
# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither"
default = "deny"
# The confidence threshold for detecting a license from license text.
# The higher the value, the more closely the license text must be to the
# canonical license text of a valid SPDX license file.
# [possible values: any between 0.0 and 1.0].
confidence-threshold = 0.8
# Allow 1 or more licenses on a per-crate basis, so that particular licenses
# aren't accepted for every possible crate as with the normal allow list
exceptions = [
# Each entry is the crate and version constraint, and its specific allow
# list
#{ allow = ["Zlib"], name = "adler32", version = "*" },
]
# Some crates don't have (easily) machine readable licensing information,
# adding a clarification entry for it allows you to manually specify the
# licensing information
#[[licenses.clarify]]
# The name of the crate the clarification applies to
#name = "ring"
# The optional version constraint for the crate
#version = "*"
# The SPDX expression for the license requirements of the crate
#expression = "MIT AND ISC AND OpenSSL"
# One or more files in the crate's source used as the "source of truth" for
# the license expression. If the contents match, the clarification will be used
# when running the license check, otherwise the clarification will be ignored
# and the crate will be checked normally, which may produce warnings or errors
# depending on the rest of your configuration
#license-files = [
# Each entry is a crate relative path, and the (opaque) hash of its contents
#{ path = "LICENSE", hash = 0xbd0eed23 }
#]
[licenses.private]
# If true, ignores workspace crates that aren't published, or are only
# published to private registries.
# To see how to mark a crate as unpublished (to the official registry),
# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field.
ignore = false
# One or more private registries that you might publish crates to, if a crate
# is only published to private registries, and ignore is true, the crate will
# not have its license(s) checked
registries = [
#"https://sekretz.com/registry
]
# This section is considered when running `cargo deny check bans`.
# More documentation about the 'bans' section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
[bans]
# Lint level for when multiple versions of the same crate are detected
multiple-versions = "warn"
# Lint level for when a crate version requirement is `*`
wildcards = "allow"
# The graph highlighting used when creating dotgraphs for crates
# with multiple versions
# * lowest-version - The path to the lowest versioned duplicate is highlighted
# * simplest-path - The path to the version with the fewest edges is highlighted
# * all - Both lowest-version and simplest-path are used
highlight = "all"
# List of crates that are allowed. Use with care!
allow = [
#{ name = "ansi_term", version = "=0.11.0" },
]
# List of crates to deny
deny = [
# Each entry the name of a crate and a version range. If version is
# not specified, all versions will be matched.
#{ name = "ansi_term", version = "=0.11.0" },
#
# Wrapper crates can optionally be specified to allow the crate when it
# is a direct dependency of the otherwise banned crate
#{ name = "ansi_term", version = "=0.11.0", wrappers = [] },
]
# Certain crates/versions that will be skipped when doing duplicate detection.
skip = [
#{ name = "ansi_term", version = "=0.11.0" },
]
# Similarly to `skip` allows you to skip certain crates during duplicate
# detection. Unlike skip, it also includes the entire tree of transitive
# dependencies starting at the specified crate, up to a certain depth, which is
# by default infinite
skip-tree = [
#{ name = "ansi_term", version = "=0.11.0", depth = 20 },
]
# This section is considered when running `cargo deny check sources`.
# More documentation about the 'sources' section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
[sources]
# Lint level for what to happen when a crate from a crate registry that is not
# in the allow list is encountered
unknown-registry = "warn"
# Lint level for what to happen when a crate from a git repository that is not
# in the allow list is encountered
unknown-git = "warn"
# List of URLs for allowed crate registries. Defaults to the crates.io index
# if not specified. If it is specified but empty, no registries are allowed.
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
# List of URLs for allowed Git repositories
allow-git = []
[sources.allow-org]
# 1 or more github.com organizations to allow git sources for
#github = [""]
# 1 or more gitlab.com organizations to allow git sources for
#gitlab = [""]
# 1 or more bitbucket.org organizations to allow git sources for
#bitbucket = [""]

@ -0,0 +1,80 @@
/* 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.
*/
use crate::ForeignTypeRef;
/// The BoringSSL implemented SHA-256 digest algorithm.
#[derive(Clone)]
pub struct Sha256 {}
/// The BoringSSL implemented SHA-512 digest algorithm.
#[derive(Clone)]
pub struct Sha512 {}
/// A reference to an [`Md`], which abstracts the details of a specific hash function allowing code
/// to deal with the concept of a "hash function" without needing to know exactly which hash function
/// it is.
pub(crate) struct MdRef;
unsafe impl ForeignTypeRef for MdRef {
type CType = bssl_sys::EVP_MD;
}
/// Used internally to get a BoringSSL internal MD
pub(crate) trait Md {
/// gets a reference to a message digest algorithm to be used by the hkdf implementation
fn get_md() -> &'static MdRef;
}
impl Md for Sha256 {
fn get_md() -> &'static MdRef {
// Safety:
// - this always returns a valid pointer to an EVP_MD
unsafe { MdRef::from_ptr(bssl_sys::EVP_sha256() as *mut _) }
}
}
impl Md for Sha512 {
fn get_md() -> &'static MdRef {
// Safety:
// - this always returns a valid pointer to an EVP_MD
unsafe { MdRef::from_ptr(bssl_sys::EVP_sha512() as *mut _) }
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_sha256_c_type() {
unsafe {
assert_eq!(
MdRef::from_ptr(bssl_sys::EVP_sha256() as *mut _).as_ptr(),
bssl_sys::EVP_sha256() as *mut _
)
}
}
#[test]
fn test_sha512_c_type() {
unsafe {
assert_eq!(
MdRef::from_ptr(bssl_sys::EVP_sha512() as *mut _).as_ptr(),
bssl_sys::EVP_sha512() as *mut _
)
}
}
}

@ -0,0 +1,391 @@
/* 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.
*/
use crate::{
digest::{Md, Sha256, Sha512},
CSlice, ForeignTypeRef as _, PanicResultHandler,
};
use core::{
ffi::{c_uint, c_void},
marker::PhantomData,
ptr,
};
/// Computes the HMAC-SHA-256 of `data` as a one-shot operation.
///
/// Calculates the HMAC of data, using the given `key` and returns the result.
/// It returns the computed hmac or `InvalidLength` of the input key size is too large.
/// Can panic if memory allocation fails in the underlying BoringSSL code.
pub fn hmac_sha_256(key: &[u8], data: &[u8]) -> Result<[u8; 32], InvalidLength> {
hmac::<32, Sha256>(key, data)
}
/// Computes the HMAC-SHA-512 of `data` as a one-shot operation.
///
/// Calculates the HMAC of data, using the given `key` and returns the result.
/// It returns the computed hmac or `InvalidLength` of the input key size is too large.
/// Can panic if memory allocation fails in the underlying BoringSSL code.
pub fn hmac_sha_512(key: &[u8], data: &[u8]) -> Result<[u8; 64], InvalidLength> {
hmac::<64, Sha512>(key, data)
}
/// The BoringSSL HMAC-SHA-256 implementation. The operations may panic if memory allocation fails
/// in BoringSSL.
pub struct HmacSha256(Hmac<32, Sha256>);
impl HmacSha256 {
/// Create a new hmac from a fixed size key.
pub fn new(key: [u8; 32]) -> Self {
Self(Hmac::new(key))
}
/// Create new hmac value from variable size key.
pub fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
Hmac::new_from_slice(key).map(Self)
}
/// Update state using the provided data.
pub fn update(&mut self, data: &[u8]) {
self.0.update(data)
}
/// Obtain the hmac computation consuming the hmac instance.
pub fn finalize(self) -> [u8; 32] {
self.0.finalize()
}
/// Check that the tag value is correct for the processed input.
pub fn verify_slice(self, tag: &[u8]) -> Result<(), MacError> {
self.0.verify_slice(tag)
}
/// Check that the tag value is correct for the processed input.
pub fn verify(self, tag: [u8; 32]) -> Result<(), MacError> {
self.0.verify(tag)
}
/// Check truncated tag correctness using left side bytes of the calculated tag.
pub fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError> {
self.0.verify_truncated_left(tag)
}
}
/// The BoringSSL HMAC-SHA-512 implementation. The operations may panic if memory allocation fails
/// in BoringSSL.
pub struct HmacSha512(Hmac<64, Sha512>);
impl HmacSha512 {
/// Create a new hmac from a fixed size key.
pub fn new(key: [u8; 64]) -> Self {
Self(Hmac::new(key))
}
/// Create new hmac value from variable size key.
pub fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
Hmac::new_from_slice(key).map(Self)
}
/// Update state using the provided data.
pub fn update(&mut self, data: &[u8]) {
self.0.update(data)
}
/// Obtain the hmac computation consuming the hmac instance.
pub fn finalize(self) -> [u8; 64] {
self.0.finalize()
}
/// Check that the tag value is correct for the processed input.
pub fn verify_slice(self, tag: &[u8]) -> Result<(), MacError> {
self.0.verify_slice(tag)
}
/// Check that the tag value is correct for the processed input.
pub fn verify(self, tag: [u8; 64]) -> Result<(), MacError> {
self.0.verify(tag)
}
/// Check truncated tag correctness using left side bytes of the calculated tag.
pub fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError> {
self.0.verify_truncated_left(tag)
}
}
/// Error type for when the provided key material length is invalid.
#[derive(Debug)]
pub struct InvalidLength;
/// Error type for when the output of the hmac operation is not equal to the expected value.
#[derive(Debug)]
pub struct MacError;
/// Private generically implemented function for computing hmac as a oneshot operation.
/// This should only be exposed publicly by types with the correct output size `N` which corresponds
/// to the output size of the provided generic hash function. Ideally `N` would just come from `M`,
/// but this is not possible until the Rust language can support the `min_const_generics` feature.
/// Until then we will have to pass both separately: https://github.com/rust-lang/rust/issues/60551
#[inline]
fn hmac<const N: usize, M: Md>(key: &[u8], data: &[u8]) -> Result<[u8; N], InvalidLength> {
let mut out = [0_u8; N];
let mut size: c_uint = 0;
// Safety:
// - buf always contains N bytes of space
// - If NULL is returned on error we panic immediately
unsafe {
bssl_sys::HMAC(
M::get_md().as_ptr(),
CSlice::from(key).as_ptr(),
key.len(),
CSlice::from(data).as_ptr(),
data.len(),
out.as_mut_ptr(),
&mut size as *mut c_uint,
)
}
.panic_if_error();
Ok(out)
}
/// Private generically implemented hmac instance given a generic hash function and a length `N`,
/// where `N` is the output size of the hash function. This should only be exposed publicly by
/// wrapper types with the correct output size `N` which corresponds to the output size of the
/// provided generic hash function. Ideally `N` would just come from `M`, but this is not possible
/// until the Rust language can support the `min_const_generics` feature. Until then we will have to
/// pass both separately: https://github.com/rust-lang/rust/issues/60551
struct Hmac<const N: usize, M: Md> {
ctx: *mut bssl_sys::HMAC_CTX,
_marker: PhantomData<M>,
}
impl<const N: usize, M: Md> Hmac<N, M> {
/// Infallible HMAC creation from a fixed length key.
fn new(key: [u8; N]) -> Self {
#[allow(clippy::expect_used)]
Self::new_from_slice(&key).expect("output length of hash is always a valid hmac key size")
}
/// Create new hmac value from variable size key. Panics on allocation failure
/// returns InvalidLength if the key length is greater than the max message digest block size.
fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
(validate_key_len(key.len()))
.then(|| {
// Safety:
// - HMAC_CTX_new panics if allocation fails
let ctx = unsafe { bssl_sys::HMAC_CTX_new() };
ctx.panic_if_error();
// Safety:
// - HMAC_Init_ex must be called with a context previously created with HMAC_CTX_new,
// which is the line above.
// - HMAC_Init_ex may return an error if key is null but the md is different from
// before. This is avoided here since key is guaranteed to be non-null.
// - HMAC_Init_ex returns 0 on allocation failure in which case we panic
unsafe {
bssl_sys::HMAC_Init_ex(
ctx,
CSlice::from(key).as_ptr() as *const c_void,
key.len(),
M::get_md().as_ptr(),
ptr::null_mut(),
)
}
.panic_if_error();
Self {
ctx,
_marker: Default::default(),
}
})
.ok_or(InvalidLength)
}
/// Update state using the provided data, can be called repeatedly.
fn update(&mut self, data: &[u8]) {
unsafe {
// Safety: HMAC_Update will always return 1, in case it doesnt we panic
bssl_sys::HMAC_Update(self.ctx, data.as_ptr(), data.len())
}
.panic_if_error()
}
/// Obtain the hmac computation consuming the hmac instance.
fn finalize(self) -> [u8; N] {
let mut buf = [0_u8; N];
let mut size: c_uint = 0;
// Safety:
// - hmac has a fixed size output of N which will never exceed the length of an N
// length array
// - on allocation failure we panic
unsafe { bssl_sys::HMAC_Final(self.ctx, buf.as_mut_ptr(), &mut size as *mut c_uint) }
.panic_if_error();
buf
}
/// Check that the tag value is correct for the processed input.
fn verify(self, tag: [u8; N]) -> Result<(), MacError> {
self.verify_slice(&tag)
}
/// Check truncated tag correctness using all bytes
/// of calculated tag.
///
/// Returns `Error` if `tag` is not valid or not equal in length
/// to MAC's output.
fn verify_slice(self, tag: &[u8]) -> Result<(), MacError> {
tag.len().eq(&N).then_some(()).ok_or(MacError)?;
self.verify_truncated_left(tag)
}
/// Check truncated tag correctness using left side bytes
/// (i.e. `tag[..n]`) of calculated tag.
///
/// Returns `Error` if `tag` is not valid or empty.
fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError> {
let len = tag.len();
if len == 0 || len > N {
return Err(MacError);
}
let result = &self.finalize()[..len];
// Safety:
// - if a != b is undefined, it simply returns a non-zero result
unsafe {
bssl_sys::CRYPTO_memcmp(
CSlice::from(result).as_ptr() as *const c_void,
CSlice::from(tag).as_ptr() as *const c_void,
result.len(),
)
}
.eq(&0)
.then_some(())
.ok_or(MacError)
}
}
impl<const N: usize, M: Md> Drop for Hmac<N, M> {
fn drop(&mut self) {
unsafe { bssl_sys::HMAC_CTX_free(self.ctx) }
}
}
// make sure key len is within a valid range
fn validate_key_len(len: usize) -> bool {
if len > bssl_sys::EVP_MAX_MD_BLOCK_SIZE as usize {
return false;
}
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hmac_sha256_test() {
let expected_hmac = [
0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
0x2e, 0x32, 0xcf, 0xf7,
];
let key: [u8; 20] = [0x0b; 20];
let data = b"Hi There";
let mut hmac = HmacSha256::new_from_slice(&key).expect("length is valid");
hmac.update(data);
let hmac_result: [u8; 32] = hmac.finalize();
// let hmac_result =
// hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
assert_eq!(&hmac_result, &expected_hmac);
}
#[test]
fn hmac_sha256_fixed_size_key_test() {
let expected_hmac = [
0x19, 0x8a, 0x60, 0x7e, 0xb4, 0x4b, 0xfb, 0xc6, 0x99, 0x3, 0xa0, 0xf1, 0xcf, 0x2b,
0xbd, 0xc5, 0xba, 0xa, 0xa3, 0xf3, 0xd9, 0xae, 0x3c, 0x1c, 0x7a, 0x3b, 0x16, 0x96,
0xa0, 0xb6, 0x8c, 0xf7,
];
let key: [u8; 32] = [0x0b; 32];
let data = b"Hi There";
let mut hmac = HmacSha256::new(key);
hmac.update(data);
let hmac_result: [u8; 32] = hmac.finalize();
assert_eq!(&hmac_result, &expected_hmac);
}
#[test]
fn hmac_sha256_update_test() {
let expected_hmac = [
0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
0x2e, 0x32, 0xcf, 0xf7,
];
let key: [u8; 20] = [0x0b; 20];
let data = b"Hi There";
let mut hmac: HmacSha256 = HmacSha256::new_from_slice(&key).expect("");
hmac.update(data);
let result = hmac.finalize();
assert_eq!(&result, &expected_hmac);
assert_eq!(result.len(), 32);
}
#[test]
fn hmac_sha256_test_big_buffer() {
let expected_hmac = [
0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
0x2e, 0x32, 0xcf, 0xf7,
];
let key: [u8; 20] = [0x0b; 20];
let data = b"Hi There";
let hmac_result = hmac_sha_256(&key, data).expect("Couldn't calculate sha256 hmac");
assert_eq!(&hmac_result, &expected_hmac);
}
#[test]
fn hmac_sha256_update_chunks_test() {
let expected_hmac = [
0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
0x2e, 0x32, 0xcf, 0xf7,
];
let key: [u8; 20] = [0x0b; 20];
let mut hmac = HmacSha256::new_from_slice(&key).expect("key is valid length");
hmac.update(b"Hi");
hmac.update(b" There");
let result = hmac.finalize();
assert_eq!(&result, &expected_hmac);
}
#[test]
fn hmac_sha256_verify_test() {
let expected_hmac = [
0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
0x2e, 0x32, 0xcf, 0xf7,
];
let key: [u8; 20] = [0x0b; 20];
let data = b"Hi There";
let mut hmac: HmacSha256 = HmacSha256::new_from_slice(&key).expect("");
hmac.update(data);
assert!(hmac.verify(expected_hmac).is_ok())
}
}

@ -0,0 +1,118 @@
/* 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,
clippy::indexing_slicing,
clippy::unwrap_used,
clippy::panic,
clippy::expect_used
)]
//! Rust boringssl binding
extern crate core;
use core::ops::Not;
/// BoringSSL implemented hmac operations.
pub mod hmac;
/// BoringSSL implemented hash functions.
pub mod digest;
/// Used for handling result types from C APIs.
trait PanicResultHandler {
/// Panics if a C api returns an invalid result
/// Used for APIs which return error codes for allocation failures.
fn panic_if_error(&self);
}
impl PanicResultHandler for i32 {
/// BoringSSL APIs return 1 on success or 0 on allocation failure.
#[allow(clippy::expect_used)]
fn panic_if_error(&self) {
self.gt(&0).then_some(()).expect("allocation failed!")
}
}
impl<T> PanicResultHandler for *mut T {
/// Boringssl APIs return NULL on allocation failure for APIs that return a CTX.
#[allow(clippy::expect_used)]
fn panic_if_error(&self) {
self.is_null()
.not()
.then_some(())
.expect("allocation failed!")
}
}
struct CSlice<'a>(&'a [u8]);
impl CSlice<'_> {
pub fn as_ptr<T>(&self) -> *const T {
if self.0.is_empty() {
std::ptr::null()
} else {
self.0.as_ptr() as *const T
}
}
}
impl<'a> From<&'a [u8]> for CSlice<'a> {
fn from(value: &'a [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_mut_ptr(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());
&*(ptr as *mut _)
}
/// Constructs a mutable reference of this type from its raw type.
///
/// # Safety
///
/// `ptr` must be a valid, unique, instance of the type for the `'a` lifetime.
#[inline]
unsafe fn from_ptr_mut<'a>(ptr: *mut Self::CType) -> &'a mut Self {
debug_assert!(!ptr.is_null());
&mut *(ptr as *mut _)
}
/// Returns a raw pointer to the wrapped value.
#[inline]
fn as_ptr(&self) -> *mut Self::CType {
self as *const _ as *mut _
}
}

@ -0,0 +1,40 @@
# Additional interop for things like macros and inlined functions.
add_library(rust_wrapper STATIC rust_wrapper.c)
target_link_libraries(rust_wrapper crypto)
# Generate architecture-specific wrappers.
set(WRAPPER_TARGET ${CMAKE_BINARY_DIR}/rust/bssl-sys/src/wrapper_${RUST_BINDINGS}.rs)
set(COMMAND ${BINDGEN_EXECUTABLE} "wrapper.h"
-o ${WRAPPER_TARGET}
--no-derive-default
--enable-function-attribute-detection
--use-core
--size_t-is-usize
--default-macro-constant-type="signed"
--rustified-enum="point_conversion_form_t"
--allowlist-file=".*/include/openssl/.*\\.h"
--allowlist-file=".*/rust_wrapper\\.h"
-- # these are LLVM arg passthroughs
-I../../include
# https://doc.rust-lang.org/nightly/rustc/platform-support.html
--target=${RUST_BINDINGS})
set(INCLUDES "include!(\"wrapper_${RUST_BINDINGS}.rs\");\n")
add_custom_target(
bindgen_rust_${RUST_BINDINGS}
ALL
${COMMAND}
BYPRODUCTS ${WRAPPER_TARGET}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
# move files into build directory
configure_file("src/lib.rs" "src/lib.rs")
if(NOT BUILD_SHARED_LIBS)
configure_file("build.rs" "build.rs" COPYONLY)
endif()
configure_file("Cargo.toml" "Cargo.toml" COPYONLY)

@ -3,6 +3,8 @@ name = "bssl-sys"
version = "0.1.0" version = "0.1.0"
authors = ["Benjamin Brittain <bwb@google.com>"] authors = ["Benjamin Brittain <bwb@google.com>"]
edition = "2018" edition = "2018"
publish = false
license = "MIT"
[dependencies] [dependencies]
libc = "0.2" libc = "0.2"

@ -19,18 +19,21 @@ use std::path::Path;
fn main() { fn main() {
let dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let crate_path = Path::new(&dir); let crate_path = Path::new(&dir);
let parent_path = crate_path.parent().unwrap();
// building bssl-sys with: `mkdir build && cd build && cmake -G Ninja .. -DRUST_BINDINGS="$(gcc -dumpmachine)" && ninja`
// outputs this crate to /build/rust/bssl-sys/ so need to go up 3 levels to the root of the repo
let repo_root = crate_path.parent().unwrap().parent().unwrap();
// Statically link libraries. // Statically link libraries.
println!( println!(
"cargo:rustc-link-search=native={}", "cargo:rustc-link-search=native={}",
parent_path.join("crypto").display() repo_root.join("crypto").display()
); );
println!("cargo:rustc-link-lib=static=crypto"); println!("cargo:rustc-link-lib=static=crypto");
println!( println!(
"cargo:rustc-link-search=native={}", "cargo:rustc-link-search=native={}",
parent_path.join("ssl").display() repo_root.join("ssl").display()
); );
println!("cargo:rustc-link-lib=static=ssl"); println!("cargo:rustc-link-lib=static=ssl");

@ -0,0 +1,79 @@
#include "../../include/openssl/aes.h"
#include "../../include/openssl/asn1.h"
#include "../../include/openssl/asn1_mac.h"
#include "../../include/openssl/asn1t.h"
#include "../../include/openssl/base.h"
#include "../../include/openssl/base64.h"
#include "../../include/openssl/bio.h"
#include "../../include/openssl/blake2.h"
#include "../../include/openssl/blowfish.h"
#include "../../include/openssl/bn.h"
#include "../../include/openssl/buf.h"
#include "../../include/openssl/buffer.h"
#include "../../include/openssl/bytestring.h"
#include "../../include/openssl/cast.h"
#include "../../include/openssl/chacha.h"
#include "../../include/openssl/cipher.h"
#include "../../include/openssl/cmac.h"
#include "../../include/openssl/conf.h"
#include "../../include/openssl/cpu.h"
#include "../../include/openssl/crypto.h"
#include "../../include/openssl/ctrdrbg.h"
#include "../../include/openssl/curve25519.h"
#include "../../include/openssl/des.h"
#include "../../include/openssl/dh.h"
#include "../../include/openssl/digest.h"
#include "../../include/openssl/dsa.h"
#include "../../include/openssl/dtls1.h"
#include "../../include/openssl/e_os2.h"
#include "../../include/openssl/ec.h"
#include "../../include/openssl/ec_key.h"
#include "../../include/openssl/ecdh.h"
#include "../../include/openssl/ecdsa.h"
#include "../../include/openssl/engine.h"
#include "../../include/openssl/err.h"
#include "../../include/openssl/evp.h"
#include "../../include/openssl/evp_errors.h"
#include "../../include/openssl/ex_data.h"
#include "../../include/openssl/hkdf.h"
#include "../../include/openssl/hmac.h"
#include "../../include/openssl/hpke.h"
#include "../../include/openssl/hrss.h"
#include "../../include/openssl/is_boringssl.h"
#include "../../include/openssl/kdf.h"
#include "../../include/openssl/lhash.h"
#include "../../include/openssl/md4.h"
#include "../../include/openssl/md5.h"
#include "../../include/openssl/mem.h"
#include "../../include/openssl/obj.h"
#include "../../include/openssl/obj_mac.h"
#include "../../include/openssl/objects.h"
#include "../../include/openssl/opensslconf.h"
#include "../../include/openssl/opensslv.h"
#include "../../include/openssl/ossl_typ.h"
#include "../../include/openssl/pem.h"
#include "../../include/openssl/pkcs12.h"
#include "../../include/openssl/pkcs7.h"
#include "../../include/openssl/pkcs8.h"
#include "../../include/openssl/poly1305.h"
#include "../../include/openssl/pool.h"
#include "../../include/openssl/rand.h"
#include "../../include/openssl/rc4.h"
#include "../../include/openssl/ripemd.h"
#include "../../include/openssl/rsa.h"
#include "../../include/openssl/safestack.h"
#include "../../include/openssl/sha.h"
#include "../../include/openssl/siphash.h"
#include "../../include/openssl/span.h"
#include "../../include/openssl/srtp.h"
#include "../../include/openssl/ssl.h"
#include "../../include/openssl/ssl3.h"
#include "../../include/openssl/stack.h"
#include "../../include/openssl/thread.h"
#include "../../include/openssl/tls1.h"
#include "../../include/openssl/trust_token.h"
#include "../../include/openssl/x509.h"
#include "../../include/openssl/x509_vfy.h"
#include "../../include/openssl/x509v3.h"
#include "rust_wrapper.h"

@ -1,79 +0,0 @@
#include "../include/openssl/aes.h"
#include "../include/openssl/asn1.h"
#include "../include/openssl/asn1_mac.h"
#include "../include/openssl/asn1t.h"
#include "../include/openssl/base.h"
#include "../include/openssl/base64.h"
#include "../include/openssl/bio.h"
#include "../include/openssl/blake2.h"
#include "../include/openssl/blowfish.h"
#include "../include/openssl/bn.h"
#include "../include/openssl/buf.h"
#include "../include/openssl/buffer.h"
#include "../include/openssl/bytestring.h"
#include "../include/openssl/cast.h"
#include "../include/openssl/chacha.h"
#include "../include/openssl/cipher.h"
#include "../include/openssl/cmac.h"
#include "../include/openssl/conf.h"
#include "../include/openssl/cpu.h"
#include "../include/openssl/crypto.h"
#include "../include/openssl/ctrdrbg.h"
#include "../include/openssl/curve25519.h"
#include "../include/openssl/des.h"
#include "../include/openssl/dh.h"
#include "../include/openssl/digest.h"
#include "../include/openssl/dsa.h"
#include "../include/openssl/dtls1.h"
#include "../include/openssl/e_os2.h"
#include "../include/openssl/ec.h"
#include "../include/openssl/ec_key.h"
#include "../include/openssl/ecdh.h"
#include "../include/openssl/ecdsa.h"
#include "../include/openssl/engine.h"
#include "../include/openssl/err.h"
#include "../include/openssl/evp.h"
#include "../include/openssl/evp_errors.h"
#include "../include/openssl/ex_data.h"
#include "../include/openssl/hkdf.h"
#include "../include/openssl/hmac.h"
#include "../include/openssl/hpke.h"
#include "../include/openssl/hrss.h"
#include "../include/openssl/is_boringssl.h"
#include "../include/openssl/kdf.h"
#include "../include/openssl/lhash.h"
#include "../include/openssl/md4.h"
#include "../include/openssl/md5.h"
#include "../include/openssl/mem.h"
#include "../include/openssl/obj.h"
#include "../include/openssl/obj_mac.h"
#include "../include/openssl/objects.h"
#include "../include/openssl/opensslconf.h"
#include "../include/openssl/opensslv.h"
#include "../include/openssl/ossl_typ.h"
#include "../include/openssl/pem.h"
#include "../include/openssl/pkcs12.h"
#include "../include/openssl/pkcs7.h"
#include "../include/openssl/pkcs8.h"
#include "../include/openssl/poly1305.h"
#include "../include/openssl/pool.h"
#include "../include/openssl/rand.h"
#include "../include/openssl/rc4.h"
#include "../include/openssl/ripemd.h"
#include "../include/openssl/rsa.h"
#include "../include/openssl/safestack.h"
#include "../include/openssl/sha.h"
#include "../include/openssl/siphash.h"
#include "../include/openssl/span.h"
#include "../include/openssl/srtp.h"
#include "../include/openssl/ssl.h"
#include "../include/openssl/ssl3.h"
#include "../include/openssl/stack.h"
#include "../include/openssl/thread.h"
#include "../include/openssl/tls1.h"
#include "../include/openssl/trust_token.h"
#include "../include/openssl/x509.h"
#include "../include/openssl/x509_vfy.h"
#include "../include/openssl/x509v3.h"
#include "rust_wrapper.h"
Loading…
Cancel
Save