summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2015-11-27 07:21:46 -0800
committerchrome-bot <chrome-bot@chromium.org>2015-12-03 16:08:39 -0800
commit5de8d35d113dcb2595207d490d55ac0c78ece270 (patch)
tree2f4e3476579f1dfcccd86f22b1395c295ad7b021
parentbf91b0048620b5b22a8636f989cbfa89c0a4ee59 (diff)
downloadchrome-ec-5de8d35d113dcb2595207d490d55ac0c78ece270.tar.gz
cr50: add extension command for testing hash primitives
A new extended subcommand code (1) is being added to handle hash testing. The new subcommand handler keeps track of multiple sha1 and sha256 contexts the host might want to exercise. The number of available contexts is limited by the amount of available free memory. One of four hash operations could be requested by the host: 'Start', 'Continue', 'Finish' - when hashing a single stream over multiple extended command messages, and 'Single' when the entire message to be hashed is included in one extended command payload. The command payload had the following format: * field | size | note * =================================================================== * mode | 1 | 0 - start, 1 - cont., 2 - finish, 3 - single * hash_mode | 1 | 0 - sha1, 1 - sha256 * handle | 1 | seassion handle, ignored in 'single' mode * text_len | 2 | size of the text to process, big endian * text | text_len | text to hash As soon as the first 'Start' message is encountered, the handler tries to allocate shared memory to keep track of the test contexts, the amount of available memory determines how many contexts the handler can support concurrently. As soon as the last 'Finish' command is encountered, the handler returns the shared memory to the 'heap'. BRANCH=none BUG=chrome-os-partner:43025 TEST=after adding the host side implementation and fixing a couple of bugs, hash tests pass (see upcoming patches). Change-Id: Iae18552d6220d670d1c6f32294f0af1a8d0d5c90 Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/314692 Reviewed-by: Bill Richardson <wfrichar@chromium.org>
-rw-r--r--board/cr50/tpm2/hash.c212
-rw-r--r--include/extension.h19
2 files changed, 228 insertions, 3 deletions
diff --git a/board/cr50/tpm2/hash.c b/board/cr50/tpm2/hash.c
index 157e8526ef..ec036c8497 100644
--- a/board/cr50/tpm2/hash.c
+++ b/board/cr50/tpm2/hash.c
@@ -137,3 +137,215 @@ uint16_t _cpri__CompleteHash(CPRI_HASH_STATE *state,
memcpy(out, DCRYPTO_HASH_final(ctx), out_len);
return out_len;
}
+
+#ifdef CRYPTO_TEST_SETUP
+
+#include "console.h"
+#include "extension.h"
+#include "shared_mem.h"
+
+#define CPRINTF(format, args...) cprintf(CC_EXTENSION, format, ## args)
+
+struct test_context {
+ int context_handle;
+ CPRI_HASH_STATE hstate;
+};
+
+static struct {
+ int current_context_count;
+ int max_contexts;
+ struct test_context *contexts;
+} hash_test_db;
+
+struct test_context *find_context(int handle)
+{
+ int i;
+
+ for (i = 0; i < hash_test_db.current_context_count; i++)
+ if (hash_test_db.contexts[i].context_handle == handle)
+ return hash_test_db.contexts + i;
+ return NULL;
+}
+
+static void process_start(TPM_ALG_ID alg, int handle, void *response_body,
+ size_t *response_size)
+{
+ uint8_t *response = response_body;
+ struct test_context *new_context;
+
+ if (find_context(handle)) {
+ *response = EXC_HASH_DUPLICATED_HANDLE;
+ *response_size = 1;
+ return;
+ }
+
+ if (!hash_test_db.max_contexts) {
+ /* Check how many contexts could possible fit. */
+ hash_test_db.max_contexts = shared_mem_size() /
+ sizeof(struct test_context);
+ }
+
+ if (!hash_test_db.contexts)
+ shared_mem_acquire(shared_mem_size(),
+ (char **)&hash_test_db.contexts);
+
+ if (!hash_test_db.contexts ||
+ (hash_test_db.current_context_count == hash_test_db.max_contexts)) {
+ *response = EXC_HASH_TOO_MANY_HANDLES;
+ *response_size = 1;
+ return;
+ }
+
+ new_context = hash_test_db.contexts +
+ hash_test_db.current_context_count++;
+ new_context->context_handle = handle;
+ _cpri__StartHash(alg, 0, &new_context->hstate);
+}
+
+static void process_continue(int handle, void *cmd_body, uint16_t text_len,
+ void *response_body, size_t *response_size)
+{
+ struct test_context *context = find_context(handle);
+
+ if (!context) {
+ *((uint8_t *)response_body) = EXC_HASH_UNKNOWN_CONTEXT;
+ *response_size = 1;
+ return;
+ }
+
+ _cpri__UpdateHash(&context->hstate, text_len, cmd_body);
+}
+
+static void process_finish(int handle, void *response_body,
+ size_t *response_size)
+{
+ struct test_context *context = find_context(handle);
+
+ if (!context) {
+ *((uint8_t *)response_body) = EXC_HASH_UNKNOWN_CONTEXT;
+ *response_size = 1;
+ return;
+ }
+
+ /* There for sure is enough room in the TPM buffer. */
+ *response_size = _cpri__CompleteHash(&context->hstate,
+ SHA_DIGEST_MAX_BYTES,
+ response_body);
+
+ /* drop this context from the database. */
+ hash_test_db.current_context_count--;
+ if (!hash_test_db.current_context_count) {
+ shared_mem_release(hash_test_db.contexts);
+ return;
+ }
+
+ /* Nothing to do, if the deleted context is the last one in memory. */
+ if (context == (hash_test_db.contexts +
+ hash_test_db.current_context_count))
+ return;
+
+ memcpy(context,
+ hash_test_db.contexts + hash_test_db.current_context_count,
+ sizeof(*context));
+}
+
+static void hash_command_handler(void *cmd_body,
+ size_t cmd_size,
+ size_t *response_size)
+{
+ int mode;
+ int hash_mode;
+ int handle;
+ uint16_t text_len;
+ uint8_t *cmd;
+ size_t response_room = *response_size;
+ TPM_ALG_ID alg;
+
+ cmd = cmd_body;
+
+ /*
+ * Empty response is sent as a success indication when the digest is
+ * not yet expected (i.e. in response to 'start' and 'cont' commands,
+ * as defined below).
+ *
+ * Single byte responses indicate errors, test successes are
+ * communicated as responses of the size of the appropriate digests.
+ */
+ *response_size = 0;
+
+ /*
+ * Command structure, shared out of band with the test driver running
+ * on the host:
+ *
+ * field | size | note
+ * ===================================================================
+ * mode | 1 | 0 - start, 1 - cont., 2 - finish, 3 - single
+ * hash_mode | 1 | 0 - sha1, 1 - sha256
+ * handle | 1 | seassion handle, ignored in 'single' mode
+ * text_len | 2 | size of the text to process, big endian
+ * text | text_len | text to hash
+ */
+
+ mode = *cmd++;
+ hash_mode = *cmd++;
+ handle = *cmd++;
+ text_len = *cmd++;
+ text_len = text_len * 256 + *cmd++;
+
+ switch (hash_mode) {
+ case 0:
+ alg = TPM_ALG_SHA1;
+ break;
+ case 1:
+ alg = TPM_ALG_SHA256;
+ break;
+
+ default:
+ return;
+ }
+
+ switch (mode) {
+ case 0: /* Start a new hash context. */
+ process_start(alg, handle, cmd_body, response_size);
+ if (*response_size)
+ break; /* Something went wrong. */
+ process_continue(handle, cmd, text_len,
+ cmd_body, response_size);
+ break;
+
+ case 1:
+ process_continue(handle, cmd, text_len,
+ cmd_body, response_size);
+ break;
+
+ case 2:
+ process_continue(handle, cmd, text_len,
+ cmd_body, response_size);
+ if (*response_size)
+ break; /* Something went wrong. */
+
+ process_finish(handle, cmd_body, response_size);
+ CPRINTF("%s:%d response size %d\n", __func__, __LINE__,
+ *response_size);
+ break;
+
+ case 3: /* Process a buffer in a single shot. */
+ if (!text_len)
+ break;
+ /*
+ * Error responses are just 1 byte in size, valid responses
+ * are of various hash sizes.
+ */
+ *response_size = _cpri__HashBlock(alg, text_len,
+ cmd, response_room, cmd_body);
+ CPRINTF("%s:%d response size %d\n", __func__,
+ __LINE__, *response_size);
+ break;
+ default:
+ break;
+ }
+}
+
+DECLARE_EXTENSION_COMMAND(EXTENSION_HASH, hash_command_handler);
+
+#endif /* CRYPTO_TEST_SETUP */
diff --git a/include/extension.h b/include/extension.h
index 45637d9518..f9757c1510 100644
--- a/include/extension.h
+++ b/include/extension.h
@@ -30,8 +30,9 @@ typedef void (*extension_handler)(void *buffer,
* @param buffer Data to be processd by the handler, the same space
* is used for data returned by the handler.
* @command_size Size of the input data.
- * @param size On input - max size of the buffer, on output - actual
- * number of data returned by the handler.
+ * @param size On input - max size of the buffer, on output - actual number of
+ * data returned by the handler. A single byte return
+ * usually indicates an error and contains the error code.
*/
void extension_route_command(uint16_t command_code,
void *buffer,
@@ -43,9 +44,21 @@ struct extension_command {
extension_handler handler;
} __packed;
-/* Values for different extension commands. */
+/* Values for different extension subcommands. */
enum {
EXTENSION_AES = 0,
+ EXTENSION_HASH = 1,
+};
+
+
+/* Error codes reported by extension commands. */
+enum {
+ /* EXTENSION_HASH error codes */
+ /* Attempt to start a session on an active handle. */
+ EXC_HASH_DUPLICATED_HANDLE = 1,
+ EXC_HASH_TOO_MANY_HANDLES = 2, /* No room to allocate a new context. */
+ /* Continuation/finish on unknown context. */
+ EXC_HASH_UNKNOWN_CONTEXT = 3
};
#define DECLARE_EXTENSION_COMMAND(code, handler) \