Add AES-GCM mode.

Cherry-picked from internal.

Bug: 19919114
Change-Id: I9c82fb50d2372b2ef7a84d74f9f4868994d80869
diff --git a/aes_operation.cpp b/aes_operation.cpp
index 73549a8..7c62d74 100644
--- a/aes_operation.cpp
+++ b/aes_operation.cpp
@@ -30,6 +30,10 @@
 
 namespace keymaster {
 
+static const size_t GCM_NONCE_SIZE = 12;
+static const size_t GCM_MAX_TAG_LENGTH = 16;
+static const size_t GCM_MIN_TAG_LENGTH = 12;
+
 Operation* AesOperationFactory::CreateOperation(const Key& key,
                                                 const AuthorizationSet& begin_params,
                                                 keymaster_error_t* error) {
@@ -50,30 +54,38 @@
     if (!begin_params.GetTagValue(TAG_BLOCK_MODE, &block_mode)) {
         LOG_E("%d block modes specified in begin params", begin_params.GetTagCount(TAG_BLOCK_MODE));
         *error = KM_ERROR_UNSUPPORTED_BLOCK_MODE;
-        return nullptr;
     } else if (!supported(block_mode)) {
         LOG_E("Block mode %d not supported", block_mode);
         *error = KM_ERROR_UNSUPPORTED_BLOCK_MODE;
-        return nullptr;
     } else if (!key.authorizations().Contains(TAG_BLOCK_MODE, block_mode)) {
         LOG_E("Block mode %d was specified, but not authorized by key", block_mode);
         *error = KM_ERROR_INCOMPATIBLE_BLOCK_MODE;
-        return nullptr;
+    }
+
+    size_t tag_length = 0;
+    if (block_mode == KM_MODE_GCM && purpose() == KM_PURPOSE_ENCRYPT) {
+        uint32_t tag_length_bits;
+        if (!begin_params.GetTagValue(TAG_MAC_LENGTH, &tag_length_bits))
+            *error = KM_ERROR_MISSING_MAC_LENGTH;
+        tag_length = tag_length_bits / 8;
+        if (tag_length_bits % 8 != 0 || tag_length > GCM_MAX_TAG_LENGTH ||
+            tag_length < GCM_MIN_TAG_LENGTH)
+            *error = KM_ERROR_UNSUPPORTED_MAC_LENGTH;
     }
 
     keymaster_padding_t padding;
     if (!begin_params.GetTagValue(TAG_PADDING, &padding)) {
         LOG_E("%d padding modes specified in begin params", begin_params.GetTagCount(TAG_PADDING));
         *error = KM_ERROR_UNSUPPORTED_PADDING_MODE;
-        return nullptr;
     } else if (!supported(padding)) {
         LOG_E("Padding mode %d not supported", padding);
         *error = KM_ERROR_UNSUPPORTED_PADDING_MODE;
-        return nullptr;
+    } else if (block_mode == KM_MODE_CTR && padding != KM_PAD_NONE) {
+        LOG_E("CTR mode does not support padding", 0);
+        *error = KM_ERROR_INCOMPATIBLE_PADDING_MODE;
     } else if (!key.authorizations().Contains(TAG_PADDING, padding)) {
         LOG_E("Padding mode %d was specified, but not authorized by key", padding);
         *error = KM_ERROR_INCOMPATIBLE_PADDING_MODE;
-        return nullptr;
     }
 
     bool caller_nonce = key.authorizations().GetTagValue(TAG_CALLER_NONCE);
@@ -81,30 +93,17 @@
     if (*error != KM_ERROR_OK)
         return nullptr;
 
-    switch (block_mode) {
-    case KM_MODE_ECB:
-    case KM_MODE_CBC:
-        return CreateEvpOperation(*symmetric_key, block_mode, padding, caller_nonce, error);
-    case KM_MODE_CTR:
-        if (padding != KM_PAD_NONE) {
-            *error = KM_ERROR_INCOMPATIBLE_PADDING_MODE;
-            return nullptr;
-        }
-        return CreateEvpOperation(*symmetric_key, block_mode, padding, caller_nonce, error);
-    default:
-        *error = KM_ERROR_UNSUPPORTED_BLOCK_MODE;
-        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,
-                                                   keymaster_error_t* error) {
+                                                   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, key.key_data(),
+        op = new AesEvpEncryptOperation(block_mode, padding, caller_iv, tag_length, key.key_data(),
                                         key.key_data_size());
         break;
     case KM_PURPOSE_DECRYPT:
@@ -121,7 +120,7 @@
 }
 
 static const keymaster_block_mode_t supported_block_modes[] = {KM_MODE_ECB, KM_MODE_CBC,
-                                                               KM_MODE_CTR};
+                                                               KM_MODE_CTR, KM_MODE_GCM};
 
 const keymaster_block_mode_t*
 AesOperationFactory::SupportedBlockModes(size_t* block_mode_count) const {
@@ -139,8 +138,8 @@
 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), key_size_(key_size), block_mode_(block_mode), padding_(padding),
-      caller_iv_(caller_iv) {
+    : Operation(purpose), block_mode_(block_mode), caller_iv_(caller_iv), aad_provided_(false),
+      key_size_(key_size), padding_(padding) {
     memcpy(key_, key, key_size_);
     EVP_CIPHER_CTX_init(&ctx_);
 }
@@ -197,14 +196,26 @@
             return KM_ERROR_UNSUPPORTED_KEY_SIZE;
         }
         break;
+    case KM_MODE_GCM:
+        switch (key_size_) {
+        case 16:
+            cipher = EVP_aes_128_gcm();
+            break;
+        case 24:
+            cipher = EVP_aes_192_gcm();
+            break;
+        case 32:
+            cipher = EVP_aes_256_gcm();
+            break;
+        default:
+            return KM_ERROR_UNSUPPORTED_KEY_SIZE;
+        }
+        break;
     default:
         return KM_ERROR_UNSUPPORTED_BLOCK_MODE;
     }
 
