Crate hpke

source · []
Expand description

HPKE 🤝 hacspec

💡 This is a hacspec representation of the HPKE RFC. The text is mostly verbatim from the RFC with changes where required. It demonstrates the possibilities of hacspec for specifications.

This document describes a scheme for hybrid public-key encryption (HPKE). This scheme provides a variant of public-key encryption of arbitrary-sized plaintexts for a recipient public key. It also includes three authenticated variants, including one which authenticates possession of a pre-shared key, and two optional ones which authenticate possession of a KEM private key. HPKE works for any combination of an asymmetric key encapsulation mechanism (KEM), key derivation function (KDF), and authenticated encryption with additional data (AEAD) encryption function. Some authenticated variants may not be supported by all KEMs. We provide instantiations of the scheme using widely used and efficient primitives, such as Elliptic Curve Diffie-Hellman key agreement, HKDF, and SHA2.

The original document is a product of the Crypto Forum Research Group (CFRG) in the IRTF.


Encryption schemes that combine asymmetric and symmetric algorithms have been specified and practiced since the early days of public-key cryptography, e.g., RFC1421. Combining the two yields the key management advantages of asymmetric cryptography and the performance benefits of symmetric cryptography. The traditional combination has been “encrypt the symmetric key with the public key.” “Hybrid” public-key encryption schemes (HPKE), specified here, take a different approach: “generate the symmetric key and its encapsulation with the public key.” Specifically, encrypted messages convey an encryption key encapsulated with a public-key scheme, along with one or more arbitrary-sized ciphertexts encrypted using that key. This type of public key encryption has many applications in practice, including Messaging Layer Security mls-protocol and TLS Encrypted ClientHello tls-esni.

Currently, there are numerous competing and non-interoperable standards and variants for hybrid encryption, mostly based on ECIES, including ANSI X9.63 (ECIES), IEEE1363, ISO/IEC 18033-2, and SECG SEC 1. See MAEA10 for a thorough comparison. All these existing schemes have problems, e.g., because they rely on outdated primitives, lack proofs of IND-CCA2 security, or fail to provide test vectors.

This document defines an HPKE scheme that provides a subset of the functions provided by the collection of schemes above, but specified with sufficient clarity that they can be interoperably implemented. The HPKE construction defined herein is secure against (adaptive) chosen ciphertext attacks (IND-CCA2 secure) under classical assumptions about the underlying primitives HPKEAnalysis, ABHKLR20. A summary of these analyses is in Section 9.1.

This document represents the consensus of the Crypto Forum Research Group (CFRG).


The following terms are used throughout this document to describe the operations, roles, and behaviors of HPKE:

  • (skX, pkX): A Key Encapsulation Mechanism (KEM) key pair used in role X, where X is one of S, R, or E as sender, recipient, and ephemeral, respectively; skX is the private key and pkX is the public key.
  • pk(skX): The KEM public key corresponding to the KEM private key skX.
  • Sender (S): Role of entity which sends an encrypted message.
  • Recipient (R): Role of entity which receives an encrypted message.
  • Ephemeral (E): Role of a fresh random value meant for one-time use.
  • I2OSP(n, w): Convert non-negative integer n to a w-length, big-endian byte string as described in RFC8017.
  • OS2IP(x): Convert byte string x to a non-negative integer as described in RFC8017, assuming big-endian byte order.
  • concat(x0, ..., xN): Concatenation of byte strings. concat(0x01, 0x0203, 0x040506) = 0x010203040506.
  • random(n): A pseudorandom byte string of length n bytes
  • xor(a,b): XOR of byte strings; xor(0xF0F0, 0x1234) = 0xE2C4. It is an error to call this function with two arguments of unequal length.

Cryptographic Dependencies

HPKE variants rely on the following primitives:

  • A Key Encapsulation Mechanism (KEM)
  • A Key Derivation Function (KDF)
  • An AEAD encryption algorithm RFC5116

A ciphersuite is a triple (KEM, KDF, AEAD) containing a choice of algorithm for each primitive.

A set of algorithm identifiers for concrete instantiations of these primitives is provided in KEM, KDF, and AEAD. Algorithm identifier values are two bytes long. Future specifications may introduce new KEM, KDF, and AEAD algorithm identifiers and retain the security guarantees presented in this document.

Note that GenerateKeyPair can be implemented as DeriveKeyPair(random(Nsk)).

The notation pk(skX), depending on its use and the KEM and its implementation, is either the computation of the public key using the private key, or just syntax expressing the retrieval of the public key assuming it is stored along with the private key object.

Hybrid Public Key Encryption

In this section, we define a few HPKE variants. All variants take a recipient public key and a sequence of plaintexts pt, and produce an encapsulated key enc and a sequence of ciphertexts ct. These outputs are constructed so that only the holder of skR can decapsulate the key from enc and decrypt the ciphertexts. All the algorithms also take an info parameter that can be used to influence the generation of keys (e.g., to fold in identity information) and an aad parameter that provides Additional Authenticated Data to the AEAD algorithm in use.

In addition to the base case of encrypting to a public key, we include three authenticated variants, one which authenticates possession of a pre-shared key, one which authenticates possession of a KEM private key, and one which authenticates possession of both a pre-shared key and a KEM private key. All authenticated variants contribute additional keying material to the encryption operation. The following one-byte values will be used to distinguish between modes:


All these cases follow the same basic two-step pattern:

  1. Set up an encryption context that is shared between the sender and the recipient.
  2. Use that context to encrypt or decrypt content.

A context is an implementation-specific structure that encodes the AEAD algorithm and key in use, and manages the nonces used so that the same nonce is not used with multiple plaintexts. It also has an interface for exporting secret values, as described in Context_Export. See HPKE DEM for a description of this structure and its interfaces. HPKE decryption fails when the underlying AEAD decryption fails.

The constructions described here presume that the relevant non-private parameters (enc, psk_id, etc.) are transported between the sender and the recipient by some application making use of HPKE. Moreover, a recipient with more than one public key needs some way of determining which of its public keys was used for the encapsulation operation. As an example, applications may send this information alongside a ciphertext from sender to recipient. Specification of such a mechanism is left to the application. See Message Encoding for more details.

Note that some KEMs may not support AuthEncap() or AuthDecap(). For such KEMs, only mode_base or mode_psk are supported. Future specifications which define new KEMs MUST indicate whether these modes are supported. See Future KEMs for more details.

The procedures described in this section are laid out in a Python-like pseudocode. The algorithms in use are left implicit. See the Implementation Considerations Section for details on the differences to this hacspec implementation.

Creating the Encryption Context

The variants of HPKE defined in this document share a common key schedule that translates the protocol inputs into an encryption context.

See KeySchedule() for details.

Encryption to a Public Key

The most basic function of an HPKE scheme is to enable encryption to the holder of a given KEM private key.

See SetupBaseS() and SetupBaseR() for details.

Authentication using a Pre-Shared Key

This variant extends the base mechanism by allowing the recipient to authenticate that the sender possessed a given PSK.

See SetupPSKS() and SetupPSKR() for details.

Authentication using an Asymmetric Key

This variant extends the base mechanism by allowing the recipient to authenticate that the sender possessed a given KEM private key.

See SetupAuthS() and SetupAuthR() for details.

Authentication using both a PSK and an Asymmetric Key

This mode is a straightforward combination of the PSK and authenticated modes.

See SetupAuthPSKS() and SetupAuthPSKR() for details.

Encryption and Decryption

HPKE allows multiple encryption operations to be done based on a given setup transaction.

See ContextS_Seal and ContextR_Open for details.

Secret Export

HPKE provides an interface for exporting secrets from the encryption context using a variable-length PRF, similar to the TLS 1.3 exporter interface.

See Context_Export for details.

Single-Shot APIs

Encryption and Decryption - Single-Shot

In many cases, applications encrypt only a single message to a recipient’s public key.

See HpkeSeal and HpkeOpen for details.

Secret Export - Single-Shot

Applications may also want to derive a secret known only to a given recipient. This section provides templates for HPKE APIs that implement stateless “single-shot” secret export using APIs specified in Secret Export:

See SendExport and ReceiveExport.

As in Single Shot Encryption, the MODE template parameter is one of Base, PSK, Auth, or AuthPSK. The optional parameters indicated by “…” depend on MODE and may be empty.

