Add HMAC-SHA256 support.

Change-Id: I64c7bdf77388e3cb491b702c52c6746d32f317b0
diff --git a/Android.mk b/Android.mk
index 3c5fab1..78aed7e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -57,6 +57,8 @@
 		google_keymaster.cpp \
 		google_keymaster_messages.cpp \
 		google_keymaster_utils.cpp \
+		hmac_key.cpp \
+		hmac_operation.cpp \
 		key.cpp \
 		key_blob.cpp \
 		ocb.c \
diff --git a/Makefile b/Makefile
index 6f675d3..b06e51d 100644
--- a/Makefile
+++ b/Makefile
@@ -39,6 +39,8 @@
 	google_keymaster_test.cpp \
 	google_keymaster_test_utils.cpp \
 	google_keymaster_utils.cpp \
+	hmac_key.cpp \
+	hmac_operation.cpp \
 	key.cpp \
 	key_blob.cpp \
 	key_blob_test.cpp \
@@ -139,6 +141,8 @@
 	google_keymaster_messages.o \
 	google_keymaster_test_utils.o \
 	google_keymaster_utils.o \
+	hmac_key.o \
+	hmac_operation.o \
 	key.o \
 	key_blob.o \
 	ocb.o \
diff --git a/google_keymaster_test.cpp b/google_keymaster_test.cpp
index 596aeed..1ee9c02 100644
--- a/google_keymaster_test.cpp
+++ b/google_keymaster_test.cpp
@@ -85,6 +85,14 @@
 
     keymaster_device* device() { return reinterpret_cast<keymaster_device*>(device_.hw_device()); }
 
+    void GenerateKey(AuthorizationSet* params) {
+        FreeKeyBlob();
+        FreeCharacteristics();
+        AddClientParams(params);
+        EXPECT_EQ(KM_ERROR_OK, device()->generate_key(device(), params->data(), params->size(),
+                                                      &blob_, &characteristics_));
+    }
+
     keymaster_error_t BeginOperation(keymaster_purpose_t purpose,
                                      const keymaster_key_blob_t& key_blob) {
         return device()->begin(device(), purpose, &key_blob, client_params_,
@@ -133,6 +141,8 @@
         blob_.key_material = NULL;
     }
 
+    void AddClientParams(AuthorizationSet* set) { set->push_back(TAG_APPLICATION_ID, "app_id", 6); }
+
     const keymaster_key_blob_t& key_blob() { return blob_; }
 
     SoftKeymasterDevice device_;
@@ -488,7 +498,6 @@
         Authorization(TAG_MAC_LENGTH, 16), Authorization(TAG_PADDING, KM_PAD_NONE),
     };
     params_.Reinitialize(params, array_length(params));
-    params_.Reinitialize(params, array_length(params));
     EXPECT_EQ(KM_ERROR_OK, device()->generate_key(device(), params_.data(), params_.size(), &blob_,
                                                   &characteristics_));
 
@@ -531,6 +540,16 @@
     }
 }
 
+TEST_F(NewKeyGeneration, HmacSha256) {
+    keymaster_key_param_t params[] = {
+        Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+        Authorization(TAG_ALGORITHM, KM_ALGORITHM_HMAC), Authorization(TAG_KEY_SIZE, 128),
+        Authorization(TAG_MAC_LENGTH, 16), Authorization(TAG_DIGEST, KM_DIGEST_SHA_2_256),
+    };
+    EXPECT_EQ(KM_ERROR_OK, device()->generate_key(device(), params, array_length(params), &blob_,
+                                                  &characteristics_));
+}
+
 typedef KeymasterTest GetKeyCharacteristics;
 TEST_F(GetKeyCharacteristics, SimpleRsa) {
     keymaster_key_param_t params[] = {
@@ -564,6 +583,9 @@
         FreeSignature();
     }
 
+    // TODO(swillden): Refactor and move common test utils to KeymasterTest
+    using KeymasterTest::GenerateKey;
+
     void GenerateKey(keymaster_algorithm_t algorithm, keymaster_digest_t digest,
                      keymaster_padding_t padding, uint32_t key_size) {
         params_.push_back(Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN));
@@ -602,6 +624,8 @@
         signature_ = NULL;
     }
 
+    const keymaster_key_blob_t& key_blob() { return blob_; }
+
     void corrupt_key_blob() {
         uint8_t* tmp = const_cast<uint8_t*>(blob_.key_material);
         ++tmp[blob_.key_material_size / 2];
@@ -669,6 +693,54 @@
     EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, device()->abort(device(), op_handle_));
 }
 
