From 1ad3e00b6c6e67676d5a1de9689c18bfca31aa1a Mon Sep 17 00:00:00 2001 From: Vadim Sukhomlinov Date: Tue, 12 Oct 2021 14:53:04 -0700 Subject: cr50: cleanup public API for FIPS module Move declarations of AES-GCM, AES-CMAC, ECIES, HKDF and few other functions from dcrypto.h into internal.h. Merge tpm2/hkdf.c into dcrypto/hkdf.c. It contains only function used for testing and HKDF itself is only used for CRYPTO_TEST=1. BUG=b:134594373 TEST=make buildall -j; make BOARD=cr50 CRYPTO_TEST=1 Signed-off-by: Vadim Sukhomlinov Change-Id: I56c03ff4e8838871cdb28c0d9946c39754d9e054 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3219576 Reviewed-by: Vadim Sukhomlinov Reviewed-by: Andrey Pronin Tested-by: Vadim Sukhomlinov Commit-Queue: Andrey Pronin --- board/cr50/build.mk | 1 - board/cr50/dcrypto/dcrypto.h | 128 ++++++------------------------------------ board/cr50/dcrypto/hkdf.c | 87 ++++++++++++++++++++++++++++ board/cr50/dcrypto/internal.h | 108 +++++++++++++++++++++++++++++++++++ board/cr50/tpm2/aes.c | 1 + board/cr50/tpm2/hkdf.c | 94 ------------------------------- 6 files changed, 213 insertions(+), 206 deletions(-) delete mode 100644 board/cr50/tpm2/hkdf.c diff --git a/board/cr50/build.mk b/board/cr50/build.mk index 6080e9c173..a7bde7a688 100644 --- a/board/cr50/build.mk +++ b/board/cr50/build.mk @@ -172,7 +172,6 @@ board-y += tpm2/ecc.o board-y += tpm2/endorsement.o board-y += tpm2/hash.o board-y += tpm2/hash_data.o -board-y += tpm2/hkdf.o board-y += tpm2/manufacture.o board-y += tpm2/nvmem_ops.o board-y += tpm2/platform.o diff --git a/board/cr50/dcrypto/dcrypto.h b/board/cr50/dcrypto/dcrypto.h index 3712f56fc4..cef877969a 100644 --- a/board/cr50/dcrypto/dcrypto.h +++ b/board/cr50/dcrypto/dcrypto.h @@ -315,7 +315,7 @@ enum dcrypto_result DCRYPTO_sw_hash_init( union hash_ctx *ctx, enum hashing_mode mode) __warn_unused_result; /** - * Initialize hardware-acceleated or software version of hash computation, + * Initialize hardware-accelerated or software version of hash computation, * preferring hardware version when available. * * @param ctx storage for context @@ -349,7 +349,7 @@ enum dcrypto_result DCRYPTO_sw_hmac_init(union hmac_ctx *ctx, const void *key, __warn_unused_result; /** - * Initialize hardware-acceleated or software version of HMAC computation, + * Initialize hardware-accelerated or software version of HMAC computation, * preferring hardware version when available. * * @param ctx storage for context @@ -404,7 +404,7 @@ __always_inline void HASH_update(union hash_ctx *const ctx, const void *data, } /** * Finalize hash computation by adding padding, message length. - * Returns pointer to computed digest stored inside provided context. + * Returns pointer to the computed digest stored inside the provided context. * * @param ctx digest context (can be one of union subtypes). * @@ -430,7 +430,7 @@ __always_inline void SHA256_update(struct sha256_ctx *const ctx, /** * Finalize hash computation by adding padding, message length. - * Returns pointer to computed digest stored inside provided context. + * Returns pointer to the computed digest stored inside the provided context. * * @param ctx SHA256 digest context. * @@ -632,68 +632,6 @@ enum dcrypto_result DCRYPTO_aes_ctr(uint8_t *out, const uint8_t *key, void DCRYPTO_aes_write_iv(const uint8_t *iv); void DCRYPTO_aes_read_iv(uint8_t *iv); -/* AES-GCM-128/192/256 - * NIST Special Publication 800-38D, IV is provided externally - * Caller should use IV length according to section 8.2 of SP 800-38D - * And choose appropriate IV construction method, constrain number - * of invocations according to section 8.3 of SP 800-38D - */ -struct GCM_CTX { - union { - uint32_t d[4]; - uint8_t c[16]; - } block, Ej0; - - uint64_t aad_len; - uint64_t count; - size_t remainder; -}; - -/* Initialize the GCM context structure. */ -void DCRYPTO_gcm_init(struct GCM_CTX *ctx, uint32_t key_bits, - const uint8_t *key, const uint8_t *iv, size_t iv_len); -/* Additional authentication data to include in the tag calculation. */ -void DCRYPTO_gcm_aad(struct GCM_CTX *ctx, const uint8_t *aad_data, size_t len); -/* Encrypt & decrypt return the number of bytes written to out - * (always an integral multiple of 16), or -1 on error. These functions - * may be called repeatedly with incremental data. - * - * NOTE: if in_len is not a integral multiple of 16, then out_len must - * be atleast in_len - (in_len % 16) + 16 bytes. - */ -int DCRYPTO_gcm_encrypt(struct GCM_CTX *ctx, uint8_t *out, size_t out_len, - const uint8_t *in, size_t in_len); -int DCRYPTO_gcm_decrypt(struct GCM_CTX *ctx, uint8_t *out, size_t out_len, - const uint8_t *in, size_t in_len); -/* Encrypt & decrypt a partial final block, if any. These functions - * return the number of bytes written to out (<= 15), or -1 on error. - */ -int DCRYPTO_gcm_encrypt_final(struct GCM_CTX *ctx, uint8_t *out, - size_t out_len); -int DCRYPTO_gcm_decrypt_final(struct GCM_CTX *ctx, uint8_t *out, - size_t out_len); -/* Compute the tag over AAD + encrypt or decrypt data, and return the - * number of bytes written to tag. Returns -1 on error. - */ -int DCRYPTO_gcm_tag(struct GCM_CTX *ctx, uint8_t *tag, size_t tag_len); -/* Cleanup secrets. */ -void DCRYPTO_gcm_finish(struct GCM_CTX *ctx); - -/* AES-CMAC-128 - * NIST Special Publication 800-38B, RFC 4493 - * K: 128-bit key, M: message, len: number of bytes in M - * Writes 128-bit tag to T; returns 0 if an error is encountered and 1 - * otherwise. - */ -enum dcrypto_result DCRYPTO_aes_cmac(const uint8_t *K, const uint8_t *M, - size_t len, uint32_t T[4]); -/* key: 128-bit key, M: message, len: number of bytes in M, - * T: tag to be verified - * Returns 1 if the tag is correct and 0 otherwise. - */ -enum dcrypto_result DCRYPTO_aes_cmac_verify(const uint8_t *key, - const uint8_t *M, size_t len, - const uint32_t T[4]); /* * BIGNUM utility methods. @@ -959,37 +897,6 @@ enum dcrypto_result DCRYPTO_p256_ecdsa_sign(const p256_int *key, /************************************************************/ -/* P256 based integration encryption (DH+AES128+SHA256). - * Not FIPS 140-2 compliant, not used other than for tests - * Authenticated data may be provided, where the first auth_data_len - * bytes of in will be authenticated but not encrypted. * - * Supports in-place encryption / decryption. * - * The output format is: - * 0x04 || PUBKEY || AUTH_DATA || AES128_CTR(PLAINTEXT) || - * HMAC_SHA256(AUTH_DATA || CIPHERTEXT) - */ -size_t DCRYPTO_ecies_encrypt(void *out, size_t out_len, const void *in, - size_t in_len, size_t auth_data_len, - const uint8_t *iv, const p256_int *pub_x, - const p256_int *pub_y, const uint8_t *salt, - size_t salt_len, const uint8_t *info, - size_t info_len); -size_t DCRYPTO_ecies_decrypt(void *out, size_t out_len, const void *in, - size_t in_len, size_t auth_data_len, - const uint8_t *iv, const p256_int *d, - const uint8_t *salt, size_t salt_len, - const uint8_t *info, size_t info_len); - -/* - * HKDF as per RFC 5869. Mentioned as conforming NIST SP 800-56C Rev.1 - * [RFC 5869] specifies a version of the above extraction-then-expansion - * key-derivation procedure using HMAC for both the extraction and expansion - * steps. - */ -int DCRYPTO_hkdf(uint8_t *OKM, size_t OKM_len, const uint8_t *salt, - size_t salt_len, const uint8_t *IKM, size_t IKM_len, - const uint8_t *info, size_t info_len); - /* * BN. */ @@ -998,9 +905,12 @@ int DCRYPTO_hkdf(uint8_t *OKM, size_t OKM_len, const uint8_t *salt, * Returns DCRYPTO_OK if test passed, DCRYPTO_FAIL otherwise */ enum dcrypto_result DCRYPTO_bn_generate_prime(struct LITE_BIGNUM *p); -void DCRYPTO_bn_wrap(struct LITE_BIGNUM *b, void *buf, size_t len); + +/* Compute c = a * b. */ void DCRYPTO_bn_mul(struct LITE_BIGNUM *c, const struct LITE_BIGNUM *a, const struct LITE_BIGNUM *b); + +/* Compute (quotient, remainder) = input / divisor. */ int DCRYPTO_bn_div(struct LITE_BIGNUM *quotient, struct LITE_BIGNUM *remainder, const struct LITE_BIGNUM *input, const struct LITE_BIGNUM *divisor); @@ -1015,7 +925,7 @@ size_t DCRYPTO_asn1_pubp(uint8_t *buf, const p256_int *x, const p256_int *y); * X509. */ /* DCRYPTO_x509_verify verifies that the provided X509 certificate was issued - * by the specified certifcate authority. + * by the specified certificate authority. * * cert is a pointer to a DER encoded X509 certificate, as specified * in https://tools.ietf.org/html/rfc5280#section-4.1. In ASN.1 @@ -1083,11 +993,19 @@ enum dcrypto_appid { /* This enum value should not exceed 7. */ }; +/* Retrieve Firmware Root Key from hardware key ladder. */ int DCRYPTO_ladder_compute_frk2(size_t major_fw_version, uint8_t *frk2); + +/* Revoke access to hardware key ladder. */ void DCRYPTO_ladder_revoke(void); +/* Preload application specific secret into key ladder register. */ int DCRYPTO_appkey_init(enum dcrypto_appid id); + +/* Clean-up secret loaded from key ladder. */ void DCRYPTO_appkey_finish(void); + +/* Compute application-specific, hardware-bound constant. */ int DCRYPTO_appkey_derive(enum dcrypto_appid appid, const uint32_t input[8], uint32_t output[8]); @@ -1138,24 +1056,12 @@ int DCRYPTO_ladder_is_enabled(void); **/ void fips_init_trng(void); -/** - * Returns random number from TRNG 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. - * Random is passed continuous TRNG health tests. - * - * @return uint64_t, low 32 bits - random high 32 bits - validity status - */ -uint64_t fips_trng_rand32(void); - /** * Return true if fips_trng_rand() result contains valid random from TRNG. * @param rand value from fips_trng_rand32() or read_rand() * * @return true if rand contains valid random */ - inline bool rand_valid(uint64_t rand) { return (rand >> 32) != 0; diff --git a/board/cr50/dcrypto/hkdf.c b/board/cr50/dcrypto/hkdf.c index 8e06455337..83c29c865f 100644 --- a/board/cr50/dcrypto/hkdf.c +++ b/board/cr50/dcrypto/hkdf.c @@ -78,3 +78,90 @@ int DCRYPTO_hkdf(uint8_t *OKM, size_t OKM_len, always_memset(PRK, 0, sizeof(PRK)); return result; } + +#ifdef CRYPTO_TEST_SETUP + +#include "extension.h" + +enum { + TEST_RFC = 0, +}; + +#define MAX_OKM_BYTES 1024 + +static void hkdf_command_handler(void *cmd_body, + size_t cmd_size, + size_t *response_size) +{ + uint8_t *cmd; + uint8_t *out; + uint8_t op; + size_t salt_len; + const uint8_t *salt; + size_t IKM_len; + const uint8_t *IKM; + size_t info_len; + const uint8_t *info; + size_t OKM_len; + uint8_t OKM[MAX_OKM_BYTES]; + + /* Command format. + * + * WIDTH FIELD + * 1 OP + * 1 MSB SALT LEN + * 1 LSB SALT LEN + * SALT_LEN SALT + * 1 MSB IKM LEN + * 1 LSB IKM LEN + * IKM_LEN IKM + * 1 MSB INFO LEN + * 1 LSB INFO LEN + * INFO_LEN INFO + * 1 MSB OKM LEN + * 1 LSB OKM LEN + */ + cmd = (uint8_t *) cmd_body; + out = (uint8_t *) cmd_body; + op = *cmd++; + + salt_len = ((uint16_t) (cmd[0] << 8)) | cmd[1]; + cmd += 2; + salt = cmd; + cmd += salt_len; + + IKM_len = ((uint16_t) (cmd[0] << 8)) | cmd[1]; + cmd += 2; + IKM = cmd; + cmd += IKM_len; + + info_len = ((uint16_t) (cmd[0] << 8)) | cmd[1]; + cmd += 2; + info = cmd; + cmd += info_len; + + OKM_len = ((uint16_t) (cmd[0] << 8)) | cmd[1]; + + if (OKM_len > MAX_OKM_BYTES) { + *response_size = 0; + return; + } + + switch (op) { + case TEST_RFC: + if (DCRYPTO_hkdf(OKM, OKM_len, salt, salt_len, + IKM, IKM_len, info, info_len)) { + memcpy(out, OKM, OKM_len); + *response_size = OKM_len; + } else { + *response_size = 0; + } + break; + default: + *response_size = 0; + } +} + +DECLARE_EXTENSION_COMMAND(EXTENSION_HKDF, hkdf_command_handler); + +#endif /* CRYPTO_TEST_SETUP */ diff --git a/board/cr50/dcrypto/internal.h b/board/cr50/dcrypto/internal.h index 1fb66f710c..706baeaa17 100644 --- a/board/cr50/dcrypto/internal.h +++ b/board/cr50/dcrypto/internal.h @@ -308,6 +308,19 @@ uint64_t read_rand(void); */ bool fips_trng_startup(int stage); + + +/** + * Returns random number from TRNG 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. + * Random is passed continuous TRNG health tests. + * + * @return uint64_t, low 32 bits - random high 32 bits - validity status + */ +uint64_t fips_trng_rand32(void); + /** * Check that Cr50-wide HMAC_DRBG seeded according NIST SP 800-90A * recomendation is properly initialized and can be used. @@ -494,6 +507,101 @@ void dcrypto_imem_load(size_t offset, const uint32_t *opcodes, */ uint32_t dcrypto_dmem_load(size_t offset, const void *words, size_t n_words); +/* P256 based integration encryption (DH+AES128+SHA256). + * Not FIPS 140-2 compliant, not used other than for tests + * Authenticated data may be provided, where the first auth_data_len + * bytes of in will be authenticated but not encrypted. * + * Supports in-place encryption / decryption. * + * The output format is: + * 0x04 || PUBKEY || AUTH_DATA || AES128_CTR(PLAINTEXT) || + * HMAC_SHA256(AUTH_DATA || CIPHERTEXT) + */ +size_t DCRYPTO_ecies_encrypt(void *out, size_t out_len, const void *in, + size_t in_len, size_t auth_data_len, + const uint8_t *iv, const p256_int *pub_x, + const p256_int *pub_y, const uint8_t *salt, + size_t salt_len, const uint8_t *info, + size_t info_len); +size_t DCRYPTO_ecies_decrypt(void *out, size_t out_len, const void *in, + size_t in_len, size_t auth_data_len, + const uint8_t *iv, const p256_int *d, + const uint8_t *salt, size_t salt_len, + const uint8_t *info, size_t info_len); + +/* + * HKDF as per RFC 5869. Mentioned as conforming NIST SP 800-56C Rev.1 + * [RFC 5869] specifies a version of the above extraction-then-expansion + * key-derivation procedure using HMAC for both the extraction and expansion + * steps. + */ +int DCRYPTO_hkdf(uint8_t *OKM, size_t OKM_len, const uint8_t *salt, + size_t salt_len, const uint8_t *IKM, size_t IKM_len, + const uint8_t *info, size_t info_len); + + +/* AES-GCM-128/192/256 + * NIST Special Publication 800-38D, IV is provided externally + * Caller should use IV length according to section 8.2 of SP 800-38D + * And choose appropriate IV construction method, constrain number + * of invocations according to section 8.3 of SP 800-38D + */ +struct GCM_CTX { + union { + uint32_t d[4]; + uint8_t c[16]; + } block, Ej0; + + uint64_t aad_len; + uint64_t count; + size_t remainder; +}; + +/* Initialize the GCM context structure. */ +void DCRYPTO_gcm_init(struct GCM_CTX *ctx, uint32_t key_bits, + const uint8_t *key, const uint8_t *iv, size_t iv_len); +/* Additional authentication data to include in the tag calculation. */ +void DCRYPTO_gcm_aad(struct GCM_CTX *ctx, const uint8_t *aad_data, size_t len); +/* Encrypt & decrypt return the number of bytes written to out + * (always an integral multiple of 16), or -1 on error. These functions + * may be called repeatedly with incremental data. + * + * NOTE: if in_len is not a integral multiple of 16, then out_len must + * be at least in_len - (in_len % 16) + 16 bytes. + */ +int DCRYPTO_gcm_encrypt(struct GCM_CTX *ctx, uint8_t *out, size_t out_len, + const uint8_t *in, size_t in_len); +int DCRYPTO_gcm_decrypt(struct GCM_CTX *ctx, uint8_t *out, size_t out_len, + const uint8_t *in, size_t in_len); +/* Encrypt & decrypt a partial final block, if any. These functions + * return the number of bytes written to out (<= 15), or -1 on error. + */ +int DCRYPTO_gcm_encrypt_final(struct GCM_CTX *ctx, uint8_t *out, + size_t out_len); +int DCRYPTO_gcm_decrypt_final(struct GCM_CTX *ctx, uint8_t *out, + size_t out_len); +/* Compute the tag over AAD + encrypt or decrypt data, and return the + * number of bytes written to tag. Returns -1 on error. + */ +int DCRYPTO_gcm_tag(struct GCM_CTX *ctx, uint8_t *tag, size_t tag_len); +/* Cleanup secrets. */ +void DCRYPTO_gcm_finish(struct GCM_CTX *ctx); + +/* AES-CMAC-128 + * NIST Special Publication 800-38B, RFC 4493 + * K: 128-bit key, M: message, len: number of bytes in M + * Writes 128-bit tag to T; returns 0 if an error is encountered and 1 + * otherwise. + */ +enum dcrypto_result DCRYPTO_aes_cmac(const uint8_t *K, const uint8_t *M, + size_t len, uint32_t T[4]); +/* key: 128-bit key, M: message, len: number of bytes in M, + * T: tag to be verified + * Returns 1 if the tag is correct and 0 otherwise. + */ +enum dcrypto_result DCRYPTO_aes_cmac_verify(const uint8_t *key, + const uint8_t *M, size_t len, + const uint32_t T[4]); + #ifndef __alias #define __alias(func) __attribute__((alias(#func))) diff --git a/board/cr50/tpm2/aes.c b/board/cr50/tpm2/aes.c index f515d0364b..56e3cbbb3b 100644 --- a/board/cr50/tpm2/aes.c +++ b/board/cr50/tpm2/aes.c @@ -216,6 +216,7 @@ CRYPT_RESULT _cpri__AESEncryptOFB( #include "extension.h" #include "hooks.h" #include "uart.h" +#include "internal.h" enum aes_test_cipher_mode { TEST_MODE_ECB = 0, diff --git a/board/cr50/tpm2/hkdf.c b/board/cr50/tpm2/hkdf.c deleted file mode 100644 index d950d865ab..0000000000 --- a/board/cr50/tpm2/hkdf.c +++ /dev/null @@ -1,94 +0,0 @@ -/* Copyright 2016 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 "dcrypto.h" -#include "util.h" - -#ifdef CRYPTO_TEST_SETUP - -#include "extension.h" - -enum { - TEST_RFC = 0, -}; - -#define MAX_OKM_BYTES 1024 - -static void hkdf_command_handler(void *cmd_body, - size_t cmd_size, - size_t *response_size) -{ - uint8_t *cmd; - uint8_t *out; - uint8_t op; - size_t salt_len; - const uint8_t *salt; - size_t IKM_len; - const uint8_t *IKM; - size_t info_len; - const uint8_t *info; - size_t OKM_len; - uint8_t OKM[MAX_OKM_BYTES]; - - /* Command format. - * - * WIDTH FIELD - * 1 OP - * 1 MSB SALT LEN - * 1 LSB SALT LEN - * SALT_LEN SALT - * 1 MSB IKM LEN - * 1 LSB IKM LEN - * IKM_LEN IKM - * 1 MSB INFO LEN - * 1 LSB INFO LEN - * INFO_LEN INFO - * 1 MSB OKM LEN - * 1 LSB OKM LEN - */ - cmd = (uint8_t *) cmd_body; - out = (uint8_t *) cmd_body; - op = *cmd++; - - salt_len = ((uint16_t) (cmd[0] << 8)) | cmd[1]; - cmd += 2; - salt = cmd; - cmd += salt_len; - - IKM_len = ((uint16_t) (cmd[0] << 8)) | cmd[1]; - cmd += 2; - IKM = cmd; - cmd += IKM_len; - - info_len = ((uint16_t) (cmd[0] << 8)) | cmd[1]; - cmd += 2; - info = cmd; - cmd += info_len; - - OKM_len = ((uint16_t) (cmd[0] << 8)) | cmd[1]; - - if (OKM_len > MAX_OKM_BYTES) { - *response_size = 0; - return; - } - - switch (op) { - case TEST_RFC: - if (DCRYPTO_hkdf(OKM, OKM_len, salt, salt_len, - IKM, IKM_len, info, info_len)) { - memcpy(out, OKM, OKM_len); - *response_size = OKM_len; - } else { - *response_size = 0; - } - break; - default: - *response_size = 0; - } -} - -DECLARE_EXTENSION_COMMAND(EXTENSION_HKDF, hkdf_command_handler); - -#endif /* CRYPTO_TEST_SETUP */ -- cgit v1.2.1