1#![doc = include_str!("../README.md")]
2#![no_std]
3#![allow(clippy::match_same_arms)]
4#![allow(clippy::must_use_candidate)]
5
6#[cfg(feature = "_backend")]
7pub mod backend;
8pub mod kdf;
9
10extern crate alloc;
11
12#[cfg(feature = "std")]
13extern crate std;
14
15use alloc::boxed::Box;
16use alloc::vec::Vec;
17use core::fmt;
18use core::ops::DerefMut;
19
20use smallvec::SmallVec;
21use subtle::ConstantTimeEq;
22
23pub trait Crypto: fmt::Debug + Send + Sync {
25 fn secure_random_fill(&mut self, buf: &mut [u8]) -> Result<(), CryptoError>;
31
32 fn is_kem_supported(&self, alg: &HpkeKemId) -> bool;
35
36 fn kem_generate_key_pair(&mut self, alg: HpkeKemId) -> Result<HpkeKeyPair, CryptoError>;
50
51 fn kem_encap(
67 &self,
68 _alg: HpkeKemId,
69 _pk_r: HpkePublicKeyRef<'_>,
70 ) -> Result<(SharedSecret, EncapsulatedSecret), CryptoError> {
71 Err(CryptoError::KemOpUnsupported)
72 }
73
74 fn kem_decap(
89 &self,
90 _alg: HpkeKemId,
91 _enc: EncapsulatedSecret,
92 _sk_r: HpkePrivateKeyRef<'_>,
93 ) -> Result<SharedSecret, CryptoError> {
94 Err(CryptoError::KemOpUnsupported)
95 }
96
97 fn is_kdf_supported(&self, alg: &HpkeKdfId) -> bool;
100
101 fn kdf_extract(&self, alg: HpkeKdfId, salt: &[u8], ikm: IkmRef<'_>)
113 -> Result<Prk, CryptoError>;
114
115 fn kdf_extract_concated(
129 &self,
130 alg: HpkeKdfId,
131 salt: &[u8],
132 ikms: &[IkmRef<'_>],
133 ) -> Result<Prk, CryptoError> {
134 let mut concated = Vec::new();
135
136 for ikm in ikms {
137 concated.extend_from_slice(ikm);
138 }
139
140 self.kdf_extract(alg, salt, IkmRef::from(&concated))
141 }
142
143 fn kdf_expand(
154 &self,
155 alg: HpkeKdfId,
156 prk: PrkRef<'_>,
157 info: &[u8],
158 l: usize,
159 ) -> Result<Okm, CryptoError>;
160
161 fn kdf_expand_multi_info(
175 &self,
176 alg: HpkeKdfId,
177 prk: PrkRef<'_>,
178 infos: &[&[u8]],
179 l: usize,
180 ) -> Result<Okm, CryptoError> {
181 self.kdf_expand(alg, prk, &infos.concat(), l)
182 }
183
184 fn is_aead_supported(&self, alg: &HpkeAeadId) -> bool;
187
188 fn aead_seal(
203 &self,
204 crypto_info: &HpkeAead,
205 aad: &[u8],
206 plaintext: &[u8],
207 ) -> Result<Vec<u8>, CryptoError> {
208 let mut buffer = plaintext.to_vec();
209 self.aead_seal_in_place(crypto_info, aad, &mut buffer)?;
210 Ok(buffer)
211 }
212
213 fn aead_seal_in_place(
229 &self,
230 crypto_info: &HpkeAead,
231 aad: &[u8],
232 buffer: &mut Vec<u8>,
233 ) -> Result<(), CryptoError>;
234
235 fn aead_open(
250 &self,
251 crypto_info: &HpkeAead,
252 aad: &[u8],
253 ciphertext: &[u8],
254 ) -> Result<Vec<u8>, CryptoError> {
255 let mut buffer = ciphertext.to_vec();
256 self.aead_open_in_place(crypto_info, aad, &mut buffer)?;
257 Ok(buffer)
258 }
259
260 fn aead_open_in_place(
278 &self,
279 crypto_info: &HpkeAead,
280 aad: &[u8],
281 buffer: &mut Vec<u8>,
282 ) -> Result<(), CryptoError>;
283
284 fn sk(&self, alg: HpkeKemId, sk: &[u8]) -> Result<HpkePrivateKey, CryptoError>;
292
293 fn pk(&self, alg: HpkeKemId, sk: HpkePrivateKeyRef<'_>) -> Result<HpkePublicKey, CryptoError>;
306
307 fn dh(
321 &self,
322 alg: HpkeKemId,
323 sk_x: HpkePrivateKeyRef<'_>,
324 pk_y: HpkePublicKeyRef<'_>,
325 ) -> Result<SharedSecret, CryptoError>;
326}
327
328impl<T> Crypto for T
329where
330 T: DerefMut<Target = dyn Crypto> + fmt::Debug + Send + Sync + ?Sized,
331{
332 fn secure_random_fill(&mut self, buf: &mut [u8]) -> Result<(), CryptoError> {
333 (**self).secure_random_fill(buf)
334 }
335
336 fn is_kem_supported(&self, alg: &HpkeKemId) -> bool {
337 (**self).is_kem_supported(alg)
338 }
339
340 fn kem_generate_key_pair(&mut self, alg: HpkeKemId) -> Result<HpkeKeyPair, CryptoError> {
341 (**self).kem_generate_key_pair(alg)
342 }
343
344 fn kem_encap(
345 &self,
346 alg: HpkeKemId,
347 pk_r: HpkePublicKeyRef<'_>,
348 ) -> Result<(SharedSecret, EncapsulatedSecret), CryptoError> {
349 (**self).kem_encap(alg, pk_r)
350 }
351
352 fn kem_decap(
353 &self,
354 alg: HpkeKemId,
355 enc: EncapsulatedSecret,
356 sk_r: HpkePrivateKeyRef<'_>,
357 ) -> Result<SharedSecret, CryptoError> {
358 (**self).kem_decap(alg, enc, sk_r)
359 }
360
361 fn is_kdf_supported(&self, alg: &HpkeKdfId) -> bool {
362 (**self).is_kdf_supported(alg)
363 }
364
365 fn kdf_extract(
366 &self,
367 alg: HpkeKdfId,
368 salt: &[u8],
369 ikm: IkmRef<'_>,
370 ) -> Result<Prk, CryptoError> {
371 (**self).kdf_extract(alg, salt, ikm)
372 }
373
374 fn kdf_extract_concated(
375 &self,
376 alg: HpkeKdfId,
377 salt: &[u8],
378 ikms: &[IkmRef<'_>],
379 ) -> Result<Prk, CryptoError> {
380 (**self).kdf_extract_concated(alg, salt, ikms)
381 }
382
383 fn kdf_expand(
384 &self,
385 alg: HpkeKdfId,
386 prk: PrkRef<'_>,
387 info: &[u8],
388 l: usize,
389 ) -> Result<Okm, CryptoError> {
390 (**self).kdf_expand(alg, prk, info, l)
391 }
392
393 fn kdf_expand_multi_info(
394 &self,
395 alg: HpkeKdfId,
396 prk: PrkRef<'_>,
397 infos: &[&[u8]],
398 l: usize,
399 ) -> Result<Okm, CryptoError> {
400 (**self).kdf_expand_multi_info(alg, prk, infos, l)
401 }
402
403 fn is_aead_supported(&self, alg: &HpkeAeadId) -> bool {
404 (**self).is_aead_supported(alg)
405 }
406
407 fn aead_seal(
408 &self,
409 crypto_info: &HpkeAead,
410 aad: &[u8],
411 plaintext: &[u8],
412 ) -> Result<Vec<u8>, CryptoError> {
413 (**self).aead_seal(crypto_info, aad, plaintext)
414 }
415
416 fn aead_seal_in_place(
417 &self,
418 crypto_info: &HpkeAead,
419 aad: &[u8],
420 buffer: &mut Vec<u8>,
421 ) -> Result<(), CryptoError> {
422 (**self).aead_seal_in_place(crypto_info, aad, buffer)
423 }
424
425 fn aead_open(
426 &self,
427 crypto_info: &HpkeAead,
428 aad: &[u8],
429 ciphertext: &[u8],
430 ) -> Result<Vec<u8>, CryptoError> {
431 (**self).aead_open(crypto_info, aad, ciphertext)
432 }
433
434 fn aead_open_in_place(
435 &self,
436 crypto_info: &HpkeAead,
437 aad: &[u8],
438 buffer: &mut Vec<u8>,
439 ) -> Result<(), CryptoError> {
440 (**self).aead_open_in_place(crypto_info, aad, buffer)
441 }
442
443 fn sk(&self, alg: HpkeKemId, sk: &[u8]) -> Result<HpkePrivateKey, CryptoError> {
444 (**self).sk(alg, sk)
445 }
446
447 fn pk(&self, alg: HpkeKemId, sk: HpkePrivateKeyRef<'_>) -> Result<HpkePublicKey, CryptoError> {
448 (**self).pk(alg, sk)
449 }
450
451 fn dh(
452 &self,
453 alg: HpkeKemId,
454 sk_x: HpkePrivateKeyRef<'_>,
455 pk_y: HpkePublicKeyRef<'_>,
456 ) -> Result<SharedSecret, CryptoError> {
457 (**self).dh(alg, sk_x, pk_y)
458 }
459}
460
461#[derive(Debug)]
462pub enum CryptoError {
464 KdfExpandInvalidPrkLen,
470
471 KdfExpandInvalidOutputLen,
478
479 KdfUnsupported,
481
482 KemDeriveKeyPair,
489
490 KemMalformedSkX,
492
493 KemMalformedPkX,
495
496 KemOpUnsupported,
501
502 KemUnsupported,
504
505 AeadInvalidKey,
507
508 AeadInvalidNonce,
510
511 AeadInvalidCt,
517
518 AeadSeal,
520
521 AeadOpen,
523
524 AeadUnsupported,
526
527 InsufficientRandomness,
531
532 Unspecified,
534
535 Custom(Box<dyn core::error::Error + Send + Sync + 'static>),
537}
538
539impl core::error::Error for CryptoError {
540 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
541 match self {
542 Self::Custom(e) => Some(&**e),
543 _ => None,
544 }
545 }
546}
547
548impl fmt::Display for CryptoError {
549 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
550 match self {
551 Self::KdfExpandInvalidPrkLen => write!(f, "KDF expand: invalid PRK length"),
552 Self::KdfExpandInvalidOutputLen => write!(f, "KDF expand: invalid output length"),
553 Self::KdfUnsupported => write!(f, "KDF unsupported"),
554 Self::KemDeriveKeyPair => write!(f, "KEM derive key pair failed"),
555 Self::KemMalformedSkX => write!(f, "KEM malformed private key"),
556 Self::KemMalformedPkX => write!(f, "KEM malformed public key"),
557 Self::KemOpUnsupported => write!(f, "KEM operation unsupported"),
558 Self::KemUnsupported => write!(f, "KEM unsupported"),
559 Self::AeadInvalidKey => write!(f, "AEAD invalid key"),
560 Self::AeadInvalidNonce => write!(f, "AEAD invalid nonce"),
561 Self::AeadInvalidCt => write!(f, "AEAD invalid cipher text"),
562 Self::AeadSeal => write!(f, "AEAD seal error"),
563 Self::AeadOpen => write!(f, "AEAD open error"),
564 Self::AeadUnsupported => write!(f, "AEAD unsupported"),
565 Self::InsufficientRandomness => write!(f, "Insufficient randomness"),
566 Self::Unspecified => write!(f, "Unspecified crypto error"),
567 Self::Custom(e) => write!(f, "Crypto library error: {e}"),
568 }
569 }
570}
571
572#[derive(Debug, Clone, Copy)]
575pub struct HpkeCipherSuite {
578 pub kem_id: HpkeKemId,
580
581 pub kdf_id: HpkeKdfId,
583
584 pub aead_id: HpkeAeadId,
586}
587
588impl HpkeCipherSuite {
589 pub fn suite_id(&self) -> [u8; 10] {
628 let mut suite_id = [0u8; 10];
629
630 suite_id[0..4].copy_from_slice(b"HPKE");
631 suite_id[4..6].copy_from_slice(&self.kem_id.to_int().to_be_bytes());
632 suite_id[6..8].copy_from_slice(&self.kdf_id.to_int().to_be_bytes());
633 suite_id[8..10].copy_from_slice(&self.aead_id.to_int().to_be_bytes());
634
635 suite_id
636 }
637}
638
639macro_rules! enum_builder {
640 (
641 type Error = $error:ident;
642 #[repr($uint:ty)]
643 $(#[$enum_meta:meta])*
644 $vis:vis enum $name:ident
645 {
646 $(
647 $(#[doc = $registry_comment:literal])*
648 $registry_name:ident = $registry_value:literal
649 ),+
650 $(,)?
651 }
652 ) => {
653 #[non_exhaustive]
654 #[allow(clippy::upper_case_acronyms)]
655 #[allow(non_camel_case_types)]
656 #[derive(PartialEq, Eq, Clone, Copy)]
657 #[repr($uint)]
658 $(#[$enum_meta])*
659 $vis enum $name {
660 $(
661 $(#[doc = $registry_comment])*
662 $registry_name = $registry_value,
663 )+
664 }
665
666 impl $name {
667 #[inline]
668 #[allow(unused)]
669 #[allow(clippy::missing_errors_doc)]
670 $vis const fn try_from_int(x: $uint) -> Result<Self, $error> {
672 match x {
673 $(
674 $registry_value => Ok(Self::$registry_name),
675 )+
676 _ => Err($error (x)),
677 }
678 }
679
680 #[inline]
681 #[allow(unused)]
682 $vis const fn to_int(self) -> $uint {
684 self as $uint
685 }
686
687 #[inline]
688 #[allow(unused)]
689 $vis const fn to_array(self) -> [u8; core::mem::size_of::<$uint>()] {
691 self.to_int().to_be_bytes()
692 }
693
694 #[inline]
695 #[allow(unused)]
696 $vis const fn as_str(&self) -> &'static str {
698 match self {
699 $(
700 Self::$registry_name => stringify!($registry_name),
701 )+
702 }
703 }
704 }
705
706 impl From<$name> for $uint {
707 fn from(value: $name) -> Self {
708 value.to_int()
709 }
710 }
711
712 impl TryFrom<$uint> for $name {
713 type Error = $error;
714
715 fn try_from(x: $uint) -> Result<Self, Self::Error> {
716 Self::try_from_int(x)
717 }
718 }
719
720 impl ::core::fmt::Debug for $name {
721 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
722 match self {
723 $(
724 Self::$registry_name => f.write_str(stringify!($registry_name)),
725 )+
726 }
727 }
728 }
729
730 impl ::core::fmt::Display for $name {
731 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
732 write!(f, "{:?}", self)
733 }
734 }
735
736 #[cfg(feature = "serde")]
737 impl serde::Serialize for $name {
738 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
739 where
740 S: serde::Serializer,
741 {
742 self.to_int().serialize(serializer)
743 }
744 }
745
746 #[cfg(feature = "serde")]
747 impl<'a> serde::Deserialize<'a> for $name {
748 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
749 where
750 D: serde::Deserializer<'a>,
751 {
752 let v = <$uint>::deserialize(deserializer)?;
753
754 Self::try_from(v).map_err(serde::de::Error::custom)
755 }
756 }
757 };
758}
759
760#[derive(Debug, Clone, Copy)]
761pub struct UnknownHpkeKemId(pub u16);
763
764impl fmt::Display for UnknownHpkeKemId {
765 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
766 write!(f, "Unknown HPKE KEM ID: {}", self.0)
767 }
768}
769
770enum_builder!(
771 type Error = UnknownHpkeKemId;
772
773 #[repr(u16)]
774 pub enum HpkeKemId {
780 DHKEM_P256_HKDF_SHA256 = 0x0010,
782
783 DHKEM_P384_HKDF_SHA384 = 0x0011,
785
786 DHKEM_P521_HKDF_SHA512 = 0x0012,
788
789 DHKEM_X25519_HKDF_SHA256 = 0x0020,
791
792 DHKEM_X448_HKDF_SHA512 = 0x0021,
794 }
795);
796
797impl HpkeKemId {
798 #[inline]
799 pub const fn kdf_id(&self) -> HpkeKdfId {
803 match self {
804 Self::DHKEM_P256_HKDF_SHA256 | Self::DHKEM_X25519_HKDF_SHA256 => HpkeKdfId::HKDF_SHA256,
805 Self::DHKEM_P384_HKDF_SHA384 => HpkeKdfId::HKDF_SHA384,
806 Self::DHKEM_P521_HKDF_SHA512 | Self::DHKEM_X448_HKDF_SHA512 => HpkeKdfId::HKDF_SHA512,
807 }
808 }
809
810 #[inline]
811 pub fn suite_id(&self) -> [u8; 5] {
839 let mut suite_id = [0u8; 5];
840
841 suite_id[0..3].copy_from_slice(b"KEM");
842 suite_id[3..5].copy_from_slice(&self.to_int().to_be_bytes());
843
844 suite_id
845 }
846
847 #[inline]
848 pub const fn n_secret(&self) -> usize {
855 match self {
856 Self::DHKEM_P256_HKDF_SHA256 => 32,
857 Self::DHKEM_P384_HKDF_SHA384 => 48,
858 Self::DHKEM_P521_HKDF_SHA512 => 64,
859 Self::DHKEM_X25519_HKDF_SHA256 => 32,
860 Self::DHKEM_X448_HKDF_SHA512 => 64,
861 }
862 }
863
864 #[inline]
865 pub const fn n_enc(&self) -> usize {
872 match self {
873 Self::DHKEM_P256_HKDF_SHA256 => 65,
874 Self::DHKEM_P384_HKDF_SHA384 => 97,
875 Self::DHKEM_P521_HKDF_SHA512 => 133,
876 Self::DHKEM_X25519_HKDF_SHA256 => 32,
877 Self::DHKEM_X448_HKDF_SHA512 => 56,
878 }
879 }
880
881 #[inline]
882 pub const fn n_pk(&self) -> usize {
889 match self {
890 Self::DHKEM_P256_HKDF_SHA256 => 65,
891 Self::DHKEM_P384_HKDF_SHA384 => 97,
892 Self::DHKEM_P521_HKDF_SHA512 => 133,
893 Self::DHKEM_X25519_HKDF_SHA256 => 32,
894 Self::DHKEM_X448_HKDF_SHA512 => 56,
895 }
896 }
897
898 #[inline]
899 pub const fn n_sk(&self) -> usize {
906 match self {
907 Self::DHKEM_P256_HKDF_SHA256 => 32,
908 Self::DHKEM_P384_HKDF_SHA384 => 48,
909 Self::DHKEM_P521_HKDF_SHA512 => 66,
910 Self::DHKEM_X25519_HKDF_SHA256 => 32,
911 Self::DHKEM_X448_HKDF_SHA512 => 56,
912 }
913 }
914}
915
916#[derive(Debug, Clone, Copy)]
917pub struct UnknownHpkeKdfId(pub u16);
919
920impl fmt::Display for UnknownHpkeKdfId {
921 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
922 write!(f, "Unknown HPKE KDF ID: {}", self.0)
923 }
924}
925
926enum_builder!(
927 type Error = UnknownHpkeKdfId;
928
929 #[repr(u16)]
930 pub enum HpkeKdfId {
936 HKDF_SHA256 = 0x0001,
938
939 HKDF_SHA384 = 0x0002,
941
942 HKDF_SHA512 = 0x0003,
944 }
945);
946
947impl HpkeKdfId {
948 #[inline]
949 pub const fn n_hash(&self) -> usize {
951 match self {
952 Self::HKDF_SHA256 => 32,
953 Self::HKDF_SHA384 => 48,
954 Self::HKDF_SHA512 => 64,
955 }
956 }
957}
958
959impl From<HpkeKemId> for HpkeKdfId {
960 #[inline]
961 fn from(kem: HpkeKemId) -> Self {
962 match kem {
963 HpkeKemId::DHKEM_P256_HKDF_SHA256 | HpkeKemId::DHKEM_X25519_HKDF_SHA256 => {
964 Self::HKDF_SHA256
965 }
966 HpkeKemId::DHKEM_P384_HKDF_SHA384 => Self::HKDF_SHA384,
967 HpkeKemId::DHKEM_P521_HKDF_SHA512 | HpkeKemId::DHKEM_X448_HKDF_SHA512 => {
968 Self::HKDF_SHA512
969 }
970 }
971 }
972}
973
974#[derive(Debug, Clone, Copy)]
975pub struct UnknownAeadAlgorithm(pub u16);
977
978impl fmt::Display for UnknownAeadAlgorithm {
979 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
980 write!(f, "Unknown HPKE AEAD ID: {}", self.0)
981 }
982}
983
984enum_builder!(
985 type Error = UnknownAeadAlgorithm;
986
987 #[repr(u16)]
988 pub enum HpkeAeadId {
995 AES_128_GCM = 0x0001,
997
998 AES_256_GCM = 0x0002,
1000
1001 CHACHA20_POLY1305 = 0x0003,
1003
1004 EXPORT_ONLY = 0xFFFF,
1006 }
1007);
1008
1009impl HpkeAeadId {
1010 #[inline]
1011 pub const fn n_key(&self) -> usize {
1013 match self {
1014 Self::AES_128_GCM => 16,
1015 Self::AES_256_GCM => 32,
1016 Self::CHACHA20_POLY1305 => 32,
1017 Self::EXPORT_ONLY => 0,
1018 }
1019 }
1020
1021 #[inline]
1022 pub const fn n_nonce(&self) -> usize {
1024 match self {
1025 Self::AES_128_GCM => 12,
1026 Self::AES_256_GCM => 12,
1027 Self::CHACHA20_POLY1305 => 12,
1028 Self::EXPORT_ONLY => 0,
1029 }
1030 }
1031
1032 #[inline]
1033 pub const fn n_tag(&self) -> usize {
1036 match self {
1037 Self::AES_128_GCM => 16,
1038 Self::AES_256_GCM => 16,
1039 Self::CHACHA20_POLY1305 => 16,
1040 Self::EXPORT_ONLY => 0,
1041 }
1042 }
1043
1044 #[inline]
1045 pub fn new_crypto_info(
1053 &self,
1054 key: &[u8],
1055 nonce: &[u8],
1056 ) -> Result<Option<HpkeAead>, CryptoError> {
1057 Ok(match self {
1058 Self::AES_128_GCM => Some(HpkeAead::Aes128Gcm {
1059 key: key
1060 .try_into()
1061 .map_err(|_| CryptoError::AeadInvalidKey)?,
1062 nonce: nonce
1063 .try_into()
1064 .map_err(|_| CryptoError::AeadInvalidNonce)?,
1065 }),
1066 Self::AES_256_GCM => Some(HpkeAead::Aes256Gcm {
1067 key: key
1068 .try_into()
1069 .map_err(|_| CryptoError::AeadInvalidKey)?,
1070 nonce: nonce
1071 .try_into()
1072 .map_err(|_| CryptoError::AeadInvalidNonce)?,
1073 }),
1074 Self::CHACHA20_POLY1305 => Some(HpkeAead::ChaCha20Poly1305 {
1075 key: key
1076 .try_into()
1077 .map_err(|_| CryptoError::AeadInvalidKey)?,
1078 nonce: nonce
1079 .try_into()
1080 .map_err(|_| CryptoError::AeadInvalidNonce)?,
1081 }),
1082 Self::EXPORT_ONLY => None,
1083 })
1084 }
1085}
1086
1087macro_rules! debug_hex {
1088 ($name:ty, $inner:ident) => {
1089 impl fmt::Debug for $name {
1090 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1091 f.debug_tuple(core::any::type_name::<Self>())
1092 .field(&const_hex::encode(&self.$inner).as_str())
1093 .finish()
1094 }
1095 }
1096 };
1097}
1098
1099#[derive(Clone, PartialEq, Eq)]
1100pub struct HpkeKeyPair {
1102 inner: SmallVec<u8, 240>,
1103 split_offset: u8,
1104}
1105
1106impl fmt::Debug for HpkeKeyPair {
1107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1108 f.debug_struct(core::any::type_name::<Self>())
1109 .field("sk", &self.sk())
1110 .field("pk", &self.pk())
1111 .finish()
1112 }
1113}
1114
1115impl HpkeKeyPair {
1116 #[inline]
1117 pub fn new_unchecked(
1128 alg: HpkeKemId,
1129 sk: impl AsRef<[u8]>,
1130 pk: impl AsRef<[u8]>,
1131 ) -> Result<Self, CryptoError> {
1132 if sk.as_ref().len() != alg.n_sk() {
1133 return Err(CryptoError::KemMalformedSkX);
1134 }
1135
1136 if pk.as_ref().len() != alg.n_pk() {
1137 return Err(CryptoError::KemMalformedPkX);
1138 }
1139
1140 let mut inner = SmallVec::new();
1141 inner.extend_from_slice(sk.as_ref());
1142 inner.extend_from_slice(pk.as_ref());
1143
1144 #[allow(
1145 clippy::cast_possible_truncation,
1146 reason = "`Nsk` must be less than 256 for all defined KEMs"
1147 )]
1148 Ok(Self {
1149 inner,
1150 split_offset: sk.as_ref().len() as u8,
1151 })
1152 }
1153
1154 #[inline]
1155 pub fn sk(&self) -> HpkePrivateKeyRef<'_> {
1157 HpkePrivateKeyRef::from_inner(&self.inner[..self.split_offset as usize])
1158 }
1159
1160 #[inline]
1161 pub fn pk(&self) -> HpkePublicKeyRef<'_> {
1163 HpkePublicKeyRef::from_inner(&self.inner[self.split_offset as usize..])
1164 }
1165}
1166
1167wrapper_lite::wrapper!(
1168 #[wrapper_impl(AsRef<[u8]>)]
1169 #[wrapper_impl(Deref<[u8]>)]
1170 #[derive(Clone, PartialEq, Eq)]
1171 pub struct HpkePublicKey(SmallVec<u8, 184>);
1175);
1176
1177debug_hex!(HpkePublicKey, inner);
1178
1179impl HpkePublicKey {
1180 #[inline]
1181 pub fn new(alg: HpkeKemId, bytes: &[u8]) -> Result<Self, CryptoError> {
1192 if bytes.len() != alg.n_pk() {
1193 return Err(CryptoError::KemMalformedPkX);
1194 }
1195
1196 Ok(Self {
1197 inner: SmallVec::from(bytes),
1198 })
1199 }
1200}
1201
1202wrapper_lite::wrapper!(
1203 #[wrapper_impl(AsRef<[u8]>)]
1204 #[wrapper_impl(Deref<[u8]>)]
1205 #[wrapper_impl(From)]
1206 #[derive(Clone, Copy, PartialEq, Eq)]
1207 pub struct HpkePublicKeyRef<'a>(&'a [u8]);
1209);
1210
1211debug_hex!(HpkePublicKeyRef<'_>, inner);
1212
1213impl<'a> From<&'a HpkePublicKey> for HpkePublicKeyRef<'a> {
1214 fn from(value: &'a HpkePublicKey) -> Self {
1215 Self::from_inner(&value.inner)
1216 }
1217}
1218
1219impl HpkePublicKeyRef<'_> {
1220 #[inline]
1221 pub fn to_owned(&self) -> HpkePublicKey {
1223 HpkePublicKey {
1224 inner: SmallVec::from(self.inner),
1225 }
1226 }
1227}
1228
1229wrapper_lite::wrapper!(
1230 #[wrapper_impl(AsRef<[u8]>)]
1231 #[wrapper_impl(Deref<[u8]>)]
1232 #[derive(Eq)]
1233 #[derive(zeroize_derive::ZeroizeOnDrop)]
1234 #[cfg_attr(feature = "hazmat", derive(Clone))]
1235 pub struct HpkePrivateKey(SmallVec<u8, 120>);
1239);
1240
1241impl PartialEq for HpkePrivateKey {
1242 fn eq(&self, other: &Self) -> bool {
1243 self.inner
1244 .ct_eq(other.inner.as_ref())
1245 .into()
1246 }
1247}
1248
1249impl fmt::Debug for HpkePrivateKey {
1250 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1251 #[cfg(feature = "hazmat")]
1252 {
1253 f.debug_tuple(core::any::type_name::<Self>())
1254 .field(&const_hex::encode(self.inner.as_ref()).as_str())
1255 .finish()
1256 }
1257
1258 #[cfg(not(feature = "hazmat"))]
1259 {
1260 f.debug_tuple(core::any::type_name::<Self>())
1261 .finish_non_exhaustive()
1262 }
1263 }
1264}
1265
1266impl HpkePrivateKey {
1267 #[inline]
1268 pub fn new(alg: HpkeKemId, bytes: &[u8]) -> Result<Self, CryptoError> {
1278 if bytes.len() != alg.n_sk() {
1279 return Err(CryptoError::KemMalformedSkX);
1280 }
1281
1282 Ok(Self {
1283 inner: SmallVec::from(bytes),
1284 })
1285 }
1286}
1287
1288wrapper_lite::wrapper!(
1289 #[wrapper_impl(AsRef<[u8]>)]
1290 #[wrapper_impl(Deref<[u8]>)]
1291 #[wrapper_impl(From)]
1292 #[derive(Clone, Copy, Eq)]
1293 pub struct HpkePrivateKeyRef<'a>(&'a [u8]);
1297);
1298
1299impl fmt::Debug for HpkePrivateKeyRef<'_> {
1300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1301 #[cfg(feature = "hazmat")]
1302 {
1303 f.debug_tuple(core::any::type_name::<Self>())
1304 .field(&const_hex::encode(self.inner).as_str())
1305 .finish()
1306 }
1307
1308 #[cfg(not(feature = "hazmat"))]
1309 {
1310 f.debug_tuple(core::any::type_name::<Self>())
1311 .finish_non_exhaustive()
1312 }
1313 }
1314}
1315
1316impl PartialEq for HpkePrivateKeyRef<'_> {
1317 fn eq(&self, other: &Self) -> bool {
1318 self.inner
1319 .ct_eq(other.inner.as_ref())
1320 .into()
1321 }
1322}
1323
1324impl<'a> From<&'a HpkePrivateKey> for HpkePrivateKeyRef<'a> {
1325 fn from(value: &'a HpkePrivateKey) -> Self {
1326 Self::from_inner(&value.inner)
1327 }
1328}
1329
1330wrapper_lite::wrapper!(
1331 #[wrapper_impl(AsRef<[u8]>)]
1332 #[wrapper_impl(Deref<[u8]>)]
1333 #[wrapper_impl(From)]
1334 #[derive(Clone, PartialEq, Eq)]
1335 pub struct SharedSecret(SmallVec<u8, 56>);
1337);
1338
1339debug_hex!(SharedSecret, inner);
1340
1341impl SharedSecret {
1342 #[inline]
1343 pub fn new(bytes: &[u8]) -> Self {
1345 Self {
1346 inner: SmallVec::from(bytes),
1347 }
1348 }
1349
1350 #[inline]
1351 pub fn from_okm(okm: Okm) -> Self {
1354 Self { inner: okm.inner }
1355 }
1356}
1357
1358wrapper_lite::wrapper!(
1359 #[wrapper_impl(AsRef<[u8]>)]
1360 #[wrapper_impl(Deref<[u8]>)]
1361 #[wrapper_impl(From)]
1362 #[derive(Clone, PartialEq, Eq)]
1363 pub struct SharedSecretRef<'a>(&'a [u8]);
1365);
1366
1367debug_hex!(SharedSecretRef<'_>, inner);
1368
1369impl<'a> From<&'a SharedSecret> for SharedSecretRef<'a> {
1370 fn from(value: &'a SharedSecret) -> Self {
1371 Self::from_inner(&value.inner)
1372 }
1373}
1374
1375wrapper_lite::wrapper!(
1376 #[wrapper_impl(AsRef<[u8]>)]
1377 #[wrapper_impl(Deref<[u8]>)]
1378 #[wrapper_impl(From)]
1379 #[derive(Clone, PartialEq, Eq)]
1380 pub struct EncapsulatedSecret(SmallVec<u8, 184>);
1382);
1383
1384debug_hex!(EncapsulatedSecret, inner);
1385
1386impl EncapsulatedSecret {
1387 #[inline]
1388 pub fn new(bytes: &[u8]) -> Self {
1391 Self {
1392 inner: SmallVec::from(bytes),
1393 }
1394 }
1395
1396 pub fn new_from_pk_e(pk: HpkePublicKey) -> Self {
1399 Self { inner: pk.inner }
1400 }
1401}
1402
1403wrapper_lite::wrapper!(
1404 #[wrapper_impl(AsRef<[u8]>)]
1405 #[wrapper_impl(Deref<[u8]>)]
1406 #[wrapper_impl(From)]
1407 #[derive(Clone, PartialEq, Eq)]
1408 pub struct EncapsulatedSecretRef<'a>(&'a [u8]);
1410);
1411
1412debug_hex!(EncapsulatedSecretRef<'_>, inner);
1413
1414impl<'a> From<&'a EncapsulatedSecret> for EncapsulatedSecretRef<'a> {
1415 fn from(value: &'a EncapsulatedSecret) -> Self {
1416 Self::from_inner(&value.inner)
1417 }
1418}
1419
1420wrapper_lite::wrapper!(
1421 #[wrapper_impl(AsRef<[u8]>)]
1422 #[wrapper_impl(Deref<[u8]>)]
1423 #[wrapper_impl(From)]
1424 #[derive(Clone, Copy, PartialEq, Eq)]
1425 pub struct IkmRef<'a>(&'a [u8]);
1427);
1428
1429debug_hex!(IkmRef<'_>, inner);
1430
1431wrapper_lite::wrapper!(
1432 #[wrapper_impl(AsRef<[u8]>)]
1433 #[wrapper_impl(Deref<[u8]>)]
1434 #[derive(Clone, PartialEq, Eq)]
1435 pub struct Prk(SmallVec<u8, 64>);
1437);
1438
1439debug_hex!(Prk, inner);
1440
1441impl Prk {
1442 #[inline]
1443 pub fn new_less_safe(bytes: &[u8]) -> Self {
1445 Self {
1446 inner: SmallVec::from(bytes),
1447 }
1448 }
1449}
1450
1451wrapper_lite::wrapper!(
1452 #[wrapper_impl(AsRef<[u8]>)]
1453 #[wrapper_impl(Deref<[u8]>)]
1454 #[wrapper_impl(From)]
1455 #[derive(Clone, PartialEq, Eq)]
1456 pub struct PrkRef<'a>(&'a [u8]);
1458);
1459
1460debug_hex!(PrkRef<'_>, inner);
1461
1462impl<'a> From<&'a Prk> for PrkRef<'a> {
1463 fn from(value: &'a Prk) -> Self {
1464 Self::from_inner(&value.inner)
1465 }
1466}
1467
1468wrapper_lite::wrapper!(
1469 #[wrapper_impl(AsRef<[u8]>)]
1470 #[wrapper_impl(Deref<[u8]>)]
1471 #[derive(Clone, PartialEq, Eq)]
1472 pub struct Okm(SmallVec<u8, 56>);
1474);
1475
1476debug_hex!(Okm, inner);
1477
1478impl Okm {
1479 #[inline]
1480 pub const fn empty() -> Self {
1485 Self {
1486 inner: SmallVec::new(),
1487 }
1488 }
1489
1490 pub fn truncate(&mut self, len: usize) {
1495 self.inner.truncate(len);
1496 }
1497
1498 pub fn as_mut_buffer(&mut self, len: usize) -> &mut [u8] {
1504 self.inner.resize(len, 0);
1505 &mut self.inner
1506 }
1507}
1508
1509#[non_exhaustive]
1510#[derive(Debug)]
1511#[derive(zeroize_derive::ZeroizeOnDrop)]
1512#[cfg_attr(feature = "hazmat", derive(PartialEq, Eq, Clone))]
1513pub enum HpkeAead {
1515 Aes128Gcm {
1517 key: [u8; HpkeAeadId::AES_128_GCM.n_key()],
1519
1520 nonce: [u8; HpkeAeadId::AES_128_GCM.n_nonce()],
1522 },
1523
1524 Aes256Gcm {
1526 key: [u8; HpkeAeadId::AES_256_GCM.n_key()],
1528
1529 nonce: [u8; HpkeAeadId::AES_256_GCM.n_nonce()],
1531 },
1532
1533 ChaCha20Poly1305 {
1535 key: [u8; HpkeAeadId::CHACHA20_POLY1305.n_key()],
1537
1538 nonce: [u8; HpkeAeadId::CHACHA20_POLY1305.n_nonce()],
1540 },
1541}
1542
1543impl HpkeAead {
1544 #[inline]
1545 pub const fn aead_id(&self) -> HpkeAeadId {
1547 match self {
1548 Self::Aes128Gcm { .. } => HpkeAeadId::AES_128_GCM,
1549 Self::Aes256Gcm { .. } => HpkeAeadId::AES_256_GCM,
1550 Self::ChaCha20Poly1305 { .. } => HpkeAeadId::CHACHA20_POLY1305,
1551 }
1552 }
1553
1554 #[allow(clippy::return_self_not_must_use)]
1555 pub fn copied_updating_nonce<F>(&self, update_nonce_f: F) -> Self
1560 where
1561 F: FnOnce(&mut [u8]),
1562 {
1563 match self {
1566 Self::Aes128Gcm { key, nonce } => {
1567 let mut nonce = *nonce;
1568
1569 update_nonce_f(&mut nonce);
1570
1571 Self::Aes128Gcm { key: *key, nonce }
1572 }
1573 Self::Aes256Gcm { key, nonce } => {
1574 let mut nonce = *nonce;
1575
1576 update_nonce_f(&mut nonce);
1577
1578 Self::Aes256Gcm { key: *key, nonce }
1579 }
1580 Self::ChaCha20Poly1305 { key, nonce } => {
1581 let mut nonce = *nonce;
1582
1583 update_nonce_f(&mut nonce);
1584
1585 Self::ChaCha20Poly1305 { key: *key, nonce }
1586 }
1587 }
1588 }
1589
1590 #[inline]
1591 pub const fn key(&self) -> &[u8] {
1593 match self {
1594 Self::Aes128Gcm { key, .. } => key,
1595 Self::Aes256Gcm { key, .. } => key,
1596 Self::ChaCha20Poly1305 { key, .. } => key,
1597 }
1598 }
1599
1600 #[inline]
1601 pub const fn nonce(&self) -> &[u8] {
1603 match self {
1604 Self::Aes128Gcm { nonce, .. } => nonce,
1605 Self::Aes256Gcm { nonce, .. } => nonce,
1606 Self::ChaCha20Poly1305 { nonce, .. } => nonce,
1607 }
1608 }
1609}
1610
1611impl zeroize::Zeroize for HpkeAead {
1612 fn zeroize(&mut self) {
1613 match self {
1614 Self::Aes128Gcm { key, .. } => {
1615 key.zeroize();
1616 }
1617 Self::Aes256Gcm { key, .. } => {
1618 key.zeroize();
1619 }
1620 Self::ChaCha20Poly1305 { key, .. } => {
1621 key.zeroize();
1622 }
1623 }
1624 }
1625}