-
Notifications
You must be signed in to change notification settings - Fork 145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
async public key operations #291
base: master
Are you sure you want to change the base?
Changes from 1 commit
8ccf436
f163190
5dcab74
7f53e02
cc22775
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The second run here the start_off will be 0 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It can be either. When calling |
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function will be called twice, and we may save the info for efficiency There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed! I think there is no need to generate signdata when this |
||
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if returning to the start, then it requires to push message again. Could we optimize it by keeping this offset? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We cannot keep the offset, as the application is allowed to modify the buffer once That said, I do not think it's worth the effort. There are very few amount of work done before reaching this line. The cost of redoing them is going to be much much less than encrypting the TLS record that would contain the CertificateVerify message. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. okay, understand. |
||
} | ||
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; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The second time runs in sign_certificate, it will fail to find the scheme_md
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is intentional.
By the time
sign_certificate
callback is called for the second time, the signature using an algorithm selected by the async crypto would already be available. The callback no longer needs access to the list of algorithms that the client has offered.I understand it causes a bit of pain when implementing a fake sign_certificate callback, but my preference goes to keeping the TLS stack simple. FWIW, the fake callback added in 5dcab74 caches the selected algorithm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I saw it, and it works perfectly.