From ccd28fda677ed0f4ed0a15ffa173a1f163249ef5 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Tue, 12 Sep 2023 15:27:05 -0600 Subject: [PATCH] PKCS#7: add example to verify existing SignedData file --- .gitignore | 2 + pkcs7/README.md | 44 +++++ pkcs7/signedData-verifyFile.c | 316 ++++++++++++++++++++++++++++++++++ 3 files changed, 362 insertions(+) create mode 100644 pkcs7/signedData-verifyFile.c diff --git a/.gitignore b/.gitignore index 1519913b..0812e858 100644 --- a/.gitignore +++ b/.gitignore @@ -185,6 +185,8 @@ pkcs7/signedData-CompressedFirmwarePkgData pkcs7/signedData-EncryptedCompressedFirmwarePkgData pkcs7/signedData-EncryptedFirmwareCB pkcs7/signedData-p7b +pkcs7/signedData-cryptocb +pkcs7/signedData-verifyFile *.dSYM certmanager/certloadverifybuffer diff --git a/pkcs7/README.md b/pkcs7/README.md index cf03c4de..a422ae88 100644 --- a/pkcs7/README.md +++ b/pkcs7/README.md @@ -572,6 +572,50 @@ Successfully encoded Signed Encrypted Compressed FirmwarePkgData (signedEncrypte Successfully extracted and verified bundle contents ``` +### Verify SignedData bundle from existing file + +Example file: `signedData-verifyFile.c` + +This example allows the caller to pass in an existing PKCS#7/CMS bundle +in DER format, then attempts to verify the SignedData bundle using wolfCrypt. + +Usage for this example is: + +``` +signedData-verifyFile X.X.X (NOTE: All files relative to current directory) +-? Help, print this usage +-b PKCS#7/CMS bundle to verify (DER format) +-c Detached content, if needed +``` + +If wolfSSL has been configured and compiled with debug support, the bytes +of the bundle will be printed out to the terminal window. For example to verify +the bundle created by the `signedData` example: + +``` +./signedData-verifyFile -b signedData_noattrs.der +wolfCrypt PKCS#7/CMS SignedData verification example + +Read 1982 bytes from signedData_noattrs.der +Decoded content size is 11 bytes +Successfully verified SignedData bundle! +``` + +To verify SignedData bundles that represent a detached signature (which does +not include content in the bundle), use the `-c` option to pass in a file to +be used as the content. For example, to verify the bundle created by the +example application `signedData-DetachedSignature`: + +``` +./signedData-verifyFile -b signedData_detached_attrs.der -c content.txt +wolfCrypt PKCS#7/CMS SignedData verification example + +Read 1987 bytes from signedData_detached_attrs.der +Read 11 bytes from content file: content.txt +Decoded content size is 11 bytes +Successfully verified SignedData bundle! +``` + ### Converting P7B Certificate Bundle to PEM using PKCS7 SignedData API Build wolfssl using: `./configure --enable-pkcs7 CFLAGS="-DWOLFSSL_DER_TO_PEM"` diff --git a/pkcs7/signedData-verifyFile.c b/pkcs7/signedData-verifyFile.c new file mode 100644 index 00000000..38430dbe --- /dev/null +++ b/pkcs7/signedData-verifyFile.c @@ -0,0 +1,316 @@ +/* signedData-verifyFile.c + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL 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 2 of the License, or + * (at your option) any later version. + * + * wolfSSL 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-1301, USA + */ +#include +#include +#include +#include +#include + +#include +#include + +/* Max PEM cert size, used to allocate memory below. Change as needed + * for your expected PEM certificate sizes */ +#define MAX_PEM_CERT_SIZE 4096 + +/* The index of the command line option */ +int myoptind = 0; + +/* The current command line option */ +char* myoptarg = NULL; + +/** + * Verify PKCS#7/CMS bundle contained in bundleBytes, of size bundleSz. + * + * Return 0 on success, negative on error + */ +static int VerifySignedData(byte* bundleBytes, word32 bundleSz, + byte* detachedContent, word32 detachedContentSz) +{ + int ret, i; + PKCS7* pkcs7 = NULL; + byte* singleCertDer; /* tmp ptr to one DER cert in decoded PKCS7 */ + word32 singleCertDerSz; /* tmp size of one DER cert in decoded PKCS7 */ +#ifdef WOLFSSL_DER_TO_PEM + byte* singleCertPem; + word32 singleCertPemSz; +#endif + (void)singleCertDer; + + if (bundleBytes == NULL || bundleSz == 0) { + return BAD_FUNC_ARG; + } + + pkcs7 = wc_PKCS7_New(NULL, INVALID_DEVID); + if (pkcs7 == NULL) { + printf("wc_PKCS7_New failed\n"); + return -1; + } + + if (detachedContent != NULL) { + pkcs7->content = detachedContent; + pkcs7->contentSz = detachedContentSz; + } + + /* Decode signedData, returns size */ + ret = wc_PKCS7_VerifySignedData(pkcs7, bundleBytes, bundleSz); + if (ret < 0) { + wc_PKCS7_Free(pkcs7); + return ret; + } + +#ifdef DEBUG_WOLFSSL + printf("Decoded content (%d bytes):\n", pkcs7->contentSz); + WOLFSSL_BUFFER(pkcs7->content, pkcs7->contentSz); +#else + printf("Decoded content size is %d bytes\n", pkcs7->contentSz); +#endif + + /* wc_PKCS7_VerifySignedData() decodes input PKCS#7 and stores + * decoded DER certificates into pkcs7->cert[]. Sizes of each cert entry + * is stored in the separate pkcs7->certSz[] array. Max size of each + * of the arrays is MAX_PKCS7_CERTS. Array memory is owned by wolfCrypt + * PKCS7 and freed when calling wc_PKCS7_Free(). */ + + for (i = 0; i < MAX_PKCS7_CERTS; i++) { + if (pkcs7->certSz[i] == 0) { + /* reached end of valid certs in array */ + break; + } + + singleCertDer = pkcs7->cert[i]; + singleCertDerSz = pkcs7->certSz[i]; + printf("CERT [%d] size = %d bytes\n", i, singleCertDerSz); + + #ifdef WOLFSSL_DER_TO_PEM + /* allocate array for PEM */ + singleCertPem = (byte*)XMALLOC(MAX_PEM_CERT_SIZE, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (singleCertPem == NULL) { + printf("Error allocating memory for PEM\n"); + break; + } + singleCertPemSz = MAX_PEM_CERT_SIZE; + XMEMSET(singleCertPem, 0, singleCertPemSz); + + /* convert DER to PEM */ + singleCertPemSz = wc_DerToPem(singleCertDer, singleCertDerSz, + singleCertPem, singleCertPemSz, + CERT_TYPE); + if (singleCertPem < 0) { + printf("Error converting DER to PEM, ret = %d\n", ret); + XFREE(singleCertPem, NULL, DYNAMIC_TYPE_TMP_BUFFER); + break; + } + printf("converted DER to PEM, pemSz = %d\n", singleCertPemSz); + printf("CERT [%d] PEM:\n", i); + + /* print PEM to terminal, only if able to NULL terminate */ + if (singleCertPemSz < MAX_PEM_CERT_SIZE - 1) { + singleCertPem[singleCertPemSz] = 0; + printf("%s\n", singleCertPem); + } + + /* PEM is now in singleCertPem, of size singleCertPemSz */ + XFREE(singleCertPem, NULL, DYNAMIC_TYPE_TMP_BUFFER); + #endif /* WOLFSSL_DER_TO_PEM */ + } + + wc_PKCS7_Free(pkcs7); + + return ret; +} + +#ifdef HAVE_PKCS7 + +/** + * Read file into newly-allocated buffer fileBytes, set size of allocated + * buffer into fileSz. + * + * Return 0 on success, negative on error + */ +static int ReadFile(char* fileName, byte** fileBytes, word32* fileSz) +{ + int ret = 0; + FILE* fp = NULL; + word32 sz = 0; + + if (fileName == NULL || fileBytes == NULL || fileSz == NULL) { + return -1; + } + + fp = XFOPEN(fileName, "rb"); + if (fp == XBADFILE) { + return -1; + } + + if (XFSEEK(fp, 0, XSEEK_END) != 0) { + ret = -1; + } + + if (ret == 0) { + sz = XFTELL(fp); + if (sz <= 0) { + ret = -1; + } + } + + if (ret == 0) { + if (XFSEEK(fp, 0, XSEEK_SET) != 0) { + ret = -1; + } + } + + if (ret == 0) { + *fileBytes = (byte*)XMALLOC(sz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (*fileBytes == NULL) { + ret = MEMORY_E; + } + else { + XMEMSET(*fileBytes, 0, sz); + *fileSz = sz; + } + } + + if (ret == 0) { + if ((size_t)XFREAD(*fileBytes, 1, (size_t)sz, fp) != (size_t)sz) { + ret = -1; + } + } + + if (fp != XBADFILE) { + XFCLOSE(fp); + } + + return ret; +} + +static void Usage(void) +{ + printf("signedData-verifyFile " LIBWOLFSSL_VERSION_STRING + " (NOTE: All files relative to current directory)\n"); + printf("-? Help, print this usage\n"); + printf("-b PKCS#7/CMS bundle to verify (DER format)\n"); + printf("-c Detached content, if needed\n"); +} + +int main(int argc, char** argv) +{ + int ret = 0; + int ch = 0; + + char* bundleFile = NULL; + byte* bundleBytes = NULL; + word32 bundleSz = 0; + + char* detachedContentFile = NULL; + byte* detachedContentBytes = NULL; + word32 detachedContentSz = 0; + + printf("wolfCrypt PKCS#7/CMS SignedData verification example\n\n"); + + while ((ch = mygetopt(argc, argv, "?b:c:")) != -1) { + switch (ch) { + case '?': + Usage(); + exit(EXIT_SUCCESS); + + /* File containing PKCS#7/CMS bundle */ + case 'b': + bundleFile = myoptarg; + break; + + case 'c': + detachedContentFile = myoptarg; + break; + + default: + Usage(); + exit(MY_EX_USAGE); + } + } + +#ifdef DEBUG_WOLFSSL + wolfSSL_Debugging_ON(); +#endif + + wolfSSL_Init(); + + /* Read PKCS#7 bundle file into array */ + ret = ReadFile(bundleFile, &bundleBytes, &bundleSz); + if (ret == 0) { + printf("Read %d bytes from %s\n", bundleSz, bundleFile); + } + else { + printf("Failed to read bundle file: %s\n", bundleFile); + } + + /* Read detached content file into array, if given */ + if (ret == 0 && detachedContentFile != NULL) { + ret = ReadFile(detachedContentFile, &detachedContentBytes, + &detachedContentSz); + if (ret == 0) { + printf("Read %d bytes from content file: %s\n", + detachedContentSz, detachedContentFile); + } + else { + printf("Failed to read content file: %s\n", detachedContentFile); + } + } + + /* Verify PKCS#7/CMS SignedData bundle */ + if (ret == 0) { + ret = VerifySignedData(bundleBytes, bundleSz, + detachedContentBytes, detachedContentSz); + if (ret == 0) { + printf("Successfully verified SignedData bundle!\n"); + } + else { + printf("Failed to verify SignedData bundle, ret = %d\n", ret); + } + } + + /* Free detached content array, allocated by ReadFile() */ + if (detachedContentBytes != NULL) { + XMEMSET(detachedContentBytes, 0, detachedContentSz); + XFREE(detachedContentBytes, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + /* Free bundle array, allocated by ReadFile() */ + if (bundleBytes != NULL) { + XMEMSET(bundleBytes, 0, bundleSz); + XFREE(bundleBytes, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + + wolfSSL_Cleanup(); + + exit(EXIT_SUCCESS); +} + +#else + +int main(int argc, char** argv) +{ + printf("Must build wolfSSL using ./configure --enable-pkcs7\n"); + return 0; +} + +#endif /* HAVE_PKCS7 */ +