summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorVadim Sukhomlinov <sukhomlinov@google.com>2021-07-16 23:51:06 -0700
committerCommit Bot <commit-bot@chromium.org>2021-09-02 00:51:52 +0000
commit4b109d0b957a66bb9e6726f54db22d55452999b2 (patch)
tree7e5bee5162759297d1c0deb1916c1c4ae3d622f0 /include
parentdc96ffc9dc48af55ba79846cd954ce55821b31eb (diff)
downloadchrome-ec-4b109d0b957a66bb9e6726f54db22d55452999b2.tar.gz
u2f: refactoring to split command processing and crypto
Split U2F crypto from U2F command processing by moving all crypto code into boards/cr50 (platform hooks). U2F state management is part of common code and passed to U2F crypto as a parameter. Previously reviewed as https://crrev.com/c/3034852, but reverted due to ChromeOS dependency on include/u2f.h. In this revision this is addressed by restoring include/u2f.h with previous content and new additions and adjusting dependencies in other headers. BUG=b:134594373 TEST=make BOARD=cr50 CRYPTO_TEST=1 console: u2f_test test/tpmtest.py FAFT U2F tests pass Signed-off-by: Vadim Sukhomlinov <sukhomlinov@google.com> Change-Id: Iff1973c8e475216b801d7adde23b1ef6c4a6f699 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3119223 Reviewed-by: Vadim Sukhomlinov <sukhomlinov@chromium.org> Reviewed-by: Andrey Pronin <apronin@chromium.org> Tested-by: Vadim Sukhomlinov <sukhomlinov@chromium.org> Commit-Queue: Vadim Sukhomlinov <sukhomlinov@chromium.org>
Diffstat (limited to 'include')
-rw-r--r--include/physical_presence.h14
-rw-r--r--include/u2f.h76
-rw-r--r--include/u2f_cmds.h59
-rw-r--r--include/u2f_impl.h250
4 files changed, 297 insertions, 102 deletions
diff --git a/include/physical_presence.h b/include/physical_presence.h
index 0acbc65691..c71c6af1ea 100644
--- a/include/physical_presence.h
+++ b/include/physical_presence.h
@@ -73,4 +73,18 @@ enum pp_fsm_state {
};
enum pp_fsm_state physical_presense_fsm_state(void);
+/* ---- Physical presence ---- */
+enum touch_state {
+ POP_TOUCH_NO = 0, /* waiting for a user touch */
+ POP_TOUCH_YES = 1, /* touch recorded and latched */
+};
+
+/*
+ * Check whether the user presence event was latched.
+ *
+ * @param consume reset the latched touch event and the presence LED.
+ * @return POP_TOUCH_NO or POP_TOUCH_YES.
+ */
+enum touch_state pop_check_presence(int consume);
+
#endif /* __CROS_EC_PHYSICAL_PRESENCE_H */
diff --git a/include/u2f.h b/include/u2f.h
index 6680ef5300..f24eac84a4 100644
--- a/include/u2f.h
+++ b/include/u2f.h
@@ -5,6 +5,14 @@
#ifndef __U2F_H_INCLUDED__
#define __U2F_H_INCLUDED__
+/**
+ * Note: This header file should be self-sufficient as it is shared
+ * with other boards and with userland daemons (u2fd).
+ *
+ * chromeos-ec-headers package installs it in ChromeOS development environment.
+ *
+ */
+
#ifdef _MSC_VER /* Windows */
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
@@ -20,7 +28,7 @@ extern "C" {
/* General constants */
-#define U2F_EC_KEY_SIZE 32 /* EC key size in bytes */
+#define U2F_EC_KEY_SIZE 32 /* EC key size in bytes, NIST P-256 Curve */
#define U2F_EC_POINT_SIZE ((U2F_EC_KEY_SIZE * 2) + 1) /* Size of EC point */
#define U2F_MAX_KH_SIZE 128 /* Max size of key handle */
#define U2F_MAX_ATT_CERT_SIZE 2048 /* Max size of attestation certificate */
@@ -30,8 +38,15 @@ extern "C" {
#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
+/* Origin seed is a random nonce generated during key handle creation. */
+#define U2F_ORIGIN_SEED_SIZE 32
+#define U2F_USER_SECRET_SIZE 32 /* Size of user secret */
+
+#define U2F_AUTH_TIME_SECRET_SIZE 32
#define SHA256_DIGEST_SIZE 32
+#define U2F_MESSAGE_DIGEST_SIZE SHA256_DIGEST_SIZE
+
#define ENC_SIZE(x) ((x + 7) & 0xfff8)
@@ -56,15 +71,29 @@ struct u2f_ec_point {
#define U2F_KH_VERSION_1 0x01
#define U2F_AUTHORIZATION_SALT_SIZE 16
+#define U2F_V0_KH_SIZE 64
+
+/**
+ * Key handle version = 1 for WebAuthn, bound to device and user.
+ */
+#define U2F_V1_KH_SIZE 113
+
+/* Header is composed of version || origin_seed || kh_hmac */
+#define U2F_V1_KH_HEADER_SIZE (U2F_ORIGIN_SEED_SIZE + SHA256_DIGEST_SIZE + 1)
+
+struct u2f_signature {
+ uint8_t sig_r[U2F_EC_KEY_SIZE]; /* Signature */
+ uint8_t sig_s[U2F_EC_KEY_SIZE]; /* Signature */
+};
struct u2f_key_handle {
- uint8_t origin_seed[U2F_P256_SIZE];
+ uint8_t origin_seed[U2F_ORIGIN_SEED_SIZE];
uint8_t hmac[SHA256_DIGEST_SIZE];
};
struct u2f_versioned_key_handle_header {
uint8_t version;
- uint8_t origin_seed[U2F_P256_SIZE];
+ uint8_t origin_seed[U2F_ORIGIN_SEED_SIZE];
uint8_t kh_hmac[SHA256_DIGEST_SIZE];
};
@@ -75,17 +104,46 @@ struct u2f_versioned_key_handle {
uint8_t authorization_hmac[SHA256_DIGEST_SIZE];
};
+/**
+ * Alternative definitions of key handles.
+ *
+ * struct u2f_key_handle_v0 == struct u2f_key_handle
+ * struct u2f_key_handle_v1 == struct u2f_versioned_key_handle
+ *
+ */
+
+/* Key handle version = 0, only bound to device. */
+struct u2f_key_handle_v0 {
+ uint8_t origin_seed[U2F_ORIGIN_SEED_SIZE];
+ uint8_t hmac[SHA256_DIGEST_SIZE];
+};
+
+/* Key handle version = 1, bound to device and user. */
+struct u2f_key_handle_v1 {
+ uint8_t version;
+ uint8_t origin_seed[U2F_ORIGIN_SEED_SIZE];
+ uint8_t kh_hmac[SHA256_DIGEST_SIZE];
+ /* Optionally checked in u2f_sign. */
+ uint8_t authorization_salt[U2F_AUTHORIZATION_SALT_SIZE];
+ uint8_t authorization_hmac[SHA256_DIGEST_SIZE];
+};
+
+union u2f_key_handle_variant {
+ struct u2f_key_handle_v0 v0;
+ struct u2f_key_handle_v1 v1;
+};
+
/* TODO(louiscollard): Add Descriptions. */
struct u2f_generate_req {
uint8_t appId[U2F_APPID_SIZE]; /* Application id */
- uint8_t userSecret[U2F_P256_SIZE];
+ uint8_t userSecret[U2F_USER_SECRET_SIZE];
uint8_t flags;
/*
* If generating versioned KH, derive an hmac from it and append to
* the key handle. Otherwise unused.
*/
- uint8_t authTimeSecretHash[SHA256_DIGEST_SIZE];
+ uint8_t authTimeSecretHash[U2F_AUTH_TIME_SECRET_SIZE];
};
struct u2f_generate_resp {
@@ -100,7 +158,7 @@ struct u2f_generate_versioned_resp {
struct u2f_sign_req {
uint8_t appId[U2F_APPID_SIZE]; /* Application id */
- uint8_t userSecret[U2F_P256_SIZE];
+ uint8_t userSecret[U2F_USER_SECRET_SIZE];
struct u2f_key_handle keyHandle;
uint8_t hash[U2F_P256_SIZE];
uint8_t flags;
@@ -108,8 +166,8 @@ struct u2f_sign_req {
struct u2f_sign_versioned_req {
uint8_t appId[U2F_APPID_SIZE]; /* Application id */
- uint8_t userSecret[U2F_P256_SIZE];
- uint8_t authTimeSecret[U2F_P256_SIZE];
+ uint8_t userSecret[U2F_USER_SECRET_SIZE];
+ uint8_t authTimeSecret[U2F_AUTH_TIME_SECRET_SIZE];
uint8_t hash[U2F_P256_SIZE];
uint8_t flags;
struct u2f_versioned_key_handle keyHandle;
@@ -121,7 +179,7 @@ struct u2f_sign_resp {
};
struct u2f_attest_req {
- uint8_t userSecret[U2F_P256_SIZE];
+ uint8_t userSecret[U2F_USER_SECRET_SIZE];
uint8_t format;
uint8_t dataLen;
uint8_t data[U2F_MAX_ATTEST_SIZE];
diff --git a/include/u2f_cmds.h b/include/u2f_cmds.h
new file mode 100644
index 0000000000..96c2883e2d
--- /dev/null
+++ b/include/u2f_cmds.h
@@ -0,0 +1,59 @@
+/* Copyright 2021 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.
+ */
+
+#ifndef __U2F_CMDS_H_INCLUDED__
+#define __U2F_CMDS_H_INCLUDED__
+
+/* Load platform hooks/definitions */
+#include "common.h"
+#include "tpm_vendor_cmds.h"
+#include "u2f.h"
+
+/* Until u2fd migrates to new structs, check they are compatible. */
+BUILD_ASSERT(sizeof(struct u2f_key_handle_v0) == sizeof(struct u2f_key_handle));
+
+BUILD_ASSERT(sizeof(struct u2f_key_handle_v1) ==
+ sizeof(struct u2f_versioned_key_handle));
+
+BUILD_ASSERT(sizeof(struct u2f_signature) == sizeof(struct u2f_sign_resp));
+BUILD_ASSERT(sizeof(struct u2f_signature) == sizeof(struct u2f_attest_resp));
+
+struct g2f_register_msg {
+ uint8_t reserved;
+ uint8_t app_id[U2F_APPID_SIZE];
+ uint8_t challenge[U2F_CHAL_SIZE];
+ struct u2f_key_handle_v0 key_handle;
+ struct u2f_ec_point public_key;
+};
+
+/**
+ * U2F_GENERATE command handler. Generates a key handle according to input
+ * parameters.
+ */
+enum vendor_cmd_rc u2f_generate_cmd(enum vendor_cmd_cc code, void *buf,
+ size_t input_size, size_t *response_size);
+
+/**
+ * U2F_SIGN command handler. Verifies a key handle is owned and signs data with
+ * it.
+ */
+enum vendor_cmd_rc u2f_sign_cmd(enum vendor_cmd_cc code, void *buf,
+ size_t input_size, size_t *response_size);
+
+
+/* Maximum size in bytes of G2F attestation certificate. */
+#define G2F_ATTESTATION_CERT_MAX_LEN 315
+
+/**
+ * Gets the x509 certificate for the attestation key pair returned
+ * by g2f_individual_keypair().
+ *
+ * @param buf pointer to a buffer that must be at least
+ * G2F_ATTESTATION_CERT_MAX_LEN bytes.
+ * @return size of certificate written to buf, 0 on error.
+ */
+size_t g2f_attestation_cert(uint8_t *buf);
+
+#endif /* __U2F_CMDS_H_INCLUDED__ */
diff --git a/include/u2f_impl.h b/include/u2f_impl.h
index 75e50cc6c7..7f0f10ef36 100644
--- a/include/u2f_impl.h
+++ b/include/u2f_impl.h
@@ -9,121 +9,196 @@
#define __CROS_EC_U2F_IMPL_H
#include "common.h"
-
#include "dcrypto.h"
-
-#include "tpm_vendor_cmds.h"
#include "u2f.h"
-/* ---- Physical presence ---- */
+/* ---- platform cryptography hooks ---- */
-enum touch_state {
- POP_TOUCH_NO = 0, /* waiting for a user touch */
- POP_TOUCH_YES = 1, /* touch recorded and latched */
+/* ---- non-volatile U2F state, shared with common code ---- */
+struct u2f_state {
+ /* G2F key gen seed. */
+ uint32_t salt[8];
+ /* HMAC key for U2F key handle authentication. */
+ uint32_t hmac_key[SHA256_DIGEST_SIZE / sizeof(uint32_t)];
+ /* Stored DRBG entropy. */
+ uint32_t drbg_entropy[16];
+ size_t drbg_entropy_size;
};
-/*
- * Check whether the user presence event was latched.
- *
- * @param consume reset the latched touch event and the presence LED.
- * @return POP_TOUCH_NO or POP_TOUCH_YES.
- */
-enum touch_state pop_check_presence(int consume);
+/* Make sure common declaration is compatible. */
+BUILD_ASSERT(U2F_EC_KEY_SIZE == P256_NBYTES);
+BUILD_ASSERT(sizeof(struct u2f_ec_point) == U2F_EC_POINT_SIZE);
-/* ---- non-volatile U2F state ---- */
+BUILD_ASSERT(sizeof(struct u2f_key_handle_v0) <= U2F_MAX_KH_SIZE);
+BUILD_ASSERT(sizeof(struct u2f_key_handle_v0) == U2F_V0_KH_SIZE);
-struct u2f_state {
- uint32_t salt[8];
- uint32_t salt_kek[8];
- uint32_t salt_kh[8];
-};
+BUILD_ASSERT(sizeof(struct u2f_key_handle_v1) <= U2F_MAX_KH_SIZE);
+BUILD_ASSERT(sizeof(struct u2f_key_handle_v1) == U2F_V1_KH_SIZE);
+
+
+BUILD_ASSERT(sizeof(union u2f_key_handle_variant) <= U2F_MAX_KH_SIZE);
/**
- * Get the current u2f state from the board.
+ * Create or update DRBG entropy in U2F state. Used when changing ownership
+ * to cryptographically discard previously generated keys.
+ *
+ * @param state u2f state to update
+ *
+ * @return EC_SUCCESS if successful
*/
-struct u2f_state *get_state(void);
+enum ec_error_list u2f_generate_drbg_entropy(struct u2f_state *state);
-/* ---- platform cryptography hooks ---- */
+/**
+ * Create or update HMAC key in U2F state. Used when changing ownership to
+ * cryptographically discard previously generated keys.
+ *
+ * @param state u2f state to update
+ *
+ * @return EC_SUCCESS if successful
+ */
+enum ec_error_list u2f_generate_hmac_key(struct u2f_state *state);
+
+/**
+ * Create or update G2F secret in U2F state.
+ *
+ * @param state u2f state to update
+ *
+ * @return EC_SUCCESS if successful
+ */
+enum ec_error_list u2f_generate_g2f_secret(struct u2f_state *state);
/**
- * Pack the specified origin, user secret and origin-specific seed
- * into a key handle.
+ * Create a randomized key handle for specified origin, user secret.
+ * Generate associated signing key.
*
+ * @param state initialized u2f state
* @param origin pointer to origin id
* @param user pointer to user secret
- * @param seed pointer to origin-specific random seed
- * @param key_handle buffer to hold the output key handle
+ * @param authTimeSecretHash authentication time secret
+ * @param kh output key handle header
+ * @param kh_version - key handle version to generate
+ * @param pubKey - generated public key
*
- * @return EC_SUCCESS if a valid keypair was created.
+ * @return EC_SUCCESS if successful
*/
-int u2f_origin_user_keyhandle(const uint8_t *origin, const uint8_t *user,
- const uint8_t *seed,
- struct u2f_key_handle *key_handle);
+enum ec_error_list u2f_generate(const struct u2f_state *state,
+ const uint8_t *user, const uint8_t *origin,
+ const uint8_t *authTimeSecretHash,
+ union u2f_key_handle_variant *kh,
+ uint8_t kh_version,
+ struct u2f_ec_point *pubKey);
/**
- * Pack the specified origin, user secret, origin-specific seed and version
- * byte into a key handle.
+ * Create a randomized key handle for specified origin, user secret.
+ * Generate associated signing key.
*
+ * @param state initialized u2f state
+ * @param kh output key handle header
+ * @param kh_version - key handle version to generate
* @param origin pointer to origin id
* @param user pointer to user secret
- * @param seed pointer to origin-specific random seed
- * @param version the version byte to pack; should be greater than 0.
- * @param key_handle_header buffer to hold the output key handle header
+ * @param authTimeSecretHash pointer to user's authentication secret.
+ * can be set to NULL if authorization_hmac check is not needed.
+ * @param r - generated part of signature
+ * @param s - generated part of signature
*
- * @return EC_SUCCESS if a valid keypair was created.
+ * @return EC_SUCCESS if a valid key pair was created
+ * EC_ACCESS_DENIED if key handle can't authenticated
*/
-int u2f_origin_user_versioned_keyhandle(
- const uint8_t *origin, const uint8_t *user, const uint8_t *seed,
- uint8_t version,
- struct u2f_versioned_key_handle_header *key_handle_header);
+enum ec_error_list u2f_sign(const struct u2f_state *state,
+ const union u2f_key_handle_variant *kh,
+ uint8_t kh_version, const uint8_t *user,
+ const uint8_t *origin,
+ const uint8_t *authTimeSecretHash,
+ const uint8_t *hash, struct u2f_signature *sig);
/**
- * Generate an origin and user-specific ECDSA keypair from the specified
- * key handle.
+ * Verify that key handle matches provided origin, user and user's
+ * authentication secret and was created on this device (signed with
+ * U2F state HMAC key).
*
- * If pk_x and pk_y are NULL, public key generation will be skipped.
+ * @param state initialized u2f state
+ * @param kh input key handle
+ * @param kh_version - key handle version to verify
+ * @param user pointer to user secret
+ * @param origin pointer to origin id
+ * @param authTimeSecretHash pointer to user's authentication secret.
+ * can be set to NULL if authorization_hmac check is not needed.
*
- * @param key_handle pointer to the key handle
- * @param key_handle_size size of the key handle in bytes
- * @param d pointer to ECDSA private key
- * @param pk_x pointer to public key point
- * @param pk_y pointer to public key point
+ * @return EC_SUCCESS if handle can be authenticated
+ */
+enum ec_error_list u2f_authorize_keyhandle(const struct u2f_state *state,
+ const union u2f_key_handle_variant *kh,
+ uint8_t kh_version, const uint8_t *user,
+ const uint8_t *origin,
+ const uint8_t *authTimeSecretHash);
+
+/**
+ * Gets the x509 certificate for the attestation key pair returned
+ * by g2f_individual_keypair().
+ *
+ * @param state U2F state parameters
+ * @param serial Device serial number
+ * @param buf pointer to a buffer that must be at least
*
- * @return EC_SUCCESS if a valid keypair was created.
+ * G2F_ATTESTATION_CERT_MAX_LEN bytes.
+ * @return size of certificate written to buf, 0 on error.
*/
-int u2f_origin_user_keypair(const uint8_t *key_handle, size_t key_handle_size,
- p256_int *d, p256_int *pk_x, p256_int *pk_y);
+size_t g2f_attestation_cert_serial(const struct u2f_state *state,
+ const uint8_t *serial, uint8_t *buf);
/**
- * Derive an hmac from the given salt, key handle and hash. The salt is to make
- * sure the hmac is different for different key handles of one user. The key
- * handle header is encoded into the authorization hmac to protect against
- * swapping auth time secret.
+ * Verify that provided key handle and public key match.
+ * @param state U2F state parameters
+ * @param key_handle key handle
+ * @param kh_version key handle version (0 - legacy, 1 - versioned)
+ * @param user pointer to user secret
+ * @param origin pointer to origin id
+ * @param authTimeSecretHash pointer to user's authentication secret.
+ * can be set to NULL if authorization_hmac check is not needed.
+ * @param public_key pointer to public key point (big endian)
+ * @param data data to sign
+ * @param data_size data size in bytes
+ *
+ * @param r part of generated signature
+ * @param s part of generated signature
+ *
+ * @return EC_SUCCESS if public key matches key handle,
+ * (r,s) set to valid signature
+ * EC_ACCESS_DENIED if key handle can't authenticated
*/
-int u2f_authorization_hmac(const uint8_t *authorization_salt,
- const struct u2f_versioned_key_handle_header *header,
- const uint8_t *auth_time_secret_hash, uint8_t *hmac);
+enum ec_error_list u2f_attest(const struct u2f_state *state,
+ const union u2f_key_handle_variant *kh,
+ uint8_t kh_version, const uint8_t *user,
+ const uint8_t *origin,
+ const uint8_t *authTimeSecretHash,
+ const struct u2f_ec_point *public_key,
+ const uint8_t *data, size_t data_size,
+ struct u2f_signature *sig);
-/***
- * Generate a hardware derived 256b private key.
+
+/**
+ *
+ * Board U2F key management part implemented.
*
- * @param kek ptr to store the generated key.
- * @param key_len size of the storage buffer. Should be 32 bytes.
- * @return EC_SUCCESS if a valid key was created.
*/
-int u2f_gen_kek(const uint8_t *origin, uint8_t *kek, size_t key_len);
/**
- * Generate a hardware derived ECDSA keypair for individual attestation.
+ * Get the current u2f state from the board.
*
- * @param seed ptr to store 32-byte seed to regenerate this key on this chip
- * @param d pointer to ECDSA private key
- * @param pk_x pointer to public key point
- * @param pk_y pointer to public key point
+ * @return pointer to static state if successful, NULL otherwise
+ */
+struct u2f_state *u2f_get_state(void);
+
+/**
+ * Try to load U2F keys or create if failed.
*
- * @return EC_SUCCESS if a valid keypair was created.
+ * @param state - buffer for state to load/create
+ * @param force_create - if true, always create all keys
+ *
+ * @return true if state is properly initialized and will persist in flash.
*/
-int g2f_individual_keypair(p256_int *d, p256_int *pk_x, p256_int *pk_y);
+bool u2f_load_or_create_state(struct u2f_state *state, bool force_create);
/***
* Generates and persists to nvram a new seed that will be used to
@@ -133,33 +208,22 @@ int g2f_individual_keypair(p256_int *d, p256_int *pk_x, p256_int *pk_y);
* @return EC_SUCCESS if seed was successfully created
* (and persisted if requested).
*/
-int u2f_gen_kek_seed(int commit);
-
-/* Maximum size in bytes of G2F attestation certificate. */
-#define G2F_ATTESTATION_CERT_MAX_LEN 315
+enum ec_error_list u2f_gen_kek_seed(int commit);
/**
- * Gets the x509 certificate for the attestation keypair returned
- * by g2f_individual_keypair().
+ * Zeroize U2F keys. Can be used to switch to FIPS-compliant path by
+ * destroying old keys.
*
- * @param buf pointer to a buffer that must be at least
- * G2F_ATTESTATION_CERT_MAX_LEN bytes.
- * @return size of certificate written to buf, 0 on error.
+ * @return true if state is properly initialized and will persist in flash.
*/
-int g2f_attestation_cert(uint8_t *buf);
+enum ec_error_list u2f_zeroize_keys(void);
/**
- * U2F_GENERATE command handler. Generates a key handle according to input
- * parameters.
- */
-enum vendor_cmd_rc u2f_generate(enum vendor_cmd_cc code, void *buf,
- size_t input_size, size_t *response_size);
-
-/**
- * U2F_SIGN command handler. Verifies a key handle is owned and signs data with
- * it.
+ * Update keys to a newer (FIPS-compliant) version if needed. Do nothing if
+ * keys are already updated.
+ *
+ * @return EC_SUCCESS or error code.
*/
-enum vendor_cmd_rc u2f_sign(enum vendor_cmd_cc code, void *buf,
- size_t input_size, size_t *response_size);
+enum ec_error_list u2f_update_keys(void);
#endif /* __CROS_EC_U2F_IMPL_H */