Add support for KM_TAG_MIN_MAC_LENGTH.

HMAC and AES-GCM keys must be bound to a mininum MAC/tag length at
creation, and operations may not specify a length smaller than the
minimum, or provide a length smaller than the minimum during
verification.

Bug: 22337277
Change-Id: Id5ae2f4259045ba1418c28e9de8f4a47e67fd433
diff --git a/aes_key.cpp b/aes_key.cpp
index bc940df..92ea66e 100644
--- a/aes_key.cpp
+++ b/aes_key.cpp
@@ -48,6 +48,18 @@
     if (!key)
         return KM_ERROR_OUTPUT_PARAMETER_NULL;
 
+    uint32_t min_mac_length = 0;
+    if (hw_enforced.Contains(TAG_BLOCK_MODE, KM_MODE_GCM) ||
+        sw_enforced.Contains(TAG_BLOCK_MODE, KM_MODE_GCM)) {
+
+        if (!hw_enforced.GetTagValue(TAG_MIN_MAC_LENGTH, &min_mac_length) &&
+            !sw_enforced.GetTagValue(TAG_MIN_MAC_LENGTH, &min_mac_length)) {
+
+            LOG_E("AES-GCM key must have KM_TAG_MIN_MAC_LENGTH", 0);
+            return KM_ERROR_INVALID_KEY_BLOB;
+        }
+    }
+
     keymaster_error_t error = KM_ERROR_OK;
     key->reset(new (std::nothrow) AesKey(key_material, hw_enforced, sw_enforced, &error));
     if (!key->get())
@@ -55,4 +67,27 @@
     return error;
 }
 
+keymaster_error_t AesKeyFactory::validate_algorithm_specific_new_key_params(
+    const AuthorizationSet& key_description) const {
+    if (key_description.Contains(TAG_BLOCK_MODE, KM_MODE_GCM)) {
+        uint32_t min_tag_length;
+        if (!key_description.GetTagValue(TAG_MIN_MAC_LENGTH, &min_tag_length))
+            return KM_ERROR_MISSING_MIN_MAC_LENGTH;
+
+        if (min_tag_length % 8 != 0)
+            return KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH;
+
+        if (min_tag_length < kMinGcmTagLength || min_tag_length > kMaxGcmTagLength)
+            return KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH;
+    } else {
+        // Not GCM
+        if (key_description.find(TAG_MIN_MAC_LENGTH) != -1) {
+            LOG_W("KM_TAG_MIN_MAC_LENGTH found for non AES-GCM key", 0);
+            return KM_ERROR_INVALID_TAG;
+        }
+    }
+
+    return KM_ERROR_OK;
+}
+
 }  // namespace keymaster
diff --git a/aes_key.h b/aes_key.h
index dfc9578..9c58647 100644
--- a/aes_key.h
+++ b/aes_key.h
@@ -23,6 +23,9 @@
 
 namespace keymaster {
 
+const size_t kMinGcmTagLength = 12 * 8;
+const size_t kMaxGcmTagLength = 16 * 8;
+
 class AesKeyFactory : public SymmetricKeyFactory {
   public:
     AesKeyFactory(const KeymasterContext* context) : SymmetricKeyFactory(context) {}
@@ -40,6 +43,8 @@
     bool key_size_supported(size_t key_size_bits) const override {
         return key_size_bits == 128 || key_size_bits == 192 || key_size_bits == 256;
     }
+    keymaster_error_t validate_algorithm_specific_new_key_params(
+        const AuthorizationSet& key_description) const override;
 };
 
 class AesKey : public SymmetricKey {
diff --git a/aes_operation.cpp b/aes_operation.cpp
index bf18041..c3bc662 100644
--- a/aes_operation.cpp
+++ b/aes_operation.cpp
@@ -50,6 +50,33 @@
     return false;
 }
 
+static keymaster_error_t GetAndValidateGcmTagLength(const AuthorizationSet& begin_params,
+                                                    const AuthorizationSet& key_params,
+                                                    size_t* tag_length) {
+    uint32_t tag_length_bits;
+    if (!begin_params.GetTagValue(TAG_MAC_LENGTH, &tag_length_bits)) {
+        return KM_ERROR_MISSING_MAC_LENGTH;
+    }
+
+    uint32_t min_tag_length_bits;
+    if (!key_params.GetTagValue(TAG_MIN_MAC_LENGTH, &min_tag_length_bits)) {
+        LOG_E("AES GCM key must have KM_TAG_MIN_MAC_LENGTH", 0);
+        return KM_ERROR_INVALID_KEY_BLOB;
+    }
+
+    if (tag_length_bits % 8 != 0 || tag_length_bits > kMaxGcmTagLength ||
+        tag_length_bits < kMinGcmTagLength) {
+        return KM_ERROR_UNSUPPORTED_MAC_LENGTH;
+    }
+
+    if (tag_length_bits < min_tag_length_bits) {
+        return KM_ERROR_INVALID_MAC_LENGTH;
+    }
+
+    *tag_length = tag_length_bits / 8;
+    return KM_ERROR_OK;
+}
+
 Operation* AesOperationFactory::CreateOperation(const Key& key,
                                                 const AuthorizationSet& begin_params,
                                                 keymaster_error_t* error) {
@@ -83,22 +110,16 @@
 
     size_t tag_length = 0;
     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;
-            return nullptr;
-        }
-        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;
+        *error = GetAndValidateGcmTagLength(begin_params, key.authorizations(), &tag_length);
+        if (*error != KM_ERROR_OK) {
             return nullptr;
         }
     }
 
     keymaster_padding_t padding;