-    int init_result =
-        EVP_CipherInit_ex(&ctx_, cipher, NULL /* engine */, key_, iv_.get(), evp_encrypt_mode());
-
-    if (!init_result)
+    if (!EVP_CipherInit_ex(&ctx_, cipher, NULL /* engine */, key_, iv_.get(), evp_encrypt_mode()))
         return TranslateLastOpenSslError();
 
     switch (padding_) {
@@ -225,6 +236,7 @@
     switch (block_mode_) {
     case KM_MODE_CBC:
     case KM_MODE_CTR:
+    case KM_MODE_GCM:
         return true;
     case KM_MODE_ECB:
         return false;
@@ -235,38 +247,41 @@
     }
 }
 
-keymaster_error_t AesEvpOperation::Begin(const AuthorizationSet& input_params,
-                                         AuthorizationSet* output_params) {
+keymaster_error_t AesEvpDecryptOperation::Begin(const AuthorizationSet& input_params,
+                                                AuthorizationSet* output_params) {
     if (!output_params)
         return KM_ERROR_OUTPUT_PARAMETER_NULL;
 
-    keymaster_error_t error = KM_ERROR_OK;
     if (need_iv()) {
-        switch (purpose()) {
-        case KM_PURPOSE_ENCRYPT:
-            if (input_params.find(TAG_NONCE) == -1)
-                error = GenerateIv();
-            else if (caller_iv_)
-                error = GetIv(input_params);
-            else
-                error = KM_ERROR_CALLER_NONCE_PROHIBITED;
-
-            if (error == KM_ERROR_OK)
-                output_params->push_back(TAG_NONCE, iv_.get(), AES_BLOCK_SIZE);
-            break;
-
-        case KM_PURPOSE_DECRYPT:
-            error = GetIv(input_params);
-            break;
-        default:
-            return KM_ERROR_UNSUPPORTED_PURPOSE;
-        }
+        keymaster_error_t error = GetIv(input_params);
+        if (error != KM_ERROR_OK)
+            return error;
     }
 
-    if (error == KM_ERROR_OK)
-        error = InitializeCipher();
+    return InitializeCipher();
+}
 
-    return error;
+keymaster_error_t AesEvpEncryptOperation::Begin(const AuthorizationSet& input_params,
+                                                AuthorizationSet* output_params) {
+    if (!output_params)
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+
+    if (need_iv()) {
+        keymaster_error_t error = KM_ERROR_OK;
+        if (input_params.find(TAG_NONCE) == -1)
+            error = GenerateIv();
+        else if (caller_iv_)
+            error = GetIv(input_params);
+        else
+            error = KM_ERROR_CALLER_NONCE_PROHIBITED;
+
+        if (error == KM_ERROR_OK)
+            output_params->push_back(TAG_NONCE, iv_.get(), iv_length_);
+        else
+            return error;
+    }
+
+    return InitializeCipher();
 }
 
 keymaster_error_t AesEvpOperation::GetIv(const AuthorizationSet& input_params) {
@@ -275,7 +290,13 @@
         LOG_E("No IV provided", 0);
         return KM_ERROR_INVALID_ARGUMENT;
     }
-    if (iv_blob.data_length != AES_BLOCK_SIZE) {
+    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;
@@ -283,14 +304,16 @@
     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 AesEvpOperation::GenerateIv() {
-    iv_.reset(new uint8_t[AES_BLOCK_SIZE]);
+keymaster_error_t AesEvpEncryptOperation::GenerateIv() {
+    iv_length_ = (block_mode_ == KM_MODE_GCM) ? GCM_NONCE_SIZE : AES_BLOCK_SIZE;
+    iv_.reset(new uint8_t[iv_length_]);
     if (!iv_.get())
         return KM_ERROR_MEMORY_ALLOCATION_FAILED;
-    if (RAND_bytes(iv_.get(), AES_BLOCK_SIZE) != 1)
+    if (RAND_bytes(iv_.get(), iv_length_) != 1)
         return TranslateLastOpenSslError();
     return KM_ERROR_OK;
 }
@@ -301,10 +324,22 @@
     return b;
 }
 
-keymaster_error_t AesEvpOperation::Update(const AuthorizationSet& /* additional_params */,
+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;
+    }
+
     output->reserve(input.available_read() + AES_BLOCK_SIZE);
 
     const uint8_t* input_pos = input.peek_read();
@@ -322,6 +357,33 @@
     return KM_ERROR_OK;
 }
 
+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;
+
+            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;
+    }
+
+    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) {
@@ -329,6 +391,8 @@
 
     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();
     }
