wolfBoot/tools/tpm/policy_sign.c

411 lines
12 KiB
C

/* policy_sign.c
*
* Copyright (C) 2006-2023 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/* Standalone tool to create a singed policy from a pcr digest. Based on
* wolfTPM/example/pcr/policy_sign.c
*/
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/ecc.h>
#include <wolfssl/wolfcrypt/hash.h>
#include <wolftpm/tpm2_wrap.h>
#include "tpm.h"
/* Default PCR (test) */
#define DEFAULT_PCR 16
/* Prefer SHA2-256 for PCR's, and all TPM 2.0 devices support it */
#define USE_PCR_ALG TPM_ALG_SHA256
static void usage(void)
{
printf("Expected usage:\n");
printf("./examples/pcr/policy_sign [-ecc256/-ecc384] [-key=pem/der] [-pcr=] [-pcrdigest=] [-policydigest=][-outpolicy=]\n");
printf("* -ecc256/-ecc384: Key type (currently only ECC) (default SECP256R1)\n");
printf("* -key=keyfile: Private key to sign PCR policy (PEM or DER) (default wolfboot_signing_private_key.der)\n");
printf("* -pcr=index: PCR index < 24 (multiple can be supplied) (default %d)\n", DEFAULT_PCR);
printf("* -pcrdigest=hexstr: PCR Digest (default=Read actual PCR's)\n");
printf("* -policydigest=hexstr: Policy Digest (policy based on PCR digest and PCR(s)\n");
printf("* -outpolicy=file: Signature file (default policy.bin.sig)\n");
printf("Example:\n");
printf("\t./tools/tpm/policy_sign -ecc256 -pcr=0 -pcrdigest=eca4e8eda468b8667244ae972b8240d3244ea72341b2bf2383e79c66643bbecc\n");
}
#ifndef WC_MAX_ENCODED_DIG_ASN_SZ
#define WC_MAX_ENCODED_DIG_ASN_SZ 9 /* enum(bit or octet) + length(4) */
#endif
static int loadFile(const char* fname, byte** buf, size_t* bufLen)
{
int ret = 0;
ssize_t fileSz, readLen;
FILE *fp;
if (fname == NULL || buf == NULL || bufLen == NULL)
return BAD_FUNC_ARG;
/* open file (read-only binary) */
fp = fopen(fname, "rb");
if (fp == NULL) {
fprintf(stderr, "Error loading %s\n", fname);
return BUFFER_E;
}
fseek(fp, 0, SEEK_END);
fileSz = ftell(fp);
rewind(fp);
if (fileSz > 0) {
if (*buf == NULL) {
*buf = (byte*)XMALLOC(fileSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (*buf == NULL)
ret = MEMORY_E;
}
else if (*buf != NULL && fileSz > (ssize_t)*bufLen) {
ret = BUFFER_E;
}
*bufLen = (size_t)fileSz;
if (ret == 0) {
readLen = fread(*buf, 1, *bufLen, fp);
ret = (readLen == (ssize_t)*bufLen) ? 0 : -1;
}
}
else {
ret = BUFFER_E;
}
fclose(fp);
return ret;
}
/* Function to sign policy with external key */
static int PolicySign(int alg, const char* keyFile, byte* hash, word32 hashSz,
byte* sig, word32* sigSz)
{
int rc = 0;
byte* buf = NULL;
size_t bufSz = 0;
WC_RNG rng;
union {
#ifndef NO_RSA
RsaKey rsa;
#endif
#ifdef HAVE_ECC
ecc_key ecc;
#endif
} key;
memset(&key, 0, sizeof(key));
memset(&rng, 0, sizeof(rng));
rc = wc_InitRng(&rng);
if (rc != 0) {
printf("wc_InitRng failed\n");
return rc;
}
rc = loadFile(keyFile, &buf, &bufSz);
if (rc == 0 && (alg == ECC_SECP256R1 || alg == ECC_SECP384R1)) {
word32 keySz = 32;
if (alg == ECC_SECP384R1)
keySz = 48;
rc = wc_ecc_init(&key.ecc);
if (rc == 0) {
rc = wc_ecc_import_unsigned(&key.ecc, buf,
(buf) + keySz, buf + (keySz*2), alg);
if (rc == 0) {
mp_int r, s;
rc = mp_init_multi(&r, &s, NULL, NULL, NULL, NULL);
if (rc == 0) {
rc = wc_ecc_sign_hash_ex(hash, hashSz, &rng, &key.ecc, &r, &s);
}
if (rc == 0) {
word32 rSz, sSz;
*sigSz = keySz * 2;
memset(sig, 0, *sigSz);
/* export sign r/s - zero pad to key size */
rSz = mp_unsigned_bin_size(&r);
mp_to_unsigned_bin(&r, &sig[keySz - rSz]);
sSz = mp_unsigned_bin_size(&s);
mp_to_unsigned_bin(&s, &sig[keySz + (keySz - sSz)]);
mp_clear(&r);
mp_clear(&s);
}
}
wc_ecc_free(&key.ecc);
}
}
else {
rc = BAD_FUNC_ARG;
}
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
wc_FreeRng(&rng);
if (rc != 0) {
printf("Policy Sign with external key failed %d\n", rc);
}
return rc;
}
static signed char hexCharToByte(signed char ch)
{
signed char ret = (signed char)ch;
if (ret >= '0' && ret <= '9')
ret -= '0';
else if (ret >= 'A' && ret <= 'F')
ret -= 'A' - 10;
else if (ret >= 'a' && ret <= 'f')
ret -= 'a' - 10;
else
ret = -1; /* error case - return code must be signed */
return ret;
}
static int hexToByte(const char *hex, unsigned char *output, unsigned long sz)
{
int outSz = 0;
word32 i;
for (i = 0; i < sz; i+=2) {
signed char ch1, ch2;
ch1 = hexCharToByte(hex[i]);
ch2 = hexCharToByte(hex[i+1]);
if ((ch1 < 0) || (ch2 < 0)) {
return -1;
}
output[outSz++] = (unsigned char)((ch1 << 4) + ch2);
}
return outSz;
}
static void printHexString(const unsigned char* bin, unsigned long sz,
unsigned long maxLine)
{
unsigned long i;
printf("\t");
for (i = 0; i < sz; i++) {
printf("%02x", bin[i]);
if (maxLine > 0 && ((i+1) % maxLine) == 0 && i+1 != sz)
printf("\n\t");
}
printf("\n");
}
static int writeBin(const char* filename, const byte *buf, word32 bufSz)
{
int rc = TPM_RC_FAILURE;
if (filename == NULL || buf == NULL)
return BAD_FUNC_ARG;
FILE *fp = NULL;
size_t fileSz = 0;
fp = fopen(filename, "wt");
if (fp != NULL) {
fileSz = fwrite(buf, 1, bufSz, fp);
/* sanity check */
if (fileSz == (word32)bufSz) {
rc = TPM_RC_SUCCESS;
}
fclose(fp);
}
return rc;
}
int policy_sign(int argc, char *argv[])
{
int i;
int rc = -1;
TPM_ALG_ID pcrAlg = USE_PCR_ALG;
int alg = ECC_SECP256R1;
byte pcrArray[PCR_SELECT_MAX*2];
word32 pcrArraySz = 0;
const char* keyFile = "wolfboot_signing_private_key.der";
const char* outPolicyFile = "policy.bin.sig";
byte pcrDigest[WC_MAX_DIGEST_SIZE];
word32 pcrDigestSz = 0;
byte digest[WC_MAX_DIGEST_SIZE];
word32 digestSz = 0;
byte policy[512 + sizeof(word32)];
byte sig[512]; /* up to 4096-bit key */
word32 sigSz = 0;
byte* policyRef = NULL; /* optional nonce */
word32 policyRefSz = 0;
word32 pcrMask;
if (argc >= 2) {
if (XSTRCMP(argv[1], "-?") == 0 ||
XSTRCMP(argv[1], "-h") == 0 ||
XSTRCMP(argv[1], "--help") == 0) {
usage();
return 0;
}
}
while (argc > 1) {
if (XSTRCMP(argv[argc-1], "-ecc256") == 0) {
alg = ECC_SECP256R1;
}
else if (XSTRCMP(argv[argc-1], "-ecc384") == 0) {
alg = ECC_SECP384R1;
}
else if (strncmp(argv[argc-1], "-pcr=", strlen("-pcr=")) == 0) {
const char* pcrStr = argv[argc-1] + strlen("-pcr=");
byte pcrIndex = (byte)XATOI(pcrStr);
if (pcrIndex > PCR_LAST) {
printf("PCR index is out of range (0-23)\n");
usage();
return 0;
}
pcrArray[pcrArraySz] = pcrIndex;
pcrArraySz++;
}
else if (strncmp(argv[argc-1], "-pcrdigest=", strlen("-pcrdigest=")) == 0) {
const char* hashHexStr = argv[argc-1] + strlen("-pcrdigest=");
int hashHexStrlen = (int)strlen(hashHexStr);
if (hashHexStrlen > (int)sizeof(pcrDigest)*2+1)
pcrDigestSz = -1;
else
pcrDigestSz = hexToByte(hashHexStr, pcrDigest, hashHexStrlen);
if (pcrDigestSz <= 0) {
fprintf(stderr, "Invalid PCR hash length\n");
usage();
return -1;
}
}
else if (strncmp(argv[argc-1], "-policydigest=", strlen("-policydigest=")) == 0) {
const char* hashHexStr = argv[argc-1] + strlen("-policydigest=");
int hashHexStrlen = (int)strlen(hashHexStr);
if (hashHexStrlen > (int)sizeof(digest)*2+1)
digestSz = -1;
else
digestSz = hexToByte(hashHexStr, digest, hashHexStrlen);
if (digestSz <= 0) {
fprintf(stderr, "Invalid Policy Digest hash length\n");
usage();
return -1;
}
}
else if (strncmp(argv[argc-1], "-key=",
strlen("-key=")) == 0) {
keyFile = argv[argc-1] + strlen("-key=");
}
else if (strncmp(argv[argc-1], "-outpolicy=",
strlen("-outpolicy=")) == 0) {
outPolicyFile = argv[argc-1] + strlen("-outpolicy=");
}
else {
printf("Warning: Unrecognized option: %s\n", argv[argc-1]);
}
argc--;
}
printf("Sign PCR Policy Tool\n");
if (pcrArraySz == 0) {
pcrArray[pcrArraySz] = DEFAULT_PCR;
pcrArraySz++;
}
printf("Signing Algorithm: %s\n",
(alg == ECC_SECP256R1) ? "ECC256" :
(alg == ECC_SECP384R1) ? "ECC384" :
"Unknown"
);
printf("PCR Index(s) (%s): ", TPM2_GetAlgName(pcrAlg));
for (i = 0; i < (int)pcrArraySz; i++) {
printf("%d ", pcrArray[i]);
}
printf("\n");
/* Policy Signing Key */
if (keyFile == NULL) {
printf("Need private key to sign the policy\n");
goto exit;
}
else {
printf("Policy Signing Key: %s\n", keyFile);
}
/* PCR Hash - Use provided PCR digest or Policy digest */
if (pcrDigestSz == 0 && digestSz == 0) {
printf("Error: Must supply either PCR or Policy digest!\n");
usage();
return -1;
}
printf("PCR Digest (%d bytes):\n", pcrDigestSz);
printHexString(pcrDigest, pcrDigestSz, pcrDigestSz);
if (digestSz == 0) {
/* If not supplied, build PCR Policy to Sign */
memset(digest, 0, sizeof(digest));
digestSz = TPM2_GetHashDigestSize(pcrAlg);
rc = wolfTPM2_PolicyPCRMake(pcrAlg, pcrArray, pcrArraySz,
pcrDigest, pcrDigestSz, digest, &digestSz);
if (rc != 0) goto exit;
printf("PCR Policy Digest (%d bytes):\n", digestSz);
printHexString(digest, digestSz, digestSz);
/* Add policyRef (if blank just re-hash) */
rc = wolfTPM2_PolicyRefMake(pcrAlg, digest, &digestSz, policyRef, policyRefSz);
if (rc != 0) goto exit;
}
printf("PCR Policy Digest (w/PolicyRef) (%d bytes):\n", digestSz);
printHexString(digest, digestSz, digestSz);
/* Sign the PCR policy (use private key provided or do externally) */
rc = PolicySign(alg, keyFile, digest, digestSz, sig, &sigSz);
if (rc == 0) {
pcrMask = 0;
for (i = 0; i < (int)pcrArraySz; i++)
pcrMask |= (1 << pcrArray[i]);
memcpy(policy, &pcrMask, sizeof(pcrMask));
memcpy(policy + sizeof(pcrMask), sig, sigSz);
printf("PCR Mask (0x%x) and Policy Signature (%d bytes):\n",
(int)pcrMask, (int)(sigSz + sizeof(pcrMask)));
printHexString(policy, sizeof(pcrMask), 0);
printHexString(policy + sizeof(pcrMask), sigSz, 32);
rc = writeBin(outPolicyFile, policy, sigSz+sizeof(pcrMask));
if (rc == 0) {
printf("Wrote PCR Mask + Signature (%d bytes) to %s\n",
(int)(sigSz + sizeof(pcrMask)), outPolicyFile);
}
}
exit:
if (rc != 0) {
printf("Failure 0x%x: %s\n", rc, wolfTPM2_GetRCString(rc));
}
return rc;
}
int main(int argc, char *argv[])
{
return policy_sign(argc, argv);
}