-    if (!GetAndValidatePadding(begin_params, key, &padding, error))
+    if (!GetAndValidatePadding(begin_params, key, &padding, error)) {
         return nullptr;
+    }
     if (!allows_padding(block_mode) && padding != KM_PAD_NONE) {
         LOG_E("Mode does not support padding", 0);
         *error = KM_ERROR_INCOMPATIBLE_PADDING_MODE;
diff --git a/android_keymaster_test.cpp b/android_keymaster_test.cpp
index c0d6bb7..96fc8b2 100644
--- a/android_keymaster_test.cpp
+++ b/android_keymaster_test.cpp
@@ -438,8 +438,61 @@
 }
 
 TEST_P(NewKeyGeneration, HmacSha256) {
-    ASSERT_EQ(KM_ERROR_OK,
-              GenerateKey(AuthorizationSetBuilder().HmacKey(128).Digest(KM_DIGEST_SHA_2_256)));
+    ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+                                           .HmacKey(128)
+                                           .Digest(KM_DIGEST_SHA_2_256)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 256)));
+
+    EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(NewKeyGeneration, HmacMultipleDigests) {
+    ASSERT_EQ(KM_ERROR_UNSUPPORTED_DIGEST,
+              GenerateKey(AuthorizationSetBuilder()
+                              .HmacKey(128)
+                              .Digest(KM_DIGEST_SHA1)
+                              .Digest(KM_DIGEST_SHA_2_256)
+                              .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(NewKeyGeneration, HmacDigestNone) {
+    ASSERT_EQ(KM_ERROR_UNSUPPORTED_DIGEST,
+              GenerateKey(AuthorizationSetBuilder()
+                              .HmacKey(128)
+                              .Digest(KM_DIGEST_NONE)
+                              .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(NewKeyGeneration, HmacSha256TooShortMacLength) {
+    ASSERT_EQ(KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH,
+              GenerateKey(AuthorizationSetBuilder()
+                              .HmacKey(128)
+                              .Digest(KM_DIGEST_SHA_2_256)
+                              .Authorization(TAG_MIN_MAC_LENGTH, 48)));
+
+    EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(NewKeyGeneration, HmacSha256NonIntegralOctetMacLength) {
+    ASSERT_EQ(KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH,
+              GenerateKey(AuthorizationSetBuilder()
+                              .HmacKey(128)
+                              .Digest(KM_DIGEST_SHA_2_256)
+                              .Authorization(TAG_MIN_MAC_LENGTH, 130)));
+
+    EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(NewKeyGeneration, HmacSha256TooLongMacLength) {
+    ASSERT_EQ(KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH,
+              GenerateKey(AuthorizationSetBuilder()
+                              .HmacKey(128)
+                              .Digest(KM_DIGEST_SHA_2_256)
+                              .Authorization(TAG_MIN_MAC_LENGTH, 384)));
 
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
 }
@@ -759,94 +812,67 @@
 }
 
 TEST_P(SigningOperationsTest, HmacSha1Success) {
-    GenerateKey(AuthorizationSetBuilder().HmacKey(128).Digest(KM_DIGEST_SHA1));
+    GenerateKey(AuthorizationSetBuilder()
+                    .HmacKey(128)
+                    .Digest(KM_DIGEST_SHA1)
+                    .Authorization(TAG_MIN_MAC_LENGTH, 160));
     string message = "12345678901234567890123456789012";
     string signature;
-    MacMessage(message, &signature, KM_DIGEST_SHA1, 160);
+    MacMessage(message, &signature, 160);
     ASSERT_EQ(20U, signature.size());
 
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
 }
 
 TEST_P(SigningOperationsTest, HmacSha224Success) {
-    ASSERT_EQ(KM_ERROR_OK,
-              GenerateKey(AuthorizationSetBuilder().HmacKey(128).Digest(KM_DIGEST_SHA_2_224)));
+    ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+                                           .HmacKey(128)
+                                           .Digest(KM_DIGEST_SHA_2_224)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 160)));
     string message = "12345678901234567890123456789012";
     string signature;
-    MacMessage(message, &signature, KM_DIGEST_SHA_2_224, 224);
+    MacMessage(message, &signature, 224);
     ASSERT_EQ(28U, signature.size());
 
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
 }
 
 TEST_P(SigningOperationsTest, HmacSha256Success) {
-    ASSERT_EQ(KM_ERROR_OK,
-              GenerateKey(AuthorizationSetBuilder().HmacKey(128).Digest(KM_DIGEST_SHA_2_256)));
+    ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+                                           .HmacKey(128)
+                                           .Digest(KM_DIGEST_SHA_2_256)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 256)));
     string message = "12345678901234567890123456789012";
     string signature;
-    MacMessage(message, &signature, KM_DIGEST_SHA_2_256, 256);
+    MacMessage(message, &signature, 256);
     ASSERT_EQ(32U, signature.size());
 
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
 }
 
 TEST_P(SigningOperationsTest, HmacSha384Success) {
-    ASSERT_EQ(KM_ERROR_OK,
-              GenerateKey(AuthorizationSetBuilder().HmacKey(128).Digest(KM_DIGEST_SHA_2_384)));
+    ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+                                           .HmacKey(128)
+                                           .Digest(KM_DIGEST_SHA_2_384)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 384)));
 
     string message = "12345678901234567890123456789012";
     string signature;
