int aws_cryptosdk_aes_gcm_decrypt()

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