From 1a59fa26fea42c3999a0bea89dbbd519db0d3ed4 Mon Sep 17 00:00:00 2001 From: Huang Yang Date: Tue, 26 Feb 2019 15:10:23 +0800 Subject: [PATCH] DM: Attestation Keybox support in SOS DM Retrieve the encrypted attestation Keybox from CSE and provision it to RPMB storage. Tracked-On: #2625 Signed-off-by: Huang Yang Signed-off-by: Wei Xinghai Signed-off-by: Chen Gang Acked-by: Zhu Bing --- devicemodel/Makefile | 1 + devicemodel/hw/pci/virtio/virtio_rpmb.c | 240 ++++++++++++++++++++-- devicemodel/hw/platform/rpmb/att_keybox.c | 197 ++++++++++++++++++ devicemodel/include/att_keybox.h | 82 ++++++++ 4 files changed, 499 insertions(+), 21 deletions(-) create mode 100644 devicemodel/hw/platform/rpmb/att_keybox.c create mode 100644 devicemodel/include/att_keybox.h diff --git a/devicemodel/Makefile b/devicemodel/Makefile index 2e09263ca..9a15bbe49 100644 --- a/devicemodel/Makefile +++ b/devicemodel/Makefile @@ -83,6 +83,7 @@ SRCS += hw/platform/acpi/acpi.c SRCS += hw/platform/acpi/acpi_pm.c SRCS += hw/platform/rpmb/rpmb_sim.c SRCS += hw/platform/rpmb/rpmb_backend.c +SRCS += hw/platform/rpmb/att_keybox.c SRCS += hw/platform/debugexit.c SRCS += hw/pci/wdt_i6300esb.c SRCS += hw/pci/lpc.c diff --git a/devicemodel/hw/pci/virtio/virtio_rpmb.c b/devicemodel/hw/pci/virtio/virtio_rpmb.c index 34a7dca67..7c3ed6c90 100644 --- a/devicemodel/hw/pci/virtio/virtio_rpmb.c +++ b/devicemodel/hw/pci/virtio/virtio_rpmb.c @@ -42,7 +42,7 @@ #include #include #include - +#include #include "dm.h" #include "pci_core.h" #include "virtio.h" @@ -52,10 +52,15 @@ #include "rpmb.h" #include "rpmb_sim.h" #include "rpmb_backend.h" +#include "att_keybox.h" -#define VIRTIO_RPMB_RINGSZ 64 -#define VIRTIO_RPMB_MAXSEGS 5 -#define ADDR_NOT_PRESENT -2 +#define VIRTIO_RPMB_RINGSZ 64 +#define VIRTIO_RPMB_MAXSEGS 5 +#define ERROR_ADDRESS_OUT_OF_RANGE -2 +#define BLOCK_BARA_BASE_ADDRESS 1 +#define BLOCK_BARA_SIGNATURE "BARA" +#define SIGNATURE_LENGTH (sizeof(BLOCK_BARA_SIGNATURE) - 1) +#define ATTKB_PRESENT_FLAG_BIT 0x1 static const char PHYSICAL_RPMB_STR[] = "physical_rpmb"; static int virtio_rpmb_debug = 1; @@ -130,14 +135,14 @@ rpmb_check_response(const char *cmd_str, enum rpmb_response response_type, for (i = 0; i < frame_cnt; i++) { if (swap16(frames[i].req_resp) != response_type) { DPRINTF(("%s: Bad response type, 0x%x, expected 0x%x\n", - cmd_str, swap16(frames[i].req_resp), response_type)); + cmd_str, swap16(frames[i].req_resp), response_type)); return -1; } if (swap16(frames[i].result) != RPMB_RES_OK) { if (swap16(frames[i].result) == RPMB_RES_ADDR_FAILURE) { DPRINTF(("%s: Addr failure, %u\n", cmd_str, swap16(frames[i].addr))); - return ADDR_NOT_PRESENT; + return ERROR_ADDRESS_OUT_OF_RANGE; } DPRINTF(("%s: Bad result, 0x%x\n", cmd_str, swap16(frames[i].result))); return -1; @@ -198,19 +203,14 @@ rpmb_get_counter(__u8 mode, __u8 *key, __u32 *counter, __u16 *result) iseq.h.num_of_cmds = 2; if (mode == RPMB_PHY_MODE) { - /* open rpmb device.*/ fd = open(RPMB_PHY_PATH_NAME, O_RDWR | O_NONBLOCK); if (fd < 0) { DPRINTF(("failed to open %s.\n", RPMB_PHY_PATH_NAME)); return fd; } - /* send ioctl cmd.*/ rc = ioctl(fd, RPMB_IOC_SEQ_CMD, &iseq); - - /* close rpmb device.*/ close(fd); - if (rc) { DPRINTF(("get counter for physical rpmb failed.\n")); return rc; @@ -229,10 +229,15 @@ rpmb_get_counter(__u8 mode, __u8 *key, __u32 *counter, __u16 *result) return -1; } - rc = rpmb_check_mac(key, &frame_out, 1); - if (rc) { - DPRINTF(("rpmb counter check mac failed.\n")); - return rc; + /*In PHY RPMB MODE, DM doesn't have real RPMB key, + *so no necessary to check the mac in the response. + */ + if (mode != RPMB_PHY_MODE) { + rc = rpmb_check_mac(key, &frame_out, 1); + if (rc) { + DPRINTF(("rpmb counter check mac failed.\n")); + return rc; + } } *counter = swap32(frame_out.write_counter); @@ -241,6 +246,90 @@ rpmb_get_counter(__u8 mode, __u8 *key, __u32 *counter, __u16 *result) return rc; } +static int +rpmb_write_block(__u8 mode, __u8 *key, __u16 addr, void *buf, __u32 count) +{ + int rc; + int fd; + __u32 i; + __u32 write_counter; + __u16 result; + struct { + struct rpmb_ioc_seq_cmd h; + struct rpmb_ioc_cmd cmd[3]; + } iseq = {}; + struct rpmb_frame frame_write; + struct rpmb_frame frame_rel[count]; + struct rpmb_frame frame_read; + + if (!buf || count == 0) { + DPRINTF(("%s:buf or count is invalid!\n", __func__)); + return -ENOBUFS; + } + + rc = rpmb_get_counter(mode, key, &write_counter, &result); + if (rc) { + DPRINTF(("%s: virtio_rpmb_get_counter failed\n", __func__)); + return rc; + } + + frame_write.addr = swap16(addr); + frame_write.req_resp = swap16(RPMB_REQ_RESULT_READ); + frame_write.write_counter = swap32(write_counter); + + for (i = 0; i < count; i++) { + memset(&frame_rel[i], 0, sizeof(frame_rel[i])); + memcpy(frame_rel[i].data, buf + i * sizeof(frame_rel[i].data), sizeof(frame_rel[i].data)); + frame_rel[i].write_counter = swap32(write_counter); + frame_rel[i].addr = swap16(addr); + frame_rel[i].block_count = swap16(count); + frame_rel[i].req_resp = swap16(RPMB_REQ_DATA_WRITE); + } + + iseq.cmd[0].flags = RPMB_F_WRITE | RPMB_F_REL_WRITE; + iseq.cmd[0].nframes = count; + iseq.cmd[0].frames_ptr = (__aligned_u64)(intptr_t)(frame_rel); + iseq.cmd[1].flags = RPMB_F_WRITE; + iseq.cmd[1].nframes = 1; + iseq.cmd[1].frames_ptr = (__aligned_u64)(intptr_t)(&frame_write); + iseq.cmd[2].flags = 0; + iseq.cmd[2].nframes = 1; + iseq.cmd[2].frames_ptr = (__aligned_u64)(intptr_t)(&frame_read); + iseq.h.num_of_cmds = 3; + + if (mode == RPMB_PHY_MODE) { + fd = open(RPMB_PHY_PATH_NAME, O_RDWR | O_NONBLOCK); + if (fd < 0) { + DPRINTF(("failed to open %s for read blocks.\n", RPMB_PHY_PATH_NAME)); + return fd; + } + + rc = ioctl(fd, RPMB_IOC_SEQ_CMD, &iseq); + close(fd); + if (rc) { + DPRINTF(("read blocks for physical rpmb failed.\n")); + return rc; + } + + /*In PHY RPMB MODE, DM doesn't have real RPMB key, + *so no necessary to check the mac in the response. + */ + rc = rpmb_check_response("write blocks", RPMB_RESP_DATA_WRITE, + &frame_read, 1, NULL, NULL, &addr); + } else { + rc = rpmb_sim_send(&iseq); + if (rc) { + DPRINTF(("read blocks for simulated rpmb failed.\n")); + return rc; + } + + rc = rpmb_check_response("write blocks", RPMB_RESP_DATA_WRITE, + &frame_read, 1, key, NULL, &addr); + } + + return rc; +} + static int rpmb_read_block(__u8 mode, __u8 *key, __u16 addr, void *buf, __u32 count) { @@ -272,19 +361,14 @@ rpmb_read_block(__u8 mode, __u8 *key, __u16 addr, void *buf, __u32 count) iseq.h.num_of_cmds = 2; if (mode == RPMB_PHY_MODE) { - /* open rpmb device.*/ fd = open(RPMB_PHY_PATH_NAME, O_RDWR | O_NONBLOCK); if (fd < 0) { DPRINTF(("failed to open %s for read blocks.\n", RPMB_PHY_PATH_NAME)); return fd; } - /* send ioctl cmd.*/ rc = ioctl(fd, RPMB_IOC_SEQ_CMD, &iseq); - - /* close rpmb device.*/ close(fd); - if (rc) { DPRINTF(("read blocks for physical rpmb failed.\n")); return rc; @@ -335,7 +419,7 @@ rpmb_search_size(__u8 mode, __u8 *key, __u16 hint) case 0: low = curr + 1; break; - case ADDR_NOT_PRESENT: + case ERROR_ADDRESS_OUT_OF_RANGE: high = curr - 1; break; default: @@ -358,6 +442,116 @@ rpmb_get_blocks(void) return rpmb_block_count; } +static int +rpmb_read_bara(__u8 mode, __u8 *key, rpmb_block_t *block_table) +{ + int ret; + + ret = rpmb_read_block(mode, key, BLOCK_BARA_BASE_ADDRESS, block_table, 1); + if (ret) { + DPRINTF(("rpmb read block table fail!\n")); + } + + return ret; +} + +static void +rpmb_bara_init(rpmb_block_t *block_table, uint16_t size) +{ + memcpy(block_table->signature, BLOCK_BARA_SIGNATURE, SIGNATURE_LENGTH); + block_table->length = RPMB_BLOCK_SIZE; + block_table->revision = 0; + block_table->flag |= ATTKB_PRESENT_FLAG_BIT; + block_table->attkb_addr = BLOCK_BARA_BASE_ADDRESS + 1; + block_table->attkb_size = size; + block_table->attkb_svn = 0; +} + +/* Read RPMB BARA block from RPMB to check if AttKB exists or not. + * If does NOT exist, DM will send cmd to CSE via HECI to get AttKB size + * and AttKB. Both AttKB and BARA block will be written to specified RPMB address. + */ +static int +rpmb_keybox_retrieve(__u8 mode, __u8 *key) +{ + int ret; + uint32_t i; + uint32_t block_num; + rpmb_block_t *block_table; + uint8_t *attkb = NULL; + uint16_t kb_size; + uint32_t kb_buf_size = 0; + + block_table = malloc(sizeof(rpmb_block_t)); + if (!block_table) { + DPRINTF(("%s: block table malloc fail!\n", __func__)); + return -ENOMEM; + } + + memset(block_table, 0, sizeof(rpmb_block_t)); + /* read block table */ + ret = rpmb_read_bara(mode, key, block_table); + if (ret) { + DPRINTF(("get block table fail!\n")); + goto out; + } + + if (memcmp(BLOCK_BARA_SIGNATURE, block_table->signature, SIGNATURE_LENGTH) || !(block_table->flag & ATTKB_PRESENT_FLAG_BIT)) { + kb_size = get_attkb_size(); + if (kb_size == 0) { + DPRINTF(("rpmb get_attkb_size fail!\n")); + ret = -1; + goto out; + } + + if (kb_size % RPMB_BLOCK_SIZE) { + kb_buf_size = (kb_size / RPMB_BLOCK_SIZE + 1) * RPMB_BLOCK_SIZE; + } else { + kb_buf_size = kb_size; + } + + attkb = (uint8_t *)malloc(kb_buf_size); + if (!attkb) { + DPRINTF(("%s: attkb malloc fail!\n", __func__)); + ret = -ENOMEM; + goto out; + } + + memset(attkb, 0, kb_buf_size); + ret = read_attkb(attkb, kb_size); + if (ret == 0) { + DPRINTF(("failed to read attkb")); + ret = -1; + goto out; + } + + rpmb_bara_init(block_table, kb_size); + block_num = (kb_size - 1) / RPMB_BLOCK_SIZE + 1; + for (i = 0; i < block_num; i++) { + ret = rpmb_write_block(mode, key, block_table->attkb_addr + i, attkb + i * RPMB_BLOCK_SIZE, 1); + if (ret) { + DPRINTF(("rpmb write key box fail!\n")); + goto out; + } + } + + ret = rpmb_write_block(mode, key, BLOCK_BARA_BASE_ADDRESS, block_table, 1); + if (ret) { + DPRINTF(("rpmb write block table fail!\n")); + goto out; + } + } + +out: + if (attkb) { + memset(attkb, 0, kb_buf_size); + free(attkb); + } + + free(block_table); + return ret; +} + static int virtio_rpmb_seq_handler(struct virtio_rpmb *rpmb, struct iovec *iov, int n, int *tlen) @@ -545,6 +739,10 @@ virtio_rpmb_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) DPRINTF(("RPMB in physical mode!\n")); rpmb_mode_init(RPMB_PHY_MODE); rpmb_block_count = rpmb_search_size(RPMB_PHY_MODE, key, 0); + rc = rpmb_keybox_retrieve(RPMB_PHY_MODE, key); + if (rc < 0) { + DPRINTF(("rpmb_keybox_retrieve failed!\n")); + } } else { DPRINTF(("RPMB in simulated mode!\n")); rc = rpmb_sim_key_init(key); diff --git a/devicemodel/hw/platform/rpmb/att_keybox.c b/devicemodel/hw/platform/rpmb/att_keybox.c new file mode 100644 index 000000000..bbbb69084 --- /dev/null +++ b/devicemodel/hw/platform/rpmb/att_keybox.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2019 Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer in + * this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "att_keybox.h" + +static const uuid_le HECI_CLIENT_GUID = + UUID_LE(0x8e6a6715, 0x9abc, 0x4043, + 0x88, 0xef, 0x9e, 0x39, 0xc6, 0xf6, 0x3e, 0x0f); + +#define MEI_DEV "/dev/mei0" +#define HECI_READ_ATTKB_GRP_ID 0x0a +#define HECI_READ_ATTKB_EX_CMD_REQ 0x1a +#define HECI_MTU 2048 + +uint16_t get_attkb_size(void) +{ + struct mei_connect_client_data mdcd; + int fd = -1, ret = 0; + HECI_READ_ATTKB_EX_Request req; + HECI_READ_ATTKB_EX_Response resp; + + fd = open(MEI_DEV, O_RDWR); + if (fd == -1) { + fprintf(stderr, "Failed to open %s, %s\n", + MEI_DEV, strerror(errno)); + return 0; + } + + memset(&mdcd, 0, sizeof(mdcd)); + memcpy(&mdcd.in_client_uuid, &HECI_CLIENT_GUID, sizeof(HECI_CLIENT_GUID)); + + ret = ioctl(fd, IOCTL_MEI_CONNECT_CLIENT, &mdcd); + if (ret) { + fprintf(stderr, "HECI connection failed, %s\n", + strerror(errno)); + goto err; + } + + /* Get the size of encrypted attestation keybox */ + memset(&req, 0, sizeof(req)); + req.header.groupid = HECI_READ_ATTKB_GRP_ID; + req.header.command = HECI_READ_ATTKB_EX_CMD_REQ; + req.size = 0; + req.offset = 0; + req.flags.encryption = 1; + + ret = write(fd, &req, sizeof(req)); + if (ret != sizeof(req)) { + fprintf(stderr, "Failed to send HECI command, %s\n", + strerror(errno)); + goto err; + } + + ret = read(fd, &resp, sizeof(resp)); + if (ret != sizeof(resp)) { + fprintf(stderr, "ret = %d,Failed to read HECI command result, %d:%s\n", + ret, errno, strerror(errno)); + goto err; + } + + if ((resp.header.is_response != 1) || (resp.header.result != 0)) { + fprintf(stderr, "Failed to check resp header = 0x%x\n", + resp.header.result); + goto err; + } + + if (resp.total_file_size == 0) { + fprintf(stderr, "ret = %d, Unexpected filesize 0!\n", ret); + } + + close(fd); + + return resp.total_file_size; + +err: + close(fd); + return 0; +} + +uint16_t read_attkb(void *data, uint16_t size) +{ + struct mei_connect_client_data mdcd; + HECI_READ_ATTKB_EX_Request req; + HECI_READ_ATTKB_EX_Response resp; + int fd = -1, ret = 0; + uint16_t left_size = 0; + uint16_t bytes_read = 0; + void *ptr = data; + + if ((ptr == NULL) || (size == 0)) { + return 0; + } + + fd = open(MEI_DEV, O_RDWR); + if (fd == -1) { + fprintf(stderr, "Failed to open %s, %s\n", + MEI_DEV, strerror(errno)); + return 0; + } + + memset(&mdcd, 0, sizeof(mdcd)); + memcpy(&mdcd.in_client_uuid, &HECI_CLIENT_GUID, sizeof(HECI_CLIENT_GUID)); + + ret = ioctl(fd, IOCTL_MEI_CONNECT_CLIENT, &mdcd); + if (ret) { + fprintf(stderr, "HECI connection failed, %s\n", + strerror(errno)); + goto err; + } + + left_size = size; + + memset(&req, 0, sizeof(req)); + req.header.groupid = HECI_READ_ATTKB_GRP_ID; + req.header.command = HECI_READ_ATTKB_EX_CMD_REQ; + req.offset = 0; + req.size = HECI_MTU > left_size ? left_size : HECI_MTU; + req.flags.encryption = 1; + + while (left_size) { + req.offset = bytes_read; + req.size = HECI_MTU > left_size ? left_size : HECI_MTU; + + ret = write(fd, &req, sizeof(req)); + if (ret != sizeof(req)) { + fprintf(stderr, "Failed to send HECI command, %s\n", + strerror(errno)); + goto err; + } + + ret = read(fd, &resp, sizeof(resp)); + if (ret != sizeof(resp)) { + fprintf(stderr, "Failed to read HECI command result, %s\n", + strerror(errno)); + goto err; + } + + if ((resp.header.is_response != 1) || (resp.header.result != 0)) { + fprintf(stderr, "Failed to check resp header = 0x%x\n", + resp.header.result); + goto err; + } + + ret = read(fd, (uint8_t *)data + bytes_read, req.size); + if (ret != req.size) { + fprintf(stderr, "Failed to read attkb, %s\n", + strerror(errno)); + goto err; + } + + ptr += ret; + bytes_read += ret; + left_size -= ret; + } + + close(fd); + + return bytes_read; + +err: + close(fd); + return 0; +} diff --git a/devicemodel/include/att_keybox.h b/devicemodel/include/att_keybox.h new file mode 100644 index 000000000..d4cd6772f --- /dev/null +++ b/devicemodel/include/att_keybox.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2019, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _ATT_KEYBOX_H +#define _ATT_KEYBOX_H + +typedef struct _HECI_MESSAGE_HEADER { + uint32_t groupid : 8; + uint32_t command : 7; + uint32_t is_response : 1; + uint32_t reserved : 8; + uint32_t result : 8; +} HECI_MESSAGE_HEADER; + +typedef struct +{ + HECI_MESSAGE_HEADER header; + /* Size of the whole attkb file, including the added encryption header. */ + uint16_t total_file_size; + uint16_t read_offset; + /* Size in bytes actually read */ + uint16_t read_size; + uint16_t reserved; + uint8_t file_data[]; +} HECI_READ_ATTKB_EX_Response; + +typedef struct +{ + HECI_MESSAGE_HEADER header; + uint16_t offset; // Offset in file in bytes. + uint16_t size; // Size in bytes to read + struct + { + uint32_t encryption : 1; + uint32_t reserved : 31; + } flags; +} HECI_READ_ATTKB_EX_Request; + +#pragma pack (1) +typedef struct rpmb_block { + uint8_t signature[4]; + uint32_t length; + uint32_t revision; + uint32_t flag; + uint16_t attkb_addr; + uint32_t attkb_size; + uint16_t attkb_svn; + uint16_t uos_rpmb_size; + uint8_t reserved[230]; +} rpmb_block_t; +#pragma pack () + +uint16_t read_attkb(void *data, uint16_t size); +uint16_t get_attkb_size(void); + +#endif // _ATT_KEYBOX_H