Algorithm Identifiers

This section lists algorithm identifiers suitable for different HPKE configurations. Future specifications may introduce new KEM, KDF, and AEAD algorithm identifiers and retain the security guarantees presented in this document provided they adhere to the security requirements in KEM Security, KDF Security, and AEAD Security, respectively.

See KDF, KEM, and AEAD for details on the algorithms.

API Considerations

This section documents considerations for interfaces to implementations of HPKE. This includes error handling considerations and recommendations that improve interoperability when HPKE is used in applications.

Auxiliary Authenticated Application Information

HPKE has two places at which applications can specify auxiliary authenticated information: (1) during context construction via the Setup info parameter, and (2) during Context operations, i.e., with the aad parameter for Open() and Seal(), and the exporter_context parameter for Export(). Application information applicable to multiple operations on a single Context should use the Setup info parameter. This avoids redundantly processing this information for each Context operation. In contrast, application information that varies on a per-message basis should be specified via the Context APIs (Seal(), Open(), or Export()).

Applications that only use the single-shot APIs described in {{single-shot-apis}} should use the Setup info parameter for specifying auxiliary authenticated information. Implementations which only expose single-shot APIs should not allow applications to use both Setup info and Context aad or exporter_context auxiliary information parameters.


The high-level, public HPKE APIs specified in this document are all fallible.

See Errors for details.

Message Encoding

This document does not specify a wire format encoding for HPKE messages. Applications that adopt HPKE must therefore specify an unambiguous encoding mechanism which includes, minimally: the encapsulated value enc, ciphertext value(s) (and order if there are multiple), and any info values that are not implicit. One example of a non-implicit value is the recipient public key used for encapsulation, which may be needed if a recipient has more than one public key.

The AEAD interface used in this document is based on RFC5116, which produces and consumes a single ciphertext value. As discussed in RFC5116, this ciphertext value contains the encrypted plaintext as well as any authentication data, encoded in a manner described by the individual AEAD scheme. Some implementations are not structured in this way, instead providing a separate ciphertext and authentication tag. When such AEAD implementations are used in HPKE implementations, the HPKE implementation must combine these inputs into a single ciphertext value within Seal(), and parse them out within Open(), where the parsing details are defined by the AEAD scheme. For example, with the AES-GCM schemes specified in this document, the GCM authentication tag is placed in the last Nt bytes of the ciphertext output.

Security Properties

HPKE has several security goals, depending on the mode of operation, against active and adaptive attackers that can compromise partial secrets of senders and recipients. The desired security goals are detailed below:

  • Message secrecy: Confidentiality of the sender’s messages against chosen ciphertext attacks
  • Export key secrecy: Indistinguishability of each export secret from a uniformly random bitstring of equal length, i.e., Context.Export is a variable-length PRF
  • Sender authentication: Proof of sender origin for PSK, Auth, and AuthPSK modes

These security goals are expected to hold for any honest sender and honest recipient keys, as well as if the honest sender and honest recipient keys are the same.

HPKE mitigates malleability problems (called benign malleability SECG SEC 1) in prior public key encryption standards based on ECIES by including all public keys in the context of the key schedule.

HPKE does not provide forward secrecy with respect to recipient compromise. In the Base and Auth modes, the secrecy properties are only expected to hold if the recipient private key skR is not compromised at any point in time. In the PSK and AuthPSK modes, the secrecy properties are expected to hold if the recipient private key skR and the pre-shared key are not both compromised at any point in time. See the non-goals section for more details.

In the Auth mode, sender authentication is generally expected to hold if the sender private key skS is not compromised at the time of message reception. In the AuthPSK mode, sender authentication is generally expected to hold if at the time of message reception, the sender private key skS and the pre-shared key are not both compromised.

Besides forward secrecy and key-compromise impersonation, which are highlighted in this section because of their particular cryptographic importance, HPKE has other non-goals that are described in the non-goals section: no tolerance of message reordering or loss, no downgrade or replay prevention, no hiding of the plaintext length, no protection against bad ephemeral randomness. The non-goals section suggests application-level mitigations for some of them.

Key-Compromise Impersonation

