mirror of https://github.com/openssl/openssl.git
278 lines
9.2 KiB
C
278 lines
9.2 KiB
C
/*
|
|
* Copyright 2024 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
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "apps.h"
|
|
#include "progs.h"
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/bio.h>
|
|
#include <openssl/evp.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/bn.h>
|
|
#include <openssl/pem.h>
|
|
#include <openssl/rand.h>
|
|
#include <openssl/hpke.h>
|
|
|
|
#include <openssl/objects.h>
|
|
#include <openssl/x509.h>
|
|
|
|
#ifndef OPENSSL_NO_ECH
|
|
|
|
# define OSSL_ECH_KEYGEN_MODE 0 /* default: generate a key pair/ECHConfig */
|
|
# define OSSL_ECH_SELPRINT_MODE 1 /* we can print/down-select ECHConfigList */
|
|
# define OSSL_ECH_MAXINFILES 5 /* we'll only take this many inputs */
|
|
|
|
typedef enum OPTION_choice {
|
|
/* standard openssl options */
|
|
OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, OPT_VERBOSE, OPT_TEXT,
|
|
OPT_OUT, OPT_IN,
|
|
/* ECHConfig specifics */
|
|
OPT_PUBLICNAME, OPT_ECHVERSION,
|
|
OPT_MAXNAMELENGTH, OPT_HPKESUITE,
|
|
OPT_SELECT
|
|
} OPTION_CHOICE;
|
|
|
|
const OPTIONS ech_options[] = {
|
|
OPT_SECTION("General options"),
|
|
{"help", OPT_HELP, '-', "Display this summary"},
|
|
{"verbose", OPT_VERBOSE, '-', "Provide additional output"},
|
|
{"text", OPT_TEXT, '-', "Provide human-readable output"},
|
|
OPT_SECTION("Key generation"),
|
|
{"out", OPT_OUT, '>',
|
|
"Private key and/or ECHConfig [default: echconfig.pem]"},
|
|
{"public_name", OPT_PUBLICNAME, 's', "public_name value"},
|
|
{"max_name_len", OPT_MAXNAMELENGTH, 'n',
|
|
"Maximum host name length value [default: 0]"},
|
|
{"suite", OPT_HPKESUITE, 's', "HPKE ciphersuite: e.g. \"0x20,1,3\""},
|
|
{"ech_version", OPT_ECHVERSION, 'n',
|
|
"ECHConfig version [default: 0xff0d (13)]"},
|
|
OPT_SECTION("ECH PEM file downselect/display"),
|
|
{"in", OPT_IN, '<', "An ECH PEM file"},
|
|
{"select", OPT_SELECT, 'n', "Downselect to the numbered ECH config"},
|
|
{NULL}
|
|
};
|
|
|
|
/**
|
|
* @brief map version string like 0xff01 or 65291 to uint16_t
|
|
* @param arg is the version string, from command line
|
|
* @return is the uint16_t value (with zero for error cases)
|
|
*/
|
|
static uint16_t verstr2us(char *arg)
|
|
{
|
|
long lv = strtol(arg, NULL, 0);
|
|
uint16_t rv = 0;
|
|
|
|
if (lv < 0xffff && lv > 0)
|
|
rv = (uint16_t)lv;
|
|
return rv;
|
|
}
|
|
|
|
int ech_main(int argc, char **argv)
|
|
{
|
|
char *prog = NULL;
|
|
OPTION_CHOICE o;
|
|
int i, rv = 1, verbose = 0, text = 0, outsupp = 0;
|
|
int select = OSSL_ECHSTORE_ALL, numinfiles = 0;
|
|
char *outfile = NULL, *infile = NULL;
|
|
char *infiles[OSSL_ECH_MAXINFILES] = { NULL };
|
|
char *public_name = NULL, *suitestr = NULL;
|
|
uint16_t ech_version = OSSL_ECH_CURRENT_VERSION;
|
|
uint8_t max_name_length = 0;
|
|
OSSL_HPKE_SUITE hpke_suite = OSSL_HPKE_SUITE_DEFAULT;
|
|
int mode = OSSL_ECH_KEYGEN_MODE; /* key generation */
|
|
OSSL_ECHSTORE *es = NULL;
|
|
BIO *ecf = NULL;
|
|
|
|
prog = opt_init(argc, argv, ech_options);
|
|
while ((o = opt_next()) != OPT_EOF) {
|
|
switch (o) {
|
|
case OPT_EOF:
|
|
case OPT_ERR:
|
|
BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
|
|
goto end;
|
|
case OPT_HELP:
|
|
opt_help(ech_options);
|
|
rv = 0;
|
|
goto end;
|
|
case OPT_VERBOSE:
|
|
verbose = 1;
|
|
break;
|
|
case OPT_TEXT:
|
|
text = 1;
|
|
break;
|
|
case OPT_SELECT:
|
|
mode = OSSL_ECH_SELPRINT_MODE;
|
|
select = strtol(opt_arg(), NULL, 10);
|
|
break;
|
|
case OPT_OUT:
|
|
outfile = opt_arg();
|
|
outsupp = 1;
|
|
break;
|
|
case OPT_IN:
|
|
mode = OSSL_ECH_SELPRINT_MODE;
|
|
infile = opt_arg();
|
|
if (numinfiles >= OSSL_ECH_MAXINFILES) {
|
|
BIO_printf(bio_err, "too many input files, only %d allowed\n",
|
|
OSSL_ECH_MAXINFILES);
|
|
goto opthelp;
|
|
}
|
|
infiles[numinfiles] = infile;
|
|
numinfiles++;
|
|
break;
|
|
case OPT_PUBLICNAME:
|
|
public_name = opt_arg();
|
|
break;
|
|
case OPT_ECHVERSION:
|
|
ech_version = verstr2us(opt_arg());
|
|
break;
|
|
case OPT_MAXNAMELENGTH:
|
|
{
|
|
long tmp = strtol(opt_arg(), NULL, 10);
|
|
|
|
if (tmp < 0 || tmp > OSSL_ECH_MAX_MAXNAMELEN) {
|
|
BIO_printf(bio_err,
|
|
"max name length out of range [0,%d] (%ld)\n",
|
|
OSSL_ECH_MAX_MAXNAMELEN, tmp);
|
|
goto opthelp;
|
|
} else {
|
|
max_name_length = (uint8_t)tmp;
|
|
}
|
|
}
|
|
break;
|
|
case OPT_HPKESUITE:
|
|
suitestr = opt_arg();
|
|
break;
|
|
}
|
|
}
|
|
argc = opt_num_rest();
|
|
argv = opt_rest();
|
|
if (argc != 0) {
|
|
BIO_printf(bio_err, "%s: Unknown parameter %s\n", prog, argv[0]);
|
|
goto opthelp;
|
|
}
|
|
/* Check ECH-specific inputs */
|
|
switch (ech_version) {
|
|
case OSSL_ECH_RFCXXXX_VERSION: /* fall through */
|
|
case 13:
|
|
ech_version = OSSL_ECH_RFCXXXX_VERSION;
|
|
break;
|
|
default:
|
|
BIO_printf(bio_err, "Un-supported version (0x%04x)\n", ech_version);
|
|
goto end;
|
|
}
|
|
if (max_name_length > OSSL_ECH_MAX_MAXNAMELEN) {
|
|
BIO_printf(bio_err, "Weird max name length (0x%04x) - biggest is "
|
|
"(0x%04x) - exiting\n", max_name_length,
|
|
OSSL_ECH_MAX_MAXNAMELEN);
|
|
ERR_print_errors(bio_err);
|
|
goto end;
|
|
}
|
|
if (suitestr != NULL) {
|
|
if (OSSL_HPKE_str2suite(suitestr, &hpke_suite) != 1) {
|
|
BIO_printf(bio_err, "Bad OSSL_HPKE_SUITE (%s)\n", suitestr);
|
|
ERR_print_errors(bio_err);
|
|
goto end;
|
|
}
|
|
}
|
|
/* Set default if needed */
|
|
if (outfile == NULL)
|
|
outfile = "echconfig.pem";
|
|
es = OSSL_ECHSTORE_new(NULL, NULL);
|
|
if (es == NULL)
|
|
goto end;
|
|
if (mode == OSSL_ECH_KEYGEN_MODE) {
|
|
if (verbose)
|
|
BIO_printf(bio_err, "Calling OSSL_ECHSTORE_new_config\n");
|
|
if ((ecf = BIO_new_file(outfile, "w")) == NULL
|
|
|| OSSL_ECHSTORE_new_config(es, ech_version, max_name_length,
|
|
public_name, hpke_suite) != 1
|
|
|| OSSL_ECHSTORE_write_pem(es, 0, ecf) != 1) {
|
|
BIO_printf(bio_err, "OSSL_ECHSTORE_new_config error\n");
|
|
goto end;
|
|
}
|
|
if (verbose)
|
|
BIO_printf(bio_err, "OSSL_ECHSTORE_new_config success\n");
|
|
rv = 0;
|
|
}
|
|
if (mode == OSSL_ECH_SELPRINT_MODE) {
|
|
if (numinfiles == 0)
|
|
goto opthelp;
|
|
for (i = 0; i != numinfiles; i++) {
|
|
if ((ecf = BIO_new_file(infiles[i], "r")) == NULL
|
|
|| OSSL_ECHSTORE_read_pem(es, ecf, OSSL_ECH_FOR_RETRY) != 1) {
|
|
if (verbose)
|
|
BIO_printf(bio_err, "OSSL_ECHSTORE_read_pem error for %s\n",
|
|
infiles[i]);
|
|
/* try read it as an ECHConfigList */
|
|
goto end;
|
|
}
|
|
BIO_free(ecf);
|
|
ecf = NULL;
|
|
}
|
|
if (verbose)
|
|
BIO_printf(bio_err, "Success reading %d files\n", numinfiles);
|
|
if (outsupp == 1) {
|
|
/* write result to that, with downselection if required */
|
|
if (verbose)
|
|
BIO_printf(bio_err, "Will write to %s\n", outfile);
|
|
if (verbose && select != OSSL_ECHSTORE_ALL)
|
|
BIO_printf(bio_err, "Selected entry: %d\n", select);
|
|
if ((ecf = BIO_new_file(outfile, "w")) == NULL
|
|
|| OSSL_ECHSTORE_write_pem(es, select, ecf) != 1) {
|
|
BIO_printf(bio_err, "OSSL_ECHSTORE_write_pem error\n");
|
|
goto end;
|
|
}
|
|
if (verbose)
|
|
BIO_printf(bio_err, "Success writing to %s\n", outfile);
|
|
}
|
|
rv = 0;
|
|
}
|
|
if (text) {
|
|
int oi_ind, oi_cnt = 0;
|
|
|
|
if (OSSL_ECHSTORE_num_entries(es, &oi_cnt) != 1)
|
|
goto end;
|
|
if (verbose)
|
|
BIO_printf(bio_err, "Printing %d ECHConfigList\n", oi_cnt);
|
|
for (oi_ind = 0; oi_ind != oi_cnt; oi_ind++) {
|
|
time_t secs = 0;
|
|
char *pn = NULL, *ec = NULL;
|
|
int has_priv, for_retry;
|
|
|
|
if (OSSL_ECHSTORE_get1_info(es, oi_ind, &secs, &pn, &ec,
|
|
&has_priv, &for_retry) != 1) {
|
|
OPENSSL_free(pn); /* just in case */
|
|
OPENSSL_free(ec);
|
|
goto end;
|
|
}
|
|
BIO_printf(bio_err, "ECH entry: %d public_name: %s age: %lld%s\n",
|
|
oi_ind, pn, (long long)secs,
|
|
has_priv ? " (has private key)" : "");
|
|
BIO_printf(bio_err, "\t%s\n", ec);
|
|
OPENSSL_free(pn);
|
|
OPENSSL_free(ec);
|
|
}
|
|
if (verbose)
|
|
BIO_printf(bio_err, "Success printing %d ECHConfigList\n", oi_cnt);
|
|
rv = 0;
|
|
}
|
|
end:
|
|
OSSL_ECHSTORE_free(es);
|
|
BIO_free_all(ecf);
|
|
return rv;
|
|
opthelp:
|
|
BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
|
|
BIO_printf(bio_err, "\tup to %d -in instances allowed\n", OSSL_ECH_MAXINFILES);
|
|
return rv;
|
|
}
|
|
|
|
#endif
|