From 577a2900bd7628f010a514b81e1eb645e03ceda7 Mon Sep 17 00:00:00 2001 From: Vadim Sukhomlinov Date: Wed, 8 Sep 2021 12:12:51 -0700 Subject: cr50: consolidate FIPS module sources under board/cr50/dcrypto To simplify identification of FIPS module boundary, move all sources into same place. BUG=b:134594373 TEST=make buildall -j Signed-off-by: Vadim Sukhomlinov Change-Id: I6acd12d12c00a3362041914bd515534f72a08ab2 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3150057 Reviewed-by: Vadim Sukhomlinov Reviewed-by: Vadim Bendebury Tested-by: Vadim Sukhomlinov Auto-Submit: Vadim Sukhomlinov Commit-Queue: Vadim Bendebury --- board/cr50/build.mk | 6 +- board/cr50/dcrypto/fips.c | 761 +++++++++++++++++++++++++++++++++++++++++ board/cr50/dcrypto/fips.h | 169 +++++++++ board/cr50/dcrypto/fips_rand.c | 407 ++++++++++++++++++++++ board/cr50/dcrypto/fips_rand.h | 134 ++++++++ board/cr50/dcrypto/u2f.c | 639 ++++++++++++++++++++++++++++++++++ board/cr50/dcrypto/u2f_impl.h | 229 +++++++++++++ board/cr50/fips.c | 761 ----------------------------------------- board/cr50/fips.h | 169 --------- board/cr50/fips_rand.c | 407 ---------------------- board/cr50/fips_rand.h | 134 -------- board/cr50/u2f.c | 639 ---------------------------------- chip/host/build.mk | 2 +- include/u2f_impl.h | 229 ------------- test/build.mk | 2 +- 15 files changed, 2344 insertions(+), 2344 deletions(-) create mode 100644 board/cr50/dcrypto/fips.c create mode 100644 board/cr50/dcrypto/fips.h create mode 100644 board/cr50/dcrypto/fips_rand.c create mode 100644 board/cr50/dcrypto/fips_rand.h create mode 100644 board/cr50/dcrypto/u2f.c create mode 100644 board/cr50/dcrypto/u2f_impl.h delete mode 100644 board/cr50/fips.c delete mode 100644 board/cr50/fips.h delete mode 100644 board/cr50/fips_rand.c delete mode 100644 board/cr50/fips_rand.h delete mode 100644 board/cr50/u2f.c delete mode 100644 include/u2f_impl.h diff --git a/board/cr50/build.mk b/board/cr50/build.mk index f9401b9527..4576e30c0e 100644 --- a/board/cr50/build.mk +++ b/board/cr50/build.mk @@ -81,9 +81,9 @@ board-${CONFIG_USB_I2C} += usb_i2c.o board-y += recovery_button.o fips-y= -fips-y += fips.o -fips-y += fips_rand.o -fips-$(CONFIG_U2F) += u2f.o +fips-y += dcrypto/fips.o +fips-y += dcrypto/fips_rand.o +fips-$(CONFIG_U2F) += dcrypto/u2f.o fips-${CONFIG_DCRYPTO_BOARD} += dcrypto/aes.o fips-${CONFIG_DCRYPTO_BOARD} += dcrypto/app_cipher.o fips-${CONFIG_DCRYPTO_BOARD} += dcrypto/app_key.o diff --git a/board/cr50/dcrypto/fips.c b/board/cr50/dcrypto/fips.c new file mode 100644 index 0000000000..349117d49b --- /dev/null +++ b/board/cr50/dcrypto/fips.c @@ -0,0 +1,761 @@ +/* Copyright 2020 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. + */ + +#include "builtin/endian.h" +#include "console.h" +#include "dcrypto.h" +#include "ec_commands.h" +#include "extension.h" +#include "fips.h" +#include "fips_rand.h" +#include "flash.h" +#include "flash_info.h" +#include "flash_log.h" +#include "hooks.h" +#include "new_nvmem.h" +#include "nvmem.h" +#include "nvmem_vars.h" +#include "registers.h" +#include "scratch_reg1.h" +#include "shared_mem.h" +#include "system.h" +#include "tpm_nvmem_ops.h" +#include "u2f_impl.h" + +#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args) + +/** + * Combined FIPS status & global FIPS error. + * default value is = FIPS_UNINITIALIZED + */ +static enum fips_status _fips_status; + +/* Return current FIPS status, but prevent direct modification of state. */ +enum fips_status fips_status(void) +{ + return _fips_status; +} + +#ifdef CRYPTO_TEST_SETUP +/* Flag to simulate specific error condition in power-up tests. */ +uint8_t fips_break_cmd; +#else +/* For production set it to zero, so check is eliminated. */ +#define fips_break_cmd 0 +#endif + +/** + * Return true if no blocking crypto errors detected. + * Until self-integrity works properly (b/138578318), ignore it. + * TODO(b/138578318): remove ignoring of FIPS_FATAL_SELF_INTEGRITY. + */ +static inline bool fips_is_no_crypto_error(void) +{ + return (_fips_status & + (FIPS_ERROR_MASK & (~FIPS_FATAL_SELF_INTEGRITY))) == 0; +} + +/* Return true if crypto can be used (no failures detected). */ +bool fips_crypto_allowed(void) +{ + /** + * We never allow crypto if there were errors, no matter + * if we are in FIPS approved or not-approved mode. + */ + return ((_fips_status & FIPS_POWER_UP_TEST_DONE) && + fips_is_no_crypto_error() && DCRYPTO_ladder_is_enabled()); +} + +int crypto_enabled(void) +{ + return fips_crypto_allowed(); +} + +void fips_throw_err(enum fips_status err) +{ + /* if not a new error, don't write it in the flash log */ + if ((_fips_status & err) == err) + return; + fips_set_status(err); + if (!fips_is_no_crypto_error()) { +#ifdef CONFIG_FLASH_LOG + fips_vtable->flash_log_add_event(FE_LOG_FIPS_FAILURE, + sizeof(_fips_status), + &_fips_status); +#endif + /* Revoke access to secrets in HW Key ladder. */ + DCRYPTO_ladder_revoke(); + } +} + +/** + * Set status of FIPS power-up tests on wake from sleep. We don't want to + * run lengthy KAT & power-up tests on every wake-up, so need to 'cache' + * result in long life register which content persists during sleep mode. + * + * @param asserted: false power-up tests should run on resume, otherwise + * can be skipped. + */ +static void fips_set_power_up(bool asserted) +{ + /* Enable writing to the long life register */ + if (asserted) + GREG32(PMU, PWRDN_SCRATCH22) = BOARD_FIPS_POWERUP_DONE; + else + GREG32(PMU, PWRDN_SCRATCH22) = 0; +} + +/** + * Return true if FIPS KAT tests completed successfully after waking up + * from sleep mode which clears RAM. + */ +static bool fips_is_power_up_done(void) +{ + return !!(GREG32(PMU, PWRDN_SCRATCH22) == BOARD_FIPS_POWERUP_DONE); +} + +void fips_set_status(enum fips_status status) +{ + /** + * if FIPS error took place, drop indication of FIPS approved mode. + * Next cycle of sleep will power-cycle HW crypto components, so any + * soft-errors will be recovered. In case of hard errors it + * will be detected again. + */ + /* Accumulate status (errors). */ + _fips_status |= status; + + status = _fips_status; + /* if we have error, require power up tests on resume. */ + if (status & FIPS_ERROR_MASK) + fips_set_power_up(false); +} + +/** + * Test vectors for Known-Answer Tests (KATs) and driving functions. + */ + +/* KAT for SHA256, test values from OpenSSL. */ +static bool fips_sha256_kat(void) +{ + struct sha256_ctx ctx; + + static const uint8_t in[] = /* "etaonrishd" */ { 0x65, 0x74, 0x61, 0x6f, + 0x6e, 0x72, 0x69, 0x73, + 0x68, 0x64 }; + static const uint8_t ans[] = { 0xf5, 0x53, 0xcd, 0xb8, 0xcf, 0x1, 0xee, + 0x17, 0x9b, 0x93, 0xc9, 0x68, 0xc0, 0xea, + 0x40, 0x91, 0x6, 0xec, 0x8e, 0x11, 0x96, + 0xc8, 0x5d, 0x1c, 0xaf, 0x64, 0x22, 0xe6, + 0x50, 0x4f, 0x47, 0x57 }; + + SHA256_hw_init(&ctx); + SHA256_update(&ctx, in, sizeof(in)); + return !(fips_break_cmd == FIPS_BREAK_SHA256) && + (DCRYPTO_equals(SHA256_final(&ctx), ans, SHA256_DIGEST_SIZE) == + DCRYPTO_OK); +} + +/* KAT for HMAC-SHA256, test values from OpenSSL. */ +static bool fips_hmac_sha256_kat(void) +{ + struct hmac_sha256_ctx ctx; + + static const uint8_t k[SHA256_DIGEST_SIZE] = + /* "etaonrishd" */ { 0x65, 0x74, 0x61, 0x6f, 0x6e, 0x72, 0x69, + 0x73, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + static const uint8_t in[] = + /* "Sample text" */ { 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x20, 0x74, 0x65, 0x78, 0x74 }; + static const uint8_t ans[] = { 0xe9, 0x17, 0xc1, 0x7b, 0x4c, 0x6b, 0x77, + 0xda, 0xd2, 0x30, 0x36, 0x02, 0xf5, 0x72, + 0x33, 0x87, 0x9f, 0xc6, 0x6e, 0x7b, 0x7e, + 0xa8, 0xea, 0xaa, 0x9f, 0xba, 0xee, 0x51, + 0xff, 0xda, 0x24, 0xf4 }; + + HMAC_SHA256_hw_init(&ctx, k, sizeof(k)); + HMAC_SHA256_update(&ctx, in, sizeof(in)); + return !(fips_break_cmd == FIPS_BREAK_HMAC_SHA256) && + (DCRYPTO_equals(HMAC_SHA256_hw_final(&ctx), ans, + SHA256_DIGEST_SIZE) == DCRYPTO_OK); +} + +/** + * DRBG test vector source recorded 6/1/17 from + * http://shortn/_eNfI4wD6j8 -> https://csrc.nist.gov/projects/ + * cryptographic-algorithm-validation-program/random-number-generators + * http://shortn/_9hsazxHKn7 -> https://csrc.nist.gov/CSRC/media/Projects/ + * Cryptographic-Algorithm-Validation-Program/documents/drbg/drbgtestvectors.zip + * Input values: + * [SHA-256] + * [PredictionResistance = True] + * [EntropyInputLen = 256] + * [NonceLen = 128] + * [PersonalizationStringLen = 256] + * [AdditionalInputLen = 256] + * [ReturnedBitsLen = 1024] + * COUNT = 0 + * EntropyInput = + * 4294671d493dc085b5184607d7de2ff2b6aceb734a1b026f6cfee7c5a90f03da + * Nonce = d071544e599235d5eb38b64b551d2a6e + * PersonalizationString = + * 63bc769ae1d95a98bde870e4db7776297041d37c8a5c688d4e024b78d83f4d78 + * AdditionalInput = + * 28848becd3f47696f124f4b14853a456156f69be583a7d4682cff8d44b39e1d3 + * EntropyInputPR = + * db9b4790b62336fbb9a684b82947065393eeef8f57bd2477141ad17e776dac34 + * AdditionalInput = + * 8bfce0b7132661c3cd78175d83926f643e36f7608eec2c5dac3ddcbacc8c2182 + * EntropyInputPR = + * 4a9abe80f6f522f29878bedf8245b27940a76471006fb4a4110beb4decb6c341 + * ReturnedBits = + * e580dc969194b2b18a97478aef9d1a72390aff14562747bf080d741527a6655 + * ce7fc135325b457483a9f9c70f91165a811cf4524b50d51199a0df3bd60d12abac27d0bf6618 + * e6b114e05420352e23f3603dfe8a225dc19b3d1fff1dc245dc6b1df24c741744bec3f9437dbb + * f222df84881a457a589e7815ef132f686b760f012 + * DRBG KAT generation sequence: + * hmac_drbg_init(entropy0, nonce0, perso0) + * hmac_drbg_reseed(entropy1, addtl_input1) + * hmac_drbg_generate() + * hmac_drbg_reseed(entropy2, addtl_input2) + * hmac_drbg_generate() + */ +static const uint8_t drbg_entropy0[] = { + 0x42, 0x94, 0x67, 0x1d, 0x49, 0x3d, 0xc0, 0x85, 0xb5, 0x18, 0x46, + 0x07, 0xd7, 0xde, 0x2f, 0xf2, 0xb6, 0xac, 0xeb, 0x73, 0x4a, 0x1b, + 0x02, 0x6f, 0x6c, 0xfe, 0xe7, 0xc5, 0xa9, 0x0f, 0x03, 0xda +}; +static const uint8_t drbg_nonce0[] = { 0xd0, 0x71, 0x54, 0x4e, 0x59, 0x92, + 0x35, 0xd5, 0xeb, 0x38, 0xb6, 0x4b, + 0x55, 0x1d, 0x2a, 0x6e }; +static const uint8_t drbg_perso0[] = { 0x63, 0xbc, 0x76, 0x9a, 0xe1, 0xd9, 0x5a, + 0x98, 0xbd, 0xe8, 0x70, 0xe4, 0xdb, 0x77, + 0x76, 0x29, 0x70, 0x41, 0xd3, 0x7c, 0x8a, + 0x5c, 0x68, 0x8d, 0x4e, 0x02, 0x4b, 0x78, + 0xd8, 0x3f, 0x4d, 0x78 }; + +static const uint8_t drbg_entropy1[] = { + 0xdb, 0x9b, 0x47, 0x90, 0xb6, 0x23, 0x36, 0xfb, 0xb9, 0xa6, 0x84, + 0xb8, 0x29, 0x47, 0x06, 0x53, 0x93, 0xee, 0xef, 0x8f, 0x57, 0xbd, + 0x24, 0x77, 0x14, 0x1a, 0xd1, 0x7e, 0x77, 0x6d, 0xac, 0x34 +}; +static const uint8_t drbg_addtl_input1[] = { + 0x28, 0x84, 0x8b, 0xec, 0xd3, 0xf4, 0x76, 0x96, 0xf1, 0x24, 0xf4, + 0xb1, 0x48, 0x53, 0xa4, 0x56, 0x15, 0x6f, 0x69, 0xbe, 0x58, 0x3a, + 0x7d, 0x46, 0x82, 0xcf, 0xf8, 0xd4, 0x4b, 0x39, 0xe1, 0xd3 +}; + +static const uint8_t drbg_entropy2[] = { + 0x4a, 0x9a, 0xbe, 0x80, 0xf6, 0xf5, 0x22, 0xf2, 0x98, 0x78, 0xbe, + 0xdf, 0x82, 0x45, 0xb2, 0x79, 0x40, 0xa7, 0x64, 0x71, 0x00, 0x6f, + 0xb4, 0xa4, 0x11, 0x0b, 0xeb, 0x4d, 0xec, 0xb6, 0xc3, 0x41 +}; +static const uint8_t drbg_addtl_input2[] = { + 0x8b, 0xfc, 0xe0, 0xb7, 0x13, 0x26, 0x61, 0xc3, 0xcd, 0x78, 0x17, + 0x5d, 0x83, 0x92, 0x6f, 0x64, 0x3e, 0x36, 0xf7, 0x60, 0x8e, 0xec, + 0x2c, 0x5d, 0xac, 0x3d, 0xdc, 0xba, 0xcc, 0x8c, 0x21, 0x82 +}; + +/* Known-answer test for HMAC_DRBG SHA256 instantiate. */ +static bool fips_hmac_drbg_instantiate_kat(struct drbg_ctx *ctx) +{ + /* Expected internal drbg state */ + static const uint32_t K0[] = { 0x7fe2b43a, 0x94f11b33, 0x2b76c5ce, + 0xfbb784af, 0x81cfe716, 0xc43596d6, + 0xbdfe968b, 0x189c80fb }; + static const uint32_t V0[] = { 0xc42b237a, 0x929cdd0b, 0xe7fbafdd, + 0xba22a36a, 0x4d23471a, 0xd8607022, + 0x687e18ac, 0x2ac08007 }; + + hmac_drbg_init(ctx, drbg_entropy0, sizeof(drbg_entropy0), + drbg_nonce0, sizeof(drbg_nonce0), drbg_perso0, + sizeof(drbg_perso0)); + + return (DCRYPTO_equals(ctx->v, V0, sizeof(V0)) == DCRYPTO_OK) && + (DCRYPTO_equals(ctx->k, K0, sizeof(K0)) == DCRYPTO_OK); +} + +/* Known-answer test for HMAC_DRBG SHA256 reseed. */ +static bool fips_hmac_drbg_reseed_kat(struct drbg_ctx *ctx) +{ + /* Expected internal drbg state */ + static const uint32_t K1[] = { 0x3118D36E, 0x05DEEC48, 0x7EFB6363, + 0x3D575CDE, 0xCFCD14C1, 0x8D4F937D, + 0x896B811E, 0x0EF038EB }; + static const uint32_t V1[] = { 0xC8ED8EEC, 0x24DD7B66, 0x09C635CD, + 0x6AC74196, 0xC70067D7, 0xC2E71FEF, + 0x918D9EB7, 0xAE0CD544 }; + + hmac_drbg_reseed(ctx, drbg_entropy1, sizeof(drbg_entropy1), + drbg_addtl_input1, sizeof(drbg_addtl_input1), NULL, 0); + + return (DCRYPTO_equals(ctx->v, V1, sizeof(V1)) == DCRYPTO_OK) && + (DCRYPTO_equals(ctx->k, K1, sizeof(K1)) == DCRYPTO_OK); +} + +/* Known-answer test for HMAC_DRBG SHA256 generate. */ +static bool fips_hmac_drbg_generate_kat(struct drbg_ctx *ctx) +{ + /* Expected internal drbg state */ + static const uint32_t K2[] = { 0x980ccd6a, 0x0b34f7e1, 0x594aabd7, + 0x33b66049, 0xb919bd57, 0x8ecc7194, + 0xaf1748a3, 0x80982577 }; + static const uint32_t V2[] = { 0xe4927cdb, 0xb3435cc5, 0x601ab870, + 0x46e1f024, 0x966ca875, 0x102b4167, + 0xa71e5dce, 0xe4c15962 }; + /* Expected output */ + static const uint8_t KA[] = { + 0xe5, 0x80, 0xdc, 0x96, 0x91, 0x94, 0xb2, 0xb1, 0x8a, 0x97, + 0x47, 0x8a, 0xef, 0x9d, 0x1a, 0x72, 0x39, 0x0a, 0xff, 0x14, + 0x56, 0x27, 0x47, 0xbf, 0x08, 0x0d, 0x74, 0x15, 0x27, 0xa6, + 0x65, 0x5c, 0xe7, 0xfc, 0x13, 0x53, 0x25, 0xb4, 0x57, 0x48, + 0x3a, 0x9f, 0x9c, 0x70, 0xf9, 0x11, 0x65, 0xa8, 0x11, 0xcf, + 0x45, 0x24, 0xb5, 0x0d, 0x51, 0x19, 0x9a, 0x0d, 0xf3, 0xbd, + 0x60, 0xd1, 0x2a, 0xba, 0xc2, 0x7d, 0x0b, 0xf6, 0x61, 0x8e, + 0x6b, 0x11, 0x4e, 0x05, 0x42, 0x03, 0x52, 0xe2, 0x3f, 0x36, + 0x03, 0xdf, 0xe8, 0xa2, 0x25, 0xdc, 0x19, 0xb3, 0xd1, 0xff, + 0xf1, 0xdc, 0x24, 0x5d, 0xc6, 0xb1, 0xdf, 0x24, 0xc7, 0x41, + 0x74, 0x4b, 0xec, 0x3f, 0x94, 0x37, 0xdb, 0xbf, 0x22, 0x2d, + 0xf8, 0x48, 0x81, 0xa4, 0x57, 0xa5, 0x89, 0xe7, 0x81, 0x5e, + 0xf1, 0x32, 0xf6, 0x86, 0xb7, 0x60, 0xf0, 0x12 + }; + uint8_t buf[128]; + + hmac_drbg_generate(ctx, buf, sizeof(buf), NULL, 0); + /* Verify internal drbg state */ + if (DCRYPTO_equals(ctx->v, V2, sizeof(V2)) != DCRYPTO_OK || + DCRYPTO_equals(ctx->k, K2, sizeof(K2)) != DCRYPTO_OK) { + return false; + } + + hmac_drbg_reseed(ctx, drbg_entropy2, sizeof(drbg_entropy2), + drbg_addtl_input2, sizeof(drbg_addtl_input2), NULL, 0); + /** + * reuse entropy buffer to avoid allocating too much stack and memory + * it will be cleaned up in TRNG health test + */ + hmac_drbg_generate(ctx, buf, sizeof(buf), NULL, 0); + return !(fips_break_cmd == FIPS_BREAK_HMAC_DRBG) && + DCRYPTO_equals(buf, KA, sizeof(KA) == DCRYPTO_OK); +} + +/* Known-answer test for HMAC_DRBG SHA256. */ +static bool fips_hmac_drbg_kat(void) +{ + struct drbg_ctx ctx; + + return fips_hmac_drbg_instantiate_kat(&ctx) && + fips_hmac_drbg_reseed_kat(&ctx) && + fips_hmac_drbg_generate_kat(&ctx); +} + +/* Known-answer test for ECDSA NIST P-256 verify. */ +static bool fips_ecdsa_verify_kat(void) +{ + static const p256_int qx = { .a = { 0xf49abf3c, 0xf82e6e12, 0x7a67c074, + 0x5134e16f, 0xf8957a0c, 0xef4344a7, + 0xd4bb3cb7, 0xe424dc61 } }; + static const p256_int qy = { .a = { 0xdfaee927, 0x3d6f60e7, 0xac85d124, + 0x127e5965, 0xe1dddaf0, 0x1545949d, + 0xa2bc4865, 0x970eed7a } }; + static const p256_int r = { .a = { 0xd9347f4f, 0xb72f981f, 0x6349b9da, + 0x2ff540c7, 0x42017c64, 0x910be331, + 0xa49c705c, 0xbf96b99a } }; + static const p256_int s = { .a = { 0x57ec871c, 0x920b9e0f, 0x75d98f31, + 0x444e3230, 0x15abdf12, 0xe03b9cd4, + 0x819089c2, 0x17c55095 } }; + static const uint8_t msg[128] = { + 0xe1, 0x13, 0x0a, 0xf6, 0xa3, 0x8c, 0xcb, 0x41, 0x2a, 0x9c, + 0x8d, 0x13, 0xe1, 0x5d, 0xbf, 0xc9, 0xe6, 0x9a, 0x16, 0x38, + 0x5a, 0xf3, 0xc3, 0xf1, 0xe5, 0xda, 0x95, 0x4f, 0xd5, 0xe7, + 0xc4, 0x5f, 0xd7, 0x5e, 0x2b, 0x8c, 0x36, 0x69, 0x92, 0x28, + 0xe9, 0x28, 0x40, 0xc0, 0x56, 0x2f, 0xbf, 0x37, 0x72, 0xf0, + 0x7e, 0x17, 0xf1, 0xad, 0xd5, 0x65, 0x88, 0xdd, 0x45, 0xf7, + 0x45, 0x0e, 0x12, 0x17, 0xad, 0x23, 0x99, 0x22, 0xdd, 0x9c, + 0x32, 0x69, 0x5d, 0xc7, 0x1f, 0xf2, 0x42, 0x4c, 0xa0, 0xde, + 0xc1, 0x32, 0x1a, 0xa4, 0x70, 0x64, 0xa0, 0x44, 0xb7, 0xfe, + 0x3c, 0x2b, 0x97, 0xd0, 0x3c, 0xe4, 0x70, 0xa5, 0x92, 0x30, + 0x4c, 0x5e, 0xf2, 0x1e, 0xed, 0x9f, 0x93, 0xda, 0x56, 0xbb, + 0x23, 0x2d, 0x1e, 0xeb, 0x00, 0x35, 0xf9, 0xbf, 0x0d, 0xfa, + 0xfd, 0xcc, 0x46, 0x06, 0x27, 0x2b, 0x20, 0xa3 + }; + + p256_int p256_digest; + struct sha256_digest digest; + uint8_t bad_msg[128]; + int passed; + + SHA256_hw_hash(msg, sizeof(msg), &digest); + p256_from_bin(digest.b8, &p256_digest); + passed = dcrypto_p256_ecdsa_verify(&qx, &qy, &p256_digest, &r, &s); + if (!passed) + return false; + /** + * create bad_msg same as msg but has one bit flipped in byte 92 (0x0a + * vs 0x1a) this is to save space in flash vs. having bad message as + * constant + */ + memcpy(bad_msg, msg, sizeof(msg)); + bad_msg[92] ^= 0x10; + SHA256_hw_hash(bad_msg, sizeof(bad_msg), &digest); + p256_from_bin(digest.b8, &p256_digest); + passed = dcrypto_p256_ecdsa_verify(&qx, &qy, &p256_digest, &r, &s); + return !(fips_break_cmd == FIPS_BREAK_ECDSA) && (passed == 0); +} + +#ifdef CONFIG_FIPS_AES_CBC_256 +#define AES_BLOCK_LEN 16 + +/* Known-answer test for AES-256 encrypt/decrypt. */ +static bool fips_aes256_kat(void) +{ + uint8_t enc[AES_BLOCK_LEN]; + uint8_t dec[AES_BLOCK_LEN]; + uint8_t iv[AES_BLOCK_LEN]; + + static const uint8_t kat_aes128_k[AES256_BLOCK_CIPHER_KEY_SIZE] = { + 0x65, 0x74, 0x61, 0x6f, 0x6e, 0x72, 0x69, 0x73, + 0x68, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + static const uint8_t kat_aes128_msg[AES_BLOCK_LEN] = { + 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, + 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA + }; + + static const uint8_t ans_aes128[AES_BLOCK_LEN] = { + 0x64, 0x62, 0x89, 0x41, 0x73, 0x63, 0x70, 0xe9, + 0x12, 0x7e, 0xa7, 0x1b, 0x1b, 0xc3, 0x57, 0x8d + }; + + memset(iv, 0, sizeof(iv)); + DCRYPTO_aes_init(kat_aes128_k, 256, iv, CIPHER_MODE_CBC, ENCRYPT_MODE); + DCRYPTO_aes_block(kat_aes128_msg, enc); + if (memcmp(enc, ans_aes128, AES_BLOCK_LEN)) + return false; + + DCRYPTO_aes_init(kat_aes128_k, 256, iv, CIPHER_MODE_CBC, DECRYPT_MODE); + DCRYPTO_aes_block(enc, dec); + + return !(fips_break_cmd == FIPS_BREAK_AES256) && + (memcmp(kat_aes128_msg, dec, AES_BLOCK_LEN) == 0); +} +#endif + +#ifdef CONFIG_FIPS_RSA2048 +/* Known-answer test for RSA 2048. */ +static bool fips_rsa2048_verify_kat(void) +{ + struct sha256_digest digest; + static const uint32_t pub[64] = { + 0xf8729219, 0x2b42fc45, 0xfe6f4397, 0xa6ba59df, 0x4ce45ab8, + 0x4be044ea, 0xdade58ec, 0xf871ada6, 0x3a6355a1, 0x43739940, + 0x2fbdff33, 0x3e6f8953, 0xd2f99a29, 0xb0835670, 0x4d9144e1, + 0x3518387f, 0x808bef09, 0x1f612714, 0xa109e770, 0xcf0f4123, + 0x1d74505e, 0xa9b7c557, 0x176fcc28, 0xe0e86a16, 0x699b54eb, + 0x2c3514b8, 0xf236634f, 0xf4f5b4ae, 0x12d180a4, 0x5e587a1a, + 0xd7b9bd27, 0x649965dc, 0x5097e8aa, 0xa42c8ae7, 0x1e252547, + 0x11ed1901, 0x898ed7c4, 0x05705388, 0x866ac091, 0x5769c900, + 0x05108735, 0xca60769e, 0x7ab9ae85, 0xce7440eb, 0xe60eb7c8, + 0xd8d80ee8, 0xa151febc, 0x93d49bbc, 0xc0a79b3f, 0x48dbad30, + 0x9ff65c53, 0x2db20805, 0x175d83de, 0xfffceebd, 0x203e209e, + 0xafee1f86, 0x39b46031, 0x36b0c302, 0x85222b79, 0x891b7941, + 0x69d37fab, 0xec6cca57, 0xc81e692b, 0xd5e1b4e8 + }; + static const uint8_t sig[256] = { + 0x02, 0xa7, 0x8c, 0x15, 0x44, 0x00, 0x44, 0x2f, 0x2e, 0x45, + 0xb2, 0xf6, 0x11, 0x01, 0xdf, 0xcf, 0x28, 0xfd, 0x50, 0xf2, + 0x89, 0x59, 0x7c, 0x93, 0x1f, 0xec, 0x7d, 0xf9, 0xf7, 0x66, + 0xf1, 0xf5, 0x9d, 0x81, 0xad, 0x7a, 0x05, 0xcd, 0x93, 0xea, + 0x93, 0x0a, 0x41, 0x60, 0x34, 0x3d, 0xeb, 0x2f, 0x87, 0x8f, + 0x25, 0x13, 0x07, 0x61, 0xd8, 0x86, 0x64, 0xca, 0x74, 0xd7, + 0xff, 0xbf, 0xc3, 0xdc, 0xef, 0x5a, 0xcf, 0xa0, 0xff, 0x3a, + 0xe5, 0x91, 0x4b, 0xd1, 0xa6, 0x01, 0xe5, 0xb0, 0x98, 0xf5, + 0x01, 0x65, 0xe6, 0x62, 0xf4, 0x51, 0x15, 0xc0, 0xba, 0xe6, + 0xee, 0x0a, 0xa5, 0x83, 0xfb, 0x25, 0x1d, 0x09, 0x95, 0x49, + 0xc0, 0xf7, 0x32, 0x2d, 0x44, 0x49, 0xa4, 0x51, 0xa7, 0x2c, + 0xa5, 0x79, 0xc9, 0x80, 0x90, 0xd8, 0x3c, 0xd5, 0x25, 0x37, + 0x31, 0x04, 0xb1, 0x9b, 0x3e, 0xed, 0x3e, 0x49, 0x2c, 0xc2, + 0x11, 0xf2, 0x58, 0x36, 0x6c, 0x63, 0x15, 0xef, 0x34, 0x81, + 0xb2, 0xb8, 0xa3, 0x6b, 0x4a, 0x87, 0x0f, 0xd8, 0x87, 0x27, + 0x76, 0x2c, 0x51, 0x7d, 0xa3, 0x8e, 0xc7, 0xa1, 0x08, 0x47, + 0x35, 0xa4, 0x63, 0xd2, 0xe6, 0x05, 0x70, 0x15, 0x12, 0xbe, + 0x38, 0x95, 0x15, 0x3c, 0xf7, 0xed, 0xb0, 0x1a, 0xba, 0x81, + 0x93, 0x08, 0xe6, 0xec, 0x08, 0xe9, 0x5f, 0x35, 0x9d, 0x12, + 0xc2, 0xf7, 0x0f, 0xfc, 0x67, 0x40, 0x69, 0x90, 0x6e, 0x0a, + 0x3d, 0x3b, 0x83, 0x66, 0x2e, 0xee, 0x3d, 0xad, 0xad, 0xdd, + 0x46, 0xfd, 0x3d, 0x9b, 0x00, 0xd8, 0x45, 0xa6, 0xb5, 0x20, + 0x29, 0x88, 0x5f, 0x92, 0xa0, 0x63, 0x5f, 0x51, 0x17, 0xfb, + 0xde, 0xb2, 0x05, 0xb6, 0xc8, 0x4e, 0x58, 0x2b, 0xfc, 0xc5, + 0x04, 0x7d, 0x17, 0x4c, 0xd6, 0x7c, 0x05, 0xed, 0x10, 0xf8, + 0x98, 0x1e, 0xb2, 0x3a, 0x6c, 0x6d + }; + static const uint8_t msg[128] = { + 0x2d, 0xfc, 0x5d, 0xbd, 0x44, 0x2a, 0xb6, 0x48, 0x1d, 0x6c, + 0xc7, 0xce, 0xa4, 0xcd, 0x01, 0x47, 0xff, 0xae, 0xd2, 0xbe, + 0x1d, 0x0a, 0xd5, 0xb2, 0x92, 0xfe, 0x46, 0xbb, 0xa2, 0x88, + 0xb8, 0x71, 0x9b, 0x8f, 0x0a, 0x89, 0x69, 0x23, 0x97, 0x41, + 0x64, 0x07, 0xad, 0xff, 0x6c, 0x6c, 0x41, 0x34, 0x38, 0x00, + 0xe0, 0x87, 0xeb, 0x27, 0xe9, 0x30, 0xe8, 0x88, 0xfa, 0xa1, + 0xe8, 0xcc, 0xa8, 0x6c, 0x4a, 0xa2, 0x73, 0x61, 0xaa, 0x07, + 0xf8, 0xf6, 0xb4, 0xc4, 0x69, 0xed, 0x3a, 0x38, 0x3b, 0x30, + 0x85, 0x57, 0x1e, 0x00, 0xe9, 0xf3, 0x32, 0x4e, 0x9c, 0x3b, + 0x78, 0x69, 0xc9, 0x81, 0x87, 0xda, 0xdf, 0x40, 0x80, 0x8c, + 0x2f, 0x5d, 0x43, 0x31, 0xb6, 0xad, 0xe3, 0xe0, 0x37, 0xb8, + 0x58, 0x03, 0x8e, 0xbc, 0x74, 0x70, 0x40, 0xf5, 0x19, 0xd6, + 0x56, 0x1c, 0xa8, 0x5b, 0x6c, 0x2e, 0xbc, 0x83 + }; + /* same as msg but has one bit flipped */ + static const uint8_t bad_msg[128] = { + 0x2d, 0xfc, 0x5d, 0xbd, 0x44, 0x2a, 0xb6, 0x48, 0x1d, 0x6c, + 0xc7, 0xce, 0xa4, 0xcd, 0x01, 0x47, 0xff, 0xae, 0xd2, 0xbe, + 0x1d, 0x0a, 0xd5, 0xb2, 0x92, 0xfe, 0x46, 0xbb, 0xa2, 0x88, + 0xb8, 0x71, 0x9b, 0x8f, 0x0a, 0x89, 0x69, 0x23, 0x97, 0x41, + 0x64, 0x07, 0xad, 0xff, 0x6c, 0x6c, 0x41, 0x34, 0x38, 0x00, + 0xe0, 0x87, 0xeb, 0x27, 0xe9, 0x30, 0xe8, 0x88, 0xfa, 0xa1, + 0xe8, 0xcc, 0xa8, 0x6c, 0x4a, 0xa2, 0x73, 0x61, 0xaa, 0x07, + 0xf8, 0xf6, 0xb4, 0xc5, 0x69, 0xed, /**/ + 0x3a, 0x38, 0x3b, 0x30, 0x85, 0x57, 0x1e, 0x00, 0xe9, 0xf3, + 0x32, 0x4e, 0x9c, 0x3b, 0x78, 0x69, 0xc9, 0x81, 0x87, 0xda, + 0xdf, 0x40, 0x80, 0x8c, 0x2f, 0x5d, 0x43, 0x31, 0xb6, 0xad, + 0xe3, 0xe0, 0x37, 0xb8, 0x58, 0x03, 0x8e, 0xbc, 0x74, 0x70, + 0x40, 0xf5, 0x19, 0xd6, 0x56, 0x1c, 0xa8, 0x5b, 0x6c, 0x2e, + 0xbc, 0x83 + }; + static const struct RSA rsa = { + .e = 0x00010001, + .N = { .dmax = sizeof(pub) / 4, + .d = (struct access_helper *)pub } + }; + + int passed; + + SHA256_hw_hash(msg, sizeof(msg), &digest); + passed = DCRYPTO_rsa_verify(&rsa, digest.b8, sizeof(digest), sig, + sizeof(sig), PADDING_MODE_PKCS1, + HASH_SHA256); + if (!passed) + return false; + SHA256_hw_hash(bad_msg, sizeof(bad_msg), &digest); + + /* now signature should fail */ + return !DCRYPTO_rsa_verify(&rsa, digest.b8, sizeof(digest), sig, + sizeof(sig), PADDING_MODE_PKCS1, + HASH_SHA256); +} +#endif + +/* Call function using provided stack. */ +static bool call_on_stack(void *new_stack, bool (*func)(void)) +{ + bool result; + /* Call whilst switching stacks */ + __asm__ volatile("mov r4, sp\n" /* save sp */ + "mov sp, %[new_stack]\n" + "blx %[func]\n" + "mov sp, r4\n" /* restore sp */ + "mov %[result], r0\n" + : [result] "=r"(result) + : [new_stack] "r"(new_stack), + [func] "r"(func) + : "r0", "r1", "r2", "r3", "r4", + "lr" /* clobbers */ + ); + return result; +} + +/* Placeholder for SHA256 digest of module computed during build time. */ +const struct sha256_digest fips_integrity + __attribute__((section(".rodata.fips.checksum"))); + +static enum dcrypto_result fips_self_integrity(void) +{ + struct sha256_digest digest; + size_t module_length = &__fips_module_end - &__fips_module_start; + +#ifdef CR50_DEV + CPRINTS("FIPS self-integrity start %x, length %u", + (uintptr_t)&__fips_module_start, module_length); +#endif + SHA256_hw_hash(&__fips_module_start, module_length, &digest); + +#ifdef CR50_DEV + CPRINTS("Stored: %ph", + HEX_BUF(fips_integrity.b8, SHA256_DIGEST_SIZE)); + CPRINTS("Computed: %ph", + HEX_BUF(digest.b8, SHA256_DIGEST_SIZE)); +#endif + + return DCRYPTO_equals(fips_integrity.b8, digest.b8, sizeof(digest)); +} + +/* Duration of FIPS tests. */ +uint64_t fips_last_kat_test_duration; + +#define FIPS_KAT_STACK_SIZE 2048 +void fips_power_up_tests(void) +{ + char *stack_buf; + void *stack; + uint64_t starttime; + + starttime = fips_vtable->get_time().val; + + if (fips_self_integrity() != DCRYPTO_OK) + _fips_status |= FIPS_FATAL_SELF_INTEGRITY; + + /* Make sure hardware is properly configured. */ + if (!DCRYPTO_ladder_is_enabled()) + _fips_status |= FIPS_FATAL_OTHER; + + /** + * Since we are very limited on stack and static RAM, acquire + * shared memory for KAT tests temporary larger stack. + */ + if (EC_SUCCESS == + fips_vtable->shared_mem_acquire(FIPS_KAT_STACK_SIZE, &stack_buf)) { + stack = stack_buf + FIPS_KAT_STACK_SIZE; + if (!call_on_stack(stack, &fips_sha256_kat)) + _fips_status |= FIPS_FATAL_SHA256; + if (!call_on_stack(stack, &fips_hmac_sha256_kat)) + _fips_status |= FIPS_FATAL_HMAC_SHA256; + /** + * Since TRNG FIFO takes some time to fill in, we can mask + * latency by splitting TRNG tests in 2 halves, each + * 2048 bits. This saves 20 ms on start. + * first call to TRNG warm-up + */ + fips_trng_startup(0); + if (!call_on_stack(stack, &fips_ecdsa_verify_kat)) + _fips_status |= FIPS_FATAL_ECDSA; + + if (!call_on_stack(stack, &fips_hmac_drbg_kat)) + _fips_status |= FIPS_FATAL_HMAC_DRBG; + +#ifdef CONFIG_FIPS_AES_CBC_256 + if (!call_on_stack(stack, &fips_aes256_kat)) + _fips_status |= FIPS_FATAL_AES256; +#endif + +#ifdef CONFIG_FIPS_RSA2048 + /* RSA KAT adds 30ms and not used for U2F */ + if (!call_on_stack(stack, &fips_rsa2048_verify_kat)) + _fips_status |= FIPS_FATAL_RSA2048; +#endif + /** + * Grab the SHA hardware lock to force the following KATs to use + * the software implementation. + */ + if (!dcrypto_grab_sha_hw()) + _fips_status |= FIPS_FATAL_SHA256; + + if (!call_on_stack(stack, &fips_sha256_kat)) + _fips_status |= FIPS_FATAL_SHA256; + if (!call_on_stack(stack, &fips_hmac_sha256_kat)) + _fips_status |= FIPS_FATAL_HMAC_SHA256; +#ifdef CONFIG_FIPS_SW_HMAC_DRBG + /* SW HMAC DRBG adds 30ms and not used for U2F */ + if (!call_on_stack(stack, &fips_hmac_drbg_kat)) + _fips_status |= FIPS_FATAL_HMAC_DRBG; +#endif + dcrypto_release_sha_hw(); + fips_vtable->shared_mem_release(stack_buf); + + /* Second call to TRNG warm-up. */ + fips_trng_startup(1); + + /* If no errors, set not to run tests on wake from sleep. */ + if (fips_is_no_crypto_error()) + fips_set_power_up(true); +#ifdef CONFIG_FLASH_LOG + else /* write combined error to flash log */ + fips_vtable->flash_log_add_event(FE_LOG_FIPS_FAILURE, + sizeof(_fips_status), + &_fips_status); +#endif + /* Set the bit that power-up tests completed, even if failed. */ + _fips_status |= FIPS_POWER_UP_TEST_DONE; + } else + _fips_status |= FIPS_FATAL_OTHER; + + fips_last_kat_test_duration = fips_vtable->get_time().val - starttime; +} + +void fips_power_on(void) +{ + fips_last_kat_test_duration = -1ULL; + /* make sure on power-on / resume it's cleared */ + _fips_status = FIPS_UNINITIALIZED; + + /** + * If this was a power-on or power-up tests weren't executed + * for some reason, run them now. Board FIPS KAT status will + * be updated by fips_power_up_tests() if all tests pass. + */ + if (!fips_is_power_up_done()) + fips_power_up_tests(); + else /* tests were already completed before sleep */ + _fips_status |= FIPS_POWER_UP_TEST_DONE; + + /* Check if we can set FIPS-approved mode. */ + if (fips_crypto_allowed()) + fips_set_status(FIPS_MODE_ACTIVE); + +} + +const struct fips_vtable *fips_vtable; + +/** + * Check that given address is in same half of flash as FIPS code. + * This rejects addresses in SRAM and provides additional security. + */ +static bool is_flash_address(const void *ptr) +{ + uintptr_t my_addr = + (uintptr_t)is_flash_address - CONFIG_PROGRAM_MEMORY_BASE; + uintptr_t offset = (uintptr_t)ptr - CONFIG_PROGRAM_MEMORY_BASE; + + if (my_addr >= CONFIG_RW_MEM_OFF && + my_addr < CFG_TOP_A_OFF) + return (offset >= CONFIG_RW_MEM_OFF) && + (offset <= CFG_TOP_A_OFF); + if (my_addr >= CONFIG_RW_B_MEM_OFF && + my_addr < CFG_TOP_B_OFF) + return (offset >= CONFIG_RW_B_MEM_OFF) && + (offset <= CFG_TOP_B_OFF); + + /* Otherwise, we don't know what's going on, don't accept it. */ + return false; +} + +void fips_set_callbacks(const struct fips_vtable *vtable) +{ + if (is_flash_address(vtable) && + is_flash_address(vtable->shared_mem_acquire) && + is_flash_address(vtable->shared_mem_release) && +#ifdef CONFIG_FLASH_LOG + is_flash_address(vtable->flash_log_add_event) && +#endif +#ifdef CONFIG_WATCHDOG + is_flash_address(vtable->watchdog_reload) && +#endif + is_flash_address(vtable->get_time) && + is_flash_address(vtable->task_enable_irq) && + is_flash_address(vtable->task_wait_event_mask) && + is_flash_address(vtable->task_set_event) && + is_flash_address(vtable->task_get_current) && + is_flash_address(vtable->task_start_irq_handler) && + is_flash_address(vtable->task_resched_if_needed) && + is_flash_address(vtable->mutex_lock) && + is_flash_address(vtable->mutex_unlock)) + + fips_vtable = vtable; + else + fips_vtable = NULL; +} diff --git a/board/cr50/dcrypto/fips.h b/board/cr50/dcrypto/fips.h new file mode 100644 index 0000000000..52d8ec68c6 --- /dev/null +++ b/board/cr50/dcrypto/fips.h @@ -0,0 +1,169 @@ +/* Copyright 2020 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 __EC_BOARD_CR50_FIPS_H__ +#define __EC_BOARD_CR50_FIPS_H__ + +#include "common.h" +#include "timer.h" +#include "task.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Signals start in the top most bits, errors in the least significant bits. */ +enum fips_status { + /* FIPS status */ + FIPS_MODE_ACTIVE = 1U << 31, + FIPS_POWER_UP_TEST_DONE = 1U << 30, + + FIPS_UNINITIALIZED = 0, /* Default value */ + + /* FIPS errors */ + FIPS_FATAL_TRNG_RCT = 1 << 1, + FIPS_FATAL_TRNG_APT = 1 << 2, + FIPS_FATAL_TRNG_OTHER = 1 << 3, + FIPS_FATAL_SHA256 = 1 << 4, + FIPS_FATAL_HMAC_SHA256 = 1 << 5, + FIPS_FATAL_HMAC_DRBG = 1 << 6, + FIPS_FATAL_ECDSA = 1 << 7, +#ifdef CONFIG_FIPS_RSA2048 + FIPS_FATAL_RSA2048 = 1 << 8, +#endif +#ifdef CONFIG_FIPS_AES_CBC_256 + FIPS_FATAL_AES256 = 1 << 9, +#endif + FIPS_FATAL_SELF_INTEGRITY = 1 << 10, + FIPS_FATAL_BN_MATH = 1 << 11, + FIPS_FATAL_OTHER = 1 << 15, + FIPS_ERROR_MASK = 0xffff, + FIPS_RFU_MASK = 0x7fff0000 +}; + +/* Simulate error in specific block. */ +enum fips_break { + FIPS_NO_BREAK = 0, + FIPS_BREAK_TRNG = 1, + FIPS_BREAK_SHA256 = 2, + FIPS_BREAK_HMAC_SHA256 = 3, + FIPS_BREAK_HMAC_DRBG = 4, + FIPS_BREAK_ECDSA = 5, + FIPS_BREAK_ECDSA_PWCT = 6, +#ifdef CONFIG_FIPS_AES_CBC_256 + FIPS_BREAK_AES256 = 7, +#endif +#ifdef CONFIG_FIPS_RSA2048 + FIPS_BREAK_RSA2048 = 8, +#endif +}; + +#ifdef CRYPTO_TEST_SETUP +extern uint8_t fips_break_cmd; +#endif + +/* Duration of last FIPS KAT / Power-up tests. */ +extern uint64_t fips_last_kat_test_duration; + +/* Command codes for VENDOR_CC_FIPS_CMD. */ +enum fips_cmd { + FIPS_CMD_GET_STATUS = 0, + FIPS_CMD_ON = 1, + FIPS_CMD_TEST = 2, + FIPS_CMD_BREAK_TRNG = 3, + FIPS_CMD_BREAK_SHA256 = 4, + FIPS_CMD_BREAK_HMAC_SHA256 = 5, + FIPS_CMD_BREAK_HMAC_DRBG = 6, + FIPS_CMD_BREAK_ECDSA = 7, + FIPS_CMD_BREAK_ECDSA_PWCT = 8, +#ifdef CONFIG_FIPS_AES_CBC_256 + FIPS_CMD_BREAK_AES256 = 9, +#endif +#ifdef CONFIG_FIPS_RSA2048 + FIPS_CMD_BREAK_RSA2048 = 10, +#endif + FIPS_CMD_NO_BREAK = 11 +}; + +/* These symbols defined in core/cortex-m/ec.lds.S. */ +extern uint8_t __fips_module_start; +extern uint8_t __fips_module_end; + +/* Return current FIPS status of operations. */ +enum fips_status fips_status(void); + +/** + * Crypto is enabled when either FIPS mode is not enforced, + * or if it is enforced and in good health + * @returns non-zero if crypto can be executed. + */ +bool fips_crypto_allowed(void); + +/** + * Update FIPS status without updating log + */ +void fips_set_status(enum fips_status status); + +/** + * Update FIPS status with error code, write error in the log. + */ +void fips_throw_err(enum fips_status err); + +/** + * FIPS Power-up and known-answer tests. + * Single point of initialization for all FIPS-compliant + * cryptography. Responsible for KATs, TRNG testing, and signalling a + * fatal error. + * + * Set FIPS status globally as a result. + */ +void fips_power_up_tests(void); + +struct fips_vtable { + int (*shared_mem_acquire)(int size, char **dest_ptr); + void (*shared_mem_release)(void *ptr); +#ifdef CONFIG_FLASH_LOG + void (*flash_log_add_event)(uint8_t type, uint8_t size, void *payload); +#endif + void (*cflush)(void); + timestamp_t (*get_time)(void); + + void (*task_enable_irq)(int irq); + uint32_t (*task_wait_event_mask)(uint32_t event_mask, int timeout_us); + uint32_t (*task_set_event)(task_id_t tskid, uint32_t event, int wait); + task_id_t (*task_get_current)(void); + void (*task_start_irq_handler)(void *excep_return); + void (*task_resched_if_needed)(void *excep_return); + void (*mutex_lock)(struct mutex *mtx); + void (*mutex_unlock)(struct mutex *mtx); +#ifdef CONFIG_WATCHDOG + void (*watchdog_reload)(void); +#endif +}; + +/* Pointer to external callbacks used by FIPS module. */ +extern const struct fips_vtable *fips_vtable; + +/** + * Set FIPS module vtable. Called during board_init() phase to provide + * pointers to several system functions required by module to function. + * + * This should be called before any other FIPS functions are invoked as + * vtable is used during FIPS power-up tests. Internally it checks that + * provided vtable and referenced functions are in the same flash bank + * as the FIPS module for additional security. + */ +void fips_set_callbacks(const struct fips_vtable *vtable); + +/** + * Run FIPS self-integrity, power-on and known-answer tests. + * Called from board_init() during power-up and resume from sleep. + * Enables crypto operation on successful completion. + */ +void fips_power_on(void); + +#ifdef __cplusplus +} +#endif +#endif /* __EC_BOARD_CR50_FIPS_H__ */ diff --git a/board/cr50/dcrypto/fips_rand.c b/board/cr50/dcrypto/fips_rand.c new file mode 100644 index 0000000000..926af66f96 --- /dev/null +++ b/board/cr50/dcrypto/fips_rand.c @@ -0,0 +1,407 @@ +/* Copyright 2020 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. + */ + +#include "console.h" +#include "fips.h" +#include "fips_rand.h" +#include "flash_log.h" +#include "init_chip.h" +#include "registers.h" +#include "task.h" +#include "timer.h" +#include "util.h" + +/** + * FIPS-compliant CR50-wide DRBG + * Since raw TRNG input shouldn't be used as random number generator, + * all FIPS-compliant code use DRBG, seeded from TRNG + */ +static struct drbg_ctx fips_drbg; + +#define ENTROPY_SIZE_BITS 512 +#define ENTROPY_SIZE_WORDS (BITS_TO_WORDS(ENTROPY_SIZE_BITS)) + +/** + * buffer for entropy condensing. initialized during + * fips_trng_startup(), but also used in KAT tests, + * thus size is enough to accommodate needs + */ +static uint32_t entropy_fifo[ENTROPY_SIZE_WORDS]; + +/** + * NIST FIPS TRNG health tests (NIST SP 800-90B 4.3) + * If any of the approved continuous health tests are used by the entropy + * source, the false positive probability for these tests shall be set to + * at least 2^-50 + */ + + +/* state data for TRNG health test */ +static struct { + uint32_t count; /* number of 1-bits in APT test window */ + int last_clz; /* running count of leading 0 bits */ + int last_clo; /* running count of leading 1 bits */ + uint8_t pops[APT_WINDOW_SIZE_NWORDS]; + uint8_t rct_count; /* current windows size for RCT */ + uint8_t oldest; /* position in APT window */ + bool apt_initialized; /* flag APT window is filled with data */ + bool drbg_initialized; /* flag DRBG is initialized */ +} rand_state; + +/** + * NIST SP 800-90B 4.4.1 + * The repetition count test detects abnormal runs of 0s or 1s. + * RCT_CUTOFF_BITS must be >= 32. + * If a single value appears more than 100/H times in a row, + * the tests must detect this with high probability. + * + * This implementation assumes TRNG is configured to produce 1-bit + * readings, packed into 32-bit words. + * @return false if test failed + */ +static bool repetition_count_test(uint32_t rnd) +{ + uint32_t clz, ctz, clo, cto; + /* count repeating 0 and 1 bits from each side */ + clz = __builtin_clz(rnd); /* # of leading 0s */ + ctz = __builtin_ctz(rnd); /* # of trailing 0s */ + clo = __builtin_clz(~rnd); /* # of leading 1s */ + cto = __builtin_ctz(~rnd); /* # of trailing 1s */ + + /** + * check that number of trailing 0/1 in current sample added to + * leading 0/1 of previous sample is less than cut off value, so + * we don't have long repetitive series of 0s or 1s + */ + if ((ctz + rand_state.last_clz >= RCT_CUTOFF_SAMPLES) || + (cto + rand_state.last_clo >= RCT_CUTOFF_SAMPLES)) + return false; + + /** + * merge series of repetitive values - update running counters in + * such way that if current sample is all 0s then add previous + * counter of zeros to current number which will be 32, + * otherwise (we had 1s) - just use current value. Same for 1s + */ + if (rnd == 0) /* if all 32 samples are 0s */ + clz += rand_state.last_clz; + + if (rnd == ~0) /* if all 32 samples are 1s */ + clo += rand_state.last_clo; + rand_state.last_clz = clz; + rand_state.last_clo = clo; + + /* check we collected enough bits for statistics */ + if (rand_state.rct_count < RCT_CUTOFF_WORDS) + ++rand_state.rct_count; + return true; +} + +static int misbalanced(uint32_t count) +{ + return count > APT_CUTOFF_SAMPLES || + count < APT_WINDOW_SIZE_BITS - APT_CUTOFF_SAMPLES; +} + +/* Returns number of set bits (1s) in 32-bit word */ +static int popcount(uint32_t x) +{ + x = x - ((x >> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x + (x >> 4)) & 0x0F0F0F0F; + x = x + (x >> 8); + x = x + (x >> 16); + return x & 0x0000003F; +} + +/** + * NIST SP 800-90B 4.4.2 Adaptive Proportion Test. + * Implementation for 1-bit alphabet. + * Instead of storing actual samples we can store pop counts + * of each 32bit reading, which would fit in 8-bit. + */ +bool adaptive_proportion_test(uint32_t rnd) +{ + /* update rolling count */ + rand_state.count -= rand_state.pops[rand_state.oldest]; + /** + * Since we have 1-bit samples, in order to have running window to + * count ratio of 1s and 0s in it we can just store number of 1s in + * each 32-bit sample which requires 1 byte vs. 4 bytes. + * number of zeros = 32 - number of 1s + */ + rand_state.pops[rand_state.oldest] = popcount(rnd); + + /* update rolling count with current sample statistics */ + rand_state.count += rand_state.pops[rand_state.oldest]; + if (++rand_state.oldest >= APT_WINDOW_SIZE_NWORDS) { + rand_state.apt_initialized = true; /* saw full window */ + rand_state.oldest = 0; + } + /* check when initialized */ + if (rand_state.apt_initialized && misbalanced(rand_state.count)) + return false; + return true; +} + +static bool fips_powerup_passed(void) +{ + return rand_state.rct_count >= RCT_CUTOFF_WORDS && + rand_state.apt_initialized; +} + + +/** + * get random from TRNG and run continuous health tests. + * it is also can simulate stuck-bit error + * @param power_up if non-zero indicates warm-up mode + * @return random value from TRNG + */ +static uint64_t fips_trng32(int power_up) +{ + uint64_t r; + + /* Continuous health tests should have been initialized by now */ + if (!(power_up || fips_crypto_allowed())) + return 0; + + /* get noise */ + r = read_rand(); + + if (rand_valid(r)) { + if (!repetition_count_test((uint32_t)r)) { + fips_set_status(FIPS_FATAL_TRNG_RCT); + r = (uint32_t)r; + } + if (!adaptive_proportion_test((uint32_t)r)) { + fips_set_status(FIPS_FATAL_TRNG_APT); + r = (uint32_t)r; + } + } else + fips_set_status(FIPS_FATAL_TRNG_OTHER); + + return r; +} + +bool fips_trng_bytes(void *buffer, size_t len) +{ + uint8_t *buf = (uint8_t *)buffer; + size_t random_togo = 0; + uint64_t rand; + uint32_t r; + /** + * Retrieve random numbers in 4 byte quantities and pack as many bytes + * as needed into 'buffer'. If len is not divisible by 4, the + * remaining random bytes get dropped. + */ + while (len--) { + if (!random_togo) { + rand = fips_trng32(0); + if (!rand_valid(rand)) + return false; + r = (uint32_t)rand; + random_togo = sizeof(r); + } + *buf++ = (uint8_t)r; + random_togo--; + r >>= 8; + } + return true; +} + +/* FIPS TRNG power-up tests */ +bool fips_trng_startup(int stage) +{ + if (!stage) { + /** + * To hide TRNG latency, split it into 2 stages. + * at stage 0, initialize variables + */ + rand_state.rct_count = 0; + rand_state.apt_initialized = 0; + } + /* Startup tests per NIST SP800-90B, Section 4 */ + /* 4096 1-bit samples, in 2 steps, 2048 bit in each */ + for (uint32_t i = 0; i < (TRNG_INIT_WORDS) / 2; i++) { + uint64_t r = fips_trng32(1); + + if (!rand_valid(r)) + return false; + /* store entropy for further use */ + entropy_fifo[i % ARRAY_SIZE(entropy_fifo)] = (uint32_t)r; + } + return fips_powerup_passed(); +} + +bool fips_drbg_init(void) +{ + uint64_t nonce; + uint32_t random; + + if (!fips_crypto_allowed()) + return false; + + /** + * initialize DRBG with 440 bits of entropy as required + * by NIST SP 800-90A 10.1. Includes entropy and nonce, + * both received from entropy source. + * entropy_fifo contains 512 bits of noise with H>= 0.85 + * this is roughly equal to 435 bits of full entropy. + * Add 32 * 0.85 = 27 bits from nonce. + */ + nonce = fips_trng32(0); + if (!rand_valid(nonce)) + return false; + random = (uint32_t)nonce; + + /* read another 512 bits of noise */ + if (!fips_trng_bytes(&entropy_fifo, sizeof(entropy_fifo))) + return false; + + hmac_drbg_init(&fips_drbg, &entropy_fifo, sizeof(entropy_fifo), + &random, sizeof(random), NULL, + 0); + + set_fast_random_seed((uint32_t)fips_trng32(0)); + rand_state.drbg_initialized = 1; + return true; +} + +/* zeroize DRBG state */ +void fips_drbg_clear(void) +{ + drbg_exit(&fips_drbg); + rand_state.drbg_initialized = 0; +} + +enum hmac_result fips_hmac_drbg_generate_reseed(struct drbg_ctx *ctx, void *out, + size_t out_len, + const void *input, + size_t input_len) +{ + enum hmac_result err = + hmac_drbg_generate(ctx, out, out_len, input, input_len); + + while (err == HMAC_DRBG_RESEED_REQUIRED) { + /* read another 512 bits of noise */ + if (!fips_trng_bytes(&entropy_fifo, sizeof(entropy_fifo))) { + /* FIPS error is reported by failed TRNG test. */ + return HMAC_DRBG_RESEED_REQUIRED; + } + + hmac_drbg_reseed(ctx, entropy_fifo, sizeof(entropy_fifo), NULL, + 0, NULL, 0); + err = hmac_drbg_generate(ctx, out, out_len, input, input_len); + } + return err; +} + +bool fips_rand_bytes(void *buffer, size_t len) +{ + if (!fips_crypto_allowed()) + return false; + /** + * make sure cr50 DRBG is initialized after power-on or resume, + * but do it on first use to minimize latency of board_init() + */ + if (!rand_state.drbg_initialized && !fips_drbg_init()) + return false; + + /* HMAC_DRBG can only return up to 7500 bits in a single request */ + while (len) { + size_t request = (len > (7500 / 8)) ? (7500 / 8) : len; + + if (fips_hmac_drbg_generate_reseed(&fips_drbg, buffer, request, + NULL, + 0) != HMAC_DRBG_SUCCESS) + return false; + len -= request; + buffer += request; + } + return true; +} + +/* return codes match dcrypto_p256_ecdsa_sign */ +int fips_p256_ecdsa_sign(const p256_int *key, const p256_int *message, + p256_int *r, p256_int *s) +{ + if (!fips_crypto_allowed()) + return 0; + if (!rand_state.drbg_initialized && !fips_drbg_init()) + return false; + + return dcrypto_p256_ecdsa_sign(&fips_drbg, key, message, r, s); +} + +#ifndef CRYPTO_TEST_CMD_RAND_PERF +#define CRYPTO_TEST_CMD_RAND_PERF 0 +#endif + +#if defined(CRYPTO_TEST_SETUP) && CRYPTO_TEST_CMD_RAND_PERF +#include "endian.h" +#include "extension.h" +#include "trng.h" +#include "watchdog.h" + +static int cmd_rand_perf(int argc, char **argv) +{ + uint64_t starttime; + static uint32_t buf[SHA256_DIGEST_WORDS]; + int j, k; + + starttime = get_time().val; + + /* run power-up tests to measure raw performance */ + if (fips_trng_startup(0) && fips_trng_startup(1)) { + starttime = get_time().val - starttime; + ccprintf("time for fips_trng_startup = %llu\n", starttime); + } else + ccprintf("TRNG power up test failed\n"); + + cflush(); + + starttime = get_time().val; + fips_drbg_init(); + starttime = get_time().val - starttime; + ccprintf("time for drbg_init = %llu\n", starttime); + cflush(); + starttime = get_time().val; + for (k = 0; k < 10; k++) { + for (j = 0; j < 100; j++) + fips_rand_bytes(buf, sizeof(buf)); + watchdog_reload(); + cflush(); + } + starttime = get_time().val - starttime; + ccprintf("time for 1000 drbg reads = %llu\n", starttime); + cflush(); + + starttime = get_time().val; + for (k = 0; k < 10; k++) { + for (j = 0; j < 100; j++) + rand_bytes(&buf, sizeof(buf)); + watchdog_reload(); + } + starttime = get_time().val - starttime; + ccprintf("time for 1000 rand_byte() = %llu\n", starttime); + cflush(); + + starttime = get_time().val; + for (k = 0; k < 10; k++) { + for (j = 0; j < 100; j++) + if (!fips_trng_bytes(&buf, sizeof(buf))) + ccprintf("FIPS TRNG error\n"); + watchdog_reload(); + } + starttime = get_time().val - starttime; + ccprintf("time for 1000 fips_trng_byte() = %llu\n", starttime); + cflush(); + + return 0; +} + +DECLARE_SAFE_CONSOLE_COMMAND(rand_perf, cmd_rand_perf, NULL, NULL); + +#endif /* CRYPTO_TEST_SETUP */ diff --git a/board/cr50/dcrypto/fips_rand.h b/board/cr50/dcrypto/fips_rand.h new file mode 100644 index 0000000000..dca1f473bf --- /dev/null +++ b/board/cr50/dcrypto/fips_rand.h @@ -0,0 +1,134 @@ +/* Copyright 2020 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 __EC_BOARD_CR50_FIPS_RAND_H +#define __EC_BOARD_CR50_FIPS_RAND_H + +#include +#include + +#include "common.h" +#include "dcrypto.h" +#include "util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define TRNG_SAMPLE_BITS 1 + +/** + * Initialize the true random number generator (TRNG) in FIPS-compliant + * way: + * 1. Set 1-bit alphabet + * 2. Set maximum possible range for internal ring-oscillator + * 3. Disable any other post-processing beyond #2 + **/ +void fips_init_trng(void); + +/** + * Returns random number with indication wherever reading is valid. This is + * different from rand() which doesn't provide any indication. + * High 32-bits set to zero in case of error; otherwise value >> 32 == 1 + * Use of uint64_t vs. struct results in more efficient code. + */ +uint64_t read_rand(void); + +/* Return true if read_rand() result contains valid random from TRNG. */ +static inline bool rand_valid(uint64_t rand) +{ + return (rand >> 32) != 0; +} + +/** + * TRNG Health Tests + * + * If any of the approved continuous health tests are used by the entropy + * source, the false positive probability for these tests shall be set to + * at least 2^(-50) (NIST SP 800-90B 4.3). + * Reason for 2^(-50) vs 2^(-40) is to minimize impact to user experience + * due to false positives. + * + * For H1 minimal assessed entropy H >=0.85 for 1-bit samples + * using NIST Entropy Assessment tool. + */ + +/** + * The entropy source's startup tests shall run the continuous health tests + * over at least 4096 consecutive samples. We use 1-bit samples + */ +#define TRNG_INIT_BITS (4096 * TRNG_SAMPLE_BITS) +#define TRNG_INIT_WORDS (BITS_TO_WORDS(TRNG_INIT_BITS)) + +/** + * (1) Repetition Count Test (RCT) NIST SP 800-90B 4.4.1 + * Cut off value is computed as: + * c = ceil(1 + (-log2 alpha)/H); + * alpha = 2^-50, H = 0.85; RCT_CUTOFF = CEIL(1+(50/0.85)) + */ +#define RCT_CUTOFF_SAMPLES 60 + +/** + * Number of 32-bit words containing RCT_CUTOFF_SAMPLES samples + */ +#define RCT_CUTOFF_WORDS (BITS_TO_WORDS(RCT_CUTOFF_SAMPLES)) + +/** + * (2) Adaptive Proportion Test (APT), NIST SP 800-90B 4.4.2, Table 2 + */ +#if TRNG_SAMPLE_BITS == 1 +/* APT Windows size W = 1024 for 1 bit samples */ +#define APT_WINDOW_SIZE_SAMPLES 1024 +#else +/* or 512 samples if more than 1 bit per sample */ +#define APT_WINDOW_SIZE_SAMPLES 512 +#endif +#define APT_WINDOW_SIZE_BITS (APT_WINDOW_SIZE_SAMPLES * TRNG_SAMPLE_BITS) +#define APT_WINDOW_SIZE_NWORDS (BITS_TO_WORDS(APT_WINDOW_SIZE_BITS)) +/** + * Cut off value = CRITBINOM(W, power(2,(-H)),1-α). + * 692 = CRITBINOM(1024, power(2,(-0.85)), 1 - 2^(-50)) + */ +#define APT_CUTOFF_SAMPLES 692 + +/** + * FIPS-compliant TRNG startup. + * The entropy source's startup tests shall run the continuous health tests + * over at least 4096 consecutive samples. + * Note: This function can throw FIPS_FATAL_TRNG error + * + * To hide latency of reading TRNG data, this test is executed in 2 stages + * @param stage is 0 or 1, choosing the stage. On each stage 2048 + * samples are processed. Assuming that some other tasks can be executed + * between stages, when TRNG FIFO if filled with samples. + * + * Some number of samples will be available in entropy_fifo + */ +bool fips_trng_startup(int stage); + +bool fips_trng_bytes(void *buffer, size_t len); + +/* initialize cr50-wide DRBG replacing rand */ +bool fips_drbg_init(void); +/* mark cr50-wide DRBG as not initialized */ +void fips_drbg_init_clear(void); + +/* random bytes using FIPS-compliant HMAC_DRBG */ +bool fips_rand_bytes(void *buffer, size_t len); + +/* wrapper around dcrypto_p256_ecdsa_sign using FIPS-compliant HMAC_DRBG */ +int fips_p256_ecdsa_sign(const p256_int *key, const p256_int *message, + p256_int *r, p256_int *s); +/** + * wrapper around hmac_drbg_generate to automatically reseed drbg + * when needed. + */ +enum hmac_result fips_hmac_drbg_generate_reseed(struct drbg_ctx *ctx, void *out, + size_t out_len, + const void *input, + size_t input_len); +#ifdef __cplusplus +} +#endif +#endif /* ! __EC_BOARD_CR50_FIPS_RAND_H */ diff --git a/board/cr50/dcrypto/u2f.c b/board/cr50/dcrypto/u2f.c new file mode 100644 index 0000000000..9f987fbd64 --- /dev/null +++ b/board/cr50/dcrypto/u2f.c @@ -0,0 +1,639 @@ +/* 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. + */ + +#if defined(CRYPTO_TEST_SETUP) || defined(CR50_DEV) +#include "console.h" +#endif + +#include "dcrypto.h" +#include "fips_rand.h" + +#include "u2f_cmds.h" +#include "u2f_impl.h" +#include "util.h" + +enum ec_error_list u2f_generate_hmac_key(struct u2f_state *state) +{ + /* HMAC key for key handle. */ + if (!fips_rand_bytes(state->hmac_key, sizeof(state->hmac_key))) + return EC_ERROR_HW_INTERNAL; + return EC_SUCCESS; +} + +enum ec_error_list u2f_generate_drbg_entropy(struct u2f_state *state) +{ + state->drbg_entropy_size = 0; + /* Get U2F entropy from health-checked TRNG. */ + if (!fips_trng_bytes(state->drbg_entropy, sizeof(state->drbg_entropy))) + return EC_ERROR_HW_INTERNAL; + state->drbg_entropy_size = sizeof(state->drbg_entropy); + return EC_SUCCESS; +} + +enum ec_error_list u2f_generate_g2f_secret(struct u2f_state *state) +{ + /* G2F specific path. */ + if (!fips_rand_bytes(state->salt, sizeof(state->salt))) + return EC_ERROR_HW_INTERNAL; + return EC_SUCCESS; +} + +/* Compute Key handle's HMAC. */ +static void u2f_origin_user_mac(const struct u2f_state *state, + const uint8_t *user, const uint8_t *origin, + const uint8_t *origin_seed, uint8_t kh_version, + uint8_t *kh_hmac) +{ + struct hmac_sha256_ctx ctx; + + /* HMAC(u2f_hmac_key, origin || user || origin seed || version) */ + + HMAC_SHA256_hw_init(&ctx, state->hmac_key, SHA256_DIGEST_SIZE); + HMAC_SHA256_update(&ctx, origin, U2F_APPID_SIZE); + HMAC_SHA256_update(&ctx, user, U2F_USER_SECRET_SIZE); + HMAC_SHA256_update(&ctx, origin_seed, U2F_ORIGIN_SEED_SIZE); + if (kh_version != 0) + HMAC_SHA256_update(&ctx, &kh_version, sizeof(kh_version)); +#ifdef CR50_DEV_U2F_VERBOSE + ccprintf("origin %ph\n", HEX_BUF(origin, U2F_APPID_SIZE)); + ccprintf("user %ph\n", HEX_BUF(user, U2F_USER_SECRET_SIZE)); + ccprintf("origin_seed %ph\n", + HEX_BUF(origin_seed, U2F_ORIGIN_SEED_SIZE)); + cflush(); +#endif + memcpy(kh_hmac, HMAC_SHA256_final(&ctx), SHA256_DIGEST_SIZE); +#ifdef CR50_DEV_U2F_VERBOSE + ccprintf("kh_hmac %ph\n", HEX_BUF(kh_hmac, SHA256_DIGEST_SIZE)); + cflush(); +#endif +} + +static void u2f_authorization_mac(const struct u2f_state *state, + const union u2f_key_handle_variant *kh, + uint8_t kh_version, + const uint8_t *auth_time_secret_hash, + uint8_t *kh_auth_mac) +{ + struct hmac_sha256_ctx ctx; + const uint8_t *auth_salt = NULL; + const void *kh_header = NULL; + size_t kh_header_size = 0; + + if (kh_version == 0) { + memset(kh_auth_mac, 0xff, SHA256_DIGEST_SIZE); + return; + } + /* At some point we may have v2 key handle, so prepare for it. */ + if (kh_version == 1) { + auth_salt = kh->v1.authorization_salt; + kh_header = &kh->v1; + kh_header_size = U2F_V1_KH_HEADER_SIZE; + } + + /** + * HMAC(u2f_hmac_key, auth_salt || key_handle_header + * || authTimeSecret) + */ + HMAC_SHA256_hw_init(&ctx, state->hmac_key, SHA256_DIGEST_SIZE); + HMAC_SHA256_update(&ctx, auth_salt, U2F_AUTHORIZATION_SALT_SIZE); + HMAC_SHA256_update(&ctx, kh_header, kh_header_size); + + HMAC_SHA256_update(&ctx, auth_time_secret_hash, + U2F_AUTH_TIME_SECRET_SIZE); + + memcpy(kh_auth_mac, HMAC_SHA256_final(&ctx), SHA256_DIGEST_SIZE); +} + +static int app_hw_device_id(enum dcrypto_appid appid, const uint32_t input[8], + uint32_t output[8]) +{ + struct APPKEY_CTX ctx; + int result; + + /** + * Setup USR-based application key. This loads (if not already done) + * application-specific DeviceID. + * Internally it computes: + * HMAC(hw_device_id, SHA256(name[appid])), but we don't care about + * process. + * Important property: + * For same appid it will load same value. + */ + if (!DCRYPTO_appkey_init(appid, &ctx)) + return 0; + + /** + * Compute HMAC(HMAC(hw_device_id, SHA256(name[appid])), input) + * It is not used as a key though, and treated as personalization + * string for DRBG. + */ + result = DCRYPTO_appkey_derive(appid, input, output); + + DCRYPTO_appkey_finish(&ctx); + return result; +} + +/** + * Generate an origin and user-specific ECDSA key pair from the specified + * key handle. + * + * If pk_x and pk_y are NULL, public key generation will be skipped. + * + * @param state U2F state parameters + * @param kh key handle + * @param kh_version key handle version (0 - legacy, 1 - versioned) + * @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 a valid key pair was created. + */ +static enum ec_error_list u2f_origin_user_key_pair( + const struct u2f_state *state, const union u2f_key_handle_variant *kh, + uint8_t kh_version, p256_int *d, p256_int *pk_x, p256_int *pk_y) +{ + uint32_t dev_salt[P256_NDIGITS]; + uint8_t key_seed[P256_NBYTES]; + + struct drbg_ctx drbg; + size_t key_handle_size = 0; + uint8_t *key_handle = NULL; + + if (kh_version == 0) { + key_handle_size = sizeof(struct u2f_key_handle_v0); + key_handle = (uint8_t *)&kh->v0; + } else if ((kh_version == 1) && (kh->v1.version == kh_version)) { + key_handle_size = U2F_V1_KH_HEADER_SIZE; + key_handle = (uint8_t *)&kh->v1; + } else { + return EC_ERROR_INVAL; + } + + /* TODO(sukhomlinov): implement new FIPS path. */ + if (!app_hw_device_id(U2F_ORIGIN, state->hmac_key, dev_salt)) + return EC_ERROR_UNKNOWN; + + hmac_drbg_init(&drbg, state->drbg_entropy, P256_NBYTES, dev_salt, + P256_NBYTES, NULL, 0); + + hmac_drbg_generate(&drbg, key_seed, sizeof(key_seed), key_handle, + key_handle_size); + + if (!DCRYPTO_p256_key_from_bytes(pk_x, pk_y, d, key_seed)) + return EC_ERROR_TRY_AGAIN; + +#ifdef CR50_DEV_U2F_VERBOSE + ccprintf("user private key %ph\n", HEX_BUF(d, sizeof(*d))); + cflush(); + if (pk_x) + ccprintf("user public x %ph\n", HEX_BUF(pk_x, sizeof(*pk_x))); + if (pk_y) + ccprintf("user public y %ph\n", HEX_BUF(pk_y, sizeof(*pk_y))); + cflush(); +#endif + + return EC_SUCCESS; +} + +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) +{ + uint8_t *kh_hmac, *kh_origin_seed; + int generate_key_pair_rc; + /* Generated public keys associated with key handle. */ + p256_int opk_x, opk_y; + + /* Compute constants for request key handler version. */ + if (kh_version == 0) { + kh_hmac = kh->v0.hmac; + kh_origin_seed = kh->v0.origin_seed; + } else if (kh_version == 1) { + kh_hmac = kh->v1.kh_hmac; + kh_origin_seed = kh->v1.origin_seed; + /** + * This may overwrite input parameters if shared + * request/response buffer is used by caller. + */ + kh->v1.version = kh_version; + } else + return EC_ERROR_INVAL; + + /* Generate key handle candidates and origin-specific key pair. */ + do { + p256_int od; + /* Generate random origin seed for key handle candidate. */ + if (!fips_rand_bytes(kh_origin_seed, U2F_ORIGIN_SEED_SIZE)) + return EC_ERROR_HW_INTERNAL; + + u2f_origin_user_mac(state, user, origin, kh_origin_seed, + kh_version, kh_hmac); + + /** + * Try to generate key pair using key handle. This may fail if + * key handle results in private key which is out of allowed + * range. If this is the case, repeat with another origin seed. + */ + generate_key_pair_rc = u2f_origin_user_key_pair( + state, kh, kh_version, &od, &opk_x, &opk_y); + + p256_clear(&od); + } while (generate_key_pair_rc == EC_ERROR_TRY_AGAIN); + + if (generate_key_pair_rc != EC_SUCCESS) + return generate_key_pair_rc; + + if (kh_version == 1) { + if (!fips_rand_bytes(kh->v1.authorization_salt, + U2F_AUTHORIZATION_SALT_SIZE)) + return EC_ERROR_HW_INTERNAL; + + u2f_authorization_mac(state, kh, kh_version, authTimeSecretHash, + kh->v1.authorization_hmac); + } + + pubKey->pointFormat = U2F_POINT_UNCOMPRESSED; + p256_to_bin(&opk_x, pubKey->x); /* endianness */ + p256_to_bin(&opk_y, pubKey->y); /* endianness */ + + return EC_SUCCESS; +} + +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) +{ + /* Re-created key handle. */ + uint8_t recreated_hmac[SHA256_DIGEST_SIZE]; + const uint8_t *origin_seed, *kh_hmac; + int result = 0; + + /* + * Re-create the key handle and compare against that which + * was provided. This allows us to verify that the key handle + * is owned by this combination of device, current user and origin. + */ + if (kh_version == 0) { + origin_seed = kh->v0.origin_seed; + kh_hmac = kh->v0.hmac; + } else { + origin_seed = kh->v1.origin_seed; + kh_hmac = kh->v1.kh_hmac; + } + /* First, check inner part. */ + u2f_origin_user_mac(state, user, origin, origin_seed, kh_version, + recreated_hmac); + + /** + * DCRYPTO_equals return 1 if success, by subtracting 1 we make it + * zero, and other results - zero or non-zero will be detected. + */ + result |= DCRYPTO_equals(&recreated_hmac, kh_hmac, + sizeof(recreated_hmac)) - DCRYPTO_OK; + + always_memset(recreated_hmac, 0, sizeof(recreated_hmac)); + + if ((kh_version != 0) && (authTimeSecretHash != NULL)) { + u2f_authorization_mac(state, kh, kh_version, authTimeSecretHash, + recreated_hmac); + result |= DCRYPTO_equals(&recreated_hmac, + kh->v1.authorization_hmac, + sizeof(recreated_hmac)) - DCRYPTO_OK; + always_memset(recreated_hmac, 0, sizeof(recreated_hmac)); + } + + return (result == 0) ? EC_SUCCESS : EC_ERROR_ACCESS_DENIED; +} + +static enum ec_error_list +u2f_attest_keyhandle_pubkey(const struct u2f_state *state, + const union u2f_key_handle_variant *key_handle, + uint8_t kh_version, const uint8_t *user, + const uint8_t *origin, + const uint8_t *authTimeSecretHash, + const struct u2f_ec_point *public_key) +{ + struct u2f_ec_point kh_pubkey; + p256_int od, opk_x, opk_y; + enum ec_error_list result; + + /* Check this is a correct key handle for provided user/origin. */ + result = u2f_authorize_keyhandle(state, key_handle, kh_version, user, + origin, authTimeSecretHash); + + if (result != EC_SUCCESS) + return result; + + /* Recreate public key from key handle. */ + result = u2f_origin_user_key_pair(state, key_handle, kh_version, &od, + &opk_x, &opk_y); + if (result != EC_SUCCESS) + return result; + + p256_clear(&od); + /* Reconstruct the public key. */ + p256_to_bin(&opk_x, kh_pubkey.x); + p256_to_bin(&opk_y, kh_pubkey.y); + kh_pubkey.pointFormat = U2F_POINT_UNCOMPRESSED; + +#ifdef CR50_DEV_U2F_VERBOSE + ccprintf("recreated key %ph\n", HEX_BUF(&kh_pubkey, sizeof(kh_pubkey))); + ccprintf("provided key %ph\n", HEX_BUF(public_key, sizeof(kh_pubkey))); +#endif + return (DCRYPTO_equals(&kh_pubkey, public_key, + sizeof(struct u2f_ec_point)) == DCRYPTO_OK) ? + EC_SUCCESS : + EC_ERROR_ACCESS_DENIED; +} + +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) +{ + /* Origin private key. */ + p256_int origin_d; + + /* Hash, and corresponding signature. */ + p256_int h, r, s; + + struct drbg_ctx ctx; + enum ec_error_list result; + + result = u2f_authorize_keyhandle(state, kh, kh_version, user, origin, + authTimeSecretHash); + + if (result != EC_SUCCESS) + return result; + + /* Re-create origin-specific key. */ + result = u2f_origin_user_key_pair(state, kh, kh_version, &origin_d, + NULL, NULL); + if (result != EC_SUCCESS) + return result; + + /* Prepare hash to sign. */ + p256_from_bin(hash, &h); + + /* Now, we processed input parameters, so clean-up output. */ + memset(sig, 0, sizeof(*sig)); + + /* Sign. */ + hmac_drbg_init_rfc6979(&ctx, &origin_d, &h); + result = (dcrypto_p256_ecdsa_sign(&ctx, &origin_d, &h, &r, &s) != 0) ? + EC_SUCCESS : + EC_ERROR_HW_INTERNAL; + + p256_clear(&origin_d); + + p256_to_bin(&r, sig->sig_r); + p256_to_bin(&s, sig->sig_s); + + return result; +} + +/** + * Generate a hardware derived ECDSA key pair for individual attestation. + * + * @param state U2F state parameters + * @param d pointer to ECDSA private key + * @param pk_x pointer to public key point + * @param pk_y pointer to public key point + * + * @return true if a valid key pair was created. + */ +static bool g2f_individual_key_pair(const struct u2f_state *state, p256_int *d, + p256_int *pk_x, p256_int *pk_y) +{ + uint8_t buf[SHA256_DIGEST_SIZE]; + + /* Incorporate HIK & diversification constant. */ + if (!app_hw_device_id(U2F_ATTEST, state->salt, (uint32_t *)buf)) + return false; + + /* Generate unbiased private key (non-FIPS path). */ + while (!DCRYPTO_p256_key_from_bytes(pk_x, pk_y, d, buf)) { + struct sha256_ctx sha; + + SHA256_hw_init(&sha); + SHA256_update(&sha, buf, sizeof(buf)); + memcpy(buf, SHA256_final(&sha), sizeof(buf)); + } + + return true; +} + +#define G2F_CERT_NAME "CrO2" + +size_t g2f_attestation_cert_serial(const struct u2f_state *state, + const uint8_t *serial, uint8_t *buf) +{ + p256_int d, pk_x, pk_y; + + if (g2f_individual_key_pair(state, &d, &pk_x, &pk_y)) + return 0; + + /* Note that max length is not currently respected here. */ + return DCRYPTO_x509_gen_u2f_cert_name(&d, &pk_x, &pk_y, + (p256_int *)serial, G2F_CERT_NAME, + buf, + G2F_ATTESTATION_CERT_MAX_LEN); +} + +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) +{ + struct sha256_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; + + enum ec_error_list result; + + result = u2f_attest_keyhandle_pubkey(state, kh, kh_version, user, + origin, authTimeSecretHash, + public_key); + + if (result != EC_SUCCESS) + return result; + + /* Derive G2F Attestation Key. */ + if (!g2f_individual_key_pair(state, &d, &pk_x, &pk_y)) { +#ifdef CR50_DEV + ccprintf("G2F Attestation key generation failed\n"); +#endif + return EC_ERROR_HW_INTERNAL; + } + + /* Message signature. */ + SHA256_hw_init(&h_ctx); + SHA256_update(&h_ctx, data, data_size); + p256_from_bin(SHA256_final(&h_ctx)->b8, &h); + + /* Now, we processed input parameters, so clean-up output. */ + memset(sig, 0, sizeof(*sig)); + + /* Sign over the response w/ the attestation key. */ + hmac_drbg_init_rfc6979(&dr_ctx, &d, &h); + + result = (dcrypto_p256_ecdsa_sign(&dr_ctx, &d, &h, &r, &s) != 0) ? + EC_SUCCESS : + EC_ERROR_HW_INTERNAL; + p256_clear(&d); + + p256_to_bin(&r, sig->sig_r); + p256_to_bin(&s, sig->sig_s); + + return result; +} + +#ifndef CRYPTO_TEST_CMD_U2F_TEST +#define CRYPTO_TEST_CMD_U2F_TEST 0 +#endif + +#if defined(CRYPTO_TEST_SETUP) && CRYPTO_TEST_CMD_U2F_TEST + +static const char *expect_bool(enum ec_error_list value, + enum ec_error_list expect) +{ + if (value == expect) + return "PASSED"; + return "NOT PASSED"; +} + +static int cmd_u2f_test(int argc, char **argv) +{ + static struct u2f_state state; + static union u2f_key_handle_variant kh; + static const uint8_t origin[32] = { 0xff, 0xfe, 0xfd, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8 }; + static const uint8_t user[32] = { 0x88, 0x8e, 0x8d, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 }; + static const uint8_t authTime[32] = { 0x99, 0x91, 2, 3, 4, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5 }; + static struct u2f_ec_point pubKey; + static struct u2f_signature sig; + + ccprintf("u2f_generate_hmac_key - %s\n", + expect_bool(u2f_generate_hmac_key(&state), EC_SUCCESS)); + + ccprintf("u2f_generate_g2f_secret - %s\n", + expect_bool(u2f_generate_g2f_secret(&state), EC_SUCCESS)); + + ccprintf("u2f_generate_drbg_entropy - %s\n", + expect_bool(u2f_generate_drbg_entropy(&state), EC_SUCCESS)); + + /* Version 0 key handle. */ + ccprintf("u2f_generate - %s\n", + expect_bool(u2f_generate(&state, user, origin, authTime, &kh, + 0, &pubKey), + EC_SUCCESS)); + ccprintf("kh: %ph\n", HEX_BUF(&kh, sizeof(kh.v0))); + ccprintf("pubKey: %ph\n", HEX_BUF(&pubKey, sizeof(pubKey))); + + ccprintf("u2f_authorize_keyhandle - %s\n", + expect_bool(u2f_authorize_keyhandle(&state, &kh, 0, user, + origin, authTime), + EC_SUCCESS)); + + kh.v0.origin_seed[0] ^= 0x10; + ccprintf("u2f_authorize_keyhandle - %s\n", + expect_bool(u2f_authorize_keyhandle(&state, &kh, 0, user, + origin, authTime), + EC_ERROR_ACCESS_DENIED)); + + kh.v0.origin_seed[0] ^= 0x10; + ccprintf("u2f_sign - %s\n", + expect_bool(u2f_sign(&state, &kh, 0, user, origin, authTime, + authTime, &sig), + EC_SUCCESS)); + ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig))); + + ccprintf("u2f_attest - %s\n", + expect_bool(u2f_attest(&state, &kh, 0, user, origin, authTime, + &pubKey, authTime, sizeof(authTime), + &sig), + EC_SUCCESS)); + ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig))); + + /* Should fail with incorrect key handle. */ + kh.v0.origin_seed[0] ^= 0x10; + ccprintf("u2f_sign - %s\n", + expect_bool(u2f_sign(&state, &kh, 0, user, origin, authTime, + authTime, &sig), + EC_ERROR_ACCESS_DENIED)); + ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig))); + + /* Version 1 key handle. */ + ccprintf("\nVersion 1 tests\n"); + ccprintf("u2f_generate - %s\n", + expect_bool(u2f_generate(&state, user, origin, authTime, &kh, + 1, &pubKey), + EC_SUCCESS)); + ccprintf("kh: %ph\n", HEX_BUF(&kh, sizeof(kh.v1))); + ccprintf("pubKey: %ph\n", HEX_BUF(&pubKey, sizeof(pubKey))); + + ccprintf("u2f_authorize_keyhandle - %s\n", + expect_bool(u2f_authorize_keyhandle(&state, &kh, 1, user, + origin, authTime), + EC_SUCCESS)); + + kh.v1.authorization_salt[0] ^= 0x10; + ccprintf("u2f_authorize_keyhandle - %s\n", + expect_bool(u2f_authorize_keyhandle(&state, &kh, 1, user, + origin, authTime), + EC_ERROR_ACCESS_DENIED)); + + kh.v1.authorization_salt[0] ^= 0x10; + ccprintf("u2f_sign - %s\n", + expect_bool(u2f_sign(&state, &kh, 1, user, origin, authTime, + authTime, &sig), + EC_SUCCESS)); + ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig))); + + ccprintf("u2f_attest - %s\n", + expect_bool(u2f_attest(&state, &kh, 1, user, origin, authTime, + &pubKey, authTime, sizeof(authTime), + &sig), + EC_SUCCESS)); + ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig))); + + /* Should fail with incorrect key handle. */ + kh.v1.origin_seed[0] ^= 0x10; + ccprintf("u2f_sign - %s\n", + expect_bool(u2f_sign(&state, &kh, 1, user, origin, authTime, + authTime, &sig), + EC_ERROR_ACCESS_DENIED)); + ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig))); + + cflush(); + + return 0; +} + +DECLARE_SAFE_CONSOLE_COMMAND(u2f_test, cmd_u2f_test, NULL, + "Test U2F functionality"); + +#endif diff --git a/board/cr50/dcrypto/u2f_impl.h b/board/cr50/dcrypto/u2f_impl.h new file mode 100644 index 0000000000..7f0f10ef36 --- /dev/null +++ b/board/cr50/dcrypto/u2f_impl.h @@ -0,0 +1,229 @@ +/* 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. + */ + +/* U2F implementation-specific callbacks and parameters. */ + +#ifndef __CROS_EC_U2F_IMPL_H +#define __CROS_EC_U2F_IMPL_H + +#include "common.h" +#include "dcrypto.h" +#include "u2f.h" + +/* ---- platform cryptography hooks ---- */ + +/* ---- 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; +}; + +/* 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); + +BUILD_ASSERT(sizeof(struct u2f_key_handle_v0) <= U2F_MAX_KH_SIZE); +BUILD_ASSERT(sizeof(struct u2f_key_handle_v0) == U2F_V0_KH_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); + + +BUILD_ASSERT(sizeof(union u2f_key_handle_variant) <= U2F_MAX_KH_SIZE); + +/** + * 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 + */ +enum ec_error_list u2f_generate_drbg_entropy(struct u2f_state *state); + +/** + * 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); + +/** + * 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 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 successful + */ +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); + +/** + * 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 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 key pair was created + * EC_ACCESS_DENIED if key handle can't authenticated + */ +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); + +/** + * 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). + * + * @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. + * + * @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 + * + * G2F_ATTESTATION_CERT_MAX_LEN bytes. + * @return size of certificate written to buf, 0 on error. + */ +size_t g2f_attestation_cert_serial(const struct u2f_state *state, + const uint8_t *serial, uint8_t *buf); + +/** + * 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 + */ +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); + + +/** + * + * Board U2F key management part implemented. + * + */ + +/** + * Get the current u2f state from the board. + * + * @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. + * + * @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. + */ +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 + * derive kek in future calls to u2f_gen_kek(). + * + * @param commit whether to commit nvram changes before returning. + * @return EC_SUCCESS if seed was successfully created + * (and persisted if requested). + */ +enum ec_error_list u2f_gen_kek_seed(int commit); + +/** + * Zeroize U2F keys. Can be used to switch to FIPS-compliant path by + * destroying old keys. + * + * @return true if state is properly initialized and will persist in flash. + */ +enum ec_error_list u2f_zeroize_keys(void); + +/** + * Update keys to a newer (FIPS-compliant) version if needed. Do nothing if + * keys are already updated. + * + * @return EC_SUCCESS or error code. + */ +enum ec_error_list u2f_update_keys(void); + +#endif /* __CROS_EC_U2F_IMPL_H */ diff --git a/board/cr50/fips.c b/board/cr50/fips.c deleted file mode 100644 index 349117d49b..0000000000 --- a/board/cr50/fips.c +++ /dev/null @@ -1,761 +0,0 @@ -/* Copyright 2020 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. - */ - -#include "builtin/endian.h" -#include "console.h" -#include "dcrypto.h" -#include "ec_commands.h" -#include "extension.h" -#include "fips.h" -#include "fips_rand.h" -#include "flash.h" -#include "flash_info.h" -#include "flash_log.h" -#include "hooks.h" -#include "new_nvmem.h" -#include "nvmem.h" -#include "nvmem_vars.h" -#include "registers.h" -#include "scratch_reg1.h" -#include "shared_mem.h" -#include "system.h" -#include "tpm_nvmem_ops.h" -#include "u2f_impl.h" - -#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args) - -/** - * Combined FIPS status & global FIPS error. - * default value is = FIPS_UNINITIALIZED - */ -static enum fips_status _fips_status; - -/* Return current FIPS status, but prevent direct modification of state. */ -enum fips_status fips_status(void) -{ - return _fips_status; -} - -#ifdef CRYPTO_TEST_SETUP -/* Flag to simulate specific error condition in power-up tests. */ -uint8_t fips_break_cmd; -#else -/* For production set it to zero, so check is eliminated. */ -#define fips_break_cmd 0 -#endif - -/** - * Return true if no blocking crypto errors detected. - * Until self-integrity works properly (b/138578318), ignore it. - * TODO(b/138578318): remove ignoring of FIPS_FATAL_SELF_INTEGRITY. - */ -static inline bool fips_is_no_crypto_error(void) -{ - return (_fips_status & - (FIPS_ERROR_MASK & (~FIPS_FATAL_SELF_INTEGRITY))) == 0; -} - -/* Return true if crypto can be used (no failures detected). */ -bool fips_crypto_allowed(void) -{ - /** - * We never allow crypto if there were errors, no matter - * if we are in FIPS approved or not-approved mode. - */ - return ((_fips_status & FIPS_POWER_UP_TEST_DONE) && - fips_is_no_crypto_error() && DCRYPTO_ladder_is_enabled()); -} - -int crypto_enabled(void) -{ - return fips_crypto_allowed(); -} - -void fips_throw_err(enum fips_status err) -{ - /* if not a new error, don't write it in the flash log */ - if ((_fips_status & err) == err) - return; - fips_set_status(err); - if (!fips_is_no_crypto_error()) { -#ifdef CONFIG_FLASH_LOG - fips_vtable->flash_log_add_event(FE_LOG_FIPS_FAILURE, - sizeof(_fips_status), - &_fips_status); -#endif - /* Revoke access to secrets in HW Key ladder. */ - DCRYPTO_ladder_revoke(); - } -} - -/** - * Set status of FIPS power-up tests on wake from sleep. We don't want to - * run lengthy KAT & power-up tests on every wake-up, so need to 'cache' - * result in long life register which content persists during sleep mode. - * - * @param asserted: false power-up tests should run on resume, otherwise - * can be skipped. - */ -static void fips_set_power_up(bool asserted) -{ - /* Enable writing to the long life register */ - if (asserted) - GREG32(PMU, PWRDN_SCRATCH22) = BOARD_FIPS_POWERUP_DONE; - else - GREG32(PMU, PWRDN_SCRATCH22) = 0; -} - -/** - * Return true if FIPS KAT tests completed successfully after waking up - * from sleep mode which clears RAM. - */ -static bool fips_is_power_up_done(void) -{ - return !!(GREG32(PMU, PWRDN_SCRATCH22) == BOARD_FIPS_POWERUP_DONE); -} - -void fips_set_status(enum fips_status status) -{ - /** - * if FIPS error took place, drop indication of FIPS approved mode. - * Next cycle of sleep will power-cycle HW crypto components, so any - * soft-errors will be recovered. In case of hard errors it - * will be detected again. - */ - /* Accumulate status (errors). */ - _fips_status |= status; - - status = _fips_status; - /* if we have error, require power up tests on resume. */ - if (status & FIPS_ERROR_MASK) - fips_set_power_up(false); -} - -/** - * Test vectors for Known-Answer Tests (KATs) and driving functions. - */ - -/* KAT for SHA256, test values from OpenSSL. */ -static bool fips_sha256_kat(void) -{ - struct sha256_ctx ctx; - - static const uint8_t in[] = /* "etaonrishd" */ { 0x65, 0x74, 0x61, 0x6f, - 0x6e, 0x72, 0x69, 0x73, - 0x68, 0x64 }; - static const uint8_t ans[] = { 0xf5, 0x53, 0xcd, 0xb8, 0xcf, 0x1, 0xee, - 0x17, 0x9b, 0x93, 0xc9, 0x68, 0xc0, 0xea, - 0x40, 0x91, 0x6, 0xec, 0x8e, 0x11, 0x96, - 0xc8, 0x5d, 0x1c, 0xaf, 0x64, 0x22, 0xe6, - 0x50, 0x4f, 0x47, 0x57 }; - - SHA256_hw_init(&ctx); - SHA256_update(&ctx, in, sizeof(in)); - return !(fips_break_cmd == FIPS_BREAK_SHA256) && - (DCRYPTO_equals(SHA256_final(&ctx), ans, SHA256_DIGEST_SIZE) == - DCRYPTO_OK); -} - -/* KAT for HMAC-SHA256, test values from OpenSSL. */ -static bool fips_hmac_sha256_kat(void) -{ - struct hmac_sha256_ctx ctx; - - static const uint8_t k[SHA256_DIGEST_SIZE] = - /* "etaonrishd" */ { 0x65, 0x74, 0x61, 0x6f, 0x6e, 0x72, 0x69, - 0x73, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 }; - static const uint8_t in[] = - /* "Sample text" */ { 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, - 0x20, 0x74, 0x65, 0x78, 0x74 }; - static const uint8_t ans[] = { 0xe9, 0x17, 0xc1, 0x7b, 0x4c, 0x6b, 0x77, - 0xda, 0xd2, 0x30, 0x36, 0x02, 0xf5, 0x72, - 0x33, 0x87, 0x9f, 0xc6, 0x6e, 0x7b, 0x7e, - 0xa8, 0xea, 0xaa, 0x9f, 0xba, 0xee, 0x51, - 0xff, 0xda, 0x24, 0xf4 }; - - HMAC_SHA256_hw_init(&ctx, k, sizeof(k)); - HMAC_SHA256_update(&ctx, in, sizeof(in)); - return !(fips_break_cmd == FIPS_BREAK_HMAC_SHA256) && - (DCRYPTO_equals(HMAC_SHA256_hw_final(&ctx), ans, - SHA256_DIGEST_SIZE) == DCRYPTO_OK); -} - -/** - * DRBG test vector source recorded 6/1/17 from - * http://shortn/_eNfI4wD6j8 -> https://csrc.nist.gov/projects/ - * cryptographic-algorithm-validation-program/random-number-generators - * http://shortn/_9hsazxHKn7 -> https://csrc.nist.gov/CSRC/media/Projects/ - * Cryptographic-Algorithm-Validation-Program/documents/drbg/drbgtestvectors.zip - * Input values: - * [SHA-256] - * [PredictionResistance = True] - * [EntropyInputLen = 256] - * [NonceLen = 128] - * [PersonalizationStringLen = 256] - * [AdditionalInputLen = 256] - * [ReturnedBitsLen = 1024] - * COUNT = 0 - * EntropyInput = - * 4294671d493dc085b5184607d7de2ff2b6aceb734a1b026f6cfee7c5a90f03da - * Nonce = d071544e599235d5eb38b64b551d2a6e - * PersonalizationString = - * 63bc769ae1d95a98bde870e4db7776297041d37c8a5c688d4e024b78d83f4d78 - * AdditionalInput = - * 28848becd3f47696f124f4b14853a456156f69be583a7d4682cff8d44b39e1d3 - * EntropyInputPR = - * db9b4790b62336fbb9a684b82947065393eeef8f57bd2477141ad17e776dac34 - * AdditionalInput = - * 8bfce0b7132661c3cd78175d83926f643e36f7608eec2c5dac3ddcbacc8c2182 - * EntropyInputPR = - * 4a9abe80f6f522f29878bedf8245b27940a76471006fb4a4110beb4decb6c341 - * ReturnedBits = - * e580dc969194b2b18a97478aef9d1a72390aff14562747bf080d741527a6655 - * ce7fc135325b457483a9f9c70f91165a811cf4524b50d51199a0df3bd60d12abac27d0bf6618 - * e6b114e05420352e23f3603dfe8a225dc19b3d1fff1dc245dc6b1df24c741744bec3f9437dbb - * f222df84881a457a589e7815ef132f686b760f012 - * DRBG KAT generation sequence: - * hmac_drbg_init(entropy0, nonce0, perso0) - * hmac_drbg_reseed(entropy1, addtl_input1) - * hmac_drbg_generate() - * hmac_drbg_reseed(entropy2, addtl_input2) - * hmac_drbg_generate() - */ -static const uint8_t drbg_entropy0[] = { - 0x42, 0x94, 0x67, 0x1d, 0x49, 0x3d, 0xc0, 0x85, 0xb5, 0x18, 0x46, - 0x07, 0xd7, 0xde, 0x2f, 0xf2, 0xb6, 0xac, 0xeb, 0x73, 0x4a, 0x1b, - 0x02, 0x6f, 0x6c, 0xfe, 0xe7, 0xc5, 0xa9, 0x0f, 0x03, 0xda -}; -static const uint8_t drbg_nonce0[] = { 0xd0, 0x71, 0x54, 0x4e, 0x59, 0x92, - 0x35, 0xd5, 0xeb, 0x38, 0xb6, 0x4b, - 0x55, 0x1d, 0x2a, 0x6e }; -static const uint8_t drbg_perso0[] = { 0x63, 0xbc, 0x76, 0x9a, 0xe1, 0xd9, 0x5a, - 0x98, 0xbd, 0xe8, 0x70, 0xe4, 0xdb, 0x77, - 0x76, 0x29, 0x70, 0x41, 0xd3, 0x7c, 0x8a, - 0x5c, 0x68, 0x8d, 0x4e, 0x02, 0x4b, 0x78, - 0xd8, 0x3f, 0x4d, 0x78 }; - -static const uint8_t drbg_entropy1[] = { - 0xdb, 0x9b, 0x47, 0x90, 0xb6, 0x23, 0x36, 0xfb, 0xb9, 0xa6, 0x84, - 0xb8, 0x29, 0x47, 0x06, 0x53, 0x93, 0xee, 0xef, 0x8f, 0x57, 0xbd, - 0x24, 0x77, 0x14, 0x1a, 0xd1, 0x7e, 0x77, 0x6d, 0xac, 0x34 -}; -static const uint8_t drbg_addtl_input1[] = { - 0x28, 0x84, 0x8b, 0xec, 0xd3, 0xf4, 0x76, 0x96, 0xf1, 0x24, 0xf4, - 0xb1, 0x48, 0x53, 0xa4, 0x56, 0x15, 0x6f, 0x69, 0xbe, 0x58, 0x3a, - 0x7d, 0x46, 0x82, 0xcf, 0xf8, 0xd4, 0x4b, 0x39, 0xe1, 0xd3 -}; - -static const uint8_t drbg_entropy2[] = { - 0x4a, 0x9a, 0xbe, 0x80, 0xf6, 0xf5, 0x22, 0xf2, 0x98, 0x78, 0xbe, - 0xdf, 0x82, 0x45, 0xb2, 0x79, 0x40, 0xa7, 0x64, 0x71, 0x00, 0x6f, - 0xb4, 0xa4, 0x11, 0x0b, 0xeb, 0x4d, 0xec, 0xb6, 0xc3, 0x41 -}; -static const uint8_t drbg_addtl_input2[] = { - 0x8b, 0xfc, 0xe0, 0xb7, 0x13, 0x26, 0x61, 0xc3, 0xcd, 0x78, 0x17, - 0x5d, 0x83, 0x92, 0x6f, 0x64, 0x3e, 0x36, 0xf7, 0x60, 0x8e, 0xec, - 0x2c, 0x5d, 0xac, 0x3d, 0xdc, 0xba, 0xcc, 0x8c, 0x21, 0x82 -}; - -/* Known-answer test for HMAC_DRBG SHA256 instantiate. */ -static bool fips_hmac_drbg_instantiate_kat(struct drbg_ctx *ctx) -{ - /* Expected internal drbg state */ - static const uint32_t K0[] = { 0x7fe2b43a, 0x94f11b33, 0x2b76c5ce, - 0xfbb784af, 0x81cfe716, 0xc43596d6, - 0xbdfe968b, 0x189c80fb }; - static const uint32_t V0[] = { 0xc42b237a, 0x929cdd0b, 0xe7fbafdd, - 0xba22a36a, 0x4d23471a, 0xd8607022, - 0x687e18ac, 0x2ac08007 }; - - hmac_drbg_init(ctx, drbg_entropy0, sizeof(drbg_entropy0), - drbg_nonce0, sizeof(drbg_nonce0), drbg_perso0, - sizeof(drbg_perso0)); - - return (DCRYPTO_equals(ctx->v, V0, sizeof(V0)) == DCRYPTO_OK) && - (DCRYPTO_equals(ctx->k, K0, sizeof(K0)) == DCRYPTO_OK); -} - -/* Known-answer test for HMAC_DRBG SHA256 reseed. */ -static bool fips_hmac_drbg_reseed_kat(struct drbg_ctx *ctx) -{ - /* Expected internal drbg state */ - static const uint32_t K1[] = { 0x3118D36E, 0x05DEEC48, 0x7EFB6363, - 0x3D575CDE, 0xCFCD14C1, 0x8D4F937D, - 0x896B811E, 0x0EF038EB }; - static const uint32_t V1[] = { 0xC8ED8EEC, 0x24DD7B66, 0x09C635CD, - 0x6AC74196, 0xC70067D7, 0xC2E71FEF, - 0x918D9EB7, 0xAE0CD544 }; - - hmac_drbg_reseed(ctx, drbg_entropy1, sizeof(drbg_entropy1), - drbg_addtl_input1, sizeof(drbg_addtl_input1), NULL, 0); - - return (DCRYPTO_equals(ctx->v, V1, sizeof(V1)) == DCRYPTO_OK) && - (DCRYPTO_equals(ctx->k, K1, sizeof(K1)) == DCRYPTO_OK); -} - -/* Known-answer test for HMAC_DRBG SHA256 generate. */ -static bool fips_hmac_drbg_generate_kat(struct drbg_ctx *ctx) -{ - /* Expected internal drbg state */ - static const uint32_t K2[] = { 0x980ccd6a, 0x0b34f7e1, 0x594aabd7, - 0x33b66049, 0xb919bd57, 0x8ecc7194, - 0xaf1748a3, 0x80982577 }; - static const uint32_t V2[] = { 0xe4927cdb, 0xb3435cc5, 0x601ab870, - 0x46e1f024, 0x966ca875, 0x102b4167, - 0xa71e5dce, 0xe4c15962 }; - /* Expected output */ - static const uint8_t KA[] = { - 0xe5, 0x80, 0xdc, 0x96, 0x91, 0x94, 0xb2, 0xb1, 0x8a, 0x97, - 0x47, 0x8a, 0xef, 0x9d, 0x1a, 0x72, 0x39, 0x0a, 0xff, 0x14, - 0x56, 0x27, 0x47, 0xbf, 0x08, 0x0d, 0x74, 0x15, 0x27, 0xa6, - 0x65, 0x5c, 0xe7, 0xfc, 0x13, 0x53, 0x25, 0xb4, 0x57, 0x48, - 0x3a, 0x9f, 0x9c, 0x70, 0xf9, 0x11, 0x65, 0xa8, 0x11, 0xcf, - 0x45, 0x24, 0xb5, 0x0d, 0x51, 0x19, 0x9a, 0x0d, 0xf3, 0xbd, - 0x60, 0xd1, 0x2a, 0xba, 0xc2, 0x7d, 0x0b, 0xf6, 0x61, 0x8e, - 0x6b, 0x11, 0x4e, 0x05, 0x42, 0x03, 0x52, 0xe2, 0x3f, 0x36, - 0x03, 0xdf, 0xe8, 0xa2, 0x25, 0xdc, 0x19, 0xb3, 0xd1, 0xff, - 0xf1, 0xdc, 0x24, 0x5d, 0xc6, 0xb1, 0xdf, 0x24, 0xc7, 0x41, - 0x74, 0x4b, 0xec, 0x3f, 0x94, 0x37, 0xdb, 0xbf, 0x22, 0x2d, - 0xf8, 0x48, 0x81, 0xa4, 0x57, 0xa5, 0x89, 0xe7, 0x81, 0x5e, - 0xf1, 0x32, 0xf6, 0x86, 0xb7, 0x60, 0xf0, 0x12 - }; - uint8_t buf[128]; - - hmac_drbg_generate(ctx, buf, sizeof(buf), NULL, 0); - /* Verify internal drbg state */ - if (DCRYPTO_equals(ctx->v, V2, sizeof(V2)) != DCRYPTO_OK || - DCRYPTO_equals(ctx->k, K2, sizeof(K2)) != DCRYPTO_OK) { - return false; - } - - hmac_drbg_reseed(ctx, drbg_entropy2, sizeof(drbg_entropy2), - drbg_addtl_input2, sizeof(drbg_addtl_input2), NULL, 0); - /** - * reuse entropy buffer to avoid allocating too much stack and memory - * it will be cleaned up in TRNG health test - */ - hmac_drbg_generate(ctx, buf, sizeof(buf), NULL, 0); - return !(fips_break_cmd == FIPS_BREAK_HMAC_DRBG) && - DCRYPTO_equals(buf, KA, sizeof(KA) == DCRYPTO_OK); -} - -/* Known-answer test for HMAC_DRBG SHA256. */ -static bool fips_hmac_drbg_kat(void) -{ - struct drbg_ctx ctx; - - return fips_hmac_drbg_instantiate_kat(&ctx) && - fips_hmac_drbg_reseed_kat(&ctx) && - fips_hmac_drbg_generate_kat(&ctx); -} - -/* Known-answer test for ECDSA NIST P-256 verify. */ -static bool fips_ecdsa_verify_kat(void) -{ - static const p256_int qx = { .a = { 0xf49abf3c, 0xf82e6e12, 0x7a67c074, - 0x5134e16f, 0xf8957a0c, 0xef4344a7, - 0xd4bb3cb7, 0xe424dc61 } }; - static const p256_int qy = { .a = { 0xdfaee927, 0x3d6f60e7, 0xac85d124, - 0x127e5965, 0xe1dddaf0, 0x1545949d, - 0xa2bc4865, 0x970eed7a } }; - static const p256_int r = { .a = { 0xd9347f4f, 0xb72f981f, 0x6349b9da, - 0x2ff540c7, 0x42017c64, 0x910be331, - 0xa49c705c, 0xbf96b99a } }; - static const p256_int s = { .a = { 0x57ec871c, 0x920b9e0f, 0x75d98f31, - 0x444e3230, 0x15abdf12, 0xe03b9cd4, - 0x819089c2, 0x17c55095 } }; - static const uint8_t msg[128] = { - 0xe1, 0x13, 0x0a, 0xf6, 0xa3, 0x8c, 0xcb, 0x41, 0x2a, 0x9c, - 0x8d, 0x13, 0xe1, 0x5d, 0xbf, 0xc9, 0xe6, 0x9a, 0x16, 0x38, - 0x5a, 0xf3, 0xc3, 0xf1, 0xe5, 0xda, 0x95, 0x4f, 0xd5, 0xe7, - 0xc4, 0x5f, 0xd7, 0x5e, 0x2b, 0x8c, 0x36, 0x69, 0x92, 0x28, - 0xe9, 0x28, 0x40, 0xc0, 0x56, 0x2f, 0xbf, 0x37, 0x72, 0xf0, - 0x7e, 0x17, 0xf1, 0xad, 0xd5, 0x65, 0x88, 0xdd, 0x45, 0xf7, - 0x45, 0x0e, 0x12, 0x17, 0xad, 0x23, 0x99, 0x22, 0xdd, 0x9c, - 0x32, 0x69, 0x5d, 0xc7, 0x1f, 0xf2, 0x42, 0x4c, 0xa0, 0xde, - 0xc1, 0x32, 0x1a, 0xa4, 0x70, 0x64, 0xa0, 0x44, 0xb7, 0xfe, - 0x3c, 0x2b, 0x97, 0xd0, 0x3c, 0xe4, 0x70, 0xa5, 0x92, 0x30, - 0x4c, 0x5e, 0xf2, 0x1e, 0xed, 0x9f, 0x93, 0xda, 0x56, 0xbb, - 0x23, 0x2d, 0x1e, 0xeb, 0x00, 0x35, 0xf9, 0xbf, 0x0d, 0xfa, - 0xfd, 0xcc, 0x46, 0x06, 0x27, 0x2b, 0x20, 0xa3 - }; - - p256_int p256_digest; - struct sha256_digest digest; - uint8_t bad_msg[128]; - int passed; - - SHA256_hw_hash(msg, sizeof(msg), &digest); - p256_from_bin(digest.b8, &p256_digest); - passed = dcrypto_p256_ecdsa_verify(&qx, &qy, &p256_digest, &r, &s); - if (!passed) - return false; - /** - * create bad_msg same as msg but has one bit flipped in byte 92 (0x0a - * vs 0x1a) this is to save space in flash vs. having bad message as - * constant - */ - memcpy(bad_msg, msg, sizeof(msg)); - bad_msg[92] ^= 0x10; - SHA256_hw_hash(bad_msg, sizeof(bad_msg), &digest); - p256_from_bin(digest.b8, &p256_digest); - passed = dcrypto_p256_ecdsa_verify(&qx, &qy, &p256_digest, &r, &s); - return !(fips_break_cmd == FIPS_BREAK_ECDSA) && (passed == 0); -} - -#ifdef CONFIG_FIPS_AES_CBC_256 -#define AES_BLOCK_LEN 16 - -/* Known-answer test for AES-256 encrypt/decrypt. */ -static bool fips_aes256_kat(void) -{ - uint8_t enc[AES_BLOCK_LEN]; - uint8_t dec[AES_BLOCK_LEN]; - uint8_t iv[AES_BLOCK_LEN]; - - static const uint8_t kat_aes128_k[AES256_BLOCK_CIPHER_KEY_SIZE] = { - 0x65, 0x74, 0x61, 0x6f, 0x6e, 0x72, 0x69, 0x73, - 0x68, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - static const uint8_t kat_aes128_msg[AES_BLOCK_LEN] = { - 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, - 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA - }; - - static const uint8_t ans_aes128[AES_BLOCK_LEN] = { - 0x64, 0x62, 0x89, 0x41, 0x73, 0x63, 0x70, 0xe9, - 0x12, 0x7e, 0xa7, 0x1b, 0x1b, 0xc3, 0x57, 0x8d - }; - - memset(iv, 0, sizeof(iv)); - DCRYPTO_aes_init(kat_aes128_k, 256, iv, CIPHER_MODE_CBC, ENCRYPT_MODE); - DCRYPTO_aes_block(kat_aes128_msg, enc); - if (memcmp(enc, ans_aes128, AES_BLOCK_LEN)) - return false; - - DCRYPTO_aes_init(kat_aes128_k, 256, iv, CIPHER_MODE_CBC, DECRYPT_MODE); - DCRYPTO_aes_block(enc, dec); - - return !(fips_break_cmd == FIPS_BREAK_AES256) && - (memcmp(kat_aes128_msg, dec, AES_BLOCK_LEN) == 0); -} -#endif - -#ifdef CONFIG_FIPS_RSA2048 -/* Known-answer test for RSA 2048. */ -static bool fips_rsa2048_verify_kat(void) -{ - struct sha256_digest digest; - static const uint32_t pub[64] = { - 0xf8729219, 0x2b42fc45, 0xfe6f4397, 0xa6ba59df, 0x4ce45ab8, - 0x4be044ea, 0xdade58ec, 0xf871ada6, 0x3a6355a1, 0x43739940, - 0x2fbdff33, 0x3e6f8953, 0xd2f99a29, 0xb0835670, 0x4d9144e1, - 0x3518387f, 0x808bef09, 0x1f612714, 0xa109e770, 0xcf0f4123, - 0x1d74505e, 0xa9b7c557, 0x176fcc28, 0xe0e86a16, 0x699b54eb, - 0x2c3514b8, 0xf236634f, 0xf4f5b4ae, 0x12d180a4, 0x5e587a1a, - 0xd7b9bd27, 0x649965dc, 0x5097e8aa, 0xa42c8ae7, 0x1e252547, - 0x11ed1901, 0x898ed7c4, 0x05705388, 0x866ac091, 0x5769c900, - 0x05108735, 0xca60769e, 0x7ab9ae85, 0xce7440eb, 0xe60eb7c8, - 0xd8d80ee8, 0xa151febc, 0x93d49bbc, 0xc0a79b3f, 0x48dbad30, - 0x9ff65c53, 0x2db20805, 0x175d83de, 0xfffceebd, 0x203e209e, - 0xafee1f86, 0x39b46031, 0x36b0c302, 0x85222b79, 0x891b7941, - 0x69d37fab, 0xec6cca57, 0xc81e692b, 0xd5e1b4e8 - }; - static const uint8_t sig[256] = { - 0x02, 0xa7, 0x8c, 0x15, 0x44, 0x00, 0x44, 0x2f, 0x2e, 0x45, - 0xb2, 0xf6, 0x11, 0x01, 0xdf, 0xcf, 0x28, 0xfd, 0x50, 0xf2, - 0x89, 0x59, 0x7c, 0x93, 0x1f, 0xec, 0x7d, 0xf9, 0xf7, 0x66, - 0xf1, 0xf5, 0x9d, 0x81, 0xad, 0x7a, 0x05, 0xcd, 0x93, 0xea, - 0x93, 0x0a, 0x41, 0x60, 0x34, 0x3d, 0xeb, 0x2f, 0x87, 0x8f, - 0x25, 0x13, 0x07, 0x61, 0xd8, 0x86, 0x64, 0xca, 0x74, 0xd7, - 0xff, 0xbf, 0xc3, 0xdc, 0xef, 0x5a, 0xcf, 0xa0, 0xff, 0x3a, - 0xe5, 0x91, 0x4b, 0xd1, 0xa6, 0x01, 0xe5, 0xb0, 0x98, 0xf5, - 0x01, 0x65, 0xe6, 0x62, 0xf4, 0x51, 0x15, 0xc0, 0xba, 0xe6, - 0xee, 0x0a, 0xa5, 0x83, 0xfb, 0x25, 0x1d, 0x09, 0x95, 0x49, - 0xc0, 0xf7, 0x32, 0x2d, 0x44, 0x49, 0xa4, 0x51, 0xa7, 0x2c, - 0xa5, 0x79, 0xc9, 0x80, 0x90, 0xd8, 0x3c, 0xd5, 0x25, 0x37, - 0x31, 0x04, 0xb1, 0x9b, 0x3e, 0xed, 0x3e, 0x49, 0x2c, 0xc2, - 0x11, 0xf2, 0x58, 0x36, 0x6c, 0x63, 0x15, 0xef, 0x34, 0x81, - 0xb2, 0xb8, 0xa3, 0x6b, 0x4a, 0x87, 0x0f, 0xd8, 0x87, 0x27, - 0x76, 0x2c, 0x51, 0x7d, 0xa3, 0x8e, 0xc7, 0xa1, 0x08, 0x47, - 0x35, 0xa4, 0x63, 0xd2, 0xe6, 0x05, 0x70, 0x15, 0x12, 0xbe, - 0x38, 0x95, 0x15, 0x3c, 0xf7, 0xed, 0xb0, 0x1a, 0xba, 0x81, - 0x93, 0x08, 0xe6, 0xec, 0x08, 0xe9, 0x5f, 0x35, 0x9d, 0x12, - 0xc2, 0xf7, 0x0f, 0xfc, 0x67, 0x40, 0x69, 0x90, 0x6e, 0x0a, - 0x3d, 0x3b, 0x83, 0x66, 0x2e, 0xee, 0x3d, 0xad, 0xad, 0xdd, - 0x46, 0xfd, 0x3d, 0x9b, 0x00, 0xd8, 0x45, 0xa6, 0xb5, 0x20, - 0x29, 0x88, 0x5f, 0x92, 0xa0, 0x63, 0x5f, 0x51, 0x17, 0xfb, - 0xde, 0xb2, 0x05, 0xb6, 0xc8, 0x4e, 0x58, 0x2b, 0xfc, 0xc5, - 0x04, 0x7d, 0x17, 0x4c, 0xd6, 0x7c, 0x05, 0xed, 0x10, 0xf8, - 0x98, 0x1e, 0xb2, 0x3a, 0x6c, 0x6d - }; - static const uint8_t msg[128] = { - 0x2d, 0xfc, 0x5d, 0xbd, 0x44, 0x2a, 0xb6, 0x48, 0x1d, 0x6c, - 0xc7, 0xce, 0xa4, 0xcd, 0x01, 0x47, 0xff, 0xae, 0xd2, 0xbe, - 0x1d, 0x0a, 0xd5, 0xb2, 0x92, 0xfe, 0x46, 0xbb, 0xa2, 0x88, - 0xb8, 0x71, 0x9b, 0x8f, 0x0a, 0x89, 0x69, 0x23, 0x97, 0x41, - 0x64, 0x07, 0xad, 0xff, 0x6c, 0x6c, 0x41, 0x34, 0x38, 0x00, - 0xe0, 0x87, 0xeb, 0x27, 0xe9, 0x30, 0xe8, 0x88, 0xfa, 0xa1, - 0xe8, 0xcc, 0xa8, 0x6c, 0x4a, 0xa2, 0x73, 0x61, 0xaa, 0x07, - 0xf8, 0xf6, 0xb4, 0xc4, 0x69, 0xed, 0x3a, 0x38, 0x3b, 0x30, - 0x85, 0x57, 0x1e, 0x00, 0xe9, 0xf3, 0x32, 0x4e, 0x9c, 0x3b, - 0x78, 0x69, 0xc9, 0x81, 0x87, 0xda, 0xdf, 0x40, 0x80, 0x8c, - 0x2f, 0x5d, 0x43, 0x31, 0xb6, 0xad, 0xe3, 0xe0, 0x37, 0xb8, - 0x58, 0x03, 0x8e, 0xbc, 0x74, 0x70, 0x40, 0xf5, 0x19, 0xd6, - 0x56, 0x1c, 0xa8, 0x5b, 0x6c, 0x2e, 0xbc, 0x83 - }; - /* same as msg but has one bit flipped */ - static const uint8_t bad_msg[128] = { - 0x2d, 0xfc, 0x5d, 0xbd, 0x44, 0x2a, 0xb6, 0x48, 0x1d, 0x6c, - 0xc7, 0xce, 0xa4, 0xcd, 0x01, 0x47, 0xff, 0xae, 0xd2, 0xbe, - 0x1d, 0x0a, 0xd5, 0xb2, 0x92, 0xfe, 0x46, 0xbb, 0xa2, 0x88, - 0xb8, 0x71, 0x9b, 0x8f, 0x0a, 0x89, 0x69, 0x23, 0x97, 0x41, - 0x64, 0x07, 0xad, 0xff, 0x6c, 0x6c, 0x41, 0x34, 0x38, 0x00, - 0xe0, 0x87, 0xeb, 0x27, 0xe9, 0x30, 0xe8, 0x88, 0xfa, 0xa1, - 0xe8, 0xcc, 0xa8, 0x6c, 0x4a, 0xa2, 0x73, 0x61, 0xaa, 0x07, - 0xf8, 0xf6, 0xb4, 0xc5, 0x69, 0xed, /**/ - 0x3a, 0x38, 0x3b, 0x30, 0x85, 0x57, 0x1e, 0x00, 0xe9, 0xf3, - 0x32, 0x4e, 0x9c, 0x3b, 0x78, 0x69, 0xc9, 0x81, 0x87, 0xda, - 0xdf, 0x40, 0x80, 0x8c, 0x2f, 0x5d, 0x43, 0x31, 0xb6, 0xad, - 0xe3, 0xe0, 0x37, 0xb8, 0x58, 0x03, 0x8e, 0xbc, 0x74, 0x70, - 0x40, 0xf5, 0x19, 0xd6, 0x56, 0x1c, 0xa8, 0x5b, 0x6c, 0x2e, - 0xbc, 0x83 - }; - static const struct RSA rsa = { - .e = 0x00010001, - .N = { .dmax = sizeof(pub) / 4, - .d = (struct access_helper *)pub } - }; - - int passed; - - SHA256_hw_hash(msg, sizeof(msg), &digest); - passed = DCRYPTO_rsa_verify(&rsa, digest.b8, sizeof(digest), sig, - sizeof(sig), PADDING_MODE_PKCS1, - HASH_SHA256); - if (!passed) - return false; - SHA256_hw_hash(bad_msg, sizeof(bad_msg), &digest); - - /* now signature should fail */ - return !DCRYPTO_rsa_verify(&rsa, digest.b8, sizeof(digest), sig, - sizeof(sig), PADDING_MODE_PKCS1, - HASH_SHA256); -} -#endif - -/* Call function using provided stack. */ -static bool call_on_stack(void *new_stack, bool (*func)(void)) -{ - bool result; - /* Call whilst switching stacks */ - __asm__ volatile("mov r4, sp\n" /* save sp */ - "mov sp, %[new_stack]\n" - "blx %[func]\n" - "mov sp, r4\n" /* restore sp */ - "mov %[result], r0\n" - : [result] "=r"(result) - : [new_stack] "r"(new_stack), - [func] "r"(func) - : "r0", "r1", "r2", "r3", "r4", - "lr" /* clobbers */ - ); - return result; -} - -/* Placeholder for SHA256 digest of module computed during build time. */ -const struct sha256_digest fips_integrity - __attribute__((section(".rodata.fips.checksum"))); - -static enum dcrypto_result fips_self_integrity(void) -{ - struct sha256_digest digest; - size_t module_length = &__fips_module_end - &__fips_module_start; - -#ifdef CR50_DEV - CPRINTS("FIPS self-integrity start %x, length %u", - (uintptr_t)&__fips_module_start, module_length); -#endif - SHA256_hw_hash(&__fips_module_start, module_length, &digest); - -#ifdef CR50_DEV - CPRINTS("Stored: %ph", - HEX_BUF(fips_integrity.b8, SHA256_DIGEST_SIZE)); - CPRINTS("Computed: %ph", - HEX_BUF(digest.b8, SHA256_DIGEST_SIZE)); -#endif - - return DCRYPTO_equals(fips_integrity.b8, digest.b8, sizeof(digest)); -} - -/* Duration of FIPS tests. */ -uint64_t fips_last_kat_test_duration; - -#define FIPS_KAT_STACK_SIZE 2048 -void fips_power_up_tests(void) -{ - char *stack_buf; - void *stack; - uint64_t starttime; - - starttime = fips_vtable->get_time().val; - - if (fips_self_integrity() != DCRYPTO_OK) - _fips_status |= FIPS_FATAL_SELF_INTEGRITY; - - /* Make sure hardware is properly configured. */ - if (!DCRYPTO_ladder_is_enabled()) - _fips_status |= FIPS_FATAL_OTHER; - - /** - * Since we are very limited on stack and static RAM, acquire - * shared memory for KAT tests temporary larger stack. - */ - if (EC_SUCCESS == - fips_vtable->shared_mem_acquire(FIPS_KAT_STACK_SIZE, &stack_buf)) { - stack = stack_buf + FIPS_KAT_STACK_SIZE; - if (!call_on_stack(stack, &fips_sha256_kat)) - _fips_status |= FIPS_FATAL_SHA256; - if (!call_on_stack(stack, &fips_hmac_sha256_kat)) - _fips_status |= FIPS_FATAL_HMAC_SHA256; - /** - * Since TRNG FIFO takes some time to fill in, we can mask - * latency by splitting TRNG tests in 2 halves, each - * 2048 bits. This saves 20 ms on start. - * first call to TRNG warm-up - */ - fips_trng_startup(0); - if (!call_on_stack(stack, &fips_ecdsa_verify_kat)) - _fips_status |= FIPS_FATAL_ECDSA; - - if (!call_on_stack(stack, &fips_hmac_drbg_kat)) - _fips_status |= FIPS_FATAL_HMAC_DRBG; - -#ifdef CONFIG_FIPS_AES_CBC_256 - if (!call_on_stack(stack, &fips_aes256_kat)) - _fips_status |= FIPS_FATAL_AES256; -#endif - -#ifdef CONFIG_FIPS_RSA2048 - /* RSA KAT adds 30ms and not used for U2F */ - if (!call_on_stack(stack, &fips_rsa2048_verify_kat)) - _fips_status |= FIPS_FATAL_RSA2048; -#endif - /** - * Grab the SHA hardware lock to force the following KATs to use - * the software implementation. - */ - if (!dcrypto_grab_sha_hw()) - _fips_status |= FIPS_FATAL_SHA256; - - if (!call_on_stack(stack, &fips_sha256_kat)) - _fips_status |= FIPS_FATAL_SHA256; - if (!call_on_stack(stack, &fips_hmac_sha256_kat)) - _fips_status |= FIPS_FATAL_HMAC_SHA256; -#ifdef CONFIG_FIPS_SW_HMAC_DRBG - /* SW HMAC DRBG adds 30ms and not used for U2F */ - if (!call_on_stack(stack, &fips_hmac_drbg_kat)) - _fips_status |= FIPS_FATAL_HMAC_DRBG; -#endif - dcrypto_release_sha_hw(); - fips_vtable->shared_mem_release(stack_buf); - - /* Second call to TRNG warm-up. */ - fips_trng_startup(1); - - /* If no errors, set not to run tests on wake from sleep. */ - if (fips_is_no_crypto_error()) - fips_set_power_up(true); -#ifdef CONFIG_FLASH_LOG - else /* write combined error to flash log */ - fips_vtable->flash_log_add_event(FE_LOG_FIPS_FAILURE, - sizeof(_fips_status), - &_fips_status); -#endif - /* Set the bit that power-up tests completed, even if failed. */ - _fips_status |= FIPS_POWER_UP_TEST_DONE; - } else - _fips_status |= FIPS_FATAL_OTHER; - - fips_last_kat_test_duration = fips_vtable->get_time().val - starttime; -} - -void fips_power_on(void) -{ - fips_last_kat_test_duration = -1ULL; - /* make sure on power-on / resume it's cleared */ - _fips_status = FIPS_UNINITIALIZED; - - /** - * If this was a power-on or power-up tests weren't executed - * for some reason, run them now. Board FIPS KAT status will - * be updated by fips_power_up_tests() if all tests pass. - */ - if (!fips_is_power_up_done()) - fips_power_up_tests(); - else /* tests were already completed before sleep */ - _fips_status |= FIPS_POWER_UP_TEST_DONE; - - /* Check if we can set FIPS-approved mode. */ - if (fips_crypto_allowed()) - fips_set_status(FIPS_MODE_ACTIVE); - -} - -const struct fips_vtable *fips_vtable; - -/** - * Check that given address is in same half of flash as FIPS code. - * This rejects addresses in SRAM and provides additional security. - */ -static bool is_flash_address(const void *ptr) -{ - uintptr_t my_addr = - (uintptr_t)is_flash_address - CONFIG_PROGRAM_MEMORY_BASE; - uintptr_t offset = (uintptr_t)ptr - CONFIG_PROGRAM_MEMORY_BASE; - - if (my_addr >= CONFIG_RW_MEM_OFF && - my_addr < CFG_TOP_A_OFF) - return (offset >= CONFIG_RW_MEM_OFF) && - (offset <= CFG_TOP_A_OFF); - if (my_addr >= CONFIG_RW_B_MEM_OFF && - my_addr < CFG_TOP_B_OFF) - return (offset >= CONFIG_RW_B_MEM_OFF) && - (offset <= CFG_TOP_B_OFF); - - /* Otherwise, we don't know what's going on, don't accept it. */ - return false; -} - -void fips_set_callbacks(const struct fips_vtable *vtable) -{ - if (is_flash_address(vtable) && - is_flash_address(vtable->shared_mem_acquire) && - is_flash_address(vtable->shared_mem_release) && -#ifdef CONFIG_FLASH_LOG - is_flash_address(vtable->flash_log_add_event) && -#endif -#ifdef CONFIG_WATCHDOG - is_flash_address(vtable->watchdog_reload) && -#endif - is_flash_address(vtable->get_time) && - is_flash_address(vtable->task_enable_irq) && - is_flash_address(vtable->task_wait_event_mask) && - is_flash_address(vtable->task_set_event) && - is_flash_address(vtable->task_get_current) && - is_flash_address(vtable->task_start_irq_handler) && - is_flash_address(vtable->task_resched_if_needed) && - is_flash_address(vtable->mutex_lock) && - is_flash_address(vtable->mutex_unlock)) - - fips_vtable = vtable; - else - fips_vtable = NULL; -} diff --git a/board/cr50/fips.h b/board/cr50/fips.h deleted file mode 100644 index 52d8ec68c6..0000000000 --- a/board/cr50/fips.h +++ /dev/null @@ -1,169 +0,0 @@ -/* Copyright 2020 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 __EC_BOARD_CR50_FIPS_H__ -#define __EC_BOARD_CR50_FIPS_H__ - -#include "common.h" -#include "timer.h" -#include "task.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Signals start in the top most bits, errors in the least significant bits. */ -enum fips_status { - /* FIPS status */ - FIPS_MODE_ACTIVE = 1U << 31, - FIPS_POWER_UP_TEST_DONE = 1U << 30, - - FIPS_UNINITIALIZED = 0, /* Default value */ - - /* FIPS errors */ - FIPS_FATAL_TRNG_RCT = 1 << 1, - FIPS_FATAL_TRNG_APT = 1 << 2, - FIPS_FATAL_TRNG_OTHER = 1 << 3, - FIPS_FATAL_SHA256 = 1 << 4, - FIPS_FATAL_HMAC_SHA256 = 1 << 5, - FIPS_FATAL_HMAC_DRBG = 1 << 6, - FIPS_FATAL_ECDSA = 1 << 7, -#ifdef CONFIG_FIPS_RSA2048 - FIPS_FATAL_RSA2048 = 1 << 8, -#endif -#ifdef CONFIG_FIPS_AES_CBC_256 - FIPS_FATAL_AES256 = 1 << 9, -#endif - FIPS_FATAL_SELF_INTEGRITY = 1 << 10, - FIPS_FATAL_BN_MATH = 1 << 11, - FIPS_FATAL_OTHER = 1 << 15, - FIPS_ERROR_MASK = 0xffff, - FIPS_RFU_MASK = 0x7fff0000 -}; - -/* Simulate error in specific block. */ -enum fips_break { - FIPS_NO_BREAK = 0, - FIPS_BREAK_TRNG = 1, - FIPS_BREAK_SHA256 = 2, - FIPS_BREAK_HMAC_SHA256 = 3, - FIPS_BREAK_HMAC_DRBG = 4, - FIPS_BREAK_ECDSA = 5, - FIPS_BREAK_ECDSA_PWCT = 6, -#ifdef CONFIG_FIPS_AES_CBC_256 - FIPS_BREAK_AES256 = 7, -#endif -#ifdef CONFIG_FIPS_RSA2048 - FIPS_BREAK_RSA2048 = 8, -#endif -}; - -#ifdef CRYPTO_TEST_SETUP -extern uint8_t fips_break_cmd; -#endif - -/* Duration of last FIPS KAT / Power-up tests. */ -extern uint64_t fips_last_kat_test_duration; - -/* Command codes for VENDOR_CC_FIPS_CMD. */ -enum fips_cmd { - FIPS_CMD_GET_STATUS = 0, - FIPS_CMD_ON = 1, - FIPS_CMD_TEST = 2, - FIPS_CMD_BREAK_TRNG = 3, - FIPS_CMD_BREAK_SHA256 = 4, - FIPS_CMD_BREAK_HMAC_SHA256 = 5, - FIPS_CMD_BREAK_HMAC_DRBG = 6, - FIPS_CMD_BREAK_ECDSA = 7, - FIPS_CMD_BREAK_ECDSA_PWCT = 8, -#ifdef CONFIG_FIPS_AES_CBC_256 - FIPS_CMD_BREAK_AES256 = 9, -#endif -#ifdef CONFIG_FIPS_RSA2048 - FIPS_CMD_BREAK_RSA2048 = 10, -#endif - FIPS_CMD_NO_BREAK = 11 -}; - -/* These symbols defined in core/cortex-m/ec.lds.S. */ -extern uint8_t __fips_module_start; -extern uint8_t __fips_module_end; - -/* Return current FIPS status of operations. */ -enum fips_status fips_status(void); - -/** - * Crypto is enabled when either FIPS mode is not enforced, - * or if it is enforced and in good health - * @returns non-zero if crypto can be executed. - */ -bool fips_crypto_allowed(void); - -/** - * Update FIPS status without updating log - */ -void fips_set_status(enum fips_status status); - -/** - * Update FIPS status with error code, write error in the log. - */ -void fips_throw_err(enum fips_status err); - -/** - * FIPS Power-up and known-answer tests. - * Single point of initialization for all FIPS-compliant - * cryptography. Responsible for KATs, TRNG testing, and signalling a - * fatal error. - * - * Set FIPS status globally as a result. - */ -void fips_power_up_tests(void); - -struct fips_vtable { - int (*shared_mem_acquire)(int size, char **dest_ptr); - void (*shared_mem_release)(void *ptr); -#ifdef CONFIG_FLASH_LOG - void (*flash_log_add_event)(uint8_t type, uint8_t size, void *payload); -#endif - void (*cflush)(void); - timestamp_t (*get_time)(void); - - void (*task_enable_irq)(int irq); - uint32_t (*task_wait_event_mask)(uint32_t event_mask, int timeout_us); - uint32_t (*task_set_event)(task_id_t tskid, uint32_t event, int wait); - task_id_t (*task_get_current)(void); - void (*task_start_irq_handler)(void *excep_return); - void (*task_resched_if_needed)(void *excep_return); - void (*mutex_lock)(struct mutex *mtx); - void (*mutex_unlock)(struct mutex *mtx); -#ifdef CONFIG_WATCHDOG - void (*watchdog_reload)(void); -#endif -}; - -/* Pointer to external callbacks used by FIPS module. */ -extern const struct fips_vtable *fips_vtable; - -/** - * Set FIPS module vtable. Called during board_init() phase to provide - * pointers to several system functions required by module to function. - * - * This should be called before any other FIPS functions are invoked as - * vtable is used during FIPS power-up tests. Internally it checks that - * provided vtable and referenced functions are in the same flash bank - * as the FIPS module for additional security. - */ -void fips_set_callbacks(const struct fips_vtable *vtable); - -/** - * Run FIPS self-integrity, power-on and known-answer tests. - * Called from board_init() during power-up and resume from sleep. - * Enables crypto operation on successful completion. - */ -void fips_power_on(void); - -#ifdef __cplusplus -} -#endif -#endif /* __EC_BOARD_CR50_FIPS_H__ */ diff --git a/board/cr50/fips_rand.c b/board/cr50/fips_rand.c deleted file mode 100644 index 926af66f96..0000000000 --- a/board/cr50/fips_rand.c +++ /dev/null @@ -1,407 +0,0 @@ -/* Copyright 2020 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. - */ - -#include "console.h" -#include "fips.h" -#include "fips_rand.h" -#include "flash_log.h" -#include "init_chip.h" -#include "registers.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -/** - * FIPS-compliant CR50-wide DRBG - * Since raw TRNG input shouldn't be used as random number generator, - * all FIPS-compliant code use DRBG, seeded from TRNG - */ -static struct drbg_ctx fips_drbg; - -#define ENTROPY_SIZE_BITS 512 -#define ENTROPY_SIZE_WORDS (BITS_TO_WORDS(ENTROPY_SIZE_BITS)) - -/** - * buffer for entropy condensing. initialized during - * fips_trng_startup(), but also used in KAT tests, - * thus size is enough to accommodate needs - */ -static uint32_t entropy_fifo[ENTROPY_SIZE_WORDS]; - -/** - * NIST FIPS TRNG health tests (NIST SP 800-90B 4.3) - * If any of the approved continuous health tests are used by the entropy - * source, the false positive probability for these tests shall be set to - * at least 2^-50 - */ - - -/* state data for TRNG health test */ -static struct { - uint32_t count; /* number of 1-bits in APT test window */ - int last_clz; /* running count of leading 0 bits */ - int last_clo; /* running count of leading 1 bits */ - uint8_t pops[APT_WINDOW_SIZE_NWORDS]; - uint8_t rct_count; /* current windows size for RCT */ - uint8_t oldest; /* position in APT window */ - bool apt_initialized; /* flag APT window is filled with data */ - bool drbg_initialized; /* flag DRBG is initialized */ -} rand_state; - -/** - * NIST SP 800-90B 4.4.1 - * The repetition count test detects abnormal runs of 0s or 1s. - * RCT_CUTOFF_BITS must be >= 32. - * If a single value appears more than 100/H times in a row, - * the tests must detect this with high probability. - * - * This implementation assumes TRNG is configured to produce 1-bit - * readings, packed into 32-bit words. - * @return false if test failed - */ -static bool repetition_count_test(uint32_t rnd) -{ - uint32_t clz, ctz, clo, cto; - /* count repeating 0 and 1 bits from each side */ - clz = __builtin_clz(rnd); /* # of leading 0s */ - ctz = __builtin_ctz(rnd); /* # of trailing 0s */ - clo = __builtin_clz(~rnd); /* # of leading 1s */ - cto = __builtin_ctz(~rnd); /* # of trailing 1s */ - - /** - * check that number of trailing 0/1 in current sample added to - * leading 0/1 of previous sample is less than cut off value, so - * we don't have long repetitive series of 0s or 1s - */ - if ((ctz + rand_state.last_clz >= RCT_CUTOFF_SAMPLES) || - (cto + rand_state.last_clo >= RCT_CUTOFF_SAMPLES)) - return false; - - /** - * merge series of repetitive values - update running counters in - * such way that if current sample is all 0s then add previous - * counter of zeros to current number which will be 32, - * otherwise (we had 1s) - just use current value. Same for 1s - */ - if (rnd == 0) /* if all 32 samples are 0s */ - clz += rand_state.last_clz; - - if (rnd == ~0) /* if all 32 samples are 1s */ - clo += rand_state.last_clo; - rand_state.last_clz = clz; - rand_state.last_clo = clo; - - /* check we collected enough bits for statistics */ - if (rand_state.rct_count < RCT_CUTOFF_WORDS) - ++rand_state.rct_count; - return true; -} - -static int misbalanced(uint32_t count) -{ - return count > APT_CUTOFF_SAMPLES || - count < APT_WINDOW_SIZE_BITS - APT_CUTOFF_SAMPLES; -} - -/* Returns number of set bits (1s) in 32-bit word */ -static int popcount(uint32_t x) -{ - x = x - ((x >> 1) & 0x55555555); - x = (x & 0x33333333) + ((x >> 2) & 0x33333333); - x = (x + (x >> 4)) & 0x0F0F0F0F; - x = x + (x >> 8); - x = x + (x >> 16); - return x & 0x0000003F; -} - -/** - * NIST SP 800-90B 4.4.2 Adaptive Proportion Test. - * Implementation for 1-bit alphabet. - * Instead of storing actual samples we can store pop counts - * of each 32bit reading, which would fit in 8-bit. - */ -bool adaptive_proportion_test(uint32_t rnd) -{ - /* update rolling count */ - rand_state.count -= rand_state.pops[rand_state.oldest]; - /** - * Since we have 1-bit samples, in order to have running window to - * count ratio of 1s and 0s in it we can just store number of 1s in - * each 32-bit sample which requires 1 byte vs. 4 bytes. - * number of zeros = 32 - number of 1s - */ - rand_state.pops[rand_state.oldest] = popcount(rnd); - - /* update rolling count with current sample statistics */ - rand_state.count += rand_state.pops[rand_state.oldest]; - if (++rand_state.oldest >= APT_WINDOW_SIZE_NWORDS) { - rand_state.apt_initialized = true; /* saw full window */ - rand_state.oldest = 0; - } - /* check when initialized */ - if (rand_state.apt_initialized && misbalanced(rand_state.count)) - return false; - return true; -} - -static bool fips_powerup_passed(void) -{ - return rand_state.rct_count >= RCT_CUTOFF_WORDS && - rand_state.apt_initialized; -} - - -/** - * get random from TRNG and run continuous health tests. - * it is also can simulate stuck-bit error - * @param power_up if non-zero indicates warm-up mode - * @return random value from TRNG - */ -static uint64_t fips_trng32(int power_up) -{ - uint64_t r; - - /* Continuous health tests should have been initialized by now */ - if (!(power_up || fips_crypto_allowed())) - return 0; - - /* get noise */ - r = read_rand(); - - if (rand_valid(r)) { - if (!repetition_count_test((uint32_t)r)) { - fips_set_status(FIPS_FATAL_TRNG_RCT); - r = (uint32_t)r; - } - if (!adaptive_proportion_test((uint32_t)r)) { - fips_set_status(FIPS_FATAL_TRNG_APT); - r = (uint32_t)r; - } - } else - fips_set_status(FIPS_FATAL_TRNG_OTHER); - - return r; -} - -bool fips_trng_bytes(void *buffer, size_t len) -{ - uint8_t *buf = (uint8_t *)buffer; - size_t random_togo = 0; - uint64_t rand; - uint32_t r; - /** - * Retrieve random numbers in 4 byte quantities and pack as many bytes - * as needed into 'buffer'. If len is not divisible by 4, the - * remaining random bytes get dropped. - */ - while (len--) { - if (!random_togo) { - rand = fips_trng32(0); - if (!rand_valid(rand)) - return false; - r = (uint32_t)rand; - random_togo = sizeof(r); - } - *buf++ = (uint8_t)r; - random_togo--; - r >>= 8; - } - return true; -} - -/* FIPS TRNG power-up tests */ -bool fips_trng_startup(int stage) -{ - if (!stage) { - /** - * To hide TRNG latency, split it into 2 stages. - * at stage 0, initialize variables - */ - rand_state.rct_count = 0; - rand_state.apt_initialized = 0; - } - /* Startup tests per NIST SP800-90B, Section 4 */ - /* 4096 1-bit samples, in 2 steps, 2048 bit in each */ - for (uint32_t i = 0; i < (TRNG_INIT_WORDS) / 2; i++) { - uint64_t r = fips_trng32(1); - - if (!rand_valid(r)) - return false; - /* store entropy for further use */ - entropy_fifo[i % ARRAY_SIZE(entropy_fifo)] = (uint32_t)r; - } - return fips_powerup_passed(); -} - -bool fips_drbg_init(void) -{ - uint64_t nonce; - uint32_t random; - - if (!fips_crypto_allowed()) - return false; - - /** - * initialize DRBG with 440 bits of entropy as required - * by NIST SP 800-90A 10.1. Includes entropy and nonce, - * both received from entropy source. - * entropy_fifo contains 512 bits of noise with H>= 0.85 - * this is roughly equal to 435 bits of full entropy. - * Add 32 * 0.85 = 27 bits from nonce. - */ - nonce = fips_trng32(0); - if (!rand_valid(nonce)) - return false; - random = (uint32_t)nonce; - - /* read another 512 bits of noise */ - if (!fips_trng_bytes(&entropy_fifo, sizeof(entropy_fifo))) - return false; - - hmac_drbg_init(&fips_drbg, &entropy_fifo, sizeof(entropy_fifo), - &random, sizeof(random), NULL, - 0); - - set_fast_random_seed((uint32_t)fips_trng32(0)); - rand_state.drbg_initialized = 1; - return true; -} - -/* zeroize DRBG state */ -void fips_drbg_clear(void) -{ - drbg_exit(&fips_drbg); - rand_state.drbg_initialized = 0; -} - -enum hmac_result fips_hmac_drbg_generate_reseed(struct drbg_ctx *ctx, void *out, - size_t out_len, - const void *input, - size_t input_len) -{ - enum hmac_result err = - hmac_drbg_generate(ctx, out, out_len, input, input_len); - - while (err == HMAC_DRBG_RESEED_REQUIRED) { - /* read another 512 bits of noise */ - if (!fips_trng_bytes(&entropy_fifo, sizeof(entropy_fifo))) { - /* FIPS error is reported by failed TRNG test. */ - return HMAC_DRBG_RESEED_REQUIRED; - } - - hmac_drbg_reseed(ctx, entropy_fifo, sizeof(entropy_fifo), NULL, - 0, NULL, 0); - err = hmac_drbg_generate(ctx, out, out_len, input, input_len); - } - return err; -} - -bool fips_rand_bytes(void *buffer, size_t len) -{ - if (!fips_crypto_allowed()) - return false; - /** - * make sure cr50 DRBG is initialized after power-on or resume, - * but do it on first use to minimize latency of board_init() - */ - if (!rand_state.drbg_initialized && !fips_drbg_init()) - return false; - - /* HMAC_DRBG can only return up to 7500 bits in a single request */ - while (len) { - size_t request = (len > (7500 / 8)) ? (7500 / 8) : len; - - if (fips_hmac_drbg_generate_reseed(&fips_drbg, buffer, request, - NULL, - 0) != HMAC_DRBG_SUCCESS) - return false; - len -= request; - buffer += request; - } - return true; -} - -/* return codes match dcrypto_p256_ecdsa_sign */ -int fips_p256_ecdsa_sign(const p256_int *key, const p256_int *message, - p256_int *r, p256_int *s) -{ - if (!fips_crypto_allowed()) - return 0; - if (!rand_state.drbg_initialized && !fips_drbg_init()) - return false; - - return dcrypto_p256_ecdsa_sign(&fips_drbg, key, message, r, s); -} - -#ifndef CRYPTO_TEST_CMD_RAND_PERF -#define CRYPTO_TEST_CMD_RAND_PERF 0 -#endif - -#if defined(CRYPTO_TEST_SETUP) && CRYPTO_TEST_CMD_RAND_PERF -#include "endian.h" -#include "extension.h" -#include "trng.h" -#include "watchdog.h" - -static int cmd_rand_perf(int argc, char **argv) -{ - uint64_t starttime; - static uint32_t buf[SHA256_DIGEST_WORDS]; - int j, k; - - starttime = get_time().val; - - /* run power-up tests to measure raw performance */ - if (fips_trng_startup(0) && fips_trng_startup(1)) { - starttime = get_time().val - starttime; - ccprintf("time for fips_trng_startup = %llu\n", starttime); - } else - ccprintf("TRNG power up test failed\n"); - - cflush(); - - starttime = get_time().val; - fips_drbg_init(); - starttime = get_time().val - starttime; - ccprintf("time for drbg_init = %llu\n", starttime); - cflush(); - starttime = get_time().val; - for (k = 0; k < 10; k++) { - for (j = 0; j < 100; j++) - fips_rand_bytes(buf, sizeof(buf)); - watchdog_reload(); - cflush(); - } - starttime = get_time().val - starttime; - ccprintf("time for 1000 drbg reads = %llu\n", starttime); - cflush(); - - starttime = get_time().val; - for (k = 0; k < 10; k++) { - for (j = 0; j < 100; j++) - rand_bytes(&buf, sizeof(buf)); - watchdog_reload(); - } - starttime = get_time().val - starttime; - ccprintf("time for 1000 rand_byte() = %llu\n", starttime); - cflush(); - - starttime = get_time().val; - for (k = 0; k < 10; k++) { - for (j = 0; j < 100; j++) - if (!fips_trng_bytes(&buf, sizeof(buf))) - ccprintf("FIPS TRNG error\n"); - watchdog_reload(); - } - starttime = get_time().val - starttime; - ccprintf("time for 1000 fips_trng_byte() = %llu\n", starttime); - cflush(); - - return 0; -} - -DECLARE_SAFE_CONSOLE_COMMAND(rand_perf, cmd_rand_perf, NULL, NULL); - -#endif /* CRYPTO_TEST_SETUP */ diff --git a/board/cr50/fips_rand.h b/board/cr50/fips_rand.h deleted file mode 100644 index dca1f473bf..0000000000 --- a/board/cr50/fips_rand.h +++ /dev/null @@ -1,134 +0,0 @@ -/* Copyright 2020 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 __EC_BOARD_CR50_FIPS_RAND_H -#define __EC_BOARD_CR50_FIPS_RAND_H - -#include -#include - -#include "common.h" -#include "dcrypto.h" -#include "util.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define TRNG_SAMPLE_BITS 1 - -/** - * Initialize the true random number generator (TRNG) in FIPS-compliant - * way: - * 1. Set 1-bit alphabet - * 2. Set maximum possible range for internal ring-oscillator - * 3. Disable any other post-processing beyond #2 - **/ -void fips_init_trng(void); - -/** - * Returns random number with indication wherever reading is valid. This is - * different from rand() which doesn't provide any indication. - * High 32-bits set to zero in case of error; otherwise value >> 32 == 1 - * Use of uint64_t vs. struct results in more efficient code. - */ -uint64_t read_rand(void); - -/* Return true if read_rand() result contains valid random from TRNG. */ -static inline bool rand_valid(uint64_t rand) -{ - return (rand >> 32) != 0; -} - -/** - * TRNG Health Tests - * - * If any of the approved continuous health tests are used by the entropy - * source, the false positive probability for these tests shall be set to - * at least 2^(-50) (NIST SP 800-90B 4.3). - * Reason for 2^(-50) vs 2^(-40) is to minimize impact to user experience - * due to false positives. - * - * For H1 minimal assessed entropy H >=0.85 for 1-bit samples - * using NIST Entropy Assessment tool. - */ - -/** - * The entropy source's startup tests shall run the continuous health tests - * over at least 4096 consecutive samples. We use 1-bit samples - */ -#define TRNG_INIT_BITS (4096 * TRNG_SAMPLE_BITS) -#define TRNG_INIT_WORDS (BITS_TO_WORDS(TRNG_INIT_BITS)) - -/** - * (1) Repetition Count Test (RCT) NIST SP 800-90B 4.4.1 - * Cut off value is computed as: - * c = ceil(1 + (-log2 alpha)/H); - * alpha = 2^-50, H = 0.85; RCT_CUTOFF = CEIL(1+(50/0.85)) - */ -#define RCT_CUTOFF_SAMPLES 60 - -/** - * Number of 32-bit words containing RCT_CUTOFF_SAMPLES samples - */ -#define RCT_CUTOFF_WORDS (BITS_TO_WORDS(RCT_CUTOFF_SAMPLES)) - -/** - * (2) Adaptive Proportion Test (APT), NIST SP 800-90B 4.4.2, Table 2 - */ -#if TRNG_SAMPLE_BITS == 1 -/* APT Windows size W = 1024 for 1 bit samples */ -#define APT_WINDOW_SIZE_SAMPLES 1024 -#else -/* or 512 samples if more than 1 bit per sample */ -#define APT_WINDOW_SIZE_SAMPLES 512 -#endif -#define APT_WINDOW_SIZE_BITS (APT_WINDOW_SIZE_SAMPLES * TRNG_SAMPLE_BITS) -#define APT_WINDOW_SIZE_NWORDS (BITS_TO_WORDS(APT_WINDOW_SIZE_BITS)) -/** - * Cut off value = CRITBINOM(W, power(2,(-H)),1-α). - * 692 = CRITBINOM(1024, power(2,(-0.85)), 1 - 2^(-50)) - */ -#define APT_CUTOFF_SAMPLES 692 - -/** - * FIPS-compliant TRNG startup. - * The entropy source's startup tests shall run the continuous health tests - * over at least 4096 consecutive samples. - * Note: This function can throw FIPS_FATAL_TRNG error - * - * To hide latency of reading TRNG data, this test is executed in 2 stages - * @param stage is 0 or 1, choosing the stage. On each stage 2048 - * samples are processed. Assuming that some other tasks can be executed - * between stages, when TRNG FIFO if filled with samples. - * - * Some number of samples will be available in entropy_fifo - */ -bool fips_trng_startup(int stage); - -bool fips_trng_bytes(void *buffer, size_t len); - -/* initialize cr50-wide DRBG replacing rand */ -bool fips_drbg_init(void); -/* mark cr50-wide DRBG as not initialized */ -void fips_drbg_init_clear(void); - -/* random bytes using FIPS-compliant HMAC_DRBG */ -bool fips_rand_bytes(void *buffer, size_t len); - -/* wrapper around dcrypto_p256_ecdsa_sign using FIPS-compliant HMAC_DRBG */ -int fips_p256_ecdsa_sign(const p256_int *key, const p256_int *message, - p256_int *r, p256_int *s); -/** - * wrapper around hmac_drbg_generate to automatically reseed drbg - * when needed. - */ -enum hmac_result fips_hmac_drbg_generate_reseed(struct drbg_ctx *ctx, void *out, - size_t out_len, - const void *input, - size_t input_len); -#ifdef __cplusplus -} -#endif -#endif /* ! __EC_BOARD_CR50_FIPS_RAND_H */ diff --git a/board/cr50/u2f.c b/board/cr50/u2f.c deleted file mode 100644 index 9f987fbd64..0000000000 --- a/board/cr50/u2f.c +++ /dev/null @@ -1,639 +0,0 @@ -/* 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. - */ - -#if defined(CRYPTO_TEST_SETUP) || defined(CR50_DEV) -#include "console.h" -#endif - -#include "dcrypto.h" -#include "fips_rand.h" - -#include "u2f_cmds.h" -#include "u2f_impl.h" -#include "util.h" - -enum ec_error_list u2f_generate_hmac_key(struct u2f_state *state) -{ - /* HMAC key for key handle. */ - if (!fips_rand_bytes(state->hmac_key, sizeof(state->hmac_key))) - return EC_ERROR_HW_INTERNAL; - return EC_SUCCESS; -} - -enum ec_error_list u2f_generate_drbg_entropy(struct u2f_state *state) -{ - state->drbg_entropy_size = 0; - /* Get U2F entropy from health-checked TRNG. */ - if (!fips_trng_bytes(state->drbg_entropy, sizeof(state->drbg_entropy))) - return EC_ERROR_HW_INTERNAL; - state->drbg_entropy_size = sizeof(state->drbg_entropy); - return EC_SUCCESS; -} - -enum ec_error_list u2f_generate_g2f_secret(struct u2f_state *state) -{ - /* G2F specific path. */ - if (!fips_rand_bytes(state->salt, sizeof(state->salt))) - return EC_ERROR_HW_INTERNAL; - return EC_SUCCESS; -} - -/* Compute Key handle's HMAC. */ -static void u2f_origin_user_mac(const struct u2f_state *state, - const uint8_t *user, const uint8_t *origin, - const uint8_t *origin_seed, uint8_t kh_version, - uint8_t *kh_hmac) -{ - struct hmac_sha256_ctx ctx; - - /* HMAC(u2f_hmac_key, origin || user || origin seed || version) */ - - HMAC_SHA256_hw_init(&ctx, state->hmac_key, SHA256_DIGEST_SIZE); - HMAC_SHA256_update(&ctx, origin, U2F_APPID_SIZE); - HMAC_SHA256_update(&ctx, user, U2F_USER_SECRET_SIZE); - HMAC_SHA256_update(&ctx, origin_seed, U2F_ORIGIN_SEED_SIZE); - if (kh_version != 0) - HMAC_SHA256_update(&ctx, &kh_version, sizeof(kh_version)); -#ifdef CR50_DEV_U2F_VERBOSE - ccprintf("origin %ph\n", HEX_BUF(origin, U2F_APPID_SIZE)); - ccprintf("user %ph\n", HEX_BUF(user, U2F_USER_SECRET_SIZE)); - ccprintf("origin_seed %ph\n", - HEX_BUF(origin_seed, U2F_ORIGIN_SEED_SIZE)); - cflush(); -#endif - memcpy(kh_hmac, HMAC_SHA256_final(&ctx), SHA256_DIGEST_SIZE); -#ifdef CR50_DEV_U2F_VERBOSE - ccprintf("kh_hmac %ph\n", HEX_BUF(kh_hmac, SHA256_DIGEST_SIZE)); - cflush(); -#endif -} - -static void u2f_authorization_mac(const struct u2f_state *state, - const union u2f_key_handle_variant *kh, - uint8_t kh_version, - const uint8_t *auth_time_secret_hash, - uint8_t *kh_auth_mac) -{ - struct hmac_sha256_ctx ctx; - const uint8_t *auth_salt = NULL; - const void *kh_header = NULL; - size_t kh_header_size = 0; - - if (kh_version == 0) { - memset(kh_auth_mac, 0xff, SHA256_DIGEST_SIZE); - return; - } - /* At some point we may have v2 key handle, so prepare for it. */ - if (kh_version == 1) { - auth_salt = kh->v1.authorization_salt; - kh_header = &kh->v1; - kh_header_size = U2F_V1_KH_HEADER_SIZE; - } - - /** - * HMAC(u2f_hmac_key, auth_salt || key_handle_header - * || authTimeSecret) - */ - HMAC_SHA256_hw_init(&ctx, state->hmac_key, SHA256_DIGEST_SIZE); - HMAC_SHA256_update(&ctx, auth_salt, U2F_AUTHORIZATION_SALT_SIZE); - HMAC_SHA256_update(&ctx, kh_header, kh_header_size); - - HMAC_SHA256_update(&ctx, auth_time_secret_hash, - U2F_AUTH_TIME_SECRET_SIZE); - - memcpy(kh_auth_mac, HMAC_SHA256_final(&ctx), SHA256_DIGEST_SIZE); -} - -static int app_hw_device_id(enum dcrypto_appid appid, const uint32_t input[8], - uint32_t output[8]) -{ - struct APPKEY_CTX ctx; - int result; - - /** - * Setup USR-based application key. This loads (if not already done) - * application-specific DeviceID. - * Internally it computes: - * HMAC(hw_device_id, SHA256(name[appid])), but we don't care about - * process. - * Important property: - * For same appid it will load same value. - */ - if (!DCRYPTO_appkey_init(appid, &ctx)) - return 0; - - /** - * Compute HMAC(HMAC(hw_device_id, SHA256(name[appid])), input) - * It is not used as a key though, and treated as personalization - * string for DRBG. - */ - result = DCRYPTO_appkey_derive(appid, input, output); - - DCRYPTO_appkey_finish(&ctx); - return result; -} - -/** - * Generate an origin and user-specific ECDSA key pair from the specified - * key handle. - * - * If pk_x and pk_y are NULL, public key generation will be skipped. - * - * @param state U2F state parameters - * @param kh key handle - * @param kh_version key handle version (0 - legacy, 1 - versioned) - * @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 a valid key pair was created. - */ -static enum ec_error_list u2f_origin_user_key_pair( - const struct u2f_state *state, const union u2f_key_handle_variant *kh, - uint8_t kh_version, p256_int *d, p256_int *pk_x, p256_int *pk_y) -{ - uint32_t dev_salt[P256_NDIGITS]; - uint8_t key_seed[P256_NBYTES]; - - struct drbg_ctx drbg; - size_t key_handle_size = 0; - uint8_t *key_handle = NULL; - - if (kh_version == 0) { - key_handle_size = sizeof(struct u2f_key_handle_v0); - key_handle = (uint8_t *)&kh->v0; - } else if ((kh_version == 1) && (kh->v1.version == kh_version)) { - key_handle_size = U2F_V1_KH_HEADER_SIZE; - key_handle = (uint8_t *)&kh->v1; - } else { - return EC_ERROR_INVAL; - } - - /* TODO(sukhomlinov): implement new FIPS path. */ - if (!app_hw_device_id(U2F_ORIGIN, state->hmac_key, dev_salt)) - return EC_ERROR_UNKNOWN; - - hmac_drbg_init(&drbg, state->drbg_entropy, P256_NBYTES, dev_salt, - P256_NBYTES, NULL, 0); - - hmac_drbg_generate(&drbg, key_seed, sizeof(key_seed), key_handle, - key_handle_size); - - if (!DCRYPTO_p256_key_from_bytes(pk_x, pk_y, d, key_seed)) - return EC_ERROR_TRY_AGAIN; - -#ifdef CR50_DEV_U2F_VERBOSE - ccprintf("user private key %ph\n", HEX_BUF(d, sizeof(*d))); - cflush(); - if (pk_x) - ccprintf("user public x %ph\n", HEX_BUF(pk_x, sizeof(*pk_x))); - if (pk_y) - ccprintf("user public y %ph\n", HEX_BUF(pk_y, sizeof(*pk_y))); - cflush(); -#endif - - return EC_SUCCESS; -} - -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) -{ - uint8_t *kh_hmac, *kh_origin_seed; - int generate_key_pair_rc; - /* Generated public keys associated with key handle. */ - p256_int opk_x, opk_y; - - /* Compute constants for request key handler version. */ - if (kh_version == 0) { - kh_hmac = kh->v0.hmac; - kh_origin_seed = kh->v0.origin_seed; - } else if (kh_version == 1) { - kh_hmac = kh->v1.kh_hmac; - kh_origin_seed = kh->v1.origin_seed; - /** - * This may overwrite input parameters if shared - * request/response buffer is used by caller. - */ - kh->v1.version = kh_version; - } else - return EC_ERROR_INVAL; - - /* Generate key handle candidates and origin-specific key pair. */ - do { - p256_int od; - /* Generate random origin seed for key handle candidate. */ - if (!fips_rand_bytes(kh_origin_seed, U2F_ORIGIN_SEED_SIZE)) - return EC_ERROR_HW_INTERNAL; - - u2f_origin_user_mac(state, user, origin, kh_origin_seed, - kh_version, kh_hmac); - - /** - * Try to generate key pair using key handle. This may fail if - * key handle results in private key which is out of allowed - * range. If this is the case, repeat with another origin seed. - */ - generate_key_pair_rc = u2f_origin_user_key_pair( - state, kh, kh_version, &od, &opk_x, &opk_y); - - p256_clear(&od); - } while (generate_key_pair_rc == EC_ERROR_TRY_AGAIN); - - if (generate_key_pair_rc != EC_SUCCESS) - return generate_key_pair_rc; - - if (kh_version == 1) { - if (!fips_rand_bytes(kh->v1.authorization_salt, - U2F_AUTHORIZATION_SALT_SIZE)) - return EC_ERROR_HW_INTERNAL; - - u2f_authorization_mac(state, kh, kh_version, authTimeSecretHash, - kh->v1.authorization_hmac); - } - - pubKey->pointFormat = U2F_POINT_UNCOMPRESSED; - p256_to_bin(&opk_x, pubKey->x); /* endianness */ - p256_to_bin(&opk_y, pubKey->y); /* endianness */ - - return EC_SUCCESS; -} - -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) -{ - /* Re-created key handle. */ - uint8_t recreated_hmac[SHA256_DIGEST_SIZE]; - const uint8_t *origin_seed, *kh_hmac; - int result = 0; - - /* - * Re-create the key handle and compare against that which - * was provided. This allows us to verify that the key handle - * is owned by this combination of device, current user and origin. - */ - if (kh_version == 0) { - origin_seed = kh->v0.origin_seed; - kh_hmac = kh->v0.hmac; - } else { - origin_seed = kh->v1.origin_seed; - kh_hmac = kh->v1.kh_hmac; - } - /* First, check inner part. */ - u2f_origin_user_mac(state, user, origin, origin_seed, kh_version, - recreated_hmac); - - /** - * DCRYPTO_equals return 1 if success, by subtracting 1 we make it - * zero, and other results - zero or non-zero will be detected. - */ - result |= DCRYPTO_equals(&recreated_hmac, kh_hmac, - sizeof(recreated_hmac)) - DCRYPTO_OK; - - always_memset(recreated_hmac, 0, sizeof(recreated_hmac)); - - if ((kh_version != 0) && (authTimeSecretHash != NULL)) { - u2f_authorization_mac(state, kh, kh_version, authTimeSecretHash, - recreated_hmac); - result |= DCRYPTO_equals(&recreated_hmac, - kh->v1.authorization_hmac, - sizeof(recreated_hmac)) - DCRYPTO_OK; - always_memset(recreated_hmac, 0, sizeof(recreated_hmac)); - } - - return (result == 0) ? EC_SUCCESS : EC_ERROR_ACCESS_DENIED; -} - -static enum ec_error_list -u2f_attest_keyhandle_pubkey(const struct u2f_state *state, - const union u2f_key_handle_variant *key_handle, - uint8_t kh_version, const uint8_t *user, - const uint8_t *origin, - const uint8_t *authTimeSecretHash, - const struct u2f_ec_point *public_key) -{ - struct u2f_ec_point kh_pubkey; - p256_int od, opk_x, opk_y; - enum ec_error_list result; - - /* Check this is a correct key handle for provided user/origin. */ - result = u2f_authorize_keyhandle(state, key_handle, kh_version, user, - origin, authTimeSecretHash); - - if (result != EC_SUCCESS) - return result; - - /* Recreate public key from key handle. */ - result = u2f_origin_user_key_pair(state, key_handle, kh_version, &od, - &opk_x, &opk_y); - if (result != EC_SUCCESS) - return result; - - p256_clear(&od); - /* Reconstruct the public key. */ - p256_to_bin(&opk_x, kh_pubkey.x); - p256_to_bin(&opk_y, kh_pubkey.y); - kh_pubkey.pointFormat = U2F_POINT_UNCOMPRESSED; - -#ifdef CR50_DEV_U2F_VERBOSE - ccprintf("recreated key %ph\n", HEX_BUF(&kh_pubkey, sizeof(kh_pubkey))); - ccprintf("provided key %ph\n", HEX_BUF(public_key, sizeof(kh_pubkey))); -#endif - return (DCRYPTO_equals(&kh_pubkey, public_key, - sizeof(struct u2f_ec_point)) == DCRYPTO_OK) ? - EC_SUCCESS : - EC_ERROR_ACCESS_DENIED; -} - -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) -{ - /* Origin private key. */ - p256_int origin_d; - - /* Hash, and corresponding signature. */ - p256_int h, r, s; - - struct drbg_ctx ctx; - enum ec_error_list result; - - result = u2f_authorize_keyhandle(state, kh, kh_version, user, origin, - authTimeSecretHash); - - if (result != EC_SUCCESS) - return result; - - /* Re-create origin-specific key. */ - result = u2f_origin_user_key_pair(state, kh, kh_version, &origin_d, - NULL, NULL); - if (result != EC_SUCCESS) - return result; - - /* Prepare hash to sign. */ - p256_from_bin(hash, &h); - - /* Now, we processed input parameters, so clean-up output. */ - memset(sig, 0, sizeof(*sig)); - - /* Sign. */ - hmac_drbg_init_rfc6979(&ctx, &origin_d, &h); - result = (dcrypto_p256_ecdsa_sign(&ctx, &origin_d, &h, &r, &s) != 0) ? - EC_SUCCESS : - EC_ERROR_HW_INTERNAL; - - p256_clear(&origin_d); - - p256_to_bin(&r, sig->sig_r); - p256_to_bin(&s, sig->sig_s); - - return result; -} - -/** - * Generate a hardware derived ECDSA key pair for individual attestation. - * - * @param state U2F state parameters - * @param d pointer to ECDSA private key - * @param pk_x pointer to public key point - * @param pk_y pointer to public key point - * - * @return true if a valid key pair was created. - */ -static bool g2f_individual_key_pair(const struct u2f_state *state, p256_int *d, - p256_int *pk_x, p256_int *pk_y) -{ - uint8_t buf[SHA256_DIGEST_SIZE]; - - /* Incorporate HIK & diversification constant. */ - if (!app_hw_device_id(U2F_ATTEST, state->salt, (uint32_t *)buf)) - return false; - - /* Generate unbiased private key (non-FIPS path). */ - while (!DCRYPTO_p256_key_from_bytes(pk_x, pk_y, d, buf)) { - struct sha256_ctx sha; - - SHA256_hw_init(&sha); - SHA256_update(&sha, buf, sizeof(buf)); - memcpy(buf, SHA256_final(&sha), sizeof(buf)); - } - - return true; -} - -#define G2F_CERT_NAME "CrO2" - -size_t g2f_attestation_cert_serial(const struct u2f_state *state, - const uint8_t *serial, uint8_t *buf) -{ - p256_int d, pk_x, pk_y; - - if (g2f_individual_key_pair(state, &d, &pk_x, &pk_y)) - return 0; - - /* Note that max length is not currently respected here. */ - return DCRYPTO_x509_gen_u2f_cert_name(&d, &pk_x, &pk_y, - (p256_int *)serial, G2F_CERT_NAME, - buf, - G2F_ATTESTATION_CERT_MAX_LEN); -} - -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) -{ - struct sha256_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; - - enum ec_error_list result; - - result = u2f_attest_keyhandle_pubkey(state, kh, kh_version, user, - origin, authTimeSecretHash, - public_key); - - if (result != EC_SUCCESS) - return result; - - /* Derive G2F Attestation Key. */ - if (!g2f_individual_key_pair(state, &d, &pk_x, &pk_y)) { -#ifdef CR50_DEV - ccprintf("G2F Attestation key generation failed\n"); -#endif - return EC_ERROR_HW_INTERNAL; - } - - /* Message signature. */ - SHA256_hw_init(&h_ctx); - SHA256_update(&h_ctx, data, data_size); - p256_from_bin(SHA256_final(&h_ctx)->b8, &h); - - /* Now, we processed input parameters, so clean-up output. */ - memset(sig, 0, sizeof(*sig)); - - /* Sign over the response w/ the attestation key. */ - hmac_drbg_init_rfc6979(&dr_ctx, &d, &h); - - result = (dcrypto_p256_ecdsa_sign(&dr_ctx, &d, &h, &r, &s) != 0) ? - EC_SUCCESS : - EC_ERROR_HW_INTERNAL; - p256_clear(&d); - - p256_to_bin(&r, sig->sig_r); - p256_to_bin(&s, sig->sig_s); - - return result; -} - -#ifndef CRYPTO_TEST_CMD_U2F_TEST -#define CRYPTO_TEST_CMD_U2F_TEST 0 -#endif - -#if defined(CRYPTO_TEST_SETUP) && CRYPTO_TEST_CMD_U2F_TEST - -static const char *expect_bool(enum ec_error_list value, - enum ec_error_list expect) -{ - if (value == expect) - return "PASSED"; - return "NOT PASSED"; -} - -static int cmd_u2f_test(int argc, char **argv) -{ - static struct u2f_state state; - static union u2f_key_handle_variant kh; - static const uint8_t origin[32] = { 0xff, 0xfe, 0xfd, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8 }; - static const uint8_t user[32] = { 0x88, 0x8e, 0x8d, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7 }; - static const uint8_t authTime[32] = { 0x99, 0x91, 2, 3, 4, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5 }; - static struct u2f_ec_point pubKey; - static struct u2f_signature sig; - - ccprintf("u2f_generate_hmac_key - %s\n", - expect_bool(u2f_generate_hmac_key(&state), EC_SUCCESS)); - - ccprintf("u2f_generate_g2f_secret - %s\n", - expect_bool(u2f_generate_g2f_secret(&state), EC_SUCCESS)); - - ccprintf("u2f_generate_drbg_entropy - %s\n", - expect_bool(u2f_generate_drbg_entropy(&state), EC_SUCCESS)); - - /* Version 0 key handle. */ - ccprintf("u2f_generate - %s\n", - expect_bool(u2f_generate(&state, user, origin, authTime, &kh, - 0, &pubKey), - EC_SUCCESS)); - ccprintf("kh: %ph\n", HEX_BUF(&kh, sizeof(kh.v0))); - ccprintf("pubKey: %ph\n", HEX_BUF(&pubKey, sizeof(pubKey))); - - ccprintf("u2f_authorize_keyhandle - %s\n", - expect_bool(u2f_authorize_keyhandle(&state, &kh, 0, user, - origin, authTime), - EC_SUCCESS)); - - kh.v0.origin_seed[0] ^= 0x10; - ccprintf("u2f_authorize_keyhandle - %s\n", - expect_bool(u2f_authorize_keyhandle(&state, &kh, 0, user, - origin, authTime), - EC_ERROR_ACCESS_DENIED)); - - kh.v0.origin_seed[0] ^= 0x10; - ccprintf("u2f_sign - %s\n", - expect_bool(u2f_sign(&state, &kh, 0, user, origin, authTime, - authTime, &sig), - EC_SUCCESS)); - ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig))); - - ccprintf("u2f_attest - %s\n", - expect_bool(u2f_attest(&state, &kh, 0, user, origin, authTime, - &pubKey, authTime, sizeof(authTime), - &sig), - EC_SUCCESS)); - ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig))); - - /* Should fail with incorrect key handle. */ - kh.v0.origin_seed[0] ^= 0x10; - ccprintf("u2f_sign - %s\n", - expect_bool(u2f_sign(&state, &kh, 0, user, origin, authTime, - authTime, &sig), - EC_ERROR_ACCESS_DENIED)); - ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig))); - - /* Version 1 key handle. */ - ccprintf("\nVersion 1 tests\n"); - ccprintf("u2f_generate - %s\n", - expect_bool(u2f_generate(&state, user, origin, authTime, &kh, - 1, &pubKey), - EC_SUCCESS)); - ccprintf("kh: %ph\n", HEX_BUF(&kh, sizeof(kh.v1))); - ccprintf("pubKey: %ph\n", HEX_BUF(&pubKey, sizeof(pubKey))); - - ccprintf("u2f_authorize_keyhandle - %s\n", - expect_bool(u2f_authorize_keyhandle(&state, &kh, 1, user, - origin, authTime), - EC_SUCCESS)); - - kh.v1.authorization_salt[0] ^= 0x10; - ccprintf("u2f_authorize_keyhandle - %s\n", - expect_bool(u2f_authorize_keyhandle(&state, &kh, 1, user, - origin, authTime), - EC_ERROR_ACCESS_DENIED)); - - kh.v1.authorization_salt[0] ^= 0x10; - ccprintf("u2f_sign - %s\n", - expect_bool(u2f_sign(&state, &kh, 1, user, origin, authTime, - authTime, &sig), - EC_SUCCESS)); - ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig))); - - ccprintf("u2f_attest - %s\n", - expect_bool(u2f_attest(&state, &kh, 1, user, origin, authTime, - &pubKey, authTime, sizeof(authTime), - &sig), - EC_SUCCESS)); - ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig))); - - /* Should fail with incorrect key handle. */ - kh.v1.origin_seed[0] ^= 0x10; - ccprintf("u2f_sign - %s\n", - expect_bool(u2f_sign(&state, &kh, 1, user, origin, authTime, - authTime, &sig), - EC_ERROR_ACCESS_DENIED)); - ccprintf("sig: %ph\n", HEX_BUF(&sig, sizeof(sig))); - - cflush(); - - return 0; -} - -DECLARE_SAFE_CONSOLE_COMMAND(u2f_test, cmd_u2f_test, NULL, - "Test U2F functionality"); - -#endif diff --git a/chip/host/build.mk b/chip/host/build.mk index fd7f75f3c0..6cdd9807d4 100644 --- a/chip/host/build.mk +++ b/chip/host/build.mk @@ -15,7 +15,7 @@ chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o endif ifeq ($(CONFIG_DCRYPTO),y) -CPPFLAGS += -I$(abspath ./board/cr50) +CPPFLAGS += -I$(abspath ./board/cr50/dcrypto) dirs-y += board/cr50/dcrypto LDFLAGS_EXTRA += -lcrypto endif diff --git a/include/u2f_impl.h b/include/u2f_impl.h deleted file mode 100644 index 7f0f10ef36..0000000000 --- a/include/u2f_impl.h +++ /dev/null @@ -1,229 +0,0 @@ -/* 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. - */ - -/* U2F implementation-specific callbacks and parameters. */ - -#ifndef __CROS_EC_U2F_IMPL_H -#define __CROS_EC_U2F_IMPL_H - -#include "common.h" -#include "dcrypto.h" -#include "u2f.h" - -/* ---- platform cryptography hooks ---- */ - -/* ---- 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; -}; - -/* 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); - -BUILD_ASSERT(sizeof(struct u2f_key_handle_v0) <= U2F_MAX_KH_SIZE); -BUILD_ASSERT(sizeof(struct u2f_key_handle_v0) == U2F_V0_KH_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); - - -BUILD_ASSERT(sizeof(union u2f_key_handle_variant) <= U2F_MAX_KH_SIZE); - -/** - * 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 - */ -enum ec_error_list u2f_generate_drbg_entropy(struct u2f_state *state); - -/** - * 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); - -/** - * 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 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 successful - */ -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); - -/** - * 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 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 key pair was created - * EC_ACCESS_DENIED if key handle can't authenticated - */ -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); - -/** - * 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). - * - * @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. - * - * @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 - * - * G2F_ATTESTATION_CERT_MAX_LEN bytes. - * @return size of certificate written to buf, 0 on error. - */ -size_t g2f_attestation_cert_serial(const struct u2f_state *state, - const uint8_t *serial, uint8_t *buf); - -/** - * 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 - */ -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); - - -/** - * - * Board U2F key management part implemented. - * - */ - -/** - * Get the current u2f state from the board. - * - * @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. - * - * @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. - */ -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 - * derive kek in future calls to u2f_gen_kek(). - * - * @param commit whether to commit nvram changes before returning. - * @return EC_SUCCESS if seed was successfully created - * (and persisted if requested). - */ -enum ec_error_list u2f_gen_kek_seed(int commit); - -/** - * Zeroize U2F keys. Can be used to switch to FIPS-compliant path by - * destroying old keys. - * - * @return true if state is properly initialized and will persist in flash. - */ -enum ec_error_list u2f_zeroize_keys(void); - -/** - * Update keys to a newer (FIPS-compliant) version if needed. Do nothing if - * keys are already updated. - * - * @return EC_SUCCESS or error code. - */ -enum ec_error_list u2f_update_keys(void); - -#endif /* __CROS_EC_U2F_IMPL_H */ diff --git a/test/build.mk b/test/build.mk index 8efc82f5f0..84e3b9f2f1 100644 --- a/test/build.mk +++ b/test/build.mk @@ -95,7 +95,7 @@ thermal-y=thermal.o timer_calib-y=timer_calib.o timer_dos-y=timer_dos.o u2f-y=u2f.o -u2f-y+=../board/cr50/u2f.o +u2f-y+=../board/cr50/dcrypto/u2f.o uptime-y=uptime.o utils-y=utils.o utils_str-y=utils_str.o -- cgit v1.2.1