-    MacMessage(message, &signature, KM_DIGEST_SHA_2_384, 384);
+    MacMessage(message, &signature, 384);
     ASSERT_EQ(48U, signature.size());
 
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
 }
 
 TEST_P(SigningOperationsTest, HmacSha512Success) {
-    ASSERT_EQ(KM_ERROR_OK,
-              GenerateKey(AuthorizationSetBuilder().HmacKey(128).Digest(KM_DIGEST_SHA_2_512)));
-    string message = "12345678901234567890123456789012";
-    string signature;
-    MacMessage(message, &signature, KM_DIGEST_SHA_2_512, 512);
-    ASSERT_EQ(64U, signature.size());
-
-    EXPECT_EQ(0, GetParam()->keymaster0_calls());
-}
-
-TEST_P(SigningOperationsTest, HmacAnyDigestSuccess) {
-    ASSERT_EQ(KM_ERROR_OK,
-              GenerateKey(AuthorizationSetBuilder().HmacKey(128).Digest(KM_DIGEST_NONE)));
-    string message = "12345678901234567890123456789012";
-    string signature;
-
-    size_t len;
-    keymaster_digest_t* digests;
-    ASSERT_EQ(KM_ERROR_OK, device()->get_supported_digests(device(), KM_ALGORITHM_HMAC,
-                                                           KM_PURPOSE_SIGN, &digests, &len));
-    for (size_t i = 0; i < len; ++i)
-        MacMessage(message, &signature, digests[i], 128 /* small MAC to work with all digests */);
-    free(digests);
-
-    // Ensure that we can't actually try to do an HMAC with no digest
-    AuthorizationSet begin_params(client_params());
-    begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE);
-    begin_params.push_back(TAG_MAC_LENGTH, 128);
-    EXPECT_EQ(KM_ERROR_UNSUPPORTED_DIGEST, BeginOperation(KM_PURPOSE_SIGN, begin_params));
-
-    EXPECT_EQ(0, GetParam()->keymaster0_calls());
-}
-
-TEST_P(SigningOperationsTest, HmacLengthInKey) {
-    // TODO(swillden): unified API should generate an error on key generation.
     ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
                                            .HmacKey(128)
-                                           .Digest(KM_DIGEST_SHA_2_256)
-                                           .Authorization(TAG_MAC_LENGTH, 20)));
+                                           .Digest(KM_DIGEST_SHA_2_512)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 512)));
     string message = "12345678901234567890123456789012";
     string signature;
-    MacMessage(message, &signature, KM_DIGEST_SHA_2_256, 240);
-    // Size in key was ignored.
-    ASSERT_EQ(30U, signature.size());
+    MacMessage(message, &signature, 512);
+    ASSERT_EQ(64U, signature.size());
 
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
 }
@@ -1098,18 +1124,29 @@
 }
 
 TEST_P(SigningOperationsTest, HmacSha256TooLargeMacLength) {
-    ASSERT_EQ(KM_ERROR_OK,
-              GenerateKey(AuthorizationSetBuilder().HmacKey(128).Digest(KM_DIGEST_SHA_2_256)));
+    ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+                                           .HmacKey(128)
+                                           .Digest(KM_DIGEST_SHA_2_256)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 256)));
     AuthorizationSet begin_params(client_params());
     begin_params.push_back(TAG_MAC_LENGTH, 264);
     begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256);
-    ASSERT_EQ(KM_ERROR_OK,
+    ASSERT_EQ(KM_ERROR_UNSUPPORTED_MAC_LENGTH,
               BeginOperation(KM_PURPOSE_SIGN, begin_params, nullptr /* output_params */));
-    string message = "1234567890123456789012345678901";
-    string result;
-    size_t input_consumed;
-    ASSERT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
-    ASSERT_EQ(KM_ERROR_UNSUPPORTED_MAC_LENGTH, FinishOperation(&result));
+
+    EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, HmacSha256TooSmallMacLength) {
+    ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+                                           .HmacKey(128)
+                                           .Digest(KM_DIGEST_SHA_2_256)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+    AuthorizationSet begin_params(client_params());
+    begin_params.push_back(TAG_MAC_LENGTH, 120);
+    begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256);
+    ASSERT_EQ(KM_ERROR_INVALID_MAC_LENGTH,
+              BeginOperation(KM_PURPOSE_SIGN, begin_params, nullptr /* output_params */));
 
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
 }
@@ -1448,81 +1485,92 @@
 }
 
 TEST_P(VerificationOperationsTest, HmacSha1Success) {
-    GenerateKey(AuthorizationSetBuilder().HmacKey(128).Digest(KM_DIGEST_SHA1));
+    GenerateKey(AuthorizationSetBuilder()
+                    .HmacKey(128)
+                    .Digest(KM_DIGEST_SHA1)
+                    .Authorization(TAG_MIN_MAC_LENGTH, 128));
     string message = "123456789012345678901234567890123456789012345678";
     string signature;
-    MacMessage(message, &signature, KM_DIGEST_SHA1, 160);
-    VerifyMessage(message, signature, KM_DIGEST_SHA1);
+    MacMessage(message, &signature, 160);
+    VerifyMac(message, signature);
 
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
 }
 
 TEST_P(VerificationOperationsTest, HmacSha224Success) {
-    GenerateKey(AuthorizationSetBuilder().HmacKey(128).Digest(KM_DIGEST_SHA_2_224));
+    GenerateKey(AuthorizationSetBuilder()
+                    .HmacKey(128)
+                    .Digest(KM_DIGEST_SHA_2_224)
+                    .Authorization(TAG_MIN_MAC_LENGTH, 128));
     string message = "123456789012345678901234567890123456789012345678";
     string signature;
-    MacMessage(message, &signature, KM_DIGEST_SHA_2_224, 224);
-    VerifyMessage(message, signature, KM_DIGEST_SHA_2_224);
+    MacMessage(message, &signature, 224);
+    VerifyMac(message, signature);
 
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
 }
 
 TEST_P(VerificationOperationsTest, HmacSha256Success) {
-    GenerateKey(AuthorizationSetBuilder().HmacKey(128).Digest(KM_DIGEST_SHA_2_256));
+    GenerateKey(AuthorizationSetBuilder()
+                    .HmacKey(128)
+                    .Digest(KM_DIGEST_SHA_2_256)
+                    .Authorization(TAG_MIN_MAC_LENGTH, 128));
     string message = "123456789012345678901234567890123456789012345678";
     string signature;
-    MacMessage(message, &signature, KM_DIGEST_SHA_2_256, 256);
-    VerifyMessage(message, signature, KM_DIGEST_SHA_2_256);
+    MacMessage(message, &signature, 256);
+    VerifyMac(message, signature);
+
+    EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(VerificationOperationsTest, HmacSha256TooShortMac) {
+    GenerateKey(AuthorizationSetBuilder()
+                    .HmacKey(128)
+                    .Digest(KM_DIGEST_SHA_2_256)
+                    .Authorization(TAG_MIN_MAC_LENGTH, 128));
+    string message = "123456789012345678901234567890123456789012345678";
+    string signature;
+    MacMessage(message, &signature, 256);
+
+    // Shorten to 128 bits, should still work.
+    signature.resize(128 / 8);
+    VerifyMac(message, signature);
+
+    // Drop one more byte.
+    signature.resize(signature.length() - 1);
+
+    AuthorizationSet begin_params(client_params());
+    EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params));
+    string result;
+    size_t input_consumed;
+    EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+    EXPECT_EQ(KM_ERROR_INVALID_MAC_LENGTH, FinishOperation(signature, &result));
 
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
 }
 
 TEST_P(VerificationOperationsTest, HmacSha384Success) {
-    GenerateKey(AuthorizationSetBuilder().HmacKey(128).Digest(KM_DIGEST_SHA_2_384));
+    GenerateKey(AuthorizationSetBuilder()
+                    .HmacKey(128)
+                    .Digest(KM_DIGEST_SHA_2_384)
+                    .Authorization(TAG_MIN_MAC_LENGTH, 128));
     string message = "123456789012345678901234567890123456789012345678";
     string signature;
-    MacMessage(message, &signature, KM_DIGEST_SHA_2_384, 384);
-    VerifyMessage(message, signature, KM_DIGEST_SHA_2_384);
+    MacMessage(message, &signature, 384);
+    VerifyMac(message, signature);
 
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
 }
 
 TEST_P(VerificationOperationsTest, HmacSha512Success) {
-    GenerateKey(AuthorizationSetBuilder().HmacKey(128).Digest(KM_DIGEST_SHA_2_512));
+    GenerateKey(AuthorizationSetBuilder()
+                    .HmacKey(128)
+                    .Digest(KM_DIGEST_SHA_2_512)
+                    .Authorization(TAG_MIN_MAC_LENGTH, 128));
     string message = "123456789012345678901234567890123456789012345678";
     string signature;
-    MacMessage(message, &signature, KM_DIGEST_SHA_2_512, 512);
-    VerifyMessage(message, signature, KM_DIGEST_SHA_2_512);
-
-    EXPECT_EQ(0, GetParam()->keymaster0_calls());
-}
-
-TEST_P(VerificationOperationsTest, HmacAnyDigestSuccess) {
-    ASSERT_EQ(KM_ERROR_OK,
-              GenerateKey(AuthorizationSetBuilder().HmacKey(128).Digest(KM_DIGEST_NONE)));
-    string message = "12345678901234567890123456789012";
-    string signature;
-
-    size_t len;
-    keymaster_digest_t* digests;
-    ASSERT_EQ(KM_ERROR_OK, device()->get_supported_digests(device(), KM_ALGORITHM_HMAC,
-                                                           KM_PURPOSE_SIGN, &digests, &len));
-    for (size_t i = 0; i < len; ++i) {
-        MacMessage(message, &signature, digests[i], 128 /* small MAC to work with all digests */);
-        VerifyMessage(message, signature, digests[i]);
-        if (len > 1) {
-            size_t wrong_digest_index = (i == 0) ? 1 : 0;
-            AuthorizationSet begin_params(client_params());
-            begin_params.push_back(TAG_DIGEST, digests[wrong_digest_index]);
-            begin_params.push_back(TAG_MAC_LENGTH, 128);
-            EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params));
-            string output;
-            size_t input_consumed;
-            EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &output, &input_consumed));
-            EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(signature, &output));
-        }
-    }
-    free(digests);
+    MacMessage(message, &signature, 512);
+    VerifyMac(message, signature);
 
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
 }
@@ -1787,7 +1835,7 @@
     ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder()
                                          .HmacKey(sizeof(key_data) * 8)
                                          .Digest(KM_DIGEST_SHA_2_256)
-                                         .Authorization(TAG_MAC_LENGTH, 32),
+                                         .Authorization(TAG_MIN_MAC_LENGTH, 256),
                                      KM_KEY_FORMAT_RAW, key));
 
     EXPECT_TRUE(contains(sw_enforced(), TAG_ORIGIN, KM_ORIGIN_IMPORTED));
@@ -1795,7 +1843,7 @@
 
     string message = "Hello World!";
     string signature;
-    MacMessage(message, &signature, KM_DIGEST_SHA_2_256, 32);
+    MacMessage(message, &signature, 256);
     VerifyMessage(message, signature, KM_DIGEST_SHA_2_256);
 
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
@@ -2115,10 +2163,11 @@
 }
 
 TEST_P(EncryptionOperationsTest, HmacEncrypt) {
-    ASSERT_EQ(
-        KM_ERROR_OK,
-        GenerateKey(
-            AuthorizationSetBuilder().HmacKey(128).Digest(KM_DIGEST_NONE).Padding(KM_PAD_NONE)));
+    ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+                                           .HmacKey(128)
+                                           .Digest(KM_DIGEST_SHA_2_256)
+                                           .Padding(KM_PAD_NONE)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 128)));
     ASSERT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE, BeginOperation(KM_PURPOSE_ENCRYPT));
     ASSERT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE, BeginOperation(KM_PURPOSE_DECRYPT));
 
