From 8ccf4369db5a0406abd480e60d20aa6175d1ffcd Mon Sep 17 00:00:00 2001 From: Kazuho Oku Date: Wed, 26 Feb 2020 15:54:47 +0900 Subject: [PATCH 1/5] async mode for certificate-verify --- include/picotls.h | 12 +++-- lib/openssl.c | 4 +- lib/picotls.c | 135 ++++++++++++++++++++++++++++++++-------------- lib/uecc.c | 4 +- t/minicrypto.c | 2 +- t/picotls.c | 12 ++--- 6 files changed, 115 insertions(+), 54 deletions(-) diff --git a/include/picotls.h b/include/picotls.h index 82dd675bc..69061581f 100644 --- a/include/picotls.h +++ b/include/picotls.h @@ -155,6 +155,7 @@ extern "C" { #define PTLS_ERROR_COMPRESSION_FAILURE (PTLS_ERROR_CLASS_INTERNAL + 8) #define PTLS_ERROR_ESNI_RETRY (PTLS_ERROR_CLASS_INTERNAL + 8) #define PTLS_ERROR_REJECT_EARLY_DATA (PTLS_ERROR_CLASS_INTERNAL + 9) +#define PTLS_ERROR_ASYNC_OPERATION (PTLS_ERROR_CLASS_INTERNAL + 10) #define PTLS_ERROR_INCORRECT_BASE64 (PTLS_ERROR_CLASS_INTERNAL + 50) #define PTLS_ERROR_PEM_LABEL_NOT_FOUND (PTLS_ERROR_CLASS_INTERNAL + 51) @@ -511,10 +512,15 @@ 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); /** - * when gerenating CertificateVerify, the core calls the callback to sign the handshake context using the certificate. + * When gerenating CertificateVerify, the core calls the callback to sign the handshake context using the certificate. This callback + * may return PTLS_ERROR_ASYNC_OPERATION, and signal the application outside of picotls when the signature has been generated. At + * that point, the application should call `ptls_handshake`, which in turn would invoke this callback once again. The callback then + * fills `*selected_algorithm` and `output` with the signature being generated. Note that `algorithms` and `num_algorithms` are + * provided only when the callback is called for the first time. The callback can store arbitrary pointer specific to each signature + * generation in `*sign_ctx`. */ -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); +PTLS_CALLBACK_TYPE(int, sign_certificate, ptls_t *tls, void **sign_ctx, 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 diff --git a/lib/openssl.c b/lib/openssl.c index 31c828a2e..1e4aef7c3 100644 --- a/lib/openssl.c +++ b/lib/openssl.c @@ -920,8 +920,8 @@ ptls_define_hash(sha256, SHA256_CTX, SHA256_Init, SHA256_Update, _sha256_final); #define _sha384_final(ctx, md) SHA384_Final((md), (ctx)) ptls_define_hash(sha384, SHA512_CTX, SHA384_Init, SHA384_Update, _sha384_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, void **sign_ctx, 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; diff --git a/lib/picotls.c b/lib/picotls.c index 28f6f7846..37efae62a 100644 --- a/lib/picotls.c +++ b/lib/picotls.c @@ -166,6 +166,7 @@ struct st_ptls_t { PTLS_STATE_CLIENT_EXPECT_FINISHED, PTLS_STATE_SERVER_EXPECT_CLIENT_HELLO, PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO, + PTLS_STATE_SERVER_GENERATING_CERTIFICATE_VERIFY, PTLS_STATE_SERVER_EXPECT_CERTIFICATE, PTLS_STATE_SERVER_EXPECT_CERTIFICATE_VERIFY, /* ptls_send can be called if the state is below here */ @@ -248,6 +249,8 @@ struct st_ptls_t { struct { uint8_t pending_traffic_secret[PTLS_MAX_DIGEST_SIZE]; uint32_t early_data_skipped_bytes; /* if not UINT32_MAX, the server is skipping early data */ + unsigned can_send_session_ticket : 1; + void *sign_certificate_ctx; } server; }; /** @@ -375,6 +378,8 @@ static int hkdf_expand_label(ptls_hash_algorithm_t *algo, void *output, size_t o ptls_iovec_t hash_value, const char *label_prefix); static ptls_aead_context_t *new_aead(ptls_aead_algorithm_t *aead, ptls_hash_algorithm_t *hash, int is_enc, const void *secret, ptls_iovec_t hash_value, const char *label_prefix); +static int server_complete_handshake(ptls_t *tls, ptls_message_emitter_t *emitter, int send_cert_verify, + struct st_ptls_signature_algorithms_t *signature_algorithms); static int is_supported_version(uint16_t v) { @@ -2547,9 +2552,9 @@ static int default_emit_certificate_cb(ptls_emit_certificate_t *_self, ptls_t *t return ret; } -static int send_certificate_and_certificate_verify(ptls_t *tls, ptls_message_emitter_t *emitter, - struct st_ptls_signature_algorithms_t *signature_algorithms, - ptls_iovec_t context, const char *context_string, int push_status_request) +static int send_certificate(ptls_t *tls, ptls_message_emitter_t *emitter, + struct st_ptls_signature_algorithms_t *signature_algorithms, ptls_iovec_t context, + int push_status_request) { static ptls_emit_certificate_t default_emit_certificate = {default_emit_certificate_cb}; ptls_emit_certificate_t *emit_certificate = @@ -2565,26 +2570,43 @@ static int send_certificate_and_certificate_verify(ptls_t *tls, ptls_message_emi if ((ret = emit_certificate->cb(emit_certificate, tls, emitter, tls->key_schedule, context, push_status_request)) != 0) goto Exit; - /* build and send CertificateVerify */ - if (tls->ctx->sign_certificate != NULL) { - ptls_push_message(emitter, tls->key_schedule, PTLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY, { - ptls_buffer_t *sendbuf = emitter->buf; - size_t algo_off = sendbuf->off; - ptls_buffer_push16(sendbuf, 0); /* filled in later */ - ptls_buffer_push_block(sendbuf, 2, { - uint16_t algo; - uint8_t data[PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE]; - size_t datalen = build_certificate_verify_signdata(data, tls->key_schedule, context_string); - if ((ret = tls->ctx->sign_certificate->cb(tls->ctx->sign_certificate, tls, &algo, sendbuf, - ptls_iovec_init(data, datalen), signature_algorithms->list, - signature_algorithms->count)) != 0) { - goto Exit; +Exit: + return ret; +} + +static int send_certificate_verify(ptls_t *tls, ptls_message_emitter_t *emitter, + struct st_ptls_signature_algorithms_t *signature_algorithms, const char *context_string) +{ + size_t start_off = emitter->buf->off; + int ret; + + if (tls->ctx->sign_certificate == NULL) + return 0; + + ptls_push_message(emitter, tls->key_schedule, PTLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY, { + ptls_buffer_t *sendbuf = emitter->buf; + size_t algo_off = sendbuf->off; + ptls_buffer_push16(sendbuf, 0); /* filled in later */ + ptls_buffer_push_block(sendbuf, 2, { + uint16_t algo; + uint8_t data[PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE]; + size_t datalen = build_certificate_verify_signdata(data, tls->key_schedule, context_string); + if ((ret = tls->ctx->sign_certificate->cb(tls->ctx->sign_certificate, tls, &tls->server.sign_certificate_ctx, &algo, + sendbuf, ptls_iovec_init(data, datalen), + signature_algorithms != NULL ? signature_algorithms->list : NULL, + signature_algorithms != NULL ? signature_algorithms->count: 0)) != 0) { + if (ret == PTLS_ERROR_ASYNC_OPERATION) { + assert(tls->is_server || !"async operation only supported on the server-side"); + /* Reset the output to the end of the previous handshake message. CertificateVerify will be rebuilt when the + * async operation completes. */ + emitter->buf->off = start_off; } - sendbuf->base[algo_off] = (uint8_t)(algo >> 8); - sendbuf->base[algo_off + 1] = (uint8_t)algo; - }); + goto Exit; + } + sendbuf->base[algo_off] = (uint8_t)(algo >> 8); + sendbuf->base[algo_off + 1] = (uint8_t)algo; }); - } + }); Exit: return ret; @@ -2843,9 +2865,10 @@ static int client_handle_finished(ptls_t *tls, ptls_message_emitter_t *emitter, ret = PTLS_ALERT_ILLEGAL_PARAMETER; goto Exit; } - ret = send_certificate_and_certificate_verify(tls, emitter, &tls->client.certificate_request.signature_algorithms, - tls->client.certificate_request.context, - PTLS_CLIENT_CERTIFICATE_VERIFY_CONTEXT_STRING, 0); + if ((ret = send_certificate(tls, emitter, &tls->client.certificate_request.signature_algorithms, + tls->client.certificate_request.context, 0)) == 0) + ret = send_certificate_verify(tls, emitter, &tls->client.certificate_request.signature_algorithms, + PTLS_CLIENT_CERTIFICATE_VERIFY_CONTEXT_STRING); free(tls->client.certificate_request.context.base); tls->client.certificate_request.context = ptls_iovec_init(NULL, 0); if (ret != 0) @@ -3805,6 +3828,7 @@ static int server_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptl properties->server.selected_psk_binder.len = selected->len; } } + tls->server.can_send_session_ticket = ch.psk.ke_modes != 0; if (accept_early_data && tls->ctx->max_early_data_size != 0 && psk_index == 0) { if ((tls->pending_handshake_secret = malloc(PTLS_MAX_DIGEST_SIZE)) == NULL) { @@ -3916,23 +3940,54 @@ static int server_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptl }); }); }); - - if (ret != 0) { + if (ret != 0) goto Exit; - } } + /* send certificate */ + if ((ret = send_certificate(tls, emitter, &ch.signature_algorithms, ptls_iovec_init(NULL, 0), ch.status_request)) != 0) + goto Exit; + /* send certificateverify, finished, and complete the handshake */ + if ((ret = server_complete_handshake(tls, emitter, 1, &ch.signature_algorithms)) != 0) + goto Exit; + } else { + /* send finished, and complete the handshake */ + if ((ret = server_complete_handshake(tls, emitter, 0, NULL)) != 0) + goto Exit; + } + +Exit: + free(pubkey.base); + if (ecdh_secret.base != NULL) { + ptls_clear_memory(ecdh_secret.base, ecdh_secret.len); + free(ecdh_secret.base); + } + return ret; - ret = send_certificate_and_certificate_verify(tls, emitter, &ch.signature_algorithms, ptls_iovec_init(NULL, 0), - PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING, ch.status_request); +#undef EMIT_SERVER_HELLO +#undef EMIT_HELLO_RETRY_REQUEST +} + +int server_complete_handshake(ptls_t *tls, ptls_message_emitter_t *emitter, int send_cert_verify, + struct st_ptls_signature_algorithms_t *signature_algorithms) +{ + int ret; - if (ret != 0) { + /* send certificateverify if requested */ + if (send_cert_verify) { + if ((ret = send_certificate_verify(tls, emitter, signature_algorithms, PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING)) != + 0) { + /* signature generation might be an async operation, in that case */ + if (ret == PTLS_STATE_SERVER_GENERATING_CERTIFICATE_VERIFY) + tls->state = PTLS_STATE_SERVER_GENERATING_CERTIFICATE_VERIFY; goto Exit; } } + /* send finished */ if ((ret = send_finished(tls, emitter)) != 0) goto Exit; + /* update traffic secret, keys */ assert(tls->key_schedule->generation == 2); if ((ret = key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0))) != 0) goto Exit; @@ -3958,7 +4013,7 @@ static int server_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptl } /* send session ticket if necessary */ - if (ch.psk.ke_modes != 0 && tls->ctx->ticket_lifetime != 0) { + if (tls->server.can_send_session_ticket != 0 && tls->ctx->ticket_lifetime != 0) { if ((ret = send_session_ticket(tls, emitter)) != 0) goto Exit; } @@ -3970,15 +4025,7 @@ static int server_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptl } Exit: - free(pubkey.base); - if (ecdh_secret.base != NULL) { - ptls_clear_memory(ecdh_secret.base, ecdh_secret.len); - free(ecdh_secret.base); - } return ret; - -#undef EMIT_SERVER_HELLO -#undef EMIT_HELLO_RETRY_REQUEST } static int server_handle_end_of_early_data(ptls_t *tls, ptls_iovec_t message) @@ -4648,6 +4695,8 @@ int ptls_handshake(ptls_t *tls, ptls_buffer_t *_sendbuf, const void *input, size assert(tls->ctx->key_exchanges[0] != NULL); return send_client_hello(tls, &emitter.super, properties, NULL); } + case PTLS_STATE_SERVER_GENERATING_CERTIFICATE_VERIFY: + return server_complete_handshake(tls, &emitter.super, 1, NULL); default: break; } @@ -5226,7 +5275,13 @@ int ptls_server_handle_message(ptls_t *tls, ptls_buffer_t *sendbuf, size_t epoch {sendbuf, &tls->traffic_protection.enc, 0, begin_raw_message, commit_raw_message}, SIZE_MAX, epoch_offsets}; struct st_ptls_record_t rec = {PTLS_CONTENT_TYPE_HANDSHAKE, 0, inlen, input}; - assert(input); + if (tls->state == PTLS_STATE_SERVER_GENERATING_CERTIFICATE_VERIFY) { + int ret; + if ((ret = server_complete_handshake(tls, &emitter.super, 1, NULL)) != 0) + return ret; + } + + assert(input != NULL); if (ptls_get_read_epoch(tls) != in_epoch) return PTLS_ALERT_UNEXPECTED_MESSAGE; diff --git a/lib/uecc.c b/lib/uecc.c index 41718d269..b4b2bf25c 100644 --- a/lib/uecc.c +++ b/lib/uecc.c @@ -131,8 +131,8 @@ static int secp256r1_key_exchange(ptls_key_exchange_algorithm_t *algo, ptls_iove return ret; } -static int secp256r1sha256_sign(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 secp256r1sha256_sign(ptls_sign_certificate_t *_self, ptls_t *tls, void **sign_ctx, uint16_t *selected_algorithm, + ptls_buffer_t *outbuf, ptls_iovec_t input, const uint16_t *algorithms, size_t num_algorithms) { ptls_minicrypto_secp256r1sha256_sign_certificate_t *self = (ptls_minicrypto_secp256r1sha256_sign_certificate_t *)_self; uint8_t hash[32], sig[64]; diff --git a/t/minicrypto.c b/t/minicrypto.c index 4b0d73cf3..028a5cb68 100644 --- a/t/minicrypto.c +++ b/t/minicrypto.c @@ -52,7 +52,7 @@ static void test_secp256r1_sign(void) uECC_make_key(pub, signer.key, uECC_secp256r1()); ptls_buffer_init(&sigbuf, sigbuf_small, sizeof(sigbuf_small)); - ok(secp256r1sha256_sign(&signer.super, NULL, &selected, &sigbuf, ptls_iovec_init(msg, 32), + ok(secp256r1sha256_sign(&signer.super, NULL, NULL, &selected, &sigbuf, ptls_iovec_init(msg, 32), (uint16_t[]){PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256}, 1) == 0); ok(selected == PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256); diff --git a/t/picotls.c b/t/picotls.c index 84666bfaf..f7777832a 100644 --- a/t/picotls.c +++ b/t/picotls.c @@ -809,20 +809,20 @@ static void test_handshake(ptls_iovec_t ticket, int mode, int expect_ticket, int static ptls_sign_certificate_t *sc_orig; size_t sc_callcnt; -static int sign_certificate(ptls_sign_certificate_t *self, ptls_t *tls, uint16_t *selected_algorithm, ptls_buffer_t *output, - ptls_iovec_t input, const uint16_t *algorithms, size_t num_algorithms) +static int sign_certificate(ptls_sign_certificate_t *self, ptls_t *tls, void **sign_ctx, uint16_t *selected_algorithm, + ptls_buffer_t *output, ptls_iovec_t input, const uint16_t *algorithms, size_t num_algorithms) { ++sc_callcnt; - return sc_orig->cb(sc_orig, tls, selected_algorithm, output, input, algorithms, num_algorithms); + return sc_orig->cb(sc_orig, tls, sign_ctx, selected_algorithm, output, input, algorithms, num_algorithms); } static ptls_sign_certificate_t *second_sc_orig; -static int second_sign_certificate(ptls_sign_certificate_t *self, ptls_t *tls, uint16_t *selected_algorithm, ptls_buffer_t *output, - ptls_iovec_t input, const uint16_t *algorithms, size_t num_algorithms) +static int second_sign_certificate(ptls_sign_certificate_t *self, ptls_t *tls, void **sign_ctx, uint16_t *selected_algorithm, + ptls_buffer_t *output, ptls_iovec_t input, const uint16_t *algorithms, size_t num_algorithms) { ++sc_callcnt; - return second_sc_orig->cb(second_sc_orig, tls, selected_algorithm, output, input, algorithms, num_algorithms); + return second_sc_orig->cb(second_sc_orig, tls, sign_ctx, selected_algorithm, output, input, algorithms, num_algorithms); } static void test_full_handshake_impl(int require_client_authentication) From f16319052003316356bbf322cd7fec1c6870e16c Mon Sep 17 00:00:00 2001 From: Kazuho Oku Date: Fri, 13 Mar 2020 12:09:25 +0900 Subject: [PATCH 2/5] [refactor] separately count the invocations of sign_certificate on both sides --- t/picotls.c | 69 ++++++++++++++++++----------------------------------- 1 file changed, 23 insertions(+), 46 deletions(-) diff --git a/t/picotls.c b/t/picotls.c index f7777832a..a0f8846b0 100644 --- a/t/picotls.c +++ b/t/picotls.c @@ -47,6 +47,7 @@ static void test_is_ipaddr(void) ptls_context_t *ctx, *ctx_peer; ptls_verify_certificate_t *verify_certificate; struct st_ptls_ffx_test_variants_t ffx_variants[7]; +static unsigned server_sc_callcnt, client_sc_callcnt; static ptls_cipher_suite_t *find_cipher(ptls_context_t *ctx, uint16_t id) { @@ -567,6 +568,9 @@ static void test_handshake(ptls_iovec_t ticket, int mode, int expect_ticket, int const char *req = "GET / HTTP/1.0\r\n\r\n"; const char *resp = "HTTP/1.0 200 OK\r\n\r\nhello world\n"; + client_sc_callcnt = 0; + server_sc_callcnt = 0; + if (check_ch) ctx->verify_certificate = verify_certificate; @@ -807,12 +811,11 @@ static void test_handshake(ptls_iovec_t ticket, int mode, int expect_ticket, int } static ptls_sign_certificate_t *sc_orig; -size_t sc_callcnt; static int sign_certificate(ptls_sign_certificate_t *self, ptls_t *tls, void **sign_ctx, uint16_t *selected_algorithm, ptls_buffer_t *output, ptls_iovec_t input, const uint16_t *algorithms, size_t num_algorithms) { - ++sc_callcnt; + ++*(ptls_is_server(tls) ? &server_sc_callcnt : &client_sc_callcnt); return sc_orig->cb(sc_orig, tls, sign_ctx, selected_algorithm, output, input, algorithms, num_algorithms); } @@ -821,34 +824,23 @@ static ptls_sign_certificate_t *second_sc_orig; static int second_sign_certificate(ptls_sign_certificate_t *self, ptls_t *tls, void **sign_ctx, uint16_t *selected_algorithm, ptls_buffer_t *output, ptls_iovec_t input, const uint16_t *algorithms, size_t num_algorithms) { - ++sc_callcnt; + ++*(ptls_is_server(tls) ? &server_sc_callcnt : &client_sc_callcnt); return second_sc_orig->cb(second_sc_orig, tls, sign_ctx, selected_algorithm, output, input, algorithms, num_algorithms); } static void test_full_handshake_impl(int require_client_authentication) { - sc_callcnt = 0; - test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_1RTT, 0, 0, require_client_authentication); - if (require_client_authentication) { - ok(sc_callcnt == 2); - } else { - ok(sc_callcnt == 1); - } + ok(server_sc_callcnt == 1); + ok(client_sc_callcnt == require_client_authentication); test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_1RTT, 0, 0, require_client_authentication); - if (require_client_authentication) { - ok(sc_callcnt == 4); - } else { - ok(sc_callcnt == 2); - } + ok(server_sc_callcnt == 1); + ok(client_sc_callcnt == require_client_authentication); test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_1RTT, 0, 1, require_client_authentication); - if (require_client_authentication) { - ok(sc_callcnt == 6); - } else { - ok(sc_callcnt == 3); - } + ok(server_sc_callcnt == 1); + ok(client_sc_callcnt == require_client_authentication); } static void test_full_handshake(void) @@ -868,16 +860,14 @@ static void test_key_update(void) static void test_hrr_handshake(void) { - sc_callcnt = 0; test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_HRR, 0, 0, 0); - ok(sc_callcnt == 1); + ok(server_sc_callcnt == 1); } static void test_hrr_stateless_handshake(void) { - sc_callcnt = 0; test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_HRR_STATELESS, 0, 0, 0); - ok(sc_callcnt == 1); + ok(server_sc_callcnt == 1); } static int on_copy_ticket(ptls_encrypt_ticket_t *self, ptls_t *tls, int is_encrypt, ptls_buffer_t *dst, ptls_iovec_t src) @@ -928,44 +918,31 @@ static void test_resumption_impl(int different_preferred_key_share, int require_ ctx_peer->encrypt_ticket = &et; ctx->save_ticket = &st; - sc_callcnt = 0; test_handshake(saved_ticket, different_preferred_key_share ? TEST_HANDSHAKE_2RTT : TEST_HANDSHAKE_1RTT, 1, 0, 0); - ok(sc_callcnt == 1); + ok(server_sc_callcnt == 1); ok(saved_ticket.base != NULL); /* psk using saved ticket */ test_handshake(saved_ticket, TEST_HANDSHAKE_1RTT, 1, 0, require_client_authentication); - if (require_client_authentication == 1) { - ok(sc_callcnt == 3); - } else { - ok(sc_callcnt == 1); - } + ok(server_sc_callcnt == require_client_authentication); /* client authentication turns off resumption */ + ok(client_sc_callcnt == require_client_authentication); /* 0-rtt psk using saved ticket */ test_handshake(saved_ticket, TEST_HANDSHAKE_EARLY_DATA, 1, 0, require_client_authentication); - if (require_client_authentication == 1) { - ok(sc_callcnt == 5); - } else { - ok(sc_callcnt == 1); - } + ok(server_sc_callcnt == require_client_authentication); /* client authentication turns off resumption */ + ok(client_sc_callcnt == require_client_authentication); ctx->require_dhe_on_psk = 1; /* psk-dhe using saved ticket */ test_handshake(saved_ticket, TEST_HANDSHAKE_1RTT, 1, 0, require_client_authentication); - if (require_client_authentication == 1) { - ok(sc_callcnt == 7); - } else { - ok(sc_callcnt == 1); - } + ok(server_sc_callcnt == require_client_authentication); /* client authentication turns off resumption */ + ok(client_sc_callcnt == require_client_authentication); /* 0-rtt psk-dhe using saved ticket */ test_handshake(saved_ticket, TEST_HANDSHAKE_EARLY_DATA, 1, 0, require_client_authentication); - if (require_client_authentication == 1) { - ok(sc_callcnt == 9); - } else { - ok(sc_callcnt == 1); - } + ok(server_sc_callcnt == require_client_authentication); /* client authentication turns off resumption */ + ok(client_sc_callcnt == require_client_authentication); ctx->require_dhe_on_psk = 0; ctx_peer->ticket_lifetime = 0; From 5dcab74533e6a586889575b0cd5e18b8d8afb2cb Mon Sep 17 00:00:00 2001 From: Kazuho Oku Date: Sat, 14 Mar 2020 14:20:03 +0900 Subject: [PATCH 3/5] add an async sign_certificate callback inspired by PoC written by @pingyucn --- t/picotls.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 7 deletions(-) diff --git a/t/picotls.c b/t/picotls.c index a0f8846b0..1f6ab6ea3 100644 --- a/t/picotls.c +++ b/t/picotls.c @@ -47,7 +47,7 @@ static void test_is_ipaddr(void) ptls_context_t *ctx, *ctx_peer; ptls_verify_certificate_t *verify_certificate; struct st_ptls_ffx_test_variants_t ffx_variants[7]; -static unsigned server_sc_callcnt, client_sc_callcnt; +static unsigned server_sc_callcnt, client_sc_callcnt, async_sc_callcnt; static ptls_cipher_suite_t *find_cipher(ptls_context_t *ctx, uint16_t id) { @@ -570,6 +570,7 @@ static void test_handshake(ptls_iovec_t ticket, int mode, int expect_ticket, int client_sc_callcnt = 0; server_sc_callcnt = 0; + async_sc_callcnt = 0; if (check_ch) ctx->verify_certificate = verify_certificate; @@ -653,11 +654,14 @@ static void test_handshake(ptls_iovec_t ticket, int mode, int expect_ticket, int consumed = cbuf.off; ret = ptls_handshake(server, &sbuf, cbuf.base, &consumed, &server_hs_prop); - if (require_client_authentication == 1) { + if (require_client_authentication) { + /* at the moment, async sign-certificate is not supported in this path, neither on the client-side or the server-side */ ok(ptls_is_psk_handshake(server) == 0); ok(ret == PTLS_ERROR_IN_PROGRESS); - } else { + } else if (mode == TEST_HANDSHAKE_EARLY_DATA) { ok(ret == 0); + } else { + ok(ret == 0 || ret == PTLS_ERROR_ASYNC_OPERATION); } ok(sbuf.off != 0); @@ -672,7 +676,7 @@ static void test_handshake(ptls_iovec_t ticket, int mode, int expect_ticket, int ok(ptls_get_negotiated_protocol(server) == NULL); } - if (mode == TEST_HANDSHAKE_EARLY_DATA && require_client_authentication == 0) { + if (mode == TEST_HANDSHAKE_EARLY_DATA && !require_client_authentication) { ok(consumed < cbuf.off); memmove(cbuf.base, cbuf.base + consumed, cbuf.off - consumed); cbuf.off -= consumed; @@ -694,6 +698,21 @@ static void test_handshake(ptls_iovec_t ticket, int mode, int expect_ticket, int cbuf.off = 0; } + while (ret == PTLS_ERROR_ASYNC_OPERATION) { + consumed = sbuf.off; + ret = ptls_handshake(client, &cbuf, sbuf.base, &consumed, NULL); + ok(ret == PTLS_ERROR_IN_PROGRESS); + ok(consumed == sbuf.off); + ok(cbuf.off == 0); + sbuf.off = 0; + ret = ptls_handshake(server, &sbuf, NULL, NULL, &server_hs_prop); + } + if (require_client_authentication) { + ok(ret == PTLS_ERROR_IN_PROGRESS); + } else { + ok(ret == 0); + } + consumed = sbuf.off; ret = ptls_handshake(client, &cbuf, sbuf.base, &consumed, NULL); ok(ret == 0); @@ -819,6 +838,36 @@ static int sign_certificate(ptls_sign_certificate_t *self, ptls_t *tls, void **s return sc_orig->cb(sc_orig, tls, sign_ctx, selected_algorithm, output, input, algorithms, num_algorithms); } +static int async_sign_certificate(ptls_sign_certificate_t *self, ptls_t *tls, void **sign_ctx, uint16_t *selected_algorithm, + ptls_buffer_t *output, ptls_iovec_t input, const uint16_t *algorithms, size_t num_algorithms) +{ + if (!ptls_is_server(tls)) { + /* do it synchronously, as async mode is only supported on the server-side */ + } else if (*sign_ctx == NULL) { + /* first invocation, make a fake call to the backend and obtain the algorithm, return it, but not the signature */ + ptls_buffer_t fakebuf; + ptls_buffer_init(&fakebuf, "", 0); + int ret = sign_certificate(self, tls, NULL /* we know it's not used */, selected_algorithm, &fakebuf, input, algorithms, + num_algorithms); + assert(ret == 0); + ptls_buffer_dispose(&fakebuf); + static uint16_t selected; + selected = *selected_algorithm; + *sign_ctx = &selected; + --server_sc_callcnt; + ++async_sc_callcnt; + return PTLS_ERROR_ASYNC_OPERATION; + } else { + /* second invocation, restore algorithm, and delegate the call */ + assert(algorithms == NULL); + algorithms = *sign_ctx; + num_algorithms = 1; + } + + return sign_certificate(self, tls, NULL /* we know that it's not used */, selected_algorithm, output, input, algorithms, + num_algorithms); +} + static ptls_sign_certificate_t *second_sc_orig; static int second_sign_certificate(ptls_sign_certificate_t *self, ptls_t *tls, void **sign_ctx, uint16_t *selected_algorithm, @@ -828,29 +877,32 @@ static int second_sign_certificate(ptls_sign_certificate_t *self, ptls_t *tls, v return second_sc_orig->cb(second_sc_orig, tls, sign_ctx, selected_algorithm, output, input, algorithms, num_algorithms); } -static void test_full_handshake_impl(int require_client_authentication) +static void test_full_handshake_impl(int require_client_authentication, int is_async) { test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_1RTT, 0, 0, require_client_authentication); ok(server_sc_callcnt == 1); + ok(async_sc_callcnt == is_async); ok(client_sc_callcnt == require_client_authentication); test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_1RTT, 0, 0, require_client_authentication); ok(server_sc_callcnt == 1); + ok(async_sc_callcnt == is_async); ok(client_sc_callcnt == require_client_authentication); test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_1RTT, 0, 1, require_client_authentication); ok(server_sc_callcnt == 1); + ok(async_sc_callcnt == is_async); ok(client_sc_callcnt == require_client_authentication); } static void test_full_handshake(void) { - test_full_handshake_impl(0); + test_full_handshake_impl(0, 0); } static void test_full_handshake_with_client_authentication(void) { - test_full_handshake_impl(1); + test_full_handshake_impl(1, 0); } static void test_key_update(void) @@ -969,6 +1021,18 @@ static void test_resumption_with_client_authentication(void) test_resumption_impl(0, 1); } +static void test_async_sign_certificate(void) +{ + assert(ctx_peer->sign_certificate->cb == sign_certificate); + + ptls_sign_certificate_t async_sc = {async_sign_certificate}, *orig_sc = ctx_peer->sign_certificate; + ctx_peer->sign_certificate = &async_sc; + + test_full_handshake_impl(0, 1); + + ctx_peer->sign_certificate = orig_sc; +} + static void test_enforce_retry(int use_cookie) { ptls_t *client, *server; @@ -1419,6 +1483,8 @@ static void test_all_handshakes(void) subtest("resumption-different-preferred-key-share", test_resumption_different_preferred_key_share); subtest("resumption-with-client-authentication", test_resumption_with_client_authentication); + subtest("async-sign-certificate", test_async_sign_certificate); + subtest("enforce-retry-stateful", test_enforce_retry_stateful); subtest("enforce-retry-stateless", test_enforce_retry_stateless); From 7f53e02d7072b44cc8cb4598f75e38c17e4f872e Mon Sep 17 00:00:00 2001 From: Kazuho Oku Date: Sat, 14 Mar 2020 14:21:11 +0900 Subject: [PATCH 4/5] check for the correct error codes --- lib/picotls.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/picotls.c b/lib/picotls.c index 37efae62a..f0ae792e5 100644 --- a/lib/picotls.c +++ b/lib/picotls.c @@ -3977,7 +3977,7 @@ int server_complete_handshake(ptls_t *tls, ptls_message_emitter_t *emitter, int if ((ret = send_certificate_verify(tls, emitter, signature_algorithms, PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING)) != 0) { /* signature generation might be an async operation, in that case */ - if (ret == PTLS_STATE_SERVER_GENERATING_CERTIFICATE_VERIFY) + if (ret == PTLS_ERROR_ASYNC_OPERATION) tls->state = PTLS_STATE_SERVER_GENERATING_CERTIFICATE_VERIFY; goto Exit; } @@ -4722,6 +4722,7 @@ int ptls_handshake(ptls_t *tls, ptls_buffer_t *_sendbuf, const void *input, size case 0: case PTLS_ERROR_IN_PROGRESS: case PTLS_ERROR_STATELESS_RETRY: + case PTLS_ERROR_ASYNC_OPERATION: break; default: /* flush partially written response */ From cc22775d4823c1bd6f5f7951295e889285d1c851 Mon Sep 17 00:00:00 2001 From: Kazuho Oku Date: Sat, 14 Mar 2020 14:21:18 +0900 Subject: [PATCH 5/5] clang-format --- lib/picotls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/picotls.c b/lib/picotls.c index f0ae792e5..7baca0fa1 100644 --- a/lib/picotls.c +++ b/lib/picotls.c @@ -2594,7 +2594,7 @@ static int send_certificate_verify(ptls_t *tls, ptls_message_emitter_t *emitter, if ((ret = tls->ctx->sign_certificate->cb(tls->ctx->sign_certificate, tls, &tls->server.sign_certificate_ctx, &algo, sendbuf, ptls_iovec_init(data, datalen), signature_algorithms != NULL ? signature_algorithms->list : NULL, - signature_algorithms != NULL ? signature_algorithms->count: 0)) != 0) { + signature_algorithms != NULL ? signature_algorithms->count : 0)) != 0) { if (ret == PTLS_ERROR_ASYNC_OPERATION) { assert(tls->is_server || !"async operation only supported on the server-side"); /* Reset the output to the end of the previous handshake message. CertificateVerify will be rebuilt when the