TLS: add support for ECH (Encrypted Client Hello)

An EXPERIMENTAL feature used with CURLOPT_ECH and --ech.

Closes #11922
This commit is contained in:
Stephen Farrell 2024-04-04 14:23:35 +01:00 committed by Daniel Stenberg
parent 565d28dc8e
commit a362962b72
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
40 changed files with 3122 additions and 16 deletions

View file

@ -176,6 +176,14 @@ static void free_config_fields(struct OperationConfig *config)
Curl_safefree(config->aws_sigv4);
Curl_safefree(config->proto_str);
Curl_safefree(config->proto_redir_str);
#ifdef USE_ECH
Curl_safefree(config->ech);
config->ech = NULL;
Curl_safefree(config->ech_config);
config->ech_config = NULL;
Curl_safefree(config->ech_public);
config->ech_public = NULL;
#endif
}
void config_free(struct OperationConfig *config)

View file

@ -298,6 +298,12 @@ struct OperationConfig {
struct State state; /* for create_transfer() */
bool rm_partial; /* on error, remove partially written output
files */
#ifdef USE_ECH
char *ech; /* Config set by --ech keywords */
char *ech_config; /* Config set by "--ech esl:" option */
char *ech_public; /* Config set by "--ech pn:" option */
#endif
};
struct GlobalConfig {

View file

@ -123,6 +123,7 @@ typedef enum {
C_DOH_INSECURE,
C_DOH_URL,
C_DUMP_HEADER,
C_ECH,
C_EGD_FILE,
C_ENGINE,
C_EPRT,
@ -404,6 +405,7 @@ static const struct LongShort aliases[]= {
{"doh-insecure", ARG_BOOL, ' ', C_DOH_INSECURE},
{"doh-url" , ARG_STRG, ' ', C_DOH_URL},
{"dump-header", ARG_FILE, 'D', C_DUMP_HEADER},
{"ech", ARG_STRG, ' ', C_ECH},
{"egd-file", ARG_STRG, ' ', C_EGD_FILE},
{"engine", ARG_STRG, ' ', C_ENGINE},
{"eprt", ARG_BOOL, ' ', C_EPRT},
@ -2079,6 +2081,57 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
err = PARAM_ENGINES_REQUESTED;
}
break;
#ifndef USE_ECH
case C_ECH: /* --ech, not implemented by default */
err = PARAM_LIBCURL_DOESNT_SUPPORT;
break;
#else
case C_ECH: /* --ech */
if(strlen(nextarg) > 4 && strncasecompare("pn:", nextarg, 3)) {
/* a public_name */
err = getstr(&config->ech_public, nextarg, DENY_BLANK);
}
else if(strlen(nextarg) > 5 && strncasecompare("ecl:", nextarg, 4)) {
/* an ECHConfigList */
if('@' != *(nextarg + 4)) {
err = getstr(&config->ech_config, nextarg, DENY_BLANK);
}
else {
/* Indirect case: @filename or @- for stdin */
char *tmpcfg = NULL;
FILE *file;
nextarg++; /* skip over '@' */
if(!strcmp("-", nextarg)) {
file = stdin;
}
else {
file = fopen(nextarg, FOPEN_READTEXT);
}
if(!file) {
warnf(global,
"Couldn't read file \"%s\" "
"specified for \"--ech ecl:\" option",
nextarg);
return PARAM_BAD_USE; /* */
}
err = file2string(&tmpcfg, file);
if(file != stdin)
fclose(file);
if(err)
return err;
config->ech_config = aprintf("ecl:%s",tmpcfg);
if(!config->ech_config)
return PARAM_NO_MEM;
free(tmpcfg);
} /* file done */
}
else {
/* Simple case: just a string, with a keyword */
err = getstr(&config->ech, nextarg, DENY_BLANK);
}
break;
#endif
case C_CAPATH: /* --capath */
err = getstr(&config->capath, nextarg, DENY_BLANK);
break;

View file

@ -67,6 +67,7 @@ static const struct category_descriptors categories[] = {
{"telnet", "TELNET protocol options", CURLHELP_TELNET},
{"tftp", "TFTP protocol options", CURLHELP_TFTP},
{"tls", "All TLS/SSL related options", CURLHELP_TLS},
{"ech", "All Encrypted Client Hello (ECH) options", CURLHELP_ECH},
{"upload", "All options for uploads",
CURLHELP_UPLOAD},
{"verbose", "Options related to any kind of command line output of curl",

View file

@ -68,6 +68,7 @@ struct helptxt {
#define CURLHELP_TLS 1u << 22u
#define CURLHELP_UPLOAD 1u << 23u
#define CURLHELP_VERBOSE 1u << 24u
#define CURLHELP_ECH 1u << 25u
extern const struct helptxt helptext[];

View file

@ -168,6 +168,9 @@ const struct helptxt helptext[] = {
{"-D, --dump-header <filename>",
"Write the received headers to <filename>",
CURLHELP_HTTP | CURLHELP_FTP},
{" --ech <config>",
"Configure Encrypted Client Hello (ECH) for use with the TLS session",
CURLHELP_TLS | CURLHELP_ECH},
{" --egd-file <file>",
"EGD socket path for random data",
CURLHELP_TLS},

View file

@ -2187,6 +2187,16 @@ static CURLcode single_transfer(struct GlobalConfig *global,
if(config->hsts)
my_setopt_str(curl, CURLOPT_HSTS, config->hsts);
#ifdef USE_ECH
/* only if enabled in configure */
if(config->ech) /* only if set (optional) */
my_setopt_str(curl, CURLOPT_ECH, config->ech);
if(config->ech_public) /* only if set (optional) */
my_setopt_str(curl, CURLOPT_ECH, config->ech_public);
if(config->ech_config) /* only if set (optional) */
my_setopt_str(curl, CURLOPT_ECH, config->ech_config);
#endif
/* initialize retry vars for loop below */
per->retry_sleep_default = (config->retry_delay) ?
config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */