summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/cr50/build.mk1
-rw-r--r--board/cr50/tpm2/endorsement.c475
-rw-r--r--chip/g/flash.c26
-rw-r--r--chip/g/flash_info.h1
-rw-r--r--common/tpm_registers.c1
-rw-r--r--include/tpm_manufacture.h1
6 files changed, 505 insertions, 0 deletions
diff --git a/board/cr50/build.mk b/board/cr50/build.mk
index f1b1b18998..8710d11e41 100644
--- a/board/cr50/build.mk
+++ b/board/cr50/build.mk
@@ -39,6 +39,7 @@ board-y += tpm2/NVMem.o
board-y += tpm2/aes.o
board-y += tpm2/ecc.o
board-y += tpm2/ecies.o
+board-y += tpm2/endorsement.o
board-y += tpm2/hash.o
board-y += tpm2/hash_data.o
board-y += tpm2/hkdf.o
diff --git a/board/cr50/tpm2/endorsement.c b/board/cr50/tpm2/endorsement.c
new file mode 100644
index 0000000000..317b151ae6
--- /dev/null
+++ b/board/cr50/tpm2/endorsement.c
@@ -0,0 +1,475 @@
+/* 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 "tpm_manufacture.h"
+#include "tpm_registers.h"
+
+#include "TPM_Types.h"
+#include "TpmBuildSwitches.h"
+#include "CryptoEngine.h"
+#include "CpriECC_fp.h"
+#include "CpriRSA_fp.h"
+#include "tpm_types.h"
+
+#include "Global.h"
+#include "Hierarchy_fp.h"
+#include "InternalRoutines.h"
+#include "Manufacture_fp.h"
+#include "NV_Write_fp.h"
+#include "NV_DefineSpace_fp.h"
+
+#include "console.h"
+#include "extension.h"
+#include "flash.h"
+#include "flash_config.h"
+#include "flash_info.h"
+#include "printf.h"
+#include "registers.h"
+
+#include "dcrypto.h"
+
+#include <cryptoc/sha256.h>
+
+#include <endian.h>
+#include <string.h>
+
+#define CPRINTF(format, args...) cprintf(CC_EXTENSION, format, ## args)
+
+#define EK_CERT_NV_START_INDEX 0x01C00000
+#define INFO1_EPS_SIZE PRIMARY_SEED_SIZE
+#define INFO1_EPS_OFFSET FLASH_INFO_MANUFACTURE_STATE_OFFSET
+#define AES256_BLOCK_CIPHER_KEY_SIZE 32
+
+#define RO_CERTS_START_ADDR 0x43800
+#define RO_CERTS_REGION_SIZE 0x0800
+
+enum cros_perso_component_type {
+ CROS_PERSO_COMPONENT_TYPE_EPS = 128,
+ CROS_PERSO_COMPONENT_TYPE_RSA_CERT = 129,
+ CROS_PERSO_COMPONENT_TYPE_P256_CERT = 130
+};
+
+struct cros_perso_response_component_info_v0 {
+ uint16_t component_size;
+ uint8_t component_type;
+ uint8_t reserved[5];
+} __packed; /* Size: 8B */
+
+/* key_id: key for which this is the certificate */
+/* cert_len: length of the following certificate */
+/* cert: the certificate bytes */
+struct cros_perso_certificate_response_v0 {
+ uint8_t key_id[4];
+ uint32_t cert_len;
+ uint8_t cert[0];
+} __packed; /* Size: 8B */
+
+/* Personalization response. */
+BUILD_ASSERT(sizeof(struct cros_perso_response_component_info_v0) == 8);
+BUILD_ASSERT(sizeof(struct cros_perso_certificate_response_v0) == 8);
+
+/* TODO(ngm): replace with real pub key. */
+static const uint32_t TEST_ENDORSEMENT_CA_RSA_N[64] = {
+ 0xfa3b34ed, 0x3c59ad05, 0x912d6623, 0x83302402,
+ 0xd43b6755, 0x5777021a, 0xaf37e9a1, 0x45c0e8ad,
+ 0x9728f946, 0x4391523d, 0xdf7a9164, 0x88f1a9ae,
+ 0x036c557e, 0x5d9df43e, 0x3e65de68, 0xe172008a,
+ 0x709dc81f, 0x27a75fe0, 0x3e77f89e, 0x4f400ecc,
+ 0x51a17dae, 0x2ff9c652, 0xd1d83cdb, 0x20d26349,
+ 0xbbad71dd, 0x30051b2b, 0x276b2459, 0x809bb8e1,
+ 0xb8737049, 0xdbe94466, 0x8287072b, 0x070ef311,
+ 0x6e2a26de, 0x29d69f11, 0x96463d95, 0xb4dc6950,
+ 0x097d4dfe, 0x1b4a88cc, 0xbd6b50c8, 0x9f7a5b34,
+ 0xda22c199, 0x9d1ac04b, 0x136af5e5, 0xb1a0e824,
+ 0x4a065b34, 0x1f67fb46, 0xa1f91ab1, 0x27bb769f,
+ 0xb704c992, 0xb669cbf4, 0x9299bb6c, 0xcb1b2208,
+ 0x2dc0d9db, 0xe1513e13, 0xc7f24923, 0xa74c6bcc,
+ 0xca1a9a69, 0x1b994244, 0x4f64b0d9, 0x78607fd6,
+ 0x486fb315, 0xa1098c31, 0x5dc50dd6, 0xcdc10874
+};
+
+static const struct RSA TEST_ENDORSEMENT_CA_RSA_PUB = {
+ .e = RSA_F4,
+ .N = {
+ .dmax = sizeof(TEST_ENDORSEMENT_CA_RSA_N) / sizeof(uint32_t),
+ .d = (struct access_helper *) TEST_ENDORSEMENT_CA_RSA_N,
+ },
+ .d = {
+ .dmax = 0,
+ .d = NULL,
+ },
+};
+
+static int validate_cert(
+ const struct cros_perso_response_component_info_v0 *cert_info,
+ const struct cros_perso_certificate_response_v0 *cert,
+ const uint8_t eps[PRIMARY_SEED_SIZE])
+{
+ if (cert_info->component_type != CROS_PERSO_COMPONENT_TYPE_RSA_CERT &&
+ cert_info->component_type !=
+ CROS_PERSO_COMPONENT_TYPE_P256_CERT)
+ return 0; /* Invalid component type. */
+
+ /* TODO(ngm): verify key_id against HIK/FRK0. */
+ if (cert->cert_len > MAX_NV_BUFFER_SIZE)
+ return 0;
+
+ /* Verify certificate signature. */
+ return DCRYPTO_x509_verify(cert->cert, cert->cert_len,
+ &TEST_ENDORSEMENT_CA_RSA_PUB);
+}
+
+static int store_cert(enum cros_perso_component_type component_type,
+ const struct cros_perso_certificate_response_v0 *cert)
+{
+ const uint32_t rsa_ek_nv_index = EK_CERT_NV_START_INDEX;
+ const uint32_t ecc_ek_nv_index = EK_CERT_NV_START_INDEX + 1;
+ uint32_t nv_index;
+ NV_DefineSpace_In define_space;
+ TPMA_NV space_attributes;
+ NV_Write_In in;
+
+ /* Clear up structures potentially uszed only partially. */
+ memset(&define_space, 0, sizeof(define_space));
+ memset(&space_attributes, 0, sizeof(space_attributes));
+ memset(&in, 0, sizeof(in));
+
+ /* Indicate that a system reset has occurred, and currently
+ * running with Platform auth.
+ */
+ HierarchyStartup(SU_RESET);
+
+ if (component_type == CROS_PERSO_COMPONENT_TYPE_RSA_CERT)
+ nv_index = rsa_ek_nv_index;
+ else /* P256 certificate. */
+ nv_index = ecc_ek_nv_index;
+
+ /* EK Credential attributes specified in the "TCG PC Client
+ * Platform, TPM Profile (PTP) Specification" document.
+ */
+ /* REQUIRED: Writeable under platform auth. */
+ space_attributes.TPMA_NV_PPWRITE = 1;
+ /* OPTIONAL: Write-once; space must be deleted to be re-written. */
+ space_attributes.TPMA_NV_WRITEDEFINE = 1;
+ /* REQUIRED: Space created with platform auth. */
+ space_attributes.TPMA_NV_PLATFORMCREATE = 1;
+ /* REQUIRED: Readable under empty password? */
+ space_attributes.TPMA_NV_AUTHREAD = 1;
+ /* REQUIRED: Disable dictionary attack protection. */
+ space_attributes.TPMA_NV_NO_DA = 1;
+
+ define_space.authHandle = TPM_RH_PLATFORM;
+ define_space.auth.t.size = 0;
+ define_space.publicInfo.t.size = sizeof(
+ define_space.publicInfo.t.nvPublic);
+ define_space.publicInfo.t.nvPublic.nvIndex = nv_index;
+ define_space.publicInfo.t.nvPublic.nameAlg = TPM_ALG_SHA256;
+ define_space.publicInfo.t.nvPublic.attributes = space_attributes;
+ define_space.publicInfo.t.nvPublic.authPolicy.t.size = 0;
+ define_space.publicInfo.t.nvPublic.dataSize = cert->cert_len;
+
+ /* Define the required space first. */
+ if (TPM2_NV_DefineSpace(&define_space) != TPM_RC_SUCCESS)
+ return 0;
+
+ /* TODO(ngm): call TPM2_NV_WriteLock(nvIndex) on tpm_init();
+ * this prevents delete?
+ */
+
+ in.nvIndex = nv_index;
+ in.authHandle = TPM_RH_PLATFORM;
+ in.data.t.size = cert->cert_len;
+ memcpy(in.data.t.buffer, cert->cert, cert->cert_len);
+ in.offset = 0;
+
+ if (TPM2_NV_Write(&in) != TPM_RC_SUCCESS)
+ return 0;
+ if (NvCommit())
+ return 1;
+ return 0;
+}
+
+static uint32_t hw_key_ladder_step(uint32_t cert)
+{
+ uint32_t itop;
+
+ GREG32(KEYMGR, SHA_ITOP) = 0; /* clear status */
+
+ GREG32(KEYMGR, SHA_USE_CERT_INDEX) =
+ (cert << GC_KEYMGR_SHA_USE_CERT_INDEX_LSB) |
+ GC_KEYMGR_SHA_USE_CERT_ENABLE_MASK;
+
+ GREG32(KEYMGR, SHA_CFG_EN) =
+ GC_KEYMGR_SHA_CFG_EN_INT_EN_DONE_MASK;
+ GREG32(KEYMGR, SHA_TRIG) =
+ GC_KEYMGR_SHA_TRIG_TRIG_GO_MASK;
+
+ do {
+ itop = GREG32(KEYMGR, SHA_ITOP);
+ } while (!itop);
+
+ GREG32(KEYMGR, SHA_ITOP) = 0; /* clear status */
+
+ return !!GREG32(KEYMGR, HKEY_ERR_FLAGS);
+}
+
+
+#define KEYMGR_CERT_0 0
+#define KEYMGR_CERT_3 3
+#define KEYMGR_CERT_4 4
+#define KEYMGR_CERT_5 5
+#define KEYMGR_CERT_7 7
+#define KEYMGR_CERT_15 15
+#define KEYMGR_CERT_20 20
+#define KEYMGR_CERT_25 25
+#define KEYMGR_CERT_26 26
+
+#define K_CROS_FW_MAJOR_VERSION 0
+static const uint8_t k_cr50_max_fw_major_version = 254;
+
+static int compute_frk2(uint8_t frk2[AES256_BLOCK_CIPHER_KEY_SIZE])
+{
+ int i;
+
+ /* TODO(ngm): reading ITOP in hw_key_ladder_step hangs on
+ * second run of this function (i.e. install of ECC cert,
+ * which re-generates FRK2) unless the SHA engine is reset.
+ */
+ GREG32(KEYMGR, SHA_TRIG) =
+ GC_KEYMGR_SHA_TRIG_TRIG_RESET_MASK;
+
+ if (hw_key_ladder_step(KEYMGR_CERT_0))
+ return 0;
+
+ /* Derive HC_PHIK --> Deposited into ISR0 */
+ if (hw_key_ladder_step(KEYMGR_CERT_3))
+ return 0;
+
+ /* Cryptographically mix OBS-FBS --> Deposited into ISR1 */
+ if (hw_key_ladder_step(KEYMGR_CERT_4))
+ return 0;
+
+ /* Derive HIK_RT --> Deposited into ISR0 */
+ if (hw_key_ladder_step(KEYMGR_CERT_5))
+ return 0;
+
+ /* Derive BL_HIK --> Deposited into ISR0 */
+ if (hw_key_ladder_step(KEYMGR_CERT_7))
+ return 0;
+
+ /* Generate FRK2 by executing certs 15, 20, 25, and 26 */
+ if (hw_key_ladder_step(KEYMGR_CERT_15))
+ return 0;
+
+ if (hw_key_ladder_step(KEYMGR_CERT_20))
+ return 0;
+
+ for (i = 0; i < k_cr50_max_fw_major_version -
+ K_CROS_FW_MAJOR_VERSION; i++) {
+ if (hw_key_ladder_step(KEYMGR_CERT_25))
+ return 0;
+ }
+ if (hw_key_ladder_step(KEYMGR_CERT_26))
+ return 0;
+ memcpy(frk2, (void *) GREG32_ADDR(KEYMGR, HKEY_FRR0),
+ AES256_BLOCK_CIPHER_KEY_SIZE);
+ return 1;
+}
+
+static void flash_info_read_enable(void)
+{
+ /* Enable R access to INFO. */
+ GREG32(GLOBALSEC, FLASH_REGION7_BASE_ADDR) = FLASH_INFO_MEMORY_BASE +
+ FLASH_INFO_MANUFACTURE_STATE_OFFSET;
+ GREG32(GLOBALSEC, FLASH_REGION7_SIZE) =
+ FLASH_INFO_MANUFACTURE_STATE_SIZE - 1;
+ GREG32(GLOBALSEC, FLASH_REGION7_CTRL) =
+ GC_GLOBALSEC_FLASH_REGION7_CTRL_EN_MASK |
+ GC_GLOBALSEC_FLASH_REGION7_CTRL_RD_EN_MASK;
+}
+
+static void flash_info_read_disable(void)
+{
+ GREG32(GLOBALSEC, FLASH_REGION7_CTRL) = 0;
+}
+
+static void flash_cert_region_enable(void)
+{
+ /* Enable R access to CERT block. */
+ GREG32(GLOBALSEC, FLASH_REGION6_BASE_ADDR) = RO_CERTS_START_ADDR;
+ GREG32(GLOBALSEC, FLASH_REGION6_SIZE) =
+ RO_CERTS_REGION_SIZE - 1;
+ GREG32(GLOBALSEC, FLASH_REGION6_CTRL) =
+ GC_GLOBALSEC_FLASH_REGION6_CTRL_EN_MASK |
+ GC_GLOBALSEC_FLASH_REGION6_CTRL_RD_EN_MASK;
+}
+
+/* EPS is stored XOR'd with FRK2, so make sure that the sizes match. */
+BUILD_ASSERT(AES256_BLOCK_CIPHER_KEY_SIZE == PRIMARY_SEED_SIZE);
+static int get_decrypted_eps(uint8_t eps[PRIMARY_SEED_SIZE])
+{
+ int i;
+ uint8_t frk2[AES256_BLOCK_CIPHER_KEY_SIZE];
+
+ CPRINTF("%s: getting eps\n", __func__);
+ if (!compute_frk2(frk2))
+ return 0;
+
+ /* Setup flash region mapping. */
+ flash_info_read_enable();
+
+ for (i = 0; i < INFO1_EPS_SIZE; i += sizeof(uint32_t)) {
+ uint32_t word;
+
+ if (flash_physical_info_read_word(
+ INFO1_EPS_OFFSET + i, &word) != EC_SUCCESS) {
+ memset(frk2, 0, sizeof(frk2));
+ return 0; /* Flash read INFO1 failed. */
+ }
+ memcpy(eps + i, &word, sizeof(word));
+ }
+
+ /* Remove flash region mapping. */
+ flash_info_read_disable();
+
+ /* One-time-pad decrypt EPS. */
+ for (i = 0; i < PRIMARY_SEED_SIZE; i++)
+ eps[i] ^= frk2[i];
+
+ memset(frk2, 0, sizeof(frk2));
+ return 1;
+}
+
+static int store_eps(const uint8_t eps[PRIMARY_SEED_SIZE])
+{
+ /* gp is a TPM global state structure, declared in Global.h. */
+ memcpy(gp.EPSeed.t.buffer, eps, PRIMARY_SEED_SIZE);
+
+ /* Persist the seed to flash. */
+ NvWriteReserved(NV_EP_SEED, &gp.EPSeed);
+ return NvCommit();
+}
+
+static void endorsement_complete(void)
+{
+ CPRINTF("%s(): SUCCESS\n", __func__);
+}
+
+static int handle_cert(
+ const struct cros_perso_response_component_info_v0 *cert_info,
+ const struct cros_perso_certificate_response_v0 *cert,
+ const uint8_t *eps)
+{
+
+ /* Write RSA / P256 endorsement certificate. */
+ if (!validate_cert(cert_info, cert, eps))
+ return 0;
+
+ /* TODO(ngm): verify that storage succeeded. */
+ if (!store_cert(cert_info->component_type, cert)) {
+ CPRINTF("%s(): cert storage failed, type: %d\n", __func__,
+ cert_info->component_type);
+ return 0; /* Internal failure. */
+ }
+
+ return 1;
+}
+
+int tpm_endorse(void)
+{
+ struct ro_cert_response {
+ uint8_t key_id[4];
+ uint32_t cert_len;
+ uint8_t cert[0];
+ } __packed;
+
+ struct ro_cert {
+ const struct cros_perso_response_component_info_v0 cert_info;
+ const struct ro_cert_response cert_response;
+ } __packed;
+
+ /* 2-kB RO cert region is setup like so:
+ *
+ * | struct ro_cert | rsa_cert | struct ro_cert | ecc_cert |
+ */
+ const uint8_t *p = (const uint8_t *) RO_CERTS_START_ADDR;
+ const uint32_t *c = (const uint32_t *) RO_CERTS_START_ADDR;
+ const struct ro_cert *rsa_cert;
+ const struct ro_cert *ecc_cert;
+ int result = 0;
+ uint8_t eps[PRIMARY_SEED_SIZE];
+
+ flash_cert_region_enable();
+
+ /* First boot, certs not yet installed. */
+ if (*c == 0xFFFFFFFF)
+ return 0;
+
+ if (!get_decrypted_eps(eps)) {
+ CPRINTF("%s(): failed to read eps\n", __func__);
+ return 0;
+ }
+
+ /* Unpack rsa cert struct. */
+ rsa_cert = (const struct ro_cert *) p;
+ /* Sanity check cert region contents. */
+ if ((2 * sizeof(struct ro_cert)) +
+ rsa_cert->cert_response.cert_len > RO_CERTS_REGION_SIZE)
+ return 0;
+
+ /* Unpack ecc cert struct. */
+ ecc_cert = (const struct ro_cert *) (p + sizeof(struct ro_cert) +
+ rsa_cert->cert_response.cert_len);
+ /* Sanity check cert region contents. */
+ if ((2 * sizeof(struct ro_cert)) +
+ rsa_cert->cert_response.cert_len +
+ ecc_cert->cert_response.cert_len > RO_CERTS_REGION_SIZE)
+ return 0;
+
+ /* Verify expected component types. */
+ if (rsa_cert->cert_info.component_type !=
+ CROS_PERSO_COMPONENT_TYPE_RSA_CERT) {
+ return 0;
+ }
+ if (ecc_cert->cert_info.component_type !=
+ CROS_PERSO_COMPONENT_TYPE_P256_CERT) {
+ return 0;
+ }
+
+ do {
+ if (!handle_cert(
+ &rsa_cert->cert_info,
+ (struct cros_perso_certificate_response_v0 *)
+ &rsa_cert->cert_response, eps)) {
+ CPRINTF("%s: Failed to process RSA cert\n", __func__);
+ break;
+ }
+ CPRINTF("%s: RSA cert install success\n", __func__);
+
+ if (!handle_cert(
+ &ecc_cert->cert_info,
+ (struct cros_perso_certificate_response_v0 *)
+ &ecc_cert->cert_response, eps)) {
+ CPRINTF("%s: Failed to process ECC cert\n", __func__);
+ break;
+ }
+ CPRINTF("%s: ECC cert intsall success\n", __func__);
+
+ /* Copy EPS from INFO1 to flash data region. */
+ if (!store_eps(eps)) {
+ CPRINTF("%s(): eps storage failed\n", __func__);
+ break;
+ }
+
+ /* Mark as endorsed. */
+ endorsement_complete();
+
+ /* Chip has been marked as manufactured. */
+ result = 1;
+ } while (0);
+
+ memset(eps, 0, sizeof(eps));
+ return result;
+}
diff --git a/chip/g/flash.c b/chip/g/flash.c
index d77ad0ad35..6a329756a7 100644
--- a/chip/g/flash.c
+++ b/chip/g/flash.c
@@ -119,6 +119,7 @@ int flash_physical_protect_now(int all)
enum flash_op {
OP_ERASE_BLOCK,
OP_WRITE_BLOCK,
+ OP_READ_BLOCK,
};
static int do_flash_op(enum flash_op op, int is_info_bank,
@@ -180,6 +181,16 @@ static int do_flash_op(enum flash_op op, int is_info_bank,
/* This number is based on the TSMC spec Nmp=Tprog/Tsmp */
max_attempts = 9;
break;
+ case OP_READ_BLOCK:
+ if (!is_info_bank)
+ /* This code path only supports reading from
+ * the INFO bank.
+ */
+ return EC_ERROR_INVAL;
+ opcode = 0x16021765;
+ words = 1;
+ max_attempts = 9;
+ break;
}
/*
@@ -324,6 +335,21 @@ int flash_physical_write(int byte_offset, int num_bytes, const char *data)
return flash_physical_write_internal(byte_offset, 0, num_bytes, data);
}
+int flash_physical_info_read_word(int byte_offset, uint32_t *dst)
+{
+ int ret;
+
+ if (byte_offset % CONFIG_FLASH_WRITE_SIZE)
+ return EC_ERROR_INVAL;
+
+ ret = do_flash_op(OP_READ_BLOCK, 1, byte_offset, 1);
+ if (ret != EC_SUCCESS)
+ return ret;
+
+ *dst = GREG32(FLASH, FSH_DOUT_VAL1);
+ return EC_SUCCESS;
+}
+
void flash_info_write_enable(void)
{
/* Enable R/W access to INFO. */
diff --git a/chip/g/flash_info.h b/chip/g/flash_info.h
index 443b64e031..fae622c24b 100644
--- a/chip/g/flash_info.h
+++ b/chip/g/flash_info.h
@@ -9,5 +9,6 @@
void flash_info_write_enable(void);
void flash_info_write_disable(void);
int flash_info_physical_write(int byte_offset, int num_bytes, const char *data);
+int flash_physical_info_read_word(int byte_offset, uint32_t *dst);
#endif /* ! __EC_CHIP_G_FLASH_INFO_H */
diff --git a/common/tpm_registers.c b/common/tpm_registers.c
index 70e57c8d76..29bcf791fa 100644
--- a/common/tpm_registers.c
+++ b/common/tpm_registers.c
@@ -476,6 +476,7 @@ static void tpm_init(void)
*/
TPM_Manufacture(1);
_TPM_Init();
+ tpm_endorse();
}
_plat__SetNvAvail();
diff --git a/include/tpm_manufacture.h b/include/tpm_manufacture.h
index 57eba367f6..f43fd9ec13 100644
--- a/include/tpm_manufacture.h
+++ b/include/tpm_manufacture.h
@@ -13,5 +13,6 @@
/* Returns non-zero if the TPM manufacture steps have been completed. */
int tpm_manufactured(void);
+int tpm_endorse(void);
#endif /* __CROS_EC_TPM_MANUFACTURE_H */