diff options
author | Nick Sanders <nsanders@chromium.org> | 2017-06-27 17:58:45 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2017-10-06 00:21:29 -0700 |
commit | 02045eb040227250689caec9b9401c2cd3861363 (patch) | |
tree | f09e73f9794a6e581c3ae3ff2e5c344dd2dd0b63 | |
parent | 366c36c8f1655c57f4c05d000cb4c000020d10db (diff) | |
download | chrome-ec-02045eb040227250689caec9b9401c2cd3861363.tar.gz |
mn50: add data signing capability
Add a PERSO_AUTH appid to sign data passed through the
AUTH mn50.
Add a signer command to start and generate signatures.
Clean UART init to avoid spurious nonprinting characters
that will contaminate the siugnature.
BUG=b:36910757
BRANCH=None
TEST=generates signature for uart and spi
Signed-off-by: Nick Sanders <nsanders@chromium.org>
Change-Id: I5fc3c4ee34898421060b57b774a09734f6a1bae5
Reviewed-on: https://chromium-review.googlesource.com/670984
Reviewed-by: Marius Schilder <mschilder@chromium.org>
-rw-r--r-- | board/mn50/board.c | 70 | ||||
-rw-r--r-- | board/mn50/board.h | 1 | ||||
-rw-r--r-- | board/mn50/build.mk | 1 | ||||
-rw-r--r-- | board/mn50/ec.tasklist | 2 | ||||
-rw-r--r-- | board/mn50/signing.c | 201 | ||||
-rw-r--r-- | board/mn50/signing.h | 73 | ||||
-rw-r--r-- | board/mn50/usb_spi.c | 7 | ||||
-rw-r--r-- | chip/g/dcrypto/app_key.c | 10 | ||||
-rw-r--r-- | chip/g/dcrypto/dcrypto.h | 1 | ||||
-rw-r--r-- | chip/g/usart.c | 48 | ||||
-rw-r--r-- | chip/g/usb_spi.c | 13 | ||||
-rw-r--r-- | chip/g/usb_spi.h | 2 | ||||
-rw-r--r-- | include/config.h | 8 |
13 files changed, 431 insertions, 6 deletions
diff --git a/board/mn50/board.c b/board/mn50/board.c index 15371075d3..302a7d5296 100644 --- a/board/mn50/board.c +++ b/board/mn50/board.c @@ -23,6 +23,7 @@ #include "nvmem_vars.h" #include "registers.h" #include "signed_header.h" +#include "signing.h" #include "spi.h" #include "system.h" #include "task.h" @@ -137,9 +138,7 @@ static void board_init(void) /* Enable USB / CCD */ usb_release(); usb_console_enable(1, 0); - usb_spi_enable(&ccd_usb_spi, 1); ccd_phy_init(); - uartn_enable(UART_AP); /* Calibrate INA0 (VBUS) with 1mA/LSB scale */ i2cm_init(); @@ -258,6 +257,9 @@ void enable_socket(void) /* UART */ GWRITE(PINMUX, DIOA7_SEL, GC_PINMUX_UART1_TX_SEL); + GWRITE(PINMUX, DIOA3_SEL, GC_PINMUX_UART1_RX_SEL); + GWRITE_FIELD(PINMUX, DIOA3_CTL, PU, 1); + uartn_enable(UART_AP); /* Chip select. */ GWRITE_FIELD(PINMUX, DIOA5_CTL, PU, 1); @@ -281,7 +283,10 @@ void disable_socket(void) GWRITE(PINMUX, DIOA5_SEL, GC_PINMUX_GPIO0_GPIO10_SEL); /* UART */ + uartn_disable(UART_AP); GWRITE(PINMUX, DIOA7_SEL, 0); + GWRITE(PINMUX, DIOA3_SEL, 0); + GWRITE_FIELD(PINMUX, DIOA3_CTL, PU, 0); /* GPIOs as inputs. */ gpio_set_flags(GPIO_DUT_BOOT_CFG, GPIO_INPUT); @@ -319,6 +324,67 @@ DECLARE_SAFE_CONSOLE_COMMAND(socket, command_socket, "[enable|disable]", "Activate and deactivate socket"); +#ifdef CONFIG_STREAM_SIGNATURE +/* + * This command allows signing the contents of a data stream that passes + * through mn50/scribe. This allows critical segments of SPI readouts, + * including the haven personalization data to be verified on the server + * side as coming from a registered scribe board. (go/haven-registration) + * + * The actual interface enables capturing data (start command) on a stream + * (either SPI or UART), until stopped (sign command), at which point a + * signature is printed to the console. An "append" command is available + * to manually insert characters for testing, and should be disabled + * before release. + */ +static int command_signer(int argc, char **argv) +{ + static int initted; /* = 0; */ + char *data; + + if (!initted) { + init_signing(); + initted = 1; + } + + if (argc > 2) { + enum stream_id id; + + if (!strcasecmp("spi", argv[1])) + id = stream_spi; + else if (!strcasecmp("uart", argv[1])) + id = stream_uart; + else + return EC_ERROR_PARAM1; + + if (!strcasecmp("sign", argv[2])) { + if (argc == 3) + return sig_sign(id); + else + return EC_ERROR_PARAM3; + } else if (!strcasecmp("start", argv[2])) { + if (argc == 3) + return sig_start(id); + else + return EC_ERROR_PARAM3; + } else if (!strcasecmp("append", argv[2])) { + if (argc == 4) { + data = argv[3]; + return sig_append(id, data, strlen(data)); + } else + return EC_ERROR_PARAM3; + } else + return EC_ERROR_PARAM2; + } else + return EC_ERROR_PARAM1; + + return EC_SUCCESS; +} +DECLARE_SAFE_CONSOLE_COMMAND(signer, command_signer, + "[spi|uart] [start|append|sign] data", + "Sign data"); +#endif + void post_reboot_request(void) { /* This will never return. */ diff --git a/board/mn50/board.h b/board/mn50/board.h index 5b9c9c43af..fd63a10ff9 100644 --- a/board/mn50/board.h +++ b/board/mn50/board.h @@ -80,6 +80,7 @@ #define CONFIG_STREAM_USART #define CONFIG_STREAM_USB #define CONFIG_STREAM_USART1 +#define CONFIG_STREAM_SIGNATURE #define CONFIG_USB_PID 0x502a #define CONFIG_USB_SELF_POWERED diff --git a/board/mn50/build.mk b/board/mn50/build.mk index 2f52cfa576..ca5cc90a74 100644 --- a/board/mn50/build.mk +++ b/board/mn50/build.mk @@ -27,6 +27,7 @@ dirs-y += chip/$(CHIP)/dcrypto # Objects that we need to build board-y = board.o +board-${CONFIG_STREAM_SIGNATURE} += signing.o board-${CONFIG_USB_SPI} += usb_spi.o INCLUDE_ROOT := $(abspath ./include) diff --git a/board/mn50/ec.tasklist b/board/mn50/ec.tasklist index ac18090c67..d831b1a1ad 100644 --- a/board/mn50/ec.tasklist +++ b/board/mn50/ec.tasklist @@ -18,4 +18,4 @@ */ #define CONFIG_TASK_LIST \ TASK_ALWAYS(HOOKS, hook_task, NULL, CONFIG_STACK_SIZE) \ - TASK_ALWAYS(CONSOLE, console_task, NULL, TASK_STACK_SIZE) + TASK_ALWAYS(CONSOLE, console_task, NULL, 8192) diff --git a/board/mn50/signing.c b/board/mn50/signing.c new file mode 100644 index 0000000000..ca61f59a9d --- /dev/null +++ b/board/mn50/signing.c @@ -0,0 +1,201 @@ +/* Copyright 2017 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 "common.h" +#include "console.h" +#include "dcrypto/dcrypto.h" +#include "signing.h" +#include "task.h" + +#include "cryptoc/sha.h" + +#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args) +#define fail() cprints(CC_SYSTEM, "FAIL: %s:%d", __FILE__, __LINE__) + +static p256_int x, y, d; + +static HASH_CTX sig_sha[stream_count]; + +enum signer_states { + state_notready = 0, + state_ready, + state_started, +}; + +/* Current state of each signer stream. */ +static int signer_state[stream_count]; + +/* Bytes ingested into the hash so far. */ +static int signer_bytes[stream_count]; + +/* Human readable name of each stream. */ +static const char *signer_name[stream_count] = { + "spi", "uart" +}; + +void init_signing(void) +{ + /* Add this enum to dcrypto.h */ + enum dcrypto_appid appid = PERSO_AUTH; + struct APPKEY_CTX ctx; + uint32_t key_bytes[8]; + const uint32_t PERSO_SALT[8] = {0xd00d1e, 0xba0, 0xc0ffee}; + + /* + * Initialize signing key + */ + if (!DCRYPTO_appkey_init(appid, &ctx)) + fail(); + if (!DCRYPTO_appkey_derive(appid, PERSO_SALT, key_bytes)) + fail(); + if (!DCRYPTO_p256_key_from_bytes(&x, &y, &d, + (const uint8_t *)key_bytes)) + fail(); + + /* (x,y) = pubkey, d = privkey */ + signer_state[stream_uart] = state_ready; + signer_state[stream_spi] = state_ready; +} + +/* + * Start collecting data into a hash to be signed. + * stream_id can be either stream_uart or stream_spi. + */ +int sig_start(enum stream_id id) +{ + if ((id < 0) || (id >= stream_count)) + return EC_ERROR_PARAM1; + + if (signer_state[id] != state_ready) { + CPRINTS("Signer %d not ready", id); + return EC_ERROR_INVAL; + } + + /* Zero the hash. */ + DCRYPTO_SHA256_init(&sig_sha[id], 0); + signer_bytes[id] = 0; + signer_state[id] = state_started; + + return EC_SUCCESS; +} + +/* + * Append data into this stream's hash for future signing. + * This function is called inline with data receive, from the UART rx code + * or the SPI rx code. + * + * This can be called any time, but only hashes data when the stream + * capture is started. + */ +int sig_append(enum stream_id id, const uint8_t *data, size_t data_len) +{ + HASH_CTX *sha = &sig_sha[id]; + const uint8_t *blob = data; + size_t len = data_len; + + if ((id < 0) || (id >= stream_count)) + return EC_ERROR_PARAM1; + + if (signer_state[id] != state_started) + return EC_ERROR_INVAL; + + HASH_update(sha, blob, len); + signer_bytes[id] += len; + + return EC_SUCCESS; +} + +/* Close this stream's capture and print out the signature. */ +int sig_sign(enum stream_id id) +{ + HASH_CTX *sha = &sig_sha[id]; + p256_int r, s; /* signature tuple */ + p256_int digest; + struct drbg_ctx drbg; + + if ((id < 0) || (id >= stream_count)) + return EC_ERROR_PARAM1; + + if (signer_state[id] != state_started) { + CPRINTS("Signer %d not starter", id); + return EC_ERROR_INVAL; + } + + p256_from_bin(HASH_final(sha), &digest); + drbg_rand_init(&drbg); + + if (!dcrypto_p256_ecdsa_sign(&drbg, &d, &digest, &r, &s)) { + fail(); + return EC_ERROR_INVAL; + } + + /* Check that the signature was correctly computed */ + if (!dcrypto_p256_ecdsa_verify(&x, &y, &digest, &r, &s)) { + fail(); + return EC_ERROR_INVAL; + } + + /* Serialize r, s into output. */ + + CPRINTS("Signed %d bytes from %s.", signer_bytes[id], signer_name[id]); + CPRINTS("digest:"); + CPRINTS("%08x %08x %08x %08x", + digest.a[0], digest.a[1], digest.a[2], digest.a[3]); + CPRINTS("%08x %08x %08x %08x", + digest.a[4], digest.a[5], digest.a[6], digest.a[7]); + CPRINTS("r:"); + CPRINTS("%08x %08x %08x %08x", r.a[0], r.a[1], r.a[2], r.a[3]); + CPRINTS("%08x %08x %08x %08x", r.a[4], r.a[5], r.a[6], r.a[7]); + CPRINTS("s:"); + CPRINTS("%08x %08x %08x %08x", s.a[0], s.a[1], s.a[2], s.a[3]); + CPRINTS("%08x %08x %08x %08x", s.a[4], s.a[5], s.a[6], s.a[7]); + + signer_state[id] = state_ready; + return EC_SUCCESS; +} + + +/* + * Intercept UART data between the uart driver and usb bridge. + * + * This code is called by the ec's queue implementation, and ingests + * the UART RX queue, appends the data to the signer, then passes it + * on the the USB bridge's TX queue. + */ +void signer_written(struct consumer const *consumer, size_t count) +{ + struct signer_config const *config = + DOWNCAST(consumer, struct signer_config, consumer); + struct producer const *producer = &(config->producer); + enum stream_id id = config->id; + + /* This queue receives characters from the UART. */ + struct queue const *sig_in = consumer->queue; + + /* + * This enqueues characters into the USB bridge, + * once they have been hashed. + */ + struct queue const *sig_out = producer->queue; + char c; + + /* Copy UART rx from queue. */ + while (queue_count(sig_in) && QUEUE_REMOVE_UNITS(sig_in, &c, 1)) { + /* Append this data to the hash. */ + sig_append(id, &c, 1); + /* Pass the data to the USB bridge. */ + QUEUE_ADD_UNITS(sig_out, &c, 1); + } +} + +struct producer_ops const signer_producer_ops = { + .read = NULL, +}; + +struct consumer_ops const signer_consumer_ops = { + .written = signer_written, + .flush = NULL, +}; diff --git a/board/mn50/signing.h b/board/mn50/signing.h new file mode 100644 index 0000000000..397a78441b --- /dev/null +++ b/board/mn50/signing.h @@ -0,0 +1,73 @@ +/* Copyright 2017 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. + */ + +/* + * Add inline signing to verify records pasased through + * AUTH haven on Scribe SLT board. + */ + +#ifndef __CROS_EC_SIGNER_H +#define __CROS_EC_SIGNER_H + +#include "queue.h" +#include "queue_policies.h" + +/* + * Data signing is supported on the following inputs: + * stream_spi: data received on H1's SPI master. + * stream_uart: data received on UART1. + * + * It's possible to hash multiple streams at once. + * Currently the hash will print out as console output + * in hex format when the stream is stopped. + */ +enum stream_id { + stream_spi = 0, + stream_uart = 1, + stream_count = 2, +}; + +/* Init the signing state and derive keys. Must be called before sig_start. */ +void init_signing(void); + +/* Zero the existing hash and start processing data in the specified stream. */ +int sig_start(enum stream_id id); + +/* Append data into the specified stream, if started, otherwise do nothing. */ +int sig_append(enum stream_id id, const uint8_t *data, size_t data_len); + +/* Stop a stream and sign the hash. The signature will print to the console. */ +int sig_sign(enum stream_id id); + + +struct signer_config { + enum stream_id id; + + struct producer const producer; + struct consumer const consumer; +}; + +extern struct consumer_ops const signer_consumer_ops; +extern struct producer_ops const signer_producer_ops; + +void signer_written(struct consumer const *consumer, size_t count); + +/* + * Macro to set up inline signer. + * Usage: SIGNER_CONFIG(name, stream_id, tx queue, rx queue) + */ +#define SIGNER_CONFIG(NAME, ID, RX_QUEUE, TX_QUEUE) \ + struct signer_config const NAME = { \ + .id = ID, \ + .consumer = { \ + .queue = &TX_QUEUE, \ + .ops = &signer_consumer_ops, \ + }, \ + .producer = { \ + .queue = &RX_QUEUE, \ + .ops = &signer_producer_ops, \ + }, \ + } +#endif diff --git a/board/mn50/usb_spi.c b/board/mn50/usb_spi.c index 9dffeeae99..b44a450f91 100644 --- a/board/mn50/usb_spi.c +++ b/board/mn50/usb_spi.c @@ -7,6 +7,7 @@ #include "gpio.h" #include "hooks.h" #include "registers.h" +#include "signing.h" #include "spi.h" #include "system.h" #include "timer.h" @@ -67,6 +68,12 @@ int usb_spi_interface(struct usb_spi_config const *config, else disable_socket(); break; + case USB_SPI_REQ_SIGNING_START: + sig_start(stream_spi); + break; + case USB_SPI_REQ_SIGNING_SIGN: + sig_sign(stream_spi); + break; case USB_SPI_REQ_ENABLE_AP: case USB_SPI_REQ_ENABLE: CPRINTS("ERROR: Must specify target"); diff --git a/chip/g/dcrypto/app_key.c b/chip/g/dcrypto/app_key.c index e2b709f3c0..173bc9d214 100644 --- a/chip/g/dcrypto/app_key.c +++ b/chip/g/dcrypto/app_key.c @@ -49,6 +49,16 @@ const struct { 0xcd375bcd, 0x8065e8cc, 0xc892ed69, 0x72436c7d } }, +#ifdef CONFIG_STREAM_SIGNATURE + { + /* This key signs data from H1's configured by mn50/scribe. */ + "PERSO_AUTH", + { + 0x2019da34, 0xf1a01a13, 0x0fb9f73f, 0xf2e85f76, + 0x5ecb7690, 0x09f732c9, 0xe540bf14, 0xcc46799a + } + }, +#endif }; int DCRYPTO_appkey_init(enum dcrypto_appid appid, struct APPKEY_CTX *ctx) diff --git a/chip/g/dcrypto/dcrypto.h b/chip/g/dcrypto/dcrypto.h index 5c643382c6..e964288f84 100644 --- a/chip/g/dcrypto/dcrypto.h +++ b/chip/g/dcrypto/dcrypto.h @@ -271,6 +271,7 @@ enum dcrypto_appid { U2F_ATTEST = 2, U2F_ORIGIN = 3, U2F_WRAP = 4, + PERSO_AUTH = 5, /* This enum value should not exceed 7. */ }; diff --git a/chip/g/usart.c b/chip/g/usart.c index 3e5493c579..598b4d4ed9 100644 --- a/chip/g/usart.c +++ b/chip/g/usart.c @@ -8,25 +8,63 @@ #include "uartn.h" #include "usart.h" #include "usb-stream.h" +#ifdef CONFIG_STREAM_SIGNATURE +#include "signing.h" +#endif #define USE_UART_INTERRUPTS (!(defined(CONFIG_CUSTOMIZED_RO) && \ defined(SECTION_IS_RO))) #define QUEUE_SIZE 64 + #ifdef CONFIG_STREAM_USART1 struct usb_stream_config const ap_usb; struct usart_config const ap_uart; -static struct queue const ap_uart_to_usb = +#ifdef CONFIG_STREAM_SIGNATURE +/* + * This code adds the ability to capture UART data received, and + * sign it with H1's key. This allows the log output to be verified + * as actual UART output from this board. + * + * This functionality is enabled by redirecting the UART receive queue + * to feed into the signing module rather than the usb tx. After being + * added to the running hash, the data is then pushed by the signer + * into the usb tx queue. + */ +struct signer_config const sig; +static struct queue const ap_uart_output = + QUEUE_DIRECT(QUEUE_SIZE, uint8_t, ap_uart.producer, sig.consumer); +static struct queue const sig_to_usb = + QUEUE_DIRECT(QUEUE_SIZE, uint8_t, sig.producer, ap_usb.consumer); + +SIGNER_CONFIG(sig, stream_uart, sig_to_usb, ap_uart_output); + +#else /* Not CONFIG_STREAM_SIGNATURE */ +static struct queue const ap_uart_output = QUEUE_DIRECT(QUEUE_SIZE, uint8_t, ap_uart.producer, ap_usb.consumer); +#endif + static struct queue const ap_usb_to_uart = QUEUE_DIRECT(QUEUE_SIZE, uint8_t, ap_usb.producer, ap_uart.consumer); +/* + * AP UART data is sent to the ap_uart_output queue, and received from + * the ap_usb_to_uart queue. The ap_uart_output queue is received by the + * USB bridge, or if a signer is enabled, received by the signer, which then + * passes the data to the USB bridge after processing it. + */ USART_CONFIG(ap_uart, UART_AP, - ap_uart_to_usb, + ap_uart_output, ap_usb_to_uart); +/* + * The UART USB bridge receives character data from the UART's queue, + * unless signing is enabled, in which case it receives data from the + * signer's queue, after the signer has received it from the UART and + * processed it. + */ USB_STREAM_CONFIG(ap_usb, USB_IFACE_AP, USB_STR_AP_NAME, @@ -34,8 +72,12 @@ USB_STREAM_CONFIG(ap_usb, USB_MAX_PACKET_SIZE, USB_MAX_PACKET_SIZE, ap_usb_to_uart, - ap_uart_to_usb) +#ifdef CONFIG_STREAM_SIGNATURE + sig_to_usb) +#else + ap_uart_output) #endif +#endif /* CONFIG_STREAM_USART1 */ #ifdef CONFIG_STREAM_USART2 struct usb_stream_config const ec_usb; diff --git a/chip/g/usb_spi.c b/chip/g/usb_spi.c index 34e23bc4c5..a5f6ddd175 100644 --- a/chip/g/usb_spi.c +++ b/chip/g/usb_spi.c @@ -14,6 +14,10 @@ #include "usb_spi.h" #include "util.h" +#ifdef CONFIG_STREAM_SIGNATURE +#include "signing.h" +#endif + #define CPUTS(outstr) cputs(CC_USB, outstr) #define CPRINTS(format, args...) cprints(CC_USB, format, ## args) @@ -40,6 +44,15 @@ static uint16_t usb_spi_read_packet(struct usb_spi_config const *config) static void usb_spi_write_packet(struct usb_spi_config const *config, uint8_t count) { +#ifdef CONFIG_STREAM_SIGNATURE + /* + * This hook allows mn50 to sign SPI data read from newly + * manufactured H1 devieces. The data is added to a running + * hash until a completion message is received. + */ + sig_append(stream_spi, config->buffer, count); +#endif + QUEUE_ADD_UNITS(config->tx_queue, config->buffer, count); } diff --git a/chip/g/usb_spi.h b/chip/g/usb_spi.h index b852310f1e..72364ab469 100644 --- a/chip/g/usb_spi.h +++ b/chip/g/usb_spi.h @@ -70,6 +70,8 @@ enum usb_spi_request { USB_SPI_REQ_RESET = 0x0005, USB_SPI_REQ_BOOT_CFG = 0x0006, USB_SPI_REQ_SOCKET = 0x0007, + USB_SPI_REQ_SIGNING_START = 0x0008, + USB_SPI_REQ_SIGNING_SIGN = 0x0009, }; /* USB SPI device bitmasks */ diff --git a/include/config.h b/include/config.h index 9023a41502..5a5e232458 100644 --- a/include/config.h +++ b/include/config.h @@ -2842,6 +2842,14 @@ /* USB Power monitoring interface config */ #undef CONFIG_USB_POWER +/*****************************************************************************/ +/* + * USB stream signing config. This allows data read over UART or SPI + * to have a signature generated that can be used to validate the data + * offline based on H1's registered key. Used by mn50. + */ +#undef CONFIG_STREAM_SIGNATURE + /*****************************************************************************/ |