Skip to main content

hpke_rs/
lib.rs

1#![doc = include_str!("../Readme.md")]
2//! # Examples
3//!
4//! Oneshot HPKE encryption.
5//!
6//! ```
7//! use hpke_rs::{*, hpke_types::*};
8//! use hpke_rs_libcrux::HpkeLibcrux;
9//! use hpke_rs_crypto::{HpkeCrypto, RngCore};
10//!
11//! // Set up hpke mode.
12//! let mut hpke = Hpke::<HpkeLibcrux>::new(Mode::Base, KemAlgorithm::DhKem25519,
13//!    KdfAlgorithm::HkdfSha256, AeadAlgorithm::ChaCha20Poly1305);
14//!
15//! // Generate keys. The other parties public key must be received in some way.
16//! let (sk_r, pk_r) = hpke.generate_key_pair().unwrap().into_keys();
17//! let (sk_s, pk_s) = hpke.generate_key_pair().unwrap().into_keys();
18//!
19//! // Set the input. Only `plain_text` is required
20//! let info = b"HPKE demo info";
21//! let aad = b"HPKE demo aad";
22//! let plaintext = b"HPKE demo plain text";
23//! let exporter_context = b"HPKE demo exporter context";
24//!
25//! // We don't use authentication or PSKs here.
26//! let psk = None;
27//! let psk_id = None;
28//! let sk_s = None;
29//! let pk_s = None;
30//!
31//! // Encrypt the `plaintext` to the receiver.
32//! let (enc, ctxt) = hpke
33//!     .seal(&pk_r, info, aad, plaintext, psk, psk_id, sk_s)
34//!     .unwrap();
35//!
36//! // Decrypt the ciphertext on the receiver.
37//! let ptxt = hpke
38//!     .open(&enc, &sk_r, info, aad, &ctxt, psk, psk_id, pk_s)
39//!     .unwrap();
40//!
41//! assert_eq!(ptxt, plaintext);
42//! ```
43//!
44//! Encryption context.
45//!
46//! ```
47//! use hpke_rs::{*, hpke_types::*};
48//! use hpke_rs_libcrux::HpkeLibcrux;
49//! use hpke_rs_crypto::{HpkeCrypto, RngCore};
50//!
51//! // Set up hpke mode.
52//! let mut hpke = Hpke::<HpkeLibcrux>::new(Mode::Base, KemAlgorithm::DhKem25519,
53//!    KdfAlgorithm::HkdfSha256, AeadAlgorithm::ChaCha20Poly1305);
54//!
55//! // Generate keys. The other parties public key must be received in some way.
56//! let (sk_r, pk_r) = hpke.generate_key_pair().unwrap().into_keys();
57//! let (sk_s, pk_s) = hpke.generate_key_pair().unwrap().into_keys();
58//!
59//! // Set the input. Only `plain_text` is required
60//! let info = b"HPKE demo info";
61//! let aad = b"HPKE demo aad";
62//! let plaintext = b"HPKE demo plain text";
63//! let exporter_context = b"HPKE demo exporter context";
64//!
65//! // We don't use authentication or PSKs here.
66//! let psk = None;
67//! let psk_id = None;
68//! let sk_s = None;
69//! let pk_s = None;
70//!
71//! // Set up the context on both sides.
72//! let (enc, mut sender_context) = hpke
73//!     .setup_sender(&pk_r, info, psk, psk_id, sk_s)
74//!     .unwrap();
75//!
76//! // Share `enc` with the receiver.
77//! let mut receiver_context = hpke
78//!     .setup_receiver(&enc, &sk_r, info, psk, psk_id, pk_s)
79//!     .unwrap();
80//!
81//! // Encrypt the plaintext to the receiver, using the context.
82//! let ctxt = sender_context.seal(aad, plaintext).unwrap();
83//!
84//! // Decrypt the ciphertext on the receiver, using the context.
85//! let ptxt = receiver_context.open(aad, &ctxt).unwrap();
86//!
87//! assert_eq!(ptxt, plaintext);
88//! ```
89
90#![forbid(unsafe_code, unused_must_use, unstable_features)]
91#![deny(
92    trivial_casts,
93    trivial_numeric_casts,
94    missing_docs,
95    unused_import_braces,
96    unused_extern_crates,
97    unused_qualifications
98)]
99#![cfg_attr(not(test), no_std)]
100
101extern crate alloc;
102#[cfg(feature = "std")]
103extern crate std;
104
105use alloc::{
106    format,
107    string::{String, ToString},
108    vec,
109    vec::Vec,
110};
111
112#[cfg(feature = "hpke-test-prng")]
113use hpke_rs_crypto::HpkeTestRng;
114use hpke_rs_crypto::{
115    types::{AeadAlgorithm, KdfAlgorithm, KemAlgorithm},
116    HpkeCrypto,
117};
118use prelude::kdf::{labeled_expand, labeled_extract};
119
120/// Re-export of the HPKE types from the [`hpke_rs_crypto`] crate.
121pub use hpke_rs_crypto::types as hpke_types;
122
123/// Re-export of the RustCrypto crate.
124#[cfg(feature = "rustcrypto")]
125pub use hpke_rs_rust_crypto as rustcrypto;
126
127/// Re-export of the libcrux crate.
128#[cfg(feature = "libcrux")]
129pub use hpke_rs_libcrux as libcrux;
130
131#[cfg(not(feature = "hpke-test-prng"))]
132use rand_core::TryRngCore;
133
134#[cfg(feature = "serialization")]
135pub(crate) use serde::{Deserialize, Serialize};
136use zeroize::{Zeroize, ZeroizeOnDrop};
137
138mod dh_kem;
139pub(crate) mod kdf;
140mod kem;
141pub mod prelude;
142
143mod util;
144
145#[cfg(test)]
146mod test_aead;
147#[cfg(test)]
148mod test_kdf;
149
150#[deprecated(
151    since = "0.0.7",
152    note = "Please use HpkeError instead. This alias will be removed with the first stable  0.1 release."
153)]
154#[allow(dead_code)]
155#[allow(clippy::upper_case_acronyms)]
156type HPKEError = HpkeError;
157
158/// HPKE Error types.
159#[derive(Debug, Clone, PartialEq)]
160pub enum HpkeError {
161    /// Error opening an HPKE ciphertext.
162    OpenError,
163
164    /// Invalid configuration or arguments.
165    InvalidConfig,
166
167    /// Invalid input.
168    InvalidInput,
169
170    /// Unknown HPKE mode.
171    UnknownMode,
172
173    /// Inconsistent PSK input.
174    InconsistentPsk,
175
176    /// PSK input is required but missing.
177    MissingPsk,
178
179    /// PSK input is provided but not needed.
180    UnnecessaryPsk,
181
182    /// PSK input is too short (needs to be at least 32 bytes).
183    InsecurePsk,
184
185    /// An error in the crypto library occurred.
186    CryptoError(String),
187
188    /// The message limit for this AEAD, key, and nonce.
189    MessageLimitReached,
190
191    /// Unable to collect enough randomness.
192    InsufficientRandomness,
193}
194
195#[cfg(feature = "std")]
196impl std::error::Error for HpkeError {}
197
198impl core::fmt::Display for HpkeError {
199    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
200        write!(f, "HPKE Error: {:?}", self)
201    }
202}
203
204#[deprecated(
205    since = "0.0.7",
206    note = "Please use HpkePublicKey instead. This alias will be removed with the first stable  0.1 release."
207)]
208#[allow(clippy::upper_case_acronyms)]
209#[allow(missing_docs)]
210pub type HPKEPublicKey = HpkePublicKey;
211
212/// An HPKE public key is a byte vector.
213#[derive(Debug, PartialEq, Clone, Default)]
214#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
215pub struct HpkePublicKey {
216    value: Vec<u8>,
217}
218
219#[deprecated(
220    since = "0.0.7",
221    note = "Please use HpkePrivateKey instead. This alias will be removed with the first stable  0.1 release."
222)]
223#[allow(clippy::upper_case_acronyms)]
224#[allow(missing_docs)]
225pub type HPKEPrivateKey = HpkePrivateKey;
226
227/// An HPKE private key is a byte vector.
228#[derive(Default, Zeroize)]
229#[zeroize(drop)] // XXX: Change to `ZeroizeOnDrop` when moving to 1.5
230#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
231#[cfg_attr(feature = "hazmat", derive(Clone))]
232pub struct HpkePrivateKey {
233    value: Vec<u8>,
234}
235
236#[deprecated(
237    since = "0.0.7",
238    note = "Please use HpkeKeyPair instead. This alias will be removed with the first stable  0.1 release."
239)]
240#[allow(clippy::upper_case_acronyms)]
241#[allow(missing_docs)]
242pub type HPKEKeyPair = HpkeKeyPair;
243
244/// An HPKE key pair has an HPKE private and public key.
245#[derive(Debug, Default)]
246#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
247#[cfg_attr(feature = "hazmat", derive(Clone))]
248pub struct HpkeKeyPair {
249    private_key: HpkePrivateKey,
250    public_key: HpkePublicKey,
251}
252
253/// HPKE supports four modes.
254#[derive(PartialEq, Copy, Clone, Debug, Zeroize)]
255#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
256#[repr(u8)]
257pub enum Mode {
258    /// HPKE Base mode.
259    Base = 0x00,
260
261    /// HPKE with PSK.
262    Psk = 0x01,
263
264    /// Authenticated HPKE.
265    Auth = 0x02,
266
267    /// Authenticated HPKE with PSK.
268    AuthPsk = 0x03,
269}
270
271impl core::fmt::Display for Mode {
272    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
273        write!(f, "{:?}", self)
274    }
275}
276
277impl TryFrom<u8> for Mode {
278    type Error = HpkeError;
279    fn try_from(x: u8) -> Result<Mode, HpkeError> {
280        match x {
281            0x00 => Ok(Mode::Base),
282            0x01 => Ok(Mode::Psk),
283            0x02 => Ok(Mode::Auth),
284            0x03 => Ok(Mode::AuthPsk),
285            _ => Err(HpkeError::UnknownMode),
286        }
287    }
288}
289
290/// Type alias for encapsulated secrets.
291/// A byte vector.
292type EncapsulatedSecret = Vec<u8>;
293
294/// Type alias for ciphertexts.
295/// A byte vector.
296type Ciphertext = Vec<u8>;
297
298/// Type alias for plain text.
299/// A byte vector.
300type Plaintext = Vec<u8>;
301
302/// The HPKE context.
303/// Note that the RFC currently doesn't define this.
304/// Also see <https://github.com/cfrg/draft-irtf-cfrg-hpke/issues/161>.
305#[derive(Zeroize, ZeroizeOnDrop)]
306pub struct Context<Crypto: 'static + HpkeCrypto> {
307    key: Vec<u8>,
308    nonce: Vec<u8>,
309    exporter_secret: Vec<u8>,
310    sequence_number: u64,
311    hpke: Hpke<Crypto>,
312}
313
314#[cfg(feature = "hazmat")]
315impl<Crypto: HpkeCrypto> core::fmt::Debug for Context<Crypto> {
316    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
317        write!(
318            f,
319            "Context {{\n  key: {:?}\n  nonce: {:?}\n exporter_secret: {:?}\n seq no: {:?}\n}}",
320            self.key, self.nonce, self.exporter_secret, self.sequence_number
321        )
322    }
323}
324
325#[cfg(not(feature = "hazmat"))]
326impl<Crypto: HpkeCrypto> core::fmt::Debug for Context<Crypto> {
327    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
328        write!(
329            f,
330            "Context {{\n  key: {:?}\n  nonce: {:?}\n exporter_secret: {:?}\n seq no: {:?}\n}}",
331            &"***", &"***", &"***", &"***"
332        )
333    }
334}
335
336impl<Crypto: HpkeCrypto> Context<Crypto> {
337    /// 5.2. Encryption and Decryption
338    ///
339    /// Takes the associated data and the plain text as byte slices and returns
340    /// the ciphertext or an error.
341    ///
342    /// The context can be re-used up to the AEAD's message limit. However, we
343    /// use only a 64 bit counter, which technically limits context
344    /// re-use to 0xffffffffffffffff (2^64 - 1) uses.
345    ///
346    /// ```text
347    /// def Context.Seal(aad, pt):
348    ///   ct = Seal(self.key, self.ComputeNonce(self.seq), aad, pt)
349    ///   self.IncrementSeq()
350    ///   return ct
351    /// ```
352    pub fn seal(&mut self, aad: &[u8], plain_txt: &[u8]) -> Result<Ciphertext, HpkeError> {
353        if self.hpke.aead_id == AeadAlgorithm::HpkeExport {
354            return Err(HpkeError::InvalidConfig);
355        }
356
357        let ctxt = Crypto::aead_seal(
358            self.hpke.aead_id,
359            &self.key,
360            &self.compute_nonce(),
361            aad,
362            plain_txt,
363        )?;
364        self.increment_seq()?;
365        Ok(ctxt)
366    }
367
368    /// 5.2. Encryption and Decryption
369    ///
370    /// Takes the associated data and the ciphertext as byte slices and returns
371    /// the plain text or an error.
372    ///
373    /// The context can be re-used up to the AEAD's message limit. However, we
374    /// use only a 64 bit counter, which technically limits context
375    /// re-use to 0xffffffffffffffff (2^64 - 1) uses.
376    ///
377    /// ```text
378    /// def Context.Open(aad, ct):
379    ///   pt = Open(self.key, self.ComputeNonce(self.seq), aad, ct)
380    ///   if pt == OpenError:
381    ///     raise OpenError
382    ///   self.IncrementSeq()
383    ///   return pt
384    /// ```
385    pub fn open(&mut self, aad: &[u8], cipher_txt: &[u8]) -> Result<Plaintext, HpkeError> {
386        if self.hpke.aead_id == AeadAlgorithm::HpkeExport {
387            return Err(HpkeError::InvalidConfig);
388        }
389
390        let ptxt = Crypto::aead_open(
391            self.hpke.aead_id,
392            &self.key,
393            &self.compute_nonce(),
394            aad,
395            cipher_txt,
396        )?;
397        self.increment_seq()?;
398        Ok(ptxt)
399    }
400
401    /// 5.3. Secret Export
402    ///
403    /// Takes a serialised exporter context as byte slice and a length for the
404    /// output secret and returns an exporter secret as byte vector.
405    ///
406    /// ```text
407    /// def Context.Export(exporter_context, L):
408    ///  return LabeledExpand(self.exporter_secret, "sec", exporter_context, L)
409    ///```
410    pub fn export(&self, exporter_context: &[u8], length: usize) -> Result<Vec<u8>, HpkeError> {
411        labeled_expand::<Crypto>(
412            self.hpke.kdf_id,
413            &self.exporter_secret,
414            &self.hpke.ciphersuite(),
415            "sec",
416            exporter_context,
417            length,
418        )
419        .map_err(|e| HpkeError::CryptoError(format!("Crypto error: {}", e)))
420    }
421
422    /// def Context<ROLE>.ComputeNonce(seq):
423    ///     seq_bytes = I2OSP(seq, Nn)
424    ///     return xor(self.base_nonce, seq_bytes)
425    fn compute_nonce(&self) -> Vec<u8> {
426        let seq = self.sequence_number.to_be_bytes();
427        let mut enc_seq = vec![0u8; self.nonce.len() - seq.len()];
428        enc_seq.extend_from_slice(&seq);
429        util::xor_bytes(&enc_seq, &self.nonce)
430    }
431
432    /// def Context<ROLE>.IncrementSeq():
433    ///     if self.seq >= (1 << (8*Nn)) - 1:
434    ///       raise MessageLimitReached
435    ///     self.seq += 1
436    fn increment_seq(&mut self) -> Result<(), HpkeError> {
437        if u128::from(self.sequence_number)
438            >= ((1u128 << (8 * Crypto::aead_nonce_length(self.hpke.aead_id))) - 1)
439        {
440            // The limit is 0xffffffffffffffffffffffff for all currently implemented
441            // ciphersuites.
442            return Err(HpkeError::MessageLimitReached);
443        }
444        self.sequence_number = self
445            .sequence_number
446            // We use a u64, which is lower than the AEAD limit
447            .checked_add(1)
448            .ok_or(HpkeError::MessageLimitReached)?;
449        Ok(())
450    }
451}
452
453/// The HPKE configuration struct.
454/// This holds the configuration for HPKE but no state.
455/// To use HPKE first instantiate the configuration with
456/// `let hpke = Hpke::new(mode, kem_mode, kdf_mode, aead_mode)`.
457/// Now one can use the `hpke` configuration.
458///
459/// Note that cloning does NOT clone the PRNG state.
460#[derive(Debug, Zeroize)]
461pub struct Hpke<Crypto: 'static + HpkeCrypto> {
462    mode: Mode,
463    kem_id: KemAlgorithm,
464    kdf_id: KdfAlgorithm,
465    aead_id: AeadAlgorithm,
466    prng: Crypto::HpkePrng,
467}
468
469impl<Crypto: 'static + HpkeCrypto> Clone for Hpke<Crypto> {
470    fn clone(&self) -> Self {
471        Self {
472            mode: self.mode,
473            kem_id: self.kem_id,
474            kdf_id: self.kdf_id,
475            aead_id: self.aead_id,
476            prng: Crypto::prng(),
477        }
478    }
479}
480
481impl<Crypto: HpkeCrypto> core::fmt::Display for Hpke<Crypto> {
482    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
483        write!(
484            f,
485            "{}_{}_{}_{}",
486            self.mode.to_string().to_lowercase(),
487            self.kem_id.to_string().to_lowercase(),
488            self.kdf_id.to_string().to_lowercase(),
489            self.aead_id.to_string().to_lowercase()
490        )
491    }
492}
493
494impl<Crypto: HpkeCrypto> Hpke<Crypto> {
495    /// Set up the configuration for HPKE.
496    pub fn new(
497        mode: Mode,
498        kem_id: KemAlgorithm,
499        kdf_id: KdfAlgorithm,
500        aead_id: AeadAlgorithm,
501    ) -> Self {
502        Self {
503            mode,
504            kem_id,
505            kdf_id,
506            aead_id,
507            prng: Crypto::prng(),
508        }
509    }
510
511    /// Set up an HPKE sender.
512    ///
513    /// For the base and PSK modes this encapsulates the public key `pk_r`
514    /// of the receiver.
515    /// For the Auth and AuthPSK modes this encapsulates and authenticates
516    /// the public key `pk_r` of the receiver with the senders secret key `sk_s`.
517    ///
518    /// **Note** that this API expects the public key to be encoded.
519    /// This differs from the RFC.
520    /// But the public keys will be present in encoded form rather than raw form
521    /// such that it doesn't make sense to deserialize before passing it in.
522    ///
523    /// The encapsulated secret is returned together with the context.
524    /// If the secret key is missing in an authenticated mode, an error is returned.
525    pub fn setup_sender(
526        &mut self,
527        pk_r: &HpkePublicKey,
528        info: &[u8],
529        psk: Option<&[u8]>,
530        psk_id: Option<&[u8]>,
531        sk_s: Option<&HpkePrivateKey>,
532    ) -> Result<(EncapsulatedSecret, Context<Crypto>), HpkeError> {
533        let (zz, enc) = match self.mode {
534            Mode::Base | Mode::Psk => kem::encaps::<Crypto>(self, pk_r.value.as_slice())?,
535            Mode::Auth | Mode::AuthPsk => {
536                let sk_s = match sk_s {
537                    Some(s) => &s.value,
538                    None => return Err(HpkeError::InvalidInput),
539                };
540                kem::auth_encaps::<Crypto>(self, pk_r.value.as_slice(), sk_s)?
541            }
542        };
543        Ok((
544            enc,
545            self.clone().key_schedule(
546                &zz,
547                info,
548                psk.unwrap_or_default(),
549                psk_id.unwrap_or_default(),
550            )?,
551        ))
552    }
553
554    /// Set up an HPKE receiver.
555    ///
556    /// For the base and PSK modes this decapsulates `enc` with the secret key
557    /// `sk_r` of the receiver.
558    /// For the Auth and AuthPSK modes this decapsulates and authenticates `enc`
559    /// with the secret key `sk_r` of the receiver and the senders public key `pk_s`.
560    ///
561    /// **Note** that this API expects the public key to be encoded.
562    /// This differs from the RFC.
563    /// But the public keys will be present in encoded form rather than raw form
564    /// such that it doesn't make sense to deserialize before passing it in.
565    ///
566    /// The context based on the decapsulated values and, if present, the PSK is
567    /// returned.
568    /// If the secret key is missing in an authenticated mode, an error is returned.
569    pub fn setup_receiver(
570        &self,
571        enc: &[u8],
572        sk_r: &HpkePrivateKey,
573        info: &[u8],
574        psk: Option<&[u8]>,
575        psk_id: Option<&[u8]>,
576        pk_s: Option<&HpkePublicKey>,
577    ) -> Result<Context<Crypto>, HpkeError> {
578        let zz = match self.mode {
579            Mode::Base | Mode::Psk => kem::decaps::<Crypto>(self.kem_id, enc, &sk_r.value)?,
580            Mode::Auth | Mode::AuthPsk => {
581                let pk_s = match pk_s {
582                    Some(s) => s.value.as_slice(),
583                    None => return Err(HpkeError::InvalidInput),
584                };
585                kem::auth_decaps::<Crypto>(self.kem_id, enc, &sk_r.value, pk_s)?
586            }
587        };
588        self.clone().key_schedule(
589            &zz,
590            info,
591            psk.unwrap_or_default(),
592            psk_id.unwrap_or_default(),
593        )
594    }
595
596    /// 6. Single-Shot APIs
597    ///
598    /// 6.1. Encryption and Decryption
599    ///
600    /// Single shot API to encrypt the bytes in `plain_text` to the public key
601    /// `pk_r`.
602    ///
603    /// **Note** that this API expects the public key to be encoded.
604    /// This differs from the RFC.
605    /// But the public keys will be present in encoded form rather than raw form
606    /// such that it doesn't make sense to deserialize before passing it in.
607    ///
608    /// Returns the encapsulated secret and the ciphertext, or an error.
609    #[allow(clippy::too_many_arguments)]
610    pub fn seal(
611        &mut self,
612        pk_r: &HpkePublicKey,
613        info: &[u8],
614        aad: &[u8],
615        plain_txt: &[u8],
616        psk: Option<&[u8]>,
617        psk_id: Option<&[u8]>,
618        sk_s: Option<&HpkePrivateKey>,
619    ) -> Result<(EncapsulatedSecret, Ciphertext), HpkeError> {
620        let (enc, mut context) = self.setup_sender(pk_r, info, psk, psk_id, sk_s)?;
621        let ctxt = context.seal(aad, plain_txt)?;
622        Ok((enc, ctxt))
623    }
624
625    /// 6. Single-Shot APIs
626    ///
627    /// 6.1. Encryption and Decryption
628    ///
629    /// Single shot API to decrypt the bytes in `ct` with the private key `sk_r`.
630    ///
631    /// **Note** that this API expects the public key to be encoded.
632    /// This differs from the RFC.
633    /// But the public keys will be present in encoded form rather than raw form
634    /// such that it doesn't make sense to deserialize before passing it in.
635    ///
636    /// Returns the decrypted plain text, or an error.
637    #[allow(clippy::too_many_arguments)]
638    pub fn open(
639        &self,
640        enc: &[u8],
641        sk_r: &HpkePrivateKey,
642        info: &[u8],
643        aad: &[u8],
644        ct: &[u8],
645        psk: Option<&[u8]>,
646        psk_id: Option<&[u8]>,
647        pk_s: Option<&HpkePublicKey>,
648    ) -> Result<Plaintext, HpkeError> {
649        let mut context = self.setup_receiver(enc, sk_r, info, psk, psk_id, pk_s)?;
650        context.open(aad, ct)
651    }
652
653    /// 6. Single-Shot APIs
654    ///
655    /// 6.2. Secret Export
656    ///
657    /// Single shot API to derive an exporter secret for receiver with public key
658    /// `pk_r`.
659    ///
660    /// **Note** that this API expects the public key to be encoded.
661    /// This differs from the RFC.
662    /// But the public keys will be present in encoded form rather than raw form
663    /// such that it doesn't make sense to deserialize before passing it in.
664    ///
665    /// Returns the encapsulated secret and the exporter secret for the given
666    /// exporter context and length.
667    #[allow(clippy::too_many_arguments)]
668    pub fn send_export(
669        &mut self,
670        pk_r: &HpkePublicKey,
671        info: &[u8],
672        psk: Option<&[u8]>,
673        psk_id: Option<&[u8]>,
674        sk_s: Option<&HpkePrivateKey>,
675        exporter_context: &[u8],
676        length: usize,
677    ) -> Result<(EncapsulatedSecret, Vec<u8>), HpkeError> {
678        let (enc, context) = self.setup_sender(pk_r, info, psk, psk_id, sk_s)?;
679        Ok((enc, context.export(exporter_context, length)?))
680    }
681
682    /// 6. Single-Shot APIs
683    ///
684    /// 6.2. Secret Export
685    ///
686    /// Single shot API to derive an exporter secret for receiver with private key
687    /// `sk_r`.
688    ///
689    /// **Note** that this API expects the public key to be encoded.
690    /// This differs from the RFC.
691    /// But the public keys will be present in encoded form rather than raw form
692    /// such that it doesn't make sense to deserialize before passing it in.
693    ///
694    /// Returns the exporter secret for the given exporter context and length.
695    #[allow(clippy::too_many_arguments)]
696    pub fn receiver_export(
697        &self,
698        enc: &[u8],
699        sk_r: &HpkePrivateKey,
700        info: &[u8],
701        psk: Option<&[u8]>,
702        psk_id: Option<&[u8]>,
703        pk_s: Option<&HpkePublicKey>,
704        exporter_context: &[u8],
705        length: usize,
706    ) -> Result<Vec<u8>, HpkeError> {
707        let context = self.setup_receiver(enc, sk_r, info, psk, psk_id, pk_s)?;
708        context.export(exporter_context, length)
709    }
710
711    /// Verify PSKs.
712    #[inline(always)]
713    fn verify_psk_inputs(&self, psk: &[u8], psk_id: &[u8]) -> Result<(), HpkeError> {
714        let got_psk = !psk.is_empty();
715        let got_psk_id = !psk_id.is_empty();
716        if (got_psk && !got_psk_id) || (!got_psk && got_psk_id) {
717            return Err(HpkeError::InconsistentPsk);
718        }
719
720        if got_psk && (self.mode == Mode::Base || self.mode == Mode::Auth) {
721            return Err(HpkeError::UnnecessaryPsk);
722        }
723        if !got_psk && (self.mode == Mode::Psk || self.mode == Mode::AuthPsk) {
724            return Err(HpkeError::MissingPsk);
725        }
726
727        // The PSK MUST have at least 32 bytes of entropy and SHOULD be of length Nh bytes or longer.
728        if (self.mode == Mode::Psk || self.mode == Mode::AuthPsk) && psk.len() < 32 {
729            return Err(HpkeError::InsecurePsk);
730        }
731
732        Ok(())
733    }
734
735    #[inline]
736    fn ciphersuite(&self) -> Vec<u8> {
737        util::concat(&[
738            b"HPKE",
739            &(self.kem_id as u16).to_be_bytes(),
740            &(self.kdf_id as u16).to_be_bytes(),
741            &(self.aead_id as u16).to_be_bytes(),
742        ])
743    }
744
745    #[inline]
746    fn key_schedule_context(
747        &self,
748        info: &[u8],
749        psk_id: &[u8],
750        suite_id: &[u8],
751    ) -> Result<Vec<u8>, HpkeError> {
752        let psk_id_hash =
753            labeled_extract::<Crypto>(self.kdf_id, &[0], suite_id, "psk_id_hash", psk_id)?;
754        let info_hash = labeled_extract::<Crypto>(self.kdf_id, &[0], suite_id, "info_hash", info)?;
755        Ok(util::concat(&[
756            &[self.mode as u8],
757            &psk_id_hash,
758            &info_hash,
759        ]))
760    }
761
762    /// Creating the Encryption Context
763    /// Generate the HPKE context from the given input.
764    pub fn key_schedule(
765        &self,
766        shared_secret: &[u8],
767        info: &[u8],
768        psk: &[u8],
769        psk_id: &[u8],
770    ) -> Result<Context<Crypto>, HpkeError> {
771        self.verify_psk_inputs(psk, psk_id)?;
772        let suite_id = self.ciphersuite();
773        let key_schedule_context = self.key_schedule_context(info, psk_id, &suite_id)?;
774        let secret =
775            labeled_extract::<Crypto>(self.kdf_id, shared_secret, &suite_id, "secret", psk)
776                .map_err(|e| HpkeError::CryptoError(format!("Crypto error: {}", e)))?;
777
778        let key = labeled_expand::<Crypto>(
779            self.kdf_id,
780            &secret,
781            &suite_id,
782            "key",
783            &key_schedule_context,
784            Crypto::aead_key_length(self.aead_id),
785        )
786        .map_err(|e| HpkeError::CryptoError(format!("Crypto error: {}", e)))?;
787        let base_nonce = labeled_expand::<Crypto>(
788            self.kdf_id,
789            &secret,
790            &suite_id,
791            "base_nonce",
792            &key_schedule_context,
793            Crypto::aead_nonce_length(self.aead_id),
794        )
795        .map_err(|e| HpkeError::CryptoError(format!("Crypto error: {}", e)))?;
796        let exporter_secret = labeled_expand::<Crypto>(
797            self.kdf_id,
798            &secret,
799            &suite_id,
800            "exp",
801            &key_schedule_context,
802            Crypto::kdf_digest_length(self.kdf_id),
803        )
804        .map_err(|e| HpkeError::CryptoError(format!("Crypto error: {}", e)))?;
805
806        Ok(Context {
807            key,
808            nonce: base_nonce,
809            exporter_secret,
810            sequence_number: 0,
811            hpke: self.clone(),
812        })
813    }
814
815    /// 4. Cryptographic Dependencies
816    ///
817    /// Randomized algorithm to generate a key pair `(skX, pkX)` for the KEM.
818    /// This is equivalent to `derive_key_pair(random_vector(sk.len()))`
819    ///
820    /// Returns an `HpkeKeyPair`.
821    pub fn generate_key_pair(&mut self) -> Result<HpkeKeyPair, HpkeError> {
822        let (sk, pk) = kem::key_gen::<Crypto>(self.kem_id, &mut self.prng)?;
823        Ok(HpkeKeyPair::new(sk.0.clone(), pk))
824    }
825
826    /// 7.1.2. DeriveKeyPair
827    /// Derive a key pair for the used KEM with the given input key material.
828    ///
829    /// Returns an `HpkeKeyPair` result or an `HpkeError` if key derivation fails.
830    pub fn derive_key_pair(&self, ikm: &[u8]) -> Result<HpkeKeyPair, HpkeError> {
831        let (pk, sk) = kem::derive_key_pair::<Crypto>(self.kem_id, ikm)?;
832        Ok(HpkeKeyPair::new(sk.0.clone(), pk))
833    }
834
835    #[inline]
836    pub(crate) fn random(&mut self, len: usize) -> Result<Vec<u8>, HpkeError> {
837        let prng = &mut self.prng;
838        let mut out = vec![0u8; len];
839
840        #[cfg(feature = "hpke-test-prng")]
841        prng.try_fill_test_bytes(&mut out)
842            .map_err(|_| HpkeError::InsufficientRandomness)?;
843        #[cfg(not(feature = "hpke-test-prng"))]
844        prng.try_fill_bytes(&mut out)
845            .map_err(|_| HpkeError::InsufficientRandomness)?;
846
847        Ok(out)
848    }
849
850    /// Get the rng.
851    pub(crate) fn rng(&mut self) -> &mut Crypto::HpkePrng {
852        &mut self.prng
853    }
854}
855
856impl HpkeKeyPair {
857    /// Create a new HPKE key pair.
858    /// Consumes the private and public key bytes.
859    pub fn new(sk: Vec<u8>, pk: Vec<u8>) -> Self {
860        Self {
861            private_key: HpkePrivateKey::new(sk),
862            public_key: HpkePublicKey::new(pk),
863        }
864    }
865
866    /// Get a reference to the HPKE private key of this key pair.
867    pub fn private_key(&self) -> &HpkePrivateKey {
868        &self.private_key
869    }
870
871    /// Get a reference to the HPKE public key of this key pair.
872    pub fn public_key(&self) -> &HpkePublicKey {
873        &self.public_key
874    }
875
876    /// Split the key pair into the two keys
877    pub fn into_keys(self) -> (HpkePrivateKey, HpkePublicKey) {
878        (self.private_key, self.public_key)
879    }
880
881    /// Build a key pair from two keys
882    pub fn from_keys(private_key: HpkePrivateKey, public_key: HpkePublicKey) -> Self {
883        Self {
884            private_key,
885            public_key,
886        }
887    }
888}
889
890impl From<(Vec<u8>, Vec<u8>)> for HpkeKeyPair {
891    fn from((sk, pk): (Vec<u8>, Vec<u8>)) -> Self {
892        Self::new(sk, pk)
893    }
894}
895
896impl From<(&[u8], &[u8])> for HpkeKeyPair {
897    fn from((sk, pk): (&[u8], &[u8])) -> Self {
898        Self::new(sk.to_vec(), pk.to_vec())
899    }
900}
901
902impl HpkePrivateKey {
903    /// Create a new HPKE private key.
904    /// Consumes the private key bytes.
905    pub fn new(b: Vec<u8>) -> Self {
906        Self { value: b }
907    }
908
909    /// Get the raw key as byte slice.
910    #[cfg(feature = "hazmat")]
911    pub fn as_slice(&self) -> &[u8] {
912        &self.value
913    }
914}
915
916impl From<Vec<u8>> for HpkePrivateKey {
917    fn from(b: Vec<u8>) -> Self {
918        Self::new(b)
919    }
920}
921
922impl From<&[u8]> for HpkePrivateKey {
923    fn from(b: &[u8]) -> Self {
924        Self::new(b.to_vec())
925    }
926}
927
928/// Constant-time comparison of private key values.
929impl PartialEq for HpkePrivateKey {
930    fn eq(&self, other: &Self) -> bool {
931        use subtle::ConstantTimeEq;
932        self.value.ct_eq(&other.value).into()
933    }
934}
935
936#[cfg(not(feature = "hazmat"))]
937impl core::fmt::Debug for HpkePrivateKey {
938    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
939        f.debug_struct("HpkePrivateKey")
940            .field("value", &"***")
941            .finish()
942    }
943}
944
945#[cfg(feature = "hazmat")]
946impl core::fmt::Debug for HpkePrivateKey {
947    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
948        f.debug_struct("HpkePrivateKey")
949            .field("value", &self.value)
950            .finish()
951    }
952}
953
954impl HpkePublicKey {
955    /// Create a new HPKE public key.
956    /// Consumes the public key bytes.
957    pub fn new(value: Vec<u8>) -> Self {
958        Self { value }
959    }
960
961    /// Get the raw key as byte slice.
962    pub fn as_slice(&self) -> &[u8] {
963        self.value.as_slice()
964    }
965}
966
967impl From<Vec<u8>> for HpkePublicKey {
968    fn from(b: Vec<u8>) -> Self {
969        Self::new(b)
970    }
971}
972
973impl From<&[u8]> for HpkePublicKey {
974    fn from(b: &[u8]) -> Self {
975        Self::new(b.to_vec())
976    }
977}
978
979#[cfg(feature = "serialization")]
980impl tls_codec::Size for HpkePublicKey {
981    #[inline(always)]
982    fn tls_serialized_len(&self) -> usize {
983        tls_codec::VLByteSlice(self.as_slice()).tls_serialized_len()
984    }
985}
986
987#[cfg(feature = "serialization")]
988impl tls_codec::Serialize for HpkePublicKey {
989    #[inline(always)]
990    fn tls_serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, tls_codec::Error> {
991        tls_codec::VLByteSlice(self.as_slice()).tls_serialize(writer)
992    }
993}
994
995#[cfg(feature = "serialization")]
996impl tls_codec::Size for &HpkePublicKey {
997    #[inline(always)]
998    fn tls_serialized_len(&self) -> usize {
999        tls_codec::VLByteSlice(self.as_slice()).tls_serialized_len()
1000    }
1001}
1002
1003#[cfg(feature = "serialization")]
1004impl tls_codec::Serialize for &HpkePublicKey {
1005    #[inline(always)]
1006    fn tls_serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, tls_codec::Error> {
1007        tls_codec::VLByteSlice(self.as_slice()).tls_serialize(writer)
1008    }
1009}
1010
1011#[cfg(feature = "serialization")]
1012impl tls_codec::Deserialize for HpkePublicKey {
1013    #[inline(always)]
1014    fn tls_deserialize<R: std::io::Read>(bytes: &mut R) -> Result<Self, tls_codec::Error> {
1015        Ok(Self {
1016            value: tls_codec::VLBytes::tls_deserialize(bytes)?.into(),
1017        })
1018    }
1019}
1020
1021#[cfg(feature = "serialization")]
1022impl tls_codec::Deserialize for &HpkePublicKey {
1023    #[inline(always)]
1024    fn tls_deserialize<R: std::io::Read>(_: &mut R) -> Result<Self, tls_codec::Error> {
1025        Err(tls_codec::Error::DecodingError(
1026            "Error trying to deserialize a reference.".to_string(),
1027        ))
1028    }
1029}
1030
1031/// Test util module. Should be moved really.
1032#[cfg(feature = "hpke-test")]
1033pub mod test_util {
1034    use alloc::{format, string::String, vec, vec::Vec};
1035
1036    use crate::HpkeError;
1037    use hpke_rs_crypto::{HpkeCrypto, HpkeTestRng};
1038
1039    impl<Crypto: HpkeCrypto> super::Hpke<Crypto> {
1040        /// Set PRNG state for testing.
1041        pub fn seed(&mut self, seed: &[u8]) -> Result<(), HpkeError> {
1042            self.prng.seed(seed);
1043            Ok(())
1044        }
1045    }
1046
1047    impl<Crypto: HpkeCrypto> super::Context<Crypto> {
1048        /// Get a reference to the key in the context.
1049        #[doc(hidden)]
1050        pub fn key(&self) -> &[u8] {
1051            &self.key
1052        }
1053        /// Get a reference to the nonce in the context.
1054        #[doc(hidden)]
1055        pub fn nonce(&self) -> &[u8] {
1056            &self.nonce
1057        }
1058        /// Get a reference to the exporter secret in the context.
1059        #[doc(hidden)]
1060        pub fn exporter_secret(&self) -> &[u8] {
1061            &self.exporter_secret
1062        }
1063        /// Get a reference to the sequence number in the context.
1064        #[doc(hidden)]
1065        pub fn sequence_number(&self) -> u64 {
1066            self.sequence_number
1067        }
1068    }
1069
1070    /// Convert `bytes` to a hex string.
1071    pub fn bytes_to_hex(bytes: &[u8]) -> String {
1072        let mut hex = String::new();
1073        for &b in bytes {
1074            hex += &format!("{:02X}", b);
1075        }
1076        hex
1077    }
1078
1079    /// Convert a hex string to a byte vector.
1080    pub fn hex_to_bytes(hex: &str) -> Vec<u8> {
1081        assert!(hex.len() % 2 == 0);
1082        let mut bytes = Vec::new();
1083        for i in 0..(hex.len() / 2) {
1084            bytes.push(u8::from_str_radix(&hex[2 * i..2 * i + 2], 16).unwrap());
1085        }
1086        bytes
1087    }
1088
1089    /// Convert a hex string to a byte vector.
1090    /// If the input is `None`, this returns an empty vector.
1091    pub fn hex_to_bytes_option(hex: Option<String>) -> Vec<u8> {
1092        match hex {
1093            Some(s) => hex_to_bytes(&s),
1094            None => vec![],
1095        }
1096    }
1097
1098    /// Convert a byte slice into byte slice option.
1099    /// Returns `Nonce` if the byte slice is empty and `Some(v)` otherwise.
1100    pub fn vec_to_option_slice(v: &[u8]) -> Option<&[u8]> {
1101        if v.is_empty() {
1102            None
1103        } else {
1104            Some(v)
1105        }
1106    }
1107}
1108
1109impl From<hpke_rs_crypto::error::Error> for HpkeError {
1110    fn from(e: hpke_rs_crypto::error::Error) -> Self {
1111        match e {
1112            hpke_rs_crypto::error::Error::AeadOpenError => HpkeError::OpenError,
1113            hpke_rs_crypto::error::Error::AeadInvalidNonce
1114            | hpke_rs_crypto::error::Error::AeadInvalidCiphertext => HpkeError::InvalidInput,
1115            hpke_rs_crypto::error::Error::UnknownAeadAlgorithm => HpkeError::UnknownMode,
1116            hpke_rs_crypto::error::Error::CryptoLibraryError(s) => HpkeError::CryptoError(s),
1117            hpke_rs_crypto::error::Error::HpkeInvalidOutputLength => {
1118                HpkeError::CryptoError("Invalid HPKE output length".to_string())
1119            }
1120            hpke_rs_crypto::error::Error::UnknownKdfAlgorithm => {
1121                HpkeError::CryptoError("Unknown KDF algorithm.".to_string())
1122            }
1123            hpke_rs_crypto::error::Error::KemInvalidSecretKey => {
1124                HpkeError::CryptoError("Invalid KEM secret key".to_string())
1125            }
1126            hpke_rs_crypto::error::Error::KemInvalidPublicKey => {
1127                HpkeError::CryptoError("Invalid KEM public key".to_string())
1128            }
1129            hpke_rs_crypto::error::Error::UnknownKemAlgorithm => {
1130                HpkeError::CryptoError("Unknown KEM algorithm".to_string())
1131            }
1132            hpke_rs_crypto::error::Error::InsufficientRandomness => {
1133                HpkeError::InsufficientRandomness
1134            }
1135            hpke_rs_crypto::error::Error::UnsupportedKemOperation => HpkeError::InvalidConfig,
1136            hpke_rs_crypto::error::Error::KemInvalidCiphertext => HpkeError::InvalidInput,
1137        }
1138    }
1139}