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}