=pod =head1 NAME OSSL_HPKE_CTX_new, OSSL_HPKE_CTX_free, OSSL_HPKE_encap, OSSL_HPKE_decap, OSSL_HPKE_seal, OSSL_HPKE_open, OSSL_HPKE_export, OSSL_HPKE_suite_check, OSSL_HPKE_str2suite, OSSL_HPKE_keygen, OSSL_HPKE_get_grease_value, OSSL_HPKE_get_ciphertext_size, OSSL_HPKE_get_public_encap_size, OSSL_HPKE_get_recommended_ikmelen, OSSL_HPKE_CTX_set1_psk, OSSL_HPKE_CTX_set1_ikme, OSSL_HPKE_CTX_set1_authpriv, OSSL_HPKE_CTX_set1_authpub, OSSL_HPKE_CTX_get_seq, OSSL_HPKE_CTX_set_seq - Hybrid Public Key Encryption (HPKE) functions =head1 SYNOPSIS #include typedef struct { uint16_t kem_id; uint16_t kdf_id; uint16_t aead_id; } OSSL_HPKE_SUITE; OSSL_HPKE_CTX *OSSL_HPKE_CTX_new(int mode, OSSL_HPKE_SUITE suite, int role, OSSL_LIB_CTX *libctx, const char *propq); void OSSL_HPKE_CTX_free(OSSL_HPKE_CTX *ctx); int OSSL_HPKE_encap(OSSL_HPKE_CTX *ctx, unsigned char *enc, size_t *enclen, const unsigned char *pub, size_t publen, const unsigned char *info, size_t infolen); int OSSL_HPKE_seal(OSSL_HPKE_CTX *ctx, unsigned char *ct, size_t *ctlen, const unsigned char *aad, size_t aadlen, const unsigned char *pt, size_t ptlen); int OSSL_HPKE_keygen(OSSL_HPKE_SUITE suite, unsigned char *pub, size_t *publen, EVP_PKEY **priv, const unsigned char *ikm, size_t ikmlen, OSSL_LIB_CTX *libctx, const char *propq); int OSSL_HPKE_decap(OSSL_HPKE_CTX *ctx, const unsigned char *enc, size_t enclen, EVP_PKEY *recippriv, const unsigned char *info, size_t infolen); int OSSL_HPKE_open(OSSL_HPKE_CTX *ctx, unsigned char *pt, size_t *ptlen, const unsigned char *aad, size_t aadlen, const unsigned char *ct, size_t ctlen); int OSSL_HPKE_export(OSSL_HPKE_CTX *ctx, unsigned char *secret, size_t secretlen, const unsigned char *label, size_t labellen); int OSSL_HPKE_CTX_set1_authpriv(OSSL_HPKE_CTX *ctx, EVP_PKEY *priv); int OSSL_HPKE_CTX_set1_authpub(OSSL_HPKE_CTX *ctx, unsigned char *pub, size_t publen); int OSSL_HPKE_CTX_set1_psk(OSSL_HPKE_CTX *ctx, const char *pskid, const unsigned char *psk, size_t psklen); int OSSL_HPKE_CTX_get_seq(OSSL_HPKE_CTX *ctx, uint64_t *seq); int OSSL_HPKE_CTX_set_seq(OSSL_HPKE_CTX *ctx, uint64_t seq); int OSSL_HPKE_CTX_set1_ikme(OSSL_HPKE_CTX *ctx, const unsigned char *ikme, size_t ikmelen); int OSSL_HPKE_suite_check(OSSL_HPKE_SUITE suite); int OSSL_HPKE_get_grease_value(const OSSL_HPKE_SUITE *suite_in, OSSL_HPKE_SUITE *suite, unsigned char *enc, size_t *enclen, unsigned char *ct, size_t ctlen, OSSL_LIB_CTX *libctx, const char *propq); int OSSL_HPKE_str2suite(const char *str, OSSL_HPKE_SUITE *suite); size_t OSSL_HPKE_get_ciphertext_size(OSSL_HPKE_SUITE suite, size_t clearlen); size_t OSSL_HPKE_get_public_encap_size(OSSL_HPKE_SUITE suite); size_t OSSL_HPKE_get_recommended_ikmelen(OSSL_HPKE_SUITE suite); =head1 DESCRIPTION These functions provide an API for using the form of Hybrid Public Key Encryption (HPKE) defined in RFC9180. Understanding the HPKE specification is likely required before using these APIs. HPKE is used by various other IETF specifications, including the TLS Encrypted Client Hello (ECH) specification and others. HPKE is a standardised, highly flexible construct for encrypting "to" a public key that supports combinations of a key encapsulation method (KEM), a key derivation function (KDF) and an authenticated encryption with additional data (AEAD) algorithm, with optional sender authentication. The sender and a receiver here will generally be using some application or protocol making use of HPKE. For example, with ECH, the sender will be a browser and the receiver will be a web server. =head2 Data Structures B is a structure that holds identifiers for the algorithms used for KEM, KDF and AEAD operations. B is a context that maintains internal state as HPKE operations are carried out. Separate B objects must be used for the sender and receiver. Attempting to use a single context for both will result in errors. =head2 OSSL_HPKE_SUITE Identifiers The identifiers used by B are: The KEM identifier I is one of the following: =over 4 =item 0x10 B =item 0x11 B =item 0x12 B =item 0x20 B =item 0x21 B =back The KDF identifier I is one of the following: =over 4 =item 0x01 B =item 0x02 B =item 0x03 B =back The AEAD identifier I is one of the following: =over 4 =item 0x01 B =item 0x02 B =item 0x03 B =item 0xFFFF B The last identifier above indicates that AEAD operations are not needed. OSSL_HPKE_export() can be used, but OSSL_HPKE_open() and OSSL_HPKE_seal() will return an error if called with a context using that AEAD identifier. =back =head2 HPKE Modes HPKE supports the following variants of Authentication using a mode Identifier: =over 4 =item B, 0x00 Authentication is not used. =item B, 0x01 Authenticates possession of a pre-shared key (PSK). =item B, 0x02 Authenticates possession of a KEM-based sender private key. =item B, 0x03 A combination of B and B. Both the PSK and the senders authentication public/private must be supplied before the encapsulation/decapsulation operation will work. =back For further information related to authentication see L and L. =head2 HPKE Roles HPKE contexts have a role - either sender or receiver. This is used to control which functions can be called and so that senders do not reuse a key and nonce with different plaintexts. OSSL_HPKE_CTX_free(), OSSL_HPKE_export(), OSSL_HPKE_CTX_set1_psk(), and OSSL_HPKE_CTX_get_seq() can be called regardless of role. =over 4 =item B, 0 An I with this role can be used with OSSL_HPKE_encap(), OSSL_HPKE_seal(), OSSL_HPKE_CTX_set1_ikme() and OSSL_HPKE_CTX_set1_authpriv(). =item B, 1 An I with this role can be used with OSSL_HPKE_decap(), OSSL_HPKE_open(), OSSL_HPKE_CTX_set1_authpub() and OSSL_HPKE_CTX_set_seq(). =back Calling a function with an incorrect role set on I will result in an error. =head2 Parameter Size Limits In order to improve interoperability, RFC9180, section 7.2.1 suggests a RECOMMENDED maximum size of 64 octets for various input parameters. In this implementation we apply a limit of 66 octets for the I, I, and I parameters, and for the length of the string I for HPKE functions below. The constant I is defined as the limit of this value. (We chose 66 octets so that we can validate all the test vectors present in RFC9180, Appendix A.) In accordance with RFC9180, section 9.5, we define a constant I with a value of 32 for the minimum length of a pre-shared key, passed in I. While RFC9180 also RECOMMENDS a 64 octet limit for the I parameter, that is not sufficient for TLS Encrypted ClientHello (ECH) processing, so we enforce a limit of I with a value of 1024 as the limit for the I parameter. =head2 Context Construct/Free OSSL_HPKE_CTX_new() creates a B context object used for subsequent HPKE operations, given a I (See L), I (see L) and a I (see L). The I and I are used when fetching algorithms from providers and may be set to NULL. OSSL_HPKE_CTX_free() frees the I B that was created previously by a call to OSSL_HPKE_CTX_new(). =head2 Sender APIs A sender's goal is to use HPKE to encrypt using a public key, via use of a KEM, then a KDF and finally an AEAD. The first step is to encapsulate (using OSSL_HPKE_encap()) the sender's public value using the recipient's public key, (I) and to internally derive secrets. This produces the encapsulated public value (I) to be sent to the recipient in whatever protocol is using HPKE. Having done the encapsulation step, the sender can then make one or more calls to OSSL_HPKE_seal() to encrypt plaintexts using the secret stored within I. OSSL_HPKE_encap() uses the HPKE context I, the recipient public value I of size I, and an optional I parameter of size I, to produce the encapsulated public value I. On input I should contain the maximum size of the I buffer, and returns the output size. An error will occur if the input I is smaller than the value returned from OSSL_HPKE_get_public_encap_size(). I may be used to bind other protocol or application artefacts such as identifiers. Generally, the encapsulated public value I corresponds to a single-use ephemeral private value created as part of the encapsulation process. Only a single call to OSSL_HPKE_encap() is allowed for a given B. OSSL_HPKE_seal() takes the B context I, the plaintext buffer I of size I and optional additional authenticated data buffer I of size I, and returns the ciphertext I of size I. On input I should contain the maximum size of the I buffer, and returns the output size. An error will occur if the input I is smaller than the value returned from OSSL_HPKE_get_public_encap_size(). OSSL_HPKE_encap() must be called before the OSSL_HPKE_seal(). OSSL_HPKE_seal() may be called multiple times, with an internal "nonce" being incremented by one after each call. =head2 Recipient APIs Recipients using HPKE require a typically less ephemeral private value so that the public value can be distributed to potential senders via whatever protocol is using HPKE. For this reason, recipients will generally first generate a key pair and will need to manage their private key value using standard mechanisms outside the scope of this API. Private keys use normal L pointers so normal private key management mechanisms can be used for the relevant values. In order to enable encapsulation, the recipient needs to make it's public value available to the sender. There is no generic HPKE format defined for that - the relevant formatting is intended to be defined by the application/protocols that makes use of HPKE. ECH for example defines an ECHConfig data structure that combines the public value with other ECH data items. Normal library functions must therefore be used to extract the public value in the required format based on the L for the private value. OSSL_HPKE_keygen() provides a way for recipients to generate a key pair based on the HPKE I to be used. It returns a L pointer for the private value I and a encoded public key I of size I. On input I should contain the maximum size of the I buffer, and returns the output size. An error will occur if the input I is too small. The I and I are used when fetching algorithms from providers and may be set to NULL. The HPKE specification also defines a deterministic key generation scheme where the private value is derived from initial keying material (IKM), so OSSL_HPKE_keygen() also has an option to use that scheme, using the I parameter of size I. If either I is NULL or I is zero, then a randomly generated key for the relevant I will be produced. If required I should be greater than or equal to OSSL_HPKE_get_recommended_ikmelen(). OSSL_HPKE_decap() takes as input the sender's encapsulated public value produced by OSSL_HPKE_encap() (I) and the recipient's L pointer (I), and then re-generates the internal secret derived by the sender. As before, an optional I parameter allows binding that derived secret to other application/protocol artefacts. Only a single call to OSSL_HPKE_decap() is allowed for a given B. OSSL_HPKE_open() is used by the recipient to decrypt the ciphertext I of size I using the I and additional authenticated data I of size I, to produce the plaintext I of size I. On input I should contain the maximum size of the I buffer, and returns the output size. A I buffer that is the same size as the I buffer will suffice - generally the plaintext output will be a little smaller than the ciphertext input. An error will occur if the input I is too small. OSSL_HPKE_open() may be called multiple times, but as with OSSL_HPKE_seal() there is an internally incrementing nonce value so ciphertexts need to be presented in the same order as used by the OSSL_HPKE_seal(). See L if you need to process multiple ciphertexts in a different order. =head2 Exporting Secrets HPKE defines a way to produce exported secrets for use by the application. OSSL_HPKE_export() takes as input the B, and an application supplied label I