The DHKEM variants defined in this document are vulnerable to key-compromise impersonation attacks BJM97, which means that sender authentication cannot be expected to hold in the Auth mode if the recipient private key skR is compromised, and in the AuthPSK mode if the pre-shared key and the recipient private key skR are both compromised. NaCl’s box interface NaCl has the same issue. At the same time, this enables repudiability.

As shown by ABHKLR20, key-compromise impersonation attacks are generally possible on HPKE because KEM ciphertexts are not bound to HPKE messages. An adversary who knows a recipient’s private key can decapsulate an observed KEM ciphertext, compute the key schedule, and encrypt an arbitrary message that the recipient will accept as coming from the original sender. Importantly, this is possible even with a KEM that is resistant to key-compromise impersonation attacks. As a result, mitigating this issue requires fundamental changes that are out-of-scope of this specification.

Applications that require resistance against key-compromise impersonation SHOULD take extra steps to prevent this attack. One possibility is to produce a digital signature over (enc, ct) tuples using a sender’s private key – where ct is an AEAD ciphertext produced by the single-shot or multi-shot API, and enc the corresponding KEM encapsulated key.

Given these properties, pre-shared keys strengthen both the authentication and the secrecy properties in certain adversary models. One particular example in which this can be useful is a hybrid quantum setting: if a non-quantum-resistant KEM used with HPKE is broken by a quantum computer, the security properties are preserved through the use of a pre-shared key. As described in RFC8696 Section 7 this assumes that the pre-shared key has not been compromised.

Computational Analysis

It is shown in CS01 that a hybrid public-key encryption scheme of essentially the same form as the Base mode described here is IND-CCA2-secure as long as the underlying KEM and AEAD schemes are IND-CCA2-secure. Moreover, it is shown in HHK06 that IND-CCA2 security of the KEM and the data encapsulation mechanism are necessary conditions to achieve IND-CCA2 security for hybrid public-key encryption. The main difference between the scheme proposed in CS01 and the Base mode in this document (both named HPKE) is that we interpose some KDF calls between the KEM and the AEAD. Analyzing the HPKE Base mode instantiation in this document therefore requires verifying that the additional KDF calls do not cause the IND-CCA2 property to fail, as well as verifying the additional export key secrecy property.

Analysis of the PSK, Auth, and AuthPSK modes defined in this document additionally requires verifying the sender authentication property. While the PSK mode just adds supplementary keying material to the key schedule, the Auth and AuthPSK modes make use of a non-standard authenticated KEM construction. Generally, the authenticated modes of HPKE can be viewed and analyzed as flavors of signcryption SigncryptionDZ10.

A preliminary computational analysis of all HPKE modes has been done in HPKEAnalysis, indicating asymptotic security for the case where the KEM is DHKEM, the AEAD is any IND-CPA and INT-CTXT-secure scheme, and the DH group and KDF satisfy the following conditions:

  • DH group: The gap Diffie-Hellman (GDH) problem is hard in the appropriate subgroup GAP.
  • Extract() and Expand(): Extract() can be modeled as a random oracle. Expand() can be modeled as a pseudorandom function, wherein the first argument is the key.

In particular, the KDFs and DH groups defined in this document (see kdf-ids and kem-ids) satisfy these properties when used as specified. The analysis in HPKEAnalysis demonstrates that under these constraints, HPKE continues to provide IND-CCA2 security, and provides the additional properties noted above. Also, the analysis confirms the expected properties hold under the different key compromise cases mentioned above. The analysis considers a sender that sends one message using the encryption context, and additionally exports two independent secrets using the secret export interface.

The table below summarizes the main results from HPKEAnalysis. N/A means that a property does not apply for the given mode, whereas y means the given mode satisfies the property.

VariantMessage Sec.Export Sec.Sender Auth.

If non-DH-based KEMs are to be used with HPKE, further analysis will be necessary to prove their security. The results from CS01 provide some indication that any IND-CCA2-secure KEM will suffice here, but are not conclusive given the differences in the schemes.

