hmac: Add HMAC to 2lib library
authorDaisuke Nojiri <dnojiri@chromium.org>
Fri, 6 May 2016 00:21:29 +0000 (17:21 -0700)
committerchrome-bot <chrome-bot@chromium.org>
Wed, 11 May 2016 02:41:46 +0000 (19:41 -0700)
This patch adds HMAC. HMAC will be used to sign/verify NVM structures.
Hash algorithms can be selected from those supported
by enum vb2_hash_algorithm (i.e. SHA1, SHA256, or SHA512).

BUG=chrome-os-partner:51907
BRANCH=tot
TEST=make runtests

Change-Id: I6d349bc807874fe2a5512aabcd7fbf67a4eaa40a
Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/342880
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Makefile
firmware/2lib/2hmac.c [new file with mode: 0644]
firmware/2lib/2sha_utility.c
firmware/2lib/include/2crypto.h
firmware/2lib/include/2hmac.h [new file with mode: 0644]
firmware/2lib/include/2sha.h
tests/hmac_test.c [new file with mode: 0644]
tests/vb2_sha_tests.c

index 900214f..8989147 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -356,7 +356,8 @@ FWLIB2X_SRCS = \
        firmware/2lib/2sha256.c \
        firmware/2lib/2sha512.c \
        firmware/2lib/2sha_utility.c \
-       firmware/2lib/2tpm_bootmode.c
+       firmware/2lib/2tpm_bootmode.c \
+       firmware/2lib/2hmac.c
 
 FWLIB20_SRCS = \
        firmware/lib20/api.c \
@@ -757,7 +758,8 @@ TEST2X_NAMES = \
        tests/vb2_rsa_utility_tests \
        tests/vb2_secdata_tests \
        tests/vb2_secdatak_tests \
-       tests/vb2_sha_tests
+       tests/vb2_sha_tests \
+       tests/hmac_test
 
 TEST20_NAMES = \
        tests/vb20_api_tests \
@@ -1263,6 +1265,7 @@ ${BUILD}/tests/vb20_common3_tests: LDLIBS += ${CRYPTO_LIBS}
 ${BUILD}/tests/verify_kernel: LDLIBS += ${CRYPTO_LIBS}
 ${BUILD}/tests/bdb_test: LDLIBS += ${CRYPTO_LIBS}
 ${BUILD}/tests/bdb_sprw_test: LDLIBS += ${CRYPTO_LIBS}
+${BUILD}/tests/hmac_test: LDLIBS += ${CRYPTO_LIBS}
 
 ${TEST21_BINS}: LDLIBS += ${CRYPTO_LIBS}
 
@@ -1464,6 +1467,7 @@ run2tests: test_setup
        ${RUNTEST} ${BUILD_RUN}/tests/vb21_host_keyblock_tests ${TEST_KEYS}
        ${RUNTEST} ${BUILD_RUN}/tests/vb21_host_misc_tests ${BUILD}
        ${RUNTEST} ${BUILD_RUN}/tests/vb21_host_sig_tests ${TEST_KEYS}
+       ${RUNTEST} ${BUILD_RUN}/tests/hmac_test
 
 .PHONY: runbdbtests
 runbdbtests: test_setup
diff --git a/firmware/2lib/2hmac.c b/firmware/2lib/2hmac.c
new file mode 100644 (file)
index 0000000..651ae57
--- /dev/null
@@ -0,0 +1,60 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "2sysincludes.h"
+#include "2sha.h"
+#include "2hmac.h"
+
+int hmac(enum vb2_hash_algorithm alg,
+        const void *key, uint32_t key_size,
+        const void *msg, uint32_t msg_size,
+        uint8_t *mac, uint32_t mac_size)
+{
+       uint32_t block_size;
+       uint32_t digest_size;
+       uint8_t k[VB2_MAX_BLOCK_SIZE];
+       uint8_t o_pad[VB2_MAX_BLOCK_SIZE];
+       uint8_t i_pad[VB2_MAX_BLOCK_SIZE];
+       uint8_t b[VB2_MAX_DIGEST_SIZE];
+       struct vb2_digest_context dc;
+       int i;
+
+       if (!key | !msg | !mac)
+               return -1;
+
+       digest_size = vb2_digest_size(alg);
+       block_size = vb2_hash_block_size(alg);
+       if (!digest_size || !block_size)
+               return -1;
+
+       if (mac_size < digest_size)
+               return -1;
+
+       if (key_size > block_size) {
+               vb2_digest_buffer((uint8_t *)key, key_size, alg, k, block_size);
+               key_size = digest_size;
+       } else {
+               memcpy(k, key, key_size);
+       }
+       if (key_size < block_size)
+               memset(k + key_size, 0, block_size - key_size);
+
+       for (i = 0; i < block_size; i++) {
+               o_pad[i] = 0x5c ^ k[i];
+               i_pad[i] = 0x36 ^ k[i];
+       }
+
+       vb2_digest_init(&dc, alg);
+       vb2_digest_extend(&dc, i_pad, block_size);
+       vb2_digest_extend(&dc, msg, msg_size);
+       vb2_digest_finalize(&dc, b, digest_size);
+
+       vb2_digest_init(&dc, alg);
+       vb2_digest_extend(&dc, o_pad, block_size);
+       vb2_digest_extend(&dc, b, digest_size);
+       vb2_digest_finalize(&dc, mac, mac_size);
+
+       return 0;
+}
index b75f0e5..dd74f29 100644 (file)
@@ -70,6 +70,46 @@ int vb2_digest_size(enum vb2_hash_algorithm hash_alg)
        }
 }
 
