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