@@ -2552,7 +2601,8 @@
     ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
                                            .AesEncryptionKey(128)
                                            .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
-                                           .Authorization(TAG_PADDING, KM_PAD_NONE)));
+                                           .Authorization(TAG_PADDING, KM_PAD_NONE)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 128)));
     string aad = "foobar";
     string message = "123456789012345678901234567890123456";
     AuthorizationSet begin_params(client_params());
@@ -2590,6 +2640,70 @@
     EXPECT_EQ(0, GetParam()->keymaster0_calls());
 }
 
+TEST_P(EncryptionOperationsTest, AesGcmTooShortTag) {
+    ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+                                           .AesEncryptionKey(128)
+                                           .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+                                           .Authorization(TAG_PADDING, KM_PAD_NONE)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+    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, 96);
+
+    AuthorizationSet update_params;
+    update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+    AuthorizationSet begin_out_params;
+    EXPECT_EQ(KM_ERROR_INVALID_MAC_LENGTH,
+              BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+
+    EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmTooShortTagOnDecrypt) {
+    ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+                                           .AesEncryptionKey(128)
+                                           .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+                                           .Authorization(TAG_PADDING, KM_PAD_NONE)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+    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 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, 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.Reinitialize(client_params());
+    begin_params.push_back(begin_out_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, 96);
+
+    // Decrypt.
+    EXPECT_EQ(KM_ERROR_INVALID_MAC_LENGTH, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+
+    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,
@@ -2622,7 +2736,8 @@
                                          .AesEncryptionKey(128)
                                          .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
                                          .Authorization(TAG_PADDING, KM_PAD_NONE)
-                                         .Authorization(TAG_CALLER_NONCE),
+                                         .Authorization(TAG_CALLER_NONCE)
+                                         .Authorization(TAG_MIN_MAC_LENGTH, 128),
                                      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));
@@ -2637,7 +2752,8 @@
     ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder()
                                          .AesEncryptionKey(128)
                                          .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
-                                         .Authorization(TAG_PADDING, KM_PAD_NONE),
+                                         .Authorization(TAG_PADDING, KM_PAD_NONE)
+                                         .Authorization(TAG_MIN_MAC_LENGTH, 128),
                                      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));
@@ -2650,7 +2766,8 @@
     ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
                                            .AesEncryptionKey(128)
                                            .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
-                                           .Authorization(TAG_PADDING, KM_PAD_NONE)));
+                                           .Authorization(TAG_PADDING, KM_PAD_NONE)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 128)));
     string aad = "123456789012345678";
     string empty_message;
     AuthorizationSet begin_params(client_params());
@@ -2692,7 +2809,8 @@
     ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
                                            .AesEncryptionKey(128)
                                            .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
-                                           .Authorization(TAG_PADDING, KM_PAD_NONE)));
+                                           .Authorization(TAG_PADDING, KM_PAD_NONE)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 128)));
     AuthorizationSet begin_params(client_params());
     begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
     begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
@@ -2761,7 +2879,8 @@
     ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
                                            .AesEncryptionKey(128)
                                            .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
-                                           .Authorization(TAG_PADDING, KM_PAD_NONE)));
+                                           .Authorization(TAG_PADDING, KM_PAD_NONE)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 128)));
     string message = "123456789012345678901234567890123456";
     AuthorizationSet begin_params(client_params());
     begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
@@ -2811,7 +2930,8 @@
     ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
                                            .AesEncryptionKey(128)
                                            .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
-                                           .Authorization(TAG_PADDING, KM_PAD_NONE)));
+                                           .Authorization(TAG_PADDING, KM_PAD_NONE)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 128)));
     string message = "12345678901234567890123456789012";
     AuthorizationSet begin_params(client_params());
     begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
@@ -2857,7 +2977,8 @@
     ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
                                            .AesEncryptionKey(128)
                                            .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
-                                           .Authorization(TAG_PADDING, KM_PAD_NONE)));
+                                           .Authorization(TAG_PADDING, KM_PAD_NONE)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 128)));
     string message = "12345678901234567890123456789012";
     AuthorizationSet begin_params(client_params());
     begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
@@ -2897,7 +3018,8 @@
     ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
                                            .AesEncryptionKey(128)
                                            .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
-                                           .Authorization(TAG_PADDING, KM_PAD_NONE)));
+                                           .Authorization(TAG_PADDING, KM_PAD_NONE)
+                                           .Authorization(TAG_MIN_MAC_LENGTH, 128)));
     string aad = "foobar";
     string message = "123456789012345678901234567890123456";
     AuthorizationSet begin_params(client_params());
diff --git a/android_keymaster_test_utils.cpp b/android_keymaster_test_utils.cpp
index 36d856e..dacc413 100644
--- a/android_keymaster_test_utils.cpp
+++ b/android_keymaster_test_utils.cpp
@@ -383,12 +383,10 @@
     EXPECT_GT(signature->size(), 0U);
 }
 
-void Keymaster1Test::MacMessage(const string& message, string* signature, keymaster_digest_t digest,
-                                size_t mac_length) {
+void Keymaster1Test::MacMessage(const string& message, string* signature, size_t mac_length) {
     SCOPED_TRACE("SignMessage");
     AuthorizationSet input_params(AuthorizationSet(client_params_, array_length(client_params_)));
     input_params.push_back(TAG_MAC_LENGTH, mac_length);
-    input_params.push_back(TAG_DIGEST, digest);
     AuthorizationSet update_params;
     AuthorizationSet output_params;
     *signature =
@@ -419,6 +417,11 @@
                    &output_params);
 }
 
