Merge pull request #336 from deweerdt/rfc7250
Raw public keys (RFC 7250) support
diff --git a/include/picotls.h b/include/picotls.h
index 0162fe8..ee3b941 100644
--- a/include/picotls.h
+++ b/include/picotls.h
@@ -153,6 +153,7 @@
#define PTLS_ALERT_BAD_RECORD_MAC 20
#define PTLS_ALERT_HANDSHAKE_FAILURE 40
#define PTLS_ALERT_BAD_CERTIFICATE 42
+#define PTLS_ALERT_UNSUPPORTED_CERTIFICATE 43
#define PTLS_ALERT_CERTIFICATE_REVOKED 44
#define PTLS_ALERT_CERTIFICATE_EXPIRED 45
#define PTLS_ALERT_CERTIFICATE_UNKNOWN 46
@@ -210,6 +211,9 @@
#define PTLS_HANDSHAKE_TYPE_COMPRESSED_CERTIFICATE 25
#define PTLS_HANDSHAKE_TYPE_MESSAGE_HASH 254
+#define PTLS_CERTIFICATE_TYPE_X509 0
+#define PTLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY 2
+
#define PTLS_ZERO_DIGEST_SHA256 \
{ \
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, \
@@ -531,6 +535,10 @@
const uint16_t *list;
size_t count;
} cipher_suites;
+ struct {
+ const uint8_t *list;
+ size_t count;
+ } server_certificate_types;
/**
* if ESNI was used
*/
@@ -708,6 +716,19 @@
*/
unsigned omit_end_of_early_data : 1;
/**
+ * This option turns on support for Raw Public Keys (RFC 7250).
+ *
+ * When running as a client, this option instructs the client to request the server to send raw public keys in place of X.509
+ * certificate chain. The client should set its `certificate_verify` callback to one that is capable of validating the raw
+ * public key that will be sent by the server.
+ *
+ * When running as a server, this option instructs the server to only handle clients requesting the use of raw public keys. If
+ * the client does not, the handshake is rejected. Note however that the rejection happens only after the `on_client_hello`
+ * callback is being called. Therefore, applications can support both X.509 and raw public keys by swapping `ptls_context_t` to
+ * the correct one when that callback is being called (like handling swapping the contexts based on the value of SNI).
+ */
+ unsigned use_raw_public_keys : 1;
+ /**
*
*/
ptls_encrypt_ticket_t *encrypt_ticket;
diff --git a/include/picotls/openssl.h b/include/picotls/openssl.h
index 2bfe1b9..2cc8c96 100644
--- a/include/picotls/openssl.h
+++ b/include/picotls/openssl.h
@@ -104,6 +104,11 @@
void ptls_openssl_dispose_sign_certificate(ptls_openssl_sign_certificate_t *self);
int ptls_openssl_load_certificates(ptls_context_t *ctx, X509 *cert, STACK_OF(X509) * chain);
+typedef struct st_ptls_openssl_raw_pubkey_verify_certificate_t {
+ ptls_verify_certificate_t super;
+ EVP_PKEY *expected_pubkey;
+} ptls_openssl_raw_pubkey_verify_certificate_t;
+
typedef struct st_ptls_openssl_verify_certificate_t {
ptls_verify_certificate_t super;
X509_STORE *cert_store;
@@ -113,6 +118,9 @@
void ptls_openssl_dispose_verify_certificate(ptls_openssl_verify_certificate_t *self);
X509_STORE *ptls_openssl_create_default_certificate_store(void);
+int ptls_openssl_raw_pubkey_init_verify_certificate(ptls_openssl_raw_pubkey_verify_certificate_t *self, EVP_PKEY *pubkey);
+void ptls_openssl_raw_pubkey_dispose_verify_certificate(ptls_openssl_raw_pubkey_verify_certificate_t *self);
+
int ptls_openssl_encrypt_ticket(ptls_buffer_t *dst, ptls_iovec_t src,
int (*cb)(unsigned char *, unsigned char *, EVP_CIPHER_CTX *, HMAC_CTX *, int));
int ptls_openssl_decrypt_ticket(ptls_buffer_t *dst, ptls_iovec_t src,
diff --git a/lib/openssl.c b/lib/openssl.c
index 00c7777..3221a7e 100644
--- a/lib/openssl.c
+++ b/lib/openssl.c
@@ -1286,6 +1286,51 @@
return NULL;
}
+static int verify_raw_cert(ptls_verify_certificate_t *_self, ptls_t *tls, int (**verifier)(void *, ptls_iovec_t, ptls_iovec_t),
+ void **verify_data, ptls_iovec_t *certs, size_t num_certs)
+{
+ ptls_openssl_raw_pubkey_verify_certificate_t *self = (ptls_openssl_raw_pubkey_verify_certificate_t *)_self;
+ int ret = PTLS_ALERT_BAD_CERTIFICATE;
+ ptls_iovec_t expected_pubkey = {};
+
+ assert(num_certs != 0);
+
+ if (num_certs != 1)
+ goto Exit;
+
+ int r = i2d_PUBKEY(self->expected_pubkey, &expected_pubkey.base);
+ if (r <= 0) {
+ ret = PTLS_ALERT_BAD_CERTIFICATE;
+ goto Exit;
+ }
+
+ expected_pubkey.len = r;
+ if (certs[0].len != expected_pubkey.len)
+ goto Exit;
+
+ if (!ptls_mem_equal(expected_pubkey.base, certs[0].base, certs[0].len))
+ goto Exit;
+
+ EVP_PKEY_up_ref(self->expected_pubkey);
+ *verify_data = self->expected_pubkey;
+ *verifier = verify_sign;
+ ret = 0;
+Exit:
+ free(expected_pubkey.base);
+ return ret;
+}
+
+int ptls_openssl_raw_pubkey_init_verify_certificate(ptls_openssl_raw_pubkey_verify_certificate_t *self, EVP_PKEY *expected_pubkey)
+{
+ EVP_PKEY_up_ref(expected_pubkey);
+ *self = (ptls_openssl_raw_pubkey_verify_certificate_t){{verify_raw_cert}, expected_pubkey};
+ return 0;
+}
+void ptls_openssl_raw_pubkey_dispose_verify_certificate(ptls_openssl_raw_pubkey_verify_certificate_t *self)
+{
+ EVP_PKEY_free(self->expected_pubkey);
+}
+
#define TICKET_LABEL_SIZE 16
#define TICKET_IV_SIZE EVP_MAX_IV_LENGTH
diff --git a/lib/picotls.c b/lib/picotls.c
index 9f84120..9f405e2 100644
--- a/lib/picotls.c
+++ b/lib/picotls.c
@@ -56,6 +56,7 @@
#define PTLS_EXTENSION_TYPE_SUPPORTED_GROUPS 10
#define PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS 13
#define PTLS_EXTENSION_TYPE_ALPN 16
+#define PTLS_EXTENSION_TYPE_SERVER_CERTIFICATE_TYPE 20
#define PTLS_EXTENSION_TYPE_COMPRESS_CERTIFICATE 27
#define PTLS_EXTENSION_TYPE_PRE_SHARED_KEY 41
#define PTLS_EXTENSION_TYPE_EARLY_DATA 42
@@ -284,6 +285,7 @@
#define MAX_UNKNOWN_EXTENSIONS 16
#define MAX_CLIENT_CIPHERS 32
+#define MAX_CERTIFICATE_TYPES 8
struct st_ptls_client_hello_t {
uint16_t legacy_version;
@@ -335,6 +337,10 @@
unsigned early_data_indication : 1;
unsigned is_last_extension : 1;
} psk;
+ struct {
+ uint8_t list[MAX_CERTIFICATE_TYPES];
+ size_t count;
+ } server_certificate_types;
ptls_raw_extension_t unknown_extensions[MAX_UNKNOWN_EXTENSIONS + 1];
unsigned status_request : 1;
};
@@ -462,6 +468,10 @@
ALLOW(CLIENT_HELLO);
ALLOW(SERVER_HELLO);
});
+ EXT(SERVER_CERTIFICATE_TYPE, {
+ ALLOW(CLIENT_HELLO);
+ ALLOW(ENCRYPTED_EXTENSIONS);
+ });
#undef ALLOW
#undef EXT
@@ -2064,6 +2074,11 @@
ptls_buffer_push_block(sendbuf, 2, { ptls_buffer_pushv(sendbuf, cookie->base, cookie->len); });
});
}
+ if (tls->ctx->use_raw_public_keys) {
+ buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_CERTIFICATE_TYPE, {
+ ptls_buffer_push_block(sendbuf, 1, { ptls_buffer_push(sendbuf, PTLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY); });
+ });
+ }
if ((ret = push_additional_extensions(properties, sendbuf)) != 0)
goto Exit;
if (tls->ctx->save_ticket != NULL || resumption_secret.base != NULL) {
@@ -2424,6 +2439,7 @@
static const ptls_raw_extension_t no_unknown_extensions = {UINT16_MAX};
ptls_raw_extension_t *unknown_extensions = (ptls_raw_extension_t *)&no_unknown_extensions;
int ret, skip_early_data = 1;
+ uint8_t server_offered_cert_type = PTLS_CERTIFICATE_TYPE_X509;
decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS, &type, {
if (tls->ctx->on_extension != NULL &&
@@ -2478,6 +2494,14 @@
}
skip_early_data = 0;
break;
+ case PTLS_EXTENSION_TYPE_SERVER_CERTIFICATE_TYPE:
+ if (end - src != 1) {
+ ret = PTLS_ALERT_DECODE_ERROR;
+ goto Exit;
+ }
+ server_offered_cert_type = *src;
+ src = end;
+ break;
default:
if (should_collect_unknown_extension(tls, properties, type)) {
if (unknown_extensions == &no_unknown_extensions) {
@@ -2495,6 +2519,12 @@
src = end;
});
+ if (server_offered_cert_type !=
+ (tls->ctx->use_raw_public_keys ? PTLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY : PTLS_CERTIFICATE_TYPE_X509)) {
+ ret = PTLS_ALERT_UNSUPPORTED_CERTIFICATE;
+ goto Exit;
+ }
+
if (tls->esni != NULL) {
if (esni_nonce == NULL || !ptls_mem_equal(esni_nonce, tls->esni->nonce, PTLS_ESNI_NONCE_SIZE)) {
ret = PTLS_ALERT_ILLEGAL_PARAMETER;
@@ -3285,6 +3315,23 @@
} while (src != end);
});
break;
+ case PTLS_EXTENSION_TYPE_SERVER_CERTIFICATE_TYPE:
+ ptls_decode_block(src, end, 1, {
+ size_t list_size = end - src;
+
+ /* RFC7250 4.1: No empty list, no list with single x509 element */
+ if (list_size == 0 || (list_size == 1 && *src == PTLS_CERTIFICATE_TYPE_X509)) {
+ ret = PTLS_ALERT_DECODE_ERROR;
+ goto Exit;
+ }
+
+ do {
+ if (ch->server_certificate_types.count < PTLS_ELEMENTSOF(ch->server_certificate_types.list))
+ ch->server_certificate_types.list[ch->server_certificate_types.count++] = *src;
+ src++;
+ } while (src != end);
+ });
+ break;
case PTLS_EXTENSION_TYPE_COMPRESS_CERTIFICATE:
ptls_decode_block(src, end, 1, {
do {
@@ -3583,6 +3630,18 @@
return 0;
}
+static int certificate_type_exists(uint8_t *list, size_t count, uint8_t desired_type)
+{
+ /* empty type list means that we default to x509 */
+ if (desired_type == PTLS_CERTIFICATE_TYPE_X509 && count == 0)
+ return 1;
+ for (size_t i = 0; i < count; i++) {
+ if (list[i] == desired_type)
+ return 1;
+ }
+ return 0;
+}
+
static int server_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message,
ptls_handshake_properties_t *properties)
{
@@ -3634,8 +3693,9 @@
ret = PTLS_ERROR_NO_MEMORY;
goto Exit;
}
- *ch = (struct st_ptls_client_hello_t){0, NULL, {NULL}, {NULL}, 0, {NULL}, {NULL}, {NULL}, {{0}},
- {NULL}, {NULL}, {{{NULL}}}, {{0}}, {{0}}, {{NULL}}, {NULL}, {{UINT16_MAX}}};
+
+ *ch = (struct st_ptls_client_hello_t){0, NULL, {NULL}, {NULL}, 0, {NULL}, {NULL}, {NULL}, {{0}},
+ {NULL}, {NULL}, {{{NULL}}}, {{0}}, {{0}}, {{NULL}}, {NULL}, {{0}}, {{UINT16_MAX}}};
/* decode ClientHello */
if ((ret = decode_client_hello(tls, ch, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.base + message.len, properties)) !=
@@ -3721,15 +3781,24 @@
{ch->signature_algorithms.list, ch->signature_algorithms.count},
{ch->cert_compression_algos.list, ch->cert_compression_algos.count},
{ch->client_ciphers.list, ch->client_ciphers.count},
+ {ch->server_certificate_types.list, ch->server_certificate_types.count},
is_esni};
ret = tls->ctx->on_client_hello->cb(tls->ctx->on_client_hello, tls, ¶ms);
} else {
ret = 0;
}
+
if (is_esni)
free(server_name.base);
if (ret != 0)
goto Exit;
+
+ if (!certificate_type_exists(ch->server_certificate_types.list, ch->server_certificate_types.count,
+ tls->ctx->use_raw_public_keys ? PTLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY
+ : PTLS_CERTIFICATE_TYPE_X509)) {
+ ret = PTLS_ALERT_UNSUPPORTED_CERTIFICATE;
+ goto Exit;
+ }
} else {
if (ch->psk.early_data_indication) {
ret = PTLS_ALERT_DECODE_ERROR;
@@ -3993,6 +4062,10 @@
* The "extension_data" field of this extension SHALL be empty. (RFC 6066 section 3) */
buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_NAME, {});
}
+ if (tls->ctx->use_raw_public_keys) {
+ buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_CERTIFICATE_TYPE,
+ { ptls_buffer_push(sendbuf, PTLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY); });
+ }
if (tls->negotiated_protocol != NULL) {
buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ALPN, {
ptls_buffer_push_block(sendbuf, 2, {
diff --git a/t/assets/ec256-key-pair.pem b/t/assets/ec256-key-pair.pem
new file mode 100644
index 0000000..ce786ed
--- /dev/null
+++ b/t/assets/ec256-key-pair.pem
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIBswGBU5+SsrFYQupnJ/GVf1bYhBEmJiBpxLL4jXZRrpoAoGCCqGSM49
+AwEHoUQDQgAEWF0BvlHl/ZVaoApefcN5+emI6cjSDbR3aP843VWgMLfxNqvmWut0
+KsoRQC2OHJ+Z8HoLZcNnA7Mc3/ypHSUqrw==
+-----END EC PRIVATE KEY-----
diff --git a/t/assets/ec256-pub.pem b/t/assets/ec256-pub.pem
new file mode 100644
index 0000000..76a3396
--- /dev/null
+++ b/t/assets/ec256-pub.pem
@@ -0,0 +1,4 @@
+-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWF0BvlHl/ZVaoApefcN5+emI6cjS
+DbR3aP843VWgMLfxNqvmWut0KsoRQC2OHJ+Z8HoLZcNnA7Mc3/ypHSUqrw==
+-----END PUBLIC KEY-----
diff --git a/t/assets/server.pub b/t/assets/server.pub
new file mode 100644
index 0000000..52b7731
--- /dev/null
+++ b/t/assets/server.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7zZheZ4ph98JaedBNv9k
+qsVA9CSmhd69kBc9ZAfVFMA4VQwprOj3ZGrxf20HB3FkvqGvew9ZogUF6NjbPume
+iUObGpP21Y5wcYlPL4aojlrwMB/eOxOCpuRyQTRSSe1hDPvdJABQdmshDP5ZSEBL
+dUSgrNn4KWhIDjFj1AHXIMqeqTXetFuRgNzHdtbXQx+UWBis2B6qZJuqSArb2msV
+OC8D5gNznPPlQw7FbdPCaLNXSb6GnI0E0uj6QmYlAw9s6nkgP/zxjfFldqPNUprG
+cEqTwmAb8VVtd7XbANYrzubZ4Nn6/WXrCrVxWUmh/7Spgdwa/I4Nr1JHv9HHyL2z
+/wIDAQAB
+-----END PUBLIC KEY-----
diff --git a/t/cli.c b/t/cli.c
index bce9a65..b97c960 100644
--- a/t/cli.c
+++ b/t/cli.c
@@ -97,6 +97,7 @@
goto Exit;
}
}
+
if (server_name != NULL) {
ptls_set_server_name(tls, server_name, 0);
if ((ret = ptls_handshake(tls, &encbuf, NULL, NULL, hsprop)) != PTLS_ERROR_IN_PROGRESS) {
@@ -358,6 +359,11 @@
" -E esni-file file that stores ESNI data generated by picotls-esni\n"
" -e when resuming a session, send first 8,192 bytes of input\n"
" as early data\n"
+ " -r Load raw public keys for raw certificates\n"
+ " -r public-key-file use raw public keys (RFC 7250). When set and running as a\n"
+ " client, the argument specifies the public keys that the\n"
+ " server is expected to use. When running as a server, the\n"
+ " argument is ignored.\n"
" -u update the traffic key when handshake is complete\n"
" -v verify peer using the default certificates\n"
" -y cipher-suite cipher-suite to be used, e.g., aes128gcmsha256 (default:\n"
@@ -404,8 +410,9 @@
struct sockaddr_storage sa;
socklen_t salen;
int family = 0;
+ const char *raw_pub_key_file = NULL, *cert_location = NULL;
- while ((ch = getopt(argc, argv, "46abBC:c:i:Ik:nN:es:SE:K:l:y:vh")) != -1) {
+ while ((ch = getopt(argc, argv, "46abBC:c:i:Ik:nN:es:Sr:E:K:l:y:vh")) != -1) {
switch (ch) {
case '4':
family = AF_INET;
@@ -429,11 +436,11 @@
break;
case 'C':
case 'c':
- if (ctx.certificates.count != 0) {
+ if (cert_location != NULL) {
fprintf(stderr, "-C/-c can only be specified once\n");
return 1;
}
- load_certificate_chain(&ctx, optarg);
+ cert_location = optarg;
is_server = ch == 'c';
break;
case 'i':
@@ -451,6 +458,9 @@
case 'e':
use_early_data = 1;
break;
+ case 'r':
+ raw_pub_key_file = optarg;
+ break;
case 's':
setup_session_file(&ctx, &hsprop, optarg);
break;
@@ -540,10 +550,36 @@
}
argc -= optind;
argv += optind;
+
+ if (raw_pub_key_file != NULL) {
+ int is_dash = !strcmp(raw_pub_key_file, "-");
+ if (is_server) {
+ ctx.certificates.list = malloc(sizeof(*ctx.certificates.list));
+ load_raw_public_key(ctx.certificates.list, cert_location);
+ ctx.certificates.count = 1;
+ } else if (!is_dash) {
+ ptls_iovec_t raw_pub_key;
+ EVP_PKEY *pubkey;
+ load_raw_public_key(&raw_pub_key, raw_pub_key_file);
+ pubkey = d2i_PUBKEY(NULL, (const unsigned char **)&raw_pub_key.base, raw_pub_key.len);
+ if (pubkey == NULL) {
+ fprintf(stderr, "Failed to create an EVP_PKEY from the key found in %s\n", raw_pub_key_file);
+ return 1;
+ }
+ setup_raw_pubkey_verify_certificate(&ctx, pubkey);
+ EVP_PKEY_free(pubkey);
+ }
+ ctx.use_raw_public_keys = 1;
+ } else {
+ if (cert_location)
+ load_certificate_chain(&ctx, cert_location);
+ }
+
if ((ctx.certificates.count == 0) != (ctx.sign_certificate == NULL)) {
fprintf(stderr, "-C/-c and -k options must be used together\n");
return 1;
}
+
if (is_server) {
if (ctx.certificates.count == 0) {
fprintf(stderr, "-c and -k options must be set\n");
diff --git a/t/e2e.t b/t/e2e.t
index 577bc20..e6a4ff9 100755
--- a/t/e2e.t
+++ b/t/e2e.t
@@ -16,7 +16,7 @@
my $tempdir = tempdir(CLEANUP => 1);
subtest "hello" => sub {
- my $guard = spawn_server(qw(-i t/assets/hello.txt));
+ my $guard = spawn_server("rsa", qw(-i t/assets/hello.txt));
subtest "full-handshake" => sub {
my $resp = `$cli 127.0.0.1 $port 2> /dev/null`;
is $resp, "hello";
@@ -35,7 +35,7 @@
subtest "success" => sub {
plan skip_all => "faketime not found"
unless system("which faketime > /dev/null 2>&1") == 0;
- my $guard = spawn_server(qw(-i t/assets/hello.txt -l), "$tempdir/events");
+ my $guard = spawn_server("rsa", qw(-i t/assets/hello.txt -l), "$tempdir/events");
my $resp = `$cli -s $tempdir/session 127.0.0.1 $port`;
is $resp, "hello";
$resp = `$cli -e -s $tempdir/session 127.0.0.1 $port`;
@@ -63,17 +63,41 @@
subtest "certificate-compression" => sub {
plan skip_all => "feature disabled"
unless system("$cli -b -h > /dev/null 2>&1") == 0;
- my $guard = spawn_server(qw(-i t/assets/hello.txt -b));
+ my $guard = spawn_server("rsa", qw(-i t/assets/hello.txt -b));
my $resp = `$cli 127.0.0.1 $port 2> /dev/null`;
is $resp, "hello";
$resp = `$cli -b 127.0.0.1 $port 2> /dev/null`;
is $resp, "hello";
};
+subtest "raw-certificates-rsa" => sub {
+ my $guard = spawn_server("rsa", qw(-r - -i t/assets/hello.txt));
+ my $resp = `$cli -v -r t/assets/server.pub 127.0.0.1 $port 2> /dev/null`;
+ is $resp, "hello";
+};
+
+
+subtest "raw-certificates-ec" => sub {
+ my $guard = spawn_server("ec", qw(-r - -i t/assets/hello.txt));
+ my $resp = `$cli -v -r t/assets/ec256-pub.pem 127.0.0.1 $port 2> /dev/null`;
+ is $resp, "hello";
+};
+
+
done_testing;
sub spawn_server {
- my @cmd = ($cli, "-k", "t/assets/server.key", "-c", "t/assets/server.crt", @_, "127.0.0.1", $port);
+ my $key_type = shift;
+ my @cmd;
+ if ($key_type eq "rsa") {
+ my $ext = "crt";
+ $ext = "pub" if (grep(/^-r$/, @_));
+ @cmd = ($cli, "-k", "t/assets/server.key", "-c", "t/assets/server.$ext", @_, "127.0.0.1", $port);
+ } elsif ($key_type eq "ec") {
+ @cmd = ($cli, "-k", "t/assets/ec256-key-pair.pem", "-c", "t/assets/ec256-pub.pem", @_, "127.0.0.1", $port);
+ } else {
+ die "Unexpected key type: $key_type";
+ }
my $pid = fork;
die "fork failed:$!"
unless defined $pid;
diff --git a/t/util.h b/t/util.h
index db86416..3890737 100644
--- a/t/util.h
+++ b/t/util.h
@@ -48,6 +48,15 @@
}
}
+static inline void load_raw_public_key(ptls_iovec_t *raw_public_key, char const *cert_pem_file)
+{
+ size_t count;
+ if (ptls_load_pem_objects(cert_pem_file, "PUBLIC KEY", raw_public_key, 1, &count) != 0) {
+ fprintf(stderr, "failed to load public key:%s:%s\n", cert_pem_file, strerror(errno));
+ exit(1);
+ }
+}
+
static inline void load_private_key(ptls_context_t *ctx, const char *fn)
{
static ptls_openssl_sign_certificate_t sc;
@@ -122,6 +131,13 @@
ctx->verify_certificate = &vc.super;
}
+static inline void setup_raw_pubkey_verify_certificate(ptls_context_t *ctx, EVP_PKEY *pubkey)
+{
+ static ptls_openssl_raw_pubkey_verify_certificate_t vc;
+ ptls_openssl_raw_pubkey_init_verify_certificate(&vc, pubkey);
+ ctx->verify_certificate = &vc.super;
+}
+
static inline void setup_esni(ptls_context_t *ctx, const char *esni_fn, ptls_key_exchange_context_t **key_exchanges)
{
uint8_t esnikeys[65536];