diff options
-rw-r--r-- | chip/g/dcrypto/hmac_drbg.c | 117 | ||||
-rw-r--r-- | include/tpm_vendor_cmds.h | 5 | ||||
-rw-r--r-- | test/tpm_test/drbg_test.py | 108 | ||||
-rw-r--r-- | test/tpm_test/subcmd.py | 4 | ||||
-rwxr-xr-x | test/tpm_test/tpmtest.py | 11 |
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) |