[PATCH 1/2] Add PKCS#7 verification for wolfSSL

34 views
Skip to first unread message

Bastian Germann

unread,
Mar 21, 2023, 9:07:26 AM3/21/23
to swup...@googlegroups.com, Bastian Germann
wolfSSL has a PKCS#7 implementation that has mostly the same features like
CMS. Add signature verification based on the existing swupdate_cms_verify
module. It can only deal with one signature and cannot deal with X509
purposes, which is why CMS_SKIP_UNKNOWN_SIGNERS and
CMS_IGNORE_CERTIFICATE_PURPOSE are forcibly set with PKCS#7.

Wire up the new module in the build system.

Signed-off-by: Bastian Germann <ba...@debian.org>
---
Kconfig | 13 ++-
corelib/Makefile | 4 +-
corelib/swupdate_cms_verify.c | 2 +
corelib/swupdate_pkcs7_verify.c | 173 ++++++++++++++++++++++++++++++
corelib/swupdate_verify_private.h | 2 +
corelib/verify_signature.c | 3 +
include/sslapi.h | 3 +-
7 files changed, 194 insertions(+), 6 deletions(-)
create mode 100644 corelib/swupdate_pkcs7_verify.c

diff --git a/Kconfig b/Kconfig
index 48325bf..0c497fe 100644
--- a/Kconfig
+++ b/Kconfig
@@ -415,6 +415,8 @@ choice
config SSL_IMPL_WOLFSSL
bool "wolfSSL (with OpenSSL compatibility layer)"
depends on HAVE_WOLFSSL
+ select CMS_IGNORE_CERTIFICATE_PURPOSE
+ select CMS_SKIP_UNKNOWN_SIGNERS