A detailed computational analysis of HPKE’s Auth mode single-shot encryption API has been done in ABHKLR20. The paper defines security notions for authenticated KEMs and for authenticated public key encryption, using the outsider and insider security terminology known from signcryption SigncryptionDZ10. The analysis proves that DHKEM’s AuthEncap()/AuthDecap() interface fulfills these notions for all Diffie-Hellman groups specified in this document, and indicates exact security bounds, under the assumption that the gap Diffie-Hellman (GDH) problem is hard in the appropriate subgroup GAP, and that HKDF can be modeled as a random oracle.

Further, ABHKLR20 proves composition theorems, showing that HPKE’s Auth mode fulfills the security notions of authenticated public key encryption for all KDFs and AEAD schemes specified in this document, given any authenticated KEM satisfying the previously defined security notions for authenticated KEMs. The theorems assume that the KEM is perfectly correct; they could easily be adapted to work with KEMs that have a non-zero but negligible probability for decryption failure. The assumptions on the KDF are that Extract() and Expand() can be modeled as pseudorandom functions wherein the first argument is the key, respectively. The assumption for the AEAD is IND-CPA and IND-CTXT security.

In summary, the analysis in ABHKLR20 proves that the single-shot encryption API of HPKE’s Auth mode satisfies the desired message confidentiality and sender authentication properties listed at the beginning of this section; it does not consider multiple messages, nor the secret export API.

Post-Quantum Security

All of CS01, HPKEAnalysis, and ABHKLR20 are premised on classical security models and assumptions, and do not consider adversaries capable of quantum computation. A full proof of post-quantum security would need to take appropriate security models and assumptions into account, in addition to simply using a post-quantum KEM. However, the composition theorems from ABHKLR20 for HPKE’s Auth mode only make standard assumptions (i.e., no random oracle assumption) that are expected to hold against quantum adversaries (although with slightly worse bounds). Thus, these composition theorems, in combination with a post-quantum-secure authenticated KEM, guarantee the post-quantum security of HPKE’s Auth mode.

In future work, the analysis from ABHKLR20 can be extended to cover HPKE’s other modes and desired security properties. The hybrid quantum-resistance property described above, which is achieved by using the PSK or AuthPSK mode, is not proven in HPKEAnalysis because this analysis requires the random oracle model; in a quantum setting, this model needs adaption to, for example, the quantum random oracle model.

Pre-Shared Key Recommendations

In the PSK and AuthPSK modes, the PSK MUST have at least 32 bytes of entropy and SHOULD be of length Nh bytes or longer. Using a PSK longer than 32 bytes but shorter than Nh bytes is permitted.

HPKE is specified to use HKDF as key derivation function. HKDF is not designed to slow down dictionary attacks, see RFC5869. Thus, HPKE’s PSK mechanism is not suitable for use with a low-entropy password as the PSK: in scenarios in which the adversary knows the KEM shared secret shared_secret and has access to an oracle that allows to distinguish between a good and a wrong PSK, it can perform PSK-recovering attacks. This oracle can be the decryption operation on a captured HPKE ciphertext or any other recipient behavior which is observably different when using a wrong PSK. The adversary knows the KEM shared secret shared_secret if it knows all KEM private keys of one participant. In the PSK mode this is trivially the case if the adversary acts as sender.

To recover a lower entropy PSK, an attacker in this scenario can trivially perform a dictionary attack. Given a set S of possible PSK values, the attacker generates an HPKE ciphertext for each value in S, and submits the resulting ciphertexts to the oracle to learn which PSK is being used by the recipient. Further, because HPKE uses AEAD schemes that are not key-committing, an attacker can mount a partitioning oracle attack LGR20 which can recover the PSK from a set of S possible PSK values, with |S| = m*k, in roughly m + log k queries to the oracle using ciphertexts of length proportional to k, the maximum message length in blocks. (Applying the multi-collision algorithm from LGR20 requires a small adaptation to the algorithm wherein the appropriate nonce is computed for each candidate key. This modification adds one call to HKDF per key. The number of partitioning oracle queries remains unchanged.) As a result, the PSK must therefore be chosen with sufficient entropy so that m + log k is prohibitive for attackers (e.g., 2^128). Future specifications can define new AEAD algorithms which are key-committing.

Domain Separation

