summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2017-01-19 15:22:15 +0100
committerchrome-bot <chrome-bot@chromium.org>2017-06-29 10:37:40 -0700
commit28e76b309d595923121e70cbd9e3228affad3a09 (patch)
treee4ec97f07b5f1b26fbaa79440245eed0110eb991
parent3fdda8b6f1a127bff83ed7e30316a837b941875c (diff)
downloadchrome-ec-28e76b309d595923121e70cbd9e3228affad3a09.tar.gz
cr50: add U2F support
Implement U2F (universal second factor authentication) feature over TPM vendor commands. The raw U2F APDU as defined by the FIDO Alliance 'U2F Raw Message Formats' specification can be sent using the VENDOR_CC_U2F_APDU command. So the vendor command is taking a ISO7816-4:2005 APDU format frame as input as defined by the spec and returns another APDU using ISO7816-4 status code. The APDU is processed by the common U2F code using u2f_apdu_rcv(), this hardware specific code provides: - the user physical presence detection (done by the power button press) returned by the pop_check_presence() callback. - the connection to the cryptographic hardware to generate/derive the keys used by the U2F and individual attestation functions. This feature/vendor command has 3 modes: - disabled - U2F (only the commands/flags defined by the U2F specification) - G2F (the U2F commands plus some extensions for individual attestation) Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BRANCH=cr50 BUG=b:35545754 TEST=pass U2FTest and HIDTest. Change-Id: Ic2591f369763fb4ba67926e2b4a0c2cd35330a18 Reviewed-on: https://chromium-review.googlesource.com/518139 Commit-Ready: Vincent Palatin <vpalatin@chromium.org> Tested-by: Vincent Palatin <vpalatin@chromium.org> Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--board/cr50/board.h6
-rw-r--r--board/cr50/build.mk1
-rw-r--r--board/cr50/u2f.c226
-rw-r--r--board/cr50/wp.c4
-rw-r--r--include/tpm_vendor_cmds.h1
5 files changed, 238 insertions, 0 deletions
diff --git a/board/cr50/board.h b/board/cr50/board.h
index 14257012ad..9d0ebbce43 100644
--- a/board/cr50/board.h
+++ b/board/cr50/board.h
@@ -82,6 +82,9 @@
/* Enable debug cable detection */
#define CONFIG_RDD
+/* Also use the cr50 as a second factor authentication */
+#define CONFIG_U2F
+
/* USB configuration */
#define CONFIG_USB
#define CONFIG_USB_CONSOLE
@@ -171,6 +174,7 @@ enum device_type {
enum nvmem_vars {
NVMEM_VAR_CONSOLE_LOCKED = 0,
NVMEM_VAR_TEST_VAR,
+ NVMEM_VAR_U2F_SALT,
NVMEM_VARS_COUNT
};
@@ -197,6 +201,8 @@ int board_tpm_uses_i2c(void);
int board_tpm_uses_spi(void);
int board_id_is_mismatched(void);
+void power_button_record(void);
+
#endif /* !__ASSEMBLER__ */
/* USB interface indexes (use define rather than enum to expand them) */
diff --git a/board/cr50/build.mk b/board/cr50/build.mk
index ed2f475827..1e882e9876 100644
--- a/board/cr50/build.mk
+++ b/board/cr50/build.mk
@@ -49,6 +49,7 @@ board-y += tpm2/tpm_state.o
board-y += tpm2/trng.o
board-y += tpm_nvmem_read.o
board-y += wp.o
+board-$(CONFIG_U2F) += u2f.o
# Build and link with an external library
EXTLIB := $(realpath ../../third_party/tpm2)
diff --git a/board/cr50/u2f.c b/board/cr50/u2f.c
new file mode 100644
index 0000000000..e6f4861e1d
--- /dev/null
+++ b/board/cr50/u2f.c
@@ -0,0 +1,226 @@
+/* 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.
+ */
+
+/* Helpers to emulate a U2F HID dongle over the TPM transport */
+
+#include "console.h"
+#include "dcrypto.h"
+#include "device_state.h"
+#include "extension.h"
+#include "nvmem_vars.h"
+#include "rbox.h"
+#include "registers.h"
+#include "signed_header.h"
+#include "system.h"
+#include "tpm_vendor_cmds.h"
+#include "u2f.h"
+#include "u2f_impl.h"
+#include "util.h"
+
+#define CPRINTS(format, args...) cprints(CC_EXTENSION, format, ## args)
+
+/* ---- physical presence (using the laptop power button) ---- */
+
+static timestamp_t last_press;
+
+/* how long do we keep the last button press as valid presence */
+#define PRESENCE_TIMEOUT (10 * SECOND)
+
+void power_button_record(void)
+{
+ if (device_get_state(DEVICE_AP) == DEVICE_STATE_ON &&
+ rbox_powerbtn_is_pressed())
+ last_press = get_time();
+}
+
+enum touch_state pop_check_presence(int consume)
+{
+ int recent = (get_time().val - PRESENCE_TIMEOUT) < last_press.val;
+
+ CPRINTS("Presence:%d", recent);
+ if (consume)
+ last_press.val = 0;
+
+ /* user physical presence on the power button */
+ return recent ? POP_TOUCH_YES : POP_TOUCH_NO;
+}
+
+/* ---- non-volatile U2F parameters ---- */
+
+/*
+ * Current mode defining the behavior of the U2F feature.
+ * Identical to the one defined on the host side by the enum U2fMode
+ * in the chrome_device_policy.proto protobuf.
+ */
+enum u2f_mode {
+ MODE_UNSET = 0,
+ /* Feature disabled */
+ MODE_DISABLED = 1,
+ /* U2F as defined by the FIDO Alliance specification */
+ MODE_U2F = 2,
+ /* U2F plus extensions for individual attestation certificate */
+ MODE_U2F_EXTENDED = 3,
+};
+
+static uint32_t salt[8];
+static uint8_t u2f_mode = MODE_UNSET;
+static const uint8_t k_salt = NVMEM_VAR_U2F_SALT;
+
+static int load_state(void)
+{
+ const struct tuple *t_salt = getvar(&k_salt, sizeof(k_salt));
+
+ if (!t_salt) {
+ /* create random salt */
+ if (!DCRYPTO_ladder_random(salt))
+ return 0;
+ if (setvar(&k_salt, sizeof(k_salt),
+ (const uint8_t *)salt, sizeof(salt)))
+ return 0;
+ } else {
+ memcpy(salt, tuple_val(t_salt), sizeof(salt));
+ }
+
+ return 1;
+}
+
+static int use_u2f(void)
+{
+ /*
+ * TODO(b/62294740): Put board ID check here if needed
+ * if (!board_id_we_want)
+ * return 0;
+ */
+
+ if (u2f_mode == MODE_UNSET) {
+ if (load_state())
+ /* Start without extension enabled, host will set it */
+ u2f_mode = MODE_U2F;
+ }
+
+ return u2f_mode >= MODE_U2F;
+}
+
+int use_g2f(void)
+{
+ return use_u2f() && u2f_mode == MODE_U2F_EXTENDED;
+}
+
+unsigned u2f_custom_dispatch(uint8_t ins, struct apdu apdu,
+ uint8_t *buf, unsigned *ret_len)
+{
+ if (ins == U2F_VENDOR_MODE) {
+ if (apdu.p1) { /* Set mode */
+ u2f_mode = apdu.p2;
+ }
+ /* return the current mode */
+ buf[0] = use_u2f() ? u2f_mode : 0;
+ *ret_len = 1;
+ return U2F_SW_NO_ERROR;
+ }
+ return U2F_SW_INS_NOT_SUPPORTED;
+}
+
+/* ---- chip-specific U2F crypto ---- */
+
+static int _derive_key(enum dcrypto_appid appid, const uint32_t input[8],
+ uint32_t output[8])
+{
+ struct APPKEY_CTX ctx;
+ int result;
+
+ /* Setup USR-based application key. */
+ if (!DCRYPTO_appkey_init(appid, &ctx))
+ return 0;
+ result = DCRYPTO_appkey_derive(appid, input, output);
+
+ DCRYPTO_appkey_finish(&ctx);
+ return result;
+}
+
+int u2f_origin_keypair(uint8_t *seed, p256_int *d,
+ p256_int *pk_x, p256_int *pk_y)
+{
+ uint32_t tmp[8];
+
+ do {
+ if (!DCRYPTO_ladder_random(seed))
+ return EC_ERROR_UNKNOWN;
+ memcpy(tmp, seed, sizeof(tmp));
+ if (!_derive_key(U2F_ORIGIN, tmp, tmp))
+ return EC_ERROR_UNKNOWN;
+ } while (
+ !DCRYPTO_p256_key_from_bytes(pk_x, pk_y, d, (const uint8_t *)tmp));
+
+ return EC_SUCCESS;
+}
+
+int u2f_origin_key(const uint8_t *seed, p256_int *d)
+{
+ uint32_t tmp[8];
+
+ memcpy(tmp, seed, sizeof(tmp));
+ if (!_derive_key(U2F_ORIGIN, tmp, tmp))
+ return EC_ERROR_UNKNOWN;
+ return DCRYPTO_p256_key_from_bytes(NULL, NULL, d,
+ (const uint8_t *)tmp) == 0;
+}
+
+int u2f_gen_kek(const uint8_t *origin, uint8_t *kek, size_t key_len)
+{
+ uint32_t buf[8];
+
+ if (key_len != sizeof(buf))
+ return EC_ERROR_UNKNOWN;
+ if (!_derive_key(U2F_WRAP, salt, buf))
+ return EC_ERROR_UNKNOWN;
+ memcpy(kek, buf, key_len);
+
+ return EC_SUCCESS;
+}
+
+int g2f_individual_keypair(p256_int *d, p256_int *pk_x, p256_int *pk_y)
+{
+ uint8_t buf[32];
+
+ /* Incorporate HIK & diversification constant */
+ if (!_derive_key(U2F_ATTEST, salt, (uint32_t *)buf))
+ return EC_ERROR_UNKNOWN;
+
+ /* Generate unbiased private key */
+ while (!DCRYPTO_p256_key_from_bytes(pk_x, pk_y, d, buf)) {
+ HASH_CTX sha;
+
+ DCRYPTO_SHA256_init(&sha, 0);
+ HASH_update(&sha, buf, sizeof(buf));
+ memcpy(buf, HASH_final(&sha), SHA_DIGEST_MAX_BYTES);
+ }
+
+ return EC_SUCCESS;
+}
+
+/* ---- Send/receive U2F APDU over TPM vendor commands ---- */
+
+enum vendor_cmd_rc vc_u2f_apdu(enum vendor_cmd_cc code, void *body,
+ size_t cmd_size, size_t *response_size)
+{
+ unsigned retlen;
+
+ if (!use_u2f()) { /* the feature is disabled */
+ uint8_t *cmd = body;
+ /* process it only if the host tries to enable the feature */
+ if (cmd_size < 2 || cmd[1] != U2F_VENDOR_MODE) {
+ *response_size = 0;
+ return VENDOR_RC_NO_SUCH_COMMAND;
+ }
+ }
+
+ /* Process U2F APDU */
+ retlen = u2f_apdu_rcv(body, cmd_size, *response_size);
+
+ *response_size = retlen;
+ return VENDOR_RC_SUCCESS;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_APDU, vc_u2f_apdu);
diff --git a/board/cr50/wp.c b/board/cr50/wp.c
index 6c6188949b..3c25036647 100644
--- a/board/cr50/wp.c
+++ b/board/cr50/wp.c
@@ -399,6 +399,10 @@ static void power_button_handler(void)
{
if (unlock_in_progress)
power_button_poked();
+#ifdef CONFIG_U2F
+ else
+ power_button_record();
+#endif
GWRITE_FIELD(RBOX, INT_STATE, INTR_PWRB_IN_FED, 1);
}
diff --git a/include/tpm_vendor_cmds.h b/include/tpm_vendor_cmds.h
index 1f9e552aaa..ce1b3be057 100644
--- a/include/tpm_vendor_cmds.h
+++ b/include/tpm_vendor_cmds.h
@@ -40,6 +40,7 @@ enum vendor_cmd_cc {
VENDOR_CC_TURN_UPDATE_ON = 24,
VENDOR_CC_GET_BOARD_ID = 25,
VENDOR_CC_SET_BOARD_ID = 26,
+ VENDOR_CC_U2F_APDU = 27,
LAST_VENDOR_COMMAND = 65535,
};