diff options
author | Vadim Sukhomlinov <sukhomlinov@google.com> | 2021-07-16 23:51:06 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-08-24 23:49:46 +0000 |
commit | 5ae1c684271a117539858cb12252959dfe46803c (patch) | |
tree | 6c792a27812fdc159723bda6b768b210b88b3f28 /include/u2f_impl.h | |
parent | ea35354507745b5763bb85479a26409920ed37b0 (diff) | |
download | chrome-ec-5ae1c684271a117539858cb12252959dfe46803c.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.
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: I85442cddb2959bd3102f7f6e6047134ede90951b
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3034852
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/u2f_impl.h')
-rw-r--r-- | include/u2f_impl.h | 296 |
1 files changed, 205 insertions, 91 deletions
diff --git a/include/u2f_impl.h b/include/u2f_impl.h index 75e50cc6c7..fa59424595 100644 --- a/include/u2f_impl.h +++ b/include/u2f_impl.h @@ -9,121 +9,246 @@ #define __CROS_EC_U2F_IMPL_H #include "common.h" - #include "dcrypto.h" -#include "tpm_vendor_cmds.h" -#include "u2f.h" +/* ---- platform cryptography hooks ---- */ -/* ---- Physical presence ---- */ +#define U2F_MAX_KH_SIZE 128 /* Max size of key handle */ -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. +/* Forward declarations to reduce dependencies. */ +/* EC (uncompressed) point */ +#define U2F_EC_KEY_SIZE P256_NBYTES /* EC key size in bytes */ +#define U2F_EC_POINT_SIZE ((U2F_EC_KEY_SIZE * 2) + 1) /* Size of EC point */ + +#define U2F_POINT_UNCOMPRESSED 0x04 /* Uncompressed point format */ + +struct u2f_ec_point { + uint8_t pointFormat; /* Point type */ + uint8_t x[U2F_EC_KEY_SIZE]; /* X-value */ + uint8_t y[U2F_EC_KEY_SIZE]; /* Y-value */ +}; + +BUILD_ASSERT(sizeof(struct u2f_ec_point) == U2F_EC_POINT_SIZE); + +struct u2f_signature { + uint8_t sig_r[U2F_EC_KEY_SIZE]; /* Signature */ + uint8_t sig_s[U2F_EC_KEY_SIZE]; /* Signature */ +}; + +/* Origin seed is a random nonce generated during key handle creation. */ +#define U2F_ORIGIN_SEED_SIZE 32 +#define U2F_AUTHORIZATION_SALT_SIZE 16 + +#define U2F_V0_KH_SIZE 64 + +/* 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]; +}; + +BUILD_ASSERT(sizeof(struct u2f_key_handle_v0) <= U2F_MAX_KH_SIZE); +BUILD_ASSERT(sizeof(struct u2f_key_handle_v0) == U2F_V0_KH_SIZE); + +/** + * Key handle version = 1 for WebAuthn, bound to device and user. */ -enum touch_state pop_check_presence(int consume); +#define U2F_V1_KH_SIZE 113 -/* ---- non-volatile U2F state ---- */ +/* 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_state { - uint32_t salt[8]; - uint32_t salt_kek[8]; - uint32_t salt_kh[8]; +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]; +}; + +BUILD_ASSERT(sizeof(struct u2f_key_handle_v1) <= U2F_MAX_KH_SIZE); +BUILD_ASSERT(sizeof(struct u2f_key_handle_v1) == U2F_V1_KH_SIZE); + +union u2f_key_handle_variant { + struct u2f_key_handle_v0 v0; + struct u2f_key_handle_v1 v1; }; +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); /** - * Pack the specified origin, user secret and origin-specific seed - * into a key handle. + * 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); + +/** + * 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(). * - * @return EC_SUCCESS if a valid keypair was created. + * @param state U2F state parameters + * @param serial Device serial number + * @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. */ -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 +258,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 */ |