#![no_std]
use core::marker::PhantomData;
use libcrux_secrets::{Classify, DeclassifyRef, DeclassifyRefMut, U8};
pub mod hacl;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Algorithm {
Sha256,
Sha384,
Sha512,
}
pub fn extract(
algo: Algorithm,
prk: &mut [U8],
salt: &[U8],
ikm: &[U8],
) -> Result<(), ExtractError> {
match algo {
Algorithm::Sha256 => sha2_256::extract(prk, salt, ikm),
Algorithm::Sha384 => sha2_384::extract(prk, salt, ikm),
Algorithm::Sha512 => sha2_512::extract(prk, salt, ikm),
}
}
pub fn expand(algo: Algorithm, okm: &mut [U8], prk: &[U8], info: &[u8]) -> Result<(), ExpandError> {
match algo {
Algorithm::Sha256 => sha2_256::expand(okm, prk, info),
Algorithm::Sha384 => sha2_384::expand(okm, prk, info),
Algorithm::Sha512 => sha2_512::expand(okm, prk, info),
}
}
pub fn hkdf(
algo: Algorithm,
okm: &mut [U8],
salt: &[U8],
ikm: &[U8],
info: &[u8],
) -> Result<(), ExpandError> {
match algo {
Algorithm::Sha256 => sha2_256::hkdf(okm, salt, ikm, info),
Algorithm::Sha384 => sha2_384::hkdf(okm, salt, ikm, info),
Algorithm::Sha512 => sha2_512::hkdf(okm, salt, ikm, info),
}
}
pub struct Sha2_256;
pub struct Sha2_384;
pub struct Sha2_512;
pub struct Hkdf<Algo>(PhantomData<Algo>);
impl Algorithm {
pub const fn hash_len(self) -> usize {
match self {
Algorithm::Sha256 => 32,
Algorithm::Sha384 => 48,
Algorithm::Sha512 => 64,
}
}
}
macro_rules! impl_hkdf {
($struct_name:path, $name:ident, $string_name:literal, $mode:path, $extract:ident, $expand:ident,$hash_len:literal) => {
#[doc = concat!("HKDF implementation for ", $string_name, ".")]
pub mod $name {
use libcrux_secrets::U8;
use super::*;
impl Hkdf<$struct_name> {
#[inline(always)]
pub fn extract_arrayref(
prk: &mut [U8; $hash_len],
salt: &[U8],
ikm: &[U8],
) -> Result<(), ArrayReferenceExtractError> {
extract_arrayref(prk, salt, ikm)
}
#[inline(always)]
pub fn extract(
prk: &mut [U8],
salt: &[U8],
ikm: &[U8],
) -> Result<(), ExtractError> {
extract(prk, salt, ikm)
}
#[inline(always)]
pub fn expand_arrayref(
okm: &mut [U8],
prk: &[U8; $hash_len],
info: &[u8],
) -> Result<(), ArrayReferenceExpandError> {
if okm.len() > 255 * $hash_len {
return Err(ArrayReferenceExpandError::OutputTooLong);
}
expand_arrayref(okm, prk, info)
}
#[inline(always)]
pub fn expand(okm: &mut [U8], prk: &[U8], info: &[u8]) -> Result<(), ExpandError> {
expand(okm, prk, info)
}
#[inline(always)]
pub fn hkdf(
okm: &mut [U8],
salt: &[U8],
ikm: &[U8],
info: &[u8],
) -> Result<(), ExpandError> {
hkdf(okm, salt, ikm, info)
}
}
#[inline(always)]
pub fn extract_arrayref(
prk: &mut [U8; $hash_len],
salt: &[U8],
ikm: &[U8],
) -> Result<(), ArrayReferenceExtractError> {
Ok(crate::hacl::$extract(
prk.declassify_ref_mut(),
salt.declassify_ref(),
checked_u32(salt.len())?,
ikm.declassify_ref(),
checked_u32(ikm.len())?,
))
}
#[inline(always)]
pub fn extract(prk: &mut [U8], salt: &[U8], ikm: &[U8]) -> Result<(), ExtractError> {
let (prk, _) = prk
.split_at_mut_checked($hash_len)
.ok_or(ExtractError::PrkTooShort)?;
let prk: &mut [U8; $hash_len] =
prk.try_into().map_err(|_| ExtractError::Unknown)?;
extract_arrayref(prk, salt, ikm).map_err(ExtractError::from)
}
#[inline(always)]
pub fn expand_arrayref(
mut okm: &mut [U8],
prk: &[U8; $hash_len],
info: &[u8],
) -> Result<(), ArrayReferenceExpandError> {
let okm_len = okm.len();
if okm_len > 255 * $hash_len {
return Err(ArrayReferenceExpandError::OutputTooLong);
}
Ok(crate::hacl::$expand(
okm.declassify_ref_mut(),
prk.declassify_ref(),
checked_u32(prk.len())?,
info,
checked_u32(info.len())?,
checked_u32(okm_len)?,
))
}
#[inline(always)]
pub fn expand(okm: &mut [U8], prk: &[U8], info: &[u8]) -> Result<(), ExpandError> {
let (prk, _) = prk
.split_at_checked($hash_len)
.ok_or(ExpandError::PrkTooShort)?;
let prk: &[U8; $hash_len] = prk.try_into().map_err(|_| ExpandError::Unknown)?;
expand_arrayref(okm, prk, info).map_err(ExpandError::from)
}
#[inline(always)]
pub fn hkdf(
okm: &mut [U8],
salt: &[U8],
ikm: &[U8],
info: &[u8],
) -> Result<(), ExpandError> {
let mut prk = [0u8; $hash_len].classify();
extract(&mut prk, salt, ikm)?;
expand(okm, &prk, info)
}
}
};
}
impl_hkdf!(
crate::Sha2_256,
sha2_256,
"SHA2-256",
Algorithm::Sha256,
extract_sha2_256,
expand_sha2_256,
32
);
impl_hkdf!(
crate::Sha2_384,
sha2_384,
"SHA2-384",
Algorithm::Sha384,
extract_sha2_384,
expand_sha2_384,
48
);
impl_hkdf!(
crate::Sha2_512,
sha2_512,
"SHA2-512",
Algorithm::Sha512,
extract_sha2_512,
expand_sha2_512,
64
);
fn checked_u32(num: usize) -> Result<u32, ArgumentsTooLongError> {
num.try_into().map_err(|_| ArgumentsTooLongError)
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ArrayReferenceExtractError {
ArgumentTooLong,
Unknown,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ExtractError {
PrkTooShort,
ArgumentTooLong,
Unknown,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ArrayReferenceExpandError {
OutputTooLong,
ArgumentTooLong,
Unknown,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ExpandError {
OutputTooLong,
PrkTooShort,
ArgumentTooLong,
Unknown,
}
#[derive(Copy, Clone, Debug, PartialEq)]
struct ArgumentsTooLongError;
impl From<ArrayReferenceExtractError> for ExtractError {
fn from(err: ArrayReferenceExtractError) -> Self {
match err {
ArrayReferenceExtractError::ArgumentTooLong => ExtractError::ArgumentTooLong,
ArrayReferenceExtractError::Unknown => ExtractError::Unknown,
}
}
}
impl From<ArrayReferenceExpandError> for ExpandError {
fn from(err: ArrayReferenceExpandError) -> Self {
match err {
ArrayReferenceExpandError::OutputTooLong => ExpandError::OutputTooLong,
ArrayReferenceExpandError::ArgumentTooLong => ExpandError::ArgumentTooLong,
ArrayReferenceExpandError::Unknown => ExpandError::Unknown,
}
}
}
impl From<ExtractError> for ExpandError {
fn from(err: ExtractError) -> Self {
match err {
ExtractError::PrkTooShort => ExpandError::PrkTooShort,
ExtractError::ArgumentTooLong => ExpandError::ArgumentTooLong,
ExtractError::Unknown => ExpandError::Unknown,
}
}
}
impl From<ArgumentsTooLongError> for ArrayReferenceExtractError {
fn from(_: ArgumentsTooLongError) -> Self {
ArrayReferenceExtractError::ArgumentTooLong
}
}
impl From<ArgumentsTooLongError> for ArrayReferenceExpandError {
fn from(_: ArgumentsTooLongError) -> Self {
ArrayReferenceExpandError::ArgumentTooLong
}
}
impl From<ArgumentsTooLongError> for ExtractError {
fn from(_: ArgumentsTooLongError) -> Self {
ExtractError::ArgumentTooLong
}
}
impl From<ArgumentsTooLongError> for ExpandError {
fn from(_: ArgumentsTooLongError) -> Self {
ExpandError::ArgumentTooLong
}
}