/* 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 #include #include #include #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); }