config SSL_IMPL_MBEDTLS
bool "mbedTLS"
@@ -451,7 +453,10 @@ choice
default SIGALG_RAWRSA
help
Select if the signature algorithm for signed images is a raw RSA signature
- (following PKCS#1.5) or if it uses Cryptographic Message Syntax (CMS).
+ (following PKCS#1.5) or if it uses Cryptographic Message Syntax (CMS) with
+ OpenSSL/LibreSSL or PKCS#7 with wolfSSL.
+ wolfSSL's PKCS#7 implementation can only deal with one signature and cannot
+ deal with X509 purposes.

config SIGALG_RAWRSA
bool "RSA PKCS#1.5"
@@ -460,12 +465,12 @@ choice
bool "RSA PSS"

config SIGALG_CMS
- bool "Cryptographic Message Syntax (CMS)"
- depends on SSL_IMPL_OPENSSL
+ bool "Cryptographic Message Syntax (CMS) / PKCS#7"
+ depends on SSL_IMPL_OPENSSL || SSL_IMPL_WOLFSSL

endchoice

-menu "CMS signature verification options"
+menu "CMS / PKCS#7 signature verification options"
depends on SIGALG_CMS

config CMS_IGNORE_EXPIRED_CERTIFICATE
diff --git a/corelib/Makefile b/corelib/Makefile
index d6ea6a2..fb0a989 100644
--- a/corelib/Makefile
+++ b/corelib/Makefile
@@ -17,9 +17,11 @@ lib-$(CONFIG_SIGALG_RAWRSA) += swupdate_rsa_verify.o
lib-$(CONFIG_SIGALG_RSAPSS) += swupdate_rsa_verify.o
endif
ifeq ($(CONFIG_SSL_IMPL_OPENSSL),y)
-# wolfSSL does not support CMS in the compatibility layer yet
lib-$(CONFIG_SIGALG_CMS) += swupdate_cms_verify.o
endif
+ifeq ($(CONFIG_SSL_IMPL_WOLFSSL),y)
+lib-$(CONFIG_SIGALG_CMS) += swupdate_pkcs7_verify.o
+endif
ifeq ($(CONFIG_SSL_IMPL_MBEDTLS),y)
lib-$(CONFIG_HASH_VERIFY) += verify_signature_mbedtls.o
ifeq ($(CONFIG_PKCS11),y)
diff --git a/corelib/swupdate_cms_verify.c b/corelib/swupdate_cms_verify.c
index 2c0ba39..49e3548 100644
--- a/corelib/swupdate_cms_verify.c
+++ b/corelib/swupdate_cms_verify.c
@@ -22,6 +22,7 @@
#define VERIFY_UNKNOWN_SIGNER_FLAGS (0)
#endif

+#ifndef CONFIG_CMS_IGNORE_CERTIFICATE_PURPOSE
int check_code_sign(const X509_PURPOSE *xp, const X509 *crt, int ca)
{
X509 *x = (X509 *)crt;
@@ -47,6 +48,7 @@ int check_code_sign(const X509_PURPOSE *xp, const X509 *crt, int ca)

return (ex_flags & EXFLAG_XKUSAGE) && (ex_xkusage & XKU_CODE_SIGN);
}
+#endif

static int cms_verify_callback(int ok, X509_STORE_CTX *ctx) {
int cert_error = X509_STORE_CTX_get_error(ctx);
diff --git a/corelib/swupdate_pkcs7_verify.c b/corelib/swupdate_pkcs7_verify.c
new file mode 100644
index 0000000..3263fcd
--- /dev/null
+++ b/corelib/swupdate_pkcs7_verify.c
@@ -0,0 +1,173 @@
+/*
+ * (C) Copyright 2019
+ * Stefano Babic, DENX Software Engineering, sba...@denx.de.
+ * (C) Copyright 2023
+ * Bastian Germann
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Code mostly taken from openssl examples
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include "swupdate.h"
+#include "sslapi.h"
+#include "util.h"
+#include "swupdate_verify_private.h"
+
+static int store_verify_callback(int ok, X509_STORE_CTX *ctx) {
+ int cert_error = X509_STORE_CTX_get_error(ctx);
+
+ if (!ok) {
+ switch (cert_error) {
+#if defined(CONFIG_CMS_IGNORE_EXPIRED_CERTIFICATE)
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ ok = 1;
+ break;
+#endif
+#if defined(CONFIG_CMS_IGNORE_CERTIFICATE_PURPOSE)
+ case X509_V_ERR_INVALID_PURPOSE:
+ ok = 1;
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+
+ return ok;
+}
+
+X509_STORE *load_cert_chain(const char *file)
+{
+ X509_STORE *castore = X509_STORE_new();
+ if (!castore) {
+ return NULL;
+ }
+
+ /*
+ * Set error callback function for verification of CRTs and CRLs in order
+ * to ignore some errors depending on configuration
+ */
+ X509_STORE_set_verify_cb(castore, store_verify_callback);
+
+ BIO *castore_bio = BIO_new_file(file, "r");
+ if (!castore_bio) {
+ TRACE("failed: BIO_new_file(%s)", file);
+ return NULL;
+ }
+
+ int crt_count = 0;
+ X509 *crt = NULL;
+ do {
+ crt = PEM_read_bio_X509(castore_bio, NULL, 0, NULL);
+ if (crt) {
+ crt_count++;
+ char *subj = X509_NAME_oneline(X509_get_subject_name(crt), NULL, 0);
+ char *issuer = X509_NAME_oneline(X509_get_issuer_name(crt), NULL, 0);
+ TRACE("Read PEM #%d: %s %s", crt_count, issuer, subj);
+ free(subj);
+ free(issuer);
+ if (X509_STORE_add_cert(castore, crt) == 0) {
+ TRACE("Adding certificate to X509_STORE failed");
+ BIO_free(castore_bio);
+ X509_STORE_free(castore);
+ return NULL;
+ }
+ }
+ } while (crt);
+ BIO_free(castore_bio);
+
+ if (crt_count == 0) {
+ X509_STORE_free(castore);
+ return NULL;
+ }
+
+ return castore;
+}
+
+static int check_signer_name(const char *name)
+{
+ // TODO work around wolfSSL's PKCS7_get0_signers always returning NULL
+ if (name)
+ WARN("The X.509 common name might not be equal to %s.", name);
+ return 0;
+}
+
+int swupdate_verify_file(struct swupdate_digest *dgst, const char *sigfile,
+ const char *file, const char *signer_name)
+{
+ int status = -EFAULT;
+ WOLFSSL_PKCS7* pkcs7 = (WOLFSSL_PKCS7 *)PKCS7_new();
+ BIO *bio_mem = NULL;
+ BIO *content_bio = NULL;
+
+ /* Open detached signature that needs to be checked */
+ BIO *sigfile_bio = BIO_new_file(sigfile, "rb");
+ if (!sigfile_bio) {
+ ERROR("%s cannot be opened", sigfile);
+ status = -EBADF;
+ goto out;
+ }
+
+ pkcs7->len = wolfSSL_BIO_get_len(sigfile_bio);
+ pkcs7->data = calloc(1, pkcs7->len);
+ BIO_read(sigfile_bio, pkcs7->data, pkcs7->len);
+ if (!pkcs7->data) {
+ ERROR("%s cannot be parsed as DER-encoded PKCS#7 signature blob", sigfile);
+ status = -EFAULT;
+ goto out;
+ }
+
+ /* Open the content file (data which was signed) */
+ content_bio = BIO_new_file(file, "rb");
+ if (!content_bio) {
+ ERROR("%s cannot be opened", file);
+ status = -EBADF;
+ goto out;
+ }
+ long content_len = wolfSSL_BIO_get_len(content_bio);
+ char *content = calloc(1, content_len);
+ BIO_read(content_bio, content, content_len);
+ bio_mem = BIO_new_mem_buf(content, content_len);
+
+ /* Then try to verify signature. The BIO* in parameter has to be a mem BIO.
+ See https://github.com/wolfSSL/wolfssl/issues/6174. */
+ if (!PKCS7_verify((PKCS7 *)pkcs7, NULL, dgst->certs, bio_mem,
+ NULL, PKCS7_BINARY)) {
+ ERR_print_errors_fp(stderr);
+ ERROR("Signature verification failed");
+ status = -EBADMSG;
+ goto out;
+ }
+
+ if (check_signer_name(signer_name)) {
+ ERROR("failed to verify signer name");
+ status = -EFAULT;
+ goto out;
+ }
+
+ TRACE("Verified OK");
+
+ /* Signature is valid */
+ status = 0;
+out:
+
+ if (pkcs7) {
+ PKCS7_free((PKCS7 *)pkcs7);
+ }
+ if (bio_mem) {
+ BIO_free(bio_mem);
+ }
+ if (content_bio) {
+ BIO_free(content_bio);
+ }
+ if (sigfile_bio) {
+ BIO_free(sigfile_bio);
+ }
+ return status;
+}
diff --git a/corelib/swupdate_verify_private.h b/corelib/swupdate_verify_private.h
index db2c4c7..cd3c7e5 100644
--- a/corelib/swupdate_verify_private.h
+++ b/corelib/swupdate_verify_private.h
@@ -16,7 +16,9 @@ EVP_PKEY *load_pubkey(const char *file);
#endif

#ifdef CONFIG_SIGALG_CMS
+#ifndef CONFIG_CMS_IGNORE_CERTIFICATE_PURPOSE
int check_code_sign(const X509_PURPOSE *xp, const X509 *crt, int ca);
+#endif
X509_STORE *load_cert_chain(const char *file);
#endif

diff --git a/corelib/verify_signature.c b/corelib/verify_signature.c
index d91a0bf..69989fe 100644
--- a/corelib/verify_signature.c
+++ b/corelib/verify_signature.c
@@ -153,6 +153,7 @@ int swupdate_dgst_init(struct swupdate_cfg *sw, const char *keyfile)
goto dgst_init_error;
}

+#ifndef CONFIG_CMS_IGNORE_CERTIFICATE_PURPOSE
{
static char code_sign_name[] = "Code signing";
static char code_sign_sname[] = "codesign";
@@ -171,6 +172,8 @@ int swupdate_dgst_init(struct swupdate_cfg *sw, const char *keyfile)
ret = -EINVAL;
goto dgst_init_error;
}
+#endif
+
#else
TRACE("public key / cert %s ignored, you need to set SIGALG", keyfile);
#endif
diff --git a/include/sslapi.h b/include/sslapi.h
index accf3c4..50f6a96 100644
--- a/include/sslapi.h
+++ b/include/sslapi.h
@@ -38,6 +38,7 @@
#include <openssl/hmac.h>
#include <openssl/aes.h>
#include <openssl/opensslv.h>
+#include <openssl/cms.h>
#elif defined(CONFIG_SSL_IMPL_WOLFSSL)
#include <wolfssl/options.h>
#include <wolfssl/openssl/bio.h>
@@ -50,12 +51,12 @@
#include <wolfssl/openssl/hmac.h>
#include <wolfssl/openssl/aes.h>
#include <wolfssl/openssl/opensslv.h>
+#include <wolfssl/openssl/pkcs7.h>
#endif

#if defined(CONFIG_SSL_IMPL_OPENSSL) || defined(CONFIG_SSL_IMPL_WOLFSSL)

#ifdef CONFIG_SIGALG_CMS
-#include <openssl/cms.h>

static inline uint32_t SSL_X509_get_extension_flags(X509 *x)
{
--
2.39.2

Reply all
Reply to author
Forward
0 new messages