+void Keymaster1Test::VerifyMac(const string& message, const string& signature) {
+    SCOPED_TRACE("VerifyMac");
+    ProcessMessage(KM_PURPOSE_VERIFY, message, signature);
+}
+
 string Keymaster1Test::EncryptMessage(const string& message, keymaster_padding_t padding,
                                       string* generated_nonce) {
     SCOPED_TRACE("EncryptMessage");
@@ -577,11 +580,13 @@
 
 void Keymaster1Test::CheckHmacTestVector(string key, string message, keymaster_digest_t digest,
                                          string expected_mac) {
-    ASSERT_EQ(KM_ERROR_OK,
-              ImportKey(AuthorizationSetBuilder().HmacKey(key.size() * 8).Digest(digest),
-                        KM_KEY_FORMAT_RAW, key));
+    ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder()
+                                         .HmacKey(key.size() * 8)
+                                         .Authorization(TAG_MIN_MAC_LENGTH, expected_mac.size() * 8)
+                                         .Digest(digest),
+                                     KM_KEY_FORMAT_RAW, key));
     string signature;
-    MacMessage(message, &signature, digest, expected_mac.size() * 8);
+    MacMessage(message, &signature, expected_mac.size() * 8);
     EXPECT_EQ(expected_mac, signature) << "Test vector didn't match for digest " << (int)digest;
 }
 
diff --git a/android_keymaster_test_utils.h b/android_keymaster_test_utils.h
index c32460e..3d9e26a 100644
--- a/android_keymaster_test_utils.h
+++ b/android_keymaster_test_utils.h
@@ -221,13 +221,13 @@
     void SignMessage(const std::string& message, std::string* signature, keymaster_digest_t digest);
     void SignMessage(const std::string& message, std::string* signature, keymaster_digest_t digest,
                      keymaster_padding_t padding);
-    void MacMessage(const std::string& message, std::string* signature, keymaster_digest_t digest,
-                    size_t mac_length);
+    void MacMessage(const std::string& message, std::string* signature, size_t mac_length);
 
     void VerifyMessage(const std::string& message, const std::string& signature,
                        keymaster_digest_t digest);
     void VerifyMessage(const std::string& message, const std::string& signature,
                        keymaster_digest_t digest, keymaster_padding_t padding);
+    void VerifyMac(const std::string& message, const std::string& signature);
 
     std::string EncryptMessage(const std::string& message, keymaster_padding_t padding,
                                std::string* generated_nonce = NULL);
diff --git a/hmac_key.cpp b/hmac_key.cpp
index 2e814a6..91455e3 100644
--- a/hmac_key.cpp
+++ b/hmac_key.cpp
@@ -46,6 +46,13 @@
     if (!key)
         return KM_ERROR_OUTPUT_PARAMETER_NULL;
 
+    uint32_t min_mac_length;
+    if (!hw_enforced.GetTagValue(TAG_MIN_MAC_LENGTH, &min_mac_length) &&
+        !sw_enforced.GetTagValue(TAG_MIN_MAC_LENGTH, &min_mac_length)) {
+        LOG_E("HMAC key must have KM_TAG_MIN_MAC_LENGTH", 0);
+        return KM_ERROR_INVALID_KEY_BLOB;
+    }
+
     keymaster_error_t error;
     key->reset(new (std::nothrow) HmacKey(key_material, hw_enforced, sw_enforced, &error));
     if (!key->get())
@@ -53,4 +60,54 @@
     return error;
 }
 
+keymaster_error_t HmacKeyFactory::validate_algorithm_specific_new_key_params(
+    const AuthorizationSet& key_description) const {
+    uint32_t min_mac_length_bits;
+    if (!key_description.GetTagValue(TAG_MIN_MAC_LENGTH, &min_mac_length_bits))
+        return KM_ERROR_MISSING_MIN_MAC_LENGTH;
+
+    keymaster_digest_t digest;
+    if (!key_description.GetTagValue(TAG_DIGEST, &digest)) {
+        LOG_E("%d digests specified for HMAC key", key_description.GetTagCount(TAG_DIGEST));
+        return KM_ERROR_UNSUPPORTED_DIGEST;
+    }
+
+    size_t hash_size_bits = 0;
+    switch (digest) {
+    case KM_DIGEST_NONE:
+        return KM_ERROR_UNSUPPORTED_DIGEST;
+    case KM_DIGEST_MD5:
+        hash_size_bits = 128;
+        break;
+    case KM_DIGEST_SHA1:
+        hash_size_bits = 160;
+        break;
+    case KM_DIGEST_SHA_2_224:
+        hash_size_bits = 224;
+        break;
+    case KM_DIGEST_SHA_2_256:
+        hash_size_bits = 256;
+        break;
+    case KM_DIGEST_SHA_2_384:
+        hash_size_bits = 384;
+        break;
+    case KM_DIGEST_SHA_2_512:
+        hash_size_bits = 512;
+        break;
+    };
+
+    if (hash_size_bits == 0) {
+        // digest was not matched
+        return KM_ERROR_UNSUPPORTED_DIGEST;
+    }
+
+    if (min_mac_length_bits % 8 != 0 || min_mac_length_bits > hash_size_bits)
+        return KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH;
+
+    if (min_mac_length_bits < kMinHmacLengthBits)
+        return KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH;
+
+    return KM_ERROR_OK;
+}
+
 }  // namespace keymaster
