libcrux/hpke/
hpke.rs

1#![allow(non_camel_case_types, non_snake_case, unused_imports)]
2
3use super::aead::*;
4use super::kdf::*;
5use super::kem::*;
6
7use super::errors::*;
8
9// === Constants ===
10
11///  A one-byte value indicating the HPKE mode, defined in the following table.
12///
13/// | Mode          | Value |
14/// | ------------- | ----- |
15/// | mode_base     | 0x00  |
16/// | mode_psk      | 0x01  |
17/// | mode_auth     | 0x02  |
18/// | mode_auth_psk | 0x03  |
19#[derive(Clone, Copy, PartialEq, Debug)]
20pub enum Mode {
21    /// 0x00
22    mode_base,
23    /// 0x01
24    mode_psk,
25    /// 0x02
26    mode_auth,
27    /// 0x03
28    mode_auth_psk,
29}
30
31// === Types ===
32
33#[derive(Clone, Copy, Debug)]
34pub struct HPKEConfig(pub Mode, pub KEM, pub KDF, pub AEAD);
35
36pub type KemOutput = Vec<u8>;
37pub type Ciphertext = Vec<u8>;
38pub struct HPKECiphertext(pub KemOutput, pub Ciphertext);
39
40pub type HpkePrivateKey = [u8];
41pub type HpkePublicKey = [u8];
42pub struct HPKEKeyPair(pub Vec<u8>, pub Vec<u8>); // Private, Public
43
44pub type AdditionalData = [u8];
45pub type Psk = [u8];
46pub type PskId = [u8];
47
48// === String labels ===
49
50/// "info_hash" label for [`LabeledExtract()`].
51///
52/// See [`KeySchedule`] for details.
53fn info_hash_label() -> Vec<u8> {
54    vec![
55        0x69u8, 0x6eu8, 0x66u8, 0x6fu8, 0x5fu8, 0x68u8, 0x61u8, 0x73u8, 0x68u8,
56    ]
57}
58
59/// "psk_id_hash" label for [`LabeledExtract()`].
60///
61/// See [`KeySchedule`] for details.
62fn psk_id_hash_label() -> Vec<u8> {
63    vec![
64        0x70u8, 0x73u8, 0x6bu8, 0x5fu8, 0x69u8, 0x64u8, 0x5fu8, 0x68u8, 0x61u8, 0x73u8, 0x68u8,
65    ]
66}
67
68/// "secret" label for [`LabeledExtract()`].
69///
70/// See [`KeySchedule`] for details.
71fn secret_label() -> Vec<u8> {
72    vec![0x73u8, 0x65u8, 0x63u8, 0x72u8, 0x65u8, 0x74u8]
73}
74
75/// "key" label for [`LabeledExpand()`].
76///
77/// See [`KeySchedule`] for details.
78fn key_label() -> Vec<u8> {
79    vec![0x6bu8, 0x65u8, 0x79u8]
80}
81
82/// "base_nonce" label for [`LabeledExpand()`].
83///
84/// See [`KeySchedule`] for details.
85fn base_nonce_label() -> Vec<u8> {
86    vec![
87        0x62u8, 0x61u8, 0x73u8, 0x65u8, 0x5fu8, 0x6eu8, 0x6fu8, 0x6eu8, 0x63u8, 0x65u8,
88    ]
89}
90
91/// "exp" label for [`LabeledExpand()`].
92///
93/// See [`KeySchedule`] for details.
94fn exp_label() -> Vec<u8> {
95    vec![0x65u8, 0x78u8, 0x70u8]
96}
97
98/// "sec" label for [`LabeledExpand()`].
99///
100/// See [`Context_Export`] for details.
101fn sec_label() -> Vec<u8> {
102    vec![0x73u8, 0x65u8, 0x63u8]
103}
104
105/// Get the numeric value of the `mode`.
106///
107/// See [`Mode`] for details.
108fn hpke_mode_label(mode: Mode) -> Vec<u8> {
109    match mode {
110        Mode::mode_base => vec![0x00u8],
111        Mode::mode_psk => vec![0x01u8],
112        Mode::mode_auth => vec![0x02u8],
113        Mode::mode_auth_psk => vec![0x03u8],
114    }
115}
116
117/// Get the numeric value of the `aead_id`.
118///
119/// See [`AEAD`] for details.
120fn hpke_aead_value(aead_id: AEAD) -> u16 {
121    match aead_id {
122        AEAD::AES_128_GCM => 0x0001u16,
123        AEAD::AES_256_GCM => 0x0002u16,
124        AEAD::ChaCha20Poly1305 => 0x0003u16,
125        AEAD::Export_only => 0xFFFFu16,
126    }
127}
128
129/// Get the KEM algorithm from the config
130fn kem(config: HPKEConfig) -> KEM {
131    let HPKEConfig(_, kem, _, _) = config;
132    kem
133}
134
135// === Context Helper ===
136
137type EncodedHpkePublicKey = Vec<u8>;
138type EncodedHpkePublicKeyIn = [u8];
139type ExporterSecret = Vec<u8>;
140type SequenceCounter = u32;
141type Context = (Key, Nonce, SequenceCounter, ExporterSecret);
142type SenderContext = (EncodedHpkePublicKey, Context);
143
144pub type SenderContextResult = Result<SenderContext, HpkeError>;
145pub type ContextResult = Result<Context, HpkeError>;
146pub type EmptyResult = Result<(), HpkeError>;
147
148/// Cipher suite identifier
149///
150/// See [`KeySchedule`] for more details.
151///
152/// The implicit `suite_id` value used within `LabeledExtract` and `LabeledExpand`
153/// is defined based on them as follows:
154///
155/// ```text
156/// suite_id = concat(
157///     "HPKE",
158///     I2OSP(kem_id, 2),
159///     I2OSP(kdf_id, 2),
160///     I2OSP(aead_id, 2)
161///   )
162/// ```
163fn suite_id(config: HPKEConfig) -> Vec<u8> {
164    let HPKEConfig(_, kem, kdf, aead) = config;
165    let mut suite_id = vec![0x48u8, 0x50u8, 0x4bu8, 0x45u8]; // "HPKE"
166    suite_id.extend_from_slice(&kem_value(kem).to_be_bytes());
167    suite_id.extend_from_slice(&kdf_value(kdf).to_be_bytes());
168    suite_id.extend_from_slice(&hpke_aead_value(aead).to_be_bytes());
169    suite_id
170}
171
172/// The default PSK ""
173///
174/// ```text
175/// default_psk = ""
176/// ```
177///
178/// See [`KeySchedule`] for more details.
179fn default_psk() -> Vec<u8> {
180    Vec::new()
181}
182
183/// The default PSK ID ""
184///
185/// ```text
186/// default_psk_id = ""
187/// ```
188///
189/// See [`KeySchedule`] for more details.
190fn default_psk_id() -> Vec<u8> {
191    Vec::new()
192}
193
194fn empty_bytes() -> Vec<u8> {
195    Vec::new()
196}
197
198/// Creating the Encryption Context
199///
200/// ...
201///
202/// ```text
203/// def VerifyPSKInputs(mode, psk, psk_id):
204///   got_psk = (psk != default_psk)
205///   got_psk_id = (psk_id != default_psk_id)
206///   if got_psk != got_psk_id:
207///     raise Exception("Inconsistent PSK inputs")
208///
209///   if got_psk and (mode in [mode_base, mode_auth]):
210///     raise Exception("PSK input provided when not needed")
211///   if (not got_psk) and (mode in [mode_psk, mode_auth_psk]):
212///     raise Exception("Missing required PSK input")
213/// ```
214///
215/// See [`KeySchedule`] for detail.
216pub fn VerifyPSKInputs(config: HPKEConfig, psk: &Psk, psk_id: &PskId) -> EmptyResult {
217    let HPKEConfig(mode, _kem, _kdf, _aead) = config;
218    let got_psk = psk.len() != 0;
219    let got_psk_id = psk_id.len() != 0;
220    if got_psk != got_psk_id {
221        EmptyResult::Err(HpkeError::InconsistentPskInputs)
222    } else {
223        // FIXME: https://github.com/hacspec/hacspec/issues/85
224        if got_psk && (mode == Mode::mode_base || mode == Mode::mode_auth) {
225            EmptyResult::Err(HpkeError::UnnecessaryPsk)
226        } else {
227            if !got_psk && (mode == Mode::mode_psk || mode == Mode::mode_auth_psk) {
228                EmptyResult::Err(HpkeError::MissingPsk)
229            } else {
230                EmptyResult::Ok(())
231            }
232        }
233    }
234}
235
236/// ## Creating the Encryption Context
237///
238///
239/// The variants of HPKE defined in this document share a common
240/// key schedule that translates the protocol inputs into an encryption
241/// context. The key schedule inputs are as follows:
242///
243/// * `mode` - A one-byte value indicating the HPKE mode, defined in [`Mode`].
244/// * `shared_secret` - A KEM shared secret generated for this transaction.
245/// * `info` - Application-supplied information (optional; default value
246///   "").
247/// * `psk` - A pre-shared key (PSK) held by both the sender
248///   and the recipient (optional; default value "").
249/// * `psk_id` - An identifier for the PSK (optional; default value "").
250///
251/// Senders and recipients MUST validate KEM inputs and outputs as described
252/// in [`KEM`].
253///
254/// The `psk` and `psk_id` fields MUST appear together or not at all.
255/// That is, if a non-default value is provided for one of them, then
256/// the other MUST be set to a non-default value. This requirement is
257/// encoded in [`VerifyPSKInputs()`] below.
258///
259/// The `psk`, `psk_id`, and `info` fields have maximum lengths that depend
260/// on the KDF itself, on the definition of [`LabeledExtract()`], and on the
261/// constant labels used together with them. See [KDF Input Length](mod@crate::hpke::kdf#input-length-restrictions) for
262/// precise limits on these lengths.
263///
264/// The `key`, `base_nonce`, and `exporter_secret` computed by the key schedule
265/// have the property that they are only known to the holder of the recipient
266/// private key, and the entity that used the KEM to generate `shared_secret` and
267/// `enc`.
268///
269/// In the Auth and AuthPSK modes, the recipient is assured that the sender
270/// held the private key `skS`. This assurance is limited for the DHKEM
271/// variants defined in this document because of key-compromise impersonation,
272/// as described in [`mod@crate::hpke::kem#dh-based-kem`] and the [security properties section](crate#security-properties). If in the PSK and
273/// AuthPSK modes, the `psk` and `psk_id` arguments are provided as required,
274/// then the recipient is assured that the sender held the corresponding
275/// pre-shared key. See the security properties section on the [module page](`crate`) for more details.
276///
277/// The HPKE algorithm identifiers, i.e., the KEM `kem_id`, KDF `kdf_id`, and
278/// AEAD `aead_id` 2-byte code points as defined in [`KEM`], [`KDF`],
279/// and [`AEAD`], respectively, are assumed implicit from the implementation
280/// and not passed as parameters.
281/// ```text
282/// def KeySchedule<ROLE>(mode, shared_secret, info, psk, psk_id):
283///   VerifyPSKInputs(mode, psk, psk_id)
284///
285///   psk_id_hash = LabeledExtract("", "psk_id_hash", psk_id)
286///   info_hash = LabeledExtract("", "info_hash", info)
287///   key_schedule_context = concat(mode, psk_id_hash, info_hash)
288///
289///   secret = LabeledExtract(shared_secret, "secret", psk)
290///
291///   key = LabeledExpand(secret, "key", key_schedule_context, Nk)
292///   base_nonce = LabeledExpand(secret, "base_nonce",
293///                              key_schedule_context, Nn)
294///   exporter_secret = LabeledExpand(secret, "exp",
295///                                   key_schedule_context, Nh)
296///
297///   return Context<ROLE>(key, base_nonce, 0, exporter_secret)
298/// ```
299///
300/// The `ROLE` template parameter is either S or R, depending on the role of
301/// sender or recipient, respectively. See [HPKE DEM](`ContextS_Seal`) for a discussion of the
302/// key schedule output, including the role-specific `Context` structure and its API.
303///
304/// Note that the `key_schedule_context` construction in [`KeySchedule()`] is
305/// equivalent to serializing a structure of the following form in the TLS presentation
306/// syntax:
307///
308/// ~~~text
309/// struct {
310///     uint8 mode;
311///     opaque psk_id_hash[Nh];
312///     opaque info_hash[Nh];
313/// } KeyScheduleContext;
314/// ~~~
315///
316/// This function takes the `<MODE>` as argument in [`HPKEConfig`].
317pub fn KeySchedule(
318    config: HPKEConfig,
319    shared_secret: &SharedSecret,
320    info: &Info,
321    psk: &Psk,
322    psk_id: &PskId,
323) -> ContextResult {
324    VerifyPSKInputs(config, &psk, psk_id)?;
325    let HPKEConfig(mode, _kem, kdf, aead) = config;
326
327    let psk_id_hash = LabeledExtract(
328        kdf,
329        suite_id(config),
330        &empty_bytes(),
331        psk_id_hash_label(),
332        psk_id,
333    )?;
334    let info_hash = LabeledExtract(
335        kdf,
336        suite_id(config),
337        &empty_bytes(),
338        info_hash_label(),
339        info,
340    )?;
341    let mut key_schedule_context = hpke_mode_label(mode);
342    key_schedule_context.extend_from_slice(&psk_id_hash);
343    key_schedule_context.extend_from_slice(&info_hash);
344
345    let secret = LabeledExtract(kdf, suite_id(config), shared_secret, secret_label(), psk)?;
346
347    let key = LabeledExpand(
348        kdf,
349        suite_id(config),
350        &secret,
351        key_label(),
352        &key_schedule_context,
353        Nk(aead),
354    )?;
355    let base_nonce = LabeledExpand(
356        kdf,
357        suite_id(config),
358        &secret,
359        base_nonce_label(),
360        &key_schedule_context,
361        Nn(aead),
362    )?;
363    let exporter_secret = LabeledExpand(
364        kdf,
365        suite_id(config),
366        &secret,
367        exp_label(),
368        &key_schedule_context,
369        Nh(kdf),
370    )?;
371
372    Ok((key, base_nonce, 0u32, exporter_secret))
373}
374
375/// ## Encryption to a Public Key - Sender
376///
377/// The most basic function of an HPKE scheme is to enable encryption to the
378/// holder of a given KEM private key. The [`SetupBaseS()`] and [`SetupBaseR()`]
379/// procedures establish contexts that can be used to encrypt and decrypt,
380/// respectively, for a given private key. The KEM shared secret is combined via
381/// the KDF with information describing the key exchange, as well as the explicit
382/// info parameter provided by the caller.The parameter pkR is a public key,
383/// and enc is an encapsulated KEM shared secret.
384///
385/// ```text
386/// def SetupBaseS(pkR, info):
387///   shared_secret, enc = Encap(pkR)
388///   return enc, KeyScheduleS(mode_base, shared_secret, info,
389///                            default_psk, default_psk_id)
390/// ```
391pub fn SetupBaseS(
392    config: HPKEConfig,
393    pkR: &HpkePublicKey,
394    info: &Info,
395    randomness: Randomness,
396) -> SenderContextResult {
397    let (shared_secret, enc) = Encap(kem(config), pkR, randomness)?;
398    let key_schedule = KeySchedule(
399        config,
400        &shared_secret,
401        info,
402        &default_psk(),
403        &default_psk_id(),
404    )?;
405    Ok((enc, key_schedule))
406}
407
408/// ## Encryption to a Public Key - Receiver
409///
410/// See [`SetupBaseS`] for more details.
411///
412/// ```text
413/// def SetupBaseR(enc, skR, info):
414///   shared_secret = Decap(enc, skR)
415///   return KeyScheduleR(mode_base, shared_secret, info,
416///                       default_psk, default_psk_id)
417/// ```
418pub fn SetupBaseR(
419    config: HPKEConfig,
420    enc: &EncodedHpkePublicKeyIn,
421    skR: &HpkePrivateKey,
422    info: &Info,
423) -> ContextResult {
424    let shared_secret = Decap(kem(config), enc, skR)?;
425    let key_schedule = KeySchedule(
426        config,
427        &shared_secret,
428        info,
429        &default_psk(),
430        &default_psk_id(),
431    )?;
432    Ok(key_schedule)
433}
434
435/// ## Authentication using a Pre-Shared Key - Sender
436///
437/// This variant extends the base mechanism by allowing the recipient to
438/// authenticate that the sender possessed a given PSK. The PSK also improves
439/// confidentiality guarantees in certain adversary models, as described in the
440/// [security properties](crate#security-properties). We assume that both parties have been provisioned with
441/// both the PSK value psk and another byte string `psk_id` that is used to identify
442/// which PSK should be used.
443/// The primary difference from the base case is that the psk and psk_id values
444/// are used as `ikm` inputs to the KDF (instead of using the empty string). The
445/// PSK MUST have at least 32 bytes of entropy and SHOULD be of length Nh bytes
446/// or longer. See the [PSK Recommendations](crate#pre-shared-key-recommendations) for a more detailed discussion.
447///
448/// ```text
449/// def SetupPSKS(pkR, info, psk, psk_id):
450///   shared_secret, enc = Encap(pkR)
451///   return enc, KeyScheduleS(mode_psk, shared_secret, info, psk, psk_id)
452/// ```
453pub fn SetupPSKS(
454    config: HPKEConfig,
455    pkR: &HpkePublicKey,
456    info: &Info,
457    psk: &Psk,
458    psk_id: &PskId,
459    randomness: Randomness,
460) -> SenderContextResult {
461    let (shared_secret, enc) = Encap(kem(config), pkR, randomness)?;
462    let key_schedule = KeySchedule(config, &shared_secret, info, psk, psk_id)?;
463    Ok((enc, key_schedule))
464}
465
466/// ## Authentication using a Pre-Shared Key - Receiver
467///
468/// See [`SetupPSKS`] for more details.
469///
470/// ```text
471/// def SetupPSKR(enc, skR, info, psk, psk_id):
472///   shared_secret = Decap(enc, skR)
473///   return KeyScheduleR(mode_psk, shared_secret, info, psk, psk_id)
474/// ```
475pub fn SetupPSKR(
476    config: HPKEConfig,
477    enc: &EncodedHpkePublicKeyIn,
478    skR: &HpkePrivateKey,
479    info: &Info,
480    psk: &Psk,
481    psk_id: &PskId,
482) -> ContextResult {
483    let shared_secret = Decap(kem(config), enc, skR)?;
484    let key_schedule = KeySchedule(config, &shared_secret, info, psk, psk_id)?;
485    Ok(key_schedule)
486}
487
488/// ## Authentication using an Asymmetric Key - Sender
489///
490/// This variant extends the base mechanism by allowing the recipient
491/// to authenticate that the sender possessed a given KEM private key.
492/// This is because [`AuthDecap(enc, skR, pkS)`](`crate::hpke::kem::AuthDecap()`) produces the correct KEM
493/// shared secret only if the encapsulated value `enc` was produced by
494/// [`AuthEncap(pkR, skS)`](`crate::hpke::kem::AuthEncap()`), where `skS` is the private key corresponding
495/// to `pkS`.  In other words, at most two entities (precisely two, in the case
496/// of DHKEM) could have produced this secret, so if the recipient is at most one, then
497/// the sender is the other with overwhelming probability.
498///
499/// The primary difference from the base case is that the calls to
500/// `Encap()` and `Decap()` are replaced with calls to [`AuthEncap()`](`crate::hpke::kem::AuthEncap()`) and
501/// [`AuthDecap()`](`crate::hpke::kem::AuthDecap()`), which add the sender public key to their internal
502/// context string. The function parameters `pkR` and `pkS` are
503/// public keys, and `enc` is an encapsulated KEM shared secret.
504///
505/// Obviously, this variant can only be used with a KEM that provides
506/// [`AuthEncap()`](`crate::hpke::kem::AuthEncap()`) and [`AuthDecap()`](`crate::hpke::kem::AuthDecap()`) procedures.
507///
508/// This mechanism authenticates only the key pair of the sender, not
509/// any other identifier.  If an application wishes to bind HPKE
510/// ciphertexts or exported secrets to another identity for the sender
511/// (e.g., an email address or domain name), then this identifier should be
512/// included in the `info` parameter to avoid identity mis-binding issues [IMB].
513///
514/// ```text
515/// def SetupAuthS(pkR, info, skS):
516///   shared_secret, enc = AuthEncap(pkR, skS)
517///   return enc, KeyScheduleS(mode_auth, shared_secret, info,
518///                            default_psk, default_psk_id)
519/// ```
520///
521/// [IMB]: https://doi.org/10.1007/bf00124891
522pub fn SetupAuthS(
523    config: HPKEConfig,
524    pkR: &HpkePublicKey,
525    info: &Info,
526    skS: &PrivateKeyIn,
527    randomness: Randomness,
528) -> SenderContextResult {
529    let (shared_secret, enc) = AuthEncap(kem(config), pkR, skS, randomness)?;
530    let key_schedule = KeySchedule(
531        config,
532        &shared_secret,
533        info,
534        &default_psk(),
535        &default_psk_id(),
536    )?;
537    Ok((enc, key_schedule))
538}
539
540/// ## Authentication using an Asymmetric Key - Receiver
541///
542/// See [`SetupAuthS`] for more details.
543///
544/// ```text
545/// def SetupAuthR(enc, skR, info, pkS):
546///   shared_secret = AuthDecap(enc, skR, pkS)
547///   return KeyScheduleR(mode_auth, shared_secret, info,
548///                       default_psk, default_psk_id)
549/// ```
550pub fn SetupAuthR(
551    config: HPKEConfig,
552    enc: &EncodedHpkePublicKeyIn,
553    skR: &HpkePrivateKey,
554    info: &Info,
555    pkS: &PublicKeyIn,
556) -> ContextResult {
557    let shared_secret = AuthDecap(kem(config), enc, skR, pkS)?;
558    let key_schedule = KeySchedule(
559        config,
560        &shared_secret,
561        info,
562        &default_psk(),
563        &default_psk_id(),
564    )?;
565    Ok(key_schedule)
566}
567
568/// ## Authentication using both a PSK and an Asymmetric Key - Sender
569///
570/// This mode is a straightforward combination of the PSK and
571/// authenticated modes.  The PSK is passed through to the key schedule
572/// as in the former, and as in the latter, we use the authenticated KEM
573/// variants.
574///
575/// ```text
576/// def SetupAuthPSKS(pkR, info, psk, psk_id, skS):
577///   shared_secret, enc = AuthEncap(pkR, skS)
578///   return enc, KeyScheduleS(mode_auth_psk, shared_secret, info,
579///                            psk, psk_id)
580/// ```
581///
582/// The PSK MUST have at least 32 bytes of entropy and SHOULD be of length `Nh`
583/// bytes or longer.
584pub fn SetupAuthPSKS(
585    config: HPKEConfig,
586    pkR: &HpkePublicKey,
587    info: &Info,
588    psk: &Psk,
589    psk_id: &PskId,
590    skS: &HpkePrivateKey,
591    randomness: Randomness,
592) -> SenderContextResult {
593    let (shared_secret, enc) = AuthEncap(kem(config), pkR, skS, randomness)?;
594    let key_schedule = KeySchedule(config, &shared_secret, info, psk, psk_id)?;
595    Ok((enc, key_schedule))
596}
597
598/// ## Authentication using both a PSK and an Asymmetric Key - Receiver
599///
600/// See [`SetupAuthPSKS`] for more details.
601///
602/// ```text
603/// def SetupAuthPSKR(enc, skR, info, psk, psk_id, pkS):
604///   shared_secret = AuthDecap(enc, skR, pkS)
605///   return KeyScheduleR(mode_auth_psk, shared_sec
606/// ```
607pub fn SetupAuthPSKR(
608    config: HPKEConfig,
609    enc: &EncodedHpkePublicKeyIn,
610    skR: &HpkePrivateKey,
611    info: &Info,
612    psk: &Psk,
613    psk_id: &PskId,
614    pkS: &PublicKeyIn,
615) -> ContextResult {
616    let shared_secret = AuthDecap(kem(config), enc, skR, pkS)?;
617    let key_schedule = KeySchedule(config, &shared_secret, info, psk, psk_id)?;
618    Ok(key_schedule)
619}
620
621// === Stateful API ===
622
623fn bitxor(mut lhs: Vec<u8>, rhs: Vec<u8>) -> Vec<u8> {
624    assert_eq!(lhs.len(), rhs.len());
625    for i in 0..lhs.len() {
626        lhs[i] = lhs[i] ^ rhs[i]
627    }
628    lhs
629}
630
631/// ### Compute Nonce
632///
633/// The sequence number provides nonce uniqueness: The nonce used for
634/// each encryption or decryption operation is the result of XORing
635/// `base_nonce` with the current sequence number, encoded as a big-endian
636/// integer of the same length as `base_nonce`. Implementations MAY use a
637/// sequence number that is shorter than the nonce length (padding on the left
638/// with zero), but MUST raise an error if the sequence number overflows.
639///
640/// ```text
641/// def Context<ROLE>.ComputeNonce(seq):
642///   seq_bytes = I2OSP(seq, Nn)
643///   return xor(self.base_nonce, seq_bytes)
644/// ```
645pub fn ComputeNonce(aead_id: AEAD, base_nonce: &Nonce, seq: SequenceCounter) -> Vec<u8> {
646    let seq = seq.to_be_bytes();
647    let Nn = Nn(aead_id);
648    let mut seq_bytes = vec![0u8; Nn - 4];
649    seq_bytes.extend_from_slice(&seq);
650    bitxor(base_nonce.clone(), seq_bytes)
651}
652
653/// ## Encryption and Decryption
654///
655/// HPKE allows multiple encryption operations to be done based on a
656/// given setup transaction.  Since the public-key operations involved
657/// in setup are typically more expensive than symmetric encryption or
658/// decryption, this allows applications to amortize the cost of the
659/// public-key operations, reducing the overall overhead.
660///
661/// In order to avoid nonce reuse, however, this encryption must be
662/// stateful. Each of the setup procedures above produces a role-specific
663/// context object that stores the AEAD and Secret Export parameters.
664/// The AEAD parameters consist of:
665///
666/// * The AEAD algorithm in use
667/// * A secret `key`
668/// * A base nonce `base_nonce`
669/// * A sequence number (initially 0)
670///
671/// The Secret Export parameters consist of:
672///
673/// * The HPKE ciphersuite in use
674/// * An `exporter_secret` used for the Secret Export interface; see [`Context_Export`].
675///
676/// All these parameters except the AEAD sequence number are constant.
677/// The sequence number provides nonce uniqueness: The nonce used for
678/// each encryption or decryption operation is the result of XORing
679/// `base_nonce` with the current sequence number, encoded as a big-endian
680/// integer of the same length as `base_nonce`. Implementations MAY use a
681/// sequence number that is shorter than the nonce length (padding on the left
682/// with zero), but MUST raise an error if the sequence number overflows. The AEAD
683/// algorithm produces ciphertext that is Nt bytes longer than the plaintext.
684/// Nt = 16 for AEAD algorithms defined in this document.
685///
686/// Encryption is unidirectional from sender to recipient. The sender's
687/// context can encrypt a plaintext `pt` with associated data `aad` as
688/// follows:
689///
690/// ```text
691/// def ContextS.Seal(aad, pt):
692///   ct = Seal(self.key, self.ComputeNonce(self.seq), aad, pt)
693///   self.IncrementSeq()
694///   return ct
695/// ```
696///
697/// The sender's context MUST NOT be used for decryption. Similarly, the recipient's
698/// context MUST NOT be used for encryption. Higher-level protocols re-using the HPKE
699/// key exchange for more general purposes can derive separate keying material as
700/// needed using use the Secret Export interface; see [`Context_Export`] and
701/// [Bidirectional Encryption](`crate#bidirectional-encryption`) for more details.
702///
703/// It is up to the application to ensure that encryptions and decryptions are
704/// done in the proper sequence, so that encryption and decryption nonces align.
705/// If [`ContextS_Seal()`] or [`ContextR_Open()`] would cause the `seq` field to
706/// overflow, then the implementation MUST fail with an error. (In the pseudocode
707/// `Context<ROLE>.IncrementSeq()` fails with an error when `seq` overflows,
708/// which causes [`ContextS_Seal()`] and [`ContextR_Open()`] to fail accordingly.)
709/// Note that the internal `Seal()` and `Open()`
710/// calls inside correspond to the context's [`AEAD`] algorithm.
711pub fn ContextS_Seal(
712    aead_id: AEAD,
713    context: Context,
714    aad: &[u8],
715    pt: &[u8],
716) -> Result<(Ciphertext, Context), HpkeError> {
717    let (key, base_nonce, seq, exp) = context;
718    let nonce = ComputeNonce(aead_id, &base_nonce, seq);
719    let ct = AeadSeal(aead_id, &key, &nonce, aad, pt)?;
720    let seq = IncrementSeq(aead_id, seq)?;
721    Ok((ct, (key, base_nonce, seq, exp)))
722}
723
724/// ## Stateful open.
725///
726/// See [ContextR.Open](`ContextS_Seal`) for more details.
727///
728/// The recipient's context can decrypt a ciphertext `ct` with associated
729/// data `aad` as follows:
730///
731/// ```text
732/// def ContextR.Open(aad, ct):
733///   pt = Open(self.key, self.ComputeNonce(self.seq), aad, ct)
734///   if pt == OpenError:
735///     raise OpenError
736///   self.IncrementSeq()
737///   return pt
738/// ```
739pub fn ContextR_Open(
740    aead_id: AEAD,
741    context: Context,
742    aad: &[u8],
743    ct: &[u8],
744) -> Result<(Vec<u8>, Context), HpkeError> {
745    let (key, base_nonce, seq, exp) = context;
746    let nonce = ComputeNonce(aead_id, &base_nonce, seq);
747    let pt = AeadOpen(aead_id, &key, &nonce, aad, ct)?;
748    let seq = IncrementSeq(aead_id, seq)?;
749    Ok((pt, (key, base_nonce, seq, exp)))
750}
751
752/// ### Increment Sequence
753///
754/// Each encryption or decryption operation increments the sequence number for
755/// the context in use.
756///
757/// ``` text
758/// def Context<ROLE>.IncrementSeq():
759///   if self.seq >= (1 << (8*Nn)) - 1:
760///     raise MessageLimitReachedError
761///   self.seq += 1
762/// ```
763pub fn IncrementSeq(aead_id: AEAD, seq: SequenceCounter) -> Result<SequenceCounter, HpkeError> {
764    if seq as u128 >= (1u128 << (8 * Nn(aead_id))) - 1u128 {
765        Err(HpkeError::MessageLimitReachedError)
766    } else {
767        Ok(seq + 1u32)
768    }
769}
770
771/// ## Secret Export
772///
773/// HPKE provides an interface for exporting secrets from the encryption context
774/// using a variable-length PRF, similar to the TLS 1.3 exporter interface
775/// (see [RFC8446], Section 7.5). This interface takes as input a context
776/// string `exporter_context` and a desired length `L` in bytes, and produces
777/// a secret derived from the internal exporter secret using the corresponding
778/// KDF Expand function. For the KDFs defined in this specification, `L` has
779/// a maximum value of `255*Nh`. Future specifications which define new KDFs
780/// MUST specify a bound for `L`.
781///
782/// The `exporter_context` field has a maximum length that depends on the KDF
783/// itself, on the definition of `LabeledExpand()`, and on the constant labels
784/// used together with them. See [KDF Input Length](mod@crate::hpke::kdf#input-length-restrictions)
785/// for precise limits on this length.
786///
787/// ```text
788/// def Context.Export(exporter_context, L):
789///   return LabeledExpand(self.exporter_secret, "sec",
790///                        exporter_context, L)
791/// ```
792///
793/// Applications that do not use the encryption API in [`ContextS_Seal`] can use
794/// the export-only AEAD ID `0xFFFF` when computing the key schedule. Such
795/// applications can avoid computing the `key` and `base_nonce` values in the
796/// key schedule, as they are not used by the Export interface described above.
797///
798/// [RFC8446]: https://www.rfc-editor.org/info/rfc8446
799pub fn Context_Export(
800    config: HPKEConfig,
801    context: &Context,
802    exporter_context: Vec<u8>,
803    L: usize,
804) -> HpkeBytesResult {
805    let (_, _, _, exporter_secret) = context;
806    let HPKEConfig(_, _, kdf_id, _) = config;
807    LabeledExpand(
808        kdf_id,
809        suite_id(config),
810        exporter_secret,
811        sec_label(),
812        &exporter_context,
813        L,
814    )
815}
816
817// === Singe-Shot API ===
818
819/// ## Encryption
820///
821/// In many cases, applications encrypt only a single message to a recipient's
822/// public key. This section provides templates for HPKE APIs that implement
823/// stateless "single-shot" encryption and decryption using APIs specified in
824/// [`SetupBaseS()`] and [`ContextS_Seal`]:
825///
826/// ```text
827/// def Seal<MODE>(pkR, info, aad, pt, ...):
828///   enc, ctx = Setup<MODE>S(pkR, info, ...)
829///   ct = ctx.Seal(aad, pt)
830///   return enc, ct
831/// ```
832///
833/// The `MODE` template parameter is one of Base, PSK, Auth, or AuthPSK. The optional parameters
834/// indicated by "..." depend on `MODE` and may be empty.
835///
836/// This function takes the `<MODE>` as argument in [`HPKEConfig`].
837pub fn HpkeSeal(
838    config: HPKEConfig,
839    pkR: &HpkePublicKey,
840    info: &Info,
841    aad: &AdditionalData,
842    ptxt: &[u8],
843    psk: Option<&Psk>,
844    psk_id: Option<&PskId>,
845    skS: Option<&HpkePrivateKey>,
846    randomness: Randomness,
847) -> Result<HPKECiphertext, HpkeError> {
848    let HPKEConfig(mode, _kem, _kdf, aead) = config;
849    let (enc, (key, nonce, _, _)) = match mode {
850        Mode::mode_base => SetupBaseS(config, pkR, info, randomness),
851        Mode::mode_psk => SetupPSKS(config, pkR, info, psk.unwrap(), psk_id.unwrap(), randomness),
852        Mode::mode_auth => SetupAuthS(config, pkR, info, skS.unwrap(), randomness),
853        Mode::mode_auth_psk => SetupAuthPSKS(
854            config,
855            pkR,
856            info,
857            psk.unwrap(),
858            psk_id.unwrap(),
859            skS.unwrap(),
860            randomness,
861        ),
862    }?;
863    let ct = AeadSeal(aead, &key, &nonce, aad, ptxt)?;
864    Ok(HPKECiphertext(enc, ct))
865}
866
867/// ## Decryption
868///
869/// See [`HpkeSeal`] for more details.
870///
871/// ```text
872/// def Open<MODE>(enc, skR, info, aad, ct, ...):
873///   ctx = Setup<MODE>R(enc, skR, info, ...)
874///   return ctx.Open(aad, ct)
875/// ```
876///
877/// This function takes the `<MODE>` as argument in [`HPKEConfig`].
878pub fn HpkeOpen(
879    config: HPKEConfig,
880    ctxt: &HPKECiphertext,
881    skR: &HpkePrivateKey,
882    info: &Info,
883    aad: &AdditionalData,
884    psk: Option<&Psk>,
885    psk_id: Option<&PskId>,
886    pkS: Option<&HpkePublicKey>,
887) -> HpkeBytesResult {
888    let HPKEConfig(mode, _kem, _kdf, aead) = config;
889    let HPKECiphertext(enc, ct) = ctxt;
890
891    let (key, nonce, _, _) = match mode {
892        Mode::mode_base => SetupBaseR(config, enc, skR, info),
893        Mode::mode_psk => SetupPSKR(config, enc, skR, info, psk.unwrap(), psk_id.unwrap()),
894        Mode::mode_auth => SetupAuthR(config, enc, skR, info, pkS.unwrap()),
895        Mode::mode_auth_psk => SetupAuthPSKR(
896            config,
897            enc,
898            skR,
899            info,
900            psk.unwrap(),
901            psk_id.unwrap(),
902            pkS.unwrap(),
903        ),
904    }?;
905
906    let ptxt = AeadOpen(aead, &key, &nonce, aad, ct)?;
907    Ok(ptxt)
908}
909
910/// ## "single-shot" secret export sender
911///
912/// ```text
913/// def SendExport<MODE>(pkR, info, exporter_context, L, ...):
914///   enc, ctx = Setup<MODE>S(pkR, info, ...)
915///   exported = ctx.Export(exporter_context, L)
916///   return enc, exported
917/// ```
918pub fn SendExport(
919    config: HPKEConfig,
920    pkR: &HpkePublicKey,
921    info: &Info,
922    exporter_context: Vec<u8>,
923    L: usize,
924    psk: Option<&Psk>,
925    psk_id: Option<&PskId>,
926    skS: Option<&HpkePrivateKey>,
927    randomness: Randomness,
928) -> Result<HPKECiphertext, HpkeError> {
929    let HPKEConfig(mode, _kem, _kdf, _aead) = config;
930    let (enc, ctx) = match mode {
931        Mode::mode_base => SetupBaseS(config, pkR, info, randomness),
932        Mode::mode_psk => SetupPSKS(config, pkR, info, psk.unwrap(), psk_id.unwrap(), randomness),
933        Mode::mode_auth => SetupAuthS(config, pkR, info, &skS.unwrap(), randomness),
934        Mode::mode_auth_psk => SetupAuthPSKS(
935            config,
936            pkR,
937            info,
938            psk.unwrap(),
939            psk_id.unwrap(),
940            &skS.unwrap(),
941            randomness,
942        ),
943    }?;
944    let exported = Context_Export(config, &ctx, exporter_context, L)?;
945    Ok(HPKECiphertext(enc, exported))
946}
947
948/// ## "single-shot" secret export receiver
949///
950/// ``` text
951/// def ReceiveExport<MODE>(enc, skR, info, exporter_context, L, ...):
952///   ctx = Setup<MODE>R(enc, skR, info, ...)
953///   return ctx.Export(exporter_context, L)
954/// ```
955pub fn ReceiveExport(
956    config: HPKEConfig,
957    enc: &EncodedHpkePublicKeyIn,
958    skR: &HpkePrivateKey,
959    info: &Info,
960    exporter_context: Vec<u8>,
961    L: usize,
962    psk: Option<&Psk>,
963    psk_id: Option<&PskId>,
964    pkS: Option<&HpkePublicKey>,
965) -> HpkeBytesResult {
966    let HPKEConfig(mode, _kem, _kdf, _aead) = config;
967
968    let ctx = match mode {
969        Mode::mode_base => SetupBaseR(config, enc, skR, info),
970        Mode::mode_psk => SetupPSKR(config, enc, skR, info, psk.unwrap(), psk_id.unwrap()),
971        Mode::mode_auth => SetupAuthR(config, enc, skR, info, pkS.unwrap()),
972        Mode::mode_auth_psk => SetupAuthPSKR(
973            config,
974            enc,
975            skR,
976            info,
977            psk.unwrap(),
978            psk_id.unwrap(),
979            pkS.unwrap(),
980        ),
981    }?;
982    Context_Export(config, &ctx, exporter_context, L)
983}