Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
ded17f2
add PTLS_ERROR_ASYNC_OPERATION application error
sharksforarms Jul 19, 2022
aa4f09a
add async capabilities to openssl implementation
sharksforarms Jul 19, 2022
4d6f6cd
fix minicrypto and uecc impls
sharksforarms Jul 19, 2022
2b4aff9
test async api
sharksforarms Jul 19, 2022
f7ea678
internally buffer the write buffer
sharksforarms Jul 19, 2022
621509b
Revert "internally buffer the write buffer"
sharksforarms Jul 19, 2022
4ab5ba7
internally buffer the asynchronous data
sharksforarms Jul 19, 2022
a55938b
fix: openssl copies the data from ptr, send a pointer instead
sharksforarms Jul 19, 2022
04aa8cf
fix: don't clobber job ptr
sharksforarms Jul 21, 2022
b9a0040
fix header decl for `ptls_openssl_get_async_fd`
sharksforarms Jul 22, 2022
7c6707a
allocate sign_ctx when we know siglen
sharksforarms Jul 22, 2022
2792654
rename `ptls_get_sign_ctx` to `ptls_get_sign_context`
sharksforarms Jul 22, 2022
c931aef
move `ASYNC_WAIT_CTX_new` to `sign_ctx_alloc`
sharksforarms Jul 25, 2022
83607a8
add a callback to signing api to free resources
sharksforarms Jul 26, 2022
a58b7e3
define `PTLS_OPENSSL_HAVE_ASYNC`
sharksforarms Aug 15, 2022
cc5a20e
support synchronous client signing
sharksforarms Aug 16, 2022
10472b5
coverity
sharksforarms Aug 24, 2022
ec6e099
add `async_handshake` flag to ptls context
sharksforarms Aug 24, 2022
e1148cd
improve docs
sharksforarms Sep 15, 2022
b4ab91a
rename cb to cancel_cb
sharksforarms Sep 15, 2022
d2ffef1
rename server_complete_handshake to server_finish_handshake
sharksforarms Sep 15, 2022
6f558cf
move async toggle to st_ptls_openssl_sign_certificate_t
sharksforarms Sep 15, 2022
6b71b99
check the value
sharksforarms Sep 15, 2022
5c0a108
async by default
sharksforarms Sep 15, 2022
8fcc793
remove excess empty line
kazuho Sep 26, 2022
98db233
no relational operator for a boolean value
kazuho Sep 26, 2022
21c5f0a
use offsetof to obtain accurate result, avoid duplicated code
kazuho Sep 26, 2022
b9045b6
maybe alloc <-> free is better in picotls
kazuho Sep 26, 2022
6ceb6f9
clang-format
kazuho Sep 26, 2022
2ca326e
`cancel_cb` is set iff async is used
kazuho Sep 26, 2022
8c9a17d
let it build
kazuho Sep 26, 2022
20aeccb
minimize diff from master, turning async mode into an optional path
kazuho Sep 26, 2022
2224ab5
set and clear attributes
kazuho Sep 26, 2022
6f44511
clang-format
kazuho Sep 26, 2022
a419451
Merge branch 'master' into kazuho/async-sign
kazuho Sep 26, 2022
6ba2a0d
simplify the #ifdefs
kazuho Sep 26, 2022
2b539ab
rename now that the context is specifically for async operation
kazuho Sep 26, 2022
c311ed5
remove excess empty line
kazuho Sep 26, 2022
97fab0c
return error if scheme lookup fails
kazuho Sep 26, 2022
163504b
return error when matching algo is not found
kazuho Sep 26, 2022
1649016
libressl compatibility
kazuho Sep 26, 2022
a7dfa26
use "async(_ctx)" throughout, to better reflect the fact that the var…
kazuho Sep 26, 2022
b2b96bb
ptr is guaranteed to be non-null
kazuho Sep 26, 2022
3f84111
update function name
kazuho Sep 26, 2022
f11a78e
use compound init
kazuho Sep 26, 2022
a09cdeb
use OO-style context for consistency
kazuho Sep 26, 2022
28c3212
update test following prev. commit
kazuho Sep 26, 2022
f267a4e
remove stale comment
kazuho Sep 26, 2022
2a87d5e
update comment
kazuho Sep 26, 2022
eb337de
we do not need this
kazuho Sep 26, 2022
cac3a7b
add test code that runs multiple handshakes in parallel (which also t…
kazuho Sep 27, 2022
54f9bc9
for Windows compatibility use OSSL_ASYNC_FD
kazuho Sep 27, 2022
98d4bad
union member `ptls_t::server` is only available when running as a server
kazuho Oct 3, 2022
7f83d94
simply make the async task non-cancellable
kazuho Oct 3, 2022
39cd815
our convention is `#define ... 1` then use `#if`
kazuho Oct 3, 2022
dc8c0e5
disable async tests on windows, as sys/select.h and sys/time.h are un…
kazuho Oct 3, 2022
9cf089f
Merge branch 'kazuho/async-sign' into kazuho/async-sign-non-cancellable
kazuho Oct 3, 2022
3d61919
the name can be generic
kazuho Oct 3, 2022
b814413
amend
kazuho Oct 3, 2022
24b6b6f
Merge branch 'kazuho/async-sign' into kazuho/async-sign-non-cancellable
kazuho Oct 3, 2022
63d2e80
doc comment
kazuho Oct 3, 2022
04f44c1
avoid desynchronization
kazuho Oct 3, 2022
4c1d03f
oops
kazuho Oct 3, 2022
a3d3590
it can be explained at one place
kazuho Oct 3, 2022
3eaddfb
reduce diff from master
kazuho Oct 3, 2022
10ddbac
Merge branch 'kazuho/async-sign' into kazuho/async-sign-non-cancellable
kazuho Oct 3, 2022
ee8f1ff
Merge pull request #424 from h2o/kazuho/async-sign-non-cancellable
kazuho Oct 3, 2022
259f8a3
Update lib/openssl.c
kazuho Oct 3, 2022
35bf9ee
confirm that the response generated asynchronously looks okay
kazuho Oct 6, 2022
770a665
use aes128gcmsha256 in the benchmark code
kazuho Oct 6, 2022
d3dbe69
Merge branch 'master' into kazuho/async-sign
kazuho Oct 27, 2022
6656a3a
Merge branch 'master' into kazuho/async-sign
kazuho Dec 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions include/picotls.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ extern "C" {
#define PTLS_ERROR_ESNI_RETRY (PTLS_ERROR_CLASS_INTERNAL + 8)
#define PTLS_ERROR_REJECT_EARLY_DATA (PTLS_ERROR_CLASS_INTERNAL + 9)
#define PTLS_ERROR_DELEGATE (PTLS_ERROR_CLASS_INTERNAL + 10)
#define PTLS_ERROR_ASYNC_OPERATION (PTLS_ERROR_CLASS_INTERNAL + 11)

#define PTLS_ERROR_INCORRECT_BASE64 (PTLS_ERROR_CLASS_INTERNAL + 50)
#define PTLS_ERROR_PEM_LABEL_NOT_FOUND (PTLS_ERROR_CLASS_INTERNAL + 51)
Expand Down Expand Up @@ -692,10 +693,17 @@ PTLS_CALLBACK_TYPE(int, on_client_hello, ptls_t *tls, ptls_on_client_hello_param
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);
/**
* when gerenating CertificateVerify, the core calls the callback to sign the handshake context using the certificate.
* context object of an async operation (e.g., RSA signature generation)
*/
PTLS_CALLBACK_TYPE(int, sign_certificate, ptls_t *tls, uint16_t *selected_algorithm, ptls_buffer_t *output, ptls_iovec_t input,
const uint16_t *algorithms, size_t num_algorithms);
typedef struct st_ptls_async_job_t {
void (*destroy_)(struct st_ptls_async_job_t *self);
} ptls_async_job_t;
/**
* When gerenating CertificateVerify, the core calls the callback to sign the handshake context using the certificate. This callback
* supports asynchronous mode; see `ptls_openssl_sign_certificate_t` for more information.
*/
PTLS_CALLBACK_TYPE(int, sign_certificate, ptls_t *tls, ptls_async_job_t **async, uint16_t *selected_algorithm,
ptls_buffer_t *output, ptls_iovec_t input, const uint16_t *algorithms, size_t num_algorithms);
/**
* after receiving Certificate, the core calls the callback to verify the certificate chain and to obtain a pointer to a
* callback that should be used for verifying CertificateVerify. If an error occurs between a successful return from this
Expand Down Expand Up @@ -1418,6 +1426,10 @@ ptls_context_t *ptls_get_context(ptls_t *tls);
* updates the context of a connection. Can be called from `on_client_hello` callback.
*/
void ptls_set_context(ptls_t *tls, ptls_context_t *ctx);
/**
* get the signature context
*/
ptls_async_job_t *ptls_get_async_job(ptls_t *tls);
/**
* returns the client-random
*/
Expand Down
22 changes: 22 additions & 0 deletions include/picotls/openssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ extern "C" {
#endif
#endif

#if OPENSSL_VERSION_NUMBER >= 0x10100010L && !defined(LIBRESSL_VERSION_NUMBER)
#if !defined(OPENSSL_NO_ASYNC)
#include <openssl/async.h>
#define PTLS_OPENSSL_HAVE_ASYNC 1
#endif
#endif

extern ptls_key_exchange_algorithm_t ptls_openssl_secp256r1;
#ifdef NID_secp384r1
#define PTLS_OPENSSL_HAVE_SECP384R1 1
Expand Down Expand Up @@ -118,6 +125,13 @@ void ptls_openssl_random_bytes(void *buf, size_t len);
*/
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 {
uint16_t scheme_id;
const EVP_MD *(*scheme_md)(void);
Expand All @@ -127,6 +141,14 @@ 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 */
/**
* 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
* code, the user should call `ptls_openssl_get_async_fd` to obtain the file descriptor that represents the asynchronous
* operation and poll it for read. Once the file descriptor becomes readable, the user calls `ptls_handshake` once again to
* obtain the handshake messages being generated, or call `ptls_free` to discard TLS state.
*/
unsigned async : 1;
} ptls_openssl_sign_certificate_t;

int ptls_openssl_init_sign_certificate(ptls_openssl_sign_certificate_t *self, EVP_PKEY *key);
Expand Down
138 changes: 132 additions & 6 deletions lib/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -694,8 +694,106 @@ int ptls_openssl_create_key_exchange(ptls_key_exchange_context_t **ctx, EVP_PKEY
}
}

#if PTLS_OPENSSL_HAVE_ASYNC

struct async_sign_ctx {
ptls_async_job_t super;
const struct st_ptls_openssl_signature_scheme_t *scheme;
EVP_MD_CTX *ctx;
ASYNC_WAIT_CTX *waitctx;
ASYNC_JOB *job;
size_t siglen;
uint8_t sig[0]; // must be last, see `async_sign_ctx_new`
};

static void async_sign_ctx_free(ptls_async_job_t *_self)
{
struct async_sign_ctx *self = (void *)_self;

/* Once the async operation is complete, the user might call `ptls_free` instead of `ptls_handshake`. In such case, to avoid
* desynchronization, let the backend read the result from the socket. The code below is a loop, but it is not going to block;
* it is the responsibility of the user to refrain from calling `ptls_free` until the asynchronous operation is complete. */
if (self->job != NULL) {
int ret;
while (ASYNC_start_job(&self->job, self->waitctx, &ret, NULL, NULL, 0) == ASYNC_PAUSE)
;
}

EVP_MD_CTX_destroy(self->ctx);
ASYNC_WAIT_CTX_free(self->waitctx);
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)
{
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->scheme = scheme;
self->ctx = ctx;
self->waitctx = ASYNC_WAIT_CTX_new();
self->job = NULL;
self->siglen = siglen;
memset(self->sig, 0, siglen);

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;
return EVP_DigestSignFinal(async->ctx, async->sig, &async->siglen);
}

static int do_sign_async(ptls_buffer_t *outbuf, ptls_async_job_t **_async)
{
struct async_sign_ctx *async = (void *)*_async;
int ret;

switch (ASYNC_start_job(&async->job, async->waitctx, &ret, do_sign_async_job, &async, sizeof(async))) {
Comment thread
kazuho marked this conversation as resolved.
case ASYNC_PAUSE:
return PTLS_ERROR_ASYNC_OPERATION; // async operation inflight; bail out without getting rid of async context
case ASYNC_ERR:
ret = PTLS_ERROR_LIBRARY;
break;
case ASYNC_NO_JOBS:
ret = PTLS_ERROR_LIBRARY;
break;
case ASYNC_FINISH:
async->job = NULL;
ptls_buffer_pushv(outbuf, async->sig, async->siglen);
ret = 0;
break;
default:
ret = PTLS_ERROR_LIBRARY;
break;
}

Exit:
async_sign_ctx_free(&async->super);
*_async = NULL;
return ret;
}

#endif

static int do_sign(EVP_PKEY *key, const struct st_ptls_openssl_signature_scheme_t *scheme, ptls_buffer_t *outbuf,
ptls_iovec_t input)
ptls_iovec_t input, ptls_async_job_t **async)
{
EVP_MD_CTX *ctx = NULL;
const EVP_MD *md = scheme->scheme_md != NULL ? scheme->scheme_md() : NULL;
Expand Down Expand Up @@ -751,6 +849,18 @@ static int do_sign(EVP_PKEY *key, const struct st_ptls_openssl_signature_scheme_
ret = PTLS_ERROR_LIBRARY;
goto Exit;
}
/* If permitted by the caller (by providing a non-NULL `async` slot), use the asynchronous signing method and return
* immediately. */
#if PTLS_OPENSSL_HAVE_ASYNC
if (async != NULL) {
if ((*async = async_sign_ctx_new(scheme, ctx, siglen)) == NULL) {
ret = PTLS_ERROR_NO_MEMORY;
goto Exit;
}
return do_sign_async(outbuf, async);
}
#endif
/* Otherwise, generate signature synchronously. */
if ((ret = ptls_buffer_reserve(outbuf, siglen)) != 0)
goto Exit;
if (EVP_DigestSignFinal(ctx, outbuf->base + outbuf->off, &siglen) != 1) {
Expand Down Expand Up @@ -1047,13 +1157,22 @@ ptls_define_hash(sha384, SHA512_CTX, SHA384_Init, SHA384_Update, _sha384_final);
#define _sha512_final(ctx, md) SHA512_Final((md), (ctx))
ptls_define_hash(sha512, SHA512_CTX, SHA512_Init, SHA512_Update, _sha512_final);

static int sign_certificate(ptls_sign_certificate_t *_self, ptls_t *tls, uint16_t *selected_algorithm, ptls_buffer_t *outbuf,
ptls_iovec_t input, const uint16_t *algorithms, size_t num_algorithms)
static int sign_certificate(ptls_sign_certificate_t *_self, ptls_t *tls, ptls_async_job_t **async, uint16_t *selected_algorithm,
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;

/* select the algorithm (driven by server-side preference of `self->schemes`) */
/* Just resume the asynchronous operation, if one is in flight. */
#if PTLS_OPENSSL_HAVE_ASYNC
if (async != NULL && *async != NULL) {
struct async_sign_ctx *sign_ctx = (struct async_sign_ctx *)(*async);
*selected_algorithm = sign_ctx->scheme->scheme_id;
return do_sign_async(outbuf, async);
}
#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)
Expand All @@ -1064,7 +1183,14 @@ static int sign_certificate(ptls_sign_certificate_t *_self, ptls_t *tls, uint16_

Found:
*selected_algorithm = scheme->scheme_id;
return do_sign(self->key, scheme, outbuf, input);
#if PTLS_OPENSSL_HAVE_ASYNC
if (!self->async && async != NULL) {
/* indicate to `do_sign` that async mode is disabled for this operation */
assert(*async == NULL);
async = NULL;
}
#endif
return do_sign(self->key, scheme, outbuf, input, async);
}

static X509 *to_x509(ptls_iovec_t vec)
Expand Down Expand Up @@ -1154,7 +1280,7 @@ static int verify_sign(void *verify_ctx, uint16_t algo, ptls_iovec_t data, ptls_

int ptls_openssl_init_sign_certificate(ptls_openssl_sign_certificate_t *self, EVP_PKEY *key)
{
*self = (ptls_openssl_sign_certificate_t){{sign_certificate}};
*self = (ptls_openssl_sign_certificate_t){.super = {sign_certificate}, .async = 1};

if ((self->schemes = lookup_signature_schemes(key)) == NULL)
return PTLS_ERROR_INCOMPATIBLE_KEY;
Expand Down
Loading