diff --git a/hmac_key.h b/hmac_key.h
index eaae54e..c877a12 100644
--- a/hmac_key.h
+++ b/hmac_key.h
@@ -21,6 +21,8 @@
 
 namespace keymaster {
 
+const size_t kMinHmacLengthBits = 64;
+
 class HmacKeyFactory : public SymmetricKeyFactory {
   public:
     HmacKeyFactory(const KeymasterContext* context) : SymmetricKeyFactory(context) {}
@@ -37,6 +39,8 @@
         return key_size_bits > 0 && key_size_bits % 8 == 00 &&
                key_size_bits <= 2048 /* Some RFC test cases require >1024-bit keys */;
     }
+    keymaster_error_t validate_algorithm_specific_new_key_params(
+        const AuthorizationSet& key_description) const override;
 };
 
 class HmacKey : public SymmetricKey {
diff --git a/hmac_operation.cpp b/hmac_operation.cpp
index b1437c4..6b3117e 100644
--- a/hmac_operation.cpp
+++ b/hmac_operation.cpp
@@ -21,8 +21,8 @@
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
 
+#include "hmac_key.h"
 #include "openssl_err.h"
-#include "symmetric_key.h"
 
 #if defined(OPENSSL_IS_BORINGSSL)
 #include <openssl/mem.h>
@@ -36,22 +36,39 @@
 Operation* HmacOperationFactory::CreateOperation(const Key& key,
                                                  const AuthorizationSet& begin_params,
                                                  keymaster_error_t* error) {
-    uint32_t tag_length = 0;
-    begin_params.GetTagValue(TAG_MAC_LENGTH, &tag_length);
-    if (tag_length % 8 != 0) {
-        LOG_E("MAC length %d nod a multiple of 8 bits", tag_length);
-        *error = KM_ERROR_UNSUPPORTED_MAC_LENGTH;
+    uint32_t min_mac_length_bits;
+    if (!key.authorizations().GetTagValue(TAG_MIN_MAC_LENGTH, &min_mac_length_bits)) {
+        LOG_E("HMAC key must have KM_TAG_MIN_MAC_LENGTH", 0);
+        *error = KM_ERROR_INVALID_KEY_BLOB;
         return nullptr;
     }
 
+    uint32_t mac_length_bits = UINT32_MAX;
+    if (begin_params.GetTagValue(TAG_MAC_LENGTH, &mac_length_bits)) {
+        if (purpose() == KM_PURPOSE_VERIFY) {
+            LOG_E("MAC length may not be specified for verify", 0);
+            *error = KM_ERROR_INVALID_ARGUMENT;
+            return nullptr;
+        }
+    } else {
+        if (purpose() == KM_PURPOSE_SIGN) {
+            *error = KM_ERROR_MISSING_MAC_LENGTH;
+            return nullptr;
+        }
+    }
+
     keymaster_digest_t digest;
-    if (!GetAndValidateDigest(begin_params, key, &digest, error))
+    if (!key.authorizations().GetTagValue(TAG_DIGEST, &digest)) {
+        LOG_E("%d digests found in HMAC key authorizations; must be exactly 1",
+              begin_params.GetTagCount(TAG_DIGEST));
+        *error = KM_ERROR_INVALID_KEY_BLOB;
         return nullptr;
+    }
 
     const SymmetricKey* symmetric_key = static_cast<const SymmetricKey*>(&key);
-    UniquePtr<HmacOperation> op(
-        new (std::nothrow) HmacOperation(purpose(), symmetric_key->key_data(),
-                                         symmetric_key->key_data_size(), digest, tag_length / 8));
+    UniquePtr<HmacOperation> op(new (std::nothrow) HmacOperation(
+        purpose(), symmetric_key->key_data(), symmetric_key->key_data_size(), digest,
+        mac_length_bits / 8, min_mac_length_bits / 8));
     if (!op.get())
         *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
     else
@@ -72,8 +89,10 @@
 }
 
 HmacOperation::HmacOperation(keymaster_purpose_t purpose, const uint8_t* key_data,
-                             size_t key_data_size, keymaster_digest_t digest, size_t tag_length)
-    : Operation(purpose), error_(KM_ERROR_OK), tag_length_(tag_length) {
+                             size_t key_data_size, keymaster_digest_t digest, size_t mac_length,
+                             size_t min_mac_length)
+    : Operation(purpose), error_(KM_ERROR_OK), mac_length_(mac_length),
+      min_mac_length_(min_mac_length) {
     // Initialize CTX first, so dtor won't crash even if we error out later.
     HMAC_CTX_init(&ctx_);
 
@@ -105,6 +124,17 @@
         return;
     }
 
+    if (purpose == KM_PURPOSE_SIGN) {
+        if (mac_length > EVP_MD_size(md) || mac_length < kMinHmacLengthBits / 8) {
+            error_ = KM_ERROR_UNSUPPORTED_MAC_LENGTH;
+            return;
+        }
+        if (mac_length < min_mac_length) {
+            error_ = KM_ERROR_INVALID_MAC_LENGTH;
+            return;
+        }
+    }
+
     HMAC_Init_ex(&ctx_, key_data, key_data_size, md, NULL /* engine */);
 }
 
@@ -140,17 +170,21 @@
 
     switch (purpose()) {
     case KM_PURPOSE_SIGN:
-        if (tag_length_ > digest_len)
+        if (mac_length_ > digest_len)
             return KM_ERROR_UNSUPPORTED_MAC_LENGTH;
-        if (!output->reserve(tag_length_) || !output->write(digest, tag_length_))
+        if (!output->reserve(mac_length_) || !output->write(digest, mac_length_))
             return KM_ERROR_MEMORY_ALLOCATION_FAILED;
         return KM_ERROR_OK;
-    case KM_PURPOSE_VERIFY:
-        if (signature.available_read() > digest_len)
-            return KM_ERROR_INVALID_INPUT_LENGTH;
-        if (CRYPTO_memcmp(signature.peek_read(), digest, signature.available_read()) != 0)
+    case KM_PURPOSE_VERIFY: {
+        size_t siglen = signature.available_read();
+        if (siglen > digest_len || siglen < kMinHmacLengthBits / 8)
+            return KM_ERROR_UNSUPPORTED_MAC_LENGTH;
+        if (siglen < min_mac_length_)
+            return KM_ERROR_INVALID_MAC_LENGTH;
+        if (CRYPTO_memcmp(signature.peek_read(), digest, siglen) != 0)
             return KM_ERROR_VERIFICATION_FAILED;
         return KM_ERROR_OK;
+    }
     default:
         return KM_ERROR_UNSUPPORTED_PURPOSE;
     }
diff --git a/hmac_operation.h b/hmac_operation.h
index ad59a16..9c2d59b 100644
--- a/hmac_operation.h
+++ b/hmac_operation.h
@@ -26,7 +26,7 @@
 class HmacOperation : public Operation {
   public:
     HmacOperation(keymaster_purpose_t purpose, const uint8_t* key_data, size_t key_data_size,
-                  keymaster_digest_t digest, size_t tag_length);
+                  keymaster_digest_t digest, size_t mac_length, size_t min_mac_length);
     ~HmacOperation();
 
     virtual keymaster_error_t Begin(const AuthorizationSet& input_params,
@@ -44,7 +44,8 @@
   private:
     HMAC_CTX ctx_;
     keymaster_error_t error_;
-    size_t tag_length_;
+    const size_t mac_length_;
+    const size_t min_mac_length_;
 };
 
 /**
diff --git a/include/keymaster/keymaster_tags.h b/include/keymaster/keymaster_tags.h
index e62c2cc..21d6966 100644
--- a/include/keymaster/keymaster_tags.h
+++ b/include/keymaster/keymaster_tags.h
@@ -162,6 +162,7 @@
 DEFINE_KEYMASTER_TAG(KM_UINT, TAG_KEY_SIZE);
 DEFINE_KEYMASTER_TAG(KM_UINT, TAG_MAC_LENGTH);
 DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_CALLER_NONCE);
+DEFINE_KEYMASTER_TAG(KM_UINT, TAG_MIN_MAC_LENGTH);
 DEFINE_KEYMASTER_TAG(KM_ULONG, TAG_RSA_PUBLIC_EXPONENT);
 DEFINE_KEYMASTER_TAG(KM_DATE, TAG_ACTIVE_DATETIME);
 DEFINE_KEYMASTER_TAG(KM_DATE, TAG_ORIGINATION_EXPIRE_DATETIME);
diff --git a/keymaster_enforcement.cpp b/keymaster_enforcement.cpp
index 9752438..4d0aaef 100644
--- a/keymaster_enforcement.cpp
+++ b/keymaster_enforcement.cpp
@@ -286,7 +286,7 @@
         case KM_TAG_APPLICATION_DATA:
             return KM_ERROR_INVALID_KEY_BLOB;
 
-        /* Tags used for cryptographic parameters. */
+        /* Tags used for cryptographic parameters in keygen.  Nothing to enforce. */
         case KM_TAG_PURPOSE:
         case KM_TAG_ALGORITHM:
         case KM_TAG_KEY_SIZE:
@@ -295,6 +295,7 @@
         case KM_TAG_MAC_LENGTH:
         case KM_TAG_PADDING:
         case KM_TAG_NONCE:
+        case KM_TAG_MIN_MAC_LENGTH:
 
         /* Tags not used for operations. */
         case KM_TAG_BLOB_USAGE_REQUIREMENTS:
diff --git a/symmetric_key.cpp b/symmetric_key.cpp
index 3d8424c..1d51e3a 100644
--- a/symmetric_key.cpp
+++ b/symmetric_key.cpp
@@ -43,12 +43,16 @@
         !key_size_supported(key_size_bits))
         return KM_ERROR_UNSUPPORTED_KEY_SIZE;
 
