blob: c935e8438f7c4eced7e0278389b0fd55354b551a [file]
// Copyright 2022, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! BoringSSL-based implementation of HMAC.
use crate::types::HmacCtx;
use crate::{malloc_err, openssl_last_err};
use alloc::boxed::Box;
use alloc::vec::Vec;
#[cfg(soong)]
use bssl_sys as ffi;
use kmr_common::{crypto, crypto::OpaqueOr, explicit, km_err, vec_try, Error};
use kmr_wire::keymint::Digest;
use log::error;
/// [`crypto::Hmac`] implementation based on BoringSSL.
pub struct BoringHmac;
impl crypto::Hmac for BoringHmac {
fn begin(
&self,
key: OpaqueOr<crypto::hmac::Key>,
digest: Digest,
) -> Result<Box<dyn crypto::AccumulatingOperation>, Error> {
let key = explicit!(key)?;
let op = BoringHmacOperation {
// Safety: BoringSSL emits either null or a valid raw pointer, and the value is
// immediately checked for null below.
ctx: unsafe { HmacCtx(ffi::HMAC_CTX_new()) },
};
if op.ctx.0.is_null() {
return Err(malloc_err!());
}
let digest = digest_into_openssl_ffi(digest)?;
#[cfg(soong)]
let key_len = key.0.len();
#[cfg(not(soong))]
let key_len = key.0.len() as i32;
// Safety: `op.ctx` is known non-null and valid, as is the result of
// `digest_into_openssl_ffi()`. `key_len` is length of `key.0`, which is a valid `Vec<u8>`.
let result = unsafe {
ffi::HMAC_Init_ex(
op.ctx.0,
key.0.as_ptr() as *const libc::c_void,
key_len,
digest,
core::ptr::null_mut(),
)
};
if result != 1 {
error!("Failed to HMAC_Init_ex()");
return Err(openssl_last_err());
}
Ok(Box::new(op))
}
}
/// [`crypto::HmacOperation`] implementation based on BoringSSL.
///
/// This implementation uses the `unsafe` wrappers around `HMAC_*` functions directly, because
/// BoringSSL does not support the `EVP_PKEY_HMAC` implementations that are used in the rust-openssl
/// crate.
pub struct BoringHmacOperation {
// Safety: `ctx` is always non-null and valid except for initial error path in `begin()`
ctx: HmacCtx,
}
impl core::ops::Drop for BoringHmacOperation {
fn drop(&mut self) {
// Safety: `self.ctx` might be null (in the error path when `ffi::HMAC_CTX_new` fails)
// but `ffi::HMAC_CTX_free` copes with null.
unsafe {
ffi::HMAC_CTX_free(self.ctx.0);
}
}
}
impl crypto::AccumulatingOperation for BoringHmacOperation {
fn update(&mut self, data: &[u8]) -> Result<(), Error> {
// Safety: `self.ctx` is non-null and valid, and `data` is a valid slice.
let result = unsafe { ffi::HMAC_Update(self.ctx.0, data.as_ptr(), data.len()) };
if result != 1 {
return Err(openssl_last_err());
}
Ok(())
}
fn finish(self: Box<Self>) -> Result<Vec<u8>, Error> {
let mut output_len = ffi::EVP_MAX_MD_SIZE as u32;
let mut output = vec_try![0; ffi::EVP_MAX_MD_SIZE as usize]?;
// Safety: `self.ctx` is non-null and valid; `output_len` is correct size of `output`
// buffer.
let result = unsafe {
// (force line break for safety lint limitation)
ffi::HMAC_Final(self.ctx.0, output.as_mut_ptr(), &mut output_len as *mut u32)
};
if result != 1 {
return Err(openssl_last_err());
}
output.truncate(output_len as usize);
Ok(output)
}
}
/// Translate a [`keymint::Digest`] into a raw [`ffi::EVD_MD`].
fn digest_into_openssl_ffi(digest: Digest) -> Result<*const ffi::EVP_MD, Error> {
// Safety: all of the `EVP_<digest>` functions return a non-null valid pointer.
unsafe {
match digest {
Digest::Md5 => Ok(ffi::EVP_md5()),
Digest::Sha1 => Ok(ffi::EVP_sha1()),
Digest::Sha224 => Ok(ffi::EVP_sha224()),
Digest::Sha256 => Ok(ffi::EVP_sha256()),
Digest::Sha384 => Ok(ffi::EVP_sha384()),
Digest::Sha512 => Ok(ffi::EVP_sha512()),
d => Err(km_err!(UnsupportedDigest, "unknown digest {:?}", d)),
}
}
}