@@ -338,6 +402,23 @@
     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)
+        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_);
+    }
+
+    return KM_ERROR_OK;
+}
 keymaster_error_t AesEvpOperation::Abort() {
     return KM_ERROR_OK;
 }
diff --git a/aes_operation.h b/aes_operation.h
index 726a599..dc902a7 100644
--- a/aes_operation.h
+++ b/aes_operation.h
@@ -34,39 +34,51 @@
                     size_t key_size);
     ~AesEvpOperation();
 
-    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);
-    virtual keymaster_error_t Abort();
+    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;
+    keymaster_error_t Abort() override;
 
     virtual int evp_encrypt_mode() = 0;
 
-  private:
+  protected:
+    bool need_iv() const;
     keymaster_error_t InitializeCipher();
     keymaster_error_t GetIv(const AuthorizationSet& input_params);
-    keymaster_error_t GenerateIv();
-    bool need_iv() const;
 
-    EVP_CIPHER_CTX ctx_;
-    const size_t key_size_;
     const keymaster_block_mode_t block_mode_;
-    const keymaster_padding_t padding_;
-    const bool caller_iv_;
+    EVP_CIPHER_CTX ctx_;
     UniquePtr<uint8_t[]> iv_;
+    size_t iv_length_;
+    const bool caller_iv_;
+
+  private:
+    bool aad_provided_;
+    const size_t key_size_;
+    const keymaster_padding_t padding_;
     uint8_t key_[MAX_EVP_KEY_SIZE];
 };
 
 class AesEvpEncryptOperation : public AesEvpOperation {
   public:
     AesEvpEncryptOperation(keymaster_block_mode_t block_mode, keymaster_padding_t padding,
-                           bool caller_iv, const uint8_t* key, size_t key_size)
-        : AesEvpOperation(KM_PURPOSE_ENCRYPT, block_mode, padding, caller_iv, key, key_size) {}
-    int evp_encrypt_mode() { return 1; }
+                           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) {}
+
+    keymaster_error_t Begin(const AuthorizationSet& input_params,
+                            AuthorizationSet* output_params) override;
+    keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& signature,
+                             AuthorizationSet* output_params, Buffer* output) override;
+
+    int evp_encrypt_mode() override { return 1; }
+
+  private:
+    keymaster_error_t GenerateIv();
+
+    size_t tag_length_;
 };
 
 class AesEvpDecryptOperation : public AesEvpOperation {
@@ -74,9 +86,19 @@
     AesEvpDecryptOperation(keymaster_block_mode_t block_mode, keymaster_padding_t padding,
                            const uint8_t* key, size_t key_size)
         : AesEvpOperation(KM_PURPOSE_DECRYPT, block_mode, padding,
-                          false /* caller_iv -- don't care */, key, key_size) {}
+                          false /* caller_iv -- don't care */, key, key_size),
+          tag_provided_(false) {}
 