+    keymaster_error_t error = validate_algorithm_specific_new_key_params(key_description);
+    if (error != KM_ERROR_OK)
+        return error;
+
     size_t key_data_size = key_size_bits / 8;
     KeymasterKeyBlob key_material(key_data_size);
     if (!key_material.key_material)
         return KM_ERROR_MEMORY_ALLOCATION_FAILED;
 
-    keymaster_error_t error = context_->GenerateRandom(key_material.writable_data(), key_data_size);
+    error = context_->GenerateRandom(key_material.writable_data(), key_data_size);
     if (error != KM_ERROR_OK) {
         LOG_E("Error generating %d bit symmetric key", key_size_bits);
         return error;
@@ -76,6 +80,10 @@
         authorizations.push_back(TAG_KEY_SIZE, key_size_bits);
     }
 
+    keymaster_error_t error = validate_algorithm_specific_new_key_params(key_description);
+    if (error != KM_ERROR_OK)
+        return error;
+
     if (!key_size_supported(key_size_bits))
         return KM_ERROR_UNSUPPORTED_KEY_SIZE;
 
diff --git a/symmetric_key.h b/symmetric_key.h
index fd61b37..47f3329 100644
--- a/symmetric_key.h
+++ b/symmetric_key.h
@@ -45,6 +45,8 @@
 
   private:
     virtual bool key_size_supported(size_t key_size_bits) const = 0;
+    virtual keymaster_error_t
+    validate_algorithm_specific_new_key_params(const AuthorizationSet& key_description) const = 0;
 
     const keymaster_key_format_t* NoFormats(size_t* format_count) const {
         *format_count = 0;