+int vb2_hash_block_size(enum vb2_hash_algorithm alg)
+{
+       switch (alg) {
+#if VB2_SUPPORT_SHA1
+       case VB2_HASH_SHA1:
+               return VB2_SHA1_BLOCK_SIZE;
+#endif
+#if VB2_SUPPORT_SHA256
+       case VB2_HASH_SHA256:
+               return VB2_SHA256_BLOCK_SIZE;
+#endif
+#if VB2_SUPPORT_SHA512
+       case VB2_HASH_SHA512:
+               return VB2_SHA512_BLOCK_SIZE;
+#endif
+       default:
+               return 0;
+       }
+}
+
+const char *vb2_get_hash_algorithm_name(enum vb2_hash_algorithm alg)
+{
+       switch (alg) {
+#if VB2_SUPPORT_SHA1
+       case VB2_HASH_SHA1:
+               return VB2_SHA1_ALG_NAME;
+#endif
+#if VB2_SUPPORT_SHA256
+       case VB2_HASH_SHA256:
+               return VB2_SHA256_ALG_NAME;
+#endif
+#if VB2_SUPPORT_SHA512
+       case VB2_HASH_SHA512:
+               return VB2_SHA512_ALG_NAME;
+#endif
+       default:
+               return VB2_INVALID_ALG_NAME;
+       }
+}
+
 int vb2_digest_init(struct vb2_digest_context *dc,
                    enum vb2_hash_algorithm hash_alg)
 {
index 9cc877c..c1f225d 100644 (file)
@@ -57,6 +57,9 @@ enum vb2_hash_algorithm {
        /* SHA-256 and SHA-512 */
        VB2_HASH_SHA256 = 2,
        VB2_HASH_SHA512 = 3,
+
+       /* Last index. Don't add anything below. */
+       VB2_HASH_ALG_COUNT,
 };
 
 #endif /* VBOOT_REFERENCE_VBOOT_2CRYPTO_H_ */
diff --git a/firmware/2lib/include/2hmac.h b/firmware/2lib/include/2hmac.h
new file mode 100644 (file)
index 0000000..1df1939
--- /dev/null
@@ -0,0 +1,29 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef VBOOT_REFERENCE_VBOOT_2HMAC_H_
+#define VBOOT_REFERENCE_VBOOT_2HMAC_H_
+
+#include <stdint.h>
+#include "2crypto.h"
+
+/**
+ * Compute HMAC
+ *
+ * @param alg          Hash algorithm ID
+ * @param key          HMAC key
+ * @param key_size     HMAC key size
+ * @param msg          Message to compute HMAC for
+ * @param msg_size     Message size
+ * @param mac          Computed message authentication code
+ * @param mac_size     Size of the buffer pointed by <mac>
+ * @return
+ */
+int hmac(enum vb2_hash_algorithm alg,
+        const void *key, uint32_t key_size,
+        const void *msg, uint32_t msg_size,
+        uint8_t *mac, uint32_t mac_size);
+
+#endif
index 2459024..0077f37 100644 (file)
 #define VB2_SUPPORT_SHA512 1
 #endif
 
+/* These are set to the biggest values among the supported hash algorithms.
+ * They have to be updated as we add new hash algorithms */
+#define VB2_MAX_DIGEST_SIZE    VB2_SHA512_DIGEST_SIZE
+#define VB2_MAX_BLOCK_SIZE     VB2_SHA512_BLOCK_SIZE
+#define VB2_INVALID_ALG_NAME   "INVALID"
+
 #define VB2_SHA1_DIGEST_SIZE 20
 #define VB2_SHA1_BLOCK_SIZE 64
+#define VB2_SHA1_ALG_NAME      "SHA1"
 
 /* Context structs for hash algorithms */
 
@@ -47,6 +54,7 @@ struct vb2_sha1_context {
 
 #define VB2_SHA256_DIGEST_SIZE 32
 #define VB2_SHA256_BLOCK_SIZE 64
+#define VB2_SHA256_ALG_NAME    "SHA256"
 
 struct vb2_sha256_context {
        uint32_t h[8];
@@ -57,6 +65,7 @@ struct vb2_sha256_context {
 
 #define VB2_SHA512_DIGEST_SIZE 64
 #define VB2_SHA512_BLOCK_SIZE 128
+#define VB2_SHA512_ALG_NAME    "SHA512"
 
 struct vb2_sha512_context {
        uint64_t h[8];
@@ -143,6 +152,23 @@ enum vb2_hash_algorithm vb2_crypto_to_hash(uint32_t algorithm);
 int vb2_digest_size(enum vb2_hash_algorithm hash_alg);
 
 /**
+ * Return the block size of a hash algorithm.
+ *
+ * @param hash_alg     Hash algorithm
+ * @return The block size of the algorithm, or 0 if error.
+ */
+int vb2_hash_block_size(enum vb2_hash_algorithm alg);
+
+/**
+ * Return the name of a hash algorithm
+ *
+ * @param alg  Hash algorithm ID
+ * @return     String containing a hash name or VB2_INVALID_ALG_NAME
+ *             if <alg> is invalid.
+ */
+const char *vb2_get_hash_algorithm_name(enum vb2_hash_algorithm alg);
+
+/**
  * Initialize a digest context for doing block-style digesting.
  *
  * @param dc           Digest context
diff --git a/tests/hmac_test.c b/tests/hmac_test.c
new file mode 100644 (file)
index 0000000..92c52fc
--- /dev/null
@@ -0,0 +1,98 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <openssl/hmac.h>
+
+#include "2sha.h"
+#include "2hmac.h"
+#include "test_common.h"
+
+const char short_key[] = "key";
+const char message[] = "The quick brown fox jumps over the lazy dog";
+/* This is supposed to be longer than the supported block sizes */
+const char long_key[] =
+       "loooooooooooooooooooooooooooooooooooooooooooonooooooooooooooooooo"
+       "ooooooooooooooooooooooooooooooooooooooooooooonooooooooooooog key";
+
+static void test_hmac_by_openssl(enum vb2_hash_algorithm alg,
+                                const void *key, uint32_t key_size,
+                                const void *msg, uint32_t msg_size)
+{
+       uint8_t mac[VB2_MAX_DIGEST_SIZE];
+       uint32_t mac_size = sizeof(mac);
+       uint8_t md[VB2_MAX_DIGEST_SIZE];
+       uint32_t md_size = sizeof(md);
+       char test_name[256];
+
+       switch (alg) {
+       case VB2_HASH_SHA1:
+               HMAC(EVP_sha1(), key, key_size, msg, msg_size, md, &md_size);
+               break;
+       case VB2_HASH_SHA256:
+               HMAC(EVP_sha256(), key, key_size, msg, msg_size, md, &md_size);
+               break;
+       case VB2_HASH_SHA512:
+               HMAC(EVP_sha512(), key, key_size, msg, msg_size, md, &md_size);
+               break;
+       default:
+               TEST_SUCC(-1, "Unsupported hash algorithm");
+       }
+       sprintf(test_name, "%s: HMAC-%s (key_size=%d)",
+               __func__, vb2_get_hash_algorithm_name(alg), key_size);
+       TEST_SUCC(hmac(alg, key, key_size, msg, msg_size, mac, mac_size),
+                 test_name);
+       TEST_SUCC(memcmp(mac, md, md_size), "HMAC digests match");
+}
+
+static void test_hmac_error(void)
+{
+       uint8_t mac[VB2_MAX_DIGEST_SIZE];
+       enum vb2_hash_algorithm alg;
+
+       alg = VB2_HASH_SHA1;
+       TEST_TRUE(hmac(alg, NULL, 0,
+                      message, strlen(message), mac, sizeof(mac)),
+                 "key = NULL");
+       TEST_TRUE(hmac(alg, short_key, strlen(short_key),
+                      NULL, 0, mac, sizeof(mac)),
+                 "msg = NULL");
+       TEST_TRUE(hmac(alg, short_key, strlen(short_key),
+                      message, strlen(message), NULL, 0),
+                 "mac = NULL");
+       TEST_TRUE(hmac(alg, short_key, strlen(short_key),
+                      message, strlen(message), mac, 0),
+                 "Buffer too small");
+       alg = -1;
+       TEST_TRUE(hmac(alg, short_key, strlen(short_key),
+                      message, strlen(message), mac, sizeof(mac)),
+                 "Invalid algorithm");
+}
+
+static void test_hmac(void)
+{
+       int alg;
+
+       for (alg = 1; alg < VB2_HASH_ALG_COUNT; alg++) {
+               /* Try short key */
+               test_hmac_by_openssl(alg, short_key, strlen(short_key),
+                                    message, strlen(message));
+               /* Try key longer than a block size */
+               test_hmac_by_openssl(alg, long_key, strlen(long_key),
+                                    message, strlen(message));
+               /* Try empty key and message */
+               test_hmac_by_openssl(alg, "", 0, "", 0);
+       }
+}
+
+int main(void)
+{
+       test_hmac();
+       test_hmac_error();
+
+       return gTestSuccess ? 0 : 255;
+}
index 0a85096..c3185ca 100644 (file)
@@ -5,6 +5,8 @@
 
 /* FIPS 180-2 Tests for message digest functions. */
 
+#include <stdio.h>
+
 #include "2sysincludes.h"
 #include "2rsa.h"
 #include "2sha.h"
@@ -38,6 +40,9 @@ void sha1_tests(void)
                                  VB2_HASH_SHA1, digest, sizeof(digest) - 1),
                VB2_ERROR_SHA_FINALIZE_DIGEST_SIZE,
                "vb2_digest_buffer() too small");
+
+       TEST_EQ(vb2_hash_block_size(VB2_HASH_SHA1), VB2_SHA1_BLOCK_SIZE,
+               "vb2_hash_block_size(VB2_HASH_SHA1)");
 }
 
 void sha256_tests(void)
@@ -82,6 +87,9 @@ void sha256_tests(void)
        vb2_sha256_finalize(&ctx, digest);
        TEST_EQ(memcmp(digest, expect_multiple, sizeof(digest)), 0,
                "SHA-256 multiple extends");
+
+       TEST_EQ(vb2_hash_block_size(VB2_HASH_SHA256), VB2_SHA256_BLOCK_SIZE,
+               "vb2_hash_block_size(VB2_HASH_SHA256)");
 }
 
 void sha512_tests(void)
@@ -109,6 +117,9 @@ void sha512_tests(void)
                                  VB2_HASH_SHA512, digest, sizeof(digest) - 1),
                VB2_ERROR_SHA_FINALIZE_DIGEST_SIZE,
                "vb2_digest_buffer() too small");
