Merge pull request #457 from h2o/kazuho/expose-sigschemes
alternative signers: support callback-based method, add utility functions
diff --git a/include/picotls.h b/include/picotls.h
index 9762dad..c98a053 100644
--- a/include/picotls.h
+++ b/include/picotls.h
@@ -650,10 +650,23 @@
PTLS_CALLBACK_TYPE(int, emit_certificate, ptls_t *tls, ptls_message_emitter_t *emitter, ptls_key_schedule_t *key_sched,
ptls_iovec_t context, int push_status_request, const uint16_t *compress_algos, size_t num_compress_algos);
/**
- * context object of an async operation (e.g., RSA signature generation)
+ * An object that represents an asynchronous task (e.g., RSA signature generation).
+ * When `ptls_handshake` returns `PTLS_ERROR_ASYNC_OPERATION`, it has an associated task in flight. The user should obtain the
+ * reference to the associated task by calling `ptls_get_async_job`, then either wait for the file descriptor obtained from
+ * the `get_fd` callback to become readable, or set a completion callback via `set_completion_callback` and wait for its
+ * invocation. Once notified, the user should invoke `ptls_handshake` again.
+ * Async jobs typically provide support for only one of the two methods.
*/
typedef struct st_ptls_async_job_t {
void (*destroy_)(struct st_ptls_async_job_t *self);
+ /**
+ * optional callback returning a file descriptor that becomes readable when the job is complete
+ */
+ int (*get_fd)(struct st_ptls_async_job_t *self);
+ /**
+ * optional callback for setting a completion callback
+ */
+ void (*set_completion_callback)(struct st_ptls_async_job_t *self, void (*cb)(void *), void *cbdata);
} ptls_async_job_t;
/**
* When gerenating CertificateVerify, the core calls the callback to sign the handshake context using the certificate. This callback
diff --git a/include/picotls/openssl.h b/include/picotls/openssl.h
index 3a7c3b5..a892f9b 100644
--- a/include/picotls/openssl.h
+++ b/include/picotls/openssl.h
@@ -125,22 +125,25 @@
*/
int ptls_openssl_create_key_exchange(ptls_key_exchange_context_t **ctx, EVP_PKEY *pkey);
-#if PTLS_OPENSSL_HAVE_ASYNC
-/**
- * Returns the file descriptor of the asynchronous operation in flight.
- */
-OSSL_ASYNC_FD ptls_openssl_get_async_fd(ptls_t *ptls);
-#endif
-
-struct st_ptls_openssl_signature_scheme_t {
+typedef struct st_ptls_openssl_signature_scheme_t {
uint16_t scheme_id;
const EVP_MD *(*scheme_md)(void);
-};
+} ptls_openssl_signature_scheme_t;
+
+/**
+ * Given a private key, returns a list of compatible signature schemes. This list is terminated by scheme_id of UINT16_MAX.
+ */
+const ptls_openssl_signature_scheme_t *ptls_openssl_lookup_signature_schemes(EVP_PKEY *key);
+/**
+ * Given available schemes and input, choses one, or returns NULL if none is available.
+ */
+const ptls_openssl_signature_scheme_t *ptls_openssl_select_signature_scheme(const ptls_openssl_signature_scheme_t *available,
+ const uint16_t *algorithms, size_t num_algorithms);
typedef struct st_ptls_openssl_sign_certificate_t {
ptls_sign_certificate_t super;
EVP_PKEY *key;
- const struct st_ptls_openssl_signature_scheme_t *schemes; /* terminated by .scheme_id == UINT16_MAX */
+ const ptls_openssl_signature_scheme_t *schemes; /* terminated by .scheme_id == UINT16_MAX */
/**
* When set to true, indicates to the backend that the signature can be generated asynchronously. When the backend decides to
* generate the signature asynchronously, `ptls_handshake` will return PTLS_ERROR_ASYNC_OPERATION. When receiving that error
diff --git a/lib/openssl.c b/lib/openssl.c
index 511315e..71b33ae 100644
--- a/lib/openssl.c
+++ b/lib/openssl.c
@@ -89,22 +89,22 @@
#endif
-static const struct st_ptls_openssl_signature_scheme_t rsa_signature_schemes[] = {{PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256, EVP_sha256},
+static const ptls_openssl_signature_scheme_t rsa_signature_schemes[] = {{PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256, EVP_sha256},
{PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384, EVP_sha384},
{PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512, EVP_sha512},
{UINT16_MAX, NULL}};
-static const struct st_ptls_openssl_signature_scheme_t secp256r1_signature_schemes[] = {
+static const ptls_openssl_signature_scheme_t secp256r1_signature_schemes[] = {
{PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256, EVP_sha256}, {UINT16_MAX, NULL}};
#if PTLS_OPENSSL_HAVE_SECP384R1
-static const struct st_ptls_openssl_signature_scheme_t secp384r1_signature_schemes[] = {
+static const ptls_openssl_signature_scheme_t secp384r1_signature_schemes[] = {
{PTLS_SIGNATURE_ECDSA_SECP384R1_SHA384, EVP_sha384}, {UINT16_MAX, NULL}};
#endif
#if PTLS_OPENSSL_HAVE_SECP521R1
-static const struct st_ptls_openssl_signature_scheme_t secp521r1_signature_schemes[] = {
+static const ptls_openssl_signature_scheme_t secp521r1_signature_schemes[] = {
{PTLS_SIGNATURE_ECDSA_SECP521R1_SHA512, EVP_sha512}, {UINT16_MAX, NULL}};
#endif
#if PTLS_OPENSSL_HAVE_ED25519
-static const struct st_ptls_openssl_signature_scheme_t ed25519_signature_schemes[] = {{PTLS_SIGNATURE_ED25519, NULL},
+static const ptls_openssl_signature_scheme_t ed25519_signature_schemes[] = {{PTLS_SIGNATURE_ED25519, NULL},
{UINT16_MAX, NULL}};
#endif
@@ -127,9 +127,9 @@
PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256,
UINT16_MAX};
-static const struct st_ptls_openssl_signature_scheme_t *lookup_signature_schemes(EVP_PKEY *key)
+const ptls_openssl_signature_scheme_t *ptls_openssl_lookup_signature_schemes(EVP_PKEY *key)
{
- const struct st_ptls_openssl_signature_scheme_t *schemes = NULL;
+ const ptls_openssl_signature_scheme_t *schemes = NULL;
switch (EVP_PKEY_id(key)) {
case EVP_PKEY_RSA:
@@ -168,6 +168,20 @@
return schemes;
}
+const ptls_openssl_signature_scheme_t *ptls_openssl_select_signature_scheme(const ptls_openssl_signature_scheme_t *available,
+ const uint16_t *algorithms, size_t num_algorithms)
+{
+ const ptls_openssl_signature_scheme_t *scheme;
+
+ /* select the algorithm, driven by server-isde preference of `available` */
+ for (scheme = available; scheme->scheme_id != UINT16_MAX; ++scheme)
+ for (size_t i = 0; i != num_algorithms; ++i)
+ if (algorithms[i] == scheme->scheme_id)
+ return scheme;
+
+ return NULL;
+}
+
void ptls_openssl_random_bytes(void *buf, size_t len)
{
int ret = RAND_bytes(buf, (int)len);
@@ -698,7 +712,7 @@
struct async_sign_ctx {
ptls_async_job_t super;
- const struct st_ptls_openssl_signature_scheme_t *scheme;
+ const ptls_openssl_signature_scheme_t *scheme;
EVP_MD_CTX *ctx;
ASYNC_WAIT_CTX *waitctx;
ASYNC_JOB *job;
@@ -724,14 +738,26 @@
free(self);
}
-static ptls_async_job_t *async_sign_ctx_new(const struct st_ptls_openssl_signature_scheme_t *scheme, EVP_MD_CTX *ctx, size_t siglen)
+int async_sign_ctx_get_fd(ptls_async_job_t *_self)
+{
+ struct async_sign_ctx *self = (void *)_self;
+ OSSL_ASYNC_FD fds[1];
+ size_t numfds;
+
+ ASYNC_WAIT_CTX_get_all_fds(self->waitctx, NULL, &numfds);
+ assert(numfds == 1);
+ ASYNC_WAIT_CTX_get_all_fds(self->waitctx, fds, &numfds);
+ return (int)fds[0];
+}
+
+static ptls_async_job_t *async_sign_ctx_new(const ptls_openssl_signature_scheme_t *scheme, EVP_MD_CTX *ctx, size_t siglen)
{
struct async_sign_ctx *self;
if ((self = malloc(offsetof(struct async_sign_ctx, sig) + siglen)) == NULL)
return NULL;
- self->super = (ptls_async_job_t){async_sign_ctx_free};
+ self->super = (ptls_async_job_t){async_sign_ctx_free, async_sign_ctx_get_fd};
self->scheme = scheme;
self->ctx = ctx;
self->waitctx = ASYNC_WAIT_CTX_new();
@@ -742,18 +768,6 @@
return &self->super;
}
-OSSL_ASYNC_FD ptls_openssl_get_async_fd(ptls_t *ptls)
-{
- OSSL_ASYNC_FD fds[1];
- size_t numfds;
- struct async_sign_ctx *async = (void *)ptls_get_async_job(ptls);
- assert(async != NULL);
- ASYNC_WAIT_CTX_get_all_fds(async->waitctx, NULL, &numfds);
- assert(numfds == 1);
- ASYNC_WAIT_CTX_get_all_fds(async->waitctx, fds, &numfds);
- return fds[0];
-}
-
static int do_sign_async_job(void *_async)
{
struct async_sign_ctx *async = *(struct async_sign_ctx **)_async;
@@ -792,7 +806,7 @@
#endif
-static int do_sign(EVP_PKEY *key, const struct st_ptls_openssl_signature_scheme_t *scheme, ptls_buffer_t *outbuf,
+static int do_sign(EVP_PKEY *key, const ptls_openssl_signature_scheme_t *scheme, ptls_buffer_t *outbuf,
ptls_iovec_t input, ptls_async_job_t **async)
{
EVP_MD_CTX *ctx = NULL;
@@ -1161,7 +1175,7 @@
ptls_buffer_t *outbuf, ptls_iovec_t input, const uint16_t *algorithms, size_t num_algorithms)
{
ptls_openssl_sign_certificate_t *self = (ptls_openssl_sign_certificate_t *)_self;
- const struct st_ptls_openssl_signature_scheme_t *scheme;
+ const ptls_openssl_signature_scheme_t *scheme;
/* Just resume the asynchronous operation, if one is in flight. */
#if PTLS_OPENSSL_HAVE_ASYNC
@@ -1172,17 +1186,11 @@
}
#endif
- /* Select the algorithm (driven by server-side preference of `self->schemes`), or return failure if none found. */
- for (scheme = self->schemes; scheme->scheme_id != UINT16_MAX; ++scheme) {
- size_t i;
- for (i = 0; i != num_algorithms; ++i)
- if (algorithms[i] == scheme->scheme_id)
- goto Found;
- }
- return PTLS_ALERT_HANDSHAKE_FAILURE;
-
-Found:
+ /* Select the algorithm or return failure if none found. */
+ if ((scheme = ptls_openssl_select_signature_scheme(self->schemes, algorithms, num_algorithms)) == NULL)
+ return PTLS_ALERT_HANDSHAKE_FAILURE;
*selected_algorithm = scheme->scheme_id;
+
#if PTLS_OPENSSL_HAVE_ASYNC
if (!self->async && async != NULL) {
/* indicate to `do_sign` that async mode is disabled for this operation */
@@ -1202,7 +1210,7 @@
static int verify_sign(void *verify_ctx, uint16_t algo, ptls_iovec_t data, ptls_iovec_t signature)
{
EVP_PKEY *key = verify_ctx;
- const struct st_ptls_openssl_signature_scheme_t *scheme;
+ const ptls_openssl_signature_scheme_t *scheme;
EVP_MD_CTX *ctx = NULL;
EVP_PKEY_CTX *pkey_ctx = NULL;
int ret = 0;
@@ -1210,7 +1218,7 @@
if (data.base == NULL)
goto Exit;
- if ((scheme = lookup_signature_schemes(key)) == NULL) {
+ if ((scheme = ptls_openssl_lookup_signature_schemes(key)) == NULL) {
ret = PTLS_ERROR_LIBRARY;
goto Exit;
}
@@ -1282,7 +1290,7 @@
{
*self = (ptls_openssl_sign_certificate_t){.super = {sign_certificate}, .async = 0 /* libssl has it off by default too */};
- if ((self->schemes = lookup_signature_schemes(key)) == NULL)
+ if ((self->schemes = ptls_openssl_lookup_signature_schemes(key)) == NULL)
return PTLS_ERROR_INCOMPATIBLE_KEY;
EVP_PKEY_up_ref(key);
self->key = key;
diff --git a/t/openssl.c b/t/openssl.c
index b4c73b0..4ca49aa 100644
--- a/t/openssl.c
+++ b/t/openssl.c
@@ -143,7 +143,7 @@
#endif
}
-static void test_sign_verify(EVP_PKEY *key, const struct st_ptls_openssl_signature_scheme_t *schemes)
+static void test_sign_verify(EVP_PKEY *key, const ptls_openssl_signature_scheme_t *schemes)
{
for (size_t i = 0; schemes[i].scheme_id != UINT16_MAX; ++i) {
note("scheme 0x%04x", schemes[i].scheme_id);
@@ -198,7 +198,7 @@
test_sign_verify(sc->key, sc->schemes);
}
-static void do_test_ecdsa_sign(int nid, const struct st_ptls_openssl_signature_scheme_t *schemes)
+static void do_test_ecdsa_sign(int nid, const ptls_openssl_signature_scheme_t *schemes)
{
EVP_PKEY *pkey;
@@ -464,10 +464,12 @@
if (num_issued < num_total)
qat_set_pending(offending);
break;
- case PTLS_ERROR_ASYNC_OPERATION:
- qat.conns[offending].wait_fd = ptls_openssl_get_async_fd(qat.conns[offending].tls);
+ case PTLS_ERROR_ASYNC_OPERATION: {
+ ptls_async_job_t *job = ptls_get_async_job(qat.conns[offending].tls);
+ assert(job->get_fd != NULL);
+ qat.conns[offending].wait_fd = job->get_fd(job);
assert(qat.conns[offending].wait_fd != -1);
- break;
+ } break;
default:
fprintf(stderr, "ptls_handshake returned %d\n", hsret);
abort();