Skip to content
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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open

async public key operations #291

wants to merge 5 commits into from

Conversation

kazuho
Copy link
Member

@kazuho kazuho commented Feb 26, 2020

This is an attempt to extend the picotls callback API to support asynchronous public key operations. I think we would be interested in supporting async operations for both key exchange and signing, but at the moment this PR implements only the latter.

In short, this PR extends the sign_certificate callback in the following ways:

  • Introduces new return code PTLS_ERROR_ASYNC_OPERATION. The callback is expected to return this error code when it starts an asynchronous public key crypto calculation. This error code is propagated back to the application.
  • When the public key operation completes, the asynchronous callback should signal that to the application. Picotls does not define how that should be signaled - it is up to the contract between the library that implements the sign_certificate callback and the application.
  • When the application receives that signal, it should call ptls_handshake. Then, picotls would call the sign_certificate callback with the same ctx parameter. The callback then should write back the results of the public key operation, and the handshake continues.

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;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second run here the start_off will be 0

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be either.

When calling ptls_handshake, it is up to the application if it provides a fresh output buffer, or a buffer that already has some data.

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;

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

Copy link
Member Author

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.

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.

@kazuho
Copy link
Member Author

kazuho commented Mar 14, 2020

@pingyucn Thank you for the review.

I have added unit test reusing your idea of having a async callback that wraps an existing implementation, and that have led me to find and fix some bugs.

To be specific, the async callback that has been introduced is the async_sign_certificate function added to t/picotls.c. For detail, please refer to #291 (comment).

@pingyucn
Copy link

Just tried the latest version the async mode, and the test works fine. I will incorporate those code to QAT to boost the performance.

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);

Choose a reason for hiding this comment

The 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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed!

I think there is no need to generate signdata when this send_certificate_verify is called the second time. I will update the code so that the sixth argument to the sign_certificate callback would be ptls_iovec_init(NULL, 0) when send_certificate_verify is called the second time.

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;

Choose a reason for hiding this comment

The 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?

Copy link
Member Author

Choose a reason for hiding this comment

The 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 ptls_handshake returns, or to pass a different buffer which might be empty when calling ptls_handshake the second time. Therefore, the only feasible option would be to cache the bytes that we have built for the CertificateVerify message and reuse it.

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.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, understand.

@kazuho kazuho marked this pull request as ready for review March 19, 2020 15:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants