static void s_s2n_pkcs11_async_pkey_task()

in source/s2n/s2n_tls_channel_handler.c [630:808]


static void s_s2n_pkcs11_async_pkey_task(
    struct aws_channel_task *channel_task,
    void *arg,
    enum aws_task_status status) {

    struct s2n_handler *s2n_handler = AWS_CONTAINER_OF(channel_task, struct s2n_handler, async_pkey_task);
    struct aws_channel_handler *handler = &s2n_handler->handler;
    struct s2n_async_pkey_op *op = arg;
    bool success = false;

    uint8_t *input_data = NULL;     /* allocated later */
    struct aws_byte_buf output_buf; /* initialized later */
    AWS_ZERO_STRUCT(output_buf);

    /* if things started failing since this task was scheduled, just clean up and bail out */
    if (status != AWS_TASK_STATUS_RUN_READY || s2n_handler->state != NEGOTIATION_ONGOING) {
        goto clean_up;
    }

    AWS_LOGF_TRACE(AWS_LS_IO_TLS, "id=%p: Running PKCS#11 async pkey task", (void *)handler);

    /* We check all s2n_async_pkey_op functions for success,
     * but they shouldn't fail if they're called correctly.
     * Even if the output is bad, the failure will happen later in s2n_negotiate() */

    uint32_t input_size = 0;
    if (s2n_async_pkey_op_get_input_size(op, &input_size)) {
        AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Failed querying s2n async pkey op size", (void *)handler);
        aws_raise_error(AWS_ERROR_INVALID_STATE);
        goto error;
    }

    input_data = aws_mem_acquire(handler->alloc, input_size);
    if (s2n_async_pkey_op_get_input(op, input_data, input_size)) {
        AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Failed querying s2n async pkey input", (void *)handler);
        aws_raise_error(AWS_ERROR_INVALID_STATE);
        goto error;
    }
    struct aws_byte_cursor input_cursor = aws_byte_cursor_from_array(input_data, input_size);

    s2n_async_pkey_op_type op_type = 0;
    if (s2n_async_pkey_op_get_op_type(op, &op_type)) {
        AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Failed querying s2n async pkey op type", (void *)handler);
        aws_raise_error(AWS_ERROR_INVALID_STATE);
        goto error;
    }

    /* Gather additional information if this is a SIGN operation */
    enum aws_tls_signature_algorithm aws_sign_alg = 0;
    enum aws_tls_hash_algorithm aws_digest_alg = 0;
    if (op_type == S2N_ASYNC_SIGN) {
        s2n_tls_signature_algorithm s2n_sign_alg = 0;
        if (s2n_connection_get_selected_client_cert_signature_algorithm(s2n_handler->connection, &s2n_sign_alg)) {
            AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Failed getting s2n client cert signature algorithm", (void *)handler);
            aws_raise_error(AWS_ERROR_INVALID_STATE);
            goto error;
        }

        aws_sign_alg = s_s2n_to_aws_signature_algorithm(s2n_sign_alg);
        if (aws_sign_alg == AWS_TLS_SIGNATURE_UNKNOWN) {
            AWS_LOGF_ERROR(
                AWS_LS_IO_TLS,
                "id=%p: Cannot sign with s2n_tls_signature_algorithm=%d. Algorithm currently unsupported",
                (void *)handler,
                s2n_sign_alg);
            aws_raise_error(AWS_IO_TLS_SIGNATURE_ALGORITHM_UNSUPPORTED);
            goto error;
        }

        s2n_tls_hash_algorithm s2n_digest_alg = 0;
        if (s2n_connection_get_selected_client_cert_digest_algorithm(s2n_handler->connection, &s2n_digest_alg)) {
            AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Failed getting s2n client cert digest algorithm", (void *)handler);
            aws_raise_error(AWS_ERROR_INVALID_STATE);
            goto error;
        }

        aws_digest_alg = s_s2n_to_aws_hash_algorithm(s2n_digest_alg);
        if (aws_digest_alg == AWS_TLS_HASH_UNKNOWN) {
            AWS_LOGF_ERROR(
                AWS_LS_IO_TLS,
                "id=%p: Cannot sign digest created with s2n_tls_hash_algorithm=%d. Algorithm currently unsupported",
                (void *)handler,
                s2n_digest_alg);
            aws_raise_error(AWS_IO_TLS_DIGEST_ALGORITHM_UNSUPPORTED);
            goto error;
        }
    }

    /*********** BEGIN CRITICAL SECTION ***********/
    aws_mutex_lock(&s2n_handler->s2n_ctx->pkcs11.session_lock);
    bool success_while_locked = false;

    switch (op_type) {
        case S2N_ASYNC_DECRYPT:
            if (aws_pkcs11_lib_decrypt(
                    s2n_handler->s2n_ctx->pkcs11.lib,
                    s2n_handler->s2n_ctx->pkcs11.session_handle,
                    s2n_handler->s2n_ctx->pkcs11.private_key_handle,
                    s2n_handler->s2n_ctx->pkcs11.private_key_type,
                    input_cursor,
                    handler->alloc,
                    &output_buf)) {

                AWS_LOGF_ERROR(
                    AWS_LS_IO_TLS,
                    "id=%p: PKCS#11 decrypt failed, error %s",
                    (void *)handler,
                    aws_error_name(aws_last_error()));
                goto unlock;
            }
            break;

        case S2N_ASYNC_SIGN:
            if (aws_pkcs11_lib_sign(
                    s2n_handler->s2n_ctx->pkcs11.lib,
                    s2n_handler->s2n_ctx->pkcs11.session_handle,
                    s2n_handler->s2n_ctx->pkcs11.private_key_handle,
                    s2n_handler->s2n_ctx->pkcs11.private_key_type,
                    input_cursor,
                    handler->alloc,
                    aws_digest_alg,
                    aws_sign_alg,
                    &output_buf)) {

                AWS_LOGF_ERROR(
                    AWS_LS_IO_TLS,
                    "id=%p: PKCS#11 sign failed, error %s",
                    (void *)handler,
                    aws_error_name(aws_last_error()));
                goto unlock;
            }
            break;

        default:
            AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Unknown s2n_async_pkey_op_type:%d", (void *)handler, (int)op_type);
            aws_raise_error(AWS_ERROR_INVALID_STATE);
            goto unlock;
    }

    success_while_locked = true;
unlock:
    aws_mutex_unlock(&s2n_handler->s2n_ctx->pkcs11.session_lock);
    /*********** END CRITICAL SECTION ***********/

    if (!success_while_locked) {
        goto error;
    }

    AWS_LOGF_TRACE(
        AWS_LS_IO_TLS, "id=%p: PKCS#11 operation complete. output-size:%zu", (void *)handler, output_buf.len);

    if (s2n_async_pkey_op_set_output(op, output_buf.buffer, output_buf.len)) {
        AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Failed setting output on s2n async pkey op", (void *)handler);
        aws_raise_error(AWS_ERROR_INVALID_STATE);
        goto error;
    }

    if (s2n_async_pkey_op_apply(op, s2n_handler->connection)) {
        AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Failed applying s2n async pkey op", (void *)handler);
        aws_raise_error(AWS_ERROR_INVALID_STATE);
        goto error;
    }

    /* Success! */
    success = true;
    goto clean_up;

error:
    aws_channel_shutdown(s2n_handler->slot->channel, aws_last_error());

clean_up:
    s2n_async_pkey_op_free(op);
    aws_mem_release(handler->alloc, input_data);
    aws_byte_buf_clean_up(&output_buf);

    if (success) {
        s_drive_negotiation(handler);
    }
}