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>
This commit is contained in:
Nick Sanders
2017-06-27 17:58:45 -07:00
committed by chrome-bot
parent 366c36c8f1
commit 02045eb040
13 changed files with 431 additions and 6 deletions

View File

@@ -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. */

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

201
board/mn50/signing.c Normal file
View File

@@ -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,
};

73
board/mn50/signing.h Normal file
View File

@@ -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

View File

@@ -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");

View File

@@ -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)

View File

@@ -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. */
};

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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 */

View File

@@ -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
/*****************************************************************************/