int aws_cryptosdk_sig_sign_finish()

in source/cipher_openssl.c [835:1000]


int aws_cryptosdk_sig_sign_finish(
    struct aws_cryptosdk_sig_ctx *ctx, struct aws_allocator *alloc, struct aws_string **signature) {
    AWS_PRECONDITION(aws_cryptosdk_sig_ctx_is_valid(ctx));
    AWS_PRECONDITION(ctx->alloc);
    AWS_PRECONDITION(ctx->is_sign);
    AWS_PRECONDITION(alloc);
    AWS_PRECONDITION(signature);
    int result = AWS_CRYPTOSDK_ERR_CRYPTO_UNKNOWN;
    /* This needs to be big enough for all digest algorithms in use */
    uint8_t digestbuf[64];

    EVP_PKEY_CTX *sign_ctx     = NULL;
    ECDSA_SIG *sig             = NULL;
    struct aws_byte_buf sigtmp = { 0 };

    size_t digestlen, siglen;

    digestlen = EVP_MD_CTX_size(ctx->ctx);

    const EC_GROUP *group = EC_KEY_get0_group(ctx->keypair);
#ifndef OSSL_100
    const BIGNUM *order = EC_GROUP_get0_order(group);
#else
    BIGNUM *order = BN_new();

    if (!order || !EC_GROUP_get_order(group, order, NULL)) {
        result = AWS_ERROR_OOM;
        goto out;
    }
#endif

    if (digestlen > sizeof(digestbuf)) {
        /* Should never happen */
        goto out;
    }

    if (1 != EVP_DigestFinal(ctx->ctx, digestbuf, NULL)) {
        goto out;
    }

    sign_ctx = EVP_PKEY_CTX_new(ctx->pkey, NULL);
    if (!sign_ctx) {
        result = AWS_ERROR_OOM;
        goto out;
    }

    if (1 != EVP_PKEY_sign_init(sign_ctx)) {
        goto out;
    }

    if (1 != EVP_PKEY_sign(sign_ctx, NULL, &siglen, digestbuf, digestlen)) {
        goto out;
    }

    /*
     * Note that siglen is the maximum possible size of a EC signature,
     * which may differ from the size we have set for AWSES signatures.
     * We'll allocate that much for the buffer capacity, and use a combination
     * of EC math and re-signing to hit the target size.
     *
     * It's important to hit the target precisely, as the caller might have
     * relied on a precise calculation of the ciphertext size in order to
     * e.g. set the S3 content-length on a PutObject, or otherwise preallocate
     * the destination space.
     */
    if (aws_byte_buf_init(&sigtmp, alloc, siglen)) {
        goto rethrow;
    }

    sigtmp.len = 0;

    while (sigtmp.len != ctx->props->signature_len) {
        sigtmp.len = sigtmp.capacity;
        if (1 != EVP_PKEY_sign(sign_ctx, sigtmp.buffer, &sigtmp.len, digestbuf, digestlen)) {
            goto out;
        }

        if (sigtmp.len == ctx->props->signature_len) {
            break;
        }

        /*
         * The unpredictability of the signature length arises from DER encoding
         * requiring an extra byte to represent integers where the high bit is
         * aligned with the high bit of a byte, and is set - this would result
         * in an encoding which appears to be negative.
         *
         * In the vast majority of cases, we can resolve this by negating s in the
         * signature relative to the group order (which does not invalidate the
         * signature). If this fails, we'll just generate a brand new signature;
         * since ECDSA signatures contain a random component, this will usually either
         * get us to the desired size directly, or at least make it so the negation
         * trick works.
         */
        const unsigned char *psig = sigtmp.buffer;
        if (d2i_ECDSA_SIG(&sig, &psig, sigtmp.len)) {
            const BIGNUM *orig_r, *orig_s;
            ECDSA_SIG_get0(sig, (const BIGNUM **)&orig_r, (const BIGNUM **)&orig_s);

            BIGNUM *r = BN_dup(orig_r);
            BIGNUM *s = BN_dup(orig_s);

            if (!r || !s) {
                result = AWS_ERROR_OOM;
                goto out;
            }

            if (!BN_sub(s, order, s)) {
                /* Signature values are not secret, so we just use BN_free here */
                BN_free(r);
                BN_free(s);
                goto out;
            }

            /*
             * This unconditionally frees the old r/s values, so it's important that
             * we BN_dup them above.
             */
            ECDSA_SIG_set0(sig, r, s);

            unsigned char *poutsig = sigtmp.buffer;
            if (!i2d_ECDSA_SIG(sig, &poutsig)) {
                goto out;
            }

            sigtmp.len = poutsig - sigtmp.buffer;
        }

        ECDSA_SIG_free(sig);
        sig = NULL;

#ifdef CBMC
        /* Loop is potentially unbounded but has a high probability of terminating after one or two iterations. This
         * assume forces the loop to terminate after one iteration during verification with CBMC. Since each iteration
         * of the loop is independent of the others, we assume that every memory-safety error that could occur can occur
         * in one iteration, and therefore would be caught by CBMC before reaching this assume. */
        __CPROVER_assume(sigtmp.len == ctx->props->signature_len);
#endif
    }

    *signature = aws_string_new_from_array(alloc, sigtmp.buffer, sigtmp.len);
    if (!*signature) {
        goto rethrow;
    }

    result = AWS_OP_SUCCESS;
out:
    if (result != AWS_OP_SUCCESS) aws_raise_error(result);
rethrow:
#ifdef OSSL_100
    BN_free(order);
#endif
    EVP_PKEY_CTX_free(sign_ctx);
    aws_cryptosdk_sig_abort(ctx);
    aws_secure_zero(digestbuf, sizeof(digestbuf));
    aws_byte_buf_clean_up(&sigtmp);
    ECDSA_SIG_free(sig);

    if (result) {
        // We shouldn't have actually allocated signature, so just make sure it's NULL on an error path
        *signature = NULL;
    }

    AWS_POSTCONDITION(result == AWS_OP_SUCCESS ? aws_string_is_valid(*signature) : !*signature);
    return result ? AWS_OP_ERR : AWS_OP_SUCCESS;
}