summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Boichat <drinkcat@google.com>2017-06-13 10:29:06 +0800
committerchrome-bot <chrome-bot@chromium.org>2017-06-15 07:07:40 -0700
commit77e7913b696e16b7fae72e5210f940f8213ff1b5 (patch)
tree318738eb321302dd2a0e04a51c279729cbc4badd
parent7bfbedc2662dcab49d072db9885304df483de37c (diff)
downloadchrome-ec-77e7913b696e16b7fae72e5210f940f8213ff1b5.tar.gz
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 <drinkcat@chromium.org> Tested-by: Nicolas Boichat <drinkcat@chromium.org> Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--common/usb_update.c93
-rw-r--r--include/compile_time_macros.h2
-rw-r--r--include/config.h3
-rw-r--r--include/update_fw.h22
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);