+
+       TEST_EQ(vb2_hash_block_size(VB2_HASH_SHA512), VB2_SHA512_BLOCK_SIZE,
+               "vb2_hash_block_size(VB2_HASH_SHA512)");
 }
 
 void misc_tests(void)
@@ -131,6 +142,9 @@ void misc_tests(void)
        TEST_EQ(vb2_digest_size(VB2_HASH_INVALID), 0,
                "digest size invalid alg");
 
+       TEST_EQ(vb2_hash_block_size(VB2_HASH_INVALID), 0,
+               "vb2_hash_block_size(VB2_HASH_INVALID)");
+
        TEST_EQ(vb2_digest_buffer((uint8_t *)oneblock_msg, strlen(oneblock_msg),
                                  VB2_HASH_INVALID, digest, sizeof(digest)),
                VB2_ERROR_SHA_INIT_ALGORITHM,
@@ -147,6 +161,19 @@ void misc_tests(void)
                "vb2_digest_finalize() invalid alg");
 }
 
+static void hash_algorithm_name_tests(void)
+{
+       enum vb2_hash_algorithm alg;
+       char test_name[256];
+
+       for (alg = 1; alg < VB2_HASH_ALG_COUNT; alg++) {
+               sprintf(test_name, "%s: %s (alg=%d)",
+                       __func__, vb2_get_hash_algorithm_name(alg), alg);
+               TEST_STR_NEQ(vb2_get_hash_algorithm_name(alg),
+                            VB2_INVALID_ALG_NAME, test_name);
+       }
+}
+
 int main(int argc, char *argv[])
 {
        /* Initialize long_msg with 'a' x 1,000,000 */
@@ -158,6 +185,7 @@ int main(int argc, char *argv[])
        sha256_tests();
        sha512_tests();
        misc_tests();
+       hash_algorithm_name_tests();
 
        free(long_msg);