summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorVadim Sukhomlinov <sukhomlinov@google.com>2019-11-15 11:37:08 -0800
committerCommit Bot <commit-bot@chromium.org>2019-11-19 20:35:30 +0000
commit8190be1a6fef4203d743825d7afe74b5ec1fc083 (patch)
treeab821d6962572ea7c6476ac17d47f85f3de3919e /test
parente9bc15a9ac68e3b28e68c0191794e842927622ee (diff)
downloadchrome-ec-8190be1a6fef4203d743825d7afe74b5ec1fc083.tar.gz
cr50: Add TRNG_TEST command to download entropy samples for NIST tests
NIST 800-90B Entropy assesment tests requires 1M of 8-bit samples for statistical tests. While it's possible to use TPM2_GetRandom command to get entropy on cr50 (there is no software postprocessing), this command is not available when compiled with CRYPTO_TEST=1 due to lack of space in firmware. Adding vendor command which is available with CRYPTO_TEST=1 to get raw entropy from TRNG. Added support script to save entropy in file for further analysis. Since downloading entropy takes a long time, new option'-t' added to tpmtest.py which only invokes download of TRNG samples BUG=b:138577834 BRANCH=cr50 TEST=make BOARD=cr50 CRYPTO_TEST=1 && test/tpm_test/tpmtest.py -t To run NIST tests: nist_entropy.sh Change-Id: I237a4581332a6e2c0332fe6ecf40731ab0be3355 Signed-off-by: Vadim Sukhomlinov <sukhomlinov@google.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1919640 Reviewed-by: Vadim Sukhomlinov <sukhomlinov@chromium.org> Reviewed-by: Vadim Bendebury <vbendeb@chromium.org> Tested-by: Vadim Sukhomlinov <sukhomlinov@chromium.org> Commit-Queue: Vadim Bendebury <vbendeb@chromium.org> Auto-Submit: Vadim Sukhomlinov <sukhomlinov@chromium.org>
Diffstat (limited to 'test')
-rw-r--r--test/tpm_test/README.md49
-rwxr-xr-xtest/tpm_test/nist_entropy.sh21
-rwxr-xr-xtest/tpm_test/tpmtest.py29
-rw-r--r--test/tpm_test/trng_test.py50
4 files changed, 147 insertions, 2 deletions
diff --git a/test/tpm_test/README.md b/test/tpm_test/README.md
new file mode 100644
index 0000000000..80aaa0a76f
--- /dev/null
+++ b/test/tpm_test/README.md
@@ -0,0 +1,49 @@
+# TPM / Crypto unit tests
+tpmtest.py runs set of tests to check correctness of cryptographic functions
+implementation. These tests require a special firmware image built with
+CRYPTO_TEST=1 flag to enable direct exposure of low-level cryptographic
+functions via TPM extensions and vendor commands. TPM functionality itself
+is disabled due to not enough enough flash/memory to fit both.
+As such, these tests are expected to run over H1 Red board.
+
+Firmware image is expected to be built with:
+
+make BOARD=cr50 CRYPTO_TEST=1 -j
+
+Cryptographic tests are invoked when tpmtest.py is executed with no command-line
+parameters:
+
+ ./tpmtest.py
+
+Option *-d* can be used for debugging. It adds output of actual data
+sent/received from H1, which is handy when adding new functionality:
+
+ ./tpmtest.py -d
+
+# TRNG tests
+Another functionality is statistical assessment of entropy from True Random
+Number Generator (TRNG). These tests are prerequisite for FIPS 140-2/3
+certification and governed by NIST SP 800-90B.
+tpmtest.py implements a mode to download raw data from TRNG:
+
+ ./tpmtest.py -t
+
+Script nist_entropy.sh automated this testing by
+1. Downloading latest NIST Entropy Assessment tool from
+ [https://github.com/usnistgov/SP800-90B_EntropyAssessment] and building it.
+2. Gathering 1000000 of 8-bit samples from H1 TRNG and
+ storing it in /tmp/trng_output using tpmtest.py -t
+3. Running NIST tool in non-IID (independent and identically distributed) mode
+ to estimate entropy. This specific mode is choosed as there is no formal
+ proof that TRNG data is independent and identically distributed.
+ It follows manual in
+ [https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90B.pdf]
+
+The successful result is being awarded entropy estimate for TRNG, which is
+expected to be more than 7 (8 is theoretical max).
+If test fails, no entropy assessment is awarded.
+
+This script is expected to run in platform/ec/test/tpm_test directory
+(where ./tpmtest.py is located)
+
+
diff --git a/test/tpm_test/nist_entropy.sh b/test/tpm_test/nist_entropy.sh
new file mode 100755
index 0000000000..f69c5652cd
--- /dev/null
+++ b/test/tpm_test/nist_entropy.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+# 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.
+
+# NIST toolset needs sudo emerge dev-libs/libdivsufsort
+rm -rf /tmp/ea
+git clone --depth 1 https://github.com/usnistgov/SP800-90B_EntropyAssessment.git /tmp/ea/
+make -C /tmp/ea/cpp/ non_iid
+make -C /tmp/ea/cpp/ restart
+TRNG_OUT=/tmp/trng_output
+rm -f $TRNG_OUT
+./tpmtest.py -t
+if [ ! -f "$TRNG_OUT" ]; then
+ echo "$TRNG_OUT does not exist"
+ exit 1
+fi
+/tmp/ea/cpp/ea_non_iid -a $TRNG_OUT | tee ea_non_iid.log
+entropy=`grep min ea_non_iid.log | awk '{ print $5 }'`
+echo "Minimal entropy" $entropy
+/tmp/ea/cpp/ea_restart $TRNG_OUT $entropy | tee -a ea_non_iid.log
diff --git a/test/tpm_test/tpmtest.py b/test/tpm_test/tpmtest.py
index 823c3013e7..4a8e442265 100755
--- a/test/tpm_test/tpmtest.py
+++ b/test/tpm_test/tpmtest.py
@@ -7,6 +7,7 @@
from __future__ import print_function
+import getopt
import os
import struct
import sys
@@ -26,6 +27,7 @@ import hash_test
import hkdf_test
import rsa_test
import subcmd
+import trng_test
import upgrade_test
# Extension command for dcypto testing
@@ -130,12 +132,35 @@ class TPM(object):
def debug_enabled(self):
return self._debug_enabled
+def usage():
+ print ('Syntax: tpmtest.py [-d | -t | -h ]\n'
+ ' -d - prints additional debug information during tests\n'
+ ' -t - dump raw output from TRNG to /tmp/trng_output\n'
+ ' -h - this help\n')
+ return
if __name__ == '__main__':
try:
- debug_needed = len(sys.argv) == 2 and sys.argv[1] == '-d'
+ opts, args = getopt.getopt(sys.argv[1:], 'dth','help')
+ except getopt.GetoptError as err:
+ print(str(err))
+ usage()
+ sys.exit(2)
+ debug_needed = False
+ trng_only = False
+ for o, a in opts:
+ if o == '-d':
+ debug_needed = True
+ elif o == '-t':
+ trng_only = True
+ elif o == '-h' or o == '--help':
+ usage()
+ sys.exit(0)
+ try:
t = TPM(debug_mode=debug_needed)
-
+ if trng_only:
+ trng_test.trng_test(t)
+ sys.exit(1)
crypto_test.crypto_tests(t, os.path.join(root_dir, 'crypto_test.xml'))
ecc_test.ecc_test(t)
ecies_test.ecies_test(t)
diff --git a/test/tpm_test/trng_test.py b/test/tpm_test/trng_test.py
new file mode 100644
index 0000000000..aac2803076
--- /dev/null
+++ b/test/tpm_test/trng_test.py
@@ -0,0 +1,50 @@
+#!/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.
+"""Tests for trng."""
+from __future__ import print_function
+import struct
+
+import subcmd
+import utils
+
+TRNG_TEST_FMT = '>H'
+TRNG_TEST_RSP_FMT = '>H2IH'
+TRNG_TEST_CC = 0x33
+TRNG_SAMPLE_SIZE = 1000 # minimal recommended by NIST is 1000 bytes per sample
+TRNG_SAMPLE_COUNT = 1000 # NIST require at least 1000000 of 8-bit samples
+
+def get_random_command(size):
+ return struct.pack(TRNG_TEST_FMT, size)
+
+def get_random_command_rsp(size):
+ return struct.pack(TRNG_TEST_RSP_FMT, 0x8001,
+ struct.calcsize(TRNG_TEST_RSP_FMT) + size, 0, TRNG_TEST_CC)
+
+
+def trng_test(tpm):
+ """Download entropy samples from TRNG
+
+ Command structure, shared out of band with the test running on the target:
+
+ field | size | note
+ ===================================================================
+ text_len | 2 | size of the text to process, big endian
+
+ Args:
+ tpm: a tpm object used to communicate with the device
+
+ Raises:
+ subcmd.TpmTestError: on unexpected target responses
+ """
+ with open('/tmp/trng_output', 'wb') as f:
+ for x in range(0, TRNG_SAMPLE_COUNT):
+ wrapped_response = tpm.command(tpm.wrap_ext_command(TRNG_TEST_CC,
+ get_random_command(TRNG_SAMPLE_SIZE)))
+ if wrapped_response[:12] != get_random_command_rsp(TRNG_SAMPLE_SIZE):
+ raise subcmd.TpmTestError("Unexpected response to '%s': %s" %
+ ("trng", utils.hex_dump(wrapped_response)))
+ f.write(wrapped_response[12:])
+ print('%s %d%%\r' %( utils.cursor_back(), (x/10)), end=""),
+ print('%sSUCCESS: %s' % (utils.cursor_back(), 'trng'))