-    int evp_encrypt_mode() { return 0; }
+    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;
+
+    int evp_encrypt_mode() override { return 0; }
+
+  private:
+    bool tag_provided_;
 };
 
 /**
@@ -98,7 +120,7 @@
     virtual Operation* CreateEvpOperation(const SymmetricKey& key,
                                           keymaster_block_mode_t block_mode,
                                           keymaster_padding_t padding, bool caller_iv,
-                                          keymaster_error_t* error);
+                                          size_t tag_length, keymaster_error_t* error);
 };
 
 /**
diff --git a/android_keymaster_test.cpp b/android_keymaster_test.cpp
index 123f24f..4b86a2d 100644
--- a/android_keymaster_test.cpp
+++ b/android_keymaster_test.cpp
@@ -138,7 +138,7 @@
 
     ASSERT_EQ(KM_ERROR_OK, device()->get_supported_block_modes(device(), KM_ALGORITHM_AES,
                                                                KM_PURPOSE_ENCRYPT, &modes, &len));
-    EXPECT_TRUE(ResponseContains({KM_MODE_ECB, KM_MODE_CBC, KM_MODE_CTR}, modes, len));
+    EXPECT_TRUE(ResponseContains({KM_MODE_ECB, KM_MODE_CBC, KM_MODE_CTR, KM_MODE_GCM}, modes, len));
     free(modes);
 
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
@@ -2203,6 +2203,268 @@
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
 }
 
+TEST_P(EncryptionOperationsTest, AesGcmRoundTripSuccess) {
+    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));
+
+    // Grab nonce & tag.
+    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, 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(message, plaintext);
+    EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmBadAad) {
+    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 = "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;
+
+    // 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));
+
+    // Grab nonce & tag.
+    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));
+    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(message, plaintext);
+    EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmWrongNonce) {
+    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 = "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;
+
+    // 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));
+
+    // 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);
+
+    EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+    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));
+
+    // With wrong nonce, should have gotten garbage plaintext.
+    EXPECT_NE(message, plaintext);
+    EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmCorruptTag) {
+    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));
+
+    // Grab nonce & tag; corrupt tag.
+    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));
+    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(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/android_keymaster_test_utils.cpp b/android_keymaster_test_utils.cpp
index 80f6822..69ca470 100644
--- a/android_keymaster_test_utils.cpp
+++ b/android_keymaster_test_utils.cpp
@@ -249,7 +249,8 @@
 }
 
 keymaster_error_t Keymaster1Test::UpdateOperation(const AuthorizationSet& additional_params,
-                                                  const string& message, string* output,
+                                                  const string& message,
+                                                  AuthorizationSet* output_params, string* output,
                                                   size_t* input_consumed) {
     EXPECT_NE(op_handle_, OP_HANDLE_SENTINEL);
     keymaster_blob_t input = {reinterpret_cast<const uint8_t*>(message.c_str()), message.length()};
@@ -260,6 +261,9 @@
     if (error == KM_ERROR_OK && out_tmp.data)
         output->append(reinterpret_cast<const char*>(out_tmp.data), out_tmp.data_length);
     free((void*)out_tmp.data);
+    if (output_params)
+        output_params->Reinitialize(out_params);
+    keymaster_free_param_set(&out_params);
     return error;
 }
 
@@ -269,11 +273,13 @@
 
 keymaster_error_t Keymaster1Test::FinishOperation(const string& signature, string* output) {
     AuthorizationSet additional_params;
-    return FinishOperation(additional_params, signature, output);
+    AuthorizationSet output_params;
+    return FinishOperation(additional_params, signature, &output_params, output);
 }
 
 keymaster_error_t Keymaster1Test::FinishOperation(const AuthorizationSet& additional_params,
-                                                  const string& signature, string* output) {
+                                                  const string& signature,
+                                                  AuthorizationSet* output_params, string* output) {
     keymaster_blob_t sig = {reinterpret_cast<const uint8_t*>(signature.c_str()),
                             signature.length()};
     keymaster_blob_t out_tmp;
@@ -289,6 +295,9 @@
     if (out_tmp.data)
         output->append(reinterpret_cast<const char*>(out_tmp.data), out_tmp.data_length);
     free((void*)out_tmp.data);
+    if (output_params)
+        output_params->Reinitialize(out_params);
+    keymaster_free_param_set(&out_params);
     return error;
 }
 
@@ -310,12 +319,13 @@
 string Keymaster1Test::ProcessMessage(keymaster_purpose_t purpose, const string& message,
                                       const AuthorizationSet& begin_params,
                                       const AuthorizationSet& update_params,
-                                      AuthorizationSet* output_params) {
-    EXPECT_EQ(KM_ERROR_OK, BeginOperation(purpose, begin_params, output_params));
+                                      AuthorizationSet* begin_out_params) {
+    EXPECT_EQ(KM_ERROR_OK, BeginOperation(purpose, begin_params, begin_out_params));
 
     string result;
     size_t input_consumed;
-    EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &result, &input_consumed));
+    EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, nullptr /* output_params */,
+                                           &result, &input_consumed));
     EXPECT_EQ(message.size(), input_consumed);
     EXPECT_EQ(KM_ERROR_OK, FinishOperation(update_params, "", &result));
     return result;