HPKE allows combining a DHKEM variant DHKEM(Group, KDF') and a KDF such that both KDFs are instantiated by the same KDF. By design, the calls to Extract() and Expand() inside DHKEM and the remainder of HPKE use separate input domains. This justifies modeling them as independent functions even if instantiated by the same KDF. This domain separation between DHKEM and the remainder of HPKE is achieved by the suite_id values in LabeledExtract() and LabeledExpand(): The values used (KEM... in DHKEM and HPKE... in the remainder of HPKE) are prefix-free (a set is prefix-free if no element is a prefix of another within the set).

Future KEM instantiations MUST ensure, should Extract() and Expand() be used internally, that they can be modeled as functions independent from the invocations of Extract() and Expand() in the remainder of HPKE. One way to ensure this is by using LabeledExtract() and LabeledExpand() with a suite_id as defined in [base-crypto], which will ensure input domain separation as outlined above. Particular attention needs to be paid if the KEM directly invokes functions that are used internally in HPKE’s Extract() or Expand(), such as Hash() and HMAC() in the case of HKDF. It MUST be ensured that inputs to these invocations cannot collide with inputs to the internal invocations of these functions inside Extract() or Expand(). In HPKE’s KeySchedule() this is avoided by using Extract() instead of Hash() on the arbitrary-length inputs info and psk_id.

The string literal “HPKE-v1” used in LabeledExtract() and LabeledExpand() ensures that any secrets derived in HPKE are bound to the scheme’s name and version, even when possibly derived from the same Diffie-Hellman or KEM shared secret as in another scheme or version.

Application Embedding and Non-Goals

HPKE is designed to be a fairly low-level mechanism. As a result, it assumes that certain properties are provided by the application in which HPKE is embedded, and leaves certain security properties to be provided by other mechanisms. Otherwise said, certain properties are out-of-scope for HPKE.

Message Order and Message Loss

The primary requirement that HPKE imposes on applications is the requirement that ciphertexts MUST be presented to ContextR.Open() in the same order in which they were generated by ContextS.Seal(). When the single-shot API is used (see [single-shot-apis]), this is trivially true (since there is only ever one ciphertext. Applications that allow for multiple invocations of Open() / Seal() on the same context MUST enforce the ordering property described above.

Ordering requirements of this character are usually fulfilled by providing a sequence number in the framing of encrypted messages. Whatever information is used to determine the ordering of HPKE-encrypted messages SHOULD be included in the AAD passed to ContextS.Seal() and ContextR.Open(). The specifics of this scheme are up to the application.

HPKE is not tolerant of lost messages. Applications MUST be able to detect when a message has been lost. When an unrecoverable loss is detected, the application MUST discard any associated HPKE context.

Downgrade Prevention

HPKE assumes that the sender and recipient agree on what algorithms to use. Depending on how these algorithms are negotiated, it may be possible for an intermediary to force the two parties to use suboptimal algorithms.

Replay Protection

The requirement that ciphertexts be presented to the ContextR.Open() function in the same order they were generated by ContextS.Seal() provides a degree of replay protection within a stream of ciphertexts resulting from a given context. HPKE provides no other replay protection.

Forward Secrecy

HPKE ciphertexts are not forward secret with respect to recipient compromise in any mode. This means that compromise of long-term recipient secrets allows an attacker to decrypt past ciphertexts encrypted under said secrets. This is because only long-term secrets are used on the side of the recipient.

HPKE ciphertexts are forward secret with respect to sender compromise in all modes. This is because ephemeral randomness is used on the sender’s side, which is supposed to be erased directly after computation of the KEM shared secret and ciphertext.

Bad Ephemeral Randomness

If the randomness used for KEM encapsulation is bad – i.e. of low entropy or compromised because of a broken or subverted random number generator – the confidentiality guarantees of HPKE degrade significantly. In Base mode, confidentiality guarantees can be lost completely; in the other modes, at least forward secrecy with respect to sender compromise can be lost completely.

Such a situation could also lead to the reuse of the same KEM shared secret and thus to the reuse of same key-nonce pairs for the AEAD. The AEADs specified in this document are not secure in case of nonce reuse. This attack vector is particularly relevant in authenticated modes because knowledge of the ephemeral randomness is not enough to derive shared_secret in these modes.

One way for applications to mitigate the impacts of bad ephemeral randomness is to combine ephemeral randomness with a local long-term secret that has been generated securely, as described in RFC8937.

Hiding Plaintext Length

AEAD ciphertexts produced by HPKE do not hide the plaintext length. Applications requiring this level of privacy should use a suitable padding mechanism. See tls-esni and RFC8467 for examples of protocol-specific padding policies.

Bidirectional Encryption

HPKE encryption is unidirectional from sender to recipient. Applications that require bidirectional encryption can derive necessary keying material with the Secret Export interface. The type and length of such keying material depends on the application use case.

As an example, if an application needs AEAD encryption from recipient to sender, it can derive a key and nonce from the corresponding HPKE context as follows:

key = context.Export("response key", Nk)
nonce = context.Export("response nonce", Nn)

In this example, the length of each secret is based on the AEAD algorithm used for the corresponding HPKE context.

Note that HPKE’s limitations with regard to sender authentication become limits on recipient authentication in this context. In particular, in the Base mode, there is no authentication of the remote party at all. Even in the Auth mode, where the remote party has proven that they hold a specific private key, this authentication is still subject to Key-Compromise Impersonation, as discussed in kci.

Metadata Protection

The authenticated modes of HPKE (PSK, Auth, AuthPSK) require that the recipient know what key material to use for the sender. This can be signaled in applications by sending the PSK ID (psk_id above) and/or the sender’s public key (pkS). However, these values themselves might be considered sensitive, since in a given application context, they might identify the sender.

An application that wishes to protect these metadata values without requiring further provisioning of keys can use an additional instance of HPKE, using the unauthenticated Base mode. Where the application might have sent (psk_id, pkS, enc, ciphertext) before, it would now send (enc2, ciphertext2, enc, ciphertext), where (enc2, ciphertext2) represent the encryption of the psk_id and pkS values.

The cost of this approach is an additional KEM operation each for the sender and the recipient. A potential lower-cost approach (involving only symmetric operations) would be available if the nonce-protection schemes in BNT19 could be extended to cover other metadata. However, this construction would require further analysis.

hacspec implementation considerations

When defining HPKE in hacspec there are a number of considerations that have an impact on the way the code looks.

The hacspec code is as close to the RFC pseudocode as possible. But some changes are necessary.


hacspec does not allow to draw randomness. It is therefore necessary to pass in randomness every time it is needed.

This approach is pretty close to the way this would be implemented in native Rust where a random-number generator is passed in and used to generate randomness.

Configuration Parameters

The HPKE RFC makes most of the configuration implicit to the functions rather than passing the algorithm identifiers around. Because the hacspec implementation has to know which algorithm to pick, this is of course not possible here.

HPKE hacspec functions take either an HPKEConfig object with all algorithms in it or the specific algorithm identifier needed for the operation.


The HPKE RFC uses, in some cases, names that are impossible to use in hacspec because they are keywords or contain illegal characters. Further does hacspec not support member functions.

We therefore replace . in function calls such as Context.Export with an underscore, i.e. write Context_Export. Keywords such as open are replaced with a semantically equivalent, i.e. HpkeOpen.

Secret bytes

hacspec has the notion of secret integers that can’t be used for certain operations and should enforce secret-independent computation time.

For simplicity the hacspec HPKE implementation uses secret bytes everywhere even if not necessary, e.g. for cipher texts.


While the RFC defines a set of errors it does not always define which errors are raised. For example, it leaves open whether implementations convert errors from the Diffie-Hellman operations into KEM errors (EncapError/DecapError) or not.

With the specific implementation in hacspec here the errors are clearly defined.



A one-byte value indicating the HPKE mode, defined in the following table.


Compute Nonce

Stateful open.

Encryption and Decryption

Secret Export



Increment Sequence

Creating the Encryption Context

“single-shot” secret export receiver

“single-shot” secret export sender

Authentication using both a PSK and an Asymmetric Key - Receiver

Authentication using both a PSK and an Asymmetric Key - Sender

Authentication using an Asymmetric Key - Receiver

Authentication using an Asymmetric Key - Sender

Encryption to a Public Key - Receiver

Encryption to a Public Key - Sender

Authentication using a Pre-Shared Key - Receiver

Authentication using a Pre-Shared Key - Sender

Creating the Encryption Context

Type Definitions