+TEST_F(SigningOperationsTest, HmacSha256Success) {
+    keymaster_key_param_t params[] = {
+        Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+        Authorization(TAG_ALGORITHM, KM_ALGORITHM_HMAC), Authorization(TAG_KEY_SIZE, 128),
+        Authorization(TAG_MAC_LENGTH, 32), Authorization(TAG_DIGEST, KM_DIGEST_SHA_2_256),
+        Authorization(TAG_USER_ID, 7), Authorization(TAG_USER_AUTH_ID, 8),
+        Authorization(TAG_AUTH_TIMEOUT, 300),
+    };
+    params_.Reinitialize(params, array_length(params));
+    GenerateKey(&params_);
+    const char message[] = "12345678901234567890123456789012";
+    string signature;
+    SignMessage(message, array_size(message) - 1);
+    ASSERT_EQ(32, signature_length_);
+}
+
+// TODO(swillden): Add an HMACSHA256 test that validates against the test vectors from RFC4231.
+//                 Doing that requires being able to import keys, rather than just generate them
+//                 randomly.
+
+TEST_F(SigningOperationsTest, HmacSha256NoTag) {
+    keymaster_key_param_t params[] = {
+        Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+        Authorization(TAG_ALGORITHM, KM_ALGORITHM_HMAC), Authorization(TAG_KEY_SIZE, 128),
+        Authorization(TAG_MAC_LENGTH, 16), Authorization(TAG_DIGEST, KM_DIGEST_SHA_2_256),
+        Authorization(TAG_USER_ID, 7), Authorization(TAG_USER_AUTH_ID, 8),
+        Authorization(TAG_AUTH_TIMEOUT, 300),
+    };
+    params_.Reinitialize(params, array_length(params));
+    GenerateKey(&params_);
+    const char message[] = "12345678901234567890123456789012";
+    string signature;
+    SignMessage(message, array_size(message) - 1);
+}
+
+TEST_F(SigningOperationsTest, HmacSha256TooLargeTag) {
+    keymaster_key_param_t params[] = {
+        Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+        Authorization(TAG_ALGORITHM, KM_ALGORITHM_HMAC), Authorization(TAG_KEY_SIZE, 128),
+        Authorization(TAG_MAC_LENGTH, 33), Authorization(TAG_DIGEST, KM_DIGEST_SHA_2_256),
+        Authorization(TAG_USER_ID, 7), Authorization(TAG_USER_AUTH_ID, 8),
+        Authorization(TAG_AUTH_TIMEOUT, 300),
+    };
+    params_.Reinitialize(params, array_length(params));
+    GenerateKey(&params_);
+    ASSERT_EQ(KM_ERROR_UNSUPPORTED_MAC_LENGTH, BeginOperation(KM_PURPOSE_SIGN, key_blob()));
+}
+
 TEST_F(SigningOperationsTest, RsaTooShortMessage) {
     GenerateKey(KM_ALGORITHM_RSA, KM_DIGEST_NONE, KM_PAD_NONE, 256 /* key size */);
     ASSERT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_SIGN, key_blob()));
@@ -718,6 +790,22 @@
     VerifyMessage(message, array_size(message) - 1);
 }
 
+TEST_F(VerificationOperationsTest, HmacSha256Success) {
+    keymaster_key_param_t params[] = {
+        Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+        Authorization(TAG_ALGORITHM, KM_ALGORITHM_HMAC), Authorization(TAG_KEY_SIZE, 128),
+        Authorization(TAG_MAC_LENGTH, 16), Authorization(TAG_DIGEST, KM_DIGEST_SHA_2_256),
+        Authorization(TAG_USER_ID, 7), Authorization(TAG_USER_AUTH_ID, 8),
+        Authorization(TAG_AUTH_TIMEOUT, 300),
+    };
+    params_.Reinitialize(params, array_length(params));
+    GenerateKey(&params_);
+    const char message[] = "123456789012345678901234567890123456789012345678";
+    string signature;
+    SignMessage(message, array_size(message) - 1);
+    VerifyMessage(message, array_size(message) - 1);
+}
+
 typedef VerificationOperationsTest ExportKeyTest;
 TEST_F(ExportKeyTest, RsaSuccess) {
     GenerateKey(KM_ALGORITHM_RSA, KM_DIGEST_NONE, KM_PAD_NONE, 256 /* key size */);
@@ -900,13 +988,8 @@
  */
 class EncryptionOperationsTest : public KeymasterTest {
   protected:
-    void GenerateKey(AuthorizationSet* params) {
-        FreeKeyBlob();
-        FreeCharacteristics();
-        AddClientParams(params);
-        EXPECT_EQ(KM_ERROR_OK, device()->generate_key(device(), params->data(), params->size(),
-                                                      &blob_, &characteristics_));
-    }
+    // TODO(swillden): Refactor and move common test utils to KeymasterTest
+    using KeymasterTest::GenerateKey;
 
     void GenerateKey(keymaster_algorithm_t algorithm, keymaster_padding_t padding,
                      uint32_t key_size) {
@@ -962,8 +1045,6 @@
         return ProcessMessage(KM_PURPOSE_DECRYPT, blob_, ciphertext.c_str(), ciphertext.length());
     }
 
-    void AddClientParams(AuthorizationSet* set) { set->push_back(TAG_APPLICATION_ID, "app_id", 6); }
-
     const void corrupt_key_blob() {
         uint8_t* tmp = const_cast<uint8_t*>(blob_.key_material);
         ++tmp[blob_.key_material_size / 2];
diff --git a/hmac_key.cpp b/hmac_key.cpp
new file mode 100644
index 0000000..40ecfe6
--- /dev/null
+++ b/hmac_key.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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 "hmac_key.h"
+
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+#include "hmac_operation.h"
+
+namespace keymaster {
+
+Operation* HmacKey::CreateOperation(keymaster_purpose_t purpose, keymaster_error_t* error) {
+    *error = KM_ERROR_OK;
+
+    uint32_t tag_length;
+    if (!authorizations().GetTagValue(TAG_MAC_LENGTH, &tag_length))
+        *error = KM_ERROR_UNSUPPORTED_MAC_LENGTH;
+
+    keymaster_digest_t digest;
+    if (!authorizations().GetTagValue(TAG_DIGEST, &digest) || digest != KM_DIGEST_SHA_2_256)
+        *error = KM_ERROR_UNSUPPORTED_DIGEST;
+
+    if (*error != KM_ERROR_OK)
+        return NULL;
+
+    UniquePtr<Operation> op;
+    switch (purpose) {
+    case KM_PURPOSE_SIGN:
+    case KM_PURPOSE_VERIFY:
+        op.reset(
+            new HmacOperation(purpose, logger_, key_data(), key_data_size(), digest, tag_length));
+        break;
+    default:
+        *error = KM_ERROR_UNSUPPORTED_PURPOSE;
+        return NULL;
+    }
+
+    if (!op.get())
+        *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+    return op.release();
+}
+
+}  // namespace keymaster
diff --git a/hmac_key.h b/hmac_key.h
new file mode 100644
index 0000000..d60c848
--- /dev/null
+++ b/hmac_key.h
@@ -0,0 +1,35 @@
+/*
+ * 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_HMAC_KEY_H_
+#define SYSTEM_KEYMASTER_HMAC_KEY_H_
+
+#include "symmetric_key.h"
+
+namespace keymaster {
+
+class HmacKey : public SymmetricKey {
+  public:
+    HmacKey(const AuthorizationSet& auths, const Logger& logger) : SymmetricKey(auths, logger) {}
+    HmacKey(const UnencryptedKeyBlob& blob, const Logger& logger, keymaster_error_t* error)
+        : SymmetricKey(blob, logger, error) {}
+
+    virtual Operation* CreateOperation(keymaster_purpose_t purpose, keymaster_error_t* error);
+};
+
+}  // namespace keymaster
+
+#endif  // SYSTEM_KEYMASTER_HMAC_KEY_H_
diff --git a/hmac_operation.cpp b/hmac_operation.cpp
new file mode 100644
index 0000000..1620105
--- /dev/null
+++ b/hmac_operation.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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 "hmac_operation.h"
+
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+
+namespace keymaster {
+
+HmacOperation::HmacOperation(keymaster_purpose_t purpose, const Logger& logger,
+                             const uint8_t* key_data, size_t key_data_size,
+                             keymaster_digest_t digest, size_t tag_length)
+    : Operation(purpose, logger), error_(KM_ERROR_OK), tag_length_(tag_length) {
+    // Initialize CTX first, so dtor won't crash even if we error out later.
+    HMAC_CTX_init(&ctx_);
+
+    const EVP_MD* md;
+    switch (digest) {
+    case KM_DIGEST_SHA_2_256:
+        md = EVP_sha256();
+        break;
+    default:
+        error_ = KM_ERROR_UNSUPPORTED_DIGEST;
+        return;
+    }
+
+    if ((int)tag_length_ > EVP_MD_size(md)) {
+        error_ = KM_ERROR_UNSUPPORTED_MAC_LENGTH;
+        return;
+    }
+
+    HMAC_Init_ex(&ctx_, key_data, key_data_size, md, NULL /* engine */);
+}
+
+HmacOperation::~HmacOperation() {
+    HMAC_CTX_cleanup(&ctx_);
+}
+
+keymaster_error_t HmacOperation::Begin() {
+    return error_;
+}
+
+keymaster_error_t HmacOperation::Update(const Buffer& input, Buffer* /* output */,
+                                        size_t* input_consumed) {
+    if (!HMAC_Update(&ctx_, input.peek_read(), input.available_read()))
+        return KM_ERROR_UNKNOWN_ERROR;
+    *input_consumed = input.available_read();
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t HmacOperation::Abort() {
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t HmacOperation::Finish(const Buffer& signature, Buffer* output) {
+    uint8_t digest[EVP_MAX_MD_SIZE];
+    unsigned int digest_len;
+    if (!HMAC_Final(&ctx_, digest, &digest_len))
+        return KM_ERROR_UNKNOWN_ERROR;
+
+    switch (purpose()) {
+    case KM_PURPOSE_SIGN:
+        output->reserve(tag_length_);
+        output->write(digest, tag_length_);
+        return KM_ERROR_OK;
+    case KM_PURPOSE_VERIFY:
+        if (signature.available_read() != tag_length_)
+            return KM_ERROR_INVALID_INPUT_LENGTH;
+        if (memcmp(signature.peek_read(), digest, tag_length_) != 0)
+            return KM_ERROR_VERIFICATION_FAILED;
+        return KM_ERROR_OK;
+    default:
+        return KM_ERROR_UNSUPPORTED_PURPOSE;
+    }
+}
+
+}  // namespace keymaster
diff --git a/hmac_operation.h b/hmac_operation.h
new file mode 100644
index 0000000..9299b82
--- /dev/null
+++ b/hmac_operation.h
@@ -0,0 +1,45 @@
+/*
+ * 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_HMAC_OPERATION_H_
+#define SYSTEM_KEYMASTER_HMAC_OPERATION_H_
+
+#include "operation.h"
+
+#include <openssl/hmac.h>
+
+namespace keymaster {
+
+class HmacOperation : public Operation {
+  public:
+    HmacOperation(keymaster_purpose_t purpose, const Logger& logger, const uint8_t* key_data,
+                  size_t key_data_size, keymaster_digest_t digest, size_t tag_length);
+    ~HmacOperation();
+
+    virtual keymaster_error_t Begin();
+    virtual keymaster_error_t Update(const Buffer& input, Buffer* output, size_t* input_consumed);
+    virtual keymaster_error_t Abort();
+    virtual keymaster_error_t Finish(const Buffer& signature, Buffer* output);
+
+  private:
+    HMAC_CTX ctx_;
+    keymaster_error_t error_;
+    const size_t tag_length_;
+};
+
+}  // namespace keymaster
+
+#endif  // SYSTEM_KEYMASTER_HMAC_OPERATION_H_
diff --git a/key.cpp b/key.cpp
index a5a0457..053c2d5 100644
--- a/key.cpp
+++ b/key.cpp
@@ -44,6 +44,7 @@
     case KM_ALGORITHM_ECDSA:
         return new EcdsaKey(blob, logger, error);
     case KM_ALGORITHM_AES:
+    case KM_ALGORITHM_HMAC:
         return SymmetricKey::CreateKey(blob.algorithm(), blob, logger, error);
     default:
         *error = KM_ERROR_UNSUPPORTED_ALGORITHM;
@@ -66,6 +67,7 @@
     case KM_ALGORITHM_ECDSA:
         return EcdsaKey::GenerateKey(key_description, logger, error);
     case KM_ALGORITHM_AES:
+    case KM_ALGORITHM_HMAC:
         return SymmetricKey::GenerateKey(algorithm, key_description, logger, error);
     default:
         *error = KM_ERROR_UNSUPPORTED_ALGORITHM;
diff --git a/symmetric_key.cpp b/symmetric_key.cpp
index 5105c7d..9678f9d 100644
--- a/symmetric_key.cpp
+++ b/symmetric_key.cpp
@@ -22,6 +22,7 @@
 #include <openssl/rand.h>
 
 #include "aes_key.h"
+#include "hmac_key.h"
 #include "unencrypted_key_blob.h"
 
 namespace keymaster {
@@ -40,6 +41,9 @@
     case KM_ALGORITHM_AES:
         key.reset(new AesKey(key_description, logger));
         break;
+    case KM_ALGORITHM_HMAC:
+        key.reset(new HmacKey(key_description, logger));
+        break;
     default:
         *error = KM_ERROR_UNSUPPORTED_ALGORITHM;
         return NULL;
@@ -74,6 +78,8 @@
     switch (algorithm) {
     case KM_ALGORITHM_AES:
         return new AesKey(blob, logger, error);
+    case KM_ALGORITHM_HMAC:
+        return new HmacKey(blob, logger, error);
     default:
         *error = KM_ERROR_UNSUPPORTED_ALGORITHM;
         return NULL;