summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Collard <louiscollard@chromium.org>2019-01-21 19:29:47 +0800
committerchrome-bot <chrome-bot@chromium.org>2019-01-31 18:18:49 -0800
commitc758f2f435a54615507319200615a2a35a088100 (patch)
treeb4e38c892328b8e2f6e4e66a8d8527e302a4e052
parent846eb6500388e81b74730b6a02d44c4ede4b9279 (diff)
downloadchrome-ec-c758f2f435a54615507319200615a2a35a088100.tar.gz
cr50: Add new U2F generate, sign and attest commands.
These new commands expose a more generic API, which will allow a refactoring that removes most U2F-specific logic from cr50, and moves it into u2fd. CQ-DEPEND=CL:1371584 BRANCH=none BUG=b:123161715 TEST=local testing using g2ftool Signed-off-by: Louis Collard <louiscollard@chromium.org> Change-Id: I32067ce01e4bb31a331994b4e91d5b56d125cbb1 Reviewed-on: https://chromium-review.googlesource.com/1425137 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Reviewed-by: Andrey Pronin <apronin@chromium.org>
-rw-r--r--common/u2f.c240
-rw-r--r--include/tpm_vendor_cmds.h7
-rw-r--r--include/u2f.h41
3 files changed, 288 insertions, 0 deletions
diff --git a/common/u2f.c b/common/u2f.c
index b6c8a799d3..de12608ba0 100644
--- a/common/u2f.c
+++ b/common/u2f.c
@@ -9,6 +9,7 @@
#include "cryptoc/p256.h"
#include "cryptoc/sha256.h"
#include "dcrypto.h"
+#include "extension.h"
#include "nvcounter.h"
#include "system.h"
#include "u2f_impl.h"
@@ -389,3 +390,242 @@ ret_status:
return ret_len;
}
+
+/* U2F GENERATE command */
+static enum vendor_cmd_rc u2f_generate(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ U2F_GENERATE_REQ *req = buf;
+ U2F_GENERATE_RESP *resp;
+
+ /* Origin keypair */
+ uint8_t od_seed[SHA256_DIGEST_SIZE];
+ p256_int od, opk_x, opk_y;
+
+ /* Key handle */
+ uint8_t kh[U2F_FIXED_KH_SIZE];
+ uint8_t tmp[U2F_FIXED_KH_SIZE];
+
+ if (input_size != sizeof(U2F_GENERATE_REQ) ||
+ *response_size < sizeof(U2F_GENERATE_RESP))
+ return VENDOR_RC_BOGUS_ARGS;
+
+ /* Maybe enforce user presence, w/ optional consume */
+ if (pop_check_presence(req->flags & G2F_CONSUME) != POP_TOUCH_YES &&
+ (req->flags & U2F_AUTH_FLAG_TUP) != 0)
+ return VENDOR_RC_NOT_ALLOWED;
+
+ /* Generate origin-specific keypair */
+ if (u2f_origin_keypair(od_seed, &od, &opk_x, &opk_y) !=
+ EC_SUCCESS) {
+ CPRINTF("Origin keypair gen failed");
+ return VENDOR_RC_INTERNAL_ERROR;
+ }
+
+ /* Generate key handle */
+ /* Interleave origin ID and origin priv key, wrap and export. */
+ interleave32(req->appId, od_seed, tmp);
+ if (wrap_kh(NULL, tmp, kh, ENCRYPT_MODE) != EC_SUCCESS)
+ return VENDOR_RC_INTERNAL_ERROR;
+
+ /*
+ * From this point: the request 'req' content is invalid as it is
+ * overridden by the response we are building in the same buffer.
+ */
+ resp = buf;
+
+ /* Insert origin-specific public keys into the response */
+ p256_to_bin(&opk_x, resp->pubKey.x); /* endianness */
+ p256_to_bin(&opk_y, resp->pubKey.y); /* endianness */
+
+ resp->pubKey.pointFormat = U2F_POINT_UNCOMPRESSED;
+
+ /* Copy key handle to response. */
+ memcpy(resp->keyHandle, kh, sizeof(kh));
+
+ *response_size = sizeof(resp->pubKey) +
+ sizeof(kh);
+
+ return VENDOR_RC_SUCCESS;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_GENERATE, u2f_generate);
+
+/* U2F SIGN command */
+static enum vendor_cmd_rc u2f_sign(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ const U2F_SIGN_REQ *req = buf;
+ U2F_SIGN_RESP *resp;
+
+ /* Decrypted key handle. */
+ uint8_t unwrapped_kh[KH_LEN];
+
+ /* Contents of key handle after de-interleaving. */
+ uint8_t od_seed[SHA256_DIGEST_SIZE];
+ uint8_t origin[U2F_APPID_SIZE];
+
+ struct drbg_ctx ctx;
+
+ /* Origin private key. */
+ p256_int origin_d;
+
+ /* Hash, and corresponding signature. */
+ p256_int h, r, s;
+
+ if (input_size != sizeof(U2F_SIGN_REQ))
+ return VENDOR_RC_BOGUS_ARGS;
+
+ /* Decrypt and unwrap key handle. */
+ if (wrap_kh(NULL, req->keyHandle, unwrapped_kh, DECRYPT_MODE))
+ return VENDOR_RC_NOT_ALLOWED;
+ deinterleave64(unwrapped_kh, origin, od_seed);
+
+ /* Check origin matches. */
+ if (memcmp(origin, req->appId, U2F_APPID_SIZE) != 0)
+ return VENDOR_RC_NOT_ALLOWED;
+
+ /* Always enforce user presence, with optional consume. */
+ if (pop_check_presence(req->flags & G2F_CONSUME) != POP_TOUCH_YES)
+ return VENDOR_RC_NOT_ALLOWED;
+
+ /* Re-create origin-specific key. */
+ if (u2f_origin_key(od_seed, &origin_d))
+ return VENDOR_RC_INTERNAL_ERROR;
+
+ /* Prepare hash to sign. */
+ p256_from_bin(req->hash, &h);
+
+ /* Sign. */
+ hmac_drbg_init_rfc6979(&ctx, &origin_d, &h);
+ if (!dcrypto_p256_ecdsa_sign(&ctx, &origin_d, &h, &r, &s)) {
+ p256_clear(&origin_d);
+ return VENDOR_RC_INTERNAL_ERROR;
+ }
+ p256_clear(&origin_d);
+
+ /*
+ * From this point: the request 'req' content is invalid as it is
+ * overridden by the response we are building in the same buffer.
+ * The response is smaller than the request, so we have the space.
+ */
+ resp = buf;
+
+ p256_to_bin(&r, resp->sig_r);
+ p256_to_bin(&s, resp->sig_s);
+
+ *response_size = sizeof(U2F_SIGN_RESP);
+ return 0;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_SIGN, u2f_sign);
+
+struct G2F_REGISTER_MSG {
+ uint8_t reserved;
+ uint8_t app_id[U2F_APPID_SIZE];
+ uint8_t challenge[U2F_CHAL_SIZE];
+ uint8_t key_handle[U2F_APPID_SIZE + sizeof(p256_int)];
+ U2F_EC_POINT public_key;
+};
+
+static inline int u2f_attest_verify_reg_resp(uint8_t data_size,
+ const uint8_t *data)
+{
+ struct G2F_REGISTER_MSG *msg = (void *) data;
+ uint8_t unwrapped_kh[KH_LEN];
+
+ if (data_size != sizeof(struct G2F_REGISTER_MSG))
+ return VENDOR_RC_NOT_ALLOWED;
+
+ /* Check if we can decrypt keyhandle. */
+ if (wrap_kh(NULL, msg->key_handle, unwrapped_kh, DECRYPT_MODE))
+ return VENDOR_RC_NOT_ALLOWED;
+
+ return VENDOR_RC_SUCCESS;
+}
+
+static int u2f_attest_verify(uint8_t format,
+ uint8_t data_size,
+ const uint8_t *data)
+{
+ switch (format) {
+ case U2F_ATTEST_FORMAT_REG_RESP:
+ return u2f_attest_verify_reg_resp(data_size, data);
+ default:
+ return VENDOR_RC_NOT_ALLOWED;
+ }
+}
+
+static inline size_t u2f_attest_format_size(uint8_t format)
+{
+ switch (format) {
+ case U2F_ATTEST_FORMAT_REG_RESP:
+ return sizeof(struct G2F_REGISTER_MSG);
+ default:
+ return 0;
+ }
+}
+
+/* U2F ATTEST command */
+static enum vendor_cmd_rc u2f_attest(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ const U2F_ATTEST_REQ *req = buf;
+ U2F_ATTEST_RESP *resp;
+
+ int verify_ret;
+
+ HASH_CTX h_ctx;
+ struct drbg_ctx dr_ctx;
+
+ /* Data hash, and corresponding signature. */
+ p256_int h, r, s;
+
+ /* Attestation key */
+ p256_int d, pk_x, pk_y;
+
+ if (input_size != sizeof(U2F_ATTEST_REQ))
+ return VENDOR_RC_BOGUS_ARGS;
+
+ verify_ret = u2f_attest_verify(req->format, req->dataLen,
+ req->data);
+
+ if (verify_ret != VENDOR_RC_SUCCESS)
+ return verify_ret;
+
+ /* Message signature */
+ DCRYPTO_SHA256_init(&h_ctx, 0);
+ HASH_update(&h_ctx, req->data, u2f_attest_format_size(req->format));
+ p256_from_bin(HASH_final(&h_ctx), &h);
+
+ /* Derive G2F Attestation Key */
+ if (g2f_individual_keypair(&d, &pk_x, &pk_y)) {
+ CPRINTF("G2F Attestation key generation failed");
+ return VENDOR_RC_INTERNAL_ERROR;
+ }
+
+ /* Sign over the response w/ the attestation key */
+ hmac_drbg_init_rfc6979(&dr_ctx, &d, &h);
+ if (!dcrypto_p256_ecdsa_sign(&dr_ctx, &d, &h, &r, &s)) {
+ CPRINTF("Signing error");
+ return VENDOR_RC_INTERNAL_ERROR;
+ }
+ p256_clear(&d);
+
+ /*
+ * From this point: the request 'req' content is invalid as it is
+ * overridden by the response we are building in the same buffer.
+ * The response is smaller than the request, so we have the space.
+ */
+ resp = buf;
+
+ p256_to_bin(&r, resp->sig_r);
+ p256_to_bin(&s, resp->sig_s);
+
+ return VENDOR_RC_SUCCESS;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_U2F_ATTEST, u2f_attest);
diff --git a/include/tpm_vendor_cmds.h b/include/tpm_vendor_cmds.h
index a2e2b6920e..af91eaa2c3 100644
--- a/include/tpm_vendor_cmds.h
+++ b/include/tpm_vendor_cmds.h
@@ -123,6 +123,13 @@ enum vendor_cmd_cc {
*/
VENDOR_CC_GET_PWR_BTN = 43,
+ /*
+ * U2F commands.
+ */
+ VENDOR_CC_U2F_GENERATE = 44,
+ VENDOR_CC_U2F_SIGN = 45,
+ VENDOR_CC_U2F_ATTEST = 46,
+
LAST_VENDOR_COMMAND = 65535,
};
diff --git a/include/u2f.h b/include/u2f.h
index e0b70d9751..e059f98e32 100644
--- a/include/u2f.h
+++ b/include/u2f.h
@@ -28,6 +28,9 @@ extern "C" {
#define U2F_CTR_SIZE 4 // Size of counter field
#define U2F_APPID_SIZE 32 // Size of application id
#define U2F_CHAL_SIZE 32 // Size of challenge
+#define U2F_MAX_ATTEST_SIZE 256 // Size of largest blob to sign
+#define U2F_P256_SIZE 32
+#define U2F_FIXED_KH_SIZE 64 // Size of fixed size key handles
#define ENC_SIZE(x) ((x + 7) & 0xfff8)
@@ -91,6 +94,41 @@ typedef struct {
uint8_t sig[U2F_MAX_EC_SIG_SIZE]; // Signature
} U2F_AUTHENTICATE_RESP;
+// TODO(louiscollard): Add Descriptions.
+
+typedef struct {
+ uint8_t appId[U2F_APPID_SIZE]; // Application id
+ uint8_t flags;
+} U2F_GENERATE_REQ;
+
+typedef struct {
+ U2F_EC_POINT pubKey; // Generated public key
+ uint8_t keyHandle[U2F_FIXED_KH_SIZE]; // Key handle
+} U2F_GENERATE_RESP;
+
+typedef struct {
+ uint8_t appId[U2F_APPID_SIZE]; // Application id
+ uint8_t keyHandle[U2F_FIXED_KH_SIZE]; // Key handle
+ uint8_t hash[U2F_P256_SIZE];
+ uint8_t flags;
+} U2F_SIGN_REQ;
+
+typedef struct {
+ uint8_t sig_r[U2F_P256_SIZE]; // Signature
+ uint8_t sig_s[U2F_P256_SIZE]; // Signature
+} U2F_SIGN_RESP;
+
+typedef struct {
+ uint8_t format;
+ uint8_t dataLen;
+ uint8_t data[U2F_MAX_ATTEST_SIZE];
+} U2F_ATTEST_REQ;
+
+typedef struct {
+ uint8_t sig_r[U2F_P256_SIZE];
+ uint8_t sig_s[U2F_P256_SIZE];
+} U2F_ATTEST_RESP;
+
// Command status responses
#define U2F_SW_NO_ERROR 0x9000 // SW_NO_ERROR
@@ -110,6 +148,9 @@ typedef struct {
#define G2F_ATTEST 0x80 // Fixed attestation key
#define G2F_CONSUME 0x02 // Consume presence
+// U2F Attest format for U2F Register Response.
+#define U2F_ATTEST_FORMAT_REG_RESP 0
+
// Vendor command to enable/disable the extensions
#define U2F_VENDOR_MODE U2F_VENDOR_LAST