#![doc = include_str!("../README.md")]
#![no_std]
#![allow(clippy::match_same_arms)]
#![allow(clippy::must_use_candidate)]
#[cfg(feature = "_backend")]
pub mod backend;
pub mod kdf;
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::fmt;
use core::ops::DerefMut;
use smallvec::SmallVec;
use subtle::ConstantTimeEq;
pub trait Crypto: fmt::Debug + Send + Sync {
fn secure_random_fill(&mut self, buf: &mut [u8]) -> Result<(), CryptoError>;
fn is_kem_supported(&self, alg: &HpkeKemId) -> bool;
fn kem_generate_key_pair(&mut self, alg: HpkeKemId) -> Result<HpkeKeyPair, CryptoError>;
fn kem_encap(
&self,
_alg: HpkeKemId,
_pk_r: HpkePublicKeyRef<'_>,
) -> Result<(SharedSecret, EncapsulatedSecret), CryptoError> {
Err(CryptoError::KemOpUnsupported)
}
fn kem_decap(
&self,
_alg: HpkeKemId,
_enc: EncapsulatedSecret,
_sk_r: HpkePrivateKeyRef<'_>,
) -> Result<SharedSecret, CryptoError> {
Err(CryptoError::KemOpUnsupported)
}
fn is_kdf_supported(&self, alg: &HpkeKdfId) -> bool;
fn kdf_extract(&self, alg: HpkeKdfId, salt: &[u8], ikm: IkmRef<'_>)
-> Result<Prk, CryptoError>;
fn kdf_extract_concated(
&self,
alg: HpkeKdfId,
salt: &[u8],
ikms: &[IkmRef<'_>],
) -> Result<Prk, CryptoError> {
let mut concated = Vec::new();
for ikm in ikms {
concated.extend_from_slice(ikm);
}
self.kdf_extract(alg, salt, IkmRef::from(&concated))
}
fn kdf_expand(
&self,
alg: HpkeKdfId,
prk: PrkRef<'_>,
info: &[u8],
l: usize,
) -> Result<Okm, CryptoError>;
fn kdf_expand_multi_info(
&self,
alg: HpkeKdfId,
prk: PrkRef<'_>,
infos: &[&[u8]],
l: usize,
) -> Result<Okm, CryptoError> {
self.kdf_expand(alg, prk, &infos.concat(), l)
}
fn is_aead_supported(&self, alg: &HpkeAeadId) -> bool;
fn aead_seal(
&self,
crypto_info: &HpkeAead,
aad: &[u8],
plaintext: &[u8],
) -> Result<Vec<u8>, CryptoError> {
let mut buffer = plaintext.to_vec();
self.aead_seal_in_place(crypto_info, aad, &mut buffer)?;
Ok(buffer)
}
fn aead_seal_in_place(
&self,
crypto_info: &HpkeAead,
aad: &[u8],
buffer: &mut Vec<u8>,
) -> Result<(), CryptoError>;
fn aead_open(
&self,
crypto_info: &HpkeAead,
aad: &[u8],
ciphertext: &[u8],
) -> Result<Vec<u8>, CryptoError> {
let mut buffer = ciphertext.to_vec();
self.aead_open_in_place(crypto_info, aad, &mut buffer)?;
Ok(buffer)
}
fn aead_open_in_place(
&self,
crypto_info: &HpkeAead,
aad: &[u8],
buffer: &mut Vec<u8>,
) -> Result<(), CryptoError>;
fn sk(&self, alg: HpkeKemId, sk: &[u8]) -> Result<HpkePrivateKey, CryptoError>;
fn pk(&self, alg: HpkeKemId, sk: HpkePrivateKeyRef<'_>) -> Result<HpkePublicKey, CryptoError>;
fn dh(
&self,
alg: HpkeKemId,
sk_x: HpkePrivateKeyRef<'_>,
pk_y: HpkePublicKeyRef<'_>,
) -> Result<SharedSecret, CryptoError>;
}
impl<T> Crypto for T
where
T: DerefMut<Target = dyn Crypto> + fmt::Debug + Send + Sync + ?Sized,
{
fn secure_random_fill(&mut self, buf: &mut [u8]) -> Result<(), CryptoError> {
(**self).secure_random_fill(buf)
}
fn is_kem_supported(&self, alg: &HpkeKemId) -> bool {
(**self).is_kem_supported(alg)
}
fn kem_generate_key_pair(&mut self, alg: HpkeKemId) -> Result<HpkeKeyPair, CryptoError> {
(**self).kem_generate_key_pair(alg)
}
fn kem_encap(
&self,
alg: HpkeKemId,
pk_r: HpkePublicKeyRef<'_>,
) -> Result<(SharedSecret, EncapsulatedSecret), CryptoError> {
(**self).kem_encap(alg, pk_r)
}
fn kem_decap(
&self,
alg: HpkeKemId,
enc: EncapsulatedSecret,
sk_r: HpkePrivateKeyRef<'_>,
) -> Result<SharedSecret, CryptoError> {
(**self).kem_decap(alg, enc, sk_r)
}
fn is_kdf_supported(&self, alg: &HpkeKdfId) -> bool {
(**self).is_kdf_supported(alg)
}
fn kdf_extract(
&self,
alg: HpkeKdfId,
salt: &[u8],
ikm: IkmRef<'_>,
) -> Result<Prk, CryptoError> {
(**self).kdf_extract(alg, salt, ikm)
}
fn kdf_extract_concated(
&self,
alg: HpkeKdfId,
salt: &[u8],
ikms: &[IkmRef<'_>],
) -> Result<Prk, CryptoError> {
(**self).kdf_extract_concated(alg, salt, ikms)
}
fn kdf_expand(
&self,
alg: HpkeKdfId,
prk: PrkRef<'_>,
info: &[u8],
l: usize,
) -> Result<Okm, CryptoError> {
(**self).kdf_expand(alg, prk, info, l)
}
fn kdf_expand_multi_info(
&self,
alg: HpkeKdfId,
prk: PrkRef<'_>,
infos: &[&[u8]],
l: usize,
) -> Result<Okm, CryptoError> {
(**self).kdf_expand_multi_info(alg, prk, infos, l)
}
fn is_aead_supported(&self, alg: &HpkeAeadId) -> bool {
(**self).is_aead_supported(alg)
}
fn aead_seal(
&self,
crypto_info: &HpkeAead,
aad: &[u8],
plaintext: &[u8],
) -> Result<Vec<u8>, CryptoError> {
(**self).aead_seal(crypto_info, aad, plaintext)
}
fn aead_seal_in_place(
&self,
crypto_info: &HpkeAead,
aad: &[u8],
buffer: &mut Vec<u8>,
) -> Result<(), CryptoError> {
(**self).aead_seal_in_place(crypto_info, aad, buffer)
}
fn aead_open(
&self,
crypto_info: &HpkeAead,
aad: &[u8],
ciphertext: &[u8],
) -> Result<Vec<u8>, CryptoError> {
(**self).aead_open(crypto_info, aad, ciphertext)
}
fn aead_open_in_place(
&self,
crypto_info: &HpkeAead,
aad: &[u8],
buffer: &mut Vec<u8>,
) -> Result<(), CryptoError> {
(**self).aead_open_in_place(crypto_info, aad, buffer)
}
fn sk(&self, alg: HpkeKemId, sk: &[u8]) -> Result<HpkePrivateKey, CryptoError> {
(**self).sk(alg, sk)
}
fn pk(&self, alg: HpkeKemId, sk: HpkePrivateKeyRef<'_>) -> Result<HpkePublicKey, CryptoError> {
(**self).pk(alg, sk)
}
fn dh(
&self,
alg: HpkeKemId,
sk_x: HpkePrivateKeyRef<'_>,
pk_y: HpkePublicKeyRef<'_>,
) -> Result<SharedSecret, CryptoError> {
(**self).dh(alg, sk_x, pk_y)
}
}
#[derive(Debug)]
pub enum CryptoError {
KdfExpandInvalidPrkLen,
KdfExpandInvalidOutputLen,
KdfUnsupported,
KemDeriveKeyPair,
KemMalformedSkX,
KemMalformedPkX,
KemOpUnsupported,
KemUnsupported,
AeadInvalidKey,
AeadInvalidNonce,
AeadInvalidCt,
AeadSeal,
AeadOpen,
AeadUnsupported,
InsufficientRandomness,
Unspecified,
Custom(Box<dyn core::error::Error + Send + Sync + 'static>),
}
impl core::error::Error for CryptoError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::Custom(e) => Some(&**e),
_ => None,
}
}
}
impl fmt::Display for CryptoError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::KdfExpandInvalidPrkLen => write!(f, "KDF expand: invalid PRK length"),
Self::KdfExpandInvalidOutputLen => write!(f, "KDF expand: invalid output length"),
Self::KdfUnsupported => write!(f, "KDF unsupported"),
Self::KemDeriveKeyPair => write!(f, "KEM derive key pair failed"),
Self::KemMalformedSkX => write!(f, "KEM malformed private key"),
Self::KemMalformedPkX => write!(f, "KEM malformed public key"),
Self::KemOpUnsupported => write!(f, "KEM operation unsupported"),
Self::KemUnsupported => write!(f, "KEM unsupported"),
Self::AeadInvalidKey => write!(f, "AEAD invalid key"),
Self::AeadInvalidNonce => write!(f, "AEAD invalid nonce"),
Self::AeadInvalidCt => write!(f, "AEAD invalid cipher text"),
Self::AeadSeal => write!(f, "AEAD seal error"),
Self::AeadOpen => write!(f, "AEAD open error"),
Self::AeadUnsupported => write!(f, "AEAD unsupported"),
Self::InsufficientRandomness => write!(f, "Insufficient randomness"),
Self::Unspecified => write!(f, "Unspecified crypto error"),
Self::Custom(e) => write!(f, "Crypto library error: {e}"),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct HpkeCipherSuite {
pub kem_id: HpkeKemId,
pub kdf_id: HpkeKdfId,
pub aead_id: HpkeAeadId,
}
impl HpkeCipherSuite {
pub fn suite_id(&self) -> [u8; 10] {
let mut suite_id = [0u8; 10];
suite_id[0..4].copy_from_slice(b"HPKE");
suite_id[4..6].copy_from_slice(&self.kem_id.to_int().to_be_bytes());
suite_id[6..8].copy_from_slice(&self.kdf_id.to_int().to_be_bytes());
suite_id[8..10].copy_from_slice(&self.aead_id.to_int().to_be_bytes());
suite_id
}
}
macro_rules! enum_builder {
(
type Error = $error:ident;
#[repr($uint:ty)]
$(#[$enum_meta:meta])*
$vis:vis enum $name:ident
{
$(
$(#[doc = $registry_comment:literal])*
$registry_name:ident = $registry_value:literal
),+
$(,)?
}
) => {
#[non_exhaustive]
#[allow(clippy::upper_case_acronyms)]
#[allow(non_camel_case_types)]
#[derive(PartialEq, Eq, Clone, Copy)]
#[repr($uint)]
$(#[$enum_meta])*
$vis enum $name {
$(
$(#[doc = $registry_comment])*
$registry_name = $registry_value,
)+
}
impl $name {
#[inline]
#[allow(unused)]
#[allow(clippy::missing_errors_doc)]
$vis const fn try_from_int(x: $uint) -> Result<Self, $error> {
match x {
$(
$registry_value => Ok(Self::$registry_name),
)+
_ => Err($error (x)),
}
}
#[inline]
#[allow(unused)]
$vis const fn to_int(self) -> $uint {
self as $uint
}
#[inline]
#[allow(unused)]
$vis const fn to_array(self) -> [u8; core::mem::size_of::<$uint>()] {
self.to_int().to_be_bytes()
}
#[inline]
#[allow(unused)]
$vis const fn as_str(&self) -> &'static str {
match self {
$(
Self::$registry_name => stringify!($registry_name),
)+
}
}
}
impl From<$name> for $uint {
fn from(value: $name) -> Self {
value.to_int()
}
}
impl TryFrom<$uint> for $name {
type Error = $error;
fn try_from(x: $uint) -> Result<Self, Self::Error> {
Self::try_from_int(x)
}
}
impl ::core::fmt::Debug for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
$(
Self::$registry_name => f.write_str(stringify!($registry_name)),
)+
}
}
}
impl ::core::fmt::Display for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:?}", self)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.to_int().serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'a> serde::Deserialize<'a> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'a>,
{
let v = <$uint>::deserialize(deserializer)?;
Self::try_from(v).map_err(serde::de::Error::custom)
}
}
};
}
#[derive(Debug, Clone, Copy)]
pub struct UnknownHpkeKemId(pub u16);
impl fmt::Display for UnknownHpkeKemId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Unknown HPKE KEM ID: {}", self.0)
}
}
enum_builder!(
type Error = UnknownHpkeKemId;
#[repr(u16)]
pub enum HpkeKemId {
DHKEM_P256_HKDF_SHA256 = 0x0010,
DHKEM_P384_HKDF_SHA384 = 0x0011,
DHKEM_P521_HKDF_SHA512 = 0x0012,
DHKEM_X25519_HKDF_SHA256 = 0x0020,
DHKEM_X448_HKDF_SHA512 = 0x0021,
}
);
impl HpkeKemId {
#[inline]
pub const fn kdf_id(&self) -> HpkeKdfId {
match self {
Self::DHKEM_P256_HKDF_SHA256 | Self::DHKEM_X25519_HKDF_SHA256 => HpkeKdfId::HKDF_SHA256,
Self::DHKEM_P384_HKDF_SHA384 => HpkeKdfId::HKDF_SHA384,
Self::DHKEM_P521_HKDF_SHA512 | Self::DHKEM_X448_HKDF_SHA512 => HpkeKdfId::HKDF_SHA512,
}
}
#[inline]
pub fn suite_id(&self) -> [u8; 5] {
let mut suite_id = [0u8; 5];
suite_id[0..3].copy_from_slice(b"KEM");
suite_id[3..5].copy_from_slice(&self.to_int().to_be_bytes());
suite_id
}
#[inline]
pub const fn n_secret(&self) -> usize {
match self {
Self::DHKEM_P256_HKDF_SHA256 => 32,
Self::DHKEM_P384_HKDF_SHA384 => 48,
Self::DHKEM_P521_HKDF_SHA512 => 64,
Self::DHKEM_X25519_HKDF_SHA256 => 32,
Self::DHKEM_X448_HKDF_SHA512 => 64,
}
}
#[inline]
pub const fn n_enc(&self) -> usize {
match self {
Self::DHKEM_P256_HKDF_SHA256 => 65,
Self::DHKEM_P384_HKDF_SHA384 => 97,
Self::DHKEM_P521_HKDF_SHA512 => 133,
Self::DHKEM_X25519_HKDF_SHA256 => 32,
Self::DHKEM_X448_HKDF_SHA512 => 56,
}
}
#[inline]
pub const fn n_pk(&self) -> usize {
match self {
Self::DHKEM_P256_HKDF_SHA256 => 65,
Self::DHKEM_P384_HKDF_SHA384 => 97,
Self::DHKEM_P521_HKDF_SHA512 => 133,
Self::DHKEM_X25519_HKDF_SHA256 => 32,
Self::DHKEM_X448_HKDF_SHA512 => 56,
}
}
#[inline]
pub const fn n_sk(&self) -> usize {
match self {
Self::DHKEM_P256_HKDF_SHA256 => 32,
Self::DHKEM_P384_HKDF_SHA384 => 48,
Self::DHKEM_P521_HKDF_SHA512 => 66,
Self::DHKEM_X25519_HKDF_SHA256 => 32,
Self::DHKEM_X448_HKDF_SHA512 => 56,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct UnknownHpkeKdfId(pub u16);
impl fmt::Display for UnknownHpkeKdfId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Unknown HPKE KDF ID: {}", self.0)
}
}
enum_builder!(
type Error = UnknownHpkeKdfId;
#[repr(u16)]
pub enum HpkeKdfId {
HKDF_SHA256 = 0x0001,
HKDF_SHA384 = 0x0002,
HKDF_SHA512 = 0x0003,
}
);
impl HpkeKdfId {
#[inline]
pub const fn n_hash(&self) -> usize {
match self {
Self::HKDF_SHA256 => 32,
Self::HKDF_SHA384 => 48,
Self::HKDF_SHA512 => 64,
}
}
}
impl From<HpkeKemId> for HpkeKdfId {
#[inline]
fn from(kem: HpkeKemId) -> Self {
match kem {
HpkeKemId::DHKEM_P256_HKDF_SHA256 | HpkeKemId::DHKEM_X25519_HKDF_SHA256 => {
Self::HKDF_SHA256
}
HpkeKemId::DHKEM_P384_HKDF_SHA384 => Self::HKDF_SHA384,
HpkeKemId::DHKEM_P521_HKDF_SHA512 | HpkeKemId::DHKEM_X448_HKDF_SHA512 => {
Self::HKDF_SHA512
}
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct UnknownAeadAlgorithm(pub u16);
impl fmt::Display for UnknownAeadAlgorithm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Unknown HPKE AEAD ID: {}", self.0)
}
}
enum_builder!(
type Error = UnknownAeadAlgorithm;
#[repr(u16)]
pub enum HpkeAeadId {
AES_128_GCM = 0x0001,
AES_256_GCM = 0x0002,
CHACHA20_POLY1305 = 0x0003,
EXPORT_ONLY = 0xFFFF,
}
);
impl HpkeAeadId {
#[inline]
pub const fn n_key(&self) -> usize {
match self {
Self::AES_128_GCM => 16,
Self::AES_256_GCM => 32,
Self::CHACHA20_POLY1305 => 32,
Self::EXPORT_ONLY => 0,
}
}
#[inline]
pub const fn n_nonce(&self) -> usize {
match self {
Self::AES_128_GCM => 12,
Self::AES_256_GCM => 12,
Self::CHACHA20_POLY1305 => 12,
Self::EXPORT_ONLY => 0,
}
}
#[inline]
pub const fn n_tag(&self) -> usize {
match self {
Self::AES_128_GCM => 16,
Self::AES_256_GCM => 16,
Self::CHACHA20_POLY1305 => 16,
Self::EXPORT_ONLY => 0,
}
}
#[inline]
pub fn new_crypto_info(
&self,
key: &[u8],
nonce: &[u8],
) -> Result<Option<HpkeAead>, CryptoError> {
Ok(match self {
Self::AES_128_GCM => Some(HpkeAead::Aes128Gcm {
key: key
.try_into()
.map_err(|_| CryptoError::AeadInvalidKey)?,
nonce: nonce
.try_into()
.map_err(|_| CryptoError::AeadInvalidNonce)?,
}),
Self::AES_256_GCM => Some(HpkeAead::Aes256Gcm {
key: key
.try_into()
.map_err(|_| CryptoError::AeadInvalidKey)?,
nonce: nonce
.try_into()
.map_err(|_| CryptoError::AeadInvalidNonce)?,
}),
Self::CHACHA20_POLY1305 => Some(HpkeAead::ChaCha20Poly1305 {
key: key
.try_into()
.map_err(|_| CryptoError::AeadInvalidKey)?,
nonce: nonce
.try_into()
.map_err(|_| CryptoError::AeadInvalidNonce)?,
}),
Self::EXPORT_ONLY => None,
})
}
}
macro_rules! debug_hex {
($name:ty, $inner:ident) => {
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple(core::any::type_name::<Self>())
.field(&const_hex::encode(&self.$inner).as_str())
.finish()
}
}
};
}
#[derive(Clone, PartialEq, Eq)]
pub struct HpkeKeyPair {
inner: SmallVec<u8, 240>,
split_offset: u8,
}
impl fmt::Debug for HpkeKeyPair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(core::any::type_name::<Self>())
.field("sk", &self.sk())
.field("pk", &self.pk())
.finish()
}
}
impl HpkeKeyPair {
#[inline]
pub fn new_unchecked(
alg: HpkeKemId,
sk: impl AsRef<[u8]>,
pk: impl AsRef<[u8]>,
) -> Result<Self, CryptoError> {
if sk.as_ref().len() != alg.n_sk() {
return Err(CryptoError::KemMalformedSkX);
}
if pk.as_ref().len() != alg.n_pk() {
return Err(CryptoError::KemMalformedPkX);
}
let mut inner = SmallVec::new();
inner.extend_from_slice(sk.as_ref());
inner.extend_from_slice(pk.as_ref());
#[allow(
clippy::cast_possible_truncation,
reason = "`Nsk` must be less than 256 for all defined KEMs"
)]
Ok(Self {
inner,
split_offset: sk.as_ref().len() as u8,
})
}
#[inline]
pub fn sk(&self) -> HpkePrivateKeyRef<'_> {
HpkePrivateKeyRef::from_inner(&self.inner[..self.split_offset as usize])
}
#[inline]
pub fn pk(&self) -> HpkePublicKeyRef<'_> {
HpkePublicKeyRef::from_inner(&self.inner[self.split_offset as usize..])
}
}
wrapper_lite::wrapper!(
#[wrapper_impl(AsRef<[u8]>)]
#[wrapper_impl(Deref<[u8]>)]
#[derive(Clone, PartialEq, Eq)]
pub struct HpkePublicKey(SmallVec<u8, 184>);
);
debug_hex!(HpkePublicKey, inner);
impl HpkePublicKey {
#[inline]
pub fn new(alg: HpkeKemId, bytes: &[u8]) -> Result<Self, CryptoError> {
if bytes.len() != alg.n_pk() {
return Err(CryptoError::KemMalformedPkX);
}
Ok(Self {
inner: SmallVec::from(bytes),
})
}
}
wrapper_lite::wrapper!(
#[wrapper_impl(AsRef<[u8]>)]
#[wrapper_impl(Deref<[u8]>)]
#[wrapper_impl(From)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct HpkePublicKeyRef<'a>(&'a [u8]);
);
debug_hex!(HpkePublicKeyRef<'_>, inner);
impl<'a> From<&'a HpkePublicKey> for HpkePublicKeyRef<'a> {
fn from(value: &'a HpkePublicKey) -> Self {
Self::from_inner(&value.inner)
}
}
impl HpkePublicKeyRef<'_> {
#[inline]
pub fn to_owned(&self) -> HpkePublicKey {
HpkePublicKey {
inner: SmallVec::from(self.inner),
}
}
}
wrapper_lite::wrapper!(
#[wrapper_impl(AsRef<[u8]>)]
#[wrapper_impl(Deref<[u8]>)]
#[derive(Eq)]
#[derive(zeroize_derive::ZeroizeOnDrop)]
#[cfg_attr(feature = "hazmat", derive(Clone))]
pub struct HpkePrivateKey(SmallVec<u8, 120>);
);
impl PartialEq for HpkePrivateKey {
fn eq(&self, other: &Self) -> bool {
self.inner
.ct_eq(other.inner.as_ref())
.into()
}
}
impl fmt::Debug for HpkePrivateKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(feature = "hazmat")]
{
f.debug_tuple(core::any::type_name::<Self>())
.field(&const_hex::encode(self.inner.as_ref()).as_str())
.finish()
}
#[cfg(not(feature = "hazmat"))]
{
f.debug_tuple(core::any::type_name::<Self>())
.finish_non_exhaustive()
}
}
}
impl HpkePrivateKey {
#[inline]
pub fn new(alg: HpkeKemId, bytes: &[u8]) -> Result<Self, CryptoError> {
if bytes.len() != alg.n_sk() {
return Err(CryptoError::KemMalformedSkX);
}
Ok(Self {
inner: SmallVec::from(bytes),
})
}
}
wrapper_lite::wrapper!(
#[wrapper_impl(AsRef<[u8]>)]
#[wrapper_impl(Deref<[u8]>)]
#[wrapper_impl(From)]
#[derive(Clone, Copy, Eq)]
pub struct HpkePrivateKeyRef<'a>(&'a [u8]);
);
impl fmt::Debug for HpkePrivateKeyRef<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(feature = "hazmat")]
{
f.debug_tuple(core::any::type_name::<Self>())
.field(&const_hex::encode(self.inner).as_str())
.finish()
}
#[cfg(not(feature = "hazmat"))]
{
f.debug_tuple(core::any::type_name::<Self>())
.finish_non_exhaustive()
}
}
}
impl PartialEq for HpkePrivateKeyRef<'_> {
fn eq(&self, other: &Self) -> bool {
self.inner
.ct_eq(other.inner.as_ref())
.into()
}
}
impl<'a> From<&'a HpkePrivateKey> for HpkePrivateKeyRef<'a> {
fn from(value: &'a HpkePrivateKey) -> Self {
Self::from_inner(&value.inner)
}
}
wrapper_lite::wrapper!(
#[wrapper_impl(AsRef<[u8]>)]
#[wrapper_impl(Deref<[u8]>)]
#[wrapper_impl(From)]
#[derive(Clone, PartialEq, Eq)]
pub struct SharedSecret(SmallVec<u8, 56>);
);
debug_hex!(SharedSecret, inner);
impl SharedSecret {
#[inline]
pub fn new(bytes: &[u8]) -> Self {
Self {
inner: SmallVec::from(bytes),
}
}
#[inline]
pub fn from_okm(okm: Okm) -> Self {
Self { inner: okm.inner }
}
}
wrapper_lite::wrapper!(
#[wrapper_impl(AsRef<[u8]>)]
#[wrapper_impl(Deref<[u8]>)]
#[wrapper_impl(From)]
#[derive(Clone, PartialEq, Eq)]
pub struct SharedSecretRef<'a>(&'a [u8]);
);
debug_hex!(SharedSecretRef<'_>, inner);
impl<'a> From<&'a SharedSecret> for SharedSecretRef<'a> {
fn from(value: &'a SharedSecret) -> Self {
Self::from_inner(&value.inner)
}
}
wrapper_lite::wrapper!(
#[wrapper_impl(AsRef<[u8]>)]
#[wrapper_impl(Deref<[u8]>)]
#[wrapper_impl(From)]
#[derive(Clone, PartialEq, Eq)]
pub struct EncapsulatedSecret(SmallVec<u8, 184>);
);
debug_hex!(EncapsulatedSecret, inner);
impl EncapsulatedSecret {
#[inline]
pub fn new(bytes: &[u8]) -> Self {
Self {
inner: SmallVec::from(bytes),
}
}
pub fn new_from_pk_e(pk: HpkePublicKey) -> Self {
Self { inner: pk.inner }
}
}
wrapper_lite::wrapper!(
#[wrapper_impl(AsRef<[u8]>)]
#[wrapper_impl(Deref<[u8]>)]
#[wrapper_impl(From)]
#[derive(Clone, PartialEq, Eq)]
pub struct EncapsulatedSecretRef<'a>(&'a [u8]);
);
debug_hex!(EncapsulatedSecretRef<'_>, inner);
impl<'a> From<&'a EncapsulatedSecret> for EncapsulatedSecretRef<'a> {
fn from(value: &'a EncapsulatedSecret) -> Self {
Self::from_inner(&value.inner)
}
}
wrapper_lite::wrapper!(
#[wrapper_impl(AsRef<[u8]>)]
#[wrapper_impl(Deref<[u8]>)]
#[wrapper_impl(From)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct IkmRef<'a>(&'a [u8]);
);
debug_hex!(IkmRef<'_>, inner);
wrapper_lite::wrapper!(
#[wrapper_impl(AsRef<[u8]>)]
#[wrapper_impl(Deref<[u8]>)]
#[derive(Clone, PartialEq, Eq)]
pub struct Prk(SmallVec<u8, 64>);
);
debug_hex!(Prk, inner);
impl Prk {
#[inline]
pub fn new_less_safe(bytes: &[u8]) -> Self {
Self {
inner: SmallVec::from(bytes),
}
}
}
wrapper_lite::wrapper!(
#[wrapper_impl(AsRef<[u8]>)]
#[wrapper_impl(Deref<[u8]>)]
#[wrapper_impl(From)]
#[derive(Clone, PartialEq, Eq)]
pub struct PrkRef<'a>(&'a [u8]);
);
debug_hex!(PrkRef<'_>, inner);
impl<'a> From<&'a Prk> for PrkRef<'a> {
fn from(value: &'a Prk) -> Self {
Self::from_inner(&value.inner)
}
}
wrapper_lite::wrapper!(
#[wrapper_impl(AsRef<[u8]>)]
#[wrapper_impl(Deref<[u8]>)]
#[derive(Clone, PartialEq, Eq)]
pub struct Okm(SmallVec<u8, 56>);
);
debug_hex!(Okm, inner);
impl Okm {
#[inline]
pub const fn empty() -> Self {
Self {
inner: SmallVec::new(),
}
}
pub fn truncate(&mut self, len: usize) {
self.inner.truncate(len);
}
pub fn as_mut_buffer(&mut self, len: usize) -> &mut [u8] {
self.inner.resize(len, 0);
&mut self.inner
}
}
#[non_exhaustive]
#[derive(Debug)]
#[derive(zeroize_derive::ZeroizeOnDrop)]
#[cfg_attr(feature = "hazmat", derive(PartialEq, Eq, Clone))]
pub enum HpkeAead {
Aes128Gcm {
key: [u8; HpkeAeadId::AES_128_GCM.n_key()],
nonce: [u8; HpkeAeadId::AES_128_GCM.n_nonce()],
},
Aes256Gcm {
key: [u8; HpkeAeadId::AES_256_GCM.n_key()],
nonce: [u8; HpkeAeadId::AES_256_GCM.n_nonce()],
},
ChaCha20Poly1305 {
key: [u8; HpkeAeadId::CHACHA20_POLY1305.n_key()],
nonce: [u8; HpkeAeadId::CHACHA20_POLY1305.n_nonce()],
},
}
impl HpkeAead {
#[inline]
pub const fn aead_id(&self) -> HpkeAeadId {
match self {
Self::Aes128Gcm { .. } => HpkeAeadId::AES_128_GCM,
Self::Aes256Gcm { .. } => HpkeAeadId::AES_256_GCM,
Self::ChaCha20Poly1305 { .. } => HpkeAeadId::CHACHA20_POLY1305,
}
}
#[allow(clippy::return_self_not_must_use)]
pub fn copied_updating_nonce<F>(&self, update_nonce_f: F) -> Self
where
F: FnOnce(&mut [u8]),
{
match self {
Self::Aes128Gcm { key, nonce } => {
let mut nonce = *nonce;
update_nonce_f(&mut nonce);
Self::Aes128Gcm { key: *key, nonce }
}
Self::Aes256Gcm { key, nonce } => {
let mut nonce = *nonce;
update_nonce_f(&mut nonce);
Self::Aes256Gcm { key: *key, nonce }
}
Self::ChaCha20Poly1305 { key, nonce } => {
let mut nonce = *nonce;
update_nonce_f(&mut nonce);
Self::ChaCha20Poly1305 { key: *key, nonce }
}
}
}
#[inline]
pub const fn key(&self) -> &[u8] {
match self {
Self::Aes128Gcm { key, .. } => key,
Self::Aes256Gcm { key, .. } => key,
Self::ChaCha20Poly1305 { key, .. } => key,
}
}
#[inline]
pub const fn nonce(&self) -> &[u8] {
match self {
Self::Aes128Gcm { nonce, .. } => nonce,
Self::Aes256Gcm { nonce, .. } => nonce,
Self::ChaCha20Poly1305 { nonce, .. } => nonce,
}
}
}
impl zeroize::Zeroize for HpkeAead {
fn zeroize(&mut self) {
match self {
Self::Aes128Gcm { key, .. } => {
key.zeroize();
}
Self::Aes256Gcm { key, .. } => {
key.zeroize();
}
Self::ChaCha20Poly1305 { key, .. } => {
key.zeroize();
}
}
}
}