blob: 2643ee2d5c4a410b0fff4895867b353fbdd03708 [file] [log] [blame]
/* SPDX-License-Identifier: BSD-3-Clause */
/*
* swtpm.c: Programming of a swtpm using communication via fd-passing
*
* Author: Stefan Berger, stefanb@linux.ibm.com
*
* Copyright (c) IBM Corporation, 2021
*/
#include "config.h"
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <glib.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
# include <openssl/core_names.h>
# include <openssl/param_build.h>
#else
# include <openssl/rsa.h>
#endif
#include "swtpm.h"
#include "swtpm_utils.h"
#include "tpm_ioctl.h"
#include "sys_dependencies.h"
#define AS2BE(VAL) (((VAL) >> 8) & 0xff), ((VAL) & 0xff)
#define AS4BE(VAL) AS2BE((VAL) >> 16), AS2BE(VAL)
#define AS8BE(VAL) AS4BE((VAL) >> 32), AS4BE(VAL)
struct tpm_req_header {
uint16_t tag;
uint32_t size;
uint32_t ordinal;
} __attribute__((packed));
struct tpm_resp_header {
uint16_t tag;
uint32_t size;
uint32_t errcode;
} __attribute__((packed));
static int swtpm_start(struct swtpm *self)
{
g_autofree gchar *tpmstate = g_strdup_printf("backend-uri=%s", self->state_path);
g_autofree gchar *pidfile_arg = NULL;
g_autofree gchar *server_fd = NULL;
g_autofree gchar *ctrl_fd = NULL;
g_autofree gchar *keyopts = NULL;
g_autofree gchar *logop = NULL;
g_autofree gchar **argv = NULL;
struct stat statbuf;
gboolean success;
GError *error = NULL;
GSpawnFlags flags;
unsigned ctr;
int pidfile_fd;
int ret = 1;
char pidfile[] = "/tmp/.swtpm_setup.pidfile.XXXXXX";
pidfile_fd = mkstemp(pidfile);
if (pidfile_fd < 0) {
logerr(self->logfile, "Could not create pidfile: %s\n", strerror(errno));
goto error_no_pidfile;
}
// pass filename rather than fd (Cygwin)
pidfile_arg = g_strdup_printf("file=%s", pidfile);
argv = concat_arrays(self->swtpm_exec_l,
(gchar*[]){
"--flags", "not-need-init,startup-clear",
"--tpmstate", tpmstate,
"--pid", pidfile_arg,
#if 0
"--log", "file=/tmp/log,level=20",
#endif
NULL
}, FALSE);
if (self->is_tpm2)
argv = concat_arrays(argv, (gchar*[]){"--tpm2", NULL}, TRUE);
if (self->keyopts != NULL) {
keyopts = g_strdup(self->keyopts);
argv = concat_arrays(argv, (gchar*[]){"--key", keyopts, NULL}, TRUE);
}
if (gl_LOGFILE != NULL) {
logop = g_strdup_printf("file=%s", gl_LOGFILE);
argv = concat_arrays(argv, (gchar*[]){"--log", logop, NULL}, TRUE);
}
if (socketpair(AF_UNIX, SOCK_STREAM, 0, self->ctrl_fds) != 0) {
logerr(self->logfile, "Could not create socketpair: %s\n", strerror(errno));
goto error;
}
ctrl_fd = g_strdup_printf("type=unixio,clientfd=%d", self->ctrl_fds[1]);
if (socketpair(AF_UNIX, SOCK_STREAM, 0, self->data_fds) != 0) {
logerr(self->logfile, "Could not create socketpair: %s\n", strerror(errno));
goto error;
}
server_fd = g_strdup_printf("type=tcp,fd=%d", self->data_fds[1]);
argv = concat_arrays(argv, (gchar*[]){
"--server", server_fd,
"--ctrl", ctrl_fd,
NULL
}, TRUE);
#if 0
{
g_autofree gchar *join = g_strjoinv(" ", argv);
logit(self->logfile, "Starting swtpm: %s\n", join);
}
#endif
flags = G_SPAWN_LEAVE_DESCRIPTORS_OPEN;
if (gl_LOGFILE) {
flags |= G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL;
} else {
#if GLIB_CHECK_VERSION(2, 74, 0)
flags |= G_SPAWN_CHILD_INHERITS_STDOUT | G_SPAWN_CHILD_INHERITS_STDERR;
#endif
}
success = g_spawn_async(NULL, argv, NULL, flags,
NULL, NULL, &self->pid, &error);
if (!success) {
logerr(self->logfile, "Could not start swtpm: %s\n", error->message);
g_error_free(error);
goto error;
}
/* wait until the pidfile is written to or swtpm terminates */
for (ctr = 0; ctr < 1000; ctr++) {
if (kill(self->pid, 0) < 0) {
/* swtpm terminated */
self->pid = 0;
logerr(self->logfile, "swtpm process terminated unexpectedly.\n");
self->cops->stop(self);
goto error;
}
if (fstat(pidfile_fd, &statbuf) == 0 && statbuf.st_size > 0) {
printf("TPM is listening on Unix socket.\n");
ret = 0;
break;
}
usleep(5000);
}
error:
close(pidfile_fd);
unlink(pidfile);
error_no_pidfile:
return ret;
}
/* Stop a running swtpm instance and close all the file descriptors connecting to it */
static void swtpm_stop(struct swtpm *self)
{
unsigned c;
gboolean ended = FALSE;
if (self->pid > 0) {
self->cops->ctrl_shutdown(self);
for (c = 0; c < 500; c++) {
if (kill(self->pid, 0) < 0) {
ended = TRUE;
break;
}
usleep(1000);
}
if (!ended)
kill(self->pid, SIGKILL);
waitpid(self->pid, NULL, 0);
self->pid = 0;
}
if (self->ctrl_fds[0] >= 0) {
close(self->ctrl_fds[0]);
close(self->ctrl_fds[1]);
self->ctrl_fds[0] = self->ctrl_fds[1] = -1;
}
if (self->data_fds[0] >= 0) {
close(self->data_fds[0]);
close(self->data_fds[1]);
self->data_fds[0] = self->data_fds[1] = -1;
}
}
/* Destroy a running swtpm instance */
static void swtpm_destroy(struct swtpm *self)
{
self->cops->stop(self);
}
/* Send a command to swtpm and receive the response either via control or data channel */
static int transfer(struct swtpm *self, void *buffer, size_t buffer_len,
const char *cmdname, gboolean use_ctrl,
void *respbuffer, size_t *respbuffer_len)
{
size_t offset;
int sockfd;
ssize_t n;
unsigned char resp[4096];
ssize_t resplen;
uint32_t returncode;
if (use_ctrl) {
sockfd = self->ctrl_fds[0];
offset = 0;
} else {
sockfd = self->data_fds[0];
offset = 6;
}
n = write(sockfd, buffer, buffer_len);
if (n < 0) {
logerr(self->logfile, "Could not send %s buffer to swtpm: %s\n",
cmdname, strerror(errno));
return 1;
}
if ((size_t)n != buffer_len) {
logerr(self->logfile, "Could not send all bytes to swtpm: %zu < %zu\n",
(size_t)n, buffer_len);
return 1;
}
resplen = read(sockfd, resp, sizeof(resp));
if (resplen < 0) {
logerr(self->logfile, "Could not receive response to %s from swtpm: %s\n",
cmdname, strerror(errno));
return 1;
}
if (!use_ctrl) {
if ((size_t)resplen < sizeof(struct tpm_resp_header)) {
logerr(self->logfile,
"Response for %s has only %d bytes.\n", cmdname, resplen);
return 1;
}
} else if ((size_t)resplen < 4) {
logerr(self->logfile,
"Response for %s has only %d bytes.\n", cmdname, resplen);
return 1;
}
memcpy(&returncode, &resp[offset], sizeof(returncode));
returncode = be32toh(returncode);
if (returncode != 0) {
logerr(self->logfile,
"%s failed: 0x%x\n", cmdname, returncode);
return 1;
}
if (respbuffer) {
*respbuffer_len = min((size_t)resplen, *respbuffer_len);
memcpy(respbuffer, resp, *respbuffer_len);
}
return 0;
}
/* Send a CMD_SHUTDOWN over the control channel */
static int swtpm_ctrl_shutdown(struct swtpm *self)
{
uint32_t cmd = htobe32(CMD_SHUTDOWN);
return transfer(self, &cmd, sizeof(cmd), "CMD_SHUTDOWN", TRUE, NULL, 0);
}
/* Get the TPM specification parameters over the control channel */
static int swtpm_ctrl_get_tpm_specs_and_attrs(struct swtpm *self, gchar **result)
{
unsigned char req[] = {AS4BE(CMD_GET_INFO),
AS8BE(SWTPM_INFO_TPMSPECIFICATION | SWTPM_INFO_TPMATTRIBUTES),
AS4BE(0), AS4BE(0)};
unsigned char tpmresp[1024];
size_t tpmresp_len = sizeof(tpmresp);
int ret;
uint32_t length;
ret = transfer(self, req, sizeof(req), "CMD_GET_INFO", TRUE, tpmresp, &tpmresp_len);
if (ret != 0)
return 1;
if (tpmresp_len < 8 + sizeof(length))
goto err_too_short;
memcpy(&length, &tpmresp[8], sizeof(length));
length = htobe32(length);
if (tpmresp_len < 12 + length)
goto err_too_short;
*result = g_strndup((gchar *)&tpmresp[12], length);
return 0;
err_too_short:
logerr(self->logfile, "Response from CMD_GET_INFO is too short!\n");
return 1;
}
static const struct swtpm_cops swtpm_cops = {
.start = swtpm_start,
.stop = swtpm_stop,
.destroy = swtpm_destroy,
.ctrl_shutdown = swtpm_ctrl_shutdown,
.ctrl_get_tpm_specs_and_attrs = swtpm_ctrl_get_tpm_specs_and_attrs,
};
/*
* TPM 2 support
*/
#define TPM2_ST_NO_SESSIONS 0x8001
#define TPM2_ST_SESSIONS 0x8002
#define TPM2_CC_EVICTCONTROL 0x00000120
#define TPM2_CC_NV_DEFINESPACE 0x0000012a
#define TPM2_CC_PCR_ALLOCATE 0x0000012b
#define TPM2_CC_CREATEPRIMARY 0x00000131
#define TPM2_CC_NV_WRITE 0x00000137
#define TPM2_CC_NV_WRITELOCK 0x00000138
#define TPM2_CC_SHUTDOWN 0x00000145
#define TPM2_CC_GETCAPABILITY 0x0000017a
#define TPM2_SU_CLEAR 0x0000
#define TPM2_RH_OWNER 0x40000001
#define TPM2_RS_PW 0x40000009
#define TPM2_RH_ENDORSEMENT 0x4000000b
#define TPM2_RH_PLATFORM 0x4000000c
#define TPM2_ALG_RSA 0x0001
#define TPM2_ALG_SHA1 0x0004
#define TPM2_ALG_AES 0x0006
#define TPM2_ALG_SHA256 0x000b
#define TPM2_ALG_SHA384 0x000c
#define TPM2_ALG_SHA512 0x000d
#define TPM2_ALG_SHA3_256 0x0027
#define TPM2_ALG_SHA3_384 0x0028
#define TPM2_ALG_SHA3_512 0x0029
#define TPM2_ALG_NULL 0x0010
#define TPM2_ALG_SM3 0x0012
#define TPM2_ALG_ECC 0x0023
#define TPM2_ALG_CFB 0x0043
#define TPM2_CAP_PCRS 0x00000005
#define TPM2_ECC_NIST_P384 0x0004
#define TPMA_NV_PLATFORMCREATE 0x40000000
#define TPMA_NV_AUTHREAD 0x40000
#define TPMA_NV_NO_DA 0x2000000
#define TPMA_NV_PPWRITE 0x1
#define TPMA_NV_PPREAD 0x10000
#define TPMA_NV_OWNERREAD 0x20000
#define TPMA_NV_WRITEDEFINE 0x2000
// Use standard EK Cert NVRAM, EK and SRK handles per IWG spec.
// "TCG TPM v2.0 Provisioning Guide"; Version 1.0, Rev 1.0, March 15, 2017
// Table 2
#define TPM2_NV_INDEX_RSA2048_EKCERT 0x01c00002
#define TPM2_NV_INDEX_RSA2048_EKTEMPLATE 0x01c00004
#define TPM2_NV_INDEX_RSA3072_HI_EKCERT 0x01c0001c
#define TPM2_NV_INDEX_RSA3072_HI_EKTEMPLATE 0x01c0001d
// For ECC follow "TCG EK Credential Profile For TPM Family 2.0; Level 0"
// Specification Version 2.1; Revision 13; 10 December 2018
#define TPM2_NV_INDEX_PLATFORMCERT 0x01c08000
#define TPM2_NV_INDEX_ECC_SECP384R1_HI_EKCERT 0x01c00016
#define TPM2_NV_INDEX_ECC_SECP384R1_HI_EKTEMPLATE 0x01c00017
#define TPM2_EK_RSA_HANDLE 0x81010001
#define TPM2_EK_RSA3072_HANDLE 0x8101001c
#define TPM2_EK_ECC_SECP384R1_HANDLE 0x81010016
#define TPM2_SPK_HANDLE 0x81000001
#define TPM_REQ_HEADER_INITIALIZER(TAG, SIZE, ORD) \
{ \
.tag = htobe16(TAG), \
.size = htobe32(SIZE), \
.ordinal = htobe32(ORD), \
}
struct tpm2_authblock {
uint32_t auth;
uint16_t foo; // FIXME
uint8_t continueSession;
uint16_t bar; // FIMXE
} __attribute__((packed));
#define TPM2_AUTHBLOCK_INITIALIZER(AUTH, FOO, CS, BAR) \
{ \
.auth = htobe32(AUTH), \
.foo = htobe16(FOO), \
.continueSession = CS, \
.bar = htobe16(BAR), \
}
static const unsigned char NONCE_EMPTY[2] = {AS2BE(0)};
static const unsigned char NONCE_RSA2048[2+0x100] = {AS2BE(0x100), 0, };
static const unsigned char NONCE_RSA3072[2+0x180] = {AS2BE(0x180), 0, };
static const unsigned char NONCE_ECC_384[2+0x30] = {AS2BE(0x30), 0, };
static const struct bank_to_name {
uint16_t hashAlg;
const char *name;
} banks_to_names[] = {
{TPM2_ALG_SHA1, "sha1"},
{TPM2_ALG_SHA256, "sha256"},
{TPM2_ALG_SHA384, "sha384"},
{TPM2_ALG_SHA512, "sha512"},
{TPM2_ALG_SM3, "sm3-256"},
{TPM2_ALG_SHA3_256, "sha3-256"},
{TPM2_ALG_SHA3_384, "sha3-384"},
{TPM2_ALG_SHA3_512, "sha3-512"},
{0, NULL},
};
/* function prototypes */
static int swtpm_tpm2_createprimary_rsa(struct swtpm *self, uint32_t primaryhandle, unsigned int keyflags,
const unsigned char *symkeydata, size_t symkeydata_len,
const unsigned char *authpolicy, size_t authpolicy_len,
unsigned int rsa_keysize, gboolean havenonce, size_t off,
uint32_t *curr_handle,
unsigned char *ektemplate, size_t *ektemplate_len,
gchar **ekparam, const gchar **key_description);
static int swtpm_tpm2_write_nvram(struct swtpm *self, uint32_t nvindex, uint32_t nvindexattrs,
const unsigned char *data, size_t data_len, gboolean lock_nvram,
const char *purpose);
/* Given a hash algo identifier, return the name of the hash bank */
static const char *get_name_for_bank(uint16_t hashAlg) {
size_t i;
for (i = 0; banks_to_names[i].name; i++) {
if (banks_to_names[i].hashAlg == hashAlg)
return banks_to_names[i].name;
}
return NULL;
}
/* Give the name of a hash bank, return its algo identifer */
static uint16_t get_hashalg_by_bankname(const char *name) {
size_t i;
for (i = 0; banks_to_names[i].name; i++) {
if (strcmp(banks_to_names[i].name, name) == 0)
return banks_to_names[i].hashAlg;
}
return 0;
}
/* Do an SU_CLEAR shutdown of the TPM 2 */
static int swtpm_tpm2_shutdown(struct swtpm *self)
{
struct tpm2_shutdown_req {
struct tpm_req_header hdr;
uint16_t shutdownType;
} __attribute__((packed)) req = {
.hdr = TPM_REQ_HEADER_INITIALIZER(TPM2_ST_NO_SESSIONS, sizeof(req), TPM2_CC_SHUTDOWN),
.shutdownType = htobe16(TPM2_SU_CLEAR)
};
return transfer(self, &req, sizeof(req), "TPM2_Shutdown", FALSE, NULL, NULL);
}
/* Get all available PCR banks */
static int swtpm_tpm2_get_all_pcr_banks(struct swtpm *self, gchar ***all_pcr_banks)
{
struct tpm_req_header hdr = TPM_REQ_HEADER_INITIALIZER(TPM2_ST_NO_SESSIONS, 0, TPM2_CC_GETCAPABILITY);
g_autofree unsigned char *req = NULL;
ssize_t req_len;
unsigned char tpmresp[256];
size_t tpmresp_len = sizeof(tpmresp);
uint16_t count, bank;
const char *name;
uint8_t length;
size_t offset;
size_t i;
int ret;
req_len = memconcat(&req,
&hdr, sizeof(hdr),
(unsigned char[]){AS4BE(TPM2_CAP_PCRS), AS4BE(0), AS4BE(64)}, (size_t)12,
NULL);
if (req_len < 0) {
logerr(self->logfile, "Internal error in %s: memconcat failed\n", __func__);
return 1;
}
((struct tpm_req_header *)req)->size = htobe32(req_len);
ret = transfer(self, req, req_len, "TPM2_GetCapability", FALSE, tpmresp, &tpmresp_len);
if (ret != 0)
return 1;
*all_pcr_banks = NULL;
if (tpmresp_len < 17 + sizeof(count))
goto err_too_short;
memcpy(&count, &tpmresp[17], sizeof(count));
count = be16toh(count);
/* unreasonable number of PCR banks ? */
if (count > 20)
goto err_num_pcrbanks;
*all_pcr_banks = g_malloc0(sizeof(char *) * (count + 1));
offset = 19;
for (i = 0; i < count; i++) {
gchar *n;
if (tpmresp_len < offset + sizeof(bank))
goto err_too_short;
memcpy(&bank, &tpmresp[offset], sizeof(bank));
bank = be16toh(bank);
if (tpmresp_len < offset + 2 + sizeof(length))
goto err_too_short;
length = tpmresp[offset + 2];
name = get_name_for_bank(bank);
if (name != NULL)
n = g_strdup(name);
else
n = g_strdup_printf("%02x", bank);
(*all_pcr_banks)[i] = n;
offset += 2 + 1 + length;
}
return 0;
err_num_pcrbanks:
logerr(self->logfile, "Unreasonable number of PCR banks (%u) returned.\n", count);
goto err_exit;
err_too_short:
logerr(self->logfile, "Response from TPM2_GetCapability is too short!\n");
err_exit:
g_strfreev(*all_pcr_banks);
*all_pcr_banks = NULL;
return 1;
}
/* Activate all user-chosen PCR banks and deactivate all others */
static int swtpm_tpm2_set_active_pcr_banks(struct swtpm *self, gchar **pcr_banks,
gchar **all_pcr_banks, gchar ***active)
{
struct tpm_req_header hdr = TPM_REQ_HEADER_INITIALIZER(TPM2_ST_SESSIONS, 0, TPM2_CC_PCR_ALLOCATE);
struct tpm2_authblock authblock = TPM2_AUTHBLOCK_INITIALIZER(TPM2_RS_PW, 0, 0, 0);
unsigned char pcrselects[6 * 10]; // supports up to 10 PCR banks
ssize_t pcrselects_len = 0;
size_t count = 0;
size_t idx, j;
uint16_t hashAlg;
g_autofree unsigned char *req = NULL;
ssize_t req_len, len;
int ret;
uint64_t activated_mask = 0;
for (idx = 0; pcr_banks[idx] != NULL; idx++)
;
*active = g_malloc0(sizeof(char *) * (idx + 1));
for (idx = 0; pcr_banks[idx] != NULL; idx++) {
hashAlg = 0;
// Is user-chosen pcr_banks[idx] available?
for (j = 0; all_pcr_banks[j] != NULL; j++) {
if (strcmp(pcr_banks[idx], all_pcr_banks[j]) == 0) {
hashAlg = get_hashalg_by_bankname(pcr_banks[idx]);
break;
}
}
if (hashAlg != 0 && (activated_mask & ((uint64_t)1 << j)) == 0) {
(*active)[count] = g_strdup(pcr_banks[idx]);
len = concat(&pcrselects[pcrselects_len], sizeof(pcrselects) - pcrselects_len,
(unsigned char[]){AS2BE(hashAlg), 3, 0xff, 0xff, 0xff} , (size_t)6,
NULL);
if (len < 0) {
logerr(self->logfile, "Internal error in %s: pcrselects is too small\n", __func__);
return 1;
}
pcrselects_len += len;
count++;
activated_mask |= ((uint64_t)1 << j);
}
}
if (count == 0) {
logerr(self->logfile,
"No PCR banks could be allocated. None of the selected algorithms are supported.\n");
goto error;
}
// disable all the other ones not chosen by the user
for (idx = 0; all_pcr_banks[idx] != NULL; idx++) {
gboolean found = FALSE;
for (j = 0; pcr_banks[j] != NULL; j++) {
if (strcmp(pcr_banks[j], all_pcr_banks[idx]) == 0) {
found = TRUE;
break;
}
}
if (found)
continue;
/* not found, so not chosen by user */
hashAlg = get_hashalg_by_bankname(all_pcr_banks[idx]);
len = concat(&pcrselects[pcrselects_len], sizeof(pcrselects) - pcrselects_len,
(unsigned char[]){AS2BE(hashAlg), 3, 0, 0, 0}, (size_t)6,
NULL);
if (len < 0) {
logerr(self->logfile, "Internal error in %s: pcrselects is too small\n", __func__);
goto error;
}
pcrselects_len += len;
count++;
}
req_len = memconcat(&req,
&hdr, sizeof(hdr),
(unsigned char[]){
AS4BE(TPM2_RH_PLATFORM), AS4BE(sizeof(authblock))
}, (size_t)8,
&authblock, sizeof(authblock),
(unsigned char[]){AS4BE(count)}, (size_t)4,
pcrselects, pcrselects_len,
NULL);
if (req_len < 0) {
logerr(self->logfile, "Internal error in %s: req is too small\n", __func__);
goto error;
}
((struct tpm_req_header *)req)->size = htobe32(req_len);
ret = transfer(self, req, req_len, "TPM2_PCR_Allocate", FALSE, NULL, 0);
if (ret != 0)
goto error;
return 0;
error:
g_strfreev(*active);
*active = NULL;
return 1;
}
/* Make object at the curr_handler permanent with the perm_handle */
static int swtpm_tpm2_evictcontrol(struct swtpm *self, uint32_t curr_handle, uint32_t perm_handle)
{
struct tpm2_evictcontrol_req {
struct tpm_req_header hdr;
uint32_t auth;
uint32_t objectHandle;
uint32_t authblockLen;
struct tpm2_authblock authblock;
uint32_t persistentHandle;
} __attribute__((packed)) req = {
.hdr = TPM_REQ_HEADER_INITIALIZER(TPM2_ST_SESSIONS, sizeof(req), TPM2_CC_EVICTCONTROL),
.auth = htobe32(TPM2_RH_OWNER),
.objectHandle = htobe32(curr_handle),
.authblockLen = htobe32(sizeof(req.authblock)),
.authblock = TPM2_AUTHBLOCK_INITIALIZER(TPM2_RS_PW, 0, 0, 0),
.persistentHandle = htobe32(perm_handle),
};
return transfer(self, &req, sizeof(req), "TPM2_EvictControl", FALSE, NULL, 0);
}
/* Create an RSA EK */
static int swtpm_tpm2_createprimary_ek_rsa(struct swtpm *self, unsigned int rsa_keysize,
gboolean allowsigning, gboolean decryption,
uint32_t *curr_handle,
unsigned char *ektemplate, size_t *ektemplate_len,
gchar **ekparam, const gchar **key_description)
{
unsigned char authpolicy[48];
size_t authpolicy_len;
unsigned char symkeydata[6];
size_t symkeydata_len;
unsigned int keyflags;
unsigned int symkeylen;
gboolean havenonce;
size_t addlen, off;
if (rsa_keysize == 2048) {
authpolicy_len = 32;
memcpy(authpolicy, ((unsigned char []){
0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xb3, 0xf8, 0x1a, 0x90, 0xcc, 0x8d,
0x46, 0xa5, 0xd7, 0x24, 0xfd, 0x52, 0xd7, 0x6e, 0x06, 0x52, 0x0b, 0x64,
0xf2, 0xa1, 0xda, 0x1b, 0x33, 0x14, 0x69, 0xaa
}), authpolicy_len);
keyflags = 0;
symkeylen = 128;
havenonce = TRUE;
addlen = 0;
} else if (rsa_keysize == 3072) {
authpolicy_len = 48;
memcpy(authpolicy, ((unsigned char []){
0xB2, 0x6E, 0x7D, 0x28, 0xD1, 0x1A, 0x50, 0xBC, 0x53, 0xD8, 0x82, 0xBC,
0xF5, 0xFD, 0x3A, 0x1A, 0x07, 0x41, 0x48, 0xBB, 0x35, 0xD3, 0xB4, 0xE4,
0xCB, 0x1C, 0x0A, 0xD9, 0xBD, 0xE4, 0x19, 0xCA, 0xCB, 0x47, 0xBA, 0x09,
0x69, 0x96, 0x46, 0x15, 0x0F, 0x9F, 0xC0, 0x00, 0xF3, 0xF8, 0x0E, 0x12
}), authpolicy_len);
keyflags = 0x40;
symkeylen = 256;
havenonce = FALSE;
addlen = 16;
} else {
logerr(self->logfile, "Internal error in %s: unsupported RSA keysize %d.\n",
__func__, rsa_keysize);
return 1;
}
if (allowsigning && decryption) {
// keyflags: fixedTPM, fixedParent, sensitiveDatOrigin,
// adminWithPolicy, sign, decrypt
keyflags |= 0x000600b2;
// symmetric: TPM_ALG_NULL
symkeydata_len = 2;
memcpy(symkeydata, ((unsigned char[]) {AS2BE(TPM2_ALG_NULL)}), symkeydata_len);
off = 72 + addlen;
} else if (allowsigning) {
// keyflags: fixedTPM, fixedParent, sensitiveDatOrigin,
// adminWithPolicy, sign
keyflags |= 0x000400b2;
// symmetric: TPM_ALG_NULL
symkeydata_len = 2;
memcpy(symkeydata, ((unsigned char[]) {AS2BE(TPM2_ALG_NULL)}), symkeydata_len);
off = 72 + addlen;
} else {
// keyflags: fixedTPM, fixedParent, sensitiveDatOrigin,
// adminWithPolicy, restricted, decrypt
keyflags |= 0x000300b2;
// symmetric: TPM_ALG_AES, 128bit or 256bit, TPM_ALG_CFB
symkeydata_len = 6;
memcpy(symkeydata,
((unsigned char[]) {AS2BE(TPM2_ALG_AES), AS2BE(symkeylen), AS2BE(TPM2_ALG_CFB)}),
symkeydata_len);
off = 76 + addlen;
}
return swtpm_tpm2_createprimary_rsa(self, TPM2_RH_ENDORSEMENT, keyflags,
symkeydata, symkeydata_len,
authpolicy, authpolicy_len, rsa_keysize,
havenonce, off, curr_handle,
ektemplate, ektemplate_len, ekparam, key_description);
}
/* Create an RSA key with the given parameters */
static int swtpm_tpm2_createprimary_rsa(struct swtpm *self, uint32_t primaryhandle, unsigned int keyflags,
const unsigned char *symkeydata, size_t symkeydata_len,
const unsigned char *authpolicy, size_t authpolicy_len,
unsigned int rsa_keysize, gboolean havenonce, size_t off,
uint32_t *curr_handle,
unsigned char *ektemplate, size_t *ektemplate_len,
gchar **ekparam, const gchar **key_description)
{
const unsigned char *nonce;
size_t nonce_len;
uint16_t hashalg;
struct tpm_req_header hdr = TPM_REQ_HEADER_INITIALIZER(TPM2_ST_SESSIONS, 0, TPM2_CC_CREATEPRIMARY);
struct tpm2_authblock authblock = TPM2_AUTHBLOCK_INITIALIZER(TPM2_RS_PW, 0, 0, 0);
g_autofree unsigned char *public = NULL;
ssize_t public_len;
g_autofree unsigned char *createprimary = NULL;
ssize_t createprimary_len;
int ret;
unsigned char tpmresp[2048];
size_t tpmresp_len = sizeof(tpmresp);
uint16_t modlen;
if (rsa_keysize == 2048) {
nonce = NONCE_RSA2048;
nonce_len = sizeof(NONCE_RSA2048);
hashalg = TPM2_ALG_SHA256;
if (key_description)
*key_description = "rsa2048";
} else if (rsa_keysize == 3072) {
if (!havenonce) {
nonce = NONCE_EMPTY;
nonce_len = sizeof(NONCE_EMPTY);
} else {
nonce = NONCE_RSA3072;
nonce_len = sizeof(NONCE_RSA3072);
}
hashalg = TPM2_ALG_SHA384;
if (key_description)
*key_description = "rsa3072";
} else {
logerr(self->logfile, "Internal error in %s: unsupported RSA keysize %d.\n",
__func__, rsa_keysize);
return 1;
}
public_len =
memconcat(&public,
(unsigned char[]) {
AS2BE(TPM2_ALG_RSA), AS2BE(hashalg),
AS4BE(keyflags), AS2BE(authpolicy_len)
}, (size_t)10,
authpolicy, authpolicy_len,
symkeydata, symkeydata_len,
(unsigned char[]) {
AS2BE(TPM2_ALG_NULL), AS2BE(rsa_keysize), AS4BE(0)
}, (size_t)8,
nonce, nonce_len,
NULL);
if (public_len < 0) {
logerr(self->logfile, "Internal error in %s: memconcat failed\n", __func__);
return 1;
}
if (ektemplate) {
if (*ektemplate_len < (size_t)public_len) {
logerr(self->logfile, "Internal error in %s: Need %zu bytes for ektemplate (rsa) but got only %zu\n",
__func__, public_len, *ektemplate_len);
return 1;
}
memcpy(ektemplate, public, public_len);
*ektemplate_len = public_len;
}
createprimary_len =
memconcat(&createprimary,
&hdr, sizeof(hdr),
(unsigned char[]) {AS4BE(primaryhandle), AS4BE(sizeof(authblock))}, (size_t)8,
&authblock, sizeof(authblock),
(unsigned char[]) {AS2BE(4), AS4BE(0), AS2BE(public_len)}, (size_t)8,
public, public_len,
(unsigned char[]) {AS4BE(0), AS2BE(0)}, (size_t)6,
NULL);
if (createprimary_len < 0) {
logerr(self->logfile, "Internal error in %s: memconcat failed\n", __func__);
return 1;
}
((struct tpm_req_header *)createprimary)->size = htobe32(createprimary_len);
ret = transfer(self, createprimary, createprimary_len, "TPM2_CreatePrimary(RSA)", FALSE,
tpmresp, &tpmresp_len);
if (ret != 0)
return 1;
if (curr_handle) {
if (tpmresp_len < 10 + sizeof(*curr_handle))
goto err_too_short;
memcpy(curr_handle, &tpmresp[10], sizeof(*curr_handle));
*curr_handle = be32toh(*curr_handle);
}
if (tpmresp_len < off + sizeof(modlen))
goto err_too_short;
memcpy(&modlen, &tpmresp[off], sizeof(modlen));
modlen = be16toh(modlen);
if (modlen != rsa_keysize >> 3) {
logerr(self->logfile, "Internal error in %s: Getting modulus from wrong offset %zu\n",
__func__, off);
return 1;
}
if (ekparam) {
if (tpmresp_len < off + 2 + modlen)
goto err_too_short;
*ekparam = print_as_hex(&tpmresp[off + 2], modlen);
}
return 0;
err_too_short:
logerr(self->logfile, "Response from TPM2_CreatePrimary(RSA) is too short!\n");
return 1;
}
/* Create an ECC key with the given parameters */
static int swtpm_tpm2_createprimary_ecc(struct swtpm *self, uint32_t primaryhandle, unsigned int keyflags,
const unsigned char *symkeydata, size_t symkeydata_len,
const unsigned char *authpolicy, size_t authpolicy_len,
unsigned short curveid, unsigned short hashalg,
const unsigned char *nonce, size_t nonce_len,
size_t off, uint32_t *curr_handle,
unsigned char *ektemplate, size_t *ektemplate_len,
gchar **ekparam, const gchar **key_description)
{
struct tpm_req_header hdr = TPM_REQ_HEADER_INITIALIZER(TPM2_ST_SESSIONS, 0, TPM2_CC_CREATEPRIMARY);
struct tpm2_authblock authblock = TPM2_AUTHBLOCK_INITIALIZER(TPM2_RS_PW, 0, 0, 0);
g_autofree unsigned char *public = NULL;
ssize_t public_len;
g_autofree unsigned char *createprimary = NULL;
ssize_t createprimary_len;
int ret;
unsigned char tpmresp[2048];
size_t tpmresp_len = sizeof(tpmresp);
size_t off2;
uint16_t exp_ksize, ksize1, ksize2;
const char *cid;
public_len =
memconcat(&public,
(unsigned char[]){
AS2BE(TPM2_ALG_ECC), AS2BE(hashalg), AS4BE(keyflags), AS2BE(authpolicy_len)
}, (size_t)10,
authpolicy, authpolicy_len,
symkeydata, symkeydata_len,
(unsigned char[]) {AS2BE(TPM2_ALG_NULL), AS2BE(curveid), AS2BE(TPM2_ALG_NULL)}, (size_t)6,
nonce, nonce_len,
nonce, nonce_len,
NULL);
if (public_len < 0) {
logerr(self->logfile, "Internal error in %s: memconcat failed\n", __func__);
return 1;
}
if (ektemplate) {
if (*ektemplate_len < (size_t)public_len) {
logerr(self->logfile, "Internal error: Need %zu bytes for ektemplate (ecc) but got only %zu\n",
public_len, ektemplate_len);
return 1;
}
memcpy(ektemplate, public, public_len);
*ektemplate_len = public_len;
}
createprimary_len =
memconcat(&createprimary,
&hdr, sizeof(hdr),
(unsigned char[]) {AS4BE(primaryhandle), AS4BE(sizeof(authblock))}, (size_t)8,
&authblock, sizeof(authblock),
(unsigned char[]) {AS2BE(4), AS4BE(0), AS2BE(public_len)}, (size_t)8,
public, public_len,
(unsigned char[]) {AS4BE(0), AS2BE(0)}, (size_t)6,
NULL);
if (createprimary_len < 0) {
logerr(self->logfile, "Internal error in %s: memconcat failed\n", __func__);
return 1;
}
((struct tpm_req_header *)createprimary)->size = htobe32(createprimary_len);
ret = transfer(self, createprimary, createprimary_len, "TPM2_CreatePrimary(ECC)", FALSE,
tpmresp, &tpmresp_len);
if (ret != 0)
return 1;
if (curr_handle) {
if (tpmresp_len < 10 + sizeof(*curr_handle))
goto err_too_short;
memcpy(curr_handle, &tpmresp[10], sizeof(*curr_handle));
*curr_handle = be32toh(*curr_handle);
}
if (curveid == TPM2_ECC_NIST_P384) {
exp_ksize = 48;
cid = "secp384r1";
if (key_description)
*key_description = cid;
} else {
logerr(self->logfile, "Unknown curveid 0x%x\n", curveid);
return 1;
}
if (tpmresp_len < off + sizeof(ksize1))
goto err_too_short;
memcpy(&ksize1, &tpmresp[off], sizeof(ksize1));
ksize1 = be16toh(ksize1);
off2 = off + 2 + ksize1;
if (tpmresp_len < off2 + sizeof(ksize2))
goto err_too_short;
memcpy(&ksize2, &tpmresp[off2], sizeof(ksize2));
ksize2 = be16toh(ksize2);
if (ksize1 != exp_ksize || ksize2 != exp_ksize) {
logerr(self->logfile, "ECC: Getting key parameters from wrong offset\n");
return 1;
}
if (ekparam) {
unsigned char *xparam = &tpmresp[off + 2];
unsigned char *yparam = &tpmresp[off2 + 2];
if (tpmresp_len < off + 2 + ksize1 || tpmresp_len < off2 + 2 + ksize2)
goto err_too_short;
g_autofree gchar *xparam_str = print_as_hex(xparam, ksize1);
g_autofree gchar *yparam_str = print_as_hex(yparam, ksize2);
*ekparam = g_strdup_printf("x=%s,y=%s,id=%s", xparam_str, yparam_str, cid);
}
return 0;
err_too_short:
logerr(self->logfile, "Response from TPM2_CreatePrimary(ECC) is too short!\n");
return 1;
}
static int swtpm_tpm2_createprimary_spk_ecc_nist_p384(struct swtpm *self,
uint32_t *curr_handle)
{
unsigned int keyflags = 0x00030472;
const unsigned char authpolicy[0];
size_t authpolicy_len = sizeof(authpolicy);
const unsigned char symkeydata[] = {AS2BE(TPM2_ALG_AES), AS2BE(256), AS2BE(TPM2_ALG_CFB)};
size_t symkeydata_len = sizeof(symkeydata);
size_t off = 42;
return swtpm_tpm2_createprimary_ecc(self, TPM2_RH_OWNER, keyflags, symkeydata, symkeydata_len,
authpolicy, authpolicy_len, TPM2_ECC_NIST_P384, TPM2_ALG_SHA384,
NONCE_ECC_384, sizeof(NONCE_ECC_384), off, curr_handle,
NULL, 0, NULL, NULL);
}
static int swtpm_tpm2_createprimary_spk_rsa(struct swtpm *self, unsigned int rsa_keysize,
uint32_t *curr_handle)
{
unsigned int keyflags = 0x00030472;
const unsigned char authpolicy[0];
size_t authpolicy_len = sizeof(authpolicy);
unsigned short symkeylen = 0;
unsigned char symkeydata[6];
size_t symkeydata_len;
size_t off = 44;
if (rsa_keysize == 2048)
symkeylen = 128;
else if (rsa_keysize == 3072)
symkeylen = 256;
symkeydata_len = 6;
memcpy(symkeydata,
((unsigned char[]) {AS2BE(TPM2_ALG_AES), AS2BE(symkeylen), AS2BE(TPM2_ALG_CFB)}),
symkeydata_len);
return swtpm_tpm2_createprimary_rsa(self, TPM2_RH_OWNER, keyflags,
symkeydata, symkeydata_len,
authpolicy, authpolicy_len, rsa_keysize, TRUE,
off, curr_handle, NULL, 0, NULL, NULL);
}
/* Create either an ECC or RSA storage primary key */
static int swtpm_tpm2_create_spk(struct swtpm *self, gboolean isecc, unsigned int rsa_keysize)
{
int ret;
uint32_t curr_handle;
if (isecc)
ret = swtpm_tpm2_createprimary_spk_ecc_nist_p384(self, &curr_handle);
else
ret = swtpm_tpm2_createprimary_spk_rsa(self, rsa_keysize, &curr_handle);
if (ret != 0)
return 1;
ret = swtpm_tpm2_evictcontrol(self, curr_handle, TPM2_SPK_HANDLE);
if (ret == 0)
logit(self->logfile,
"Successfully created storage primary key with handle 0x%x.\n", TPM2_SPK_HANDLE);
return ret;
}
/* Create an ECC EK key that may be allowed to sign and/or decrypt */
static int swtpm_tpm2_createprimary_ek_ecc_nist_p384(struct swtpm *self, gboolean allowsigning,
gboolean decryption, uint32_t *curr_handle,
unsigned char *ektemplate, size_t *ektemplate_len,
gchar **ekparam, const char **key_description)
{
unsigned char authpolicy[48]= {
0xB2, 0x6E, 0x7D, 0x28, 0xD1, 0x1A, 0x50, 0xBC, 0x53, 0xD8, 0x82, 0xBC,
0xF5, 0xFD, 0x3A, 0x1A, 0x07, 0x41, 0x48, 0xBB, 0x35, 0xD3, 0xB4, 0xE4,
0xCB, 0x1C, 0x0A, 0xD9, 0xBD, 0xE4, 0x19, 0xCA, 0xCB, 0x47, 0xBA, 0x09,
0x69, 0x96, 0x46, 0x15, 0x0F, 0x9F, 0xC0, 0x00, 0xF3, 0xF8, 0x0E, 0x12
};
size_t authpolicy_len = 48;
unsigned char symkeydata[6];
size_t symkeydata_len;
unsigned int keyflags;
size_t off;
int ret;
if (allowsigning && decryption) {
// keyflags: fixedTPM, fixedParent, sensitiveDatOrigin,
// userWithAuth, adminWithPolicy, sign, decrypt
keyflags = 0x000600f2;
// symmetric: TPM_ALG_NULL
symkeydata_len = 2;
memcpy(symkeydata, ((unsigned char[]){AS2BE(TPM2_ALG_NULL)}), symkeydata_len);
off = 86;
} else if (allowsigning) {
// keyflags: fixedTPM, fixedParent, sensitiveDatOrigin,
// userWithAuth, adminWithPolicy, sign
keyflags = 0x000400f2;
// symmetric: TPM_ALG_NULL
symkeydata_len = 2;
memcpy(symkeydata, ((unsigned char[]){AS2BE(TPM2_ALG_NULL)}), symkeydata_len);
off = 86;
} else {
// keyflags: fixedTPM, fixedParent, sensitiveDatOrigin,
// userWithAuth, adminWithPolicy, restricted, decrypt
keyflags = 0x000300f2;
// symmetric: TPM_ALG_AES, 256bit, TPM_ALG_CFB
symkeydata_len = 6;
memcpy(symkeydata,
((unsigned char[]){ AS2BE(TPM2_ALG_AES), AS2BE(256), AS2BE(TPM2_ALG_CFB)}),
symkeydata_len);
off = 90;
}
ret = swtpm_tpm2_createprimary_ecc(self, TPM2_RH_ENDORSEMENT, keyflags, symkeydata, symkeydata_len,
authpolicy, authpolicy_len, TPM2_ECC_NIST_P384, TPM2_ALG_SHA384,
NONCE_EMPTY, sizeof(NONCE_EMPTY), off, curr_handle,
ektemplate, ektemplate_len, ekparam, key_description);
if (ret != 0)
logerr(self->logfile, "%s failed\n", __func__);
return ret;
}
/* Create an ECC or RSA EK */
static int swtpm_tpm2_create_ek(struct swtpm *self, gboolean isecc, unsigned int rsa_keysize,
gboolean allowsigning, gboolean decryption, gboolean lock_nvram,
gchar **ekparam, const gchar **key_description)
{
uint32_t tpm2_ek_handle, nvindex, curr_handle;
const char *keytype;
int ret;
unsigned char ektemplate[512];
size_t ektemplate_len = sizeof(ektemplate);
if (isecc) {
tpm2_ek_handle = TPM2_EK_ECC_SECP384R1_HANDLE;
keytype = "ECC";
nvindex = TPM2_NV_INDEX_ECC_SECP384R1_HI_EKTEMPLATE;
} else {
if (rsa_keysize == 2048) {
tpm2_ek_handle = TPM2_EK_RSA_HANDLE;
keytype = "RSA 2048";
nvindex = TPM2_NV_INDEX_RSA2048_EKTEMPLATE;
} else if (rsa_keysize == 3072) {
tpm2_ek_handle = TPM2_EK_RSA3072_HANDLE;
keytype = "RSA 3072";
nvindex = TPM2_NV_INDEX_RSA3072_HI_EKTEMPLATE;
} else {
logerr(self->logfile, "Internal error: Unsupported RSA keysize %u.\n", rsa_keysize);
return 1;
}
}
if (isecc)
ret = swtpm_tpm2_createprimary_ek_ecc_nist_p384(self, allowsigning, decryption, &curr_handle,
ektemplate, &ektemplate_len, ekparam,
key_description);
else
ret = swtpm_tpm2_createprimary_ek_rsa(self, rsa_keysize, allowsigning, decryption, &curr_handle,
ektemplate, &ektemplate_len, ekparam, key_description);
if (ret == 0)
ret = swtpm_tpm2_evictcontrol(self, curr_handle, tpm2_ek_handle);
if (ret != 0) {
logerr(self->logfile, "create_ek failed: 0x%x\n", ret);
return 1;
}
logit(self->logfile,
"Successfully created %s EK with handle 0x%x.\n", keytype, tpm2_ek_handle);
if (allowsigning) {
uint32_t nvindexattrs = TPMA_NV_PLATFORMCREATE | \
TPMA_NV_AUTHREAD | \
TPMA_NV_OWNERREAD | \
TPMA_NV_PPREAD | \
TPMA_NV_PPWRITE | \
TPMA_NV_NO_DA | \
TPMA_NV_WRITEDEFINE;
ret = swtpm_tpm2_write_nvram(self, nvindex, nvindexattrs, ektemplate, ektemplate_len,
lock_nvram, "EK template");
if (ret == 0)
logit(self->logfile,
"Successfully created NVRAM area 0x%x for %s EK template.\n",
nvindex, keytype);
}
return ret;
}
static int swtpm_tpm2_nvdefinespace(struct swtpm *self, uint32_t nvindex, uint32_t nvindexattrs,
uint16_t data_len)
{
struct tpm_req_header hdr = TPM_REQ_HEADER_INITIALIZER(TPM2_ST_SESSIONS, 0, TPM2_CC_NV_DEFINESPACE);
struct tpm2_authblock authblock = TPM2_AUTHBLOCK_INITIALIZER(TPM2_RS_PW, 0, 0, 0);
g_autofree unsigned char *nvpublic = NULL;
ssize_t nvpublic_len;
g_autofree unsigned char *req = NULL;
ssize_t req_len;
nvpublic_len = memconcat(&nvpublic,
(unsigned char[]){
AS4BE(nvindex), AS2BE(TPM2_ALG_SHA256), AS4BE(nvindexattrs),
AS2BE(0), AS2BE(data_len)}, (size_t)14,
NULL);
if (nvpublic_len < 0) {
logerr(self->logfile, "Internal error in %s: memconcat failed\n", __func__);
return 1;
}
req_len = memconcat(&req,
&hdr, sizeof(hdr),
(unsigned char[]){AS4BE(TPM2_RH_PLATFORM), AS4BE(sizeof(authblock))}, (size_t)8,
&authblock, sizeof(authblock),
(unsigned char[]){AS2BE(0), AS2BE(nvpublic_len)}, (size_t)4,
nvpublic, nvpublic_len,
NULL);
if (req_len < 0) {
logerr(self->logfile, "Internal error in %s: memconcat failed\n", __func__);
return 1;
}
((struct tpm_req_header *)req)->size = htobe32(req_len);
return transfer(self, req, req_len, "TPM2_NV_DefineSpace", FALSE, NULL, 0);
}
/* Write the data into the given NVIndex */
static int swtpm_tpm2_nv_write(struct swtpm *self, uint32_t nvindex,
const unsigned char *data, size_t data_len)
{
struct tpm_req_header hdr = TPM_REQ_HEADER_INITIALIZER(TPM2_ST_SESSIONS, 0, TPM2_CC_NV_WRITE);
struct tpm2_authblock authblock = TPM2_AUTHBLOCK_INITIALIZER(TPM2_RS_PW, 0, 0, 0);
g_autofree unsigned char *req = NULL;
ssize_t req_len;
size_t offset = 0, txlen;
int ret;
while (offset < data_len) {
txlen = min(data_len - offset, 1024);
g_free(req);
req_len = memconcat(&req,
&hdr, sizeof(hdr),
(unsigned char[]){
AS4BE(TPM2_RH_PLATFORM), AS4BE(nvindex), AS4BE(sizeof(authblock))
}, (size_t)12,
&authblock, sizeof(authblock),
(unsigned char[]){AS2BE(txlen)}, (size_t)2,
&data[offset], txlen,
(unsigned char[]){AS2BE(offset)}, (size_t)2,
NULL);
if (req_len < 0) {
logerr(self->logfile, "Internal error in %s: memconcat failed\n", __func__);
return 1;
}
((struct tpm_req_header *)req)->size = htobe32(req_len);
ret = transfer(self, req, req_len, "TPM2_NV_Write", FALSE, NULL, 0);
if (ret != 0)
return 1;
offset += txlen;
}
return 0;
}
static int swtpm_tpm2_nv_writelock(struct swtpm *self, uint32_t nvindex)
{
struct tpm_req_header hdr = TPM_REQ_HEADER_INITIALIZER(TPM2_ST_SESSIONS, 0, TPM2_CC_NV_WRITELOCK);
struct tpm2_authblock authblock = TPM2_AUTHBLOCK_INITIALIZER(TPM2_RS_PW, 0, 0, 0);
g_autofree unsigned char *req;
ssize_t req_len;
req_len = memconcat(&req,
&hdr, sizeof(hdr),
(unsigned char[]){
AS4BE(TPM2_RH_PLATFORM), AS4BE(nvindex), AS4BE(sizeof(authblock))
}, (size_t)12,
&authblock, sizeof(authblock),
NULL);
if (req_len < 0) {
logerr(self->logfile, "Internal error in %s: memconcat failed\n", __func__);
return 1;
}
((struct tpm_req_header *)req)->size = htobe32(req_len);
return transfer(self, req, req_len, "TPM2_NV_WriteLock", FALSE, NULL, 0);
}
static int swtpm_tpm2_write_nvram(struct swtpm *self, uint32_t nvindex, uint32_t nvindexattrs,
const unsigned char *data, size_t data_len, gboolean lock_nvram,
const char *purpose)
{
int ret = swtpm_tpm2_nvdefinespace(self, nvindex, nvindexattrs, data_len);
if (ret != 0) {
logerr(self->logfile, "Could not create NVRAM area 0x%x for %s.\n", nvindex, purpose);
return 1;
}
ret = swtpm_tpm2_nv_write(self, nvindex, data, data_len);
if (ret != 0) {
logerr(self->logfile,
"Could not write %s into NVRAM area 0x%x.\n", purpose, nvindex);
return 1;
}
if (lock_nvram) {
ret = swtpm_tpm2_nv_writelock(self, nvindex);
if (ret != 0) {
logerr(self->logfile, "Could not lock EK template NVRAM area 0x%x.\n", nvindex);
return 1;
}
}
return 0;
}
/* Write the platform certificate into an NVRAM area */
static int swtpm_tpm2_write_ek_cert_nvram(struct swtpm *self, gboolean isecc,
unsigned int rsa_keysize, gboolean lock_nvram,
const unsigned char *data, size_t data_len)
{
uint32_t nvindex = 0;
g_autofree gchar *keytype = NULL;
uint32_t nvindexattrs = TPMA_NV_PLATFORMCREATE |
TPMA_NV_AUTHREAD |
TPMA_NV_OWNERREAD |
TPMA_NV_PPREAD |
TPMA_NV_PPWRITE |
TPMA_NV_NO_DA |
TPMA_NV_WRITEDEFINE;
int ret;
if (!isecc) {
if (rsa_keysize == 2048)
nvindex = TPM2_NV_INDEX_RSA2048_EKCERT;
else if (rsa_keysize == 3072)
nvindex = TPM2_NV_INDEX_RSA3072_HI_EKCERT;
keytype = g_strdup_printf("RSA %d", rsa_keysize);
} else {
nvindex = TPM2_NV_INDEX_ECC_SECP384R1_HI_EKCERT;
keytype = g_strdup("ECC");
}
ret = swtpm_tpm2_write_nvram(self, nvindex, nvindexattrs, data, data_len, lock_nvram,
"EK Certificate");
if (ret == 0)
logit(self->logfile,
"Successfully created NVRAM area 0x%x for %s EK certificate.\n",
nvindex, keytype);
else
logerr(self->logfile,
"Could not create NVRAM area 0x%x for %s EK certificate.\n",
nvindex, keytype);
return ret;
}
static int swtpm_tpm2_write_platform_cert_nvram(struct swtpm *self, gboolean lock_nvram,
const unsigned char *data, size_t data_len)
{
uint32_t nvindex = TPM2_NV_INDEX_PLATFORMCERT;
uint32_t nvindexattrs = TPMA_NV_PLATFORMCREATE |
TPMA_NV_AUTHREAD |
TPMA_NV_OWNERREAD |
TPMA_NV_PPREAD |
TPMA_NV_PPWRITE |
TPMA_NV_NO_DA |
TPMA_NV_WRITEDEFINE;
int ret;
ret = swtpm_tpm2_write_nvram(self, nvindex, nvindexattrs, data, data_len, lock_nvram,
"Platform Certificate");
if (ret == 0)
logit(self->logfile,
"Successfully created NVRAM area 0x%x for platform certificate.\n", nvindex);
else
logerr(self->logfile,
"Could not create NVRAM area 0x%x for platform certificate.\n", nvindex);
return ret;
}
static const struct swtpm2_ops swtpm_tpm2_ops = {
.shutdown = swtpm_tpm2_shutdown,
.create_spk = swtpm_tpm2_create_spk,
.create_ek = swtpm_tpm2_create_ek,
.get_all_pcr_banks = swtpm_tpm2_get_all_pcr_banks,
.set_active_pcr_banks = swtpm_tpm2_set_active_pcr_banks,
.write_ek_cert_nvram = swtpm_tpm2_write_ek_cert_nvram,
.write_platform_cert_nvram = swtpm_tpm2_write_platform_cert_nvram,
};
/*
* TPM 1.2 support
*/
#define TPM_TAG_RQU_COMMAND 0x00c1
#define TPM_TAG_RQU_AUTH1_COMMAND 0x00c2
#define TPM_ORD_OIAP 0x0000000A
#define TPM_ORD_TAKE_OWNERSHIP 0x0000000D
#define TPM_ORD_PHYSICAL_ENABLE 0x0000006F
#define TPM_ORD_PHYSICAL_SET_DEACTIVATED 0x00000072
#define TPM_ORD_NV_DEFINE_SPACE 0x000000CC
#define TPM_ORD_NV_WRITE_VALUE 0x000000CD
#define TSC_ORD_PHYSICAL_PRESENCE 0x4000000A
#define TPM_ST_CLEAR 0x0001
#define TPM_PHYSICAL_PRESENCE_CMD_ENABLE 0x0020
#define TPM_PHYSICAL_PRESENCE_PRESENT 0x0008
#define TPM_ALG_RSA 0x00000001
#define TPM_KEY_STORAGE 0x0011
#define TPM_AUTH_ALWAYS 0x01
#define TPM_PID_OWNER 0x0005
#define TPM_ES_RSAESOAEP_SHA1_MGF1 0x0003
#define TPM_SS_NONE 0x0001
#define TPM_TAG_PCR_INFO_LONG 0x0006
#define TPM_TAG_NV_ATTRIBUTES 0x0017
#define TPM_TAG_NV_DATA_PUBLIC 0x0018
#define TPM_TAG_KEY12 0x0028
#define TPM_LOC_ZERO 0x01
#define TPM_LOC_ALL 0x1f
#define TPM_NV_INDEX_D_BIT 0x10000000
#define TPM_NV_INDEX_EKCERT 0xF000
#define TPM_NV_INDEX_PLATFORMCERT 0xF002
#define TPM_NV_INDEX_LOCK 0xFFFFFFFF
#define TPM_NV_PER_OWNERREAD 0x00020000
#define TPM_NV_PER_OWNERWRITE 0x00000002
#define TPM_ET_OWNER 0x02
#define TPM_ET_NV 0x0b
#define TPM_KH_EK 0x40000006
static int swtpm_tpm12_tsc_physicalpresence(struct swtpm *self, uint16_t physicalpresence)
{
struct tpm12_tsc_physicalpresence {
struct tpm_req_header hdr;
uint16_t pp;
} req = {
.hdr = TPM_REQ_HEADER_INITIALIZER(TPM_TAG_RQU_COMMAND, sizeof(req), TSC_ORD_PHYSICAL_PRESENCE),
.pp = htobe16(physicalpresence),
};
return transfer(self, &req, sizeof(req), "TSC_PhysicalPresence", FALSE, NULL, NULL);
}
static int swtpm_tpm12_physical_enable(struct swtpm *self)
{
struct tpm_req_header req = TPM_REQ_HEADER_INITIALIZER(TPM_TAG_RQU_COMMAND, sizeof(req), TPM_ORD_PHYSICAL_ENABLE);
return transfer(self, &req, sizeof(req), "TPM_PhysicalEnable", FALSE, NULL, NULL);
}
static int swtpm_tpm12_physical_set_deactivated(struct swtpm *self, uint8_t state)
{
struct tpm12_tsc_physical_set_deactivated {
struct tpm_req_header hdr;
uint8_t state;
} req = {
.hdr = TPM_REQ_HEADER_INITIALIZER(TPM_TAG_RQU_COMMAND, sizeof(req), TPM_ORD_PHYSICAL_SET_DEACTIVATED),
.state = state,
};
return transfer(self, &req, sizeof(req), "TSC_PhysicalSetDeactivated", FALSE, NULL, NULL);
}
/* Initialize the TPM1.2 */
static int swtpm_tpm12_run_swtpm_bios(struct swtpm *self)
{
if (swtpm_tpm12_tsc_physicalpresence(self, TPM_PHYSICAL_PRESENCE_CMD_ENABLE) ||
swtpm_tpm12_tsc_physicalpresence(self, TPM_PHYSICAL_PRESENCE_PRESENT) ||
swtpm_tpm12_physical_enable(self) ||
swtpm_tpm12_physical_set_deactivated(self, 0))
return 1;
return 0;
}
static int swptm_tpm12_create_endorsement_keypair(struct swtpm *self,
gchar **pubek, size_t *pubek_len)
{
unsigned char req[] = {
0x00, 0xc1, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x78, 0x38, 0xf0, 0x30, 0x81, 0x07, 0x2b,
0x0c, 0xa9, 0x10, 0x98, 0x08, 0xc0, 0x4B, 0x05, 0x11, 0xc9, 0x50, 0x23, 0x52, 0xc4, 0x00, 0x00,
0x00, 0x01, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00
};
unsigned char tpmresp[512];
size_t tpmresp_len = sizeof(tpmresp);
uint32_t length;
int ret;
ret = transfer(self, &req, sizeof(req), "TPM_CreateEndorsementKeyPair", FALSE, &tpmresp, &tpmresp_len);
if (ret != 0)
return 1;
if (tpmresp_len < 34 + sizeof(length))
goto err_too_short;
memcpy(&length, &tpmresp[34], sizeof(length));
length = be32toh(length);
if (length != 256) {
logerr(self->logfile, "Offset to EK Public key is wrong.\n");
return 1;
}
*pubek_len = 256;
if (tpmresp_len < 38 + *pubek_len)
goto err_too_short;
*pubek = g_malloc(256);
memcpy(*pubek, &tpmresp[38], *pubek_len);
return 0;
err_too_short:
logerr(self->logfile, "Response from TPM_CreateEndorsementKeyPair is too short!\n");
return 1;
}
/* Create an OIAP session */
static int swtpm_tpm12_oiap(struct swtpm *self, uint32_t *authhandle, unsigned char nonce_even[SHA_DIGEST_LENGTH])
{
struct tpm_req_header req = TPM_REQ_HEADER_INITIALIZER(TPM_TAG_RQU_COMMAND, sizeof(req), TPM_ORD_OIAP);
unsigned char tpmresp[64];
size_t tpmresp_len = sizeof(tpmresp);
int ret;
ret = transfer(self, &req, sizeof(req), "TPM_OIAP", FALSE, &tpmresp, &tpmresp_len);
if (ret != 0)
return ret;
if (tpmresp_len < 10 + sizeof(*authhandle) || tpmresp_len < 14 + SHA_DIGEST_LENGTH)
goto err_too_short;
memcpy(authhandle, &tpmresp[10], sizeof(*authhandle));
*authhandle = be32toh(*authhandle);
memcpy(nonce_even, &tpmresp[14], SHA_DIGEST_LENGTH);
return 0;
err_too_short:
logerr(self->logfile, "Response from TPM_OIAP is too short!\n");
return 1;
}
static int swtpm_tpm12_take_ownership(struct swtpm *self, const unsigned char ownerpass_digest[SHA_DIGEST_LENGTH],
const unsigned char srkpass_digest[SHA_DIGEST_LENGTH],
const unsigned char *pubek, size_t pubek_len)
{
struct tpm_req_header hdr = TPM_REQ_HEADER_INITIALIZER(TPM_TAG_RQU_AUTH1_COMMAND, 0, TPM_ORD_TAKE_OWNERSHIP);
EVP_PKEY *pkey = NULL;
EVP_PKEY_CTX *ctx = NULL;
BIGNUM *exp = BN_new();
BIGNUM *mod = NULL;
#if OPENSSL_VERSION_NUMBER < 0x30000000L
RSA *rsakey = RSA_new();
#endif
int ret = 1;
const EVP_MD *sha1 = EVP_sha1();
g_autofree unsigned char *enc_owner_auth = g_malloc(pubek_len);
size_t enc_owner_auth_len = pubek_len;
g_autofree unsigned char *enc_srk_auth = g_malloc(pubek_len);
size_t enc_srk_auth_len = pubek_len;
uint32_t auth_handle;
unsigned char nonce_even[SHA_DIGEST_LENGTH];
unsigned char nonce_odd[SHA_DIGEST_LENGTH] = {1, 2, 3, 4, 5, 6, };
g_autofree unsigned char *tpm_rsa_key_parms = NULL;
ssize_t tpm_rsa_key_parms_len;
g_autofree unsigned char *tpm_key_parms = NULL;
ssize_t tpm_key_parms_len;
g_autofree unsigned char *tpm_key12 = NULL;
ssize_t tpm_key12_len;
g_autofree unsigned char *in_auth_setup_params = NULL;
ssize_t in_auth_setup_params_len;
g_autofree unsigned char *macinput = NULL;
ssize_t macinput_len;
unsigned char in_param_digest[SHA_DIGEST_LENGTH];
unsigned char owner_auth[SHA_DIGEST_LENGTH];
unsigned int owner_auth_len = sizeof(owner_auth);
uint8_t continue_auth_session = 0;
unsigned char req[1024];
ssize_t req_len, len;
struct tpm_req_header *trh;
mod = BN_bin2bn((const unsigned char *)pubek, pubek_len, NULL);
if (exp == NULL || mod == NULL ||
BN_hex2bn(&exp, "10001") == 0) {
logerr(self->logfile, "Could not create public RSA key!\n");
goto error_free_bn;
}
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
ctx = EVP_PKEY_CTX_new_from_name(NULL, "rsa", NULL);
if (ctx != NULL) {
OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
OSSL_PARAM *params;
if (bld == NULL ||
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, exp) != 1 ||
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, mod) != 1 ||
(params = OSSL_PARAM_BLD_to_param(bld)) == NULL) {
OSSL_PARAM_BLD_free(bld);
goto error_free_bn;
}
OSSL_PARAM_BLD_free(bld);
if (EVP_PKEY_fromdata_init(ctx) != 1 ||
EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) != 1) {
logerr(self->logfile, "Could not set pkey parameters!\n");
OSSL_PARAM_free(params);
goto error_free_bn;
}
OSSL_PARAM_free(params);
EVP_PKEY_CTX_free(ctx);
} else {
logerr(self->logfile, "Could not create key creation context!\n");
goto error_free_bn;
}
ctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL);
if (ctx == NULL)
goto error_free_bn;
#else
pkey = EVP_PKEY_new();
if (pkey == NULL) {
logerr(self->logfile, "Could not allocate pkey!\n");
goto error_free_bn;
}
# if OPENSSL_VERSION_NUMBER < 0x10100000
rsakey->n = mod;
rsakey->e = exp;
# else
if (RSA_set0_key(rsakey, mod, exp, NULL) != 1) {
logerr(self->logfile, "Could not create public RSA key!\n");
goto error_free_bn;
}
# endif
if (EVP_PKEY_assign_RSA(pkey, rsakey) != 1) {
logerr(self->logfile, "Could not create public RSA key!\n");
goto error_free_pkey_and_rsa;
}
ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (ctx == NULL)
goto error_free_pkey;
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
if (EVP_PKEY_encrypt_init(ctx) < 1 ||
EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) < 1 ||
EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, sha1) < 1 ||
EVP_PKEY_CTX_set_rsa_oaep_md(ctx, sha1) < 1 ||
EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, g_strdup("TCPA"), 4) < 1 ||
EVP_PKEY_encrypt(ctx, enc_owner_auth, &enc_owner_auth_len,
ownerpass_digest, SHA_DIGEST_LENGTH) < 1||
EVP_PKEY_encrypt(ctx, enc_srk_auth, &enc_srk_auth_len,
srkpass_digest, SHA_DIGEST_LENGTH) < 1) {
logerr(self->logfile, "Internal error in %s: encryption failed\n", __func__);
goto error;
}
ret = swtpm_tpm12_oiap(self, &auth_handle, nonce_even);
if (ret != 0)
goto error;
tpm_rsa_key_parms_len = memconcat(&tpm_rsa_key_parms,
(unsigned char[]){
AS4BE(2048), AS4BE(2), AS4BE(0)
}, (size_t)12,
NULL);
if (tpm_rsa_key_parms_len < 0) {
logerr(self->logfile, "Internal error in %s: out of memory\n");
goto error;
}
tpm_key_parms_len = memconcat(&tpm_key_parms,
(unsigned char[]){
AS4BE(TPM_ALG_RSA),
AS2BE(TPM_ES_RSAESOAEP_SHA1_MGF1),
AS2BE(TPM_SS_NONE),
AS4BE(tpm_rsa_key_parms_len)}, (size_t)12,
tpm_rsa_key_parms, tpm_rsa_key_parms_len,
NULL);
if (tpm_key_parms_len < 0) {
logerr(self->logfile, "Internal error in %s: out of memory\n");
goto error;
}
tpm_key12_len = memconcat(&tpm_key12,
(unsigned char[]){
AS2BE(TPM_TAG_KEY12), AS2BE(0),
AS2BE(TPM_KEY_STORAGE), AS4BE(0), TPM_AUTH_ALWAYS
}, (size_t)11,
tpm_key_parms, tpm_key_parms_len,
(unsigned char[]){AS4BE(0), AS4BE(0), AS4BE(0)}, (size_t)12,
NULL);
if (tpm_key12_len < 0) {
logerr(self->logfile, "Internal error in %s: out of memory\n");
goto error;
}
req_len = concat(req, sizeof(req),
&hdr, sizeof(hdr),
(unsigned char[]){AS2BE(TPM_PID_OWNER), AS4BE(enc_owner_auth_len)}, (size_t)6,
enc_owner_auth, enc_owner_auth_len,
(unsigned char[]){AS4BE(enc_srk_auth_len)}, (size_t)4,
enc_srk_auth, enc_srk_auth_len,
tpm_key12, tpm_key12_len,
NULL);
if (req_len < 0) {
logerr(self->logfile, "Internal error in %s: req is too small\n");
goto error;
}
SHA1(&req[6], req_len - 6, in_param_digest);
in_auth_setup_params_len = memconcat(&in_auth_setup_params,
nonce_even, sizeof(nonce_even),
nonce_odd, sizeof(nonce_odd),
&continue_auth_session, (size_t)1,
NULL);
if (in_auth_setup_params_len < 0) {
logerr(self->logfile, "Internal error in %s: out of memory\n");
goto error;
}
macinput_len = memconcat(&macinput,
in_param_digest, sizeof(in_param_digest),
in_auth_setup_params, in_auth_setup_params_len,
NULL);
if (macinput_len < 0) {
logerr(self->logfile, "Internal error in %s: out of memory\n");
goto error;
}
HMAC(sha1, ownerpass_digest, SHA_DIGEST_LENGTH, macinput, macinput_len,
owner_auth, &owner_auth_len);
len = concat(&req[req_len], sizeof(req) - req_len,
(unsigned char[]){AS4BE(auth_handle)}, (size_t)4,
nonce_odd, sizeof(nonce_odd),
&continue_auth_session, (size_t)1,
owner_auth, owner_auth_len,
NULL);
if (len < 0) {
logerr(self->logfile, "Internal error in %s: req is too small\n");
goto error;
}
req_len += len;
trh = (struct tpm_req_header *)req; /* old gcc type-punned pointer */
trh->size = htobe32(req_len);
ret = transfer(self, req, req_len, "TPM_TakeOwnership", FALSE, NULL, 0);
error:
EVP_PKEY_free(pkey);
EVP_PKEY_CTX_free(ctx);
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
BN_free(exp);
BN_free(mod);
#endif
return ret;
error_free_bn:
BN_free(exp);
BN_free(mod);
#if OPENSSL_VERSION_NUMBER < 0x30000000L
error_free_pkey_and_rsa:
RSA_free(rsakey);
error_free_pkey:
#else
EVP_PKEY_CTX_free(ctx);
#endif
EVP_PKEY_free(pkey);
return 1;
}
static int swtpm_tpm12_nv_define_space(struct swtpm *self, uint32_t nvindex,
uint32_t nvindexattrs, size_t size)
{
struct tpm_req_header hdr = TPM_REQ_HEADER_INITIALIZER(TPM_TAG_RQU_COMMAND, 0, TPM_ORD_NV_DEFINE_SPACE);
g_autofree unsigned char *pcr_info_short = NULL;
ssize_t pcr_info_short_len;
g_autofree unsigned char *nv_data_public = NULL;
ssize_t nv_data_public_len;
g_autofree unsigned char *req = NULL;
ssize_t req_len;
unsigned char zeroes[SHA_DIGEST_LENGTH] = {0, };
pcr_info_short_len = memconcat(&pcr_info_short,
(unsigned char[]){AS2BE(3), 0, 0, 0, TPM_LOC_ALL}, (size_t)6,
zeroes, sizeof(zeroes),
NULL);
if (pcr_info_short_len < 0) {
logerr(self->logfile, "Internal error in %s: out of memory\n");
return 1;
}
nv_data_public_len = memconcat(&nv_data_public,
(unsigned char[]){
AS2BE(TPM_TAG_NV_DATA_PUBLIC), AS4BE(nvindex)
}, (size_t)6,
pcr_info_short, pcr_info_short_len,
pcr_info_short, pcr_info_short_len,
(unsigned char[]){
AS2BE(TPM_TAG_NV_ATTRIBUTES), AS4BE(nvindexattrs),
0, 0, 0, AS4BE(size)
}, (size_t)13,
NULL);
if (nv_data_public_len < 0) {
logerr(self->logfile, "Internal error in %s: out of memory\n");
return 1;
}
req_len = memconcat(&req,
&hdr, sizeof(hdr),
nv_data_public, nv_data_public_len,
zeroes, sizeof(zeroes),
NULL);
if (req_len < 0) {
logerr(self->logfile, "Internal error in %s: out of memory\n");
return 1;
}
((struct tpm_req_header *)req)->size = htobe32(req_len);
return transfer(self, req, req_len, "TPM_NV_DefineSpace", FALSE, NULL, 0);
}
static int swtpm_tpm12_nv_write_value(struct swtpm *self, uint32_t nvindex,
const unsigned char *data, size_t data_len)
{
struct tpm_req_header hdr = TPM_REQ_HEADER_INITIALIZER(TPM_TAG_RQU_COMMAND, 0, TPM_ORD_NV_WRITE_VALUE);
g_autofree unsigned char *req = NULL;
ssize_t req_len;
req_len = memconcat(&req,
&hdr, sizeof(hdr),
(unsigned char[]){AS4BE(nvindex), AS4BE(0), AS4BE(data_len)}, (size_t)12,
data, data_len,
NULL);
if (req_len < 0) {
logerr(self->logfile, "Internal error in %s: out of memory\n");
return 1;
}
((struct tpm_req_header *)req)->size = htobe32(req_len);
return transfer(self, req, req_len, "TPM_NV_DefineSpace", FALSE, NULL, 0);
}
/* Write the EK Certificate into NVRAM */
static int swtpm_tpm12_write_ek_cert_nvram(struct swtpm *self,
const unsigned char *data, size_t data_len)
{
uint32_t nvindex = TPM_NV_INDEX_EKCERT | TPM_NV_INDEX_D_BIT;
int ret = swtpm_tpm12_nv_define_space(self, nvindex,
TPM_NV_PER_OWNERREAD | TPM_NV_PER_OWNERWRITE, data_len);
if (ret != 0)
return 1;
ret = swtpm_tpm12_nv_write_value(self, nvindex, data, data_len);
if (ret != 0)
return 1;
return 0;
}
/* Write the Platform Certificate into NVRAM */
static int swtpm_tpm12_write_platform_cert_nvram(struct swtpm *self,
const unsigned char *data, size_t data_len)
{
uint32_t nvindex = TPM_NV_INDEX_PLATFORMCERT | TPM_NV_INDEX_D_BIT;
int ret = swtpm_tpm12_nv_define_space(self, nvindex,
TPM_NV_PER_OWNERREAD | TPM_NV_PER_OWNERWRITE, data_len);
if (ret != 0)
return 1;
ret = swtpm_tpm12_nv_write_value(self, nvindex, data, data_len);
if (ret != 0)
return 1;
return 0;
}
static int swtpm_tpm12_nv_lock(struct swtpm *self)
{
return swtpm_tpm12_nv_define_space(self, TPM_NV_INDEX_LOCK, 0, 0);
}
static const struct swtpm12_ops swtpm_tpm12_ops = {
.run_swtpm_bios = swtpm_tpm12_run_swtpm_bios,
.create_endorsement_key_pair = swptm_tpm12_create_endorsement_keypair,
.take_ownership = swtpm_tpm12_take_ownership,
.write_ek_cert_nvram = swtpm_tpm12_write_ek_cert_nvram,
.write_platform_cert_nvram = swtpm_tpm12_write_platform_cert_nvram,
.nv_lock = swtpm_tpm12_nv_lock,
};
static void swtpm_init(struct swtpm *swtpm,
gchar **swtpm_exec_l, const gchar *state_path,
const gchar *keyopts, const gchar *logfile,
int *fds_to_pass, size_t n_fds_to_pass,
gboolean is_tpm2)
{
swtpm->cops = &swtpm_cops;
swtpm->swtpm_exec_l = swtpm_exec_l;
swtpm->state_path = state_path;
swtpm->keyopts = keyopts;
swtpm->logfile = logfile;
swtpm->fds_to_pass = fds_to_pass;
swtpm->n_fds_to_pass = n_fds_to_pass;
swtpm->is_tpm2 = is_tpm2;
swtpm->pid = -1;
swtpm->ctrl_fds[0] = swtpm->ctrl_fds[1] = -1;
swtpm->data_fds[0] = swtpm->data_fds[1] = -1;
}
struct swtpm12 *swtpm12_new(gchar **swtpm_exec_l, const gchar *state_path,
const gchar *keyopts, const gchar *logfile,
int *fds_to_pass, size_t n_fds_to_pass)
{
struct swtpm12 *swtpm12 = g_malloc0(sizeof(struct swtpm12));
swtpm_init(&swtpm12->swtpm, swtpm_exec_l, state_path, keyopts, logfile,
fds_to_pass, n_fds_to_pass, FALSE);
swtpm12->ops = &swtpm_tpm12_ops;
return swtpm12;
}
struct swtpm2 *swtpm2_new(gchar **swtpm_exec_l, const gchar *state_path,
const gchar *keyopts, const gchar *logfile,
int *fds_to_pass, size_t n_fds_to_pass)
{
struct swtpm2 *swtpm2 = g_malloc0(sizeof(struct swtpm2));
swtpm_init(&swtpm2->swtpm, swtpm_exec_l, state_path, keyopts, logfile,
fds_to_pass, n_fds_to_pass, TRUE);
swtpm2->ops = &swtpm_tpm2_ops;
return swtpm2;
}
void swtpm_free(struct swtpm *swtpm) {
if (!swtpm)
return;
g_free(swtpm);
}