in source/cipher.c [891:948]
int aws_cryptosdk_aes_gcm_decrypt(
struct aws_byte_buf *plain,
const struct aws_byte_cursor cipher,
const struct aws_byte_cursor tag,
const struct aws_byte_cursor iv,
const struct aws_byte_cursor aad,
const struct aws_string *key) {
AWS_PRECONDITION(aws_byte_buf_is_valid(plain));
AWS_PRECONDITION(plain->buffer != NULL);
AWS_PRECONDITION(aws_byte_cursor_is_valid(&cipher));
AWS_PRECONDITION(aws_byte_cursor_is_valid(&tag));
AWS_PRECONDITION(aws_byte_cursor_is_valid(&iv));
AWS_PRECONDITION(aws_byte_cursor_is_valid(&aad));
AWS_PRECONDITION(aws_string_is_valid(key));
bool openssl_err = true;
const EVP_CIPHER *alg = get_alg_from_key_size(key->len);
if (!alg || iv.len != aes_gcm_iv_len || tag.len != aes_gcm_tag_len || plain->capacity < cipher.len)
return aws_raise_error(AWS_ERROR_INVALID_BUFFER_SIZE);
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
if (!ctx) goto decrypt_err;
if (!EVP_DecryptInit_ex(ctx, alg, NULL, aws_string_bytes(key), iv.ptr)) goto decrypt_err;
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag.len, tag.ptr)) goto decrypt_err;
/* Setting the AAD. out_len here is a throwaway. Might be able to make that argument NULL, but
* openssl wiki example does the same as this, giving it a pointer to an int and disregarding value.
*/
int out_len;
if (aad.len) {
if (!EVP_DecryptUpdate(ctx, NULL, &out_len, aad.ptr, aad.len)) goto decrypt_err;
}
if (!EVP_DecryptUpdate(ctx, plain->buffer, &out_len, cipher.ptr, cipher.len)) goto decrypt_err;
int prev_len = out_len;
/* Possible for EVP_DecryptFinal_ex to fail without generating an OpenSSL error code (e.g., tag
* mismatch) so flush the errors first to distinguish this case.
*/
flush_openssl_errors();
if (!EVP_DecryptFinal_ex(ctx, plain->buffer + out_len, &out_len)) {
if (!ERR_peek_last_error()) openssl_err = false;
goto decrypt_err;
}
EVP_CIPHER_CTX_free(ctx);
plain->len = prev_len + out_len;
assert(plain->len == cipher.len);
return AWS_OP_SUCCESS;
decrypt_err:
EVP_CIPHER_CTX_free(ctx);
aws_byte_buf_secure_zero(plain); // sets plain->len to zero
if (openssl_err) {
flush_openssl_errors();
return aws_raise_error(AWS_CRYPTOSDK_ERR_CRYPTO_UNKNOWN);
}
return aws_raise_error(AWS_CRYPTOSDK_ERR_BAD_CIPHERTEXT);
}