mirror of https://github.com/wolfSSL/wolfBoot.git
411 lines
12 KiB
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);
|
|
}
|