From 77e7913b696e16b7fae72e5210f940f8213ff1b5 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 13 Jun 2017 10:29:06 +0800 Subject: usb_update: Add handler for pairing challenge command Handle UPDATE_EXTRA_CMD_PAIR_CHALLENGE command, where the lid sends a random x25519 public key, and nonce, and the base replies with its own (stable) x25519 public key, and computes a shared secret using its private key to verify its identity. BRANCH=none BUG=b:38486828 TEST=Flash hammer, ./usb_updater2 -c always reports the same device public key, and authenticator is correct. Change-Id: Ida60ffa7476794ee92669951c740dbe35950fb9c Reviewed-on: https://chromium-review.googlesource.com/532475 Commit-Ready: Nicolas Boichat Tested-by: Nicolas Boichat Reviewed-by: Vincent Palatin --- common/usb_update.c | 93 ++++++++++++++++++++++++++++++++++++++----- include/compile_time_macros.h | 2 + include/config.h | 3 ++ include/update_fw.h | 22 ++++++++++ 4 files changed, 109 insertions(+), 11 deletions(-) diff --git a/common/usb_update.c b/common/usb_update.c index 8ec5a5fd89..440f4c3711 100644 --- a/common/usb_update.c +++ b/common/usb_update.c @@ -7,12 +7,14 @@ #include "common.h" #include "console.h" #include "consumer.h" +#include "curve25519.h" #include "extension.h" #include "flash.h" #include "queue_policies.h" #include "host_command.h" #include "rollback.h" #include "rwsig.h" +#include "sha256.h" #include "system.h" #include "update_fw.h" #include "usb-stream.h" @@ -78,6 +80,61 @@ static uint8_t block_buffer[sizeof(struct update_command) + static uint32_t block_size; static uint32_t block_index; +#ifdef CONFIG_USB_PAIRING +#define KEY_CONTEXT "device-identity" + +static int pair_challenge(struct pair_challenge *challenge) +{ + uint8_t response; + + /* Scratchpad for device secret and x25519 public/shared key. */ + uint8_t tmp[32]; + BUILD_ASSERT(sizeof(tmp) >= X25519_PUBLIC_VALUE_LEN); + BUILD_ASSERT(sizeof(tmp) >= X25519_PRIVATE_KEY_LEN); + BUILD_ASSERT(sizeof(tmp) >= CONFIG_ROLLBACK_SECRET_SIZE); + + /* Scratchpad for device_private and authenticator. */ + uint8_t tmp2[32]; + BUILD_ASSERT(sizeof(tmp2) >= X25519_PRIVATE_KEY_LEN); + BUILD_ASSERT(sizeof(tmp2) >= SHA256_DIGEST_SIZE); + + /* tmp = device_secret */ + if (rollback_get_secret(tmp) != EC_SUCCESS) { + response = EC_RES_UNAVAILABLE; + QUEUE_ADD_UNITS(&update_to_usb, &response, sizeof(response)); + return 1; + } + + /* + * Nothing can fail from now on, let's push data to the queue as soon as + * possible to save some temporary variables. + */ + response = EC_RES_SUCCESS; + QUEUE_ADD_UNITS(&update_to_usb, &response, sizeof(response)); + + /* + * tmp2 = device_private + * = HMAC_SHA256(device_secret, "device-identity") + */ + hmac_SHA256(tmp2, tmp, CONFIG_ROLLBACK_SECRET_SIZE, + KEY_CONTEXT, sizeof(KEY_CONTEXT) - 1); + + /* tmp = device_public = x25519(device_private, x25519_base_point) */ + X25519_public_from_private(tmp, tmp2); + QUEUE_ADD_UNITS(&update_to_usb, tmp, sizeof(tmp)); + + /* tmp = shared_secret = x25519(device_private, host_public) */ + X25519(tmp, tmp2, challenge->host_public); + + /* tmp2 = authenticator = HMAC_SHA256(shared_secret, nonce) */ + hmac_SHA256(tmp2, tmp, sizeof(tmp), + challenge->nonce, sizeof(challenge->nonce)); + QUEUE_ADD_UNITS(&update_to_usb, tmp2, + member_size(struct pair_challenge_response, authenticator)); + return 1; +} +#endif + /* * Fetches a transfer start frame from the queue. This can be either an update * start frame (block_size = 0, all of cmd = 0), or the beginning of a frame @@ -144,6 +201,8 @@ static int try_vendor_command(struct consumer const *consumer, size_t count) enum update_extra_command subcommand; uint8_t response; size_t response_size = sizeof(response); + int __attribute__((unused)) header_size; + int __attribute__((unused)) data_count; /* looks good, let's process it. */ rv = 1; @@ -153,6 +212,13 @@ static int try_vendor_command(struct consumer const *consumer, size_t count) subcommand = be16toh(*((uint16_t *)(cmd_buffer + 1))); + /* + * header size: update frame header + 2 bytes for subcommand + * data_count: Some commands take in extra data as parameter + */ + header_size = sizeof(*cmd_buffer) + sizeof(uint16_t); + data_count = count - header_size; + switch (subcommand) { case UPDATE_EXTRA_CMD_IMMEDIATE_RESET: CPRINTS("Rebooting!\n\n\n"); @@ -205,25 +271,30 @@ static int try_vendor_command(struct consumer const *consumer, size_t count) #ifdef CONFIG_ROLLBACK_SECRET_SIZE #ifdef CONFIG_ROLLBACK_UPDATE case UPDATE_EXTRA_CMD_INJECT_ENTROPY: { - /* - * Check that we are provided enough data (header + - * 2 bytes subcommand + secret length). - */ - int header_size = sizeof(*cmd_buffer) + 2; - int entropy_count = count - header_size; - - if (entropy_count < CONFIG_ROLLBACK_SECRET_SIZE) { + if (data_count < CONFIG_ROLLBACK_SECRET_SIZE) { CPRINTS("Entropy too short"); response = EC_RES_INVALID_PARAM; break; } - CPRINTS("Adding %db of entropy", entropy_count); + CPRINTS("Adding %db of entropy", data_count); /* Add the entropy to secret. */ - rollback_add_entropy(buffer + header_size, - entropy_count); + rollback_add_entropy(buffer + header_size, data_count); break; } +#endif /* CONFIG_ROLLBACK_UPDATE */ +#ifdef CONFIG_USB_PAIRING + case UPDATE_EXTRA_CMD_PAIR_CHALLENGE: { + if (data_count < sizeof(struct pair_challenge)) { + CPRINTS("Challenge data too short"); + response = EC_RES_INVALID_PARAM; + break; + } + + /* pair_challenge takes care of answering */ + return pair_challenge((struct pair_challenge *) + (buffer + header_size)); + } #endif #endif /* CONFIG_ROLLBACK_SECRET_SIZE */ #endif /* CONFIG_ROLLBACK */ diff --git a/include/compile_time_macros.h b/include/compile_time_macros.h index f25ffd8407..b1e627d617 100644 --- a/include/compile_time_macros.h +++ b/include/compile_time_macros.h @@ -27,6 +27,8 @@ #define offsetof(type, member) __builtin_offsetof(type, member) #endif +#define member_size(type, member) sizeof(((type *)0)->member) + #define __visible __attribute__((externally_visible)) #endif /* __CROS_EC_COMPILE_TIME_MACROS_H */ diff --git a/include/config.h b/include/config.h index 619cc53e3d..6ec29586cd 100644 --- a/include/config.h +++ b/include/config.h @@ -2762,6 +2762,9 @@ /* A different config for the same update. TODO(vbendeb): dedup these */ #undef CONFIG_USB_UPDATE +/* Add support for pairing over the USB update interface. */ +#undef CONFIG_USB_PAIRING + /* PDU size for fw update over USB (or TPM). */ #define CONFIG_UPDATE_PDU_SIZE 1024 diff --git a/include/update_fw.h b/include/update_fw.h index 72e278fcc6..5218d07bdf 100644 --- a/include/update_fw.h +++ b/include/update_fw.h @@ -166,6 +166,28 @@ enum update_extra_command { UPDATE_EXTRA_CMD_PAIR_CHALLENGE = 6, }; +/* + * Pair challenge (from host), note that the packet, with header, must fit + * in a single USB packet (64 bytes), so its maximum length is 50 bytes. + */ +struct pair_challenge { + uint8_t host_public[32]; /* X22519 public key from host */ + uint8_t nonce[16]; /* nonce to be used for HMAC */ +}; + +/* + * Pair challenge response (from device). + */ +struct pair_challenge_response { + uint8_t status; /* = EC_RES_SUCCESS */ + uint8_t device_public[32]; /* X22519 device public key of device */ + /* + * Truncated output of + * HMAC_SHA256(x25519(device_private, host_public), nonce) + */ + uint8_t authenticator[16]; +} __packed; + void fw_update_command_handler(void *body, size_t cmd_size, size_t *response_size); -- cgit v1.2.1