Internet Research Task Force (IRTF) R. Barnes
Request for Comments: 9180 Cisco
Category: Informational K. Bhargavan
ISSN: 2070-1721 B. Lipp
Inria
C. Wood
Cloudflare
February 2022
Hybrid Public Key Encryption
Abstract
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 that
authenticates possession of a pre-shared key and two optional ones
that authenticate possession of a key encapsulation mechanism (KEM)
private key. HPKE works for any combination of an asymmetric 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 (ECDH) key agreement, HMAC-based key
derivation function (HKDF), and SHA2.
This document is a product of the Crypto Forum Research Group (CFRG)
in the IRTF.
Status of This Memo
This document is not an Internet Standards Track specification; it is
published for informational purposes.
This document is a product of the Internet Research Task Force
(IRTF). The IRTF publishes the results of Internet-related research
and development activities. These results might not be suitable for
deployment. This RFC represents the consensus of the Crypto Forum
Research Group of the Internet Research Task Force (IRTF). Documents
approved for publication by the IRSG are not candidates for any level
of Internet Standard; see Section 2 of RFC 7841.
Information about the current status of this document, any errata,
and how to provide feedback on it may be obtained at
https://www.rfc-editor.org/info/rfc9180.
Copyright Notice
Copyright (c) 2022 IETF Trust and the persons identified as the
document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal
Provisions Relating to IETF Documents
(https://trustee.ietf.org/license-info) in effect on the date of
publication of this document. Please review these documents
carefully, as they describe your rights and restrictions with respect
to this document.
Table of Contents
1. Introduction
2. Requirements Notation
3. Notation
4. Cryptographic Dependencies
4.1. DH-Based KEM (DHKEM)
5. Hybrid Public Key Encryption
5.1. Creating the Encryption Context
5.1.1. Encryption to a Public Key
5.1.2. Authentication Using a Pre-Shared Key
5.1.3. Authentication Using an Asymmetric Key
5.1.4. Authentication Using Both a PSK and an Asymmetric Key
5.2. Encryption and Decryption
5.3. Secret Export
6. Single-Shot APIs
6.1. Encryption and Decryption
6.2. Secret Export
7. Algorithm Identifiers
7.1. Key Encapsulation Mechanisms (KEMs)
7.1.1. SerializePublicKey and DeserializePublicKey
7.1.2. SerializePrivateKey and DeserializePrivateKey
7.1.3. DeriveKeyPair
7.1.4. Validation of Inputs and Outputs
7.1.5. Future KEMs
7.2. Key Derivation Functions (KDFs)
7.2.1. Input Length Restrictions
7.3. Authenticated Encryption with Associated Data (AEAD)
Functions
8. API Considerations
8.1. Auxiliary Authenticated Application Information
8.2. Errors
9. Security Considerations
9.1. Security Properties
9.1.1. Key-Compromise Impersonation
9.1.2. Computational Analysis
9.1.3. Post-Quantum Security
9.2. Security Requirements on a KEM Used within HPKE
9.2.1. Encap/Decap Interface
9.2.2. AuthEncap/AuthDecap Interface
9.2.3. KEM Key Reuse
9.3. Security Requirements on a KDF
9.4. Security Requirements on an AEAD
9.5. Pre-Shared Key Recommendations
9.6. Domain Separation
9.7. Application Embedding and Non-Goals
9.7.1. Message Order and Message Loss
9.7.2. Downgrade Prevention
9.7.3. Replay Protection
9.7.4. Forward Secrecy
9.7.5. Bad Ephemeral Randomness
9.7.6. Hiding Plaintext Length
9.8. Bidirectional Encryption
9.9. Metadata Protection
10. Message Encoding
11. IANA Considerations
11.1. KEM Identifiers
11.2. KDF Identifiers
11.3. AEAD Identifiers
12. References
12.1. Normative References
12.2. Informative References
Appendix A. Test Vectors
A.1. DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, AES-128-GCM
A.1.1. Base Setup Information
A.1.2. PSK Setup Information
A.1.3. Auth Setup Information
A.1.4. AuthPSK Setup Information
A.2. DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, ChaCha20Poly1305
A.2.1. Base Setup Information
A.2.2. PSK Setup Information
A.2.3. Auth Setup Information
A.2.4. AuthPSK Setup Information
A.3. DHKEM(P-256, HKDF-SHA256), HKDF-SHA256, AES-128-GCM
A.3.1. Base Setup Information
A.3.2. PSK Setup Information
A.3.3. Auth Setup Information
A.3.4. AuthPSK Setup Information
A.4. DHKEM(P-256, HKDF-SHA256), HKDF-SHA512, AES-128-GCM
A.4.1. Base Setup Information
A.4.2. PSK Setup Information
A.4.3. Auth Setup Information
A.4.4. AuthPSK Setup Information
A.5. DHKEM(P-256, HKDF-SHA256), HKDF-SHA256, ChaCha20Poly1305
A.5.1. Base Setup Information
A.5.2. PSK Setup Information
A.5.3. Auth Setup Information
A.5.4. AuthPSK Setup Information
A.6. DHKEM(P-521, HKDF-SHA512), HKDF-SHA512, AES-256-GCM
A.6.1. Base Setup Information
A.6.2. PSK Setup Information
A.6.3. Auth Setup Information
A.6.4. AuthPSK Setup Information
A.7. DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, Export-Only AEAD
A.7.1. Base Setup Information
A.7.2. PSK Setup Information
A.7.3. Auth Setup Information
A.7.4. AuthPSK Setup Information
Acknowledgements
Authors' Addresses
1. Introduction
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 (HPKE) schemes, 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-ECH].
Currently, there are numerous competing and non-interoperable
standards and variants for hybrid encryption, mostly variants on the
Elliptic Curve Integrated Encryption Scheme (ECIES), including ANSI
X9.63 (ECIES) [ANSI], IEEE 1363a [IEEE1363], ISO/IEC 18033-2 [ISO],
and SECG SEC 1 [SECG]. See [MAEA10] for a thorough comparison. All
these existing schemes have problems, e.g., because they rely on
outdated primitives, lack proofs of indistinguishable (adaptive)
chosen-ciphertext attack (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).
2. Requirements Notation
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in
BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all
capitals, as shown here.
3. Notation
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 that sends an encrypted message.
Recipient (R): Role of entity that 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.
4. Cryptographic Dependencies
HPKE variants rely on the following primitives:
* A key encapsulation mechanism (KEM):
- GenerateKeyPair(): Randomized algorithm to generate a key pair
(skX, pkX).
- DeriveKeyPair(ikm): Deterministic algorithm to derive a key
pair (skX, pkX) from the byte string ikm, where ikm SHOULD have
at least Nsk bytes of entropy (see Section 7.1.3 for
discussion).
- SerializePublicKey(pkX): Produce a byte string of length Npk
encoding the public key pkX.
- DeserializePublicKey(pkXm): Parse a byte string of length Npk
to recover a public key. This function can raise a
DeserializeError error upon pkXm deserialization failure.
- Encap(pkR): Randomized algorithm to generate an ephemeral,
fixed-length symmetric key (the KEM shared secret) and a fixed-
length encapsulation of that key that can be decapsulated by
the holder of the private key corresponding to pkR. This
function can raise an EncapError on encapsulation failure.
- Decap(enc, skR): Deterministic algorithm using the private key
skR to recover the ephemeral symmetric key (the KEM shared
secret) from its encapsulated representation enc. This
function can raise a DecapError on decapsulation failure.
- AuthEncap(pkR, skS) (optional): Same as Encap(), and the
outputs encode an assurance that the KEM shared secret was
generated by the holder of the private key skS.
- AuthDecap(enc, skR, pkS) (optional): Same as Decap(), and the
recipient is assured that the KEM shared secret was generated
by the holder of the private key skS.
- Nsecret: The length in bytes of a KEM shared secret produced by
this KEM.
- Nenc: The length in bytes of an encapsulated key produced by
this KEM.
- Npk: The length in bytes of an encoded public key for this KEM.
- Nsk: The length in bytes of an encoded private key for this
KEM.
* A key derivation function (KDF):
- Extract(salt, ikm): Extract a pseudorandom key of fixed length
Nh bytes from input keying material ikm and an optional byte
string salt.
- Expand(prk, info, L): Expand a pseudorandom key prk using
optional string info into L bytes of output keying material.
- Nh: The output size of the Extract() function in bytes.
* An AEAD encryption algorithm [RFC5116]:
- Seal(key, nonce, aad, pt): Encrypt and authenticate plaintext
pt with associated data aad using symmetric key key and nonce
nonce, yielding ciphertext and tag ct. This function can raise
a MessageLimitReachedError upon failure.
- Open(key, nonce, aad, ct): Decrypt ciphertext and tag ct using
associated data aad with symmetric key key and nonce nonce,
returning plaintext message pt. This function can raise an
OpenError or MessageLimitReachedError upon failure.
- Nk: The length in bytes of a key for this algorithm.
- Nn: The length in bytes of a nonce for this algorithm.
- Nt: The length in bytes of the authentication tag for this
algorithm.
Beyond the above, a KEM MAY also expose the following functions,
whose behavior is detailed in Section 7.1.2:
* SerializePrivateKey(skX): Produce a byte string of length Nsk
encoding the private key skX.
* DeserializePrivateKey(skXm): Parse a byte string of length Nsk to
recover a private key. This function can raise a DeserializeError
error upon skXm deserialization failure.
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 Section 7. Algorithm identifier values are
two bytes long.
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.
The following two functions are defined to facilitate domain
separation of KDF calls as well as context binding:
def LabeledExtract(salt, label, ikm):
labeled_ikm = concat("HPKE-v1", suite_id, label, ikm)
return Extract(salt, labeled_ikm)
def LabeledExpand(prk, label, info, L):
labeled_info = concat(I2OSP(L, 2), "HPKE-v1", suite_id,
label, info)
return Expand(prk, labeled_info, L)
The value of suite_id depends on where the KDF is used; it is assumed
implicit from the implementation and not passed as a parameter. If
used inside a KEM algorithm, suite_id MUST start with "KEM" and
identify this KEM algorithm; if used in the remainder of HPKE, it
MUST start with "HPKE" and identify the entire ciphersuite in use.
See Sections 4.1 and 5.1 for details.
4.1. DH-Based KEM (DHKEM)
Suppose we are given a KDF, and a Diffie-Hellman (DH) group providing
the following operations:
* DH(skX, pkY): Perform a non-interactive Diffie-Hellman exchange
using the private key skX and public key pkY to produce a Diffie-
Hellman shared secret of length Ndh. This function can raise a
ValidationError as described in Section 7.1.4.
* Ndh: The length in bytes of a Diffie-Hellman shared secret
produced by DH().
* Nsk: The length in bytes of a Diffie-Hellman private key.
Then we can construct a KEM that implements the interface defined in
Section 4 called DHKEM(Group, KDF) in the following way, where Group
denotes the Diffie-Hellman group and KDF denotes the KDF. The
function parameters pkR and pkS are deserialized public keys, and enc
is a serialized public key. Since encapsulated keys are Diffie-
Hellman public keys in this KEM algorithm, we use
SerializePublicKey() and DeserializePublicKey() to encode and decode
them, respectively. Npk equals Nenc. GenerateKeyPair() produces a
key pair for the Diffie-Hellman group in use. Section 7.1.3 contains
the DeriveKeyPair() function specification for DHKEMs defined in this
document.
def ExtractAndExpand(dh, kem_context):
eae_prk = LabeledExtract("", "eae_prk", dh)
shared_secret = LabeledExpand(eae_prk, "shared_secret",
kem_context, Nsecret)
return shared_secret
def Encap(pkR):
skE, pkE = GenerateKeyPair()
dh = DH(skE, pkR)
enc = SerializePublicKey(pkE)
pkRm = SerializePublicKey(pkR)
kem_context = concat(enc, pkRm)
shared_secret = ExtractAndExpand(dh, kem_context)
return shared_secret, enc
def Decap(enc, skR):
pkE = DeserializePublicKey(enc)
dh = DH(skR, pkE)
pkRm = SerializePublicKey(pk(skR))
kem_context = concat(enc, pkRm)
shared_secret = ExtractAndExpand(dh, kem_context)
return shared_secret
def AuthEncap(pkR, skS):
skE, pkE = GenerateKeyPair()
dh = concat(DH(skE, pkR), DH(skS, pkR))
enc = SerializePublicKey(pkE)
pkRm = SerializePublicKey(pkR)
pkSm = SerializePublicKey(pk(skS))
kem_context = concat(enc, pkRm, pkSm)
shared_secret = ExtractAndExpand(dh, kem_context)
return shared_secret, enc
def AuthDecap(enc, skR, pkS):
pkE = DeserializePublicKey(enc)
dh = concat(DH(skR, pkE), DH(skR, pkS))
pkRm = SerializePublicKey(pk(skR))
pkSm = SerializePublicKey(pkS)
kem_context = concat(enc, pkRm, pkSm)
shared_secret = ExtractAndExpand(dh, kem_context)
return shared_secret
The implicit suite_id value used within LabeledExtract and
LabeledExpand is defined as follows, where kem_id is defined in
Section 7.1:
suite_id = concat("KEM", I2OSP(kem_id, 2))
The KDF used in DHKEM can be equal to or different from the KDF used
in the remainder of HPKE, depending on the chosen variant.
Implementations MUST make sure to use the constants (Nh) and function
calls (LabeledExtract and LabeledExpand) of the appropriate KDF when
implementing DHKEM. See Section 9.3 for a comment on the choice of a
KDF for the remainder of HPKE, and Section 9.6 for the rationale of
the labels.
For the variants of DHKEM defined in this document, the size Nsecret
of the KEM shared secret is equal to the output length of the hash
function underlying the KDF. For P-256, P-384, and P-521, the size
Ndh of the Diffie-Hellman shared secret is equal to 32, 48, and 66,
respectively, corresponding to the x-coordinate of the resulting
elliptic curve point [IEEE1363]. For X25519 and X448, the size Ndh
is equal to 32 and 56, respectively (see [RFC7748], Section 5).
It is important to note that the AuthEncap() and AuthDecap()
functions of the DHKEM variants defined in this document are
vulnerable to key-compromise impersonation (KCI). This means the
assurance that the KEM shared secret was generated by the holder of
the private key skS does not hold if the recipient private key skR is
compromised. See Section 9.1 for more details.
Senders and recipients MUST validate KEM inputs and outputs as
described in Section 7.1.
5. 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 that authenticates
possession of a pre-shared key, one that authenticates possession of
a KEM private key, and one that 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:
+===============+=======+
| Mode | Value |
+===============+=======+
| mode_base | 0x00 |
+---------------+-------+
| mode_psk | 0x01 |
+---------------+-------+
| mode_auth | 0x02 |
+---------------+-------+
| mode_auth_psk | 0x03 |
+---------------+-------+
Table 1: HPKE 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 Section 5.3.
See Section 5.2 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 the sender to the recipient.
Specification of such a mechanism is left to the application. See
Section 10 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 Section 7.1.5 for more details.
The procedures described in this section are laid out in a Python-
like pseudocode. The algorithms in use are left implicit.
5.1. 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. The key schedule inputs are as follows:
* mode: A one-byte value indicating the HPKE mode, defined in
Table 1.
* shared_secret: A KEM shared secret generated for this transaction.
* info: Application-supplied information (optional; default value
"").
* psk A pre-shared key (PSK) held by both the sender and the
recipient (optional; default value "").
* psk_id: An identifier for the PSK (optional; default value "").
Senders and recipients MUST validate KEM inputs and outputs as
described in Section 7.1.
The psk and psk_id fields MUST appear together or not at all. That
is, if a non-default value is provided for one of them, then the
other MUST be set to a non-default value. This requirement is
encoded in VerifyPSKInputs() below.
The psk, psk_id, and info fields have maximum lengths that depend on
the KDF itself, on the definition of LabeledExtract(), and on the
constant labels used together with them. See Section 7.2.1 for
precise limits on these lengths.
The key, base_nonce, and exporter_secret computed by the key schedule
have the property that they are only known to the holder of the
recipient private key, and the entity that used the KEM to generate
shared_secret and enc.
In the Auth and AuthPSK modes, the recipient is assured that the
sender held the private key skS. This assurance is limited for the
DHKEM variants defined in this document because of key-compromise
impersonation, as described in Sections 4.1 and 9.1. If in the PSK
and AuthPSK modes, the psk and psk_id arguments are provided as
required, then the recipient is assured that the sender held the
corresponding pre-shared key. See Section 9.1 for more details.
The HPKE algorithm identifiers, i.e., the KEM kem_id, KDF kdf_id, and
AEAD aead_id 2-byte code points, as defined in Tables 2, 3, and 5,
respectively, are assumed implicit from the implementation and not
passed as parameters. The implicit suite_id value used within
LabeledExtract and LabeledExpand is defined based on them as follows:
suite_id = concat(
"HPKE",
I2OSP(kem_id, 2),
I2OSP(kdf_id, 2),
I2OSP(aead_id, 2)
)
default_psk = ""
default_psk_id = ""
def VerifyPSKInputs(mode, psk, psk_id):
got_psk = (psk != default_psk)
got_psk_id = (psk_id != default_psk_id)
if got_psk != got_psk_id:
raise Exception("Inconsistent PSK inputs")
if got_psk and (mode in [mode_base, mode_auth]):
raise Exception("PSK input provided when not needed")
if (not got_psk) and (mode in [mode_psk, mode_auth_psk]):
raise Exception("Missing required PSK input")
def KeySchedule<ROLE>(mode, shared_secret, info, psk, psk_id):
VerifyPSKInputs(mode, psk, psk_id)
psk_id_hash = LabeledExtract("", "psk_id_hash", psk_id)
info_hash = LabeledExtract("", "info_hash", info)
key_schedule_context = concat(mode, psk_id_hash, info_hash)
secret = LabeledExtract(shared_secret, "secret", psk)
key = LabeledExpand(secret, "key", key_schedule_context, Nk)
base_nonce = LabeledExpand(secret, "base_nonce",
key_schedule_context, Nn)
exporter_secret = LabeledExpand(secret, "exp",
key_schedule_context, Nh)
return Context<ROLE>(key, base_nonce, 0, exporter_secret)
The ROLE template parameter is either S or R, depending on the role
of sender or recipient, respectively. See Section 5.2 for a
discussion of the key schedule output, including the role-specific
Context structure and its API.
Note that the key_schedule_context construction in KeySchedule() is
equivalent to serializing a structure of the following form in the
TLS presentation syntax:
struct {
uint8 mode;
opaque psk_id_hash[Nh];
opaque info_hash[Nh];
} KeyScheduleContext;
5.1.1. 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. The SetupBaseS() and
SetupBaseR() procedures establish contexts that can be used to
encrypt and decrypt, respectively, for a given private key.
The KEM shared secret is combined via the KDF with information
describing the key exchange, as well as the explicit info parameter
provided by the caller.
The parameter pkR is a public key, and enc is an encapsulated KEM
shared secret.
def SetupBaseS(pkR, info):
shared_secret, enc = Encap(pkR)
return enc, KeyScheduleS(mode_base, shared_secret, info,
default_psk, default_psk_id)
def SetupBaseR(enc, skR, info):
shared_secret = Decap(enc, skR)
return KeyScheduleR(mode_base, shared_secret, info,
default_psk, default_psk_id)
5.1.2. 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. The PSK also
improves confidentiality guarantees in certain adversary models, as
described in more detail in Section 9.1. We assume that both parties
have been provisioned with both the PSK value psk and another byte
string psk_id that is used to identify which PSK should be used.
The primary difference from the base case is that the psk and psk_id
values are used as ikm inputs to the KDF (instead of using the empty
string).
The PSK MUST have at least 32 bytes of entropy and SHOULD be of
length Nh bytes or longer. See Section 9.5 for a more detailed
discussion.
def SetupPSKS(pkR, info, psk, psk_id):
shared_secret, enc = Encap(pkR)
return enc, KeyScheduleS(mode_psk, shared_secret, info,
psk, psk_id)
def SetupPSKR(enc, skR, info, psk, psk_id):
shared_secret = Decap(enc, skR)
return KeyScheduleR(mode_psk, shared_secret, info, psk, psk_id)
5.1.3. 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. This
is because AuthDecap(enc, skR, pkS) produces the correct KEM shared
secret only if the encapsulated value enc was produced by
AuthEncap(pkR, skS), where skS is the private key corresponding to
pkS. In other words, at most two entities (precisely two, in the
case of DHKEM) could have produced this secret, so if the recipient
is at most one, then the sender is the other with overwhelming
probability.
The primary difference from the base case is that the calls to
Encap() and Decap() are replaced with calls to AuthEncap() and
AuthDecap(), which add the sender public key to their internal
context string. The function parameters pkR and pkS are public keys,
and enc is an encapsulated KEM shared secret.
Obviously, this variant can only be used with a KEM that provides
AuthEncap() and AuthDecap() procedures.
This mechanism authenticates only the key pair of the sender, not any
other identifier. If an application wishes to bind HPKE ciphertexts
or exported secrets to another identity for the sender (e.g., an
email address or domain name), then this identifier should be
included in the info parameter to avoid identity misbinding issues
[IMB].
def SetupAuthS(pkR, info, skS):
shared_secret, enc = AuthEncap(pkR, skS)
return enc, KeyScheduleS(mode_auth, shared_secret, info,
default_psk, default_psk_id)
def SetupAuthR(enc, skR, info, pkS):
shared_secret = AuthDecap(enc, skR, pkS)
return KeyScheduleR(mode_auth, shared_secret, info,
default_psk, default_psk_id)
5.1.4. Authentication Using Both a PSK and an Asymmetric Key
This mode is a straightforward combination of the PSK and
authenticated modes. Like the PSK mode, a PSK is provided as input
to the key schedule, and like the authenticated mode, authenticated
KEM variants are used.
def SetupAuthPSKS(pkR, info, psk, psk_id, skS):
shared_secret, enc = AuthEncap(pkR, skS)
return enc, KeyScheduleS(mode_auth_psk, shared_secret, info,
psk, psk_id)
def SetupAuthPSKR(enc, skR, info, psk, psk_id, pkS):
shared_secret = AuthDecap(enc, skR, pkS)
return KeyScheduleR(mode_auth_psk, shared_secret, info,
psk, psk_id)
The PSK MUST have at least 32 bytes of entropy and SHOULD be of
length Nh bytes or longer. See Section 9.5 for a more detailed
discussion.
5.2. Encryption and Decryption
HPKE allows multiple encryption operations to be done based on a
given setup transaction. Since the public key operations involved in
setup are typically more expensive than symmetric encryption or
decryption, this allows applications to amortize the cost of the
public key operations, reducing the overall overhead.
In order to avoid nonce reuse, however, this encryption must be
stateful. Each of the setup procedures above produces a role-
specific context object that stores the AEAD and secret export
parameters. The AEAD parameters consist of:
* The AEAD algorithm in use
* A secret key
* A base nonce base_nonce
* A sequence number (initially 0)
The secret export parameters consist of:
* The HPKE ciphersuite in use and
* An exporter_secret used for the secret export interface (see
Section 5.3)
All these parameters except the AEAD sequence number are constant.
The sequence number provides nonce uniqueness: The nonce used for
each encryption or decryption operation is the result of XORing
base_nonce with the current sequence number, encoded as a big-endian
integer of the same length as base_nonce. Implementations MAY use a
sequence number that is shorter than the nonce length (padding on the
left with zero), but MUST raise an error if the sequence number
overflows. The AEAD algorithm produces ciphertext that is Nt bytes
longer than the plaintext. Nt = 16 for AEAD algorithms defined in
this document.
Encryption is unidirectional from sender to recipient. The sender's
context can encrypt a plaintext pt with associated data aad as
follows:
def ContextS.Seal(aad, pt):
ct = Seal(self.key, self.ComputeNonce(self.seq), aad, pt)
self.IncrementSeq()
return ct
The recipient's context can decrypt a ciphertext ct with associated
data aad as follows:
def ContextR.Open(aad, ct):
pt = Open(self.key, self.ComputeNonce(self.seq), aad, ct)
if pt == OpenError:
raise OpenError
self.IncrementSeq()
return pt
Each encryption or decryption operation increments the sequence
number for the context in use. The per-message nonce and sequence
number increment details are as follows:
def Context<ROLE>.ComputeNonce(seq):
seq_bytes = I2OSP(seq, Nn)
return xor(self.base_nonce, seq_bytes)
def Context<ROLE>.IncrementSeq():
if self.seq >= (1 << (8*Nn)) - 1:
raise MessageLimitReachedError
self.seq += 1
The sender's context MUST NOT be used for decryption. Similarly, the
recipient's context MUST NOT be used for encryption. Higher-level
protocols reusing the HPKE key exchange for more general purposes can
derive separate keying material as needed using use the secret export
interface; see Sections 5.3 and 9.8 for more details.
It is up to the application to ensure that encryptions and
decryptions are done in the proper sequence, so that encryption and
decryption nonces align. If ContextS.Seal() or ContextR.Open() would
cause the seq field to overflow, then the implementation MUST fail
with an error. (In the pseudocode above,
EID 7932 (Verified) is as follows:Section: 5.2
Original Text:
(In the pseudocode below,
Corrected Text:
(In the pseudocode above,
Notes:
The Context<ROLE>.IncrementSeq() pseudocode has already been provided before the last paragraph of section 5.2.
Context<ROLE>.IncrementSeq() fails with an error when seq overflows,
which causes ContextS.Seal() and ContextR.Open() to fail
accordingly.) Note that the internal Seal() and Open() calls inside
correspond to the context's AEAD algorithm.
5.3. Secret Export
HPKE provides an interface for exporting secrets from the encryption
context using a variable-length pseudorandom function (PRF), similar
to the TLS 1.3 exporter interface (see [RFC8446], Section 7.5). This
interface takes as input a context string exporter_context and a
desired length L in bytes, and produces a secret derived from the
internal exporter secret using the corresponding KDF Expand function.
For the KDFs defined in this specification, L has a maximum value of
255*Nh. Future specifications that define new KDFs MUST specify a
bound for L.
The exporter_context field has a maximum length that depends on the
KDF itself, on the definition of LabeledExpand(), and on the constant
labels used together with them. See Section 7.2.1 for precise limits
on this length.
def Context.Export(exporter_context, L):
return LabeledExpand(self.exporter_secret, "sec",
exporter_context, L)
Applications that do not use the encryption API in Section 5.2 can
use the export-only AEAD ID 0xFFFF when computing the key schedule.
Such applications can avoid computing the key and base_nonce values
in the key schedule, as they are not used by the Export interface
described above.
6. Single-Shot APIs
6.1. Encryption and Decryption
In many cases, applications encrypt only a single message
to a recipient's public key. This section provides templates
for HPKE APIs that implement stateless "single-shot"
encryption and decryption using APIs specified in
Sections 5.1 and 5.2:
EID 6941 (Verified) is as follows:Section: 6.1
Original Text:
In many cases, applications encrypt only a single message
to a recipient's public key. This section provides templates
for HPKE APIs that implement stateless "single-shot"
encryption and decryption using APIs specified in
Sections 5.1.1 and 5.2:
Corrected Text:
In many cases, applications encrypt only a single message
to a recipient's public key. This section provides templates
for HPKE APIs that implement stateless "single-shot"
encryption and decryption using APIs specified in
Sections 5.1 and 5.2:
Notes:
5.1.1 -> 5.1: I think the description of the single-shot APIs should refer to the entire HPKE modes hence Section 5.1, instead of Section 5.1.1 which is about the base mode only.
def Seal<MODE>(pkR, info, aad, pt, ...):
enc, ctx = Setup<MODE>S(pkR, info, ...)
ct = ctx.Seal(aad, pt)
return enc, ct
def Open<MODE>(enc, skR, info, aad, ct, ...):
ctx = Setup<MODE>R(enc, skR, info, ...)
return ctx.Open(aad, ct)
The MODE template parameter is one of Base, PSK, Auth, or AuthPSK.
The optional parameters indicated by "..." depend on MODE and may be
empty. For example, SetupBase() has no additional parameters.
SealAuthPSK() and OpenAuthPSK() would be implemented as follows:
def SealAuthPSK(pkR, info, aad, pt, psk, psk_id, skS):
enc, ctx = SetupAuthPSKS(pkR, info, psk, psk_id, skS)
ct = ctx.Seal(aad, pt)
return enc, ct
def OpenAuthPSK(enc, skR, info, aad, ct, psk, psk_id, pkS):
ctx = SetupAuthPSKR(enc, skR, info, psk, psk_id, pkS)
return ctx.Open(aad, ct)
6.2. Secret Export
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 Section 5.3:
def SendExport<MODE>(pkR, info, exporter_context, L, ...):
enc, ctx = Setup<MODE>S(pkR, info, ...)
exported = ctx.Export(exporter_context, L)
return enc, exported
def ReceiveExport<MODE>(enc, skR, info, exporter_context, L, ...):
ctx = Setup<MODE>R(enc, skR, info, ...)
return ctx.Export(exporter_context, L)
As in Section 6.1, the MODE template parameter is one of Base, PSK,
Auth, or AuthPSK. The optional parameters indicated by "..." depend
on MODE and may be empty.
7. 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 Sections 9.2, 9.3, and 9.4, respectively.
7.1. Key Encapsulation Mechanisms (KEMs)
+=======+===============+=========+====+===+===+====+===============+
|Value | KEM | Nsecret |Nenc|Npk|Nsk|Auth| Reference |
+=======+===============+=========+====+===+===+====+===============+
|0x0000 | Reserved | N/A |N/A |N/A|N/A|yes | RFC 9180 |
+-------+---------------+---------+----+---+---+----+---------------+
|0x0010 | DHKEM(P-256, | 32 |65 |65 |32 |yes | [NISTCurves], |
| | HKDF-SHA256) | | | | | | [RFC5869] |
+-------+---------------+---------+----+---+---+----+---------------+
|0x0011 | DHKEM(P-384, | 48 |97 |97 |48 |yes | [NISTCurves], |
| | HKDF-SHA384) | | | | | | [RFC5869] |
+-------+---------------+---------+----+---+---+----+---------------+
|0x0012 | DHKEM(P-521, | 64 |133 |133|66 |yes | [NISTCurves], |
| | HKDF-SHA512) | | | | | | [RFC5869] |
+-------+---------------+---------+----+---+---+----+---------------+
|0x0020 | DHKEM(X25519, | 32 |32 |32 |32 |yes | [RFC5869], |
| | HKDF-SHA256) | | | | | | [RFC7748] |
+-------+---------------+---------+----+---+---+----+---------------+
|0x0021 | DHKEM(X448, | 64 |56 |56 |56 |yes | [RFC5869], |
| | HKDF-SHA512) | | | | | | [RFC7748] |
+-------+---------------+---------+----+---+---+----+---------------+
Table 2: KEM IDs
The Auth column indicates if the KEM algorithm provides the
AuthEncap()/AuthDecap() interface and is therefore suitable for the
Auth and AuthPSK modes. The meaning of all other columns is
explained in Section 11.1. All algorithms are suitable for the PSK
mode.
7.1.1. SerializePublicKey and DeserializePublicKey
For P-256, P-384, and P-521, the SerializePublicKey() function of the
KEM performs the uncompressed Elliptic-Curve-Point-to-Octet-String
conversion according to [SECG]. DeserializePublicKey() performs the
uncompressed Octet-String-to-Elliptic-Curve-Point conversion.
For X25519 and X448, the SerializePublicKey() and
DeserializePublicKey() functions are the identity function, since
these curves already use fixed-length byte strings for public keys.
Some deserialized public keys MUST be validated before they can be
used. See Section 7.1.4 for specifics.
7.1.2. SerializePrivateKey and DeserializePrivateKey
As per [SECG], P-256, P-384, and P-521 private keys are field
elements in the scalar field of the curve being used. For this
section, and for Section 7.1.3, it is assumed that implementors of
ECDH over these curves use an integer representation of private keys
that is compatible with the OS2IP() function.
For P-256, P-384, and P-521, the SerializePrivateKey() function of
the KEM performs the Field-Element-to-Octet-String conversion
according to [SECG]. If the private key is an integer outside the
range [0, order-1], where order is the order of the curve being used,
the private key MUST be reduced to its representative in [0, order-1]
before being serialized. DeserializePrivateKey() performs the Octet-
String-to-Field-Element conversion according to [SECG].
For X25519 and X448, private keys are identical to their byte string
representation, so little processing has to be done. The
SerializePrivateKey() function MUST clamp its output and the
DeserializePrivateKey() function MUST clamp its input, where
_clamping_ refers to the bitwise operations performed on k in the
decodeScalar25519() and decodeScalar448() functions defined in
Section 5 of [RFC7748].
To catch invalid keys early on, implementors of DHKEMs SHOULD check
that deserialized private keys are not equivalent to 0 (mod order),
where order is the order of the DH group. Note that this property is
trivially true for X25519 and X448 groups, since clamped values can
never be 0 (mod order).
7.1.3. DeriveKeyPair
EID 7937 (Verified) is as follows:Section: 7.1.3
Original Text:
For X25519 and X448, the DeriveKeyPair() function applies
a KDF to the input:
<function()>
Corrected Text:
For X25519 and X448, the DeriveKeyPair() function applies
a KDF to the input:
<function()>
The suite_id used implicitly in LabeledExtract() and LabeledExpand()
for DeriveKeyPair(ikm) is derived from the KEM identifier of the
DHKEM in use (see Section 7.1), that is, based on the type of key
pair been generated for that DHKEM type.
Notes:
RFC 9180 dos not specify all the internal values for LabeledExtract(…) and LabeledExpand(…) for DeriveKeyPair(ikm), specifically the suite_id value. These values are required to standardise the DeriveKeyPair(ikm) function, as it is reference in other IETF drafts, such as https://www.ietf.org/archive/id/draft-westerbaan-cfrg-hpke-xyber768d00-02.html#name-derivekeypair-2, and because it is also used in the RFC 9180 KATs: see Appendix A.
Verified on CFRG list by co-author with notes: I looked at MLSpp and mls-rs, and both of those implementations compute `suite_id`, and they both do it in the way this erratum suggests. And since they interoperate with a bunch of other MLS stacks (including on HPKE), I assume this is how people are reading what's there now. But it would be good to be explicit.
The keys that DeriveKeyPair() produces have only as much entropy as
the provided input keying material. For a given KEM, the ikm
parameter given to DeriveKeyPair() SHOULD have length at least Nsk,
and SHOULD have at least Nsk bytes of entropy.
All invocations of KDF functions (such as LabeledExtract or
LabeledExpand) in any DHKEM's DeriveKeyPair() function use the
DHKEM's associated KDF (as opposed to the ciphersuite's KDF).
For P-256, P-384, and P-521, the DeriveKeyPair() function of the KEM
performs rejection sampling over field elements:
def DeriveKeyPair(ikm):
dkp_prk = LabeledExtract("", "dkp_prk", ikm)
sk = 0
counter = 0
while sk == 0 or sk >= order:
if counter > 255:
raise DeriveKeyPairError
bytes = LabeledExpand(dkp_prk, "candidate",
I2OSP(counter, 1), Nsk)
bytes[0] = bytes[0] & bitmask
sk = OS2IP(bytes)
counter = counter + 1
return (sk, pk(sk))
order is the order of the curve being used (see Section D.1.2 of
[NISTCurves]), and is listed below for completeness.
P-256:
0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
P-384:
0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf
581a0db248b0a77aecec196accc52973
P-521:
0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409
bitmask is defined to be 0xFF for P-256 and P-384, and 0x01 for
P-521. The precise likelihood of DeriveKeyPair() failing with
DeriveKeyPairError depends on the group being used, but it is
negligibly small in all cases. See Section 8.2 for information about
dealing with such failures.
For X25519 and X448, the DeriveKeyPair() function applies a KDF to
the input:
def DeriveKeyPair(ikm):
dkp_prk = LabeledExtract("", "dkp_prk", ikm)
sk = LabeledExpand(dkp_prk, "sk", "", Nsk)
return (sk, pk(sk))
7.1.4. Validation of Inputs and Outputs
The following public keys are subject to validation if the group
requires public key validation: the sender MUST validate the
recipient's public key pkR; the recipient MUST validate the ephemeral
public key pkE; in authenticated modes, the recipient MUST validate
the sender's static public key pkS. Validation failure yields a
ValidationError.
For P-256, P-384, and P-521, senders and recipients MUST perform
partial public key validation on all public key inputs, as defined in
Section 5.6.2.3.4 of [keyagreement]. This includes checking that the
coordinates are in the correct range, that the point is on the curve,
and that the point is not the point at infinity. Additionally,
senders and recipients MUST ensure the Diffie-Hellman shared secret
is not the point at infinity.
For X25519 and X448, public keys and Diffie-Hellman outputs MUST be
validated as described in [RFC7748]. In particular, recipients MUST
check whether the Diffie-Hellman shared secret is the all-zero value
and abort if so.
7.1.5. Future KEMs
Section 9.2 lists security requirements on a KEM used within HPKE.
The AuthEncap() and AuthDecap() functions are OPTIONAL. If a KEM
algorithm does not provide them, only the Base and PSK modes of HPKE
are supported. Future specifications that define new KEMs MUST
indicate whether or not Auth and AuthPSK modes are supported.
A KEM algorithm may support different encoding algorithms, with
different output lengths, for KEM public keys. Such KEM algorithms
MUST specify only one encoding algorithm whose output length is Npk.
7.2. Key Derivation Functions (KDFs)
+========+=============+=====+===========+
| Value | KDF | Nh | Reference |
+========+=============+=====+===========+
| 0x0000 | Reserved | N/A | RFC 9180 |
+--------+-------------+-----+-----------+
| 0x0001 | HKDF-SHA256 | 32 | [RFC5869] |
+--------+-------------+-----+-----------+
| 0x0002 | HKDF-SHA384 | 48 | [RFC5869] |
+--------+-------------+-----+-----------+
| 0x0003 | HKDF-SHA512 | 64 | [RFC5869] |
+--------+-------------+-----+-----------+
Table 3: KDF IDs
7.2.1. Input Length Restrictions
This document defines LabeledExtract() and LabeledExpand() based on
the KDFs listed above. These functions add prefixes to their
respective inputs ikm and info before calling the KDF's Extract() and
Expand() functions. This leads to a reduction of the maximum input
length that is available for the inputs psk, psk_id, info,
exporter_context, ikm, i.e., the variable-length parameters provided
by HPKE applications. The following table lists the maximum allowed
lengths of these fields for the KDFs defined in this document, as
inclusive bounds in bytes:
+==================+==============+===============+===============+
| Input | HKDF-SHA256 | HKDF-SHA384 | HKDF-SHA512 |
+==================+==============+===============+===============+
| psk | 2^{61} - 88 | 2^{125} - 152 | 2^{125} - 152 |
+------------------+--------------+---------------+---------------+
| psk_id | 2^{61} - 93 | 2^{125} - 157 | 2^{125} - 157 |
+------------------+--------------+---------------+---------------+
| info | 2^{61} - 91 | 2^{125} - 155 | 2^{125} - 155 |
+------------------+--------------+---------------+---------------+
| exporter_context | 2^{61} - 120 | 2^{125} - 200 | 2^{125} - 216 |
+------------------+--------------+---------------+---------------+
| ikm | 2^{61} - 84 | 2^{125} - 148 | 2^{125} - 148 |
| (DeriveKeyPair) | | | |
+------------------+--------------+---------------+---------------+
Table 4: Application Input Limits
This shows that the limits are only marginally smaller than the
maximum input length of the underlying hash function; these limits
are large and unlikely to be reached in practical applications.
Future specifications that define new KDFs MUST specify bounds for
these variable-length parameters.
The RECOMMENDED limit for these values is 64 bytes. This would
enable interoperability with implementations that statically allocate
memory for these inputs to avoid memory allocations.
The values for psk, psk_id, info, and ikm, which are inputs to
LabeledExtract(), were computed with the following expression:
max_size_hash_input - Nb - size_version_label -
size_suite_id - size_input_label
The value for exporter_context, which is an input to LabeledExpand(),
was computed with the following expression:
max_size_hash_input - Nb - Nh - size_version_label -
size_suite_id - size_input_label - 2 - 1
In these equations, max_size_hash_input is the maximum input length
of the underlying hash function in bytes, Nb is the block size of the
underlying hash function in bytes, size_version_label is the size of
"HPKE-v1" in bytes and equals 7, size_suite_id is the size of the
suite_id in bytes and equals 5 for DHKEM (relevant for ikm) and 10
for the remainder of HPKE (relevant for psk, psk_id, info, and
exporter_context), and size_input_label is the size in bytes of the
label used as parameter to LabeledExtract() or LabeledExpand(), the
maximum of which is 13 across all labels in this document.
7.3. Authenticated Encryption with Associated Data (AEAD) Functions
+========+==================+=====+=====+=====+===========+
| Value | AEAD | Nk | Nn | Nt | Reference |
+========+==================+=====+=====+=====+===========+
| 0x0000 | Reserved | N/A | N/A | N/A | RFC 9180 |
+--------+------------------+-----+-----+-----+-----------+
| 0x0001 | AES-128-GCM | 16 | 12 | 16 | [GCM] |
+--------+------------------+-----+-----+-----+-----------+
| 0x0002 | AES-256-GCM | 32 | 12 | 16 | [GCM] |
+--------+------------------+-----+-----+-----+-----------+
| 0x0003 | ChaCha20Poly1305 | 32 | 12 | 16 | [RFC8439] |
+--------+------------------+-----+-----+-----+-----------+
| 0xFFFF | Export-only | N/A | N/A | N/A | RFC 9180 |
+--------+------------------+-----+-----+-----+-----------+
Table 5: AEAD IDs
The 0xFFFF AEAD ID is reserved for applications that only use the
Export interface; see Section 5.3 for more details.
8. 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.
8.1. 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
Section 6 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.
8.2. Errors
The high-level, public HPKE APIs specified in this document are all
fallible. These include the Setup functions and all encryption
context functions. For example, Decap() can fail if the encapsulated
key enc is invalid, and Open() may fail if ciphertext decryption
fails. The explicit errors generated throughout this specification,
along with the conditions that lead to each error, are as follows:
* ValidationError: KEM input or output validation failure;
Section 4.1.
* DeserializeError: Public or private key deserialization failure;
Section 4.
* EncapError: Encap() failure; Section 4.
* DecapError: Decap() failure; Section 4.
* OpenError: Context AEAD Open() failure; Sections 4 and 5.2.
* MessageLimitReachedError: Context AEAD sequence number overflow;
Sections 4 and 5.2.
* DeriveKeyPairError: Key pair derivation failure; Section 7.1.3.
Implicit errors may also occur. As an example, certain classes of
failures, e.g., malformed recipient public keys, may not yield
explicit errors. For example, for the DHKEM variant described in
this specification, the Encap() algorithm fails when given an invalid
recipient public key. However, other KEM algorithms may not have an
efficient algorithm for verifying the validity of public keys. As a
result, an equivalent error may not manifest until AEAD decryption at
the recipient. As another example, DHKEM's AuthDecap() function will
produce invalid output if given the wrong sender public key. This
error is not detectable until subsequent AEAD decryption.
The errors in this document are meant as a guide for implementors.
They are not an exhaustive list of all the errors an implementation
might emit. For example, future KEMs might have internal failure
cases, or an implementation might run out of memory.
How these errors are expressed in an API or handled by applications
is an implementation-specific detail. For example, some
implementations may abort or panic upon a DeriveKeyPairError failure
given that it only occurs with negligible probability, whereas other
implementations may retry the failed DeriveKeyPair operation. See
Section 7.1.3 for more information. As another example, some
implementations of the DHKEM specified in this document may choose to
transform ValidationError from DH() into an EncapError or DecapError
from Encap() or Decap(), respectively, whereas others may choose to
raise ValidationError unmodified.
Applications using HPKE APIs should not assume that the errors here
are complete, nor should they assume certain classes of errors will
always manifest the same way for all ciphersuites. For example, the
DHKEM specified in this document will emit a DeserializationError or
ValidationError if a KEM public key is invalid. However, a new KEM
might not have an efficient algorithm for determining whether or not
a public key is valid. In this case, an invalid public key might
instead yield an OpenError when trying to decrypt a ciphertext.
9. Security Considerations
9.1. 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]) 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 Section 9.7 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
Section 9.7: no tolerance of message reordering or loss, no downgrade
or replay prevention, no hiding of the plaintext length, and no
protection against bad ephemeral randomness. Section 9.7 suggests
application-level mitigations for some of them.
9.1.1. 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 is 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
Section 7 of [RFC8696] this assumes that the pre-shared key has not
been compromised.
9.1.2. 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-secure 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
Sections 7.2 and 7.1) 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.
+=========+==============+=============+==============+
| Variant | Message Sec. | Export Sec. | Sender Auth. |
+=========+==============+=============+==============+
| Base | Y | Y | N/A |
+---------+--------------+-------------+--------------+
| PSK | Y | Y | Y |
+---------+--------------+-------------+--------------+
| Auth | Y | Y | Y |
+---------+--------------+-------------+--------------+
| AuthPSK | Y | Y | Y |
+---------+--------------+-------------+--------------+
Table 6: HPKE Mode Security Properties
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 the notions of
Outsider-CCA, Insider-CCA, and Outsider-Auth for all Diffie-Hellman
groups specified in this document. It does not fulfill the notion of
Insider-Auth defined in the paper. The analysis
EID 7790 (Verified) is as follows:Section: 9.1.2
Original Text:
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.
Corrected Text:
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 the notions of
Outsider-CCA, Insider-CCA, and Outsider-Auth for all Diffie-Hellman
groups specified in this document. It does not fulfill the notion of
Insider-Auth defined in the paper.
Notes:
The referenced paper defines four notions of security, Outsider-CCA, Insider-CCA, Outsider-Auth, and Insider-Auth. It proves that HPKE meets the first three, but, contrary to the current text of the RFC, it proves that it does *not* meet Insider-Auth security and that this is infeasible for HPKE. This is an important negative security result that should have been highlighted in the RFC.
also provides exact security bounds, under the assumptions 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 nonzero 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.
9.1.3. 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.
9.2. Security Requirements on a KEM Used within HPKE
A KEM used within HPKE MUST allow HPKE to satisfy its desired
security properties described in Section 9.1. Section 9.6 lists
requirements concerning domain separation.
In particular, the KEM shared secret MUST be a uniformly random byte
string of length Nsecret. This means, for instance, that it would
not be sufficient if the KEM shared secret is only uniformly random
as an element of some set prior to its encoding as a byte string.
9.2.1. Encap/Decap Interface
As mentioned in Section 9, [CS01] provides some indications that if
the KEM's Encap()/Decap() interface (which is used in the Base and
PSK modes) is IND-CCA2-secure, HPKE is able to satisfy its desired
security properties. An appropriate definition of IND-CCA2 security
for KEMs can be found in [CS01] and [BHK09].
9.2.2. AuthEncap/AuthDecap Interface
The analysis of HPKE's Auth mode single-shot encryption API in
[ABHKLR20] provides composition theorems that guarantee that HPKE's
Auth mode achieves its desired security properties if the KEM's
AuthEncap()/AuthDecap() interface satisfies multi-user Outsider-CCA,
Outsider-Auth, and Insider-CCA security, as defined in the same
paper.
Intuitively, Outsider-CCA security formalizes confidentiality, and
Outsider-Auth security formalizes authentication of the KEM shared
secret in case none of the sender or recipient private keys are
compromised. Insider-CCA security formalizes confidentiality of the
KEM shared secret in case the sender private key is known or chosen
by the adversary. (If the recipient private key is known or chosen
by the adversary, confidentiality is trivially broken, because then
the adversary knows all secrets on the recipient's side).
An Insider-Auth security notion would formalize authentication of the
KEM shared secret in case the recipient private key is known or
chosen by the adversary. (If the sender private key is known or
chosen by the adversary, it can create KEM ciphertexts in the name of
the sender). Because of the generic attack on an analogous Insider-
Auth security notion of HPKE described in Section 9.1, a definition
of Insider-Auth security for KEMs used within HPKE is not useful.
9.2.3. KEM Key Reuse
An ikm input to DeriveKeyPair() (Section 7.1.3) MUST NOT be reused
elsewhere, in particular not with DeriveKeyPair() of a different KEM.
The randomness used in Encap() and AuthEncap() to generate the KEM
shared secret or its encapsulation MUST NOT be reused elsewhere.
Since a KEM key pair belonging to a sender or recipient works with
all modes, it can be used with multiple modes in parallel. HPKE is
constructed to be secure in such settings due to domain separation
using the suite_id variable. However, there is no formal proof of
security at the time of writing for using multiple modes in parallel;
[HPKEAnalysis] and [ABHKLR20] only analyze isolated modes.
9.3. Security Requirements on a KDF
The choice of the KDF for HPKE SHOULD be made based on the security
level provided by the KEM and, if applicable, by the PSK. The KDF
SHOULD at least have the security level of the KEM and SHOULD at
least have the security level provided by the PSK.
9.4. Security Requirements on an AEAD
All AEADs MUST be IND-CCA2-secure, as is currently true for all AEADs
listed in Section 7.3.
9.5. 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 its 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
distinguishes 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 that 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 the 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] that 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 that are key-committing.
9.6. 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 using prefix-free sets of suite_id values in LabeledExtract() and
LabeledExpand() (KEM... in DHKEM and HPKE... in the remainder of
HPKE). Recall that 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 Section 4, 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.
9.7. 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.
9.7.1. 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 Section 6), 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 associated data 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.
9.7.2. 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.
9.7.3. 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.
9.7.4. 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.
9.7.5. 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].
9.7.6. 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-ECH] and [RFC8467] for examples of
protocol-specific padding policies.
9.8. Bidirectional Encryption
As discussed in Section 5.2, HPKE encryption is unidirectional from
sender to recipient. Applications that require bidirectional
encryption can derive necessary keying material with the secret
export interface (Section 5.3). The type and length of such keying
material depends on the application use case.
As an example, if an application needs AEAD encryption from the
recipient to the 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
Section 9.1.1.
9.9. Metadata Protection
The authenticated modes of HPKE (PSK, Auth, and 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.
10. 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 that 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.
11. IANA Considerations
IANA has created three new registries:
* HPKE KEM Identifiers
* HPKE KDF Identifiers
* HPKE AEAD Identifiers
All these registries are under "Hybrid Public Key Encryption", and
administered under a Specification Required policy [RFC8126].
11.1. KEM Identifiers
The "HPKE KEM Identifiers" registry lists identifiers for key
encapsulation algorithms defined for use with HPKE. These
identifiers are two-byte values, so the maximum possible value is
0xFFFF = 65535.
Template:
Value: The two-byte identifier for the algorithm
KEM: The name of the algorithm
Nsecret: The length in bytes of a KEM shared secret produced by
the algorithm
Nenc: The length in bytes of an encoded encapsulated key produced
by the algorithm
Npk: The length in bytes of an encoded public key for the
algorithm
Nsk: The length in bytes of an encoded private key for the
algorithm
Auth: A boolean indicating if this algorithm provides the
AuthEncap()/AuthDecap() interface
Reference: Where this algorithm is defined
Initial contents: Provided in Table 2
11.2. KDF Identifiers
The "HPKE KDF Identifiers" registry lists identifiers for key
derivation functions defined for use with HPKE. These identifiers
are two-byte values, so the maximum possible value is 0xFFFF = 65535.
Template:
Value: The two-byte identifier for the algorithm
KDF: The name of the algorithm
Nh: The output size of the Extract function in bytes
Reference: Where this algorithm is defined
Initial contents: Provided in Table 3
11.3. AEAD Identifiers
The "HPKE AEAD Identifiers" registry lists identifiers for
authenticated encryption with associated data (AEAD) algorithms
defined for use with HPKE. These identifiers are two-byte values, so
the maximum possible value is 0xFFFF = 65535.
Template:
Value: The two-byte identifier for the algorithm
AEAD: The name of the algorithm
Nk: The length in bytes of a key for this algorithm
Nn: The length in bytes of a nonce for this algorithm
Nt: The length in bytes of an authentication tag for this
algorithm
Reference: Where this algorithm is defined
Initial contents: Provided in Table 5
12. References
12.1. Normative References
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
<https://www.rfc-editor.org/info/rfc2119>.
[RFC5116] McGrew, D., "An Interface and Algorithms for Authenticated
Encryption", RFC 5116, DOI 10.17487/RFC5116, January 2008,
<https://www.rfc-editor.org/info/rfc5116>.
[RFC8017] Moriarty, K., Ed., Kaliski, B., Jonsson, J., and A. Rusch,
"PKCS #1: RSA Cryptography Specifications Version 2.2",
RFC 8017, DOI 10.17487/RFC8017, November 2016,
<https://www.rfc-editor.org/info/rfc8017>.
[RFC8126] Cotton, M., Leiba, B., and T. Narten, "Guidelines for
Writing an IANA Considerations Section in RFCs", BCP 26,
RFC 8126, DOI 10.17487/RFC8126, June 2017,
<https://www.rfc-editor.org/info/rfc8126>.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, <https://www.rfc-editor.org/info/rfc8174>.
12.2. Informative References
[ABHKLR20] Alwen, J., Blanchet, B., Hauck, E., Kiltz, E., Lipp, B.,
and D. Riepel, "Analysing the HPKE Standard", November
2020, <https://eprint.iacr.org/2020/1499>.
[ANSI] American National Standards Institute (ANSI), "ANSI -
X9.63 Public Key Cryptography for the Financial Services
Industry Key Agreement and Key Transport Using Elliptic
Curve Cryptography", November 2001.
[BHK09] Bellare, M., Hofheinz, D., and E. Kiltz, "Subtleties in
the Definition of IND-CCA: When and How Should Challenge-
Decryption be Disallowed?", August 2009,
<https://eprint.iacr.org/2009/418>.
[BJM97] Blake-Wilson, S., Johnson, D., and A. Menezes, "Key
agreement protocols and their security analysis: Extended
Abstract", Crytography and Coding, pp. 30-45,
DOI 10.1007/bfb0024447, June 2005,
<https://doi.org/10.1007/bfb0024447>.
[BNT19] Bellare, M., Ng, R., and B. Tackmann, "Nonces Are Noticed:
AEAD Revisited", August 2019,
<http://dx.doi.org/10.1007/978-3-030-26948-7_9>.
[CS01] Cramer, R. and V. Shoup, "Design and Analysis of Practical
Public-Key Encryption Schemes Secure against Adaptive
Chosen Ciphertext Attack", December 2001,
<https://eprint.iacr.org/2001/108>.
[GAP] Okamoto, T. and D. Pointcheval, "The Gap-Problems: A New
Class of Problems for the Security of Cryptographic
Schemes", ISBN 978-3-540-44586-9, June 2001,
<https://link.springer.com/content/
pdf/10.1007/3-540-44586-2_8.pdf>.
[GCM] Dworkin, M., "Recommendation for Block Cipher Modes of
Operation: Galois/Counter Mode (GCM) and GMAC",
DOI 10.6028/nist.sp.800-38d, SP 800-38D, November 2007,
<https://doi.org/10.6028/nist.sp.800-38d>.
[HHK06] Herranz, J., Hofheinz, D., and E. Kiltz, "Some
(in)sufficient conditions for secure hybrid encryption.",
August 2006, <https://eprint.iacr.org/2006/265>.
[HPKEAnalysis]
Lipp, B., "An Analysis of Hybrid Public Key Encryption",
February 2020, <https://eprint.iacr.org/2020/243>.
[IEEE1363] IEEE, "IEEE Standard Specifications for Public-Key
Cryptography - Amendment 1: Additional Techniques", IEEE
Std 1363a-2004.
[IMB] Diffie, W., Van Oorschot, P., and M. Wiener,
"Authentication and authenticated key exchanges", Designs,
Codes and Cryptography, Vol. 2, pp. 107-125,
DOI 10.1007/bf00124891, June 1992,
<https://doi.org/10.1007/bf00124891>.
[ISO] International Organization for Standardization,
"Information technology - Security techniques - Encryption
algorithms - Part 2: Asymmetric ciphers", ISO/
IEC 18033-2:2006, May 2006.
[keyagreement]
Barker, E., Chen, L., Roginsky, A., Vassilev, A., and R.
Davis, "Recommendation for Pair-Wise Key-Establishment
Schemes Using Discrete Logarithm Cryptography", NIST
Special Publication 800-56A Revision 3,
DOI 10.6028/nist.sp.800-56ar3, April 2018,
<https://doi.org/10.6028/nist.sp.800-56ar3>.
[LGR20] Len, J., Grubbs, P., and T. Ristenpart, "Partitioning
Oracle Attacks".
[MAEA10] Gayoso Martinez, V., Hernandez Alvarez, F., Hernandez
Encinas, L., and C. Sanchez Avila, "A comparison of the
standardized versions of ECIES", October 2010,
<https://ieeexplore.ieee.org/abstract/document/5604194/>.
[MLS-PROTOCOL]
Barnes, R., Beurdouche, B., Robert, R., Millican, J.,
Omara, E., and K. Cohn-Gordon, "The Messaging Layer
Security (MLS) Protocol", Work in Progress, Internet-
Draft, draft-ietf-mls-protocol-12, 11 October 2021,
<https://datatracker.ietf.org/doc/html/draft-ietf-mls-
protocol-12>.
[NaCl] "Public-key authenticated encryption: crypto_box", March
2019, <https://nacl.cr.yp.to/box.html>.
[NISTCurves]
National Institute of Standards and Technology (NIST),
"Digital Signature Standard (DSS)",
DOI 10.6028/nist.fips.186-4, FIPS PUB 186-4, July 2013,
<https://doi.org/10.6028/nist.fips.186-4>.
[RFC1421] Linn, J., "Privacy Enhancement for Internet Electronic
Mail: Part I: Message Encryption and Authentication
Procedures", RFC 1421, DOI 10.17487/RFC1421, February
1993, <https://www.rfc-editor.org/info/rfc1421>.
[RFC5869] Krawczyk, H. and P. Eronen, "HMAC-based Extract-and-Expand
Key Derivation Function (HKDF)", RFC 5869,
DOI 10.17487/RFC5869, May 2010,
<https://www.rfc-editor.org/info/rfc5869>.
[RFC7748] Langley, A., Hamburg, M., and S. Turner, "Elliptic Curves
for Security", RFC 7748, DOI 10.17487/RFC7748, January
2016, <https://www.rfc-editor.org/info/rfc7748>.
[RFC8439] Nir, Y. and A. Langley, "ChaCha20 and Poly1305 for IETF
Protocols", RFC 8439, DOI 10.17487/RFC8439, June 2018,
<https://www.rfc-editor.org/info/rfc8439>.
[RFC8446] Rescorla, E., "The Transport Layer Security (TLS) Protocol
Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018,
<https://www.rfc-editor.org/info/rfc8446>.
[RFC8467] Mayrhofer, A., "Padding Policies for Extension Mechanisms
for DNS (EDNS(0))", RFC 8467, DOI 10.17487/RFC8467,
October 2018, <https://www.rfc-editor.org/info/rfc8467>.
[RFC8696] Housley, R., "Using Pre-Shared Key (PSK) in the
Cryptographic Message Syntax (CMS)", RFC 8696,
DOI 10.17487/RFC8696, December 2019,
<https://www.rfc-editor.org/info/rfc8696>.
[RFC8937] Cremers, C., Garratt, L., Smyshlyaev, S., Sullivan, N.,
and C. Wood, "Randomness Improvements for Security
Protocols", RFC 8937, DOI 10.17487/RFC8937, October 2020,
<https://www.rfc-editor.org/info/rfc8937>.
[SECG] Standards for Efficient Cryptography Group, "SEC 1:
Elliptic Curve Cryptography,", Version 2, May 2009,
<https://secg.org/sec1-v2.pdf>.
[SigncryptionDZ10]
Dent, A. and Y. Zheng, "Practical Signcryption",
Information Security and Cryptography,
DOI 10.1007/978-3-540-89411-7, November 2010,
<https://doi.org/10.1007/978-3-540-89411-7>.
[TestVectors]
"HPKE Test Vectors", <https://github.com/cfrg/draft-irtf-
cfrg-hpke/blob/5f503c564da00b0687b3de75f1dfbdfc4079ad31/
test-vectors.json>.
[TLS-ECH] Rescorla, E., Oku, K., Sullivan, N., and C. A. Wood, "TLS
Encrypted Client Hello", Work in Progress, Internet-Draft,
draft-ietf-tls-esni-14, 13 February 2022,
<https://datatracker.ietf.org/doc/html/draft-ietf-tls-
esni-14>.
Appendix A. Test Vectors
Notes:
The introduction section in Appendix A states that 'Each key pair (skX, pkX) is written in its serialized form, where skXm = SerializePrivateKey(skX) ' For X25519 entries this means that values for skXm should be clamped in the following way to modify the first and last bytes: skXm[0] &= 248; skXm[skXmlen - 1] &= 127; skXm[skXmlen - 1] |= 64; (See Section 5 of https://www.ietf.org/rfc/rfc7748.txt)
Verified. RFC 9180 Section 7.1.2 states SerializePrivateKey() MUST clamp its output for X25519. Appendix A states "skXm = SerializePrivateKey(skX)". The published sk* values are not clamped, violating the spec's MUST requirement. The erratum's corrections apply RFC 7748 Section 5 clamping exactly. Clamped values produce identical public keys (X25519 clamps internally), so this is a documentation consistency fix that enables compliant implementations to match test vectors byte-for-byte. See also: https://github.com/cfrg/draft-irtf-cfrg-hpke/issues/255
EID 7934 (Verified) is as follows:Section: Sections 5.1 and 7.2.1 say:
Original Text:
- Section 5.1:
The psk and psk_id fields MUST appear together or not at all.
The psk, psk_id, and info fields have maximum lengths that depend
on the KDF itself,...
- Section 7.2.1:
The following table lists the maximum allowed lengths of these
fields for the KDFs defined in this document,...
Corrected Text:
- Section 5.1:
[Add a new note]
The info parameter used by HPKE is not related to the optional
string info used by the LabeledExpand or Expand functions detailed
in section 4.
The psk and psk_id parameters MUST appear together or not at all.
The psk, psk_id, and info parameters have maximum lengths that depend
on the KDF itself,...
- Section 7.2.1:
The following table lists the maximum allowed lengths of these
parameters for the KDFs defined in this document,...
Notes:
The reference to the "info" parameter in sections 5.1 and 7.2.1 might be confusing. Thus, I suggest to clearly differentiate between the optional string "info" for the LabeledExpand or Expand functions, whose value is clearly defined by the RFC for each LabeledExpand function used in DHKEM or KeySchedule, and the "info" parameter used by HPKE.
Verified by co-author on CFRG list with additional note: Moreover, I would change all instances of "field" to "parameter", except for those referring to a mathematical field (e.g., "field element").
It seems section 7.2.1 refers to the "info" parameter used by HPKE, as it is referenced from section 5.1. This is the "info" parameter that is used specifically as the key for the "info_hash" LabeledExtract function in KeySchedule.
In section 5.1 the third bullet refer to the HPKE "info" parameter, but the 3rd paragraph after the bullets, as it uses the term "fields", might be confused with the "info" string (or field) used by the LabeledExpand and Expand functions.
Perhaps it would be useful to always use two separate terms along RFC 9180: - the "info" parameter used by HPKE. - the "info" string used by the LabeledExpand and Expand functions.
As a result, I would remove the term "field(s)" from sections 5.1 and 7.2.1, and replace it by parameter(s).
Additionally, I suggest to add a note in section 5.1 to differentiate both, and clarify that section 5.1 and 7.2.1, with the input length restrictions, refer to the "info" parameter, and not to the "info" string.
Verified on CFRG list by co-author with additional note: Moreover, I would change all instances of "field" to "parameter", except for those referring to a mathematical field (e.g., "field element").