summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Sukhomlinov <sukhomlinov@google.com>2019-11-12 11:20:12 -0800
committerCommit Bot <commit-bot@chromium.org>2019-11-21 19:24:22 +0000
commit3bc0a6b273fdd8bdc5ad34e5a0c1ecc88754cfa1 (patch)
tree16808c89fb37dd2b5206edde0d2eba6aeccd4c98
parent052ed9c30e9fca104ac151920de92f8a8f76fc07 (diff)
downloadchrome-ec-3bc0a6b273fdd8bdc5ad34e5a0c1ecc88754cfa1.tar.gz
cr50: Add support for ACVP tests of HMAC SHA-256 DRBG
HMAC DRBG is used for U2F key generation, and as such is subject for ACVP tests. Expose DRBG Init, Generate and Seed commands for automated testing with externally provided test vectors. BUG=b:138578319 BRANCH=cr50 TEST=make CRYPTO_TEST=1 BOARD=cr50 -j && test/tpm_test/tpmtest.py Change-Id: I50a6750864d3cd9a304a9b8a8524ef29cec04410 Signed-off-by: Vadim Sukhomlinov <sukhomlinov@google.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1912662 Reviewed-by: Vadim Sukhomlinov <sukhomlinov@chromium.org> Reviewed-by: Vadim Bendebury <vbendeb@chromium.org> Tested-by: Vadim Sukhomlinov <sukhomlinov@chromium.org> Commit-Queue: Vadim Sukhomlinov <sukhomlinov@chromium.org> Auto-Submit: Vadim Sukhomlinov <sukhomlinov@chromium.org>
-rw-r--r--chip/g/dcrypto/hmac_drbg.c117
-rw-r--r--include/tpm_vendor_cmds.h5
-rw-r--r--test/tpm_test/drbg_test.py108
-rw-r--r--test/tpm_test/subcmd.py4
-rwxr-xr-xtest/tpm_test/tpmtest.py11
5 files changed, 239 insertions, 6 deletions
diff --git a/chip/g/dcrypto/hmac_drbg.c b/chip/g/dcrypto/hmac_drbg.c
index bf49103e93..0643c9bf84 100644
--- a/chip/g/dcrypto/hmac_drbg.c
+++ b/chip/g/dcrypto/hmac_drbg.c
@@ -6,6 +6,7 @@
#include "console.h"
#include "cryptoc/util.h"
#include "dcrypto.h"
+#include "extension.h"
#include "internal.h"
#include "trng.h"
@@ -356,4 +357,120 @@ static int cmd_hmac_drbg_rand(int argc, char **argv)
return 0;
}
DECLARE_SAFE_CONSOLE_COMMAND(hmac_drbg_rand, cmd_hmac_drbg_rand, NULL, NULL);
+
+enum drbg_command {
+ DRBG_INIT = 0,
+ DRBG_RESEED = 1,
+ DRBG_GENERATE = 2
+};
+
+/*
+ * DRBG_TEST command structure:
+ *
+ * field | size | note
+ * ==========================================================================
+ * mode | 1 | 0 - DRBG_INIT, 1 - DRBG_RESEED, 2 - DRBG_GENERATE
+ * p0_len | 2 | size of first input in bytes
+ * p0 | p0_len | entropy for INIT & SEED, input for GENERATE
+ * p1_len | 2 | size of second input in bytes (for INIT & RESEED)
+ * | | or size of expected output for GENERATE
+ * p1 | p1_len | nonce for INIT & SEED
+ * p2_len | 2 | size of third input in bytes for DRBG_INIT
+ * p2 | p2_len | personalization for INIT & SEED
+ *
+ * DRBG_INIT (entropy, nonce, perso)
+ * DRBG_RESEED (entropy, additional input 1, additional input 2)
+ * DRBG_INIT and DRBG_RESEED returns empty response
+ * DRBG_GENERATE (p0_len, p0 - additional input 1, p1_len - size of output)
+ * DRBG_GENERATE returns p1_len bytes of generated data
+ * (up to a maximum of 128 bytes)
+ */
+static enum vendor_cmd_rc drbg_test(enum vendor_cmd_cc code, void *buf,
+ size_t input_size, size_t *response_size)
+{
+ static struct drbg_ctx drbg_ctx;
+ static uint8_t output[128];
+ uint8_t *p0 = NULL, *p1 = NULL, *p2 = NULL;
+ uint16_t p0_len = 0, p1_len = 0, p2_len = 0;
+ uint8_t *cmd = (uint8_t *)buf;
+ size_t max_out_len = *response_size;
+ enum drbg_command drbg_op;
+
+ *response_size = 0;
+ /* there is always op + first parameter, even if zero length */
+ if (input_size < sizeof(p0_len) + 1)
+ return VENDOR_RC_BOGUS_ARGS;
+ drbg_op = *cmd++;
+ p0_len = *cmd++;
+ p0_len = p0_len * 256 + *cmd++;
+ input_size -= 3;
+ if (p0_len > input_size)
+ return VENDOR_RC_BOGUS_ARGS;
+ input_size -= p0_len;
+ if (p0_len)
+ p0 = cmd;
+ cmd += p0_len;
+
+ /* there should be enough space for p1_len */
+ if (input_size && input_size < sizeof(p1_len))
+ return VENDOR_RC_BOGUS_ARGS;
+
+ /* DRBG_GENERATE should just have p1_len defined */
+ if (drbg_op == DRBG_GENERATE && input_size != sizeof(p1_len))
+ return VENDOR_RC_BOGUS_ARGS;
+
+ if (input_size) {
+ p1_len = *cmd++;
+ p1_len = p1_len * 256 + *cmd++;
+ input_size -= 2;
+
+ if (drbg_op != DRBG_GENERATE) {
+ if (p1_len > input_size)
+ return VENDOR_RC_BOGUS_ARGS;
+ input_size -= p1_len;
+ if (p1_len)
+ p1 = cmd;
+ cmd += p1_len;
+ }
+ }
+
+ if (input_size) {
+ if (drbg_op == DRBG_GENERATE)
+ return VENDOR_RC_BOGUS_ARGS;
+ p2_len = *cmd++;
+ p2_len = p2_len * 256 + *cmd++;
+ input_size -= 2;
+ if (p2_len > input_size)
+ return VENDOR_RC_BOGUS_ARGS;
+ if (p2_len)
+ p2 = cmd;
+ }
+
+ switch (drbg_op) {
+ case DRBG_INIT: {
+ hmac_drbg_init(&drbg_ctx, p0, p0_len, p1, p1_len, p2, p2_len);
+ break;
+ }
+ case DRBG_RESEED: {
+ hmac_drbg_reseed(&drbg_ctx, p0, p0_len, p1, p1_len, p2, p2_len);
+ break;
+ }
+ case DRBG_GENERATE: {
+ if (p1_len > sizeof(output) || max_out_len < p1_len)
+ return VENDOR_RC_BOGUS_ARGS;
+
+ hmac_drbg_generate(&drbg_ctx, output, p1_len, p0, p0_len);
+
+ memcpy(buf, output, p1_len);
+ *response_size = p1_len;
+ break;
+ }
+ default:
+ return VENDOR_RC_BOGUS_ARGS;
+ }
+
+ return VENDOR_RC_SUCCESS;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_DRBG_TEST, drbg_test);
+
#endif /* CRYPTO_TEST_SETUP */
diff --git a/include/tpm_vendor_cmds.h b/include/tpm_vendor_cmds.h
index 7244b47a4f..5afd1fc84b 100644
--- a/include/tpm_vendor_cmds.h
+++ b/include/tpm_vendor_cmds.h
@@ -136,6 +136,11 @@ enum vendor_cmd_cc {
VENDOR_CC_U2F_MODE = 49,
+ /*
+ * HMAC-SHA256 DRBG invocation for ACVP tests
+ */
+ VENDOR_CC_DRBG_TEST = 50,
+
VENDOR_CC_TRNG_TEST = 51,
LAST_VENDOR_COMMAND = 65535,
diff --git a/test/tpm_test/drbg_test.py b/test/tpm_test/drbg_test.py
new file mode 100644
index 0000000000..097a222923
--- /dev/null
+++ b/test/tpm_test/drbg_test.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python2
+# Copyright 2019 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.
+
+"""Module for testing hash functions using extended commands."""
+
+from __future__ import print_function
+
+from binascii import a2b_hex as a2b
+from struct import pack
+
+import subcmd
+import utils
+
+
+# A standard empty response to DRBG extended commands.
+EMPTY_DRBG_RESPONSE = ''.join('%c' % x for x in (0x80, 0x01,
+ 0x00, 0x00, 0x00, 0x0c,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, subcmd.DRBG_TEST))
+
+DRBG_INIT = 0
+DRBG_RESEED = 1
+DRBG_GENERATE = 2
+
+test_inputs = (
+ (DRBG_INIT,
+ ('C40894D0C37712140924115BF8A3110C7258532365BB598F81B127A5E4CB8EB0',
+ 'FBB1EDAF92D0C2699F5C0A7418D308B09AC679FFBB0D8918C8E62D35091DD2B9',
+ '2B18535D739F7E75AF4FF0C0C713DD4C9B0A6803D2E0DB2BDE3C4F3650ABF750')),
+ (DRBG_RESEED,
+ ('4D58A621857706450338CCA8A1AF5CD2BD9305F3475CF1A8752518DD8E8267B6',
+ '0153A0A1D7487E2EE9915E2CAA8488F97239C67595F418D9503D0B11CC07044E', '')),
+ (DRBG_GENERATE,
+ ('39AE66C2939D1D73EF21AE22988B04CC7E8EA2D790C75E1FC6ACC7FEEEF90F98',
+ '')),
+ (DRBG_GENERATE,
+ ('B8031829E07B09EEEADEBA149D0AC9F08B110197CD8BBDDC32744BCD66FCF3C4',
+ 'A1307377F6B472661BC3C6D44C035FB20A13CCB04D6601B2425FC4DDA3B6D7DF')),
+ (DRBG_INIT,
+ ('3A2D261884010CCB4C2C4D7B323CCB7BD4515089BEB749C565A7492710922164',
+ '9E4D22471A4546F516099DD4D737967562D1BB77D774B67B7FE4ED893AE336CF',
+ '5837CAA74345CC2D316555EF820E9F3B0FD454D8C5B7BDE68E4A176D52EE7D1C')),
+ (DRBG_GENERATE,
+ ('4D87985505D779F1AD98455E04199FE8F2FE8E550E6FEB1D26177A2C5B744B9F',
+ '')),
+ (DRBG_GENERATE,
+ ('85D011A3B36AC6B25A792F213A1C22C80BFD1C5B47BCA04CD0D9834BB466447B',
+ 'B03863C42C9396B4936D83A551871A424C5A8EDBDC9D1E0E8E89710D58B5CA1E')),
+
+)
+
+_DRBG_INIT_FORMAT = '{op:c}{p0l:s}{p0}{p1l:s}{p1}{p2l:s}{p2}'
+def _drbg_init_cmd(op, entropy, nonce, perso):
+ return _DRBG_INIT_FORMAT.format(op=op,
+ p0l=pack('>H', len(entropy)), p0=entropy,
+ p1l=pack('>H', len(nonce)), p1=nonce,
+ p2l=pack('>H', len(perso)), p2=perso)
+
+_DRBG_GEN_FORMAT = '{op:c}{p0l:s}{p0}{p1l:s}'
+
+def _drbg_gen_cmd(inp, out):
+ outlen = len(out)
+ if outlen == 0:
+ outlen = 32 # if we don't care about output value, still need to have it
+ return _DRBG_GEN_FORMAT.format(op=DRBG_GENERATE,
+ p0l=pack('>H', len(inp)), p0=inp,
+ p1l=pack('>H', outlen))
+
+
+def drbg_test(tpm):
+ """Runs DRBG test case.
+
+ Args:
+ tpm: a tpm object used to communicate with the device
+
+ Raises:
+ subcmd.TpmTestError: on unexpected target responses
+ """
+
+ for test in test_inputs:
+ drbg_op, drbg_params = test
+ if drbg_op == DRBG_INIT:
+ entropy, nonce, perso = drbg_params
+ cmd = _drbg_init_cmd(drbg_op, a2b(entropy), a2b(nonce), a2b(perso))
+ response = tpm.command(tpm.wrap_ext_command(subcmd.DRBG_TEST, cmd))
+ if response != EMPTY_DRBG_RESPONSE:
+ raise subcmd.TpmTestError("Unexpected response to DRBG_INIT: %s" %
+ (utils.hex_dump(wrapped_response)))
+ elif drbg_op == DRBG_RESEED:
+ entropy, inp1, inp2 = drbg_params
+ cmd = _drbg_init_cmd(drbg_op, a2b(entropy), a2b(inp1), a2b(inp2))
+ response = tpm.command(tpm.wrap_ext_command(subcmd.DRBG_TEST, cmd))
+ if response != EMPTY_DRBG_RESPONSE:
+ raise subcmd.TpmTestError("Unexpected response to DRBG_RESEED: %s" %
+ (utils.hex_dump(wrapped_response)))
+ elif drbg_op == DRBG_GENERATE:
+ inp, expected = drbg_params
+ cmd = _drbg_gen_cmd(a2b(inp), a2b(expected))
+ response = tpm.command(tpm.wrap_ext_command(subcmd.DRBG_TEST, cmd))
+ if expected != '':
+ result = response[12:]
+ if a2b(expected) != result:
+ raise subcmd.TpmTestError('error:\nexpected %s\nreceived %s' %
+ (utils.hex_dump(a2b(expected)),
+ utils.hex_dump(result)))
+ print('%sSUCCESS: %s' % (utils.cursor_back(), 'DRBG test'))
diff --git a/test/tpm_test/subcmd.py b/test/tpm_test/subcmd.py
index d6aeb6722f..7260df0dd5 100644
--- a/test/tpm_test/subcmd.py
+++ b/test/tpm_test/subcmd.py
@@ -5,7 +5,7 @@
"""Subcommand codes that specify the crypto module."""
-# Keep these codes in sync with include/extension.h.
+# Keep these codes in sync with include/tpm_vendor_cmds.h
AES = 0
HASH = 1
RSA = 2
@@ -13,7 +13,7 @@ ECC = 3
FW_UPGRADE = 4
HKDF = 5
ECIES = 6
-
+DRBG_TEST = 50
# The same exception class used by all tpmtest modules.
class TpmTestError(Exception):
pass
diff --git a/test/tpm_test/tpmtest.py b/test/tpm_test/tpmtest.py
index 4a8e442265..11218cbcc6 100755
--- a/test/tpm_test/tpmtest.py
+++ b/test/tpm_test/tpmtest.py
@@ -20,6 +20,7 @@ root_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
sys.path.append(os.path.join(root_dir, '..', '..', 'build', 'tpm_test'))
import crypto_test
+import drbg_test
import ecc_test
import ecies_test
import ftdi_spi_tpm
@@ -71,17 +72,18 @@ class TPM(object):
if size > 4096:
raise subcmd.TpmTestError(prefix + 'invalid size %d' % size)
if response_mode:
- # Startup response code or extension command response code
- if cmd_code == 0x100 or cmd_code == 0:
+ # Startup response code, extension or vendor command response code
+ if cmd_code == 0x100 or cmd_code == 0 or cmd_code == 0x500:
return
else:
raise subcmd.TpmTestError(
- prefix + 'invalid command code 0x%x' % cmd_code)
+ prefix + 'invalid response code 0x%x' % cmd_code)
if cmd_code >= 0x11f and cmd_code <= 0x18f:
return # This is a valid command
if cmd_code == EXT_CMD:
return # This is an extension command
-
+ if cmd_code >= 0x20000000 and cmd_code <= 0x200001ff:
+ return # this is vendor command
raise subcmd.TpmTestError(prefix + 'invalid command code 0x%x' % cmd_code)
def command(self, cmd_data):
@@ -162,6 +164,7 @@ if __name__ == '__main__':
trng_test.trng_test(t)
sys.exit(1)
crypto_test.crypto_tests(t, os.path.join(root_dir, 'crypto_test.xml'))
+ drbg_test.drbg_test(t)
ecc_test.ecc_test(t)
ecies_test.ecies_test(t)
hash_test.hash_test(t)