hpke_rs/
lib.rs

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