update main-with-bazel from master branch
This commit is contained in:
@@ -0,0 +1 @@
|
||||
imports_granularity = "Crate"
|
||||
@@ -122,8 +122,10 @@ fn new_decrypt_key<const N: usize>(key: [u8; N]) -> AesDecryptKey {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::aes::{Aes, AesDecryptKey, AesEncryptKey};
|
||||
use crate::test_helpers::decode_hex;
|
||||
use crate::{
|
||||
aes::{Aes, AesDecryptKey, AesEncryptKey},
|
||||
test_helpers::decode_hex,
|
||||
};
|
||||
|
||||
// test data from https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf F.1.1
|
||||
#[test]
|
||||
|
||||
@@ -16,11 +16,9 @@
|
||||
extern crate alloc;
|
||||
|
||||
use crate::{CSlice, CSliceMut};
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{vec, vec::Vec};
|
||||
use bssl_sys::EVP_CIPHER;
|
||||
use core::ffi::c_int;
|
||||
use core::marker::PhantomData;
|
||||
use core::{ffi::c_int, marker::PhantomData};
|
||||
|
||||
/// AES-CTR stream cipher operations.
|
||||
pub mod aes_ctr;
|
||||
|
||||
+143
-141
@@ -13,162 +13,135 @@
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
use core::marker::PhantomData;
|
||||
//! Hash functions.
|
||||
//!
|
||||
//! ```
|
||||
//! use bssl_crypto::digest;
|
||||
//!
|
||||
//! // One-shot hashing.
|
||||
//! let digest: [u8; 32] = digest::Sha256::hash(b"hello");
|
||||
//!
|
||||
//! // Incremental hashing.
|
||||
//! let mut ctx = digest::Sha256::new();
|
||||
//! ctx.update(b"hel");
|
||||
//! ctx.update(b"lo");
|
||||
//! let digest2: [u8; 32] = ctx.digest();
|
||||
//!
|
||||
//! assert_eq!(digest, digest2);
|
||||
//! ```
|
||||
|
||||
use crate::{CSlice, ForeignTypeRef};
|
||||
use crate::{sealed, FfiSlice, ForeignTypeRef};
|
||||
use bssl_sys;
|
||||
|
||||
/// The SHA-256 digest algorithm.
|
||||
#[derive(Clone)]
|
||||
pub struct Sha256 {}
|
||||
|
||||
/// The 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.
|
||||
#[non_exhaustive]
|
||||
#[doc(hidden)]
|
||||
pub struct MdRef;
|
||||
|
||||
unsafe impl ForeignTypeRef for MdRef {
|
||||
type CType = bssl_sys::EVP_MD;
|
||||
}
|
||||
|
||||
/// Used internally to get a BoringSSL internal MD
|
||||
pub trait Md {
|
||||
/// The output size of the hash operation.
|
||||
const OUTPUT_SIZE: usize;
|
||||
/// Used internally to parameterize other primitives.
|
||||
pub trait Algorithm {
|
||||
/// The size of the resulting digest.
|
||||
const OUTPUT_LEN: usize;
|
||||
|
||||
/// Gets a reference to a message digest algorithm to be used by the HKDF implementation.
|
||||
fn get_md() -> &'static MdRef;
|
||||
#[doc(hidden)]
|
||||
fn get_md(_: sealed::Sealed) -> &'static MdRef;
|
||||
}
|
||||
|
||||
impl Md for Sha256 {
|
||||
const OUTPUT_SIZE: usize = bssl_sys::SHA256_DIGEST_LENGTH as usize;
|
||||
|
||||
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 _) }
|
||||
}
|
||||
/// The insecure SHA-1 hash algorithm.
|
||||
///
|
||||
/// Some existing protocols depend on SHA-1 and so it is provided here, but it
|
||||
/// does not provide collision resistance and should not be used if at all
|
||||
/// avoidable. Use SHA-256 instead.
|
||||
#[derive(Clone)]
|
||||
pub struct InsecureSha1 {
|
||||
ctx: bssl_sys::SHA_CTX,
|
||||
}
|
||||
|
||||
impl Sha256 {
|
||||
/// Creates a new [Digest] to compute a SHA-256 hash.
|
||||
pub fn new_digest() -> Digest<Self, { Self::OUTPUT_SIZE }> {
|
||||
// Note: This cannot be in the trait because using associated constants exprs there
|
||||
// requires nightly.
|
||||
Digest::<Self, { Self::OUTPUT_SIZE }>::new()
|
||||
}
|
||||
unsafe_iuf_algo!(
|
||||
InsecureSha1,
|
||||
20,
|
||||
EVP_sha1,
|
||||
SHA1,
|
||||
SHA1_Init,
|
||||
SHA1_Update,
|
||||
SHA1_Final
|
||||
);
|
||||
|
||||
/// The SHA-256 hash algorithm.
|
||||
#[derive(Clone)]
|
||||
pub struct Sha256 {
|
||||
ctx: bssl_sys::SHA256_CTX,
|
||||
}
|
||||
|
||||
impl Md for Sha512 {
|
||||
const OUTPUT_SIZE: usize = bssl_sys::SHA512_DIGEST_LENGTH as usize;
|
||||
unsafe_iuf_algo!(
|
||||
Sha256,
|
||||
32,
|
||||
EVP_sha256,
|
||||
SHA256,
|
||||
SHA256_Init,
|
||||
SHA256_Update,
|
||||
SHA256_Final
|
||||
);
|
||||
|
||||
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 _) }
|
||||
}
|
||||
/// The SHA-384 hash algorithm.
|
||||
#[derive(Clone)]
|
||||
pub struct Sha384 {
|
||||
ctx: bssl_sys::SHA512_CTX,
|
||||
}
|
||||
|
||||
impl Sha512 {
|
||||
/// Create a new [Digest] to compute a SHA-512 hash.
|
||||
pub fn new_digest() -> Digest<Self, { Self::OUTPUT_SIZE }> {
|
||||
// Note: This cannot be in the trait because using associated constants exprs there
|
||||
// requires nightly.
|
||||
Digest::<Self, { Self::OUTPUT_SIZE }>::new()
|
||||
}
|
||||
unsafe_iuf_algo!(
|
||||
Sha384,
|
||||
48,
|
||||
EVP_sha384,
|
||||
SHA384,
|
||||
SHA384_Init,
|
||||
SHA384_Update,
|
||||
SHA384_Final
|
||||
);
|
||||
|
||||
/// The SHA-512 hash algorithm.
|
||||
#[derive(Clone)]
|
||||
pub struct Sha512 {
|
||||
ctx: bssl_sys::SHA512_CTX,
|
||||
}
|
||||
|
||||
/// A pending digest operation.
|
||||
pub struct Digest<M: Md, const OUTPUT_SIZE: usize>(bssl_sys::EVP_MD_CTX, PhantomData<M>);
|
||||
unsafe_iuf_algo!(
|
||||
Sha512,
|
||||
64,
|
||||
EVP_sha512,
|
||||
SHA512,
|
||||
SHA512_Init,
|
||||
SHA512_Update,
|
||||
SHA512_Final
|
||||
);
|
||||
|
||||
impl<M: Md, const OUTPUT_SIZE: usize> Digest<M, OUTPUT_SIZE> {
|
||||
/// Creates a new Digest from the given `Md` type parameter.
|
||||
///
|
||||
/// Panics:
|
||||
/// - If `Md::OUTPUT_SIZE` is not the same as `OUTPUT_SIZE`.
|
||||
fn new() -> Self {
|
||||
// Note: runtime assertion needed here since using {M::OUTPUT_SIZE} in return type requires
|
||||
// unstable Rust feature.
|
||||
assert_eq!(M::OUTPUT_SIZE, OUTPUT_SIZE);
|
||||
let mut md_ctx_uninit = core::mem::MaybeUninit::<bssl_sys::EVP_MD_CTX>::uninit();
|
||||
// Safety:
|
||||
// - `EVP_DigestInit` initializes `md_ctx_uninit`
|
||||
// - `MdRef` ensures the validity of `md.as_ptr`
|
||||
let result =
|
||||
unsafe { bssl_sys::EVP_DigestInit(md_ctx_uninit.as_mut_ptr(), M::get_md().as_ptr()) };
|
||||
assert_eq!(result, 1, "bssl_sys::EVP_DigestInit failed");
|
||||
// Safety:
|
||||
// - md_ctx_uninit initialized with EVP_DigestInit, and the function returned 1 (success)
|
||||
let md_ctx = unsafe { md_ctx_uninit.assume_init() };
|
||||
Self(md_ctx, PhantomData)
|
||||
}
|
||||
|
||||
/// Hashes the provided input into the current digest operation.
|
||||
pub fn update(&mut self, data: &[u8]) {
|
||||
let data_ffi = CSlice(data);
|
||||
// Safety:
|
||||
// - `data` is a CSlice from safe Rust.
|
||||
let result = unsafe {
|
||||
bssl_sys::EVP_DigestUpdate(&mut self.0, data_ffi.as_ptr() as *const _, data_ffi.len())
|
||||
};
|
||||
assert_eq!(result, 1, "bssl_sys::EVP_DigestUpdate failed");
|
||||
}
|
||||
|
||||
/// Computes the final digest value, consuming the object.
|
||||
#[allow(clippy::expect_used)]
|
||||
pub fn finalize(mut self) -> [u8; OUTPUT_SIZE] {
|
||||
let mut digest_uninit =
|
||||
core::mem::MaybeUninit::<[u8; bssl_sys::EVP_MAX_MD_SIZE as usize]>::uninit();
|
||||
let mut len_uninit = core::mem::MaybeUninit::<u32>::uninit();
|
||||
// Safety:
|
||||
// - `digest_uninit` is allocated to `EVP_MAX_MD_SIZE` bytes long, as required by
|
||||
// EVP_DigestFinal_ex
|
||||
// - `self.0` is owned by `self`, and is going to be cleaned up on drop.
|
||||
let result = unsafe {
|
||||
bssl_sys::EVP_DigestFinal_ex(
|
||||
&mut self.0,
|
||||
digest_uninit.as_mut_ptr() as *mut _,
|
||||
len_uninit.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
assert_eq!(result, 1, "bssl_sys::EVP_DigestFinal_ex failed");
|
||||
// Safety:
|
||||
// - `len_uninit` is initialized by `EVP_DigestFinal_ex`, and we checked the result above
|
||||
let len = unsafe { len_uninit.assume_init() };
|
||||
assert_eq!(
|
||||
OUTPUT_SIZE, len as usize,
|
||||
"bssl_sys::EVP_DigestFinal_ex failed"
|
||||
);
|
||||
// Safety: Result of DigestFinal_ex was checked above
|
||||
let digest = unsafe { digest_uninit.assume_init() };
|
||||
digest
|
||||
.get(..OUTPUT_SIZE)
|
||||
.and_then(|digest| digest.try_into().ok())
|
||||
.expect("The length of `digest` was checked above")
|
||||
}
|
||||
/// The SHA-512/256 hash algorithm.
|
||||
#[derive(Clone)]
|
||||
pub struct Sha512_256 {
|
||||
ctx: bssl_sys::SHA512_CTX,
|
||||
}
|
||||
|
||||
impl<M: Md, const OUTPUT_SIZE: usize> Drop for Digest<M, OUTPUT_SIZE> {
|
||||
fn drop(&mut self) {
|
||||
// Safety: `self.0` is owned by `self`, and is invalidated after `drop`.
|
||||
unsafe {
|
||||
bssl_sys::EVP_MD_CTX_cleanup(&mut self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe_iuf_algo!(
|
||||
Sha512_256,
|
||||
32,
|
||||
EVP_sha512_256,
|
||||
SHA512_256,
|
||||
SHA512_256_Init,
|
||||
SHA512_256_Update,
|
||||
SHA512_256_Final
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::test_helpers::decode_hex;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_sha256_c_type() {
|
||||
fn sha256_c_type() {
|
||||
unsafe {
|
||||
assert_eq!(
|
||||
MdRef::from_ptr(bssl_sys::EVP_sha256() as *mut _).as_ptr(),
|
||||
@@ -178,7 +151,7 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha512_c_type() {
|
||||
fn sha512_c_type() {
|
||||
unsafe {
|
||||
assert_eq!(
|
||||
MdRef::from_ptr(bssl_sys::EVP_sha512() as *mut _).as_ptr(),
|
||||
@@ -188,31 +161,60 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_digest_sha256() {
|
||||
let mut digest = Sha256::new_digest();
|
||||
let msg: [u8; 4] = decode_hex("74ba2521");
|
||||
digest.update(&msg);
|
||||
let expected_digest: [u8; 32] =
|
||||
decode_hex("b16aa56be3880d18cd41e68384cf1ec8c17680c45a02b1575dc1518923ae8b0e");
|
||||
assert_eq!(expected_digest, digest.finalize());
|
||||
fn sha1() {
|
||||
assert_eq!(
|
||||
decode_hex("a9993e364706816aba3e25717850c26c9cd0d89d"),
|
||||
InsecureSha1::hash(b"abc")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_digest_sha512() {
|
||||
let mut digest = Sha512::new_digest();
|
||||
fn sha256() {
|
||||
let msg: [u8; 4] = decode_hex("74ba2521");
|
||||
let expected_digest: [u8; 32] =
|
||||
decode_hex("b16aa56be3880d18cd41e68384cf1ec8c17680c45a02b1575dc1518923ae8b0e");
|
||||
|
||||
assert_eq!(Sha256::hash(&msg), expected_digest);
|
||||
|
||||
let mut ctx = Sha256::new();
|
||||
ctx.update(&msg);
|
||||
assert_eq!(expected_digest, ctx.digest());
|
||||
|
||||
let mut ctx = Sha256::new();
|
||||
ctx.update(&msg[0..1]);
|
||||
let mut ctx2 = ctx.clone();
|
||||
ctx2.update(&msg[1..]);
|
||||
assert_eq!(expected_digest, ctx2.digest());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sha384() {
|
||||
assert_eq!(
|
||||
decode_hex("cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"),
|
||||
Sha384::hash(b"abc")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sha512() {
|
||||
let msg: [u8; 4] = decode_hex("23be86d5");
|
||||
digest.update(&msg);
|
||||
let expected_digest: [u8; 64] = decode_hex(concat!(
|
||||
"76d42c8eadea35a69990c63a762f330614a4699977f058adb988f406fb0be8f2",
|
||||
"ea3dce3a2bbd1d827b70b9b299ae6f9e5058ee97b50bd4922d6d37ddc761f8eb"
|
||||
));
|
||||
assert_eq!(expected_digest, digest.finalize());
|
||||
|
||||
assert_eq!(Sha512::hash(&msg), expected_digest);
|
||||
|
||||
let mut ctx = Sha512::new();
|
||||
ctx.update(&msg);
|
||||
assert_eq!(expected_digest, ctx.digest());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_digest_wrong_size() {
|
||||
// This should not happen since we don't externally expose Digest::new
|
||||
Digest::<Sha256, 64>::new();
|
||||
fn sha512_256() {
|
||||
assert_eq!(
|
||||
decode_hex("53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"),
|
||||
Sha512_256::hash(b"abc")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,8 @@
|
||||
//! intended for internal use within this crate only, to create higher-level abstractions suitable
|
||||
//! to be exposed externally.
|
||||
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::panic;
|
||||
use core::{borrow::Borrow, fmt::Debug, ops::Deref};
|
||||
use alloc::{borrow::ToOwned, vec, vec::Vec};
|
||||
use core::{borrow::Borrow, fmt::Debug, ops::Deref, panic};
|
||||
|
||||
use crate::{bn::BigNum, CSlice, CSliceMut, ForeignType, ForeignTypeRef};
|
||||
|
||||
|
||||
+232
-163
@@ -12,85 +12,225 @@
|
||||
* 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;
|
||||
use crate::digest::{Sha256, Sha512};
|
||||
use crate::{CSlice, CSliceMut, ForeignTypeRef};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
//! Implements the HMAC-based Key Derivation Function from
|
||||
//! <https://datatracker.ietf.org/doc/html/rfc5869>.
|
||||
//!
|
||||
//! One-shot operation:
|
||||
//!
|
||||
//! ```
|
||||
//! use bssl_crypto::{hkdf, hkdf::HkdfSha256};
|
||||
//!
|
||||
//! let key: [u8; 32] = HkdfSha256::derive(b"secret", hkdf::Salt::NonEmpty(b"salt"),
|
||||
//! b"info");
|
||||
//! ```
|
||||
//!
|
||||
//! If deriving several keys that vary only in the `info` parameter, then part
|
||||
//! of the computation can be shared by calculating the "pseudo-random key".
|
||||
//! This is purely a performance optimisation.
|
||||
//!
|
||||
//! ```
|
||||
//! use bssl_crypto::{hkdf, hkdf::HkdfSha256};
|
||||
//!
|
||||
//! let prk = HkdfSha256::extract(b"secret", hkdf::Salt::NonEmpty(b"salt"));
|
||||
//! let key1 : [u8; 32] = prk.expand(b"info1");
|
||||
//! let key2 : [u8; 32] = prk.expand(b"info2");
|
||||
//!
|
||||
//! assert_eq!(key1, HkdfSha256::derive(b"secret", hkdf::Salt::NonEmpty(b"salt"),
|
||||
//! b"info1"));
|
||||
//! assert_eq!(key2, HkdfSha256::derive(b"secret", hkdf::Salt::NonEmpty(b"salt"),
|
||||
//! b"info2"));
|
||||
//! ```
|
||||
//!
|
||||
//! The above examples assume that the size of the outputs is known at compile
|
||||
//! time. (And only output lengths less than 256 bytes are supported.)
|
||||
//!
|
||||
//! ```compile_fail
|
||||
//! use bssl_crypto::{hkdf, hkdf::HkdfSha256};
|
||||
//!
|
||||
//! let key: [u8; 256] = HkdfSha256::derive(b"secret", hkdf::Salt::None, b"info");
|
||||
//! ```
|
||||
//!
|
||||
//! To use HKDF with longer, or run-time, lengths, use `derive_into` and
|
||||
//! `extract_into`:
|
||||
//!
|
||||
//! ```
|
||||
//! use bssl_crypto::{hkdf, hkdf::HkdfSha256};
|
||||
//!
|
||||
//! let mut out = [0u8; 50];
|
||||
//! HkdfSha256::derive_into(b"secret", hkdf::Salt::None, b"info", &mut out).expect(
|
||||
//! "HKDF can't produce that much");
|
||||
//!
|
||||
//! assert_eq!(out, HkdfSha256::derive(b"secret", hkdf::Salt::None, b"info"));
|
||||
//! ```
|
||||
|
||||
use crate::{digest, sealed, with_output_array, FfiMutSlice, FfiSlice, ForeignTypeRef};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Implementation of HKDF-SHA-256
|
||||
pub type HkdfSha256 = Hkdf<Sha256>;
|
||||
pub type HkdfSha256 = Hkdf<digest::Sha256>;
|
||||
|
||||
/// Implementation of HKDF-SHA-512
|
||||
pub type HkdfSha512 = Hkdf<Sha512>;
|
||||
pub type HkdfSha512 = Hkdf<digest::Sha512>;
|
||||
|
||||
/// Error type returned from the HKDF-Expand operations when the output key material has
|
||||
/// an invalid length
|
||||
/// Error type returned when too much output is requested from an HKDF operation.
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidLength;
|
||||
pub struct TooLong;
|
||||
|
||||
/// Implementation of HKDF operations which are generic over a provided hashing functions. Type
|
||||
/// aliases are provided above for convenience of commonly used hashes
|
||||
pub struct Hkdf<M: Md> {
|
||||
salt: Option<Vec<u8>>,
|
||||
ikm: Vec<u8>,
|
||||
_marker: PhantomData<M>,
|
||||
/// HKDF's optional salt values. See <https://datatracker.ietf.org/doc/html/rfc5869#section-3.1>
|
||||
pub enum Salt<'a> {
|
||||
/// No salt.
|
||||
None,
|
||||
/// An explicit salt. Note that an empty value here is interpreted the same
|
||||
/// as if passing `None`.
|
||||
NonEmpty(&'a [u8]),
|
||||
}
|
||||
|
||||
impl<M: Md> Hkdf<M> {
|
||||
/// The max length of the output key material used for expanding
|
||||
pub const MAX_OUTPUT_LENGTH: usize = M::OUTPUT_SIZE * 255;
|
||||
|
||||
/// Creates a new instance of HKDF from a salt and key material
|
||||
pub fn new(salt: Option<&[u8]>, ikm: &[u8]) -> Self {
|
||||
Self {
|
||||
salt: salt.map(Vec::from),
|
||||
ikm: Vec::from(ikm),
|
||||
_marker: PhantomData,
|
||||
impl Salt<'_> {
|
||||
fn as_ffi_ptr(&self) -> *const u8 {
|
||||
match self {
|
||||
Salt::None => core::ptr::null(),
|
||||
Salt::NonEmpty(salt) => salt.as_ffi_ptr(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes HKDF-Expand operation from RFC 5869. The info argument for the expand is set to
|
||||
/// the concatenation of all the elements of info_components. Returns InvalidLength if the
|
||||
/// output is too large.
|
||||
pub fn expand_multi_info(
|
||||
&self,
|
||||
info_components: &[&[u8]],
|
||||
okm: &mut [u8],
|
||||
) -> Result<(), InvalidLength> {
|
||||
self.expand(&info_components.concat(), okm)
|
||||
fn len(&self) -> usize {
|
||||
match self {
|
||||
Salt::None => 0,
|
||||
Salt::NonEmpty(salt) => salt.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// HKDF for any of the implemented hash functions. The aliases [`HkdfSha256`]
|
||||
/// and [`HkdfSha512`] are provided for the most common cases.
|
||||
pub struct Hkdf<MD: digest::Algorithm>(PhantomData<MD>);
|
||||
|
||||
impl<MD: digest::Algorithm> Hkdf<MD> {
|
||||
/// The maximum number of bytes of key material that can be produced.
|
||||
pub const MAX_OUTPUT_LEN: usize = MD::OUTPUT_LEN * 255;
|
||||
|
||||
/// Derive key material from the given secret, salt, and info. Attempting
|
||||
/// to derive more than 255 bytes is a compile-time error, see `derive_into`
|
||||
/// for longer outputs.
|
||||
///
|
||||
/// The semantics of the arguments are complex. See
|
||||
/// <https://datatracker.ietf.org/doc/html/rfc5869#section-3>.
|
||||
pub fn derive<const N: usize>(secret: &[u8], salt: Salt, info: &[u8]) -> [u8; N] {
|
||||
Self::extract(secret, salt).expand(info)
|
||||
}
|
||||
|
||||
/// Computes HKDF-Expand operation from RFC 5869. Returns InvalidLength if the output is too large.
|
||||
pub fn expand(&self, info: &[u8], okm: &mut [u8]) -> Result<(), InvalidLength> {
|
||||
// extract the salt bytes from the option, or empty slice if option is None
|
||||
let salt = self.salt.as_deref().unwrap_or_default();
|
||||
/// Derive key material from the given secret, salt, and info. Attempting
|
||||
/// to derive more than `MAX_OUTPUT_LEN` bytes is a run-time error.
|
||||
///
|
||||
/// The semantics of the arguments are complex. See
|
||||
/// <https://datatracker.ietf.org/doc/html/rfc5869#section-3>.
|
||||
pub fn derive_into(
|
||||
secret: &[u8],
|
||||
salt: Salt,
|
||||
info: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> Result<(), TooLong> {
|
||||
Self::extract(secret, salt).expand_into(info, out)
|
||||
}
|
||||
|
||||
//validate the output size
|
||||
(okm.len() <= Self::MAX_OUTPUT_LENGTH && !okm.is_empty())
|
||||
.then(|| {
|
||||
let mut okm_cslice = CSliceMut::from(okm);
|
||||
/// Extract a pseudo-random key from the given secret and salt. This can
|
||||
/// be used to avoid redoing computation when computing several keys that
|
||||
/// vary only in the `info` parameter.
|
||||
pub fn extract(secret: &[u8], salt: Salt) -> Prk {
|
||||
let mut prk = [0u8; bssl_sys::EVP_MAX_MD_SIZE as usize];
|
||||
let mut prk_len = 0usize;
|
||||
let evp_md = MD::get_md(sealed::Sealed).as_ptr();
|
||||
unsafe {
|
||||
// Safety: `EVP_MAX_MD_SIZE` is the maximum output size of
|
||||
// `HKDF_extract` so it'll never overrun the buffer.
|
||||
bssl_sys::HKDF_extract(
|
||||
prk.as_mut_ffi_ptr(),
|
||||
&mut prk_len,
|
||||
evp_md,
|
||||
secret.as_ffi_ptr(),
|
||||
secret.len(),
|
||||
salt.as_ffi_ptr(),
|
||||
salt.len(),
|
||||
);
|
||||
}
|
||||
// This is documented to be always be true.
|
||||
assert!(prk_len <= prk.len());
|
||||
Prk {
|
||||
prk,
|
||||
len: prk_len,
|
||||
evp_md,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Safety:
|
||||
// - We validate the output length above, so invalid length errors will never be hit
|
||||
// which leaves allocation failures as the only possible error case, in which case
|
||||
// we panic immediately
|
||||
let result = unsafe {
|
||||
bssl_sys::HKDF(
|
||||
okm_cslice.as_mut_ptr(),
|
||||
okm_cslice.len(),
|
||||
M::get_md().as_ptr(),
|
||||
CSlice::from(self.ikm.as_slice()).as_ptr(),
|
||||
self.ikm.as_slice().len(),
|
||||
CSlice::from(salt).as_ptr(),
|
||||
salt.len(),
|
||||
CSlice::from(info).as_ptr(),
|
||||
info.len(),
|
||||
)
|
||||
};
|
||||
assert!(result > 0, "Allocation failure in bssl_sys::HKDF");
|
||||
/// A pseudo-random key, an intermediate value in the HKDF computation.
|
||||
pub struct Prk {
|
||||
prk: [u8; bssl_sys::EVP_MAX_MD_SIZE as usize],
|
||||
len: usize,
|
||||
evp_md: *const bssl_sys::EVP_MD,
|
||||
}
|
||||
|
||||
#[allow(clippy::let_unit_value)]
|
||||
impl Prk {
|
||||
/// Derive key material for the given info parameter. Attempting
|
||||
/// to derive more than 255 bytes is a compile-time error, see `expand_into`
|
||||
/// for longer outputs.
|
||||
pub fn expand<const N: usize>(&self, info: &[u8]) -> [u8; N] {
|
||||
// This is the odd way to write a static assertion that uses a const
|
||||
// parameter in Rust. Even then, Rust cannot reference `MAX_OUTPUT_LEN`.
|
||||
// But if we safely assume that all hash functions output at least a
|
||||
// byte then 255 is a safe lower bound on `MAX_OUTPUT_LEN`.
|
||||
// A doctest at the top of the module checks that this assert is effective.
|
||||
struct StaticAssert<const N: usize, const BOUND: usize>;
|
||||
impl<const N: usize, const BOUND: usize> StaticAssert<N, BOUND> {
|
||||
const BOUNDS_CHECK: () = assert!(N < BOUND, "Large outputs not supported");
|
||||
}
|
||||
let _ = StaticAssert::<N, 256>::BOUNDS_CHECK;
|
||||
|
||||
unsafe {
|
||||
with_output_array(|out, out_len| {
|
||||
// Safety: `HKDF_expand` writes exactly `out_len` bytes or else
|
||||
// returns zero. `evp_md` is valid by construction.
|
||||
let result = bssl_sys::HKDF_expand(
|
||||
out,
|
||||
out_len,
|
||||
self.evp_md,
|
||||
self.prk.as_ffi_ptr(),
|
||||
self.len,
|
||||
info.as_ffi_ptr(),
|
||||
info.len(),
|
||||
);
|
||||
// The output length is known to be within bounds so the only other
|
||||
// possibily is an allocation failure, which we don't attempt to
|
||||
// handle.
|
||||
assert_eq!(result, 1);
|
||||
})
|
||||
.ok_or(InvalidLength)
|
||||
}
|
||||
}
|
||||
|
||||
/// Derive key material from the given info parameter. Attempting
|
||||
/// to derive more than the HKDF's `MAX_OUTPUT_LEN` bytes is a run-time
|
||||
/// error.
|
||||
pub fn expand_into(&self, info: &[u8], out: &mut [u8]) -> Result<(), TooLong> {
|
||||
// Safety: writes at most `out.len()` bytes into `out`.
|
||||
// `evp_md` is valid by construction.
|
||||
let result = unsafe {
|
||||
bssl_sys::HKDF_expand(
|
||||
out.as_mut_ffi_ptr(),
|
||||
out.len(),
|
||||
self.evp_md,
|
||||
self.prk.as_ffi_ptr(),
|
||||
self.len,
|
||||
info.as_ffi_ptr(),
|
||||
info.len(),
|
||||
)
|
||||
};
|
||||
if result == 1 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(TooLong)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,28 +242,18 @@ impl<M: Md> Hkdf<M> {
|
||||
clippy::unwrap_used
|
||||
)]
|
||||
mod tests {
|
||||
use crate::hkdf::{HkdfSha256, HkdfSha512};
|
||||
use crate::test_helpers::{decode_hex, decode_hex_into_vec};
|
||||
use core::iter;
|
||||
|
||||
struct Test {
|
||||
ikm: Vec<u8>,
|
||||
salt: Vec<u8>,
|
||||
info: Vec<u8>,
|
||||
okm: Vec<u8>,
|
||||
}
|
||||
use crate::{
|
||||
hkdf::{HkdfSha256, HkdfSha512, Salt},
|
||||
test_helpers::{decode_hex, decode_hex_into_vec},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn hkdf_sha_256_test() {
|
||||
fn sha256() {
|
||||
let ikm = decode_hex_into_vec("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
|
||||
let salt = decode_hex_into_vec("000102030405060708090a0b0c");
|
||||
let salt_vec = decode_hex_into_vec("000102030405060708090a0b0c");
|
||||
let salt = Salt::NonEmpty(&salt_vec);
|
||||
let info = decode_hex_into_vec("f0f1f2f3f4f5f6f7f8f9");
|
||||
|
||||
let hk = HkdfSha256::new(Some(salt.as_slice()), ikm.as_slice());
|
||||
let mut okm = [0u8; 42];
|
||||
hk.expand(&info, &mut okm)
|
||||
.expect("42 is a valid length for Sha256 to output");
|
||||
|
||||
let okm: [u8; 42] = HkdfSha256::derive(ikm.as_slice(), salt, info.as_slice());
|
||||
let expected = decode_hex(
|
||||
"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865",
|
||||
);
|
||||
@@ -131,15 +261,12 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hkdf_sha512_test() {
|
||||
fn sha512() {
|
||||
let ikm = decode_hex_into_vec("5d3db20e8238a90b62a600fa57fdb318");
|
||||
let salt = decode_hex_into_vec("1d6f3b38a1e607b5e6bcd4af1800a9d3");
|
||||
let salt_vec = decode_hex_into_vec("1d6f3b38a1e607b5e6bcd4af1800a9d3");
|
||||
let salt = Salt::NonEmpty(&salt_vec);
|
||||
let info = decode_hex_into_vec("2bc5f39032b6fc87da69ba8711ce735b169646fd");
|
||||
|
||||
let hk = HkdfSha512::new(Some(salt.as_slice()), ikm.as_slice());
|
||||
let mut okm = [0u8; 42];
|
||||
hk.expand(&info, &mut okm).expect("Should succeed");
|
||||
|
||||
let okm: [u8; 42] = HkdfSha512::derive(ikm.as_slice(), salt, info.as_slice());
|
||||
let expected = decode_hex(
|
||||
"8c3cf7122dcb5eb7efaf02718f1faf70bca20dcb75070e9d0871a413a6c05fc195a75aa9ffc349d70aae",
|
||||
);
|
||||
@@ -148,7 +275,13 @@ mod tests {
|
||||
|
||||
// Test Vectors from https://tools.ietf.org/html/rfc5869.
|
||||
#[test]
|
||||
fn test_rfc5869_sha256() {
|
||||
fn rfc5869_sha256() {
|
||||
struct Test {
|
||||
ikm: Vec<u8>,
|
||||
salt: Vec<u8>,
|
||||
info: Vec<u8>,
|
||||
okm: Vec<u8>,
|
||||
}
|
||||
let tests = [
|
||||
Test {
|
||||
// Test Case 1
|
||||
@@ -198,6 +331,7 @@ mod tests {
|
||||
"8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8"),
|
||||
},
|
||||
];
|
||||
|
||||
for Test {
|
||||
ikm,
|
||||
salt,
|
||||
@@ -206,90 +340,25 @@ mod tests {
|
||||
} in tests.iter()
|
||||
{
|
||||
let salt = if salt.is_empty() {
|
||||
None
|
||||
Salt::None
|
||||
} else {
|
||||
Some(salt.as_slice())
|
||||
Salt::NonEmpty(&salt)
|
||||
};
|
||||
let hkdf = HkdfSha256::new(salt, ikm.as_slice());
|
||||
let mut okm2 = vec![0u8; okm.len()];
|
||||
assert!(hkdf.expand(info.as_slice(), &mut okm2).is_ok());
|
||||
assert!(
|
||||
HkdfSha256::derive_into(ikm.as_slice(), salt, info.as_slice(), &mut okm2).is_ok()
|
||||
);
|
||||
assert_eq!(okm2.as_slice(), okm.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lengths() {
|
||||
let hkdf = HkdfSha256::new(None, &[]);
|
||||
let mut longest = vec![0u8; HkdfSha256::MAX_OUTPUT_LENGTH];
|
||||
assert!(hkdf.expand(&[], &mut longest).is_ok());
|
||||
// start at 1 since 0 is an invalid length
|
||||
let lengths = 1..HkdfSha256::MAX_OUTPUT_LENGTH + 1;
|
||||
fn max_output() {
|
||||
let hkdf = HkdfSha256::extract(b"", Salt::None);
|
||||
let mut longest = vec![0u8; HkdfSha256::MAX_OUTPUT_LEN];
|
||||
assert!(hkdf.expand_into(b"", &mut longest).is_ok());
|
||||
|
||||
for length in lengths {
|
||||
let mut okm = vec![0u8; length];
|
||||
|
||||
assert!(hkdf.expand(&[], &mut okm).is_ok());
|
||||
assert_eq!(okm.len(), length);
|
||||
assert_eq!(okm[..], longest[..length]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_max_length() {
|
||||
let hkdf = HkdfSha256::new(Some(&[]), &[]);
|
||||
let mut okm = vec![0u8; HkdfSha256::MAX_OUTPUT_LENGTH];
|
||||
assert!(hkdf.expand(&[], &mut okm).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_max_length_exceeded() {
|
||||
let hkdf = HkdfSha256::new(Some(&[]), &[]);
|
||||
let mut okm = vec![0u8; HkdfSha256::MAX_OUTPUT_LENGTH + 1];
|
||||
assert!(hkdf.expand(&[], &mut okm).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unsupported_length() {
|
||||
let hkdf = HkdfSha256::new(Some(&[]), &[]);
|
||||
let mut okm = vec![0u8; 90000];
|
||||
assert!(hkdf.expand(&[], &mut okm).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expand_multi_info() {
|
||||
let info_components = &[
|
||||
&b"09090909090909090909090909090909090909090909"[..],
|
||||
&b"8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a"[..],
|
||||
&b"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0"[..],
|
||||
&b"4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4"[..],
|
||||
&b"1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d"[..],
|
||||
];
|
||||
|
||||
let hkdf = HkdfSha256::new(None, b"some ikm here");
|
||||
|
||||
// Compute HKDF-Expand on the concatenation of all the info components
|
||||
let mut oneshot_res = [0u8; 16];
|
||||
hkdf.expand(&info_components.concat(), &mut oneshot_res)
|
||||
.unwrap();
|
||||
|
||||
// Now iteratively join the components of info_components until it's all 1 component. The value
|
||||
// of HKDF-Expand should be the same throughout
|
||||
let mut num_concatted = 0;
|
||||
let mut info_head = Vec::new();
|
||||
|
||||
while num_concatted < info_components.len() {
|
||||
info_head.extend(info_components[num_concatted]);
|
||||
|
||||
// Build the new input to be the info head followed by the remaining components
|
||||
let input: Vec<&[u8]> = iter::once(info_head.as_slice())
|
||||
.chain(info_components.iter().cloned().skip(num_concatted + 1))
|
||||
.collect();
|
||||
|
||||
// Compute and compare to the one-shot answer
|
||||
let mut multipart_res = [0u8; 16];
|
||||
hkdf.expand_multi_info(&input, &mut multipart_res).unwrap();
|
||||
assert_eq!(multipart_res, oneshot_res);
|
||||
num_concatted += 1;
|
||||
}
|
||||
let mut too_long = vec![0u8; HkdfSha256::MAX_OUTPUT_LEN + 1];
|
||||
assert!(hkdf.expand_into(b"", &mut too_long).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,9 @@
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
use crate::{
|
||||
digest::{Md, Sha256, Sha512},
|
||||
CSlice, ForeignTypeRef as _,
|
||||
digest,
|
||||
digest::{Sha256, Sha512},
|
||||
sealed, CSlice, ForeignTypeRef as _,
|
||||
};
|
||||
use core::{
|
||||
ffi::{c_uint, c_void},
|
||||
@@ -154,11 +155,11 @@ 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`,
|
||||
/// to the output size of the provided generic hash function. Ideally `N` would just come from `MD`,
|
||||
/// 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]) -> [u8; N] {
|
||||
fn hmac<const N: usize, MD: digest::Algorithm>(key: &[u8], data: &[u8]) -> [u8; N] {
|
||||
let mut out = [0_u8; N];
|
||||
let mut size: c_uint = 0;
|
||||
|
||||
@@ -167,7 +168,7 @@ fn hmac<const N: usize, M: Md>(key: &[u8], data: &[u8]) -> [u8; N] {
|
||||
// - If NULL is returned on error we panic immediately
|
||||
let result = unsafe {
|
||||
bssl_sys::HMAC(
|
||||
M::get_md().as_ptr(),
|
||||
MD::get_md(sealed::Sealed).as_ptr(),
|
||||
CSlice::from(key).as_ptr(),
|
||||
key.len(),
|
||||
CSlice::from(data).as_ptr(),
|
||||
@@ -184,15 +185,15 @@ fn hmac<const N: usize, M: Md>(key: &[u8], data: &[u8]) -> [u8; N] {
|
||||
/// 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
|
||||
/// provided generic hash function. Ideally `N` would just come from `MD`, 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> {
|
||||
struct Hmac<const N: usize, MD: digest::Algorithm> {
|
||||
ctx: *mut bssl_sys::HMAC_CTX,
|
||||
_marker: PhantomData<M>,
|
||||
_marker: PhantomData<MD>,
|
||||
}
|
||||
|
||||
impl<const N: usize, M: Md> Hmac<N, M> {
|
||||
impl<const N: usize, MD: digest::Algorithm> Hmac<N, MD> {
|
||||
/// Creates a new HMAC operation from a fixed-length key.
|
||||
fn new(key: [u8; N]) -> Self {
|
||||
Self::new_from_slice(&key)
|
||||
@@ -219,7 +220,7 @@ impl<const N: usize, M: Md> Hmac<N, M> {
|
||||
ctx,
|
||||
CSlice::from(key).as_ptr() as *const c_void,
|
||||
key.len(),
|
||||
M::get_md().as_ptr(),
|
||||
MD::get_md(sealed::Sealed).as_ptr(),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
@@ -311,7 +312,7 @@ impl<const N: usize, M: Md> Hmac<N, M> {
|
||||
self.ctx,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
M::get_md().as_ptr(),
|
||||
MD::get_md(sealed::Sealed).as_ptr(),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
@@ -319,7 +320,7 @@ impl<const N: usize, M: Md> Hmac<N, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, M: Md> Drop for Hmac<N, M> {
|
||||
impl<const N: usize, MD: digest::Algorithm> Drop for Hmac<N, MD> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { bssl_sys::HMAC_CTX_free(self.ctx) }
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@ extern crate core;
|
||||
|
||||
use core::ffi::c_void;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
/// Authenticated Encryption with Additional Data algorithms.
|
||||
pub mod aead;
|
||||
|
||||
@@ -39,13 +42,11 @@ pub mod aes;
|
||||
/// Ciphers.
|
||||
pub mod cipher;
|
||||
|
||||
/// Hash functions.
|
||||
pub mod digest;
|
||||
|
||||
/// Ed25519, a signature scheme.
|
||||
pub mod ed25519;
|
||||
|
||||
/// HKDF, a hash-based key derivation function.
|
||||
pub mod hkdf;
|
||||
|
||||
/// HMAC, a hash-based message authentication code.
|
||||
@@ -247,6 +248,20 @@ unsafe trait ForeignType {
|
||||
fn as_ptr(&self) -> *mut Self::CType;
|
||||
}
|
||||
|
||||
/// 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() }
|
||||
}
|
||||
|
||||
/// Wrap a closure that initializes an output buffer and return that buffer as
|
||||
/// an array. Requires that the closure fully initialize the given buffer.
|
||||
///
|
||||
@@ -290,3 +305,8 @@ where
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to prevent external implementations of internal traits.
|
||||
mod sealed {
|
||||
pub struct Sealed;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/* Copyright (c) 2024, 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.
|
||||
*/
|
||||
|
||||
// Generates a hash function from init/update/final-style FFI functions. Rust
|
||||
// doesn't accept function pointers as a generic arguments so this is the only
|
||||
// mechanism to avoid duplicating the code.
|
||||
//
|
||||
// The name is prefixed with "unsafe_" because it contains unsafe blocks.
|
||||
//
|
||||
// Safety: see the "Safety" sections within about the requirements for the
|
||||
// functions named in the macro parameters.
|
||||
macro_rules! unsafe_iuf_algo {
|
||||
($name:ident, $output_len:expr, $evp_md:ident, $one_shot:ident, $init:ident, $update:ident, $final_func:ident) => {
|
||||
impl Algorithm for $name {
|
||||
const OUTPUT_LEN: usize = $output_len as usize;
|
||||
|
||||
fn get_md(_: sealed::Sealed) -> &'static MdRef {
|
||||
// Safety:
|
||||
// - this always returns a valid pointer to an EVP_MD.
|
||||
unsafe { MdRef::from_ptr(bssl_sys::$evp_md() as *mut _) }
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
/// Digest `input` in a single operation.
|
||||
pub fn hash(input: &[u8]) -> [u8; $output_len] {
|
||||
// Safety: it is assumed that `$one_shot` indeed writes
|
||||
// `$output_len` bytes.
|
||||
unsafe {
|
||||
crate::with_output_array(|out, _| {
|
||||
bssl_sys::$one_shot(input.as_ffi_ptr(), input.len(), out);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new context for incremental hashing.
|
||||
pub fn new() -> Self {
|
||||
unsafe {
|
||||
Self {
|
||||
ctx: crate::initialized_struct(|ctx| {
|
||||
// Safety: type checking will ensure that `ctx` is the
|
||||
// correct type for `$init` to write into.
|
||||
bssl_sys::$init(ctx);
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Hash the contents of `input`.
|
||||
pub fn update(&mut self, input: &[u8]) {
|
||||
// Safety: arguments point to a valid buffer.
|
||||
unsafe {
|
||||
bssl_sys::$update(&mut self.ctx, input.as_ffi_void_ptr(), input.len());
|
||||
}
|
||||
}
|
||||
|
||||
/// Finish the hashing and return the digest.
|
||||
pub fn digest(mut self) -> [u8; $output_len] {
|
||||
// Safety: it is assumed that `$final_func` indeed writes
|
||||
// `$output_len` bytes.
|
||||
unsafe {
|
||||
crate::with_output_array(|out, _| {
|
||||
bssl_sys::$final_func(out, &mut self.ctx);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$name> for [u8; $output_len] {
|
||||
fn from(ctx: $name) -> [u8; $output_len] {
|
||||
ctx.digest()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$name> for alloc::vec::Vec<u8> {
|
||||
fn from(ctx: $name) -> alloc::vec::Vec<u8> {
|
||||
ctx.digest().into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::io::Write for $name {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.update(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -18,8 +18,7 @@
|
||||
//! externally.
|
||||
|
||||
use crate::{ec::EcKey, CSliceMut, ForeignType};
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::string::String;
|
||||
use alloc::{borrow::ToOwned, string::String};
|
||||
|
||||
pub(crate) struct Pkey {
|
||||
ptr: *mut bssl_sys::EVP_PKEY,
|
||||
|
||||
@@ -44,9 +44,7 @@
|
||||
//! // real protocols from a Diffie-Hellman primitive.
|
||||
//! ```
|
||||
|
||||
use crate::with_output_array;
|
||||
use crate::with_output_array_fallible;
|
||||
use crate::FfiSlice;
|
||||
use crate::{with_output_array, with_output_array_fallible, FfiSlice};
|
||||
|
||||
/// Number of bytes in a private key in X25519
|
||||
pub const PRIVATE_KEY_LEN: usize = bssl_sys::X25519_PRIVATE_KEY_LEN as usize;
|
||||
|
||||
Reference in New Issue
Block a user