summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2017-06-21 13:10:57 -0700
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2017-12-16 01:12:33 +0000
commiteef5c0f395adfc56048683196f45b613c2cc416f (patch)
tree5c41ae3bf1230ddb98af4e133bb08939b04a2ce0
parent6e08b160f69df172387ddb5ecc213b7ef362b13c (diff)
downloadchrome-ec-eef5c0f395adfc56048683196f45b613c2cc416f.tar.gz
common: Add RMA reset auth challenge-response crypto
RMA auth uses X25519 to generate a relatively small challenge and response. Currently, nothing calls the rma_auth code. We'll need console and TPM vendor commands to do so. Conflicts: include/config.h BUG=b:37952913 BRANCH=none TEST=make buildall Change-Id: Iec7f2d0e3dc8243f79b009ead16bb3ba9f1bef9d Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/544184 (cherry picked from commit 282765fdd409fd16ed1e092e5d7fee8de5af7a5a) Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/828662
-rw-r--r--common/build.mk1
-rw-r--r--common/rma_auth.c123
-rw-r--r--include/config.h6
-rw-r--r--include/rma_auth.h80
-rw-r--r--test/build.mk2
-rw-r--r--test/rma_auth.c199
-rw-r--r--test/rma_auth.tasklist17
-rw-r--r--test/test_config.h10
8 files changed, 438 insertions, 0 deletions
diff --git a/common/build.mk b/common/build.mk
index ae48325949..1c1b2415d7 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -78,6 +78,7 @@ common-$(CONFIG_POWER_BUTTON_X86)+=power_button_x86.o
common-$(CONFIG_PSTORE)+=pstore_commands.o
common-$(CONFIG_PWM)+=pwm.o
common-$(CONFIG_PWM_KBLIGHT)+=pwm_kblight.o
+common-$(CONFIG_RMA_AUTH)+=rma_auth.o
common-$(CONFIG_RSA)+=rsa.o
common-$(CONFIG_RWSIG)+=rwsig.o
common-$(CONFIG_MATH_UTIL)+=math_util.o
diff --git a/common/rma_auth.c b/common/rma_auth.c
new file mode 100644
index 0000000000..f178524927
--- /dev/null
+++ b/common/rma_auth.c
@@ -0,0 +1,123 @@
+/* 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.
+ */
+
+/* RMA authorization challenge-response */
+
+#include "common.h"
+#include "base32.h"
+#include "chip/g/board_id.h"
+#include "curve25519.h"
+#include "rma_auth.h"
+#include "sha256.h"
+#include "system.h"
+#include "timer.h"
+#include "util.h"
+
+/* Minimum time since system boot or last challenge before making a new one */
+#define CHALLENGE_INTERVAL (10 * SECOND)
+
+/* Number of tries to properly enter auth code */
+#define MAX_AUTHCODE_TRIES 3
+
+/* Server public key and key ID */
+static const uint8_t server_pub_key[32] = CONFIG_RMA_AUTH_SERVER_PUBLIC_KEY;
+static const uint8_t server_key_id = CONFIG_RMA_AUTH_SERVER_KEY_ID;
+
+static char challenge[RMA_CHALLENGE_BUF_SIZE];
+static char authcode[RMA_AUTHCODE_BUF_SIZE];
+static int tries_left;
+static uint64_t last_challenge_time;
+
+/**
+ * Create a new RMA challenge/response
+ *
+ * @return EC_SUCCESS, EC_ERROR_TIMEOUT if too soon since the last challenge,
+ * or other non-zero error code.
+ */
+int rma_create_challenge(void)
+{
+ uint8_t temp[32]; /* Private key or HMAC */
+ uint8_t secret[32];
+ struct rma_challenge c;
+ struct board_id bid;
+ uint8_t *device_id;
+ uint8_t *cptr = (uint8_t *)&c;
+ uint64_t t;
+
+ /* Clear the current challenge and authcode, if any */
+ memset(challenge, 0, sizeof(challenge));
+ memset(authcode, 0, sizeof(authcode));
+
+ /* Rate limit challenges */
+ t = get_time().val;
+ if (t - last_challenge_time < CHALLENGE_INTERVAL)
+ return EC_ERROR_TIMEOUT;
+ last_challenge_time = t;
+
+ memset(&c, 0, sizeof(c));
+ c.version_key_id = RMA_CHALLENGE_VKID_BYTE(
+ RMA_CHALLENGE_VERSION, server_key_id);
+
+ if (read_board_id(&bid))
+ return EC_ERROR_UNKNOWN;
+ memcpy(c.board_id, &bid.type, sizeof(c.board_id));
+
+ if (system_get_chip_unique_id(&device_id) != sizeof(c.device_id))
+ return EC_ERROR_UNKNOWN;
+ memcpy(c.device_id, device_id, sizeof(c.device_id));
+
+ /* Calculate a new ephemeral key pair */
+ X25519_keypair(c.device_pub_key, temp);
+
+ /* Encode the challenge */
+ if (base32_encode(challenge, sizeof(challenge), cptr, 8 * sizeof(c), 9))
+ return EC_ERROR_UNKNOWN;
+
+ /* Calculate the shared secret */
+ X25519(secret, temp, server_pub_key);
+
+ /*
+ * Auth code is a truncated HMAC of the ephemeral public key, BoardID,
+ * and DeviceID. Those are all in the right order in the challenge
+ * struct, after the version/key id byte.
+ */
+ hmac_SHA256(temp, secret, sizeof(secret), cptr + 1, sizeof(c) - 1);
+ if (base32_encode(authcode, sizeof(authcode), temp,
+ RMA_AUTHCODE_CHARS * 5, 0))
+ return EC_ERROR_UNKNOWN;
+
+ tries_left = MAX_AUTHCODE_TRIES;
+ return EC_SUCCESS;
+}
+
+const char *rma_get_challenge(void)
+{
+ return challenge;
+}
+
+int rma_try_authcode(const char *code)
+{
+ int rv = EC_ERROR_INVAL;
+
+ /* Fail if out of tries */
+ if (!tries_left)
+ return EC_ERROR_ACCESS_DENIED;
+
+ if (safe_memcmp(authcode, code, RMA_AUTHCODE_CHARS)) {
+ /* Mismatch */
+ tries_left--;
+ } else {
+ rv = EC_SUCCESS;
+ tries_left = 0;
+ }
+
+ /* Clear challenge and response if out of tries */
+ if (!tries_left) {
+ memset(challenge, 0, sizeof(challenge));
+ memset(authcode, 0, sizeof(authcode));
+ }
+
+ return rv;
+}
diff --git a/include/config.h b/include/config.h
index 81141eaa3a..93721c9baa 100644
--- a/include/config.h
+++ b/include/config.h
@@ -1764,6 +1764,12 @@
/* Support IR357x Link voltage regulator debugging / reprogramming */
#undef CONFIG_REGULATOR_IR357X
+/* Support RMA auth challenge-response */
+#undef CONFIG_RMA_AUTH
+/* If that's defined, the server public key and ID must also be defined */
+#undef CONFIG_RMA_AUTH_SERVER_PUBLIC_KEY /* 32 bytes: {0xNN, 0xNN, ... 0xNN} */
+#undef CONFIG_RMA_AUTH_SERVER_KEY_ID /* 6-bit key ID, 0xMM */
+
/* Support verifying 2048-bit RSA signature */
#undef CONFIG_RSA
diff --git a/include/rma_auth.h b/include/rma_auth.h
new file mode 100644
index 0000000000..db39468595
--- /dev/null
+++ b/include/rma_auth.h
@@ -0,0 +1,80 @@
+/* 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.
+ */
+
+/* RMA challenge-response */
+
+#ifndef __CROS_EC_RMA_AUTH_H
+#define __CROS_EC_RMA_AUTH_H
+
+#include <stdint.h>
+
+/* Test server public and private keys */
+#define RMA_TEST_SERVER_PUBLIC_KEY { \
+ 0x03, 0xae, 0x2d, 0x2c, 0x06, 0x23, 0xe0, 0x73, \
+ 0x0d, 0xd3, 0xb7, 0x92, 0xac, 0x54, 0xc5, 0xfd, \
+ 0x7e, 0x9c, 0xf0, 0xa8, 0xeb, 0x7e, 0x2a, 0xb5, \
+ 0xdb, 0xf4, 0x79, 0x5f, 0x8a, 0x0f, 0x28, 0x3f}
+#define RMA_TEST_SERVER_PRIVATE_KEY { \
+ 0x47, 0x3b, 0xa5, 0xdb, 0xc4, 0xbb, 0xd6, 0x77, \
+ 0x20, 0xbd, 0xd8, 0xbd, 0xc8, 0x7a, 0xbb, 0x07, \
+ 0x03, 0x79, 0xba, 0x7b, 0x52, 0x8c, 0xec, 0xb3, \
+ 0x4d, 0xaa, 0x69, 0xf5, 0x65, 0xb4, 0x31, 0xad}
+#define RMA_TEST_SERVER_KEY_ID 0x10
+
+/* Current challenge protocol version */
+#define RMA_CHALLENGE_VERSION 0
+
+/* Getters and setters for version_key_id byte */
+#define RMA_CHALLENGE_VKID_BYTE(version, keyid) \
+ (((version) << 6) | ((keyid) & 0x3f))
+#define RMA_CHALLENGE_GET_VERSION(vkidbyte) ((vkidbyte) >> 6)
+#define RMA_CHALLENGE_GET_KEY_ID(vkidbyte) ((vkidbyte) & 0x3f)
+
+struct __packed rma_challenge {
+ /* Top 2 bits are protocol version; bottom 6 are server KeyID */
+ uint8_t version_key_id;
+
+ /* Ephemeral public key from device */
+ uint8_t device_pub_key[32];
+
+ /* Board ID (.type) */
+ uint8_t board_id[4];
+
+ /* Device ID */
+ uint8_t device_id[8];
+};
+
+/* Size of encoded challenge and response, and buffer sizes to hold them */
+#define RMA_CHALLENGE_CHARS 80
+#define RMA_CHALLENGE_BUF_SIZE (RMA_CHALLENGE_CHARS + 1)
+
+#define RMA_AUTHCODE_CHARS 8
+#define RMA_AUTHCODE_BUF_SIZE (RMA_AUTHCODE_CHARS + 1)
+
+/**
+ * Create a new RMA challenge/response
+ *
+ * @return EC_SUCCESS, EC_ERROR_TIMEOUT if too soon since the last challenge,
+ * or other non-zero error code.
+ */
+int rma_create_challenge(void);
+
+/**
+ * Get the current challenge string
+ *
+ * @return a pointer to the challenge string. String will be empty if there
+ * is no active challenge.
+ */
+const char *rma_get_challenge(void);
+
+/**
+ * Try a RMA authorization code
+ *
+ * @param code Authorization code to try
+ * @return EC_SUCCESS if the response was correct, or non-zero error code.
+ */
+int rma_try_authcode(const char *code);
+
+#endif
diff --git a/test/build.mk b/test/build.mk
index b114794dbf..ab9804a34c 100644
--- a/test/build.mk
+++ b/test/build.mk
@@ -60,6 +60,7 @@ test-list-host += nvmem_vars
test-list-host += pingpong
test-list-host += power_button
test-list-host += queue
+test-list-host += rma_auth
test-list-host += rsa
test-list-host += rsa3
test-list-host += sbs_charging_v2
@@ -105,6 +106,7 @@ pingpong-y=pingpong.o
power_button-y=power_button.o
powerdemo-y=powerdemo.o
queue-y=queue.o
+rma_auth-y=rma_auth.o
rsa-y=rsa.o
rsa3-y=rsa.o
sbs_charging-y=sbs_charging.o
diff --git a/test/rma_auth.c b/test/rma_auth.c
new file mode 100644
index 0000000000..d833a2c33b
--- /dev/null
+++ b/test/rma_auth.c
@@ -0,0 +1,199 @@
+/* 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.
+ *
+ * Test RMA auth challenge/response
+ */
+
+#include <stdio.h>
+#include "common.h"
+#include "chip/g/board_id.h"
+#include "curve25519.h"
+#include "base32.h"
+#include "sha256.h"
+#include "rma_auth.h"
+#include "test_util.h"
+#include "timer.h"
+#include "util.h"
+
+/* Dummy implementations for testing */
+static uint8_t dummy_board_id[4] = {'Z', 'Z', 'C', 'R'};
+static uint8_t dummy_device_id[8] = {'T', 'H', 'X', 1, 1, 3, 8, 0xfe};
+static int server_protocol_version = RMA_CHALLENGE_VERSION;
+static uint8_t server_private_key[32] = RMA_TEST_SERVER_PRIVATE_KEY;
+static int server_key_id = RMA_TEST_SERVER_KEY_ID;
+
+void rand_bytes(void *buffer, size_t len)
+{
+ FILE *f = fopen("/dev/urandom", "rb");
+
+ assert(f);
+ fread(buffer, 1, len, f);
+ fclose(f);
+}
+
+int read_board_id(struct board_id *id)
+{
+ memcpy(&id->type, dummy_board_id, sizeof(id->type));
+ id->type_inv = ~id->type;
+ id->flags = 0xFF00;
+ return EC_SUCCESS;
+}
+
+int system_get_chip_unique_id(uint8_t **id)
+{
+ *id = dummy_device_id;
+ return sizeof(dummy_device_id);
+}
+
+/**
+ * Simulate the server side of a RMA challenge-response.
+ *
+ * @param out_auth_code Buffer for generated authorization code
+ * (must be >= CR50_AUTH_CODE_CHARS + 1 chars)
+ * @param challenge Challenge from device
+ * @return 0 if success, non-zero if error.
+ */
+int rma_server_side(char *out_auth_code, const char *challenge)
+{
+ int version, key_id;
+ uint32_t device_id[2];
+ uint8_t secret[32];
+ uint8_t hmac[32];
+ struct rma_challenge c;
+ uint8_t *cptr = (uint8_t *)&c;
+
+ /* Convert the challenge back into binary */
+ if (base32_decode(cptr, 8 * sizeof(c), challenge, 9) != 8 * sizeof(c)) {
+ printf("Error decoding challenge\n");
+ return -1;
+ }
+
+ version = RMA_CHALLENGE_GET_VERSION(c.version_key_id);
+ if (version != server_protocol_version) {
+ printf("Unsupported challenge version %d\n", version);
+ return -1;
+ }
+
+ key_id = RMA_CHALLENGE_GET_KEY_ID(c.version_key_id);
+
+ printf("\nChallenge: %s\n", challenge);
+ printf(" Version: %d\n", version);
+ printf(" Server KeyID: %d\n", key_id);
+ printf(" BoardID: %c%c%c%c\n",
+ isprint(c.board_id[0]) ? c.board_id[0] : '?',
+ isprint(c.board_id[1]) ? c.board_id[1] : '?',
+ isprint(c.board_id[2]) ? c.board_id[2] : '?',
+ isprint(c.board_id[3]) ? c.board_id[3] : '?');
+
+ memcpy(device_id, c.device_id, sizeof(device_id));
+ printf(" DeviceID: 0x%08x 0x%08x\n", device_id[0], device_id[1]);
+
+ if (key_id != server_key_id) {
+ printf("Unsupported KeyID %d\n", key_id);
+ return -1;
+ }
+
+ /*
+ * Make sure the current user is authorized to reset this board.
+ *
+ * Since this is just a test, here we'll just make sure the BoardID
+ * and DeviceID match what we expected.
+ */
+ if (memcmp(c.board_id, dummy_board_id, sizeof(c.board_id))) {
+ printf("BoardID mismatch\n");
+ return -1;
+ }
+ if (memcmp(c.device_id, dummy_device_id, sizeof(c.device_id))) {
+ printf("DeviceID mismatch\n");
+ return -1;
+ }
+
+ /* Calculate the shared secret */
+ X25519(secret, server_private_key, c.device_pub_key);
+
+ /*
+ * Auth code is a truncated HMAC of the ephemeral public key, BoardID,
+ * and DeviceID.
+ */
+ hmac_SHA256(hmac, secret, sizeof(secret), cptr + 1, sizeof(c) - 1);
+ if (base32_encode(out_auth_code, RMA_AUTHCODE_BUF_SIZE,
+ hmac, RMA_AUTHCODE_CHARS * 5, 0)) {
+ printf("Error encoding auth code\n");
+ return -1;
+ }
+ printf("Authcode: %s\n", out_auth_code);
+
+ return 0;
+};
+
+#define FORCE_TIME(t) { ts.val = (t); force_time(ts); }
+
+static int test_rma_auth(void)
+{
+ const char *challenge;
+ char authcode[RMA_AUTHCODE_BUF_SIZE];
+ timestamp_t ts;
+
+ /* Test rate limiting */
+ FORCE_TIME(9 * SECOND);
+ TEST_ASSERT(rma_create_challenge() == EC_ERROR_TIMEOUT);
+ TEST_ASSERT(rma_try_authcode("Bad") == EC_ERROR_ACCESS_DENIED);
+ TEST_ASSERT(strlen(rma_get_challenge()) == 0);
+
+ FORCE_TIME(10 * SECOND);
+ TEST_ASSERT(rma_create_challenge() == 0);
+ TEST_ASSERT(strlen(rma_get_challenge()) == RMA_CHALLENGE_CHARS);
+
+ /* Test using up tries */
+ TEST_ASSERT(rma_try_authcode("Bad") == EC_ERROR_INVAL);
+ TEST_ASSERT(strlen(rma_get_challenge()) == RMA_CHALLENGE_CHARS);
+ TEST_ASSERT(rma_try_authcode("BadCodeZ") == EC_ERROR_INVAL);
+ TEST_ASSERT(strlen(rma_get_challenge()) == RMA_CHALLENGE_CHARS);
+ TEST_ASSERT(rma_try_authcode("BadLongCode") == EC_ERROR_INVAL);
+ /* Out of tries now */
+ TEST_ASSERT(strlen(rma_get_challenge()) == 0);
+ TEST_ASSERT(rma_try_authcode("Bad") == EC_ERROR_ACCESS_DENIED);
+
+ FORCE_TIME(19 * SECOND);
+ TEST_ASSERT(rma_create_challenge() == EC_ERROR_TIMEOUT);
+ TEST_ASSERT(strlen(rma_get_challenge()) == 0);
+
+ FORCE_TIME(21 * SECOND);
+ TEST_ASSERT(rma_create_challenge() == 0);
+ challenge = rma_get_challenge();
+ TEST_ASSERT(strlen(challenge) == RMA_CHALLENGE_CHARS);
+ TEST_ASSERT(rma_server_side(authcode, challenge) == 0);
+ TEST_ASSERT(rma_try_authcode(authcode) == EC_SUCCESS);
+
+ /*
+ * Make sure the server-side checks for fields work. That is, test
+ * our ability to test those fields...
+ */
+ server_protocol_version++;
+ TEST_ASSERT(rma_server_side(authcode, challenge) == -1);
+ server_protocol_version--;
+
+ server_key_id++;
+ TEST_ASSERT(rma_server_side(authcode, challenge) == -1);
+ server_key_id--;
+
+ dummy_board_id[0]++;
+ TEST_ASSERT(rma_server_side(authcode, challenge) == -1);
+ dummy_board_id[0]--;
+
+ dummy_device_id[0]++;
+ TEST_ASSERT(rma_server_side(authcode, challenge) == -1);
+ dummy_device_id[0]--;
+
+ return EC_SUCCESS;
+}
+
+void run_test(void)
+{
+ test_reset();
+
+ RUN_TEST(test_rma_auth);
+
+ test_print_result();
+}
diff --git a/test/rma_auth.tasklist b/test/rma_auth.tasklist
new file mode 100644
index 0000000000..e241aab4bb
--- /dev/null
+++ b/test/rma_auth.tasklist
@@ -0,0 +1,17 @@
+/* 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.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_TEST(n, r, d, s) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST /* No test task */
diff --git a/test/test_config.h b/test/test_config.h
index 5079b173fd..b208b4a009 100644
--- a/test/test_config.h
+++ b/test/test_config.h
@@ -58,6 +58,16 @@
#define CONFIG_TABLET_MODE
#endif
+#ifdef TEST_RMA_AUTH
+#define CONFIG_BASE32
+#define CONFIG_CURVE25519
+#define CONFIG_RMA_AUTH
+#define CONFIG_RMA_AUTH_SERVER_PUBLIC_KEY RMA_TEST_SERVER_PUBLIC_KEY
+#define CONFIG_RMA_AUTH_SERVER_KEY_ID RMA_TEST_SERVER_KEY_ID
+#define CONFIG_RNG
+#define CONFIG_SHA256
+#endif
+
#ifdef TEST_RSA
#define CONFIG_RSA
#define CONFIG_RSA_KEY_SIZE 2048