SSL_SET1_ECHSTORE(3SSL)
๐ธ NAME ๐ธ
okay so this page is SSL_SET1_ECHSTORE(3SSL) from OpenSSL, and it's giving Encrypted Client Hello (ECH) functions โจ the whole squad is here: SSL_set1_echstore, OSSL_ECHSTORE_new, OSSL_ECHSTORE_free, OSSL_ECHSTORE_new_config, OSSL_ECHSTORE_write_pem, OSSL_ECHSTORE_read_echconfiglist, OSSL_ECHSTORE_get1_info, OSSL_ECHSTORE_downselect, OSSL_ECHSTORE_set1_key_and_read_pem, OSSL_ECHSTORE_read_pem, OSSL_ECHSTORE_num_entries, OSSL_ECHSTORE_num_keys, OSSL_ECHSTORE_flush_keys, SSL_CTX_set1_echstore, SSL_CTX_get1_echstore, SSL_get1_echstore, SSL_ech_set1_server_names, SSL_ech_set1_outer_server_name, SSL_ech_set1_outer_alpn_protos, SSL_ech_get1_status, SSL_ech_set1_grease_suite, SSL_ech_set_grease_type, SSL_ech_set_callback, SSL_ech_get1_retry_config, SSL_CTX_ech_set1_outer_alpn_protos, SSL_CTX_ech_set_callback, SSL_set1_ech_config_list โ all the Encrypted Client Hello (ECH) functions ๐
โจ SYNOPSIS โจ
#include <openssl/ech.h>
OSSL_ECHSTORE *OSSL_ECHSTORE_new(OSSL_LIB_CTX *libctx, const char *propq);
void OSSL_ECHSTORE_free(OSSL_ECHSTORE *es);
int OSSL_ECHSTORE_new_config(OSSL_ECHSTORE *es,
uint16_t echversion, uint16_t max_name_length,
const char *public_name, OSSL_HPKE_SUITE suite);
int OSSL_ECHSTORE_write_pem(OSSL_ECHSTORE *es, int index, BIO *out);
int OSSL_ECHSTORE_read_echconfiglist(OSSL_ECHSTORE *es, BIO *in);
int OSSL_ECHSTORE_get1_info(OSSL_ECHSTORE *es, int index, time_t *loaded_secs,
char **public_name, char **echconfig,
int *has_private, int *for_retry);
int OSSL_ECHSTORE_downselect(OSSL_ECHSTORE *es, int index);
int OSSL_ECHSTORE_set1_key_and_read_pem(OSSL_ECHSTORE *es, EVP_PKEY *priv,
BIO *in, int for_retry);
int OSSL_ECHSTORE_read_pem(OSSL_ECHSTORE *es, BIO *in, int for_retry);
int OSSL_ECHSTORE_num_entries(OSSL_ECHSTORE *es, int *numentries);
int OSSL_ECHSTORE_num_keys(OSSL_ECHSTORE *es, int *numkeys);
int OSSL_ECHSTORE_flush_keys(OSSL_ECHSTORE *es, time_t age);
int SSL_CTX_set1_echstore(SSL_CTX *ctx, OSSL_ECHSTORE *es);
int SSL_set1_echstore(SSL *s, OSSL_ECHSTORE *es);
OSSL_ECHSTORE *SSL_CTX_get1_echstore(const SSL_CTX *ctx);
OSSL_ECHSTORE *SSL_get1_echstore(const SSL *s);
int SSL_ech_set1_server_names(SSL *s, const char *inner_name,
const char *outer_name, int no_outer);
int SSL_ech_set1_outer_server_name(SSL *s, const char *outer_name, int no_outer);
int SSL_ech_set1_outer_alpn_protos(SSL *s, const unsigned char *protos,
const size_t protos_len);
int SSL_ech_get1_status(SSL *s, char **inner_sni, char **outer_sni);
int SSL_ech_set1_grease_suite(SSL *s, const char *suite);
int SSL_ech_set_grease_type(SSL *s, uint16_t type);
void SSL_ech_set_callback(SSL *s, SSL_ech_cb_func f);
int SSL_ech_get1_retry_config(SSL *s, unsigned char **ec, size_t *eclen);
void SSL_CTX_ech_set_callback(SSL_CTX *ctx, SSL_ech_cb_func f);
int SSL_CTX_ech_set1_outer_alpn_protos(SSL_CTX *ctx,
const unsigned char *protos,
const size_t protos_len);
int SSL_set1_ech_config_list(SSL *ssl, const uint8_t *ecl, size_t ecl_len);
๐ DESCRIPTION ๐
okay so the whole concept: Encrypted Client Hello (ECH) APIs described here are built around the idea of an OSSL_ECHSTORE that holds ECH configuration information relevant for an SSL_CTX or SSL connection. literally the main character of this whole page ๐โโ๏ธ
heads up bestie: this release only supports ECH shared-mode and has no support for ECH split-mode. periodt, that's the limit.
๐ฆ OSSL_ECHSTORE APIs ๐ฆ
The externally opaque type OSSL_ECHSTORE lets applications create and manage ECHConfigList values, ECH private keys and associated meta-data. The external APIs using OSSL_ECHSTORE are:
OSSL_ECHSTORE_new()andOSSL_ECHSTORE_free()โ create and free the internal storage required. ๐OSSL_ECHSTORE_new_config()โ lets the caller create a new private key value and a related "singleton" ECHConfigList structure. ("Singleton" meaning the ECHConfigList only contains one public key.) Theechversionis the ECHConfig version to use, which is typicallyOSSL_ECH_CURRENT_VERSIONwith a value of0xfe0d. Themax_name_lengthspecifies the maximum known DNS name length that will be present in an ECH extension (if known) and is used for ECH padding but should typically be zero, to indicate no known maximum. Thepublic_nameis the DNS name to use in the ECHConfigpublic_namefield, and thesuitespecifies theOSSL_HPKE_suiteto use (seeOSSL_HPKE_CTX_new(3)for details.) โจOSSL_ECHSTORE_write_pem()โ allows the caller to produce an ECH PEM data structure (conforming to the ECH PEM file format) from theOSSL_ECHSTOREentry identified by theindex. (An index ofOSSL_ECHSTORE_LASTwill select the last entry. An index ofOSSL_ECHSTORE_ALLwill output all public values, and no private values, otherwise the index selects a specific entry with zero selecting the first entry.) The output will be written to theoutBIO. ๐- The
OSSL_ECHSTOREAPIs above will typically be used via the"openssl ech"command line tool. ๐ OSSL_ECHSTORE_read_echconfiglist()โ reads from theinBIO and parses a base64-encoded ECHConfigList value normally found in the"ech="SvcParamKey present in an SVCB or HTTPS RR retrieved from the DNS. The resulting set of ECHConfig values, are associated with theesstore, and can then be associated with anSSL_CTXorSSLstructure for TLS client connections. ๐- Input BIO values (such as
inabove) must not use unterminated BIO's. ๐ซ OSSL_ECHSTORE_get1_info()โ queries the store provided byesand finds the entry within the store specified byindex. The number of seconds since that entry was first generated or loaded/decoded is stored inloaded_secs, and the related public name is stored in*public_name. This function also outputs a string useful for logging or display (as described in the"String form of ECHConfig"section) and stores that in*echconfig. It is the callers responsibility to free the strings returned inpublic_nameandechconfigafter they have been returned. If the specified entry has a private key associated with it thenhas_privatewill be set to1, otherwise it will be set to0. Similarly if the ECHConfig for this entry will be included in"retry-configs"then thefor_retrywill be set to1or zero if that ECHConfig will not be included in retry-configs. ๐- The ECH fallback scheme involving retry-configs is described in Section 6.1.1 of RFC 9849. ๐
OSSL_ECHSTORE_downselect()โ provides the caller a way to select one particular ECHConfig value based on the zero-basedindexfrom those stored in thees, discarding the rest. This can be used by a client (viaopenssl-ech(1)andopenssl-s_client(1)or equivalent) to pick a specific ECHConfig to use in a TLS connection. ๐ฏOSSL_ECHSTORE_set1_key_and_read_pem()andOSSL_ECHSTORE_read_pem()โ can be used to load a private key value and associated ECHConfigList from the BIOininto anOSSL_ECHSTOREstructure. The former function pairs a previously-loaded private key (inEVP_PKEYformat) with an associated base64-encoded ECHConfigList in theinBIO. The latter function reads both from an ECH PEM file. Those can be used (by servers) to enable ECH for anSSL_CTXorSSLconnection. In addition to loading those values, the application can also indicate viafor_retrywhich ECHConfig values are to be included in theretry_configsfallback scheme defined by the ECH protocol. ๐- An ECH PEM file may contain a private key and an ECHConfigList with more than one ECHConfig, for example if different public keys,
public_namevalues, or AEAD/KDF settings are to be supported. When such a file is read, the resultingOSSL_ECHSTOREwill contain one entry for each ECHConfig in the ECHConfigList, so will be presented to applications as a set of "singleton" ECHConfig values, with the private key associated with each matching public key value. ๐ง OSSL_ECHSTORE_num_entries()andOSSL_ECHSTORE_num_keys()โ allow an application to see how many ECH configs (innumentries) and private keys (innumkeys) are present in theesOSSL_ECHSTOREstore. ๐ขOSSL_ECHSTORE_flush_keys()โ allows a server to flush keys fromesthat were loaded more thanageseconds ago. The general model is that a server can maintain anOSSL_ECHSTOREinto which it periodically loads the "latest" set of keys, e.g. hourly, and also discards the keys that are too old, e.g. more than 3 hours old. This allows for more robust private key management even if public key distribution suffers temporary failures. ๐ฐ๏ธSSL_CTX_set1_echstore()andSSL_set1_echstore()โ allow clients and servers to associatees(anOSSL_ECHSTORE) with anSSL_CTXorSSLstructure. ECH will be enabled for the relevantSSL_CTXorSSLconnection when these functions succeed. Any previously associatedOSSL_ECHSTOREwill be freed viaOSSL_ECHSTORE_free(). Internally,OSSL_ECHSTOREvalues within anSSL_CTXorSSLconnection are deep-copied, and are not refcounted. ๐ซถSSL_CTX_get1_echstore()andSSL_get1_echstore()โ provide access to theOSSL_ECHSTOREassociated with anSSL_CTXorSSLconnection. The returnedOSSL_ECHSTOREcan be modified and then re-associated with anSSL_CTXorSSLconnection. ๐
๐ Client ECH Controls ๐
SSL_set1_ech_config_list()โ allows clients to setup ECH by associating an ECHConfigList,eclwith anSSLconnections. This is compatible with current BoringSSL APIs, allowing for smaller code changes for clients that support OpenSSL or BoringSSL. Note that the inputeclhere for OpenSSL can be either base64 or binary encoded, but for BoringSSL it must be binary encoded. ๐ฎSSL_ech_set1_server_names()andSSL_ech_set1_outer_server_name()โ allow clients to more directly control the values to be used for inner and outer Server Name Indication (SNI) values for anSSLconnection,s. Theinner_nameprovided will be used as withSSL_set_tlsext_host_name(3)to populate the SNI value for the inner ClientHello. Theouter_name, if non-NULL, can over-ride thepublic_namefield of the ECHConfig used for the connection. Theno_outerinput allows a client to emit an outer ClientHello with no SNI at all. Providing aNULLfor theouter_namemeans to send thepublic_nameprovided from the ECHConfigList unless theno_outerprovided has the value1. ๐- If a client has called
SSL_CTX_set_alpn_protos(3)orSSL_set_alpn_protos(3)then the ALPN value will be the same in the inner and outer ClientHello messages.SSL_ech_set1_outer_alpn_protos()andSSL_CTX_ech_set1_outer_alpn_protos()allow clients to set a specific value for the ALPN sent in the outer ClientHello of theSSLconnection,s. Theprotosandprotos_leninputs must be provided as forSSL_set_alpn_protos(3). ๐
So if a client attempts ECH but it fails, or it sends an ECH-GREASE'd ClientHello to a server that does support ECH, then that server might hit the client back with a set of ECH retry-config values that the client could lowkey choose to use in a later connection. The client can spot this whole situation if SSL_ech_get1_status() for the SSL connection s returns a status of SSL_ECH_STATUS_GREASE_ECH, and then it can grab the ECH retry config values via SSL_ech_get1_retry_config() โ and the ec value it hands back will contain a binary-encoded ECHConfigList of length eclen. bestie that's the tea ๐ซ
Now if a client makes a real attempt at ECH and it flops, the server can also return retry config values, and those are usable if they were authenticated under the outer SNI used for the session. In this case the TLS session fails, returning the error SSL_R_ECH_REQUIRED, and the ECH status of SSL_ECH_STATUS_FAILED_ECH is basically the server saying "retry config values are available, come get them" via SSL_ech_get1_retry_config(). The application can then choose to use those in a subsequent attempt to establish a TLS session with the server. But if the TLS session fails for some other reason (e.g. a bad server Finished message), then the retry config values will not be returned to the application. no crumbs left, sorry ๐ญ
"GREASEing" (defined in RFC8701) is a mechanism intended to discourage protocol ossification that can be used for ECH. slay for anti-ossification ๐
SSL_ech_set1_grease_suite()โ lets a client GREASE ECH for the SSL connection s, using a specificOSSL_HPKE_SUITEas the value for suite (seeOSSL_HPKE_get_grease_value(3)).SSL_ech_set_grease_type()โ lets a client add a GREASE'd ECH for the SSL connection s, using the specified ClientHello extension number type.
Clients and servers can query the status of ECH for an SSL connection s using the SSL_ech_get1_status() function. And since SNI handling is core to ECH, this will also return the inner_sni and outer_sni values that are gonna be used (or that were used) as well as a status code as the return value. These name values must be freed by the caller โ yes, you, bestie, that's on you ๐ซถ. The various status values this function returns are all described down in "Constants".
๐ Callback Functions ๐
Applications can set a callback function that gets called when the outcome from an attempt at ECH has been determined. On the server, that happens early, as part of construction of the ServerHello message. On the client, the callback happens after the ServerHello has been processed. And in the event of HelloRetryRequest, the callback only gets triggered when processing the second ServerHello. The callback function will be triggered even if the client is only GREASEing. periodt ๐
The callback function prototype is:
typedef unsigned int (*SSL_ech_cb_func)(SSL *s, const char *str);
To set a callback function for the SSL connection s, use SSL_ech_set_callback() or SSL_CTX_ech_set_callback() for a SSL_CTX ctx โ the f input should match the above prototype. matching game, but make it C โจ
When the callback function is called, the str will point at a string intended for logging that describes the state of ECH processing. Applications should NOT attempt to parse that string, because the value depends on compile time settings, local configuration and the specific processing that happened prior to the callback. Applications that need to branch based on the outcome of ECH processing should instead make a call to SSL_ech_get1_status() from within their callback function. iykyk ๐ฅ
An example string str as seen on a client might be:
ech_attempted=1
ech_attempted_type=0xfe0d
ech_atttempted_cid=0x5d
ech_done=1
ech_grease=0
ech_returned_len=0
ech_backend=0
ech_success=1
2 ECHConfig values loaded
cfg(0): [fe0d,5d,cover.defo.ie,[0020,0001,0001],190984309c1a24cb944c005eb79d9c72ca9a4a979194b553dfd0bffc6b5c152d,00,00]
cfg(1): [fe0d,fd,cover.defo.ie,[0020,0001,0001],46dd4e2c81bb15ef9d194c99b86983844e2a1387e4fb7e7d3b8d368c8e1b4d2a,00,00]
๐ Constants ๐
Some externally visible limits:
OSSL_ECH_MAX_PAYLOAD_LEN1500 โ maximum length of an ECH ciphertext to en/decodeOSSL_ECH_MIN_ECHCONFIG_LEN32 โ minimum length of an encoded ECHConfigOSSL_ECH_MAX_ECHCONFIG_LEN1500 โ maximum length of an encoded ECHConfigOSSL_ECH_MAX_ECHCONFIGEXT_LEN512 โ maximum length of an ECHConfig extensionOSSL_ECH_MAX_MAXNAMELEN255 โ maximum for ECHConfig max name lengthOSSL_ECH_MAX_PUBLICNAME255 โ maximum length of an ECHConfig public nameOSSL_ECH_MAX_ALPN_LEN255 โ maximum overall length of an ALPNOSSL_ECH_OUTERS_MAX20 โ maximum number of extensions compressed via outer-extsOSSL_ECH_ALLEXTS_MAX32 โ maximum total number of extensions allowed
ECH version โ the only supported version is 0xfe0d currently. one version ate and left no crumbs ๐
OSSL_ECH_RFC9849_VERSION0xfe0dโ official ECHConfig versionOSSL_ECH_CURRENT_VERSIONOSSL_ECH_RFC9849_VERSION
Values for for_retry
SSL_ECH_USE_FOR_RETRY1 โ use corresponding ECHConfig valuesSSL_ECH_NOT_FOR_RETRY0 โ don't use corresponding ECHConfig values
Indexing an OSSL_ECHSTORE
OSSL_ECHSTORE_LAST,-1โ select the last entry in the storeOSSL_ECHSTORE_ALL,-2โ select all entries in the store, e.g. to print public values
๐ String form of ECHConfig ๐
OSSL_ECHSTORE_get1_info() returns the string form of the ECH public name plus a string describing the content of the ECHConfig, for example:
[fe0d,4d,example.com,[0020,0001,0002],c103d20dddce9b4445829bf01f5b533b728bfa0ebe3a97da33574bc096bb846e,00,00]
In the order presented in the example above: the version is 0xfe0d, the config_id is 0x4d, the public_name is "example.com", there is one ciphersuite with a kem-id of 0x20, kdf-id is 0x01 and aead-id is 0x02; the public_key is the hexadecimal string "c1...6e", the max-name-length is 0x00 and the extensions length is 0x00 since no ECHConfig extensions are present. decode the vibe, slay ๐โจ
โจ RETURN VALUES โจ
SSL_set1_echstore(), SSL_set1_ech_config_list(), OSSL_ECHSTORE_new_config(), OSSL_ECHSTORE_write_pem(), OSSL_ECHSTORE_read_echconfiglist(), OSSL_ECHSTORE_get1_info(), OSSL_ECHSTORE_downselect(), OSSL_ECHSTORE_set1_key_and_read_pem(), OSSL_ECHSTORE_read_pem(), OSSL_ECHSTORE_num_keys(), OSSL_ECHSTORE_num_entries(), OSSL_ECHSTORE_flush_keys(), SSL_CTX_set1_echstore(), SSL_ech_set_server_names(), SSL_ech_set_outer_server_name(), SSL_ech_set_outer_alpn_protos(), SSL_ech_set_grease_suite(), SSL_ech_set_grease_type(), SSL_ech_get_retry_config() and SSL_CTX_ech_set1_outer_alpn_protos() all return zero on error and one on success.
SSL_ech_get1_status() returns one of the following values:
SSL_ECH_STATUS_BACKEND4โ ECH backend: saw anech_is_innerSSL_ECH_STATUS_GREASE_ECH3โ GREASEd and got an ECH in returnSSL_ECH_STATUS_GREASE2โ ECH GREASE happenedSSL_ECH_STATUS_SUCCESS1โ SuccessSSL_ECH_STATUS_FAILED0โ Some internal or protocol errorSSL_ECH_STATUS_BAD_CALL-100โ Some in/out arguments wereNULLSSL_ECH_STATUS_NOT_TRIED-101โ ECH wasn't attemptedSSL_ECH_STATUS_BAD_NAME-102โ ECH ok but server or client cert badSSL_ECH_STATUS_NOT_CONFIGURED-103โ ECH wasn't configuredSSL_ECH_STATUS_FAILED_ECH-105โ We tried, failed and got an ECH, from a verified nameSSL_ECH_STATUS_FAILED_ECH_BAD_NAME-106โ We tried, failed and got an ECH, from a bad name
SSL_ech_set_callback(), SSL_CTX_ech_set_callback() and OSSL_ECHSTORE_free() have no return value. they just do their thing and dip ๐ฆ
SSL_CTX_get1_echstore(), SSL_get1_echstore() and OSSL_ECHSTORE_new() return a pointer to an <cyan>OSSL_ECHSTORE</cyan>.
โ ๏ธ bestie listen โ note thatSSL_CTX_ech_set1_outer_alpn_protos()andSSL_ech_set1_outer_alpn_protos()return zero on error and 1 on success. This is in contrast toSSL_CTX_set_alpn_protos(3)andSSL_set_alpn_protos(3)which (unusually for OpenSSL) return 0 on success and 1 on error. yeah the polarity is flipped, don't get got ๐
๐ฆ SEE ALSO ๐ฆ
- Encrypted ClientHello specification: <https://datatracker.ietf.org/doc/rfc9849>
- ECH PEM file format specification: <https://datatracker.ietf.org/doc/rfc9934>
- RFC8701: <https://datatracker.ietf.org/doc/rfc8701>
ECH command line options are described in the manual pages for openssl-s_client(1), openssl-s_server(1) and openssl-ech(1). go read the whole fam ๐
๐ HISTORY ๐
The functionality described here was added in OpenSSL 4.0. new era unlocked โญ
๐ COPYRIGHT ๐
Copyright 2025-2026 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy in the file LICENSE in the source distribution or at <https://www.openssl.org/source/license.html>.
and that's the whole page bestie, you made it to the end ๐ this was version 4.0.3 of the manual, dated 2026-09-29 โ go forth and ECH responsibly ๐ ๐ธ๐
๐ฆ where this page comes from
- ubuntu stonking: libssl-doc 4.0.3-1ubuntu1 ยท Apache-2.0
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms