GCM tags in ciphertext, rather than in params.

Also, handle AAD correctly.

Bug: 21786749
Change-Id: I735f3d277c96bf4d3fed3ef433f96238b1f21187
diff --git a/aead_mode_operation.cpp b/aead_mode_operation.cpp
deleted file mode 100644
index 357dce6..0000000
--- a/aead_mode_operation.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-
-#include <openssl/aes.h>
-#include <openssl/rand.h>
-
-#include <keymaster/authorization_set.h>
-#include <keymaster/logger.h>
-
-#include "aead_mode_operation.h"
-#include "openssl_err.h"
-
-namespace keymaster {
-
-keymaster_error_t AeadModeOperation::Begin(const AuthorizationSet& input_params,
-                                           AuthorizationSet* output_params) {
-    keymaster_error_t error = Initialize(key_, key_size_, nonce_length_, tag_length_);
-    if (error != KM_ERROR_OK)
-        return error;
-
-    buffer_end_ = 0;
-    buffer_.reset(new uint8_t[processing_unit_]);
-    if (!buffer_.get())
-        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-
-    return HandleNonce(input_params, output_params);
-}
-
-inline size_t min(size_t a, size_t b) {
-    if (a < b)
-        return a;
-    return b;
-}
-
-keymaster_error_t AeadModeOperation::Update(const AuthorizationSet& additional_params,
-                                            const Buffer& input, Buffer* output,
-                                            size_t* input_consumed) {
-    // Make an effort to reserve enough output space.  The output buffer will be extended if needed,
-    // but this reduces reallocations.
-    if (!output->reserve(EstimateOutputSize(input, output)))
-        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-
-    keymaster_error_t error = KM_ERROR_OK;
-    *input_consumed = 0;
-
-    keymaster_blob_t associated_data = {0, 0};
-    additional_params.GetTagValue(TAG_ASSOCIATED_DATA, &associated_data);
-
-    const uint8_t* plaintext = input.peek_read();
-    const uint8_t* plaintext_end = plaintext + input.available_read();
-    while (plaintext < plaintext_end && error == KM_ERROR_OK) {
-        if (buffered_data_length() == processing_unit_) {
-            error = ProcessChunk(associated_data, output);
-            ClearBuffer();
-            IncrementNonce();
-        }
-        plaintext = AppendToBuffer(plaintext, plaintext_end - plaintext);
-        *input_consumed = plaintext - input.peek_read();
-    }
-    return error;
-}
-
-keymaster_error_t AeadModeOperation::Finish(const AuthorizationSet& additional_params,
-                                            const Buffer& /* signature */, Buffer* output) {
-    keymaster_blob_t associated_data = {0, 0};
-    additional_params.GetTagValue(TAG_ASSOCIATED_DATA, &associated_data);
-    return ProcessChunk(associated_data, output);
-}
-
-keymaster_error_t AeadModeOperation::ProcessChunk(const keymaster_blob_t& additional_data,
-                                                  Buffer* output) {
-    keymaster_error_t error = KM_ERROR_OK;
-    if (purpose() == KM_PURPOSE_DECRYPT) {
-        if (buffered_data_length() < tag_length_)
-            return KM_ERROR_INVALID_INPUT_LENGTH;
-        ExtractTagFromBuffer();
-        LOG_D("AeadMode decrypting %d", buffered_data_length());
-        if (!output->reserve(output->available_read() + buffered_data_length()))
-            error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
-        else
-            error = DecryptChunk(nonce_, nonce_length_, tag_, tag_length_, additional_data,
-                                 buffer_.get(), buffered_data_length(), output);
-    } else {
-        if (!output->reserve(output->available_read() + buffered_data_length() + tag_length_))
-            error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
-        else
-            error = EncryptChunk(nonce_, nonce_length_, tag_length_, additional_data, buffer_.get(),
-                                 buffered_data_length(), output);
-    }
-    return error;
-}
-
-size_t AeadModeOperation::EstimateOutputSize(const Buffer& input, Buffer* output) {
-    switch (purpose()) {
-    case KM_PURPOSE_ENCRYPT: {
-        size_t chunk_length = processing_unit_;
-        size_t chunk_count = (input.available_read() + chunk_length - 1) / chunk_length;
-        return output->available_read() + nonce_length_ +
-               chunk_count * (chunk_length + tag_length_);
-    }
-    case KM_PURPOSE_DECRYPT: {
-        size_t chunk_length = processing_unit_ - tag_length_;
-        size_t chunk_count =
-            (input.available_read() - nonce_length_ + processing_unit_ - 1) / processing_unit_;
-        return output->available_read() + chunk_length * chunk_count;
-    }
-    default:
-        LOG_E("Encountered invalid purpose %d", purpose());
-        return 0;
-    }
-}
-
-keymaster_error_t AeadModeOperation::HandleNonce(const AuthorizationSet& input_params,
-                                                 AuthorizationSet* output_params) {
-    if (!output_params)
-        return KM_ERROR_OUTPUT_PARAMETER_NULL;
-
-    switch (purpose()) {
-    case KM_PURPOSE_ENCRYPT: {
-        keymaster_error_t error;
-        if (caller_nonce_)
-            error = ExtractNonce(input_params);
-        else {
-            if (input_params.find(TAG_NONCE) != -1)
-                return KM_ERROR_CALLER_NONCE_PROHIBITED;
-            error = GenerateNonce();
-        }
-
-        if (error != KM_ERROR_OK)
-            return error;
-
-        output_params->push_back(TAG_NONCE, nonce_, nonce_length_);
-        if (output_params->is_valid() != AuthorizationSet::OK)
-            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-        break;
-    }
-    case KM_PURPOSE_DECRYPT:
-        return ExtractNonce(input_params);
-
-    default:
-        return KM_ERROR_UNSUPPORTED_PURPOSE;
-    }
-    return KM_ERROR_OK;
-}
-
-keymaster_error_t AeadModeOperation::GenerateNonce() {
-    if (RAND_bytes(nonce_, nonce_length_) == 1)
-        return KM_ERROR_OK;
-    LOG_S("Failed to generate %d-byte nonce", nonce_length_);
-    return TranslateLastOpenSslError();
-}
-
-keymaster_error_t AeadModeOperation::ExtractNonce(const AuthorizationSet& input_params) {
-    keymaster_blob_t nonce_blob;
-    if (!input_params.GetTagValue(TAG_NONCE, &nonce_blob))
-        return KM_ERROR_MISSING_NONCE;
-
-    if (nonce_blob.data_length != nonce_length_) {
-        LOG_E("Expected %d-byte nonce, got %d bytes", nonce_length_, nonce_blob.data_length);
-        return KM_ERROR_INVALID_NONCE;
-    }
-
-    memcpy(nonce_, nonce_blob.data, nonce_length_);
-    return KM_ERROR_OK;
-}
-
-void AeadModeOperation::IncrementNonce() {
-    for (int i = nonce_length_ - 1; i > 0; --i)
-        if (++nonce_[i])
-            break;
-}
-
-const uint8_t* AeadModeOperation::AppendToBuffer(const uint8_t* data, size_t data_length) {
-    // Only take as much data as we can fit.
-    if (data_length > buffer_free_space())
-        data_length = buffer_free_space();
-    memcpy(buffer_.get() + buffer_end_, data, data_length);
-    buffer_end_ += data_length;
-    return data + data_length;
-}
-
-void AeadModeOperation::ExtractTagFromBuffer() {
-    assert(buffered_data_length() >= tag_length_);
-    memcpy(tag_, buffer_.get() + buffer_end_ - tag_length_, tag_length_);
-    buffer_end_ -= tag_length_;
-}
-
-}  // namespace keymaster
diff --git a/aead_mode_operation.h b/aead_mode_operation.h
deleted file mode 100644
index ff73a7f..0000000
--- a/aead_mode_operation.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SYSTEM_KEYMASTER_AEAD_MODE_OPERATION_H_
-#define SYSTEM_KEYMASTER_AEAD_MODE_OPERATION_H_
-
-#include "operation.h"
-
-namespace keymaster {
-
-class AeadModeOperation : public Operation {
-  public:
-    static const size_t MAX_CHUNK_LENGTH = 64 * 1024;
-    static const size_t MAX_NONCE_LENGTH = 12;
-    static const size_t MAX_TAG_LENGTH = 16;
-    static const size_t MAX_KEY_LENGTH = 32;
-
-    AeadModeOperation(keymaster_purpose_t purpose, const uint8_t* key, size_t key_size,
-                      size_t chunk_length, size_t tag_length, size_t nonce_length,
-                      bool caller_nonce)
-        : Operation(purpose), key_size_(key_size), tag_length_(tag_length),
-          nonce_length_(nonce_length),
-          processing_unit_(purpose == KM_PURPOSE_DECRYPT ? chunk_length + tag_length
-                                                         : chunk_length),
-          caller_nonce_(caller_nonce) {
-        assert(key_size <= MAX_KEY_LENGTH);
-        memcpy(key_, key, key_size);
-    }
-    ~AeadModeOperation() {
-        // Wipe sensitive buffers.
-        memset_s(buffer_.get(), 0, processing_unit_);
-        memset_s(key_, 0, MAX_KEY_LENGTH);
-    }
-
-    virtual keymaster_error_t Begin(const AuthorizationSet& input_params,
-                                    AuthorizationSet* output_params);
-    virtual keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input,
-                                     AuthorizationSet* output_params, Buffer* output,
-                                     size_t* input_consumed);
-    virtual keymaster_error_t Finish(const AuthorizationSet& additional_params,
-                                     const Buffer& signature, AuthorizationSet* output_params,
-                                     Buffer* output);
-
-  protected:
-    size_t buffered_data_length() const { return buffer_end_; }
-    const uint8_t* key() const { return key_; }
-    size_t key_size() const { return key_size_; }
-
-  private:
-    /*
-     * These methods do the actual crypto operations.
-     *
-     * TODO(swillden): Consider refactoring these to a separate class, integrating them via
-     * composition rather than inheritance.
-     */
-    virtual keymaster_error_t Initialize(uint8_t* key, size_t key_size, size_t nonce_length,
-                                         size_t tag_length) = 0;
-    virtual keymaster_error_t EncryptChunk(const uint8_t* nonce, size_t nonce_length,
-                                           size_t tag_length,
-                                           const keymaster_blob_t additional_data, uint8_t* chunk,
-                                           size_t chunk_size, Buffer* output) = 0;
-    virtual keymaster_error_t DecryptChunk(const uint8_t* nonce, size_t nonce_length,
-                                           const uint8_t* tag, size_t tag_length,
-                                           const keymaster_blob_t additional_data, uint8_t* chunk,
-                                           size_t chunk_size, Buffer* output) = 0;
-
-    size_t EstimateOutputSize(const Buffer& input, Buffer* output);
-    keymaster_error_t ProcessChunk(const keymaster_blob_t& associated_data, Buffer* output);
-
-    size_t buffer_free_space() const { return processing_unit_ - buffer_end_; }
-
-    const uint8_t* AppendToBuffer(const uint8_t* data, size_t data_length);
-    void ExtractTagFromBuffer();
-    void ClearBuffer() { buffer_end_ = 0; }
-    keymaster_error_t HandleNonce(const AuthorizationSet& input_params,
-                                  AuthorizationSet* output_params);
-    keymaster_error_t ExtractNonce(const AuthorizationSet& input_params);
-    keymaster_error_t GenerateNonce();
-    void IncrementNonce();
-
-    const size_t key_size_;
-    const size_t tag_length_;
-    const size_t nonce_length_;
-    const size_t processing_unit_;
-    const bool caller_nonce_;
-    UniquePtr<uint8_t[]> buffer_;
-    size_t buffer_end_;
-    uint8_t __attribute__((aligned(16))) key_[MAX_KEY_LENGTH];
-    uint8_t __attribute__((aligned(16))) tag_[MAX_TAG_LENGTH];
-    uint8_t __attribute__((aligned(16))) nonce_[MAX_NONCE_LENGTH];
-};
-
-}  // namespace keymaster
-
-#endif  // SYSTEM_KEYMASTER_AEAD_MODE_OPERATION_H_
diff --git a/aes_operation.cpp b/aes_operation.cpp
index 7c62d74..c71a1c3 100644
--- a/aes_operation.cpp
+++ b/aes_operation.cpp
@@ -63,7 +63,7 @@
     }
 
     size_t tag_length = 0;
-    if (block_mode == KM_MODE_GCM && purpose() == KM_PURPOSE_ENCRYPT) {
+    if (block_mode == KM_MODE_GCM) {
         uint32_t tag_length_bits;
         if (!begin_params.GetTagValue(TAG_MAC_LENGTH, &tag_length_bits))
             *error = KM_ERROR_MISSING_MAC_LENGTH;
@@ -93,21 +93,15 @@
     if (*error != KM_ERROR_OK)
         return nullptr;
 
-    return CreateEvpOperation(*symmetric_key, block_mode, padding, caller_nonce, tag_length, error);
-}
-
-Operation* AesOperationFactory::CreateEvpOperation(const SymmetricKey& key,
-                                                   keymaster_block_mode_t block_mode,
-                                                   keymaster_padding_t padding, bool caller_iv,
-                                                   size_t tag_length, keymaster_error_t* error) {
     Operation* op = NULL;
     switch (purpose()) {
     case KM_PURPOSE_ENCRYPT:
-        op = new AesEvpEncryptOperation(block_mode, padding, caller_iv, tag_length, key.key_data(),
-                                        key.key_data_size());
+        op = new AesEvpEncryptOperation(block_mode, padding, caller_nonce, tag_length,
+                                        symmetric_key->key_data(), symmetric_key->key_data_size());
         break;
     case KM_PURPOSE_DECRYPT:
-        op = new AesEvpDecryptOperation(block_mode, padding, key.key_data(), key.key_data_size());
+        op = new AesEvpDecryptOperation(block_mode, padding, tag_length, symmetric_key->key_data(),
+                                        symmetric_key->key_data_size());
         break;
     default:
         *error = KM_ERROR_UNSUPPORTED_PURPOSE;
@@ -136,16 +130,90 @@
 }
 
 AesEvpOperation::AesEvpOperation(keymaster_purpose_t purpose, keymaster_block_mode_t block_mode,
-                                 keymaster_padding_t padding, bool caller_iv, const uint8_t* key,
-                                 size_t key_size)
-    : Operation(purpose), block_mode_(block_mode), caller_iv_(caller_iv), aad_provided_(false),
-      key_size_(key_size), padding_(padding) {
+                                 keymaster_padding_t padding, bool caller_iv, size_t tag_length,
+                                 const uint8_t* key, size_t key_size)
+    : Operation(purpose), block_mode_(block_mode), caller_iv_(caller_iv), tag_length_(tag_length),
+      data_started_(false), key_size_(key_size), padding_(padding) {
     memcpy(key_, key, key_size_);
     EVP_CIPHER_CTX_init(&ctx_);
 }
 
 AesEvpOperation::~AesEvpOperation() {
     EVP_CIPHER_CTX_cleanup(&ctx_);
+    memset_s(aad_block_buf_.get(), AES_BLOCK_SIZE, 0);
+}
+
+keymaster_error_t AesEvpOperation::Begin(const AuthorizationSet& /* input_params */,
+                                         AuthorizationSet* /* output_params */) {
+    if (block_mode_ == KM_MODE_GCM) {
+        aad_block_buf_length_ = 0;
+        aad_block_buf_.reset(new uint8_t[AES_BLOCK_SIZE]);
+        if (!aad_block_buf_.get())
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
+
+    return InitializeCipher();
+}
+
+keymaster_error_t AesEvpOperation::Update(const AuthorizationSet& additional_params,
+                                          const Buffer& input,
+                                          AuthorizationSet* /* output_params */, Buffer* output,
+                                          size_t* input_consumed) {
+    keymaster_error_t error;
+    if (block_mode_ == KM_MODE_GCM)
+        if (!HandleAad(additional_params, input, &error))
+            return error;
+
+    if (!InternalUpdate(input.peek_read(), input.available_read(), output, &error))
+        return error;
+    *input_consumed = input.available_read();
+
+    return KM_ERROR_OK;
+}
+
+inline bool is_bad_decrypt(unsigned long error) {
+    return (ERR_GET_LIB(error) == ERR_LIB_CIPHER &&  //
+            ERR_GET_REASON(error) == CIPHER_R_BAD_DECRYPT);
+}
+
+keymaster_error_t AesEvpOperation::Finish(const AuthorizationSet& /* additional_params */,
+                                          const Buffer& /* signature */,
+                                          AuthorizationSet* /* output_params */, Buffer* output) {
+    if (!output->reserve(AES_BLOCK_SIZE))
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+    keymaster_error_t error;
+    if (block_mode_ == KM_MODE_GCM && aad_block_buf_length_ > 0 &&
+        !ProcessBufferedAadBlock(&error)) {
+        return error;
+    }
+
+    int output_written = -1;
+    if (!EVP_CipherFinal_ex(&ctx_, output->peek_write(), &output_written)) {
+        if (tag_length_ > 0)
+            return KM_ERROR_VERIFICATION_FAILED;
+        LOG_E("Error encrypting final block: %s", ERR_error_string(ERR_peek_last_error(), NULL));
+        return TranslateLastOpenSslError();
+    }
+
+    assert(output_written <= AES_BLOCK_SIZE);
+    output->advance_write(output_written);
+    return KM_ERROR_OK;
+}
+
+bool AesEvpOperation::need_iv() const {
+    switch (block_mode_) {
+    case KM_MODE_CBC:
+    case KM_MODE_CTR:
+    case KM_MODE_GCM:
+        return true;
+    case KM_MODE_ECB:
+        return false;
+    default:
+        // Shouldn't get here.
+        assert(false);
+        return false;
+    }
 }
 
 keymaster_error_t AesEvpOperation::InitializeCipher() {
@@ -229,36 +297,128 @@
         return KM_ERROR_UNSUPPORTED_PADDING_MODE;
     }
 
+    if (block_mode_ == KM_MODE_GCM) {
+        aad_block_buf_length_ = 0;
+        aad_block_buf_.reset(new uint8_t[AES_BLOCK_SIZE]);
+        if (!aad_block_buf_.get())
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
+
     return KM_ERROR_OK;
 }
 
-bool AesEvpOperation::need_iv() const {
-    switch (block_mode_) {
-    case KM_MODE_CBC:
-    case KM_MODE_CTR:
-    case KM_MODE_GCM:
-        return true;
-    case KM_MODE_ECB:
-        return false;
-    default:
-        // Shouldn't get here.
-        assert(false);
-        return false;
+keymaster_error_t AesEvpOperation::GetIv(const AuthorizationSet& input_params) {
+    keymaster_blob_t iv_blob;
+    if (!input_params.GetTagValue(TAG_NONCE, &iv_blob)) {
+        LOG_E("No IV provided", 0);
+        return KM_ERROR_INVALID_ARGUMENT;
     }
+    if (block_mode_ != KM_MODE_GCM && iv_blob.data_length != AES_BLOCK_SIZE) {
+        LOG_E("Expected %d-byte IV for AES operation, but got %d bytes", AES_BLOCK_SIZE,
+              iv_blob.data_length);
+        return KM_ERROR_INVALID_NONCE;
+    }
+    iv_.reset(dup_array(iv_blob.data, iv_blob.data_length));
+    if (!iv_.get())
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    iv_length_ = iv_blob.data_length;
+    return KM_ERROR_OK;
 }
 
-keymaster_error_t AesEvpDecryptOperation::Begin(const AuthorizationSet& input_params,
-                                                AuthorizationSet* output_params) {
-    if (!output_params)
-        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+/*
+ * Process Incoming Associated Authentication Data.
+ *
+ * This method is more complex than might be expected, because the underlying library silently does
+ * the wrong thing when given partial AAD blocks, so we have to take care to process AAD in
+ * AES_BLOCK_SIZE increments, buffering (in aad_block_buf_) when given smaller amounts of data.
+ */
+bool AesEvpOperation::HandleAad(const AuthorizationSet& input_params, const Buffer& input,
+                                keymaster_error_t* error) {
+    assert(tag_length_ > 0);
+    assert(error);
 
-    if (need_iv()) {
-        keymaster_error_t error = GetIv(input_params);
-        if (error != KM_ERROR_OK)
-            return error;
+    keymaster_blob_t aad;
+    if (input_params.GetTagValue(TAG_ASSOCIATED_DATA, &aad)) {
+        if (data_started_) {
+            *error = KM_ERROR_INVALID_TAG;
+            return false;
+        }
+
+        if (aad_block_buf_length_ > 0) {
+            FillBufferedAadBlock(&aad);
+            if (aad_block_buf_length_ == AES_BLOCK_SIZE && !ProcessBufferedAadBlock(error))
+                return false;
+        }
+
+        size_t blocks_to_process = aad.data_length / AES_BLOCK_SIZE;
+        if (blocks_to_process && !ProcessAadBlocks(aad.data, blocks_to_process, error))
+            return false;
+        aad.data += blocks_to_process * AES_BLOCK_SIZE;
+        aad.data_length -= blocks_to_process * AES_BLOCK_SIZE;
+
+        FillBufferedAadBlock(&aad);
+        assert(aad.data_length == 0);
     }
 
-    return InitializeCipher();
+    if (input.available_read()) {
+        data_started_ = true;
+        // Data has begun, no more AAD is allowed.  Process any buffered AAD.
+        if (aad_block_buf_length_ > 0 && !ProcessBufferedAadBlock(error))
+            return false;
+    }
+
+    return true;
+}
+
+bool AesEvpOperation::ProcessBufferedAadBlock(keymaster_error_t* error) {
+    int output_written;
+    if (EVP_CipherUpdate(&ctx_, nullptr /* out */, &output_written, aad_block_buf_.get(),
+                         aad_block_buf_length_)) {
+        aad_block_buf_length_ = 0;
+        return true;
+    }
+    *error = TranslateLastOpenSslError();
+    return false;
+}
+
+bool AesEvpOperation::ProcessAadBlocks(const uint8_t* data, size_t blocks,
+                                       keymaster_error_t* error) {
+    int output_written;
+    if (EVP_CipherUpdate(&ctx_, nullptr /* out */, &output_written, data, blocks * AES_BLOCK_SIZE))
+        return true;
+    *error = TranslateLastOpenSslError();
+    return false;
+}
+
+inline size_t min(size_t a, size_t b) {
+    return (a < b) ? a : b;
+}
+
+void AesEvpOperation::FillBufferedAadBlock(keymaster_blob_t* aad) {
+    size_t to_buffer = min(AES_BLOCK_SIZE - aad_block_buf_length_, aad->data_length);
+    memcpy(aad_block_buf_.get() + aad_block_buf_length_, aad->data, to_buffer);
+    aad->data += to_buffer;
+    aad->data_length -= to_buffer;
+    aad_block_buf_length_ += to_buffer;
+}
+
+bool AesEvpOperation::InternalUpdate(const uint8_t* input, size_t input_length, Buffer* output,
+                                     keymaster_error_t* error) {
+    assert(output);
+    assert(error);
+
+    if (!output->reserve(input_length + AES_BLOCK_SIZE)) {
+        *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        return false;
+    }
+
+    int output_written = -1;
+    if (!EVP_CipherUpdate(&ctx_, output->peek_write(), &output_written, input, input_length)) {
+        *error = TranslateLastOpenSslError();
+        return false;
+    }
+    output->advance_write(output_written);
+    return true;
 }
 
 keymaster_error_t AesEvpEncryptOperation::Begin(const AuthorizationSet& input_params,
@@ -281,30 +441,29 @@
             return error;
     }
 
-    return InitializeCipher();
+    return AesEvpOperation::Begin(input_params, output_params);
 }
 
-keymaster_error_t AesEvpOperation::GetIv(const AuthorizationSet& input_params) {
-    keymaster_blob_t iv_blob;
-    if (!input_params.GetTagValue(TAG_NONCE, &iv_blob)) {
-        LOG_E("No IV provided", 0);
-        return KM_ERROR_INVALID_ARGUMENT;
-    }
-    if (block_mode_ == KM_MODE_GCM) {
-        if (iv_blob.data_length != GCM_NONCE_SIZE) {
-            LOG_E("Expected %d-byte nonce for AES-GCM operation, but got %d bytes", GCM_NONCE_SIZE,
-                  iv_blob.data_length);
-            return KM_ERROR_INVALID_NONCE;
-        }
-    } else if (iv_blob.data_length != AES_BLOCK_SIZE) {
-        LOG_E("Expected %d-byte IV for AES operation, but got %d bytes", AES_BLOCK_SIZE,
-              iv_blob.data_length);
-        return KM_ERROR_INVALID_NONCE;
-    }
-    iv_.reset(dup_array(iv_blob.data, iv_blob.data_length));
-    if (!iv_.get())
+keymaster_error_t AesEvpEncryptOperation::Finish(const AuthorizationSet& additional_params,
+                                                 const Buffer& signature,
+                                                 AuthorizationSet* output_params, Buffer* output) {
+    if (!output->reserve(AES_BLOCK_SIZE + tag_length_))
         return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-    iv_length_ = iv_blob.data_length;
+
+    keymaster_error_t error =
+        AesEvpOperation::Finish(additional_params, signature, output_params, output);
+    if (error != KM_ERROR_OK)
+        return error;
+
+    if (tag_length_ > 0) {
+        if (!output->reserve(output->available_read() + tag_length_))
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+        if (!EVP_CIPHER_CTX_ctrl(&ctx_, EVP_CTRL_GCM_GET_TAG, tag_length_, output->peek_write()))
+            return TranslateLastOpenSslError();
+        output->advance_write(tag_length_);
+    }
+
     return KM_ERROR_OK;
 }
 
@@ -318,107 +477,105 @@
     return KM_ERROR_OK;
 }
 
-inline size_t min(size_t a, size_t b) {
-    if (a < b)
-        return a;
-    return b;
-}
-
-keymaster_error_t AesEvpOperation::Update(const AuthorizationSet& additional_params,
-                                          const Buffer& input,
-                                          AuthorizationSet* /* output_params */, Buffer* output,
-                                          size_t* input_consumed) {
-    if (!aad_provided_ && block_mode_ == KM_MODE_GCM) {
-        keymaster_blob_t aad;
-        if (additional_params.GetTagValue(TAG_ASSOCIATED_DATA, &aad)) {
-            // Incantation to add AAD is to call update with null output.  Ugly.
-            int output_written;
-            if (!EVP_CipherUpdate(&ctx_, nullptr /* out */, &output_written, aad.data,
-                                  aad.data_length))
-                return TranslateLastOpenSslError();
-        }
-        aad_provided_ = true;
+keymaster_error_t AesEvpDecryptOperation::Begin(const AuthorizationSet& input_params,
+                                                AuthorizationSet* output_params) {
+    if (need_iv()) {
+        keymaster_error_t error = GetIv(input_params);
+        if (error != KM_ERROR_OK)
+            return error;
     }
 
-    output->reserve(input.available_read() + AES_BLOCK_SIZE);
+    if (tag_length_ > 0) {
+        tag_buf_length_ = 0;
+        tag_buf_.reset(new uint8_t[tag_length_]);
+        if (!tag_buf_.get())
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
 
-    const uint8_t* input_pos = input.peek_read();
-    const uint8_t* input_end = input_pos + input.available_read();
-
-    int output_written = -1;
-    if (!EVP_CipherUpdate(&ctx_, output->peek_write(), &output_written, input_pos,
-                          input_end - input_pos))
-        return TranslateLastOpenSslError();
-
-    assert(output_written >= 0);
-    assert(output_written <= (int)output->available_write());
-    output->advance_write(output_written);
-    *input_consumed = input.available_read();
-    return KM_ERROR_OK;
+    return AesEvpOperation::Begin(input_params, output_params);
 }
 
 keymaster_error_t AesEvpDecryptOperation::Update(const AuthorizationSet& additional_params,
                                                  const Buffer& input,
-                                                 AuthorizationSet* output_params, Buffer* output,
-                                                 size_t* input_consumed) {
-    if (!tag_provided_ && block_mode_ == KM_MODE_GCM && purpose() == KM_PURPOSE_DECRYPT) {
-        keymaster_blob_t tag;
-        if (additional_params.GetTagValue(TAG_AEAD_TAG, &tag)) {
-            if (tag.data_length < GCM_MIN_TAG_LENGTH || tag.data_length > GCM_MAX_TAG_LENGTH)
-                return KM_ERROR_UNSUPPORTED_MAC_LENGTH;
+                                                 AuthorizationSet* /* output_params */,
+                                                 Buffer* output, size_t* input_consumed) {
+    if (!output || !input_consumed)
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
 
-            if (!EVP_CIPHER_CTX_ctrl(&ctx_, EVP_CTRL_GCM_SET_TAG, tag.data_length,
-                                     const_cast<uint8_t*>(tag.data)))
-                return TranslateLastOpenSslError();
-            tag_provided_ = true;
-        }
-        if (!tag_provided_)
-            return KM_ERROR_INVALID_TAG;
+    // Barring error, we'll consume it all.
+    *input_consumed = input.available_read();
+
+    keymaster_error_t error;
+    if (block_mode_ == KM_MODE_GCM) {
+        if (!HandleAad(additional_params, input, &error))
+            return error;
+        return ProcessAllButTagLengthBytes(input, output);
     }
 
-    return AesEvpOperation::Update(additional_params, input, output_params, output, input_consumed);
-}
-
-inline bool is_bad_decrypt(unsigned long error) {
-    return (ERR_GET_LIB(error) == ERR_LIB_CIPHER &&  //
-            ERR_GET_REASON(error) == CIPHER_R_BAD_DECRYPT);
-}
-
-keymaster_error_t AesEvpOperation::Finish(const AuthorizationSet& /* additional_params */,
-                                          const Buffer& /* signature */,
-                                          AuthorizationSet* /* output_params */, Buffer* output) {
-    output->reserve(AES_BLOCK_SIZE);
-
-    int output_written = -1;
-    if (!EVP_CipherFinal_ex(&ctx_, output->peek_write(), &output_written)) {
-        if (block_mode_ == KM_MODE_GCM && is_bad_decrypt(ERR_peek_last_error()))
-            return KM_ERROR_VERIFICATION_FAILED;
-        LOG_E("Error encrypting final block: %s", ERR_error_string(ERR_peek_last_error(), NULL));
-        return TranslateLastOpenSslError();
-    }
-
-    assert(output_written <= AES_BLOCK_SIZE);
-    output->advance_write(output_written);
+    if (!InternalUpdate(input.peek_read(), input.available_read(), output, &error))
+        return error;
     return KM_ERROR_OK;
 }
 
-keymaster_error_t AesEvpEncryptOperation::Finish(const AuthorizationSet& additional_params,
-                                                 const Buffer& signature,
-                                                 AuthorizationSet* output_params, Buffer* output) {
-    keymaster_error_t error =
-        AesEvpOperation::Finish(additional_params, signature, output_params, output);
-    if (error != KM_ERROR_OK)
+keymaster_error_t AesEvpDecryptOperation::ProcessAllButTagLengthBytes(const Buffer& input,
+                                                                      Buffer* output) {
+    if (input.available_read() <= tag_buf_unused()) {
+        BufferCandidateTagData(input.peek_read(), input.available_read());
+        return KM_ERROR_OK;
+    }
+
+    const size_t data_available = tag_buf_length_ + input.available_read();
+
+    const size_t to_process = data_available - tag_length_;
+    const size_t to_process_from_tag_buf = min(to_process, tag_buf_length_);
+    const size_t to_process_from_input = to_process - to_process_from_tag_buf;
+
+    if (!output->reserve(to_process + AES_BLOCK_SIZE))
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+    keymaster_error_t error;
+    if (!ProcessTagBufContentsAsData(to_process_from_tag_buf, output, &error))
         return error;
 
-    if (block_mode_ == KM_MODE_GCM && purpose() == KM_PURPOSE_ENCRYPT) {
-        uint8_t tag[tag_length_];
-        if (!EVP_CIPHER_CTX_ctrl(&ctx_, EVP_CTRL_GCM_GET_TAG, tag_length_, tag))
-            return TranslateLastOpenSslError();
-        output_params->push_back(TAG_AEAD_TAG, tag, tag_length_);
-    }
+    if (!InternalUpdate(input.peek_read(), to_process_from_input, output, &error))
+        return error;
+
+    BufferCandidateTagData(input.peek_read() + to_process_from_input,
+                           input.available_read() - to_process_from_input);
+    assert(tag_buf_unused() == 0);
 
     return KM_ERROR_OK;
 }
+
+bool AesEvpDecryptOperation::ProcessTagBufContentsAsData(size_t to_process, Buffer* output,
+                                                         keymaster_error_t* error) {
+    assert(to_process <= tag_buf_length_);
+    if (!InternalUpdate(tag_buf_.get(), to_process, output, error))
+        return false;
+    if (to_process < tag_buf_length_)
+        memmove(tag_buf_.get(), tag_buf_.get() + to_process, tag_buf_length_ - to_process);
+    tag_buf_length_ -= to_process;
+    return true;
+}
+
+void AesEvpDecryptOperation::BufferCandidateTagData(const uint8_t* data, size_t data_length) {
+    assert(data_length <= tag_length_ - tag_buf_length_);
+    memcpy(tag_buf_.get() + tag_buf_length_, data, data_length);
+    tag_buf_length_ += data_length;
+}
+
+keymaster_error_t AesEvpDecryptOperation::Finish(const AuthorizationSet& additional_params,
+                                                 const Buffer& signature,
+                                                 AuthorizationSet* output_params, Buffer* output) {
+    if (tag_buf_length_ < tag_length_)
+        return KM_ERROR_INVALID_INPUT_LENGTH;
+    else if (tag_length_ > 0 &&
+             !EVP_CIPHER_CTX_ctrl(&ctx_, EVP_CTRL_GCM_SET_TAG, tag_length_, tag_buf_.get()))
+        return TranslateLastOpenSslError();
+
+    return AesEvpOperation::Finish(additional_params, signature, output_params, output);
+}
+
 keymaster_error_t AesEvpOperation::Abort() {
     return KM_ERROR_OK;
 }
diff --git a/aes_operation.h b/aes_operation.h
index dc902a7..7c0bd72 100644
--- a/aes_operation.h
+++ b/aes_operation.h
@@ -19,21 +19,52 @@
 
 #include <openssl/evp.h>
 
-#include "aead_mode_operation.h"
 #include "ocb_utils.h"
 #include "operation.h"
 
 namespace keymaster {
 
+/**
+ * Abstract base for AES operation factories.  This class does all of the work to create
+ * AES operations.
+ */
+class AesOperationFactory : public OperationFactory {
+  public:
+    KeyType registry_key() const override { return KeyType(KM_ALGORITHM_AES, purpose()); }
+
+    Operation* CreateOperation(const Key& key, const AuthorizationSet& begin_params,
+                               keymaster_error_t* error) override;
+    const keymaster_block_mode_t* SupportedBlockModes(size_t* block_mode_count) const override;
+    const keymaster_padding_t* SupportedPaddingModes(size_t* padding_count) const override;
+
+    virtual keymaster_purpose_t purpose() const = 0;
+};
+
+/**
+ * Concrete factory for AES encryption operations.
+ */
+class AesEncryptionOperationFactory : public AesOperationFactory {
+    keymaster_purpose_t purpose() const override { return KM_PURPOSE_ENCRYPT; }
+};
+
+/**
+ * Concrete factory for AES decryption operations.
+ */
+class AesDecryptionOperationFactory : public AesOperationFactory {
+    keymaster_purpose_t purpose() const override { return KM_PURPOSE_DECRYPT; }
+};
+
 static const size_t MAX_EVP_KEY_SIZE = 32;
 
 class AesEvpOperation : public Operation {
   public:
     AesEvpOperation(keymaster_purpose_t purpose, keymaster_block_mode_t block_mode,
-                    keymaster_padding_t padding, bool caller_iv, const uint8_t* key,
-                    size_t key_size);
+                    keymaster_padding_t padding, bool caller_iv, size_t tag_length,
+                    const uint8_t* key, size_t key_size);
     ~AesEvpOperation();
 
+    keymaster_error_t Begin(const AuthorizationSet& input_params,
+                            AuthorizationSet* output_params) override;
     keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input,
                              AuthorizationSet* output_params, Buffer* output,
                              size_t* input_consumed) override;
@@ -47,15 +78,25 @@
     bool need_iv() const;
     keymaster_error_t InitializeCipher();
     keymaster_error_t GetIv(const AuthorizationSet& input_params);
+    bool HandleAad(const AuthorizationSet& input_params, const Buffer& input,
+                   keymaster_error_t* error);
+    bool ProcessAadBlocks(const uint8_t* data, size_t blocks, keymaster_error_t* error);
+    void FillBufferedAadBlock(keymaster_blob_t* aad);
+    bool ProcessBufferedAadBlock(keymaster_error_t* error);
+    bool InternalUpdate(const uint8_t* input, size_t input_length, Buffer* output,
+                        keymaster_error_t* error);
 
     const keymaster_block_mode_t block_mode_;
     EVP_CIPHER_CTX ctx_;
     UniquePtr<uint8_t[]> iv_;
     size_t iv_length_;
     const bool caller_iv_;
+    size_t tag_length_;
+    UniquePtr<uint8_t[]> aad_block_buf_;
+    size_t aad_block_buf_length_;
 
   private:
-    bool aad_provided_;
+    bool data_started_;
     const size_t key_size_;
     const keymaster_padding_t padding_;
     uint8_t key_[MAX_EVP_KEY_SIZE];
@@ -65,8 +106,8 @@
   public:
     AesEvpEncryptOperation(keymaster_block_mode_t block_mode, keymaster_padding_t padding,
                            bool caller_iv, size_t tag_length, const uint8_t* key, size_t key_size)
-        : AesEvpOperation(KM_PURPOSE_ENCRYPT, block_mode, padding, caller_iv, key, key_size),
-          tag_length_(tag_length) {}
+        : AesEvpOperation(KM_PURPOSE_ENCRYPT, block_mode, padding, caller_iv, tag_length, key,
+                          key_size) {}
 
     keymaster_error_t Begin(const AuthorizationSet& input_params,
                             AuthorizationSet* output_params) override;
@@ -77,64 +118,34 @@
 
   private:
     keymaster_error_t GenerateIv();
-
-    size_t tag_length_;
 };
 
 class AesEvpDecryptOperation : public AesEvpOperation {
   public:
     AesEvpDecryptOperation(keymaster_block_mode_t block_mode, keymaster_padding_t padding,
-                           const uint8_t* key, size_t key_size)
+                           size_t tag_length, const uint8_t* key, size_t key_size)
         : AesEvpOperation(KM_PURPOSE_DECRYPT, block_mode, padding,
-                          false /* caller_iv -- don't care */, key, key_size),
-          tag_provided_(false) {}
+                          false /* caller_iv -- don't care */, tag_length, key, key_size) {}
 
     keymaster_error_t Begin(const AuthorizationSet& input_params,
                             AuthorizationSet* output_params) override;
     keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input,
                              AuthorizationSet* output_params, Buffer* output,
                              size_t* input_consumed) override;
+    keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& signature,
+                             AuthorizationSet* output_params, Buffer* output) override;
 
     int evp_encrypt_mode() override { return 0; }
 
   private:
-    bool tag_provided_;
-};
+    size_t tag_buf_unused() { return tag_length_ - tag_buf_length_; }
 
-/**
- * Abstract base for AES operation factories.  This class does all of the work to create
- * AES operations.
- */
-class AesOperationFactory : public OperationFactory {
-  public:
-    virtual KeyType registry_key() const { return KeyType(KM_ALGORITHM_AES, purpose()); }
+    keymaster_error_t ProcessAllButTagLengthBytes(const Buffer& input, Buffer* output);
+    bool ProcessTagBufContentsAsData(size_t to_process, Buffer* output, keymaster_error_t* error);
+    void BufferCandidateTagData(const uint8_t* data, size_t data_length);
 
-    virtual Operation* CreateOperation(const Key& key, const AuthorizationSet& begin_params,
-                                       keymaster_error_t* error);
-    virtual const keymaster_block_mode_t* SupportedBlockModes(size_t* block_mode_count) const;
-    virtual const keymaster_padding_t* SupportedPaddingModes(size_t* padding_count) const;
-
-    virtual keymaster_purpose_t purpose() const = 0;
-
-  private:
-    virtual Operation* CreateEvpOperation(const SymmetricKey& key,
-                                          keymaster_block_mode_t block_mode,
-                                          keymaster_padding_t padding, bool caller_iv,
-                                          size_t tag_length, keymaster_error_t* error);
-};
-
-/**
- * Concrete factory for AES encryption operations.
- */
-class AesEncryptionOperationFactory : public AesOperationFactory {
-    keymaster_purpose_t purpose() const { return KM_PURPOSE_ENCRYPT; }
-};
-
-/**
- * Concrete factory for AES decryption operations.
- */
-class AesDecryptionOperationFactory : public AesOperationFactory {
-    keymaster_purpose_t purpose() const { return KM_PURPOSE_DECRYPT; }
+    UniquePtr<uint8_t[]> tag_buf_;
+    size_t tag_buf_length_;
 };
 
 }  // namespace keymaster
diff --git a/android_keymaster_test.cpp b/android_keymaster_test.cpp
index 1a56ddd..e5af555 100644
--- a/android_keymaster_test.cpp
+++ b/android_keymaster_test.cpp
@@ -398,8 +398,7 @@
     for (size_t size : valid_sizes) {
         EXPECT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(size).Digest(
                                    KM_DIGEST_NONE)))
-            << "Failed to generate size: "
-            << size;
+            << "Failed to generate size: " << size;
     }
 
     if (GetParam()->algorithm_in_hardware(KM_ALGORITHM_EC))
@@ -2227,40 +2226,249 @@
     begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
     begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
     begin_params.push_back(TAG_MAC_LENGTH, 128);
-    AuthorizationSet begin_out_params;
 
     AuthorizationSet update_params;
     update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
-    AuthorizationSet update_out_params;
-
-    AuthorizationSet finish_params;
-    AuthorizationSet finish_out_params;
-
-    string ciphertext;
-    string discard;
-    string plaintext;
-
-    size_t input_consumed;
 
     // Encrypt
+    AuthorizationSet begin_out_params;
     EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+    string ciphertext;
+    size_t input_consumed;
+    AuthorizationSet update_out_params;
     EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
                                            &input_consumed));
     EXPECT_EQ(message.size(), input_consumed);
-    EXPECT_EQ(KM_ERROR_OK, FinishOperation(finish_params, "", &finish_out_params, &discard));
+    EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
 
-    // Grab nonce & tag.
+    // Grab nonce
     EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
-    EXPECT_NE(-1, finish_out_params.find(TAG_AEAD_TAG));
     begin_params.push_back(begin_out_params);
-    update_params.push_back(finish_out_params);
 
     // Decrypt.
-    EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+    EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+    string plaintext;
     EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
                                            &plaintext, &input_consumed));
     EXPECT_EQ(ciphertext.size(), input_consumed);
-    EXPECT_EQ(KM_ERROR_OK, FinishOperation(&discard));
+    EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+
+    EXPECT_EQ(message, plaintext);
+    EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmCorruptKey) {
+    uint8_t nonce[] = {
+        0xb7, 0x94, 0x37, 0xae, 0x08, 0xff, 0x35, 0x5d, 0x7d, 0x8a, 0x4d, 0x0f,
+    };
+    uint8_t ciphertext[] = {
+        0xb3, 0xf6, 0x79, 0x9e, 0x8f, 0x93, 0x26, 0xf2, 0xdf, 0x1e, 0x80, 0xfc, 0xd2, 0xcb, 0x16,
+        0xd7, 0x8c, 0x9d, 0xc7, 0xcc, 0x14, 0xbb, 0x67, 0x78, 0x62, 0xdc, 0x6c, 0x63, 0x9b, 0x3a,
+        0x63, 0x38, 0xd2, 0x4b, 0x31, 0x2d, 0x39, 0x89, 0xe5, 0x92, 0x0b, 0x5d, 0xbf, 0xc9, 0x76,
+        0x76, 0x5e, 0xfb, 0xfe, 0x57, 0xbb, 0x38, 0x59, 0x40, 0xa7, 0xa4, 0x3b, 0xdf, 0x05, 0xbd,
+        0xda, 0xe3, 0xc9, 0xd6, 0xa2, 0xfb, 0xbd, 0xfc, 0xc0, 0xcb, 0xa0,
+    };
+    string ciphertext_str(reinterpret_cast<char*>(ciphertext), sizeof(ciphertext));
+
+    AuthorizationSet begin_params(client_params());
+    begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+    begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+    begin_params.push_back(TAG_MAC_LENGTH, 128);
+    begin_params.push_back(TAG_NONCE, nonce, sizeof(nonce));
+
+    string plaintext;
+    size_t input_consumed;
+
+    // Import correct key and decrypt
+    uint8_t good_key[] = {
+        0xba, 0x76, 0x35, 0x4f, 0x0a, 0xed, 0x6e, 0x8d,
+        0x91, 0xf4, 0x5c, 0x4f, 0xf5, 0xa0, 0x62, 0xdb,
+    };
+    string good_key_str(reinterpret_cast<char*>(good_key), sizeof(good_key));
+    ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder()
+                                         .AesEncryptionKey(128)
+                                         .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+                                         .Authorization(TAG_PADDING, KM_PAD_NONE)
+                                         .Authorization(TAG_CALLER_NONCE),
+                                     KM_KEY_FORMAT_RAW, good_key_str));
+    EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+    EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext_str, &plaintext, &input_consumed));
+    EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+
+    // Import bad key and decrypt
+    uint8_t bad_key[] = {
+        0xbb, 0x76, 0x35, 0x4f, 0x0a, 0xed, 0x6e, 0x8d,
+        0x91, 0xf4, 0x5c, 0x4f, 0xf5, 0xa0, 0x62, 0xdb,
+    };
+    string bad_key_str(reinterpret_cast<char*>(bad_key), sizeof(bad_key));
+    ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder()
+                                         .AesEncryptionKey(128)
+                                         .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+                                         .Authorization(TAG_PADDING, KM_PAD_NONE),
+                                     KM_KEY_FORMAT_RAW, bad_key_str));
+    EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+    EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext_str, &plaintext, &input_consumed));
+    EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&plaintext));
+
+    EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmAadNoData) {
+    ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+                                           .AesEncryptionKey(128)
+                                           .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+                                           .Authorization(TAG_PADDING, KM_PAD_NONE)));
+    string aad = "123456789012345678";
+    string empty_message;
+    AuthorizationSet begin_params(client_params());
+    begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+    begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+    begin_params.push_back(TAG_MAC_LENGTH, 128);
+
+    AuthorizationSet update_params;
+    update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+    // Encrypt
+    AuthorizationSet begin_out_params;
+    EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+    string ciphertext;
+    size_t input_consumed;
+    AuthorizationSet update_out_params;
+    EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, empty_message, &update_out_params,
+                                           &ciphertext, &input_consumed));
+    EXPECT_EQ(0U, input_consumed);
+    EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+
+    // Grab nonce
+    EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+    begin_params.push_back(begin_out_params);
+
+    // Decrypt.
+    EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+    string plaintext;
+    EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
+                                           &plaintext, &input_consumed));
+    EXPECT_EQ(ciphertext.size(), input_consumed);
+    EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+
+    EXPECT_EQ(empty_message, plaintext);
+    EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmIncremental) {
+    ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+                                           .AesEncryptionKey(128)
+                                           .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+                                           .Authorization(TAG_PADDING, KM_PAD_NONE)));
+    AuthorizationSet begin_params(client_params());
+    begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+    begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+    begin_params.push_back(TAG_MAC_LENGTH, 128);
+
+    AuthorizationSet update_params;
+    update_params.push_back(TAG_ASSOCIATED_DATA, "b", 1);
+
+    // Encrypt
+    AuthorizationSet begin_out_params;
+    EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+    string ciphertext;
+    size_t input_consumed;
+    AuthorizationSet update_out_params;
+
+    // Send AAD, incrementally
+    for (int i = 0; i < 1000; ++i) {
+        EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, "", &update_out_params, &ciphertext,
+                                               &input_consumed));
+        EXPECT_EQ(0U, input_consumed);
+        EXPECT_EQ(0U, ciphertext.size());
+    }
+
+    // Now send data, incrementally, no data.
+    AuthorizationSet empty_params;
+    for (int i = 0; i < 1000; ++i) {
+        EXPECT_EQ(KM_ERROR_OK, UpdateOperation(empty_params, "a", &update_out_params, &ciphertext,
+                                               &input_consumed));
+        EXPECT_EQ(1U, input_consumed);
+    }
+    EXPECT_EQ(1000U, ciphertext.size());
+
+    // And finish.
+    EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+    EXPECT_EQ(1016U, ciphertext.size());
+
+    // Grab nonce
+    EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+    begin_params.push_back(begin_out_params);
+
+    // Decrypt.
+    EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+    string plaintext;
+
+    // Send AAD, incrementally, no data
+    for (int i = 0; i < 1000; ++i) {
+        EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, "", &update_out_params, &plaintext,
+                                               &input_consumed));
+        EXPECT_EQ(0U, input_consumed);
+        EXPECT_EQ(0U, plaintext.size());
+    }
+
+    // Now send data, incrementally.
+    for (size_t i = 0; i < ciphertext.length(); ++i) {
+        EXPECT_EQ(KM_ERROR_OK, UpdateOperation(empty_params, string(ciphertext.data() + i, 1),
+                                               &update_out_params, &plaintext, &input_consumed));
+        EXPECT_EQ(1U, input_consumed);
+    }
+    EXPECT_EQ(1000U, plaintext.size());
+    EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+
+    EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmMultiPartAad) {
+    ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+                                           .AesEncryptionKey(128)
+                                           .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+                                           .Authorization(TAG_PADDING, KM_PAD_NONE)));
+    string message = "123456789012345678901234567890123456";
+    AuthorizationSet begin_params(client_params());
+    begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+    begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+    begin_params.push_back(TAG_MAC_LENGTH, 128);
+    AuthorizationSet begin_out_params;
+
+    AuthorizationSet update_params;
+    update_params.push_back(TAG_ASSOCIATED_DATA, "foo", 3);
+
+    EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+
+    // No data, AAD only.
+    string ciphertext;
+    size_t input_consumed;
+    AuthorizationSet update_out_params;
+    EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, "" /* message */, &update_out_params,
+                                           &ciphertext, &input_consumed));
+    EXPECT_EQ(0U, input_consumed);
+
+    // AAD and data.
+    EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
+                                           &input_consumed));
+    EXPECT_EQ(message.size(), input_consumed);
+    EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+
+    // Grab nonce.
+    EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+    begin_params.push_back(begin_out_params);
+
+    // Decrypt
+    update_params.Clear();
+    update_params.push_back(TAG_ASSOCIATED_DATA, "foofoo", 6);
+
+    EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+    string plaintext;
+    EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
+                                           &plaintext, &input_consumed));
+    EXPECT_EQ(ciphertext.size(), input_consumed);
+    EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
 
     EXPECT_EQ(message, plaintext);
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
@@ -2271,50 +2479,44 @@
                                            .AesEncryptionKey(128)
                                            .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
                                            .Authorization(TAG_PADDING, KM_PAD_NONE)));
-    string aad = "foobar";
     string message = "12345678901234567890123456789012";
     AuthorizationSet begin_params(client_params());
     begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
     begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
     begin_params.push_back(TAG_MAC_LENGTH, 128);
-    AuthorizationSet begin_out_params;
 
     AuthorizationSet update_params;
-    update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
-    AuthorizationSet update_out_params;
+    update_params.push_back(TAG_ASSOCIATED_DATA, "foobar", 6);
 
     AuthorizationSet finish_params;
     AuthorizationSet finish_out_params;
 
-    string ciphertext;
-    string discard;
-    string plaintext;
-
-    size_t input_consumed;
-
     // Encrypt
+    AuthorizationSet begin_out_params;
     EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+    AuthorizationSet update_out_params;
+    string ciphertext;
+    size_t input_consumed;
     EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
                                            &input_consumed));
     EXPECT_EQ(message.size(), input_consumed);
-    EXPECT_EQ(KM_ERROR_OK, FinishOperation(finish_params, "", &finish_out_params, &discard));
+    EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
 
-    // Grab nonce & tag.
+    // Grab nonce
     EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
-    EXPECT_NE(-1, finish_out_params.find(TAG_AEAD_TAG));
     begin_params.push_back(begin_out_params);
+
     update_params.Clear();
     update_params.push_back(TAG_ASSOCIATED_DATA, "barfoo" /* Wrong AAD */, 6);
-    update_params.push_back(finish_out_params);
 
     // Decrypt.
     EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+    string plaintext;
     EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
                                            &plaintext, &input_consumed));
     EXPECT_EQ(ciphertext.size(), input_consumed);
-    EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&discard));
+    EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&plaintext));
 
-    EXPECT_EQ(message, plaintext);
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
 }
 
@@ -2323,54 +2525,35 @@
                                            .AesEncryptionKey(128)
                                            .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
                                            .Authorization(TAG_PADDING, KM_PAD_NONE)));
-    string aad = "foobar";
     string message = "12345678901234567890123456789012";
     AuthorizationSet begin_params(client_params());
     begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
     begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
     begin_params.push_back(TAG_MAC_LENGTH, 128);
-    AuthorizationSet begin_out_params;
 
     AuthorizationSet update_params;
-    update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
-    AuthorizationSet update_out_params;
-
-    AuthorizationSet finish_params;
-    AuthorizationSet finish_out_params;
-
-    string ciphertext;
-    string discard;
-    string plaintext;
-
-    size_t input_consumed;
+    update_params.push_back(TAG_ASSOCIATED_DATA, "foobar", 6);
 
     // Encrypt
+    AuthorizationSet begin_out_params;
     EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+    AuthorizationSet update_out_params;
+    string ciphertext;
+    size_t input_consumed;
     EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
                                            &input_consumed));
     EXPECT_EQ(message.size(), input_consumed);
-    EXPECT_EQ(KM_ERROR_OK, FinishOperation(finish_params, "", &finish_out_params, &discard));
+    EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
 
-    // Try with wrong-length nonce.  Should fail immediately.
-    AuthorizationSet long_nonce(begin_params);
-    long_nonce.push_back(TAG_NONCE, "1234567890123", 13);
-    EXPECT_EQ(KM_ERROR_INVALID_NONCE,
-              BeginOperation(KM_PURPOSE_DECRYPT, long_nonce, &begin_out_params));
-    AuthorizationSet short_nonce(begin_params);
-    short_nonce.push_back(TAG_NONCE, "12345678901", 11);
-    EXPECT_EQ(KM_ERROR_INVALID_NONCE,
-              BeginOperation(KM_PURPOSE_DECRYPT, short_nonce, &begin_out_params));
-
-    // Now with correct length, but incorrect content.
-    EXPECT_NE(-1, finish_out_params.find(TAG_AEAD_TAG));
-    update_params.push_back(finish_out_params);
     begin_params.push_back(TAG_NONCE, "123456789012", 12);
 
+    // Decrypt
     EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+    string plaintext;
     EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
                                            &plaintext, &input_consumed));
     EXPECT_EQ(ciphertext.size(), input_consumed);
-    EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&discard));
+    EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&plaintext));
 
     // With wrong nonce, should have gotten garbage plaintext.
     EXPECT_NE(message, plaintext);
@@ -2392,92 +2575,36 @@
 
     AuthorizationSet update_params;
     update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
-    AuthorizationSet update_out_params;
-
-    AuthorizationSet finish_params;
-    AuthorizationSet finish_out_params;
-
-    string ciphertext;
-    string discard;
-    string plaintext;
-
-    size_t input_consumed;
 
     // Encrypt
     EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+    AuthorizationSet update_out_params;
+    string ciphertext;
+    size_t input_consumed;
     EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
                                            &input_consumed));
     EXPECT_EQ(message.size(), input_consumed);
-    EXPECT_EQ(KM_ERROR_OK, FinishOperation(finish_params, "", &finish_out_params, &discard));
+    EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
 
-    // Grab nonce & tag; corrupt tag.
+    // Corrupt tag
+    (*ciphertext.rbegin())++;
+
+    // Grab nonce.
     EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
     begin_params.push_back(begin_out_params);
-    keymaster_blob_t tag;
-    EXPECT_TRUE(finish_out_params.GetTagValue(TAG_AEAD_TAG, &tag));
-    const_cast<uint8_t*>(tag.data)[tag.data_length / 2]++;
-    update_params.push_back(TAG_AEAD_TAG, tag);
 
     // Decrypt.
     EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+    string plaintext;
     EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
                                            &plaintext, &input_consumed));
     EXPECT_EQ(ciphertext.size(), input_consumed);
-    EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&discard));
+    EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&plaintext));
 
     EXPECT_EQ(message, plaintext);
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
 }
 
-TEST_P(EncryptionOperationsTest, AesGcmShortTag) {
-    ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
-                                           .AesEncryptionKey(128)
-                                           .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
-                                           .Authorization(TAG_PADDING, KM_PAD_NONE)));
-    string aad = "foobar";
-    string message = "123456789012345678901234567890123456";
-    AuthorizationSet begin_params(client_params());
-    begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
-    begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
-    begin_params.push_back(TAG_MAC_LENGTH, 128);
-    AuthorizationSet begin_out_params;
-
-    AuthorizationSet update_params;
-    update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
-    AuthorizationSet update_out_params;
-
-    AuthorizationSet finish_params;
-    AuthorizationSet finish_out_params;
-
-    string ciphertext;
-    string discard;
-    string plaintext;
-
-    size_t input_consumed;
-
-    // Encrypt
-    EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
-    EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
-                                           &input_consumed));
-    EXPECT_EQ(message.size(), input_consumed);
-    EXPECT_EQ(KM_ERROR_OK, FinishOperation(finish_params, "", &finish_out_params, &discard));
-
-    EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
-    begin_params.push_back(begin_out_params);
-    keymaster_blob_t tag;
-    EXPECT_TRUE(finish_out_params.GetTagValue(TAG_AEAD_TAG, &tag));
-    tag.data_length = 11;
-    update_params.push_back(TAG_AEAD_TAG, tag);
-
-    // Decrypt.
-    EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
-    EXPECT_EQ(KM_ERROR_UNSUPPORTED_MAC_LENGTH,
-              UpdateOperation(update_params, ciphertext, &update_out_params, &plaintext,
-                              &input_consumed));
-
-    EXPECT_EQ(0, GetParam()->keymaster0_calls());
-}
-
 typedef Keymaster1Test AddEntropyTest;
 INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, AddEntropyTest, test_params);
 
diff --git a/include/keymaster/keymaster_tags.h b/include/keymaster/keymaster_tags.h
index 3213a2e..954a93d 100644
--- a/include/keymaster/keymaster_tags.h
+++ b/include/keymaster/keymaster_tags.h
@@ -161,7 +161,6 @@
 DEFINE_KEYMASTER_TAG(KM_INVALID, TAG_INVALID);
 DEFINE_KEYMASTER_TAG(KM_INT, TAG_KEY_SIZE);
 DEFINE_KEYMASTER_TAG(KM_INT, TAG_MAC_LENGTH);
-DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_AEAD_TAG);
 DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_CALLER_NONCE);
 DEFINE_KEYMASTER_TAG(KM_LONG, TAG_RSA_PUBLIC_EXPONENT);
 DEFINE_KEYMASTER_TAG(KM_DATE, TAG_ACTIVE_DATETIME);