@@ -329,7 +339,8 @@
 
     string result;
     size_t input_consumed;
-    EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &result, &input_consumed));
+    EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, nullptr /* output_params */,
+                                           &result, &input_consumed));
     EXPECT_EQ(message.size(), input_consumed);
     EXPECT_EQ(KM_ERROR_OK, FinishOperation(update_params, signature, &result));
     return result;
diff --git a/android_keymaster_test_utils.h b/android_keymaster_test_utils.h
index ede14f5..cefd3d3 100644
--- a/android_keymaster_test_utils.h
+++ b/android_keymaster_test_utils.h
@@ -188,13 +188,18 @@
     keymaster_error_t UpdateOperation(const std::string& message, std::string* output,
                                       size_t* input_consumed);
     keymaster_error_t UpdateOperation(const AuthorizationSet& additional_params,
-                                      const std::string& message, std::string* output,
-                                      size_t* input_consumed);
+                                      const std::string& message, AuthorizationSet* output_params,
+                                      std::string* output, size_t* input_consumed);
 
     keymaster_error_t FinishOperation(std::string* output);
     keymaster_error_t FinishOperation(const std::string& signature, std::string* output);
     keymaster_error_t FinishOperation(const AuthorizationSet& additional_params,
-                                      const std::string& signature, std::string* output);
+                                      const std::string& signature, std::string* output) {
+        return FinishOperation(additional_params, signature, nullptr /* output_params */, output);
+    }
+    keymaster_error_t FinishOperation(const AuthorizationSet& additional_params,
+                                      const std::string& signature, AuthorizationSet* output_params,
+                                      std::string* output);
 
     keymaster_error_t AbortOperation();