summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/crypto
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Runtime/common/crypto
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Runtime/common/crypto')
-rw-r--r--src/VBox/Runtime/common/crypto/Makefile.kup0
-rw-r--r--src/VBox/Runtime/common/crypto/RTCrPkcs5Pbkdf2Hmac-openssl.cpp86
-rw-r--r--src/VBox/Runtime/common/crypto/RTCrRandBytes-openssl.cpp69
-rw-r--r--src/VBox/Runtime/common/crypto/RTCrStoreCertAddFromJavaKeyStore.cpp320
-rw-r--r--src/VBox/Runtime/common/crypto/RTCrStoreCertAddWantedFromFishingExpedition.cpp256
-rw-r--r--src/VBox/Runtime/common/crypto/RTCrStoreCertExportAsPem.cpp147
-rw-r--r--src/VBox/Runtime/common/crypto/RTCrStoreCreateSnapshotOfUserAndSystemTrustedCAsAndCerts.cpp89
-rw-r--r--src/VBox/Runtime/common/crypto/cipher-openssl.cpp596
-rw-r--r--src/VBox/Runtime/common/crypto/digest-builtin.cpp1175
-rw-r--r--src/VBox/Runtime/common/crypto/digest-core.cpp499
-rw-r--r--src/VBox/Runtime/common/crypto/digest-vfs.cpp80
-rw-r--r--src/VBox/Runtime/common/crypto/iprt-openssl.cpp202
-rw-r--r--src/VBox/Runtime/common/crypto/key-create-rsa-openssl.cpp104
-rw-r--r--src/VBox/Runtime/common/crypto/key-file.cpp688
-rw-r--r--src/VBox/Runtime/common/crypto/key-internal.h132
-rw-r--r--src/VBox/Runtime/common/crypto/key-openssl.cpp335
-rw-r--r--src/VBox/Runtime/common/crypto/key.cpp557
-rw-r--r--src/VBox/Runtime/common/crypto/pemfile-read.cpp663
-rw-r--r--src/VBox/Runtime/common/crypto/pemfile-write.cpp266
-rw-r--r--src/VBox/Runtime/common/crypto/pkcs7-asn1-decoder.cpp174
-rw-r--r--src/VBox/Runtime/common/crypto/pkcs7-core.cpp250
-rw-r--r--src/VBox/Runtime/common/crypto/pkcs7-file.cpp119
-rw-r--r--src/VBox/Runtime/common/crypto/pkcs7-init.cpp62
-rw-r--r--src/VBox/Runtime/common/crypto/pkcs7-internal.h47
-rw-r--r--src/VBox/Runtime/common/crypto/pkcs7-sanity.cpp221
-rw-r--r--src/VBox/Runtime/common/crypto/pkcs7-sign.cpp592
-rw-r--r--src/VBox/Runtime/common/crypto/pkcs7-template.h236
-rw-r--r--src/VBox/Runtime/common/crypto/pkcs7-verify.cpp866
-rw-r--r--src/VBox/Runtime/common/crypto/pkcs8-asn1-decoder.cpp53
-rw-r--r--src/VBox/Runtime/common/crypto/pkcs8-core.cpp54
-rw-r--r--src/VBox/Runtime/common/crypto/pkcs8-init.cpp53
-rw-r--r--src/VBox/Runtime/common/crypto/pkcs8-internal.h46
-rw-r--r--src/VBox/Runtime/common/crypto/pkcs8-sanity.cpp53
-rw-r--r--src/VBox/Runtime/common/crypto/pkcs8-template.h69
-rw-r--r--src/VBox/Runtime/common/crypto/pkix-sign.cpp262
-rw-r--r--src/VBox/Runtime/common/crypto/pkix-signature-builtin.cpp153
-rw-r--r--src/VBox/Runtime/common/crypto/pkix-signature-builtin.h51
-rw-r--r--src/VBox/Runtime/common/crypto/pkix-signature-core.cpp294
-rw-r--r--src/VBox/Runtime/common/crypto/pkix-signature-ossl.cpp288
-rw-r--r--src/VBox/Runtime/common/crypto/pkix-signature-rsa.cpp565
-rw-r--r--src/VBox/Runtime/common/crypto/pkix-util.cpp108
-rw-r--r--src/VBox/Runtime/common/crypto/pkix-verify.cpp329
-rw-r--r--src/VBox/Runtime/common/crypto/rc4-openssl.cpp75
-rw-r--r--src/VBox/Runtime/common/crypto/rsa-asn1-decoder.cpp53
-rw-r--r--src/VBox/Runtime/common/crypto/rsa-core.cpp54
-rw-r--r--src/VBox/Runtime/common/crypto/rsa-init.cpp53
-rw-r--r--src/VBox/Runtime/common/crypto/rsa-internal.h50
-rw-r--r--src/VBox/Runtime/common/crypto/rsa-sanity.cpp53
-rw-r--r--src/VBox/Runtime/common/crypto/rsa-template.h118
-rw-r--r--src/VBox/Runtime/common/crypto/spc-asn1-decoder.cpp88
-rw-r--r--src/VBox/Runtime/common/crypto/spc-core.cpp94
-rw-r--r--src/VBox/Runtime/common/crypto/spc-init.cpp55
-rw-r--r--src/VBox/Runtime/common/crypto/spc-internal.h47
-rw-r--r--src/VBox/Runtime/common/crypto/spc-sanity.cpp179
-rw-r--r--src/VBox/Runtime/common/crypto/spc-template.h198
-rw-r--r--src/VBox/Runtime/common/crypto/ssl-openssl.cpp507
-rw-r--r--src/VBox/Runtime/common/crypto/store-cert-add-basic.cpp865
-rw-r--r--src/VBox/Runtime/common/crypto/store-inmem.cpp466
-rw-r--r--src/VBox/Runtime/common/crypto/store-internal.h177
-rw-r--r--src/VBox/Runtime/common/crypto/store.cpp571
-rw-r--r--src/VBox/Runtime/common/crypto/taf-asn1-decoder.cpp54
-rw-r--r--src/VBox/Runtime/common/crypto/taf-core.cpp51
-rw-r--r--src/VBox/Runtime/common/crypto/taf-init.cpp54
-rw-r--r--src/VBox/Runtime/common/crypto/taf-internal.h47
-rw-r--r--src/VBox/Runtime/common/crypto/taf-sanity.cpp52
-rw-r--r--src/VBox/Runtime/common/crypto/taf-template.h104
-rw-r--r--src/VBox/Runtime/common/crypto/tsp-asn1-decoder.cpp51
-rw-r--r--src/VBox/Runtime/common/crypto/tsp-core.cpp51
-rw-r--r--src/VBox/Runtime/common/crypto/tsp-init.cpp51
-rw-r--r--src/VBox/Runtime/common/crypto/tsp-internal.h47
-rw-r--r--src/VBox/Runtime/common/crypto/tsp-sanity.cpp51
-rw-r--r--src/VBox/Runtime/common/crypto/tsp-template.h113
-rw-r--r--src/VBox/Runtime/common/crypto/x509-asn1-decoder.cpp230
-rw-r--r--src/VBox/Runtime/common/crypto/x509-certpaths.cpp3017
-rw-r--r--src/VBox/Runtime/common/crypto/x509-core.cpp1934
-rw-r--r--src/VBox/Runtime/common/crypto/x509-file.cpp174
-rw-r--r--src/VBox/Runtime/common/crypto/x509-init.cpp88
-rw-r--r--src/VBox/Runtime/common/crypto/x509-internal.h47
-rw-r--r--src/VBox/Runtime/common/crypto/x509-sanity.cpp171
-rw-r--r--src/VBox/Runtime/common/crypto/x509-template.h468
-rw-r--r--src/VBox/Runtime/common/crypto/x509-verify.cpp131
81 files changed, 21898 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/crypto/Makefile.kup b/src/VBox/Runtime/common/crypto/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/Makefile.kup
diff --git a/src/VBox/Runtime/common/crypto/RTCrPkcs5Pbkdf2Hmac-openssl.cpp b/src/VBox/Runtime/common/crypto/RTCrPkcs5Pbkdf2Hmac-openssl.cpp
new file mode 100644
index 00000000..8b1481aa
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/RTCrPkcs5Pbkdf2Hmac-openssl.cpp
@@ -0,0 +1,86 @@
+/* $Id: RTCrPkcs5Pbkdf2Hmac-openssl.cpp $ */
+/** @file
+ * IPRT - Crypto - RTCrPkcs5Pbkdf2Hmac implementation using OpenSSL.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef IPRT_WITH_OPENSSL
+# include "internal/iprt.h"
+# include <iprt/crypto/misc.h>
+
+# include <iprt/err.h>
+# include <iprt/rand.h>
+# include <iprt/assert.h>
+
+# include "internal/iprt-openssl.h"
+# include "internal/openssl-pre.h"
+# include <openssl/evp.h>
+# include "internal/openssl-post.h"
+
+
+RTDECL(int) RTCrPkcs5Pbkdf2Hmac(void const *pvInput, size_t cbInput, void const *pvSalt, size_t cbSalt, uint32_t cIterations,
+ RTDIGESTTYPE enmDigestType, size_t cbKeyLen, void *pvOutput)
+{
+ const EVP_MD *pDigest;
+ switch (enmDigestType)
+ {
+ case RTDIGESTTYPE_SHA1:
+ pDigest = EVP_sha1();
+ break;
+ case RTDIGESTTYPE_SHA256:
+ pDigest = EVP_sha256();
+ break;
+ case RTDIGESTTYPE_SHA512:
+ pDigest = EVP_sha512();
+ break;
+ default:
+ AssertFailedReturn(VERR_CR_DIGEST_NOT_SUPPORTED);
+ }
+
+ /* Note! This requires OpenSSL 1.0.0 or higher. */
+ Assert((size_t)(int)cbInput == cbInput);
+ Assert((size_t)(int)cbSalt == cbSalt);
+ Assert((size_t)(int)cbKeyLen == cbKeyLen);
+ int rcOssl = PKCS5_PBKDF2_HMAC((const char *)pvInput, (int)cbInput, (const unsigned char *)pvSalt, (int)cbSalt,
+ (int)cIterations, pDigest, (int)cbKeyLen, (unsigned char *)pvOutput);
+ if (rcOssl)
+ return VINF_SUCCESS;
+ return VERR_CR_PASSWORD_2_KEY_DERIVIATION_FAILED;
+}
+
+#endif /* IPRT_WITH_OPENSSL */
+
diff --git a/src/VBox/Runtime/common/crypto/RTCrRandBytes-openssl.cpp b/src/VBox/Runtime/common/crypto/RTCrRandBytes-openssl.cpp
new file mode 100644
index 00000000..a29a0240
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/RTCrRandBytes-openssl.cpp
@@ -0,0 +1,69 @@
+/* $Id: RTCrRandBytes-openssl.cpp $ */
+/** @file
+ * IPRT - Crypto - RTCrRandBytes implementation using OpenSSL.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef IPRT_WITH_OPENSSL
+# include "internal/iprt.h"
+# include <iprt/crypto/misc.h>
+
+# include <iprt/rand.h>
+# include <iprt/assert.h>
+# include <iprt/err.h>
+
+# include "internal/iprt-openssl.h"
+# include "internal/openssl-pre.h"
+# include <openssl/rand.h>
+# include "internal/openssl-post.h"
+
+
+RTDECL(int) RTCrRandBytes(void *pvDst, size_t cbDst)
+{
+ /* Make sure the return buffer is always fully initialized in case the caller
+ doesn't properly check the return value. */
+ RTRandBytes(pvDst, cbDst); /* */
+
+ /* Get cryptographically strong random. */
+ rtCrOpenSslInit();
+ Assert((size_t)(int)cbDst == cbDst);
+ int rcOpenSsl = RAND_bytes((uint8_t *)pvDst, (int)cbDst);
+ return rcOpenSsl > 0 ? VINF_SUCCESS : rcOpenSsl == 0 ? VERR_CR_RANDOM_FAILED : VERR_CR_RANDOM_SETUP_FAILED;
+}
+
+#endif /* IPRT_WITH_OPENSSL */
+
diff --git a/src/VBox/Runtime/common/crypto/RTCrStoreCertAddFromJavaKeyStore.cpp b/src/VBox/Runtime/common/crypto/RTCrStoreCertAddFromJavaKeyStore.cpp
new file mode 100644
index 00000000..3e1194ef
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/RTCrStoreCertAddFromJavaKeyStore.cpp
@@ -0,0 +1,320 @@
+/* $Id: RTCrStoreCertAddFromJavaKeyStore.cpp $ */
+/** @file
+ * IPRT - Cryptographic (Certificate) Store, RTCrStoreCertAddFromJavaKeyStore.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_CRYPTO
+#include "internal/iprt.h"
+#include <iprt/crypto/store.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/sha.h>
+#include <iprt/string.h>
+#include <iprt/log.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The java key store magic number (file endian). */
+#define JKS_MAGIC RT_H2BE_U32_C(UINT32_C(0xfeedfeed))
+/** Java key store format version 2 (file endian). */
+#define JKS_VERSION_2 RT_H2BE_U32_C(2)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Java key store (JKS) header.
+ */
+typedef struct JKSHEADER
+{
+ /** The magic (big endian) - JKS_MAGIC. */
+ uint32_t uMagic;
+ /** Format version number (big endian) - JKS_VERSION_2. */
+ uint32_t uVersion;
+ /** The number of keystore entries (big endian). */
+ uint32_t cEntries;
+} JKSHEADER;
+/** Pointer to a const java key store header. */
+typedef JKSHEADER const *PCJKSHEADER;
+
+
+RTDECL(int) RTCrStoreCertAddFromJavaKeyStoreInMem(RTCRSTORE hStore, uint32_t fFlags, void const *pvContent, size_t cbContent,
+ const char *pszErrorName, PRTERRINFO pErrInfo)
+{
+ uint8_t const *pbContent = (uint8_t const *)pvContent;
+
+ /*
+ * Check the header.
+ */
+ if (cbContent < sizeof(JKSHEADER) + RTSHA1_HASH_SIZE)
+ return RTErrInfoAddF(pErrInfo, VERR_WRONG_TYPE /** @todo better status codes */,
+ " Too small (%zu bytes) for java key store (%s)", cbContent, pszErrorName);
+ PCJKSHEADER pHdr = (PCJKSHEADER)pbContent;
+ if (pHdr->uMagic != JKS_MAGIC)
+ return RTErrInfoAddF(pErrInfo, VERR_WRONG_TYPE /** @todo better status codes */,
+ " Not java key store magic %#x (%s)", RT_BE2H_U32(pHdr->uMagic), pszErrorName);
+ if (pHdr->uVersion != JKS_VERSION_2)
+ return RTErrInfoAddF(pErrInfo, VERR_WRONG_TYPE /** @todo better status codes */,
+ " Unsupported java key store version %#x (%s)", RT_BE2H_U32(pHdr->uVersion), pszErrorName);
+ uint32_t const cEntries = RT_BE2H_U32(pHdr->cEntries);
+ if (cEntries > cbContent / 24) /* 24 = 4 for type, 4+ alias, 8 byte timestamp, 4 byte len, "X.509" or 4 cert count */
+ return RTErrInfoAddF(pErrInfo, VERR_WRONG_TYPE /** @todo better status codes */,
+ " Entry count %u is to high for %zu byte JKS (%s)", cEntries, cbContent, pszErrorName);
+
+ /*
+ * Here we should check the store signature. However, it always includes
+ * some kind of password, and that's somewhere we don't want to go right
+ * now. Later perhaps.
+ *
+ * We subtract it from the content size to make EOF checks simpler.
+ */
+ int rc = VINF_SUCCESS;
+#if 0 /* later */
+ RTSHA1CONTEXT Ctx;
+ RTSha1Init(&Ctx);
+
+ const char *pszCur = pszPassword;
+ for (;;)
+ {
+ RTUNICP Cp;
+ rc = RTStrGetCpEx(&pszCur, &Cp);
+ AssertRCReturn(rc, rc);
+ if (!Cp)
+ break;
+ uint8_t abWChar[2];
+ abWChar[0] = RT_BYTE2(Cp);
+ abWChar[1] = RT_BYTE1(Cp);
+ RTSha1Update(&Ctx, &abWChar, sizeof(abWChar));
+ }
+
+ RTSha1Update(&Ctx, RT_STR_TUPLE("Mighty Aphrodite"));
+
+ RTSha1Update(&Ctx, pbContent, cbContent - RTSHA1_HASH_SIZE);
+
+ uint8_t abSignature[RTSHA1_HASH_SIZE];
+ RTSha1Final(&Ctx, abSignature);
+
+ if (memcmp(&pbContent[cbContent - RTSHA1_HASH_SIZE], abSignature, RTSHA1_HASH_SIZE) != 0)
+ {
+ rc = RTErrInfoAddF(pErrInfo, VERR_MISMATCH, " File SHA-1 signature mismatch, %.*Rhxs instead of %.*Rhxs, for '%s'",
+ RTSHA1_HASH_SIZE, abSignature,
+ RTSHA1_HASH_SIZE, &pbContent[cbContent - RTSHA1_HASH_SIZE],
+ pszErrorName);
+ if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ return rc;
+ }
+#endif
+ cbContent -= RTSHA1_HASH_SIZE;
+
+
+ /*
+ * A bunch of macros to make decoding easier.
+ */
+#define ENSURE_CONTENT_OR_BREAK_EX(a_cbNeeded, a_pszWhat) \
+ do { \
+ if (RT_LIKELY(off + (a_cbNeeded) <= cbContent)) \
+ { /* likely */ } \
+ else \
+ { \
+ rc = RTErrInfoAddF(pErrInfo, VERR_EOF, " Unexpected end of data at %#x need %u bytes for %s (entry #%u in %s)", \
+ off, a_cbNeeded, a_pszWhat, iEntry, pszErrorName); \
+ break; \
+ } \
+ } while (0)
+#define ENSURE_CONTENT_OR_BREAK(a_Var) ENSURE_CONTENT_OR_BREAK_EX(sizeof(a_Var), #a_Var)
+#define GET_BE_U32_OR_BREAK(a_uVar) \
+ do { \
+ ENSURE_CONTENT_OR_BREAK(a_uVar); \
+ AssertCompile(sizeof(a_uVar) == sizeof(uint32_t)); \
+ a_uVar = RT_MAKE_U32_FROM_U8(pbContent[off + 3], pbContent[off + 2], pbContent[off + 1], pbContent[off + 0]); \
+ off += sizeof(uint32_t); \
+ } while (0)
+#define GET_BE_U16_OR_BREAK(a_uVar) \
+ do { \
+ ENSURE_CONTENT_OR_BREAK(a_uVar); \
+ AssertCompile(sizeof(a_uVar) == sizeof(uint16_t)); \
+ a_uVar = RT_MAKE_U16(pbContent[off + 1], pbContent[off + 0]); \
+ off += sizeof(uint16_t); \
+ } while (0)
+#define SKIP_CONTENT_BYTES_OR_BREAK(a_cbToSkip, a_pszWhat) \
+ do { \
+ ENSURE_CONTENT_OR_BREAK_EX(a_cbToSkip, a_pszWhat); \
+ off += a_cbToSkip; \
+ } while (0)
+#define CHECK_OR_BREAK(a_Expr, a_RTErrInfoAddFArgs) \
+ do { \
+ if (RT_LIKELY(a_Expr)) \
+ { /* likely */ } \
+ else \
+ { \
+ rc = RTErrInfoAddF a_RTErrInfoAddFArgs; \
+ break; \
+ } \
+ } while (0)
+
+ /*
+ * Work our way thru the keystore.
+ */
+ Log(("JKS: %u entries - '%s'\n", cEntries, pszErrorName));
+ size_t off = sizeof(JKSHEADER);
+ uint32_t iEntry = 0;
+ for (;;)
+ {
+ size_t const offEntry = off; NOREF(offEntry);
+
+ /* The entry type. */
+ uint32_t uType;
+ GET_BE_U32_OR_BREAK(uType);
+ CHECK_OR_BREAK(uType == 1 || uType == 2,
+ (pErrInfo, VERR_WRONG_TYPE, " uType=%#x (entry #%u in %s)", uType, iEntry, pszErrorName));
+
+ /* Skip the alias string. */
+ uint16_t cbAlias;
+ GET_BE_U16_OR_BREAK(cbAlias);
+ SKIP_CONTENT_BYTES_OR_BREAK(cbAlias, "szAlias");
+
+ /* Skip the creation timestamp. */
+ SKIP_CONTENT_BYTES_OR_BREAK(sizeof(uint64_t), "tsCreated");
+
+ uint32_t cTrustCerts = 0;
+ if (uType == 1)
+ {
+ /*
+ * It is a private key.
+ */
+ Log(("JKS: %#08zx: entry #%u: Private key\n", offEntry, iEntry));
+
+ /* The encoded key. */
+ uint32_t cbKey;
+ GET_BE_U32_OR_BREAK(cbKey);
+ SKIP_CONTENT_BYTES_OR_BREAK(cbKey, "key data");
+
+ /* The number of trust certificates following it. */
+ GET_BE_U32_OR_BREAK(cTrustCerts);
+ }
+ else if (uType == 2)
+ {
+ /*
+ * It is a certificate.
+ */
+ Log(("JKS: %#08zx: entry #%u: Trust certificate\n", offEntry, iEntry));
+ cTrustCerts = 1;
+ }
+ else
+ AssertFailedBreakStmt(rc = VERR_INTERNAL_ERROR_2);
+
+ /*
+ * Decode trust certificates. Keys have 0 or more of these associated with them.
+ */
+ for (uint32_t iCert = 0; iCert < cTrustCerts; iCert++)
+ {
+ /* X.509 signature */
+ static const char a_achCertType[] = { 0, 5, 'X', '.', '5', '0', '9' };
+ ENSURE_CONTENT_OR_BREAK(a_achCertType);
+ CHECK_OR_BREAK(memcmp(&pbContent[off], a_achCertType, sizeof(a_achCertType)) == 0,
+ (pErrInfo, VERR_WRONG_TYPE, " Unsupported certificate type %.7Rhxs (entry #%u in %s)",
+ &pbContent[off], iEntry, pszErrorName));
+ off += sizeof(a_achCertType);
+
+ /* The encoded certificate length. */
+ uint32_t cbEncoded;
+ GET_BE_U32_OR_BREAK(cbEncoded);
+ ENSURE_CONTENT_OR_BREAK_EX(cbEncoded, "certificate data");
+ Log(("JKS: %#08zx: %#x certificate bytes\n", off, cbEncoded));
+
+ /* Try add the certificate. */
+ RTERRINFOSTATIC StaticErrInfo;
+ int rc2 = RTCrStoreCertAddEncoded(hStore,
+ RTCRCERTCTX_F_ENC_X509_DER | (fFlags & RTCRCERTCTX_F_ADD_IF_NOT_FOUND),
+ &pbContent[off], cbEncoded, RTErrInfoInitStatic(&StaticErrInfo));
+ if (RT_FAILURE(rc2))
+ {
+ if (RTErrInfoIsSet(&StaticErrInfo.Core))
+ rc = RTErrInfoAddF(pErrInfo, rc2, " entry #%u: %s", iEntry, StaticErrInfo.Core.pszMsg);
+ else
+ rc = RTErrInfoAddF(pErrInfo, rc2, " entry #%u: %Rrc adding cert", iEntry, rc2);
+ if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ break;
+ }
+ off += cbEncoded;
+ }
+
+ /*
+ * Advance.
+ */
+ iEntry++;
+ if (iEntry >= cEntries)
+ {
+ if (off != cbContent)
+ rc = RTErrInfoAddF(pErrInfo, VERR_TOO_MUCH_DATA, " %zu tailing bytes (%s)", cbContent - off, pszErrorName);
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTCrStoreCertAddFromJavaKeyStore(RTCRSTORE hStore, uint32_t fFlags, const char *pszFilename, PRTERRINFO pErrInfo)
+{
+ AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
+
+ /*
+ * Read the whole thing into memory as that's much more convenient to work
+ * with and we don't expect a java key store to take up a lot of space.
+ */
+ size_t cbContent;
+ void *pvContent;
+ int rc = RTFileReadAllEx(pszFilename, 0, 32U*_1M, RTFILE_RDALL_O_DENY_WRITE, &pvContent, &cbContent);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrStoreCertAddFromJavaKeyStoreInMem(hStore, fFlags, pvContent, cbContent, pszFilename, pErrInfo);
+ RTFileReadAllFree(pvContent, cbContent);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "RTFileReadAllEx failed with %Rrc on '%s'", rc, pszFilename);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertAddFromJavaKeyStore);
+
diff --git a/src/VBox/Runtime/common/crypto/RTCrStoreCertAddWantedFromFishingExpedition.cpp b/src/VBox/Runtime/common/crypto/RTCrStoreCertAddWantedFromFishingExpedition.cpp
new file mode 100644
index 00000000..8c4cef9e
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/RTCrStoreCertAddWantedFromFishingExpedition.cpp
@@ -0,0 +1,256 @@
+/* $Id: RTCrStoreCertAddWantedFromFishingExpedition.cpp $ */
+/** @file
+ * IPRT - Cryptographic (Certificate) Store, RTCrStoreCertAddFromFishingExpedition.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/store.h>
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+
+#include "x509-internal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#ifdef RT_OS_WINDOWS
+# define PREFIX_UNIXROOT "${SystemDrive}/cygwin"
+#elif defined(RT_OS_OS2)
+# define PREFIX_UNIXROOT "/@unixroot@"
+#else
+# define PREFIX_UNIXROOT
+#endif
+
+
+/**
+ * Count the number of found certificates.
+ *
+ * @returns Number found.
+ * @param afFound Indicator array.
+ * @param cWanted Number of wanted certificates.
+ */
+DECLINLINE(size_t) rtCrStoreCountFound(bool const *afFound, size_t cWanted)
+{
+ size_t cFound = 0;
+ while (cWanted-- > 0)
+ if (afFound[cWanted])
+ cFound++;
+ return cFound;
+}
+
+
+RTDECL(int) RTCrStoreCertAddWantedFromFishingExpedition(RTCRSTORE hStore, uint32_t fFlags,
+ PCRTCRCERTWANTED paWanted, size_t cWanted,
+ bool *pafFound, PRTERRINFO pErrInfo)
+{
+ int rc = VINF_SUCCESS;
+ int rc2;
+
+ /*
+ * Validate input.
+ */
+ AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
+ fFlags |= RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR; /* force these! */
+ AssertReturn(cWanted, VERR_NOT_FOUND);
+ for (uint32_t i = 0; i < cWanted; i++)
+ {
+ AssertReturn(!paWanted[i].pszSubject || *paWanted[i].pszSubject, VERR_INVALID_PARAMETER);
+ AssertReturn( paWanted[i].pszSubject
+ || paWanted[i].fSha1Fingerprint
+ || paWanted[i].fSha512Fingerprint,
+ VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Make sure we've got a result array.
+ */
+ bool *pafFoundFree = NULL;
+ if (!pafFound)
+ {
+ pafFound = pafFoundFree = (bool *)RTMemTmpAllocZ(sizeof(bool) * cWanted);
+ AssertReturn(pafFound, VERR_NO_TMP_MEMORY);
+ }
+
+ /*
+ * Search the user and system stores first.
+ */
+ bool fAllFound = false;
+ RTCRSTORE hTmpStore;
+ for (int iStoreId = RTCRSTOREID_INVALID + 1; iStoreId < RTCRSTOREID_END; iStoreId++)
+ {
+ rc2 = RTCrStoreCreateSnapshotById(&hTmpStore, (RTCRSTOREID)iStoreId, NULL);
+ if (RT_SUCCESS(rc2))
+ {
+ rc2 = RTCrStoreCertAddWantedFromStore(hStore, fFlags, hTmpStore, paWanted, cWanted, pafFound);
+ RTCrStoreRelease(hTmpStore);
+ fAllFound = rc2 == VINF_SUCCESS;
+ if (fAllFound)
+ break;
+ }
+ }
+
+ /*
+ * Search alternative file based stores.
+ */
+ if (!fAllFound)
+ {
+ static const char * const s_apszFiles[] =
+ {
+ PREFIX_UNIXROOT "/usr/share/ca-certificates/trust-source/mozilla.neutral-trust.crt",
+ PREFIX_UNIXROOT "/usr/share/ca-certificates/trust-source/mozilla.trust.crt",
+ PREFIX_UNIXROOT "/usr/share/doc/mutt/samples/ca-bundle.crt",
+ PREFIX_UNIXROOT "/usr/jdk/latest/jre/lib/security/cacerts",
+ PREFIX_UNIXROOT "/usr/share/curl/curl-ca-bundle.crt",
+#ifdef RT_OS_DARWIN
+ "/opt/local/share/curl/curl-ca-bundle.crt",
+ "/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/security/cacerts",
+ "/System/Library/Java/Support/CoreDeploy.bundle/Contents/Home/lib/security/cacerts",
+ "/System/Library/Java/Support/CoreDeploy.bundle/Contents/JavaAppletPlugin.plugin/Contents/Home/lib/security/cacerts",
+ "/System/Library/Java/Support/Deploy.bundle/Contents/Home/lib/security/cacerts",
+ "/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/lib/security/cacerts",
+ "/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/itms/java/lib/security/cacerts",
+ "/Applications/Xcode-beta.app/Contents/Applications/Application Loader.app/Contents/itms/java/lib/security/cacerts",
+ "/System/Library/Java/JavaVirtualMachines/*/Contents/Home/lib/security/cacerts",
+#endif
+#ifdef RT_OS_LINUX
+ PREFIX_UNIXROOT "/etc/ssl/certs/java/cacerts",
+ PREFIX_UNIXROOT "/usr/lib/j*/*/jre/lib/security/cacerts",
+ PREFIX_UNIXROOT "/opt/*/jre/lib/security/cacerts",
+#endif
+#ifdef RT_OS_SOLARIS
+ PREFIX_UNIXROOT "/usr/java/jre/lib/security/cacerts",
+ PREFIX_UNIXROOT "/usr/jdk/instances/*/jre/lib/security/cacerts",
+#endif
+#ifdef RT_OS_WINDOWS
+ "${AllProgramFiles}/Git/bin/curl-ca-bundle.crt",
+ "${AllProgramFiles}/Mercurial/hgrc.d/cacert.pem",
+ "${AllProgramFiles}/Java/jre*/lib/security/cacerts",
+ "${AllProgramFiles}/Java/jdk*/jre/lib/security/cacerts",
+ "${AllProgramFiles}/HexChat/cert.pem",
+ "${SystemDrive}/BitNami/*/git/bin/curl-ca-bundle.crt",
+ "${SystemDrive}/BitNami/*/heroku/data/cacert.pem",
+ "${SystemDrive}/BitNami/*/heroku/vendor/gems/excon*/data/cacert.pem",
+ "${SystemDrive}/BitNami/*/php/PEAR/AWSSDKforPHP/lib/requstcore/cacert.pem",
+#endif
+ };
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_apszFiles) && !fAllFound; i++)
+ {
+ PCRTPATHGLOBENTRY pResultHead;
+ rc2 = RTPathGlob(s_apszFiles[i], RTPATHGLOB_F_NO_DIRS, &pResultHead, NULL);
+ if (RT_SUCCESS(rc2))
+ {
+ for (PCRTPATHGLOBENTRY pCur = pResultHead; pCur; pCur = pCur->pNext)
+ {
+ rc2 = RTCrStoreCertAddWantedFromFile(hStore, fFlags, pCur->szPath, paWanted, cWanted, pafFound, pErrInfo);
+ fAllFound = rc2 == VINF_SUCCESS;
+ if (fAllFound)
+ break;
+ }
+ RTPathGlobFree(pResultHead);
+ }
+ }
+ }
+
+ /*
+ * Search alternative directory based stores.
+ */
+ if (!fAllFound)
+ {
+ static const char * const s_apszFiles[] =
+ {
+ PREFIX_UNIXROOT "/usr/share/ca-certificates/mozilla/",
+#ifdef RT_OS_DARWIN
+ "/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rubygems/ssl_certs/",
+#endif
+#ifdef RT_OS_SOLARIS
+ "/etc/certs/",
+ "/etc/crypto/certs/",
+#endif
+#ifdef RT_OS_WINDOWS
+ "${AllProgramFiles}/Git/ssl/certs/",
+ "${AllProgramFiles}/Git/ssl/certs/expired/",
+ "${AllProgramFiles}/Common Files/Apple/Internet Services/security.resources/roots/",
+ "${AllProgramFiles}/Raptr/ca-certs/",
+ "${SystemDrive}/Bitname/*/git/ssl/certs/",
+ "${SystemDrive}/Bitnami/*/git/ssl/certs/expired/",
+#endif
+ };
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_apszFiles) && !fAllFound; i++)
+ {
+ PCRTPATHGLOBENTRY pResultHead;
+ rc2 = RTPathGlob(s_apszFiles[i], RTPATHGLOB_F_ONLY_DIRS, &pResultHead, NULL);
+ if (RT_SUCCESS(rc2))
+ {
+ for (PCRTPATHGLOBENTRY pCur = pResultHead; pCur; pCur = pCur->pNext)
+ {
+ rc2 = RTCrStoreCertAddWantedFromDir(hStore, fFlags, pCur->szPath, NULL /*paSuffixes*/, 0 /*cSuffixes*/,
+ paWanted, cWanted, pafFound, pErrInfo);
+ fAllFound = rc2 == VINF_SUCCESS;
+ if (fAllFound)
+ break;
+ }
+ RTPathGlobFree(pResultHead);
+ }
+ }
+ }
+
+ /*
+ * If all found, return VINF_SUCCESS, otherwise warn that we didn't find everything.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ size_t cFound = rtCrStoreCountFound(pafFound, cWanted);
+ Assert(cFound == cWanted || !fAllFound);
+ if (cFound == cWanted)
+ rc = VINF_SUCCESS;
+ else if (cFound > 0)
+ rc = VWRN_NOT_FOUND;
+ else
+ rc = VERR_NOT_FOUND;
+ }
+
+ if (pafFoundFree)
+ RTMemTmpFree(pafFoundFree);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertAddWantedFromFishingExpedition);
+
diff --git a/src/VBox/Runtime/common/crypto/RTCrStoreCertExportAsPem.cpp b/src/VBox/Runtime/common/crypto/RTCrStoreCertExportAsPem.cpp
new file mode 100644
index 00000000..66349025
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/RTCrStoreCertExportAsPem.cpp
@@ -0,0 +1,147 @@
+/* $Id: RTCrStoreCertExportAsPem.cpp $ */
+/** @file
+ * IPRT - Cryptographic (Certificate) Store, RTCrStoreCertExportAsPem.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/store.h>
+
+#include <iprt/assert.h>
+#include <iprt/base64.h>
+#include <iprt/dir.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/stream.h>
+
+
+
+RTDECL(int) RTCrStoreCertExportAsPem(RTCRSTORE hStore, uint32_t fFlags, const char *pszFilename)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+ /*
+ * Start the enumeration first as this validates the store handle.
+ */
+ RTCRSTORECERTSEARCH Search;
+ int rc = RTCrStoreCertFindAll(hStore, &Search);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Open the file for writing.
+ *
+ * Note! We must use text and no binary here, because the base-64 API
+ * below will use host specific EOL markers, not CRLF as PEM
+ * specifies.
+ */
+ PRTSTREAM hStrm;
+ rc = RTStrmOpen(pszFilename, "w", &hStrm);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Enumerate the certificates in the store, writing them out one by one.
+ */
+ size_t cbBase64 = 0;
+ char *pszBase64 = NULL;
+ PCRTCRCERTCTX pCertCtx;
+ while ((pCertCtx = RTCrStoreCertSearchNext(hStore, &Search)) != NULL)
+ {
+ const char *pszMarker;
+ switch (pCertCtx->fFlags & RTCRCERTCTX_F_ENC_MASK)
+ {
+ case RTCRCERTCTX_F_ENC_X509_DER: pszMarker = "CERTIFICATE"; break;
+ case RTCRCERTCTX_F_ENC_TAF_DER: pszMarker = "TRUST ANCHOR"; break;
+ default: pszMarker = NULL; break;
+ }
+ if (pszMarker && pCertCtx->cbEncoded > 0)
+ {
+ /*
+ * Do the base64 conversion first.
+ */
+ size_t cchEncoded = RTBase64EncodedLength(pCertCtx->cbEncoded);
+ if (cchEncoded < cbBase64)
+ { /* likely */ }
+ else
+ {
+ size_t cbNew = RT_ALIGN(cchEncoded + 64, 128);
+ void *pvNew = RTMemRealloc(pszBase64, cbNew);
+ if (!pvNew)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ cbBase64 = cbNew;
+ pszBase64 = (char *)pvNew;
+ }
+ rc = RTBase64Encode(pCertCtx->pabEncoded, pCertCtx->cbEncoded, pszBase64, cbBase64, &cchEncoded);
+ if (RT_FAILURE(rc))
+ break;
+
+ RTStrmPrintf(hStrm, "-----BEGIN %s-----\n", pszMarker);
+ RTStrmWrite(hStrm, pszBase64, cchEncoded);
+ rc = RTStrmPrintf(hStrm, "\n-----END %s-----\n", pszMarker);
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ RTCrCertCtxRelease(pCertCtx);
+ }
+ if (pCertCtx)
+ RTCrCertCtxRelease(pCertCtx);
+ RTMemFree(pszBase64);
+
+ /*
+ * Flush the output file before closing.
+ */
+ int rc2 = RTStrmFlush(hStrm);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc = rc2;
+ RTStrmClearError(hStrm); /** @todo fix RTStrmClose... */
+ rc2 = RTStrmClose(hStrm);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ int rc2 = RTCrStoreCertSearchDestroy(hStore, &Search); AssertRC(rc2);
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertExportAsPem);
+
diff --git a/src/VBox/Runtime/common/crypto/RTCrStoreCreateSnapshotOfUserAndSystemTrustedCAsAndCerts.cpp b/src/VBox/Runtime/common/crypto/RTCrStoreCreateSnapshotOfUserAndSystemTrustedCAsAndCerts.cpp
new file mode 100644
index 00000000..ef1ab550
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/RTCrStoreCreateSnapshotOfUserAndSystemTrustedCAsAndCerts.cpp
@@ -0,0 +1,89 @@
+/* $Id: RTCrStoreCreateSnapshotOfUserAndSystemTrustedCAsAndCerts.cpp $ */
+/** @file
+ * IPRT - Cryptographic (Certificate) Store, RTCrStoreCreateSnapshotOfUserAndSystemTrustedCAsAndCerts.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/store.h>
+
+#include <iprt/errcore.h>
+
+
+
+
+RTR3DECL(int) RTCrStoreCreateSnapshotOfUserAndSystemTrustedCAsAndCerts(PRTCRSTORE phStore, PRTERRINFO pErrInfo)
+{
+ /*
+ * Start by a snapshot of the user store.
+ */
+ RTCRSTORE hDstStore;
+ int rc = RTCrStoreCreateSnapshotById(&hDstStore, RTCRSTOREID_USER_TRUSTED_CAS_AND_CERTIFICATES, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Snapshot the system store and add that to the user snapshot. Ignore errors.
+ */
+ RTCRSTORE hSrcStore;
+ rc = RTCrStoreCreateSnapshotById(&hSrcStore, RTCRSTOREID_SYSTEM_TRUSTED_CAS_AND_CERTIFICATES, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrStoreCertAddFromStore(hDstStore, RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
+ hSrcStore);
+ RTCrStoreRelease(hSrcStore);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ *phStore = hDstStore;
+ return rc;
+ }
+
+ /*
+ * If we've got something in the store, return anyway despite of the error condition.
+ */
+ if (rc != VERR_NO_MEMORY && RTCrStoreCertCount(hDstStore) > 0)
+ {
+ *phStore = hDstStore;
+ return -rc;
+ }
+
+ RTCrStoreRelease(hDstStore);
+ }
+ *phStore = NIL_RTCRSTORE;
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/crypto/cipher-openssl.cpp b/src/VBox/Runtime/common/crypto/cipher-openssl.cpp
new file mode 100644
index 00000000..079af227
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/cipher-openssl.cpp
@@ -0,0 +1,596 @@
+/* $Id: cipher-openssl.cpp $ */
+/** @file
+ * IPRT - Crypto - Symmetric Cipher using OpenSSL.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef IPRT_WITH_OPENSSL
+# include "internal/iprt.h"
+# include <iprt/crypto/cipher.h>
+
+# include <iprt/asm.h>
+# include <iprt/assert.h>
+# include <iprt/err.h>
+# include <iprt/mem.h>
+# include <iprt/string.h>
+
+# include "internal/iprt-openssl.h"
+# include "internal/openssl-pre.h"
+# include <openssl/evp.h>
+# include "internal/openssl-post.h"
+
+# include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#if defined(EVP_CTRL_AEAD_GET_TAG)
+# define MY_EVP_CTRL_AEAD_GET_TAG EVP_CTRL_AEAD_GET_TAG
+#else
+# define MY_EVP_CTRL_AEAD_GET_TAG EVP_CTRL_GCM_GET_TAG
+#endif
+
+#if defined(EVP_CTRL_AEAD_SET_TAG)
+# define MY_EVP_CTRL_AEAD_SET_TAG EVP_CTRL_AEAD_SET_TAG
+#else
+# define MY_EVP_CTRL_AEAD_SET_TAG EVP_CTRL_GCM_SET_TAG
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * OpenSSL cipher instance data.
+ */
+typedef struct RTCRCIPHERINT
+{
+ /** Magic value (RTCRCIPHERINT_MAGIC). */
+ uint32_t u32Magic;
+ /** Reference count. */
+ uint32_t volatile cRefs;
+ /** The cihper. */
+ const EVP_CIPHER *pCipher;
+ /** The IPRT cipher type, if we know it. */
+ RTCRCIPHERTYPE enmType;
+} RTCRCIPHERINT;
+
+
+/**
+ * OpenSSL cipher context data
+ */
+typedef struct RTCRCIPHERCTXINT
+{
+ /** Pointer to cipher instance data */
+ RTCRCIPHERINT *phCipher;
+ /** Pointer to cipher context */
+ EVP_CIPHER_CTX *pCipherCtx;
+ /** Is decryption */
+ bool fDecryption;
+} RTCRCIPHERCTXINT;
+
+
+RTDECL(int) RTCrCipherOpenByType(PRTCRCIPHER phCipher, RTCRCIPHERTYPE enmType, uint32_t fFlags)
+{
+ AssertPtrReturn(phCipher, VERR_INVALID_POINTER);
+ *phCipher = NIL_RTCRCIPHER;
+ AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+ /*
+ * Translate the IPRT cipher type to EVP cipher.
+ */
+ const EVP_CIPHER *pCipher = NULL;
+ switch (enmType)
+ {
+ case RTCRCIPHERTYPE_XTS_AES_128:
+ pCipher = EVP_aes_128_xts();
+ break;
+ case RTCRCIPHERTYPE_XTS_AES_256:
+ pCipher = EVP_aes_256_xts();
+ break;
+ case RTCRCIPHERTYPE_GCM_AES_128:
+ pCipher = EVP_aes_128_gcm();
+ break;
+ case RTCRCIPHERTYPE_GCM_AES_256:
+ pCipher = EVP_aes_256_gcm();
+ break;
+ case RTCRCIPHERTYPE_CTR_AES_128:
+ pCipher = EVP_aes_128_ctr();
+ break;
+ case RTCRCIPHERTYPE_CTR_AES_256:
+ pCipher = EVP_aes_256_ctr();
+ break;
+
+ /* no default! */
+ case RTCRCIPHERTYPE_INVALID:
+ case RTCRCIPHERTYPE_END:
+ case RTCRCIPHERTYPE_32BIT_HACK:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+ AssertReturn(pCipher, VERR_CR_CIPHER_NOT_SUPPORTED);
+
+ /*
+ * Create the instance.
+ */
+ RTCRCIPHERINT *pThis = (RTCRCIPHERINT *)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTCRCIPHERINT_MAGIC;
+ pThis->cRefs = 1;
+ pThis->pCipher = pCipher;
+ pThis->enmType = enmType;
+ *phCipher = pThis;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(uint32_t) RTCrCipherRetain(RTCRCIPHER hCipher)
+{
+ RTCRCIPHERINT *pThis = hCipher;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRCIPHERINT_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs > 1 && cRefs < 1024);
+ return cRefs;
+}
+
+
+/**
+ * Destroys the cipher instance.
+ */
+static uint32_t rtCrCipherDestroy(RTCRCIPHER pThis)
+{
+ pThis->u32Magic= ~RTCRCIPHERINT_MAGIC;
+ pThis->pCipher = NULL;
+ RTMemFree(pThis);
+ return 0;
+}
+
+
+RTDECL(uint32_t) RTCrCipherRelease(RTCRCIPHER hCipher)
+{
+ RTCRCIPHERINT *pThis = hCipher;
+ if (pThis == NIL_RTCRCIPHER)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRCIPHERINT_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ Assert(cRefs < 1024);
+ if (cRefs == 0)
+ return rtCrCipherDestroy(pThis);
+ return cRefs;
+}
+
+
+RTDECL(uint32_t) RTCrCipherGetKeyLength(RTCRCIPHER hCipher)
+{
+ RTCRCIPHERINT *pThis = hCipher;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTCRCIPHERINT_MAGIC, 0);
+
+ return EVP_CIPHER_key_length(pThis->pCipher);
+}
+
+
+RTDECL(uint32_t) RTCrCipherGetInitializationVectorLength(RTCRCIPHER hCipher)
+{
+ RTCRCIPHERINT *pThis = hCipher;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTCRCIPHERINT_MAGIC, 0);
+
+ return EVP_CIPHER_iv_length(pThis->pCipher);
+}
+
+
+RTDECL(uint32_t) RTCrCipherGetBlockSize(RTCRCIPHER hCipher)
+{
+ RTCRCIPHERINT *pThis = hCipher;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTCRCIPHERINT_MAGIC, 0);
+
+ return EVP_CIPHER_block_size(pThis->pCipher);
+}
+
+
+RTDECL(int) RTCrCipherCtxFree(RTCRCIPHERCTX hCipherCtx)
+{
+ AssertReturn(hCipherCtx, VERR_INVALID_PARAMETER);
+ RTCRCIPHERCTXINT *pCtx = hCipherCtx;
+
+# if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+ EVP_CIPHER_CTX_free(pCtx->pCipherCtx);
+# else
+ EVP_CIPHER_CTX_cleanup(pCtx->pCipherCtx);
+ RTMemFree(pCtx->pCipherCtx);
+# endif
+ RTMemFree(pCtx);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCrCipherCtxEncryptInit(RTCRCIPHER hCipher, void const *pvKey, size_t cbKey,
+ void const *pvInitVector, size_t cbInitVector,
+ void const *pvAuthData, size_t cbAuthData,
+ PRTCRCIPHERCTX phCipherCtx)
+{
+ /*
+ * Validate input.
+ */
+ RTCRCIPHERINT *pThis = hCipher;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRCIPHERINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertMsgReturn((ssize_t)cbKey == EVP_CIPHER_key_length(pThis->pCipher),
+ ("%zu, expected %d\n", cbKey, EVP_CIPHER_key_length(pThis->pCipher)),
+ VERR_CR_CIPHER_INVALID_KEY_LENGTH);
+ AssertMsgReturn((ssize_t)cbInitVector == EVP_CIPHER_iv_length(pThis->pCipher),
+ ("%zu, expected %d\n", cbInitVector, EVP_CIPHER_iv_length(pThis->pCipher)),
+ VERR_CR_CIPHER_INVALID_INITIALIZATION_VECTOR_LENGTH);
+
+ Assert(EVP_CIPHER_block_size(pThis->pCipher) <= 1); /** @todo more complicated ciphers later */
+
+ /*
+ * Allocate and initialize the cipher context.
+ */
+ int rc = VERR_NO_MEMORY;
+ /*
+ * Create the instance.
+ */
+ RTCRCIPHERCTXINT *pCtx = (RTCRCIPHERCTXINT *)RTMemAlloc(sizeof(RTCRCIPHERCTXINT));
+ if (pCtx)
+ {
+ pCtx->phCipher = hCipher;
+ pCtx->fDecryption = false;
+# if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+ pCtx->pCipherCtx = EVP_CIPHER_CTX_new();
+ if (pCtx->pCipherCtx)
+# else
+ pCtx->pCipherCtx = (EVP_CIPHER_CTX *)RTMemAllocZ(sizeof(EVP_CIPHER_CTX));
+# endif
+ {
+ if (EVP_EncryptInit(pCtx->pCipherCtx, pCtx->phCipher->pCipher, (unsigned char const *)pvKey,
+ (unsigned char const *)pvInitVector))
+ {
+ if (pvAuthData && cbAuthData)
+ {
+ /* Add auth data. */
+ int cbEncryptedAuth = 0;
+ rc = EVP_EncryptUpdate(pCtx->pCipherCtx, NULL, &cbEncryptedAuth,
+ (unsigned char const *)pvAuthData, (int)cbAuthData) ? VINF_SUCCESS
+ : VERR_CR_CIPHER_OSSL_ENCRYPT_UPDATE_FAILED;
+ }
+ else
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_CR_CIPHER_OSSL_ENCRYPT_INIT_FAILED;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ *phCipherCtx = pCtx;
+ else
+ RTCrCipherCtxFree(pCtx);
+ return rc;
+}
+
+
+RTDECL(int) RTCrCipherCtxEncryptProcess(RTCRCIPHERCTX hCipherCtx, void const *pvPlainText, size_t cbPlainText,
+ void *pvEncrypted, size_t cbEncrypted, size_t *pcbEncrypted)
+{
+ AssertReturn(hCipherCtx, VERR_INVALID_PARAMETER);
+ AssertReturn(cbPlainText > 0, VERR_NO_DATA);
+ AssertReturn((size_t)(int)cbPlainText == cbPlainText && (int)cbPlainText > 0, VERR_OUT_OF_RANGE);
+ AssertReturn(cbEncrypted >= cbPlainText, VERR_BUFFER_OVERFLOW);
+
+ RTCRCIPHERCTXINT *pCtx = hCipherCtx;
+ AssertReturn(!pCtx->fDecryption, VERR_INVALID_STATE);
+ int cbEncrypted1 = 0;
+ int rc = VERR_CR_CIPHER_OSSL_ENCRYPT_UPDATE_FAILED;
+ if (EVP_EncryptUpdate(pCtx->pCipherCtx, (unsigned char *)pvEncrypted, &cbEncrypted1,
+ (unsigned char const *)pvPlainText, (int)cbPlainText))
+ {
+ *pcbEncrypted = cbEncrypted1;
+ rc = VINF_SUCCESS;
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTCrCipherCtxEncryptFinish(RTCRCIPHERCTX hCipherCtx,
+ void *pvEncrypted, size_t *pcbEncrypted,
+ void *pvTag, size_t cbTag, size_t *pcbTag)
+{
+ AssertReturn(hCipherCtx, VERR_INVALID_PARAMETER);
+ RTCRCIPHERCTXINT *pCtx = hCipherCtx;
+ AssertReturn(!pCtx->fDecryption, VERR_INVALID_STATE);
+ AssertReturn(!pvTag || (pvTag && cbTag == 16), VERR_CR_CIPHER_INVALID_TAG_LENGTH);
+ int cbEncrypted2 = 0;
+ int rc = VERR_CR_CIPHER_OSSL_ENCRYPT_FINAL_FAILED;
+ if (EVP_EncryptFinal(pCtx->pCipherCtx, (uint8_t *)pvEncrypted, &cbEncrypted2))
+ {
+ if (pvTag && cbTag)
+ {
+ if (EVP_CIPHER_CTX_ctrl(pCtx->pCipherCtx, MY_EVP_CTRL_AEAD_GET_TAG, (int)cbTag, pvTag))
+ {
+ *pcbTag = cbTag;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_CR_CIPHER_OSSL_GET_TAG_FAILED;
+ }
+ else
+ rc = VINF_SUCCESS;
+
+ if (RT_SUCCESS(rc) && pcbEncrypted)
+ *pcbEncrypted = cbEncrypted2;
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTCrCipherCtxDecryptInit(RTCRCIPHER hCipher, void const *pvKey, size_t cbKey,
+ void const *pvInitVector, size_t cbInitVector,
+ void const *pvAuthData, size_t cbAuthData,
+ void *pvTag, size_t cbTag, PRTCRCIPHERCTX phCipherCtx)
+{
+ /*
+ * Validate input.
+ */
+ RTCRCIPHERINT *pThis = hCipher;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRCIPHERINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertMsgReturn((ssize_t)cbKey == EVP_CIPHER_key_length(pThis->pCipher),
+ ("%zu, expected %d\n", cbKey, EVP_CIPHER_key_length(pThis->pCipher)),
+ VERR_CR_CIPHER_INVALID_KEY_LENGTH);
+ AssertMsgReturn((ssize_t)cbInitVector == EVP_CIPHER_iv_length(pThis->pCipher),
+ ("%zu, expected %d\n", cbInitVector, EVP_CIPHER_iv_length(pThis->pCipher)),
+ VERR_CR_CIPHER_INVALID_INITIALIZATION_VECTOR_LENGTH);
+ AssertReturn(!pvTag || (pvTag && cbTag == 16), VERR_CR_CIPHER_INVALID_TAG_LENGTH);
+
+ Assert(EVP_CIPHER_block_size(pThis->pCipher) <= 1); /** @todo more complicated ciphers later */
+
+ /*
+ * Allocate and initialize the cipher context.
+ */
+ int rc = VERR_NO_MEMORY;
+ /*
+ * Create the instance.
+ */
+ RTCRCIPHERCTXINT *pCtx = (RTCRCIPHERCTXINT *)RTMemAlloc(sizeof(RTCRCIPHERCTXINT));
+ if (pCtx)
+ {
+ pCtx->phCipher = hCipher;
+ pCtx->fDecryption = true;
+# if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+ pCtx->pCipherCtx = EVP_CIPHER_CTX_new();
+# else
+ pCtx->pCipherCtx = (EVP_CIPHER_CTX *)RTMemAllocZ(sizeof(EVP_CIPHER_CTX));
+# endif
+
+ if (EVP_DecryptInit(pCtx->pCipherCtx, pThis->pCipher, (unsigned char const *)pvKey,
+ (unsigned char const *)pvInitVector))
+ {
+ rc = VINF_SUCCESS;
+ if (pvTag && cbTag && !EVP_CIPHER_CTX_ctrl(pCtx->pCipherCtx, MY_EVP_CTRL_AEAD_SET_TAG, (int)cbTag, pvTag))
+ rc = VERR_CR_CIPHER_OSSL_SET_TAG_FAILED;
+
+ if (RT_SUCCESS(rc) && pvAuthData && cbAuthData)
+ {
+ /* Add auth data. */
+ int cbDecryptedAuth = 0;
+ if (!EVP_DecryptUpdate(pCtx->pCipherCtx, NULL, &cbDecryptedAuth,
+ (unsigned char const *)pvAuthData, (int)cbAuthData))
+ rc = VERR_CR_CIPHER_OSSL_DECRYPT_UPDATE_FAILED;
+ }
+ }
+ else
+ rc = VERR_CR_CIPHER_OSSL_DECRYPT_INIT_FAILED;
+ }
+
+ if (RT_SUCCESS(rc))
+ *phCipherCtx = pCtx;
+ else
+ RTCrCipherCtxFree(pCtx);
+
+ return rc;
+}
+
+
+RTDECL(int) RTCrCipherCtxDecryptProcess(RTCRCIPHERCTX hCipherCtx,
+ void const *pvEncrypted, size_t cbEncrypted,
+ void *pvPlainText, size_t cbPlainText, size_t *pcbPlainText)
+{
+ AssertReturn(hCipherCtx, VERR_INVALID_PARAMETER);
+ AssertReturn(cbEncrypted > 0, VERR_NO_DATA);
+ AssertReturn((size_t)(int)cbEncrypted == cbEncrypted && (int)cbEncrypted > 0, VERR_OUT_OF_RANGE);
+ AssertReturn(cbPlainText >= cbEncrypted, VERR_BUFFER_OVERFLOW);
+
+ RTCRCIPHERCTXINT *pCtx = hCipherCtx;
+ AssertReturn(pCtx->fDecryption, VERR_INVALID_STATE);
+ int rc = VERR_CR_CIPHER_OSSL_DECRYPT_UPDATE_FAILED;
+ int cbDecrypted1 = 0;
+ if (EVP_DecryptUpdate(pCtx->pCipherCtx, (unsigned char *)pvPlainText, &cbDecrypted1,
+ (unsigned char const *)pvEncrypted, (int)cbEncrypted))
+ {
+ *pcbPlainText = cbDecrypted1;
+ rc = VINF_SUCCESS;
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTCrCipherCtxDecryptFinish(RTCRCIPHERCTX hCipherCtx,
+ void *pvPlainText, size_t *pcbPlainText)
+{
+ AssertReturn(hCipherCtx, VERR_INVALID_PARAMETER);
+ RTCRCIPHERCTXINT *pCtx = hCipherCtx;
+ AssertReturn(pCtx->fDecryption, VERR_INVALID_STATE);
+ int cbDecrypted2 = 0;
+ int rc = VERR_CR_CIPHER_OSSL_ENCRYPT_FINAL_FAILED;
+ if (EVP_DecryptFinal(pCtx->pCipherCtx, (uint8_t *)pvPlainText, &cbDecrypted2))
+ {
+ rc = VINF_SUCCESS;
+ if (pcbPlainText)
+ *pcbPlainText = cbDecrypted2;
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTCrCipherEncrypt(RTCRCIPHER hCipher, void const *pvKey, size_t cbKey,
+ void const *pvInitVector, size_t cbInitVector,
+ void const *pvPlainText, size_t cbPlainText,
+ void *pvEncrypted, size_t cbEncrypted, size_t *pcbEncrypted)
+{
+ return RTCrCipherEncryptEx(hCipher, pvKey, cbKey, pvInitVector, cbInitVector,
+ NULL, 0, pvPlainText, cbPlainText, pvEncrypted, cbEncrypted,
+ pcbEncrypted, NULL, 0, NULL);
+}
+
+
+RTDECL(int) RTCrCipherDecrypt(RTCRCIPHER hCipher, void const *pvKey, size_t cbKey,
+ void const *pvInitVector, size_t cbInitVector,
+ void const *pvEncrypted, size_t cbEncrypted,
+ void *pvPlainText, size_t cbPlainText, size_t *pcbPlainText)
+{
+ return RTCrCipherDecryptEx(hCipher, pvKey, cbKey, pvInitVector, cbInitVector,
+ NULL, 0, NULL, 0, pvEncrypted, cbEncrypted,
+ pvPlainText, cbPlainText, pcbPlainText);
+}
+
+
+RTDECL(int) RTCrCipherEncryptEx(RTCRCIPHER hCipher, void const *pvKey, size_t cbKey,
+ void const *pvInitVector, size_t cbInitVector,
+ void const *pvAuthData, size_t cbAuthData,
+ void const *pvPlainText, size_t cbPlainText,
+ void *pvEncrypted, size_t cbEncrypted, size_t *pcbEncrypted,
+ void *pvTag, size_t cbTag, size_t *pcbTag)
+{
+ size_t const cbNeeded = cbPlainText;
+ if (pcbEncrypted)
+ {
+ *pcbEncrypted = cbNeeded;
+ AssertReturn(cbEncrypted >= cbNeeded, VERR_BUFFER_OVERFLOW);
+ }
+ else
+ AssertReturn(cbEncrypted == cbNeeded, VERR_INVALID_PARAMETER);
+ AssertReturn((size_t)(int)cbPlainText == cbPlainText && (int)cbPlainText > 0, VERR_OUT_OF_RANGE);
+
+ RTCRCIPHERCTXINT *pCtx = NIL_RTCRCIPHERCTX;
+
+ int rc = RTCrCipherCtxEncryptInit(hCipher, pvKey, cbKey, pvInitVector, cbInitVector,
+ pvAuthData, cbAuthData, &pCtx);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbEncrypted1 = 0;
+ rc = RTCrCipherCtxEncryptProcess(pCtx, pvPlainText, cbPlainText, pvEncrypted, cbEncrypted, &cbEncrypted1);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbEncrypted2 = 0;
+ rc = RTCrCipherCtxEncryptFinish(pCtx, (unsigned char *)pvEncrypted + cbEncrypted1,
+ &cbEncrypted2, pvTag, cbTag, pcbTag);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(cbEncrypted1 + cbEncrypted2 == cbNeeded);
+ if (pcbEncrypted)
+ *pcbEncrypted = cbEncrypted1 + cbEncrypted2;
+ }
+ }
+ }
+
+ if (pCtx != NIL_RTCRCIPHERCTX)
+ RTCrCipherCtxFree(pCtx);
+
+ return rc;
+}
+
+
+RTDECL(int) RTCrCipherDecryptEx(RTCRCIPHER hCipher, void const *pvKey, size_t cbKey,
+ void const *pvInitVector, size_t cbInitVector,
+ void const *pvAuthData, size_t cbAuthData,
+ void *pvTag, size_t cbTag,
+ void const *pvEncrypted, size_t cbEncrypted,
+ void *pvPlainText, size_t cbPlainText, size_t *pcbPlainText)
+{
+ size_t const cbNeeded = cbEncrypted;
+ if (pcbPlainText)
+ {
+ *pcbPlainText = cbNeeded;
+ AssertReturn(cbPlainText >= cbNeeded, VERR_BUFFER_OVERFLOW);
+ }
+ else
+ AssertReturn(cbPlainText == cbNeeded, VERR_INVALID_PARAMETER);
+ AssertReturn((size_t)(int)cbEncrypted == cbEncrypted && (int)cbEncrypted > 0, VERR_OUT_OF_RANGE);
+
+ RTCRCIPHERCTXINT *pCtx = NIL_RTCRCIPHERCTX;
+
+ int rc = RTCrCipherCtxDecryptInit(hCipher, pvKey, cbKey, pvInitVector, cbInitVector,
+ pvAuthData, cbAuthData, pvTag, cbTag, &pCtx);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbDecrypted1 = 0;
+ rc = RTCrCipherCtxDecryptProcess(pCtx, pvEncrypted, cbEncrypted, pvPlainText, cbPlainText, &cbDecrypted1);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbDecrypted2 = 0;
+ rc = RTCrCipherCtxDecryptFinish(pCtx, (unsigned char *)pvPlainText + cbDecrypted1,
+ &cbDecrypted2);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(cbDecrypted1 + cbDecrypted2 == cbNeeded);
+ if (pcbPlainText)
+ *pcbPlainText = cbDecrypted1 + cbDecrypted2;
+ }
+ }
+ }
+
+ if (pCtx != NIL_RTCRCIPHERCTX)
+ RTCrCipherCtxFree(pCtx);
+
+ return rc;
+}
+
+#endif /* IPRT_WITH_OPENSSL */
+
diff --git a/src/VBox/Runtime/common/crypto/digest-builtin.cpp b/src/VBox/Runtime/common/crypto/digest-builtin.cpp
new file mode 100644
index 00000000..500108e8
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/digest-builtin.cpp
@@ -0,0 +1,1175 @@
+/* $Id: digest-builtin.cpp $ */
+/** @file
+ * IPRT - Crypto - Cryptographic Hash / Message Digest API, Built-in providers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/digest.h>
+
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/md2.h>
+#include <iprt/md4.h>
+#include <iprt/md5.h>
+#include <iprt/sha.h>
+#include <iprt/crypto/pkix.h>
+
+#ifdef IPRT_WITH_OPENSSL
+# include "internal/iprt-openssl.h"
+# include "internal/openssl-pre.h"
+# include <openssl/evp.h>
+# include "internal/openssl-post.h"
+#endif
+
+
+
+/*
+ * MD2
+ */
+#ifndef IPRT_WITHOUT_DIGEST_MD2
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnUpdate} */
+static DECLCALLBACK(void) rtCrDigestMd2_Update(void *pvState, const void *pvData, size_t cbData)
+{
+ RTMd2Update((PRTMD2CONTEXT)pvState, pvData, cbData);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnFinal} */
+static DECLCALLBACK(void) rtCrDigestMd2_Final(void *pvState, uint8_t *pbHash)
+{
+ RTMd2Final((PRTMD2CONTEXT)pvState, pbHash);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnInit} */
+static DECLCALLBACK(int) rtCrDigestMd2_Init(void *pvState, void *pvOpaque, bool fReInit)
+{
+ RT_NOREF_PV(fReInit); RT_NOREF_PV(pvOpaque);
+ AssertReturn(pvOpaque == NULL, VERR_INVALID_PARAMETER);
+ RTMd2Init((PRTMD2CONTEXT)pvState);
+ return VINF_SUCCESS;
+}
+
+/** MD2 alias ODIs. */
+static const char * const g_apszMd2Aliases[] =
+{
+ RTCR_PKCS1_MD2_WITH_RSA_OID,
+ "1.3.14.3.2.24" /* OIW md2WithRSASignature */,
+ NULL
+};
+
+/** MD2 descriptor. */
+static RTCRDIGESTDESC const g_rtCrDigestMd2Desc =
+{
+ "md2",
+ "1.2.840.113549.2.2",
+ g_apszMd2Aliases,
+ RTDIGESTTYPE_MD2,
+ RTMD2_HASH_SIZE,
+ sizeof(RTMD2CONTEXT),
+ RTCRDIGESTDESC_F_DEPRECATED,
+ NULL,
+ NULL,
+ rtCrDigestMd2_Update,
+ rtCrDigestMd2_Final,
+ rtCrDigestMd2_Init,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+#endif /* !IPRT_WITHOUT_DIGEST_MD2 */
+
+
+/*
+ * MD4
+ */
+#ifndef IPRT_WITHOUT_DIGEST_MD4
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnUpdate} */
+static DECLCALLBACK(void) rtCrDigestMd4_Update(void *pvState, const void *pvData, size_t cbData)
+{
+ RTMd4Update((PRTMD4CONTEXT)pvState, pvData, cbData);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnFinal} */
+static DECLCALLBACK(void) rtCrDigestMd4_Final(void *pvState, uint8_t *pbHash)
+{
+ RTMd4Final((PRTMD4CONTEXT)pvState, pbHash);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnInit} */
+static DECLCALLBACK(int) rtCrDigestMd4_Init(void *pvState, void *pvOpaque, bool fReInit)
+{
+ RT_NOREF_PV(fReInit); RT_NOREF_PV(pvOpaque);
+ AssertReturn(pvOpaque == NULL, VERR_INVALID_PARAMETER);
+ RTMd4Init((PRTMD4CONTEXT)pvState);
+ return VINF_SUCCESS;
+}
+
+/** MD4 alias ODIs. */
+static const char * const g_apszMd4Aliases[] =
+{
+ RTCR_PKCS1_MD4_WITH_RSA_OID,
+ NULL
+};
+
+/** MD4 descriptor. */
+static RTCRDIGESTDESC const g_rtCrDigestMd4Desc =
+{
+ "md4",
+ "1.2.840.113549.2.4",
+ g_apszMd4Aliases,
+ RTDIGESTTYPE_MD4,
+ RTMD4_HASH_SIZE,
+ sizeof(RTMD4CONTEXT),
+ RTCRDIGESTDESC_F_DEPRECATED | RTCRDIGESTDESC_F_COMPROMISED | RTCRDIGESTDESC_F_SERVERELY_COMPROMISED,
+ NULL,
+ NULL,
+ rtCrDigestMd4_Update,
+ rtCrDigestMd4_Final,
+ rtCrDigestMd4_Init,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+#endif /* !IPRT_WITHOUT_DIGEST_MD4 */
+
+
+/*
+ * MD5
+ */
+#ifndef IPRT_WITHOUT_DIGEST_MD5
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnUpdate} */
+static DECLCALLBACK(void) rtCrDigestMd5_Update(void *pvState, const void *pvData, size_t cbData)
+{
+ RTMd5Update((PRTMD5CONTEXT)pvState, pvData, cbData);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnFinal} */
+static DECLCALLBACK(void) rtCrDigestMd5_Final(void *pvState, uint8_t *pbHash)
+{
+ RTMd5Final(pbHash, (PRTMD5CONTEXT)pvState);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnInit} */
+static DECLCALLBACK(int) rtCrDigestMd5_Init(void *pvState, void *pvOpaque, bool fReInit)
+{
+ RT_NOREF_PV(pvOpaque); RT_NOREF_PV(fReInit);
+ AssertReturn(pvOpaque == NULL, VERR_INVALID_PARAMETER);
+ RTMd5Init((PRTMD5CONTEXT)pvState);
+ return VINF_SUCCESS;
+}
+
+/** MD5 alias ODIs. */
+static const char * const g_apszMd5Aliases[] =
+{
+ RTCR_PKCS1_MD5_WITH_RSA_OID,
+ "1.3.14.3.2.25" /* OIW md5WithRSASignature */,
+ NULL
+};
+
+/** MD5 descriptor. */
+static RTCRDIGESTDESC const g_rtCrDigestMd5Desc =
+{
+ "md5",
+ "1.2.840.113549.2.5",
+ g_apszMd5Aliases,
+ RTDIGESTTYPE_MD5,
+ RTMD5_HASH_SIZE,
+ sizeof(RTMD5CONTEXT),
+ RTCRDIGESTDESC_F_COMPROMISED,
+ NULL,
+ NULL,
+ rtCrDigestMd5_Update,
+ rtCrDigestMd5_Final,
+ rtCrDigestMd5_Init,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+#endif /* !IPRT_WITHOUT_DIGEST_MD5 */
+
+
+/*
+ * SHA-1
+ */
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnUpdate} */
+static DECLCALLBACK(void) rtCrDigestSha1_Update(void *pvState, const void *pvData, size_t cbData)
+{
+ RTSha1Update((PRTSHA1CONTEXT)pvState, pvData, cbData);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnFinal} */
+static DECLCALLBACK(void) rtCrDigestSha1_Final(void *pvState, uint8_t *pbHash)
+{
+ RTSha1Final((PRTSHA1CONTEXT)pvState, pbHash);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnInit} */
+static DECLCALLBACK(int) rtCrDigestSha1_Init(void *pvState, void *pvOpaque, bool fReInit)
+{
+ RT_NOREF_PV(pvOpaque); RT_NOREF_PV(fReInit);
+ AssertReturn(pvOpaque == NULL, VERR_INVALID_PARAMETER);
+ RTSha1Init((PRTSHA1CONTEXT)pvState);
+ return VINF_SUCCESS;
+}
+
+/** SHA-1 alias ODIs. */
+static const char * const g_apszSha1Aliases[] =
+{
+ RTCR_PKCS1_SHA1_WITH_RSA_OID,
+ "1.3.14.3.2.29" /* OIW sha1WithRSASignature */,
+ RTCR_X962_ECDSA_WITH_SHA1_OID,
+ NULL
+};
+
+/** SHA-1 descriptor. */
+static RTCRDIGESTDESC const g_rtCrDigestSha1Desc =
+{
+ "sha-1",
+ "1.3.14.3.2.26",
+ g_apszSha1Aliases,
+ RTDIGESTTYPE_SHA1,
+ RTSHA1_HASH_SIZE,
+ sizeof(RTSHA1CONTEXT),
+ RTCRDIGESTDESC_F_DEPRECATED,
+ NULL,
+ NULL,
+ rtCrDigestSha1_Update,
+ rtCrDigestSha1_Final,
+ rtCrDigestSha1_Init,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+
+/*
+ * SHA-256
+ */
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnUpdate} */
+static DECLCALLBACK(void) rtCrDigestSha256_Update(void *pvState, const void *pvData, size_t cbData)
+{
+ RTSha256Update((PRTSHA256CONTEXT)pvState, pvData, cbData);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnFinal} */
+static DECLCALLBACK(void) rtCrDigestSha256_Final(void *pvState, uint8_t *pbHash)
+{
+ RTSha256Final((PRTSHA256CONTEXT)pvState, pbHash);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnInit} */
+static DECLCALLBACK(int) rtCrDigestSha256_Init(void *pvState, void *pvOpaque, bool fReInit)
+{
+ RT_NOREF_PV(pvOpaque); RT_NOREF_PV(fReInit);
+ AssertReturn(pvOpaque == NULL, VERR_INVALID_PARAMETER);
+ RTSha256Init((PRTSHA256CONTEXT)pvState);
+ return VINF_SUCCESS;
+}
+
+/** SHA-256 alias ODIs. */
+static const char * const g_apszSha256Aliases[] =
+{
+ RTCR_PKCS1_SHA256_WITH_RSA_OID,
+ RTCR_X962_ECDSA_WITH_SHA256_OID,
+ NULL
+};
+
+/** SHA-256 descriptor. */
+static RTCRDIGESTDESC const g_rtCrDigestSha256Desc =
+{
+ "sha-256",
+ "2.16.840.1.101.3.4.2.1",
+ g_apszSha256Aliases,
+ RTDIGESTTYPE_SHA256,
+ RTSHA256_HASH_SIZE,
+ sizeof(RTSHA256CONTEXT),
+ 0,
+ NULL,
+ NULL,
+ rtCrDigestSha256_Update,
+ rtCrDigestSha256_Final,
+ rtCrDigestSha256_Init,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+
+/*
+ * SHA-512
+ */
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnUpdate} */
+static DECLCALLBACK(void) rtCrDigestSha512_Update(void *pvState, const void *pvData, size_t cbData)
+{
+ RTSha512Update((PRTSHA512CONTEXT)pvState, pvData, cbData);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnFinal} */
+static DECLCALLBACK(void) rtCrDigestSha512_Final(void *pvState, uint8_t *pbHash)
+{
+ RTSha512Final((PRTSHA512CONTEXT)pvState, pbHash);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnInit} */
+static DECLCALLBACK(int) rtCrDigestSha512_Init(void *pvState, void *pvOpaque, bool fReInit)
+{
+ RT_NOREF_PV(pvOpaque); RT_NOREF_PV(fReInit);
+ AssertReturn(pvOpaque == NULL, VERR_INVALID_PARAMETER);
+ RTSha512Init((PRTSHA512CONTEXT)pvState);
+ return VINF_SUCCESS;
+}
+
+/** SHA-512 alias ODIs. */
+static const char * const g_apszSha512Aliases[] =
+{
+ RTCR_PKCS1_SHA512_WITH_RSA_OID,
+ RTCR_X962_ECDSA_WITH_SHA512_OID,
+ NULL
+};
+
+/** SHA-512 descriptor. */
+static RTCRDIGESTDESC const g_rtCrDigestSha512Desc =
+{
+ "sha-512",
+ "2.16.840.1.101.3.4.2.3",
+ g_apszSha512Aliases,
+ RTDIGESTTYPE_SHA512,
+ RTSHA512_HASH_SIZE,
+ sizeof(RTSHA512CONTEXT),
+ 0,
+ NULL,
+ NULL,
+ rtCrDigestSha512_Update,
+ rtCrDigestSha512_Final,
+ rtCrDigestSha512_Init,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+
+/*
+ * SHA-224
+ */
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnUpdate} */
+static DECLCALLBACK(void) rtCrDigestSha224_Update(void *pvState, const void *pvData, size_t cbData)
+{
+ RTSha224Update((PRTSHA224CONTEXT)pvState, pvData, cbData);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnFinal} */
+static DECLCALLBACK(void) rtCrDigestSha224_Final(void *pvState, uint8_t *pbHash)
+{
+ RTSha224Final((PRTSHA224CONTEXT)pvState, pbHash);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnInit} */
+static DECLCALLBACK(int) rtCrDigestSha224_Init(void *pvState, void *pvOpaque, bool fReInit)
+{
+ RT_NOREF_PV(pvOpaque); RT_NOREF_PV(fReInit);
+ AssertReturn(pvOpaque == NULL, VERR_INVALID_PARAMETER);
+ RTSha224Init((PRTSHA224CONTEXT)pvState);
+ return VINF_SUCCESS;
+}
+
+/** SHA-224 alias ODIs. */
+static const char * const g_apszSha224Aliases[] =
+{
+ RTCR_PKCS1_SHA224_WITH_RSA_OID,
+ RTCR_X962_ECDSA_WITH_SHA224_OID,
+ NULL
+};
+
+/** SHA-224 descriptor. */
+static RTCRDIGESTDESC const g_rtCrDigestSha224Desc =
+{
+ "sha-224",
+ "2.16.840.1.101.3.4.2.4",
+ g_apszSha224Aliases,
+ RTDIGESTTYPE_SHA224,
+ RTSHA224_HASH_SIZE,
+ sizeof(RTSHA224CONTEXT),
+ 0,
+ NULL,
+ NULL,
+ rtCrDigestSha224_Update,
+ rtCrDigestSha224_Final,
+ rtCrDigestSha224_Init,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+
+/*
+ * SHA-384
+ */
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnUpdate} */
+static DECLCALLBACK(void) rtCrDigestSha384_Update(void *pvState, const void *pvData, size_t cbData)
+{
+ RTSha384Update((PRTSHA384CONTEXT)pvState, pvData, cbData);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnFinal} */
+static DECLCALLBACK(void) rtCrDigestSha384_Final(void *pvState, uint8_t *pbHash)
+{
+ RTSha384Final((PRTSHA384CONTEXT)pvState, pbHash);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnInit} */
+static DECLCALLBACK(int) rtCrDigestSha384_Init(void *pvState, void *pvOpaque, bool fReInit)
+{
+ RT_NOREF_PV(pvOpaque); RT_NOREF_PV(fReInit);
+ AssertReturn(pvOpaque == NULL, VERR_INVALID_PARAMETER);
+ RTSha384Init((PRTSHA384CONTEXT)pvState);
+ return VINF_SUCCESS;
+}
+
+/** SHA-384 alias ODIs. */
+static const char * const g_apszSha384Aliases[] =
+{
+ RTCR_PKCS1_SHA384_WITH_RSA_OID,
+ RTCR_X962_ECDSA_WITH_SHA384_OID,
+ NULL
+};
+
+/** SHA-384 descriptor. */
+static RTCRDIGESTDESC const g_rtCrDigestSha384Desc =
+{
+ "sha-384",
+ "2.16.840.1.101.3.4.2.2",
+ g_apszSha384Aliases,
+ RTDIGESTTYPE_SHA384,
+ RTSHA384_HASH_SIZE,
+ sizeof(RTSHA384CONTEXT),
+ 0,
+ NULL,
+ NULL,
+ rtCrDigestSha384_Update,
+ rtCrDigestSha384_Final,
+ rtCrDigestSha384_Init,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+
+#ifndef IPRT_WITHOUT_SHA512T224
+/*
+ * SHA-512/224
+ */
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnUpdate} */
+static DECLCALLBACK(void) rtCrDigestSha512t224_Update(void *pvState, const void *pvData, size_t cbData)
+{
+ RTSha512t224Update((PRTSHA512T224CONTEXT)pvState, pvData, cbData);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnFinal} */
+static DECLCALLBACK(void) rtCrDigestSha512t224_Final(void *pvState, uint8_t *pbHash)
+{
+ RTSha512t224Final((PRTSHA512T224CONTEXT)pvState, pbHash);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnInit} */
+static DECLCALLBACK(int) rtCrDigestSha512t224_Init(void *pvState, void *pvOpaque, bool fReInit)
+{
+ RT_NOREF_PV(pvOpaque); RT_NOREF_PV(fReInit);
+ AssertReturn(pvOpaque == NULL, VERR_INVALID_PARAMETER);
+ RTSha512t224Init((PRTSHA512T224CONTEXT)pvState);
+ return VINF_SUCCESS;
+}
+
+/** SHA-512/224 alias ODIs. */
+static const char * const g_apszSha512t224Aliases[] =
+{
+ RTCR_PKCS1_SHA512T224_WITH_RSA_OID,
+ NULL
+};
+
+/** SHA-512/224 descriptor. */
+static RTCRDIGESTDESC const g_rtCrDigestSha512t224Desc =
+{
+ "sha-512/224",
+ "2.16.840.1.101.3.4.2.5",
+ g_apszSha512t224Aliases,
+ RTDIGESTTYPE_SHA512T224,
+ RTSHA512T224_HASH_SIZE,
+ sizeof(RTSHA512T224CONTEXT),
+ 0,
+ NULL,
+ NULL,
+ rtCrDigestSha512t224_Update,
+ rtCrDigestSha512t224_Final,
+ rtCrDigestSha512t224_Init,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+#endif /* !IPRT_WITHOUT_SHA512T224 */
+
+
+#ifndef IPRT_WITHOUT_SHA512T256
+/*
+ * SHA-512/256
+ */
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnUpdate} */
+static DECLCALLBACK(void) rtCrDigestSha512t256_Update(void *pvState, const void *pvData, size_t cbData)
+{
+ RTSha512t256Update((PRTSHA512T256CONTEXT)pvState, pvData, cbData);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnFinal} */
+static DECLCALLBACK(void) rtCrDigestSha512t256_Final(void *pvState, uint8_t *pbHash)
+{
+ RTSha512t256Final((PRTSHA512T256CONTEXT)pvState, pbHash);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnInit} */
+static DECLCALLBACK(int) rtCrDigestSha512t256_Init(void *pvState, void *pvOpaque, bool fReInit)
+{
+ RT_NOREF_PV(pvOpaque); RT_NOREF_PV(fReInit);
+ AssertReturn(pvOpaque == NULL, VERR_INVALID_PARAMETER);
+ RTSha512t256Init((PRTSHA512T256CONTEXT)pvState);
+ return VINF_SUCCESS;
+}
+
+/** SHA-512/256 alias ODIs. */
+static const char * const g_apszSha512t256Aliases[] =
+{
+ RTCR_PKCS1_SHA512T256_WITH_RSA_OID,
+ NULL
+};
+
+/** SHA-512/256 descriptor. */
+static RTCRDIGESTDESC const g_rtCrDigestSha512t256Desc =
+{
+ "sha-512/256",
+ "2.16.840.1.101.3.4.2.6",
+ g_apszSha512t256Aliases,
+ RTDIGESTTYPE_SHA512T256,
+ RTSHA512T256_HASH_SIZE,
+ sizeof(RTSHA512T256CONTEXT),
+ 0,
+ NULL,
+ NULL,
+ rtCrDigestSha512t256_Update,
+ rtCrDigestSha512t256_Final,
+ rtCrDigestSha512t256_Init,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+#endif /* !IPRT_WITHOUT_SHA512T256 */
+
+#ifndef IPRT_WITHOUT_SHA3
+
+/*
+ * SHA3-224
+ */
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnUpdate} */
+static DECLCALLBACK(void) rtCrDigestSha3t224_Update(void *pvState, const void *pvData, size_t cbData)
+{
+ int rc = RTSha3t224Update((PRTSHA3T224CONTEXT)pvState, pvData, cbData);
+ AssertRC(rc);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnFinal} */
+static DECLCALLBACK(void) rtCrDigestSha3t224_Final(void *pvState, uint8_t *pbHash)
+{
+ int rc = RTSha3t224Final((PRTSHA3T224CONTEXT)pvState, pbHash);
+ AssertRC(rc);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnInit} */
+static DECLCALLBACK(int) rtCrDigestSha3t224_Init(void *pvState, void *pvOpaque, bool fReInit)
+{
+ RT_NOREF_PV(pvOpaque);
+ AssertReturn(pvOpaque == NULL, VERR_INVALID_PARAMETER);
+ if (fReInit)
+ RTSha3t224Cleanup((PRTSHA3T224CONTEXT)pvState);
+ return RTSha3t224Init((PRTSHA3T224CONTEXT)pvState);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnDelete} */
+static DECLCALLBACK(void) rtCrDigestSha3t224_Delete(void *pvState)
+{
+ RTSha3t224Cleanup((PRTSHA3T224CONTEXT)pvState);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnDelete} */
+static DECLCALLBACK(int) rtCrDigestSha3t224_Clone(void *pvState, void const *pvSrcState)
+{
+ return RTSha3t224Clone((PRTSHA3T224CONTEXT)pvState, (PRTSHA3T224CONTEXT)pvSrcState);
+}
+
+/** SHA3-224 alias ODIs. */
+static const char * const g_apszSha3t224Aliases[] =
+{
+ "2.16.840.1.101.3.4.3.13",
+ RTCR_NIST_SHA3_224_WITH_ECDSA_OID,
+ NULL
+};
+
+/** SHA3-224 descriptor. */
+static RTCRDIGESTDESC const g_rtCrDigestSha3t224Desc =
+{
+ "sha3-224",
+ "2.16.840.1.101.3.4.2.7",
+ g_apszSha3t224Aliases,
+ RTDIGESTTYPE_SHA3_224,
+ RTSHA3_224_HASH_SIZE,
+ sizeof(RTSHA3T224CONTEXT),
+ 0,
+ NULL,
+ NULL,
+ rtCrDigestSha3t224_Update,
+ rtCrDigestSha3t224_Final,
+ rtCrDigestSha3t224_Init,
+ rtCrDigestSha3t224_Delete,
+ rtCrDigestSha3t224_Clone,
+ NULL,
+ NULL,
+};
+
+
+/*
+ * SHA3-256
+ */
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnUpdate} */
+static DECLCALLBACK(void) rtCrDigestSha3t256_Update(void *pvState, const void *pvData, size_t cbData)
+{
+ int rc = RTSha3t256Update((PRTSHA3T256CONTEXT)pvState, pvData, cbData);
+ AssertRC(rc);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnFinal} */
+static DECLCALLBACK(void) rtCrDigestSha3t256_Final(void *pvState, uint8_t *pbHash)
+{
+ int rc = RTSha3t256Final((PRTSHA3T256CONTEXT)pvState, pbHash);
+ AssertRC(rc);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnInit} */
+static DECLCALLBACK(int) rtCrDigestSha3t256_Init(void *pvState, void *pvOpaque, bool fReInit)
+{
+ RT_NOREF_PV(pvOpaque);
+ AssertReturn(pvOpaque == NULL, VERR_INVALID_PARAMETER);
+ if (fReInit)
+ RTSha3t256Cleanup((PRTSHA3T256CONTEXT)pvState);
+ return RTSha3t256Init((PRTSHA3T256CONTEXT)pvState);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnDelete} */
+static DECLCALLBACK(void) rtCrDigestSha3t256_Delete(void *pvState)
+{
+ RTSha3t256Cleanup((PRTSHA3T256CONTEXT)pvState);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnDelete} */
+static DECLCALLBACK(int) rtCrDigestSha3t256_Clone(void *pvState, void const *pvSrcState)
+{
+ return RTSha3t256Clone((PRTSHA3T256CONTEXT)pvState, (PRTSHA3T256CONTEXT)pvSrcState);
+}
+
+/** SHA3-256 alias ODIs. */
+static const char * const g_apszSha3t256Aliases[] =
+{
+ "2.16.840.1.101.3.4.3.14",
+ RTCR_NIST_SHA3_256_WITH_ECDSA_OID,
+ NULL
+};
+
+/** SHA3-256 descriptor. */
+static RTCRDIGESTDESC const g_rtCrDigestSha3t256Desc =
+{
+ "sha3-256",
+ "2.16.840.1.101.3.4.2.8",
+ g_apszSha3t256Aliases,
+ RTDIGESTTYPE_SHA3_256,
+ RTSHA3_256_HASH_SIZE,
+ sizeof(RTSHA3T256CONTEXT),
+ 0,
+ NULL,
+ NULL,
+ rtCrDigestSha3t256_Update,
+ rtCrDigestSha3t256_Final,
+ rtCrDigestSha3t256_Init,
+ rtCrDigestSha3t256_Delete,
+ rtCrDigestSha3t256_Clone,
+ NULL,
+ NULL,
+};
+
+
+/*
+ * SHA3-384
+ */
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnUpdate} */
+static DECLCALLBACK(void) rtCrDigestSha3t384_Update(void *pvState, const void *pvData, size_t cbData)
+{
+ int rc = RTSha3t384Update((PRTSHA3T384CONTEXT)pvState, pvData, cbData);
+ AssertRC(rc);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnFinal} */
+static DECLCALLBACK(void) rtCrDigestSha3t384_Final(void *pvState, uint8_t *pbHash)
+{
+ int rc = RTSha3t384Final((PRTSHA3T384CONTEXT)pvState, pbHash);
+ AssertRC(rc);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnInit} */
+static DECLCALLBACK(int) rtCrDigestSha3t384_Init(void *pvState, void *pvOpaque, bool fReInit)
+{
+ RT_NOREF_PV(pvOpaque);
+ AssertReturn(pvOpaque == NULL, VERR_INVALID_PARAMETER);
+ if (fReInit)
+ RTSha3t384Cleanup((PRTSHA3T384CONTEXT)pvState);
+ return RTSha3t384Init((PRTSHA3T384CONTEXT)pvState);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnDelete} */
+static DECLCALLBACK(void) rtCrDigestSha3t384_Delete(void *pvState)
+{
+ RTSha3t384Cleanup((PRTSHA3T384CONTEXT)pvState);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnDelete} */
+static DECLCALLBACK(int) rtCrDigestSha3t384_Clone(void *pvState, void const *pvSrcState)
+{
+ return RTSha3t384Clone((PRTSHA3T384CONTEXT)pvState, (PRTSHA3T384CONTEXT)pvSrcState);
+}
+
+/** SHA3-384 alias ODIs. */
+static const char * const g_apszSha3t384Aliases[] =
+{
+ "2.16.840.1.101.3.4.3.15",
+ RTCR_NIST_SHA3_384_WITH_ECDSA_OID,
+ NULL
+};
+
+/** SHA3-384 descriptor. */
+static RTCRDIGESTDESC const g_rtCrDigestSha3t384Desc =
+{
+ "sha3-384",
+ "2.16.840.1.101.3.4.2.9",
+ g_apszSha3t384Aliases,
+ RTDIGESTTYPE_SHA3_384,
+ RTSHA3_384_HASH_SIZE,
+ sizeof(RTSHA3T384CONTEXT),
+ 0,
+ NULL,
+ NULL,
+ rtCrDigestSha3t384_Update,
+ rtCrDigestSha3t384_Final,
+ rtCrDigestSha3t384_Init,
+ rtCrDigestSha3t384_Delete,
+ rtCrDigestSha3t384_Clone,
+ NULL,
+ NULL,
+};
+
+
+/*
+ * SHA3-512
+ */
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnUpdate} */
+static DECLCALLBACK(void) rtCrDigestSha3t512_Update(void *pvState, const void *pvData, size_t cbData)
+{
+ int rc = RTSha3t512Update((PRTSHA3T512CONTEXT)pvState, pvData, cbData);
+ AssertRC(rc);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnFinal} */
+static DECLCALLBACK(void) rtCrDigestSha3t512_Final(void *pvState, uint8_t *pbHash)
+{
+ int rc = RTSha3t512Final((PRTSHA3T512CONTEXT)pvState, pbHash);
+ AssertRC(rc);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnInit} */
+static DECLCALLBACK(int) rtCrDigestSha3t512_Init(void *pvState, void *pvOpaque, bool fReInit)
+{
+ RT_NOREF_PV(pvOpaque);
+ AssertReturn(pvOpaque == NULL, VERR_INVALID_PARAMETER);
+ if (fReInit)
+ RTSha3t512Cleanup((PRTSHA3T512CONTEXT)pvState);
+ return RTSha3t512Init((PRTSHA3T512CONTEXT)pvState);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnDelete} */
+static DECLCALLBACK(void) rtCrDigestSha3t512_Delete(void *pvState)
+{
+ RTSha3t512Cleanup((PRTSHA3T512CONTEXT)pvState);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnDelete} */
+static DECLCALLBACK(int) rtCrDigestSha3t512_Clone(void *pvState, void const *pvSrcState)
+{
+ return RTSha3t512Clone((PRTSHA3T512CONTEXT)pvState, (PRTSHA3T512CONTEXT)pvSrcState);
+}
+
+/** SHA3-512 alias ODIs. */
+static const char * const g_apszSha3t512Aliases[] =
+{
+ "2.16.840.1.101.3.4.3.16",
+ RTCR_NIST_SHA3_512_WITH_ECDSA_OID,
+ NULL
+};
+
+/** SHA3-512 descriptor. */
+static RTCRDIGESTDESC const g_rtCrDigestSha3t512Desc =
+{
+ "sha3-512",
+ "2.16.840.1.101.3.4.2.10",
+ g_apszSha3t512Aliases,
+ RTDIGESTTYPE_SHA3_512,
+ RTSHA3_512_HASH_SIZE,
+ sizeof(RTSHA3T512CONTEXT),
+ 0,
+ NULL,
+ NULL,
+ rtCrDigestSha3t512_Update,
+ rtCrDigestSha3t512_Final,
+ rtCrDigestSha3t512_Init,
+ rtCrDigestSha3t512_Delete,
+ rtCrDigestSha3t512_Clone,
+ NULL,
+ NULL,
+};
+
+#endif /* !IPRT_WITHOUT_SHA3 */
+
+
+/**
+ * Array of built in message digest vtables.
+ */
+static PCRTCRDIGESTDESC const g_apDigestOps[] =
+{
+#ifndef IPRT_WITHOUT_DIGEST_MD2
+ &g_rtCrDigestMd2Desc,
+#endif
+#ifndef IPRT_WITHOUT_DIGEST_MD4
+ &g_rtCrDigestMd4Desc,
+#endif
+#ifndef IPRT_WITHOUT_DIGEST_MD5
+ &g_rtCrDigestMd5Desc,
+#endif
+ &g_rtCrDigestSha1Desc,
+ &g_rtCrDigestSha256Desc,
+ &g_rtCrDigestSha512Desc,
+ &g_rtCrDigestSha224Desc,
+ &g_rtCrDigestSha384Desc,
+#ifndef IPRT_WITHOUT_SHA512T224
+ &g_rtCrDigestSha512t224Desc,
+#endif
+#ifndef IPRT_WITHOUT_SHA512T256
+ &g_rtCrDigestSha512t256Desc,
+#endif
+#ifndef IPRT_WITHOUT_SHA3
+ &g_rtCrDigestSha3t224Desc,
+ &g_rtCrDigestSha3t256Desc,
+ &g_rtCrDigestSha3t384Desc,
+ &g_rtCrDigestSha3t512Desc,
+#endif
+};
+
+
+#ifdef IPRT_WITH_OPENSSL
+/*
+ * OpenSSL EVP.
+ */
+
+# if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+/** @impl_interface_method{RTCRDIGESTDESC::pfnNew} */
+static DECLCALLBACK(void*) rtCrDigestOsslEvp_New(void)
+{
+ return EVP_MD_CTX_new();
+}
+
+static DECLCALLBACK(void) rtCrDigestOsslEvp_Free(void *pvState)
+{
+ EVP_MD_CTX_free((EVP_MD_CTX*)pvState);
+}
+
+# endif
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnUpdate} */
+static DECLCALLBACK(void) rtCrDigestOsslEvp_Update(void *pvState, const void *pvData, size_t cbData)
+{
+ EVP_DigestUpdate((EVP_MD_CTX *)pvState, pvData, cbData);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnFinal} */
+static DECLCALLBACK(void) rtCrDigestOsslEvp_Final(void *pvState, uint8_t *pbHash)
+{
+ unsigned int cbHash = EVP_MAX_MD_SIZE;
+ EVP_DigestFinal((EVP_MD_CTX *)pvState, (unsigned char *)pbHash, &cbHash);
+}
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnInit} */
+static DECLCALLBACK(int) rtCrDigestOsslEvp_Init(void *pvState, void *pvOpaque, bool fReInit)
+{
+ EVP_MD_CTX *pThis = (EVP_MD_CTX *)pvState;
+ EVP_MD const *pEvpType = (EVP_MD const *)pvOpaque;
+
+ if (fReInit)
+ {
+ pEvpType = EVP_MD_CTX_md(pThis);
+# if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+ EVP_MD_CTX_reset(pThis);
+# else
+ EVP_MD_CTX_cleanup(pThis);
+# endif
+ }
+
+ AssertPtrReturn(pEvpType, VERR_INVALID_PARAMETER);
+# if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+ Assert(EVP_MD_block_size(pEvpType));
+# else
+ Assert(pEvpType->md_size);
+# endif
+ if (EVP_DigestInit(pThis, pEvpType))
+ return VINF_SUCCESS;
+ return VERR_CR_DIGEST_OSSL_DIGEST_INIT_ERROR;
+}
+
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfn} */
+static DECLCALLBACK(void) rtCrDigestOsslEvp_Delete(void *pvState)
+{
+ EVP_MD_CTX *pThis = (EVP_MD_CTX *)pvState;
+# if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+ EVP_MD_CTX_reset(pThis);
+# else
+ EVP_MD_CTX_cleanup(pThis);
+# endif
+}
+
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnClone} */
+static DECLCALLBACK(int) rtCrDigestOsslEvp_Clone(void *pvState, void const *pvSrcState)
+{
+ EVP_MD_CTX *pThis = (EVP_MD_CTX *)pvState;
+ EVP_MD_CTX const *pSrc = (EVP_MD_CTX const *)pvSrcState;
+
+ if (EVP_MD_CTX_copy(pThis, pSrc))
+ return VINF_SUCCESS;
+ return VERR_CR_DIGEST_OSSL_DIGEST_CTX_COPY_ERROR;
+}
+
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnGetHashSize} */
+static DECLCALLBACK(uint32_t) rtCrDigestOsslEvp_GetHashSize(void *pvState)
+{
+ EVP_MD_CTX *pThis = (EVP_MD_CTX *)pvState;
+ return EVP_MD_size(EVP_MD_CTX_md(pThis));
+}
+
+
+/** @impl_interface_method{RTCRDIGESTDESC::pfnGetHashSize} */
+static DECLCALLBACK(RTDIGESTTYPE) rtCrDigestOsslEvp_GetDigestType(void *pvState)
+{
+ RT_NOREF_PV(pvState); //EVP_MD_CTX *pThis = (EVP_MD_CTX *)pvState;
+ /** @todo figure which digest algorithm it is! */
+ return RTDIGESTTYPE_UNKNOWN;
+}
+
+
+/** Descriptor for the OpenSSL EVP base message digest provider. */
+static RTCRDIGESTDESC const g_rtCrDigestOpenSslDesc =
+{
+ "OpenSSL EVP",
+ NULL,
+ NULL,
+ RTDIGESTTYPE_UNKNOWN,
+ EVP_MAX_MD_SIZE,
+# if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+ 0,
+# else
+ sizeof(EVP_MD_CTX),
+# endif
+ 0,
+# if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+ rtCrDigestOsslEvp_New,
+ rtCrDigestOsslEvp_Free,
+# else
+ NULL,
+ NULL,
+# endif
+ rtCrDigestOsslEvp_Update,
+ rtCrDigestOsslEvp_Final,
+ rtCrDigestOsslEvp_Init,
+ rtCrDigestOsslEvp_Delete,
+ rtCrDigestOsslEvp_Clone,
+ rtCrDigestOsslEvp_GetHashSize,
+ rtCrDigestOsslEvp_GetDigestType
+};
+
+#endif /* IPRT_WITH_OPENSSL */
+
+
+RTDECL(PCRTCRDIGESTDESC) RTCrDigestFindByObjIdString(const char *pszObjId, void **ppvOpaque)
+{
+ if (ppvOpaque)
+ *ppvOpaque = NULL;
+
+ /*
+ * Primary OIDs.
+ */
+ uint32_t i = RT_ELEMENTS(g_apDigestOps);
+ while (i-- > 0)
+ if (strcmp(g_apDigestOps[i]->pszObjId, pszObjId) == 0)
+ return g_apDigestOps[i];
+
+ /*
+ * Alias OIDs.
+ */
+ i = RT_ELEMENTS(g_apDigestOps);
+ while (i-- > 0)
+ {
+ const char * const *ppszAliases = g_apDigestOps[i]->papszObjIdAliases;
+ if (ppszAliases)
+ for (; *ppszAliases; ppszAliases++)
+ if (strcmp(*ppszAliases, pszObjId) == 0)
+ return g_apDigestOps[i];
+ }
+
+#ifdef IPRT_WITH_OPENSSL
+ /*
+ * Try EVP and see if it knows the algorithm.
+ */
+ if (ppvOpaque)
+ {
+ rtCrOpenSslInit();
+ int iAlgoNid = OBJ_txt2nid(pszObjId);
+ if (iAlgoNid != NID_undef)
+ {
+ const char *pszAlogSn = OBJ_nid2sn(iAlgoNid);
+ const EVP_MD *pEvpMdType = EVP_get_digestbyname(pszAlogSn);
+ if (pEvpMdType)
+ {
+ /*
+ * Return the OpenSSL provider descriptor and the EVP_MD address.
+ */
+# if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+ Assert(EVP_MD_block_size(pEvpMdType));
+# else
+ Assert(pEvpMdType->md_size);
+# endif
+ *ppvOpaque = (void *)pEvpMdType;
+ return &g_rtCrDigestOpenSslDesc;
+ }
+ }
+ }
+#endif
+ return NULL;
+}
+
+
+RTDECL(PCRTCRDIGESTDESC) RTCrDigestFindByObjId(PCRTASN1OBJID pObjId, void **ppvOpaque)
+{
+ return RTCrDigestFindByObjIdString(pObjId->szObjId, ppvOpaque);
+}
+
+
+RTDECL(int) RTCrDigestCreateByObjIdString(PRTCRDIGEST phDigest, const char *pszObjId)
+{
+ void *pvOpaque;
+ PCRTCRDIGESTDESC pDesc = RTCrDigestFindByObjIdString(pszObjId, &pvOpaque);
+ if (pDesc)
+ return RTCrDigestCreate(phDigest, pDesc, pvOpaque);
+ return VERR_NOT_FOUND;
+}
+
+
+RTDECL(int) RTCrDigestCreateByObjId(PRTCRDIGEST phDigest, PCRTASN1OBJID pObjId)
+{
+ void *pvOpaque;
+ PCRTCRDIGESTDESC pDesc = RTCrDigestFindByObjId(pObjId, &pvOpaque);
+ if (pDesc)
+ return RTCrDigestCreate(phDigest, pDesc, pvOpaque);
+ return VERR_NOT_FOUND;
+}
+
+
+RTDECL(PCRTCRDIGESTDESC) RTCrDigestFindByType(RTDIGESTTYPE enmDigestType)
+{
+ AssertReturn(enmDigestType > RTDIGESTTYPE_INVALID && enmDigestType <= RTDIGESTTYPE_END, NULL);
+
+ uint32_t i = RT_ELEMENTS(g_apDigestOps);
+ while (i-- > 0)
+ if (g_apDigestOps[i]->enmType == enmDigestType)
+ return g_apDigestOps[i];
+ return NULL;
+}
+
+
+RTDECL(int) RTCrDigestCreateByType(PRTCRDIGEST phDigest, RTDIGESTTYPE enmDigestType)
+{
+ PCRTCRDIGESTDESC pDesc = RTCrDigestFindByType(enmDigestType);
+ if (pDesc)
+ return RTCrDigestCreate(phDigest, pDesc, NULL);
+ return VERR_NOT_FOUND;
+}
+
diff --git a/src/VBox/Runtime/common/crypto/digest-core.cpp b/src/VBox/Runtime/common/crypto/digest-core.cpp
new file mode 100644
index 00000000..e4c0c658
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/digest-core.cpp
@@ -0,0 +1,499 @@
+/* $Id: digest-core.cpp $ */
+/** @file
+ * IPRT - Crypto - Cryptographic Hash / Message Digest API
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/digest.h>
+
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/crypto/x509.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Generic message digest instance.
+ */
+typedef struct RTCRDIGESTINT
+{
+ /** Magic value (RTCRDIGESTINT_MAGIC). */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** Pointer to the message digest descriptor. */
+ PCRTCRDIGESTDESC pDesc;
+ /** The offset into abState of the storage space . At
+ * least RTCRDIGESTDESC::cbHash bytes is available at that location. */
+ uint32_t offHash;
+ /** State. */
+ uint32_t uState;
+ /** The number of bytes consumed. */
+ uint64_t cbConsumed;
+ /** Pointer to the data specific to the message digest algorithm. Points
+ * either to &abState[0] or to memory allocated with pDesc->pfnNew. */
+ void *pvState;
+ /** Opaque data specific to the message digest algorithm, size given by
+ * RTCRDIGESTDESC::cbState. This is followed by space for the final hash
+ * at offHash with size RTCRDIGESTDESC::cbHash. The data specific to the
+ * message digest algorithm can also be 0. In this case, pDesc->pfnNew()
+ * and pDesc->pfnFree() must not be NULL. */
+ uint8_t abState[1];
+} RTCRDIGESTINT;
+/** Pointer to a message digest instance. */
+typedef RTCRDIGESTINT *PRTCRDIGESTINT;
+
+/** Magic value for RTCRDIGESTINT::u32Magic (Ralph C. Merkle). */
+#define RTCRDIGESTINT_MAGIC UINT32_C(0x19520202)
+
+/** @name RTCRDIGESTINT::uState values.
+ * @{ */
+/** Ready for more data. */
+#define RTCRDIGEST_STATE_READY UINT32_C(1)
+/** The hash has been finalized and can be found at offHash. */
+#define RTCRDIGEST_STATE_FINAL UINT32_C(2)
+/** Busted state, can happen after re-init. */
+#define RTCRDIGEST_STATE_BUSTED UINT32_C(3)
+/** @} */
+
+
+
+/**
+ * Used for successful returns which wants to hit at digest security.
+ *
+ * @retval VINF_SUCCESS
+ * @retval VINF_CR_DIGEST_DEPRECATED
+ * @retval VINF_CR_DIGEST_COMPROMISED
+ * @retval VINF_CR_DIGEST_SEVERELY_COMPROMISED
+ * @param pDesc The digest descriptor.
+ */
+DECLINLINE(int) rtCrDigestSuccessWithDigestWarnings(PCRTCRDIGESTDESC pDesc)
+{
+ uint32_t const fFlags = pDesc->fFlags
+ & (RTCRDIGESTDESC_F_DEPRECATED | RTCRDIGESTDESC_F_COMPROMISED | RTCRDIGESTDESC_F_SERVERELY_COMPROMISED);
+ if (!fFlags)
+ return VINF_SUCCESS;
+ if (fFlags & RTCRDIGESTDESC_F_SERVERELY_COMPROMISED)
+ return VINF_CR_DIGEST_SEVERELY_COMPROMISED;
+ if (fFlags & RTCRDIGESTDESC_F_COMPROMISED)
+ return VINF_CR_DIGEST_COMPROMISED;
+ return VINF_CR_DIGEST_DEPRECATED;
+}
+
+
+RTDECL(int) RTCrDigestCreate(PRTCRDIGEST phDigest, PCRTCRDIGESTDESC pDesc, void *pvOpaque)
+{
+ AssertPtrReturn(phDigest, VERR_INVALID_POINTER);
+ AssertPtrReturn(pDesc, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ uint32_t const offHash = RT_ALIGN_32(pDesc->cbState, 8);
+ AssertReturn(pDesc->pfnNew || offHash, VERR_INVALID_PARAMETER);
+ AssertReturn(!pDesc->pfnNew || (pDesc->pfnFree && pDesc->pfnInit && pDesc->pfnClone), VERR_INVALID_PARAMETER);
+ PRTCRDIGESTINT pThis = (PRTCRDIGESTINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTCRDIGESTINT, abState[offHash + pDesc->cbHash]));
+ if (pThis)
+ {
+ if (pDesc->pfnNew)
+ pThis->pvState = pDesc->pfnNew();
+ else
+ pThis->pvState = &pThis->abState[0];
+ if (pThis->pvState)
+ {
+ pThis->u32Magic = RTCRDIGESTINT_MAGIC;
+ pThis->cRefs = 1;
+ pThis->offHash = offHash;
+ pThis->pDesc = pDesc;
+ pThis->uState = RTCRDIGEST_STATE_READY;
+ if (pDesc->pfnInit)
+ rc = pDesc->pfnInit(pThis->pvState, pvOpaque, false /*fReInit*/);
+ if (RT_SUCCESS(rc))
+ {
+ *phDigest = pThis;
+ return rtCrDigestSuccessWithDigestWarnings(pDesc);
+ }
+ if (pDesc->pfnFree)
+ pDesc->pfnFree(pThis->pvState);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ pThis->u32Magic = 0;
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+RTDECL(int) RTCrDigestClone(PRTCRDIGEST phDigest, RTCRDIGEST hSrc)
+{
+ AssertPtrReturn(phDigest, VERR_INVALID_POINTER);
+ AssertPtrReturn(hSrc, VERR_INVALID_HANDLE);
+ AssertReturn(hSrc->u32Magic == RTCRDIGESTINT_MAGIC, VERR_INVALID_HANDLE);
+
+ int rc = VINF_SUCCESS;
+ uint32_t const offHash = hSrc->offHash;
+ PRTCRDIGESTINT pThis = (PRTCRDIGESTINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTCRDIGESTINT, abState[offHash + hSrc->pDesc->cbHash]));
+ if (pThis)
+ {
+ if (hSrc->pDesc->pfnNew)
+ pThis->pvState = hSrc->pDesc->pfnNew();
+ else
+ pThis->pvState = &pThis->abState[0];
+ if (pThis->pvState)
+ {
+ pThis->u32Magic = RTCRDIGESTINT_MAGIC;
+ pThis->cRefs = 1;
+ pThis->offHash = offHash;
+ pThis->pDesc = hSrc->pDesc;
+ if (hSrc->pDesc->pfnClone)
+ rc = hSrc->pDesc->pfnClone(pThis->pvState, hSrc->pvState);
+ else
+ {
+ Assert(!hSrc->pDesc->pfnNew);
+ memcpy(pThis->pvState, hSrc->pvState, offHash);
+ }
+ memcpy(&pThis->abState[offHash], &hSrc->abState[offHash], hSrc->pDesc->cbHash);
+ pThis->uState = hSrc->uState;
+
+ if (RT_SUCCESS(rc))
+ {
+ *phDigest = pThis;
+ return rtCrDigestSuccessWithDigestWarnings(pThis->pDesc);
+ }
+ if (hSrc->pDesc->pfnFree)
+ hSrc->pDesc->pfnFree(pThis->pvState);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ pThis->u32Magic = 0;
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+RTDECL(int) RTCrDigestReset(RTCRDIGEST hDigest)
+{
+ PRTCRDIGESTINT pThis = hDigest;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRDIGESTINT_MAGIC, VERR_INVALID_HANDLE);
+
+ pThis->cbConsumed = 0;
+ pThis->uState = RTCRDIGEST_STATE_READY;
+
+ int rc = VINF_SUCCESS;
+ if (pThis->pDesc->pfnInit)
+ {
+ rc = pThis->pDesc->pfnInit(pThis->pvState, NULL, true /*fReInit*/);
+ if (RT_FAILURE(rc))
+ pThis->uState = RTCRDIGEST_STATE_BUSTED;
+ RT_BZERO(&pThis->abState[pThis->offHash], pThis->pDesc->cbHash);
+ }
+ else
+ {
+ Assert(!pThis->pDesc->pfnNew);
+ RT_BZERO(pThis->pvState, pThis->offHash + pThis->pDesc->cbHash);
+ }
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTCrDigestRetain(RTCRDIGEST hDigest)
+{
+ PRTCRDIGESTINT pThis = hDigest;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRDIGESTINT_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs < 64);
+ return cRefs;
+}
+
+
+RTDECL(uint32_t) RTCrDigestRelease(RTCRDIGEST hDigest)
+{
+ PRTCRDIGESTINT pThis = hDigest;
+ if (pThis == NIL_RTCRDIGEST)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRDIGESTINT_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ if (!cRefs)
+ {
+ pThis->u32Magic = ~RTCRDIGESTINT_MAGIC;
+ if (pThis->pDesc->pfnDelete)
+ pThis->pDesc->pfnDelete(pThis->pvState);
+ if (pThis->pDesc->pfnFree)
+ pThis->pDesc->pfnFree(pThis->pvState);
+ RTMemFree(pThis);
+ }
+ Assert(cRefs < 64);
+ return cRefs;
+}
+
+
+RTDECL(int) RTCrDigestUpdate(RTCRDIGEST hDigest, void const *pvData, size_t cbData)
+{
+ PRTCRDIGESTINT pThis = hDigest;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRDIGESTINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->uState == RTCRDIGEST_STATE_READY, VERR_INVALID_STATE);
+
+ pThis->pDesc->pfnUpdate(pThis->pvState, pvData, cbData);
+ pThis->cbConsumed += cbData;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCrDigestFinal(RTCRDIGEST hDigest, void *pvHash, size_t cbHash)
+{
+ PRTCRDIGESTINT pThis = hDigest;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRDIGESTINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->uState == RTCRDIGEST_STATE_READY || pThis->uState == RTCRDIGEST_STATE_FINAL, VERR_INVALID_STATE);
+ AssertPtrNullReturn(pvHash, VERR_INVALID_POINTER);
+
+ /*
+ * Make sure the hash calculation is final.
+ */
+ if (pThis->uState == RTCRDIGEST_STATE_READY)
+ {
+ pThis->pDesc->pfnFinal(pThis->pvState, &pThis->abState[pThis->offHash]);
+ pThis->uState = RTCRDIGEST_STATE_FINAL;
+ }
+ else
+ AssertReturn(pThis->uState == RTCRDIGEST_STATE_FINAL, VERR_INVALID_STATE);
+
+ /*
+ * Copy out the hash if requested.
+ */
+ if (cbHash > 0)
+ {
+ uint32_t cbNeeded = pThis->pDesc->cbHash;
+ if (pThis->pDesc->pfnGetHashSize)
+ cbNeeded = pThis->pDesc->pfnGetHashSize(pThis->pvState);
+ Assert(cbNeeded > 0);
+
+ if (cbNeeded == cbHash)
+ memcpy(pvHash, &pThis->abState[pThis->offHash], cbNeeded);
+ else if (cbNeeded < cbHash)
+ {
+ memcpy(pvHash, &pThis->abState[pThis->offHash], cbNeeded);
+ memset((uint8_t *)pvHash + cbNeeded, 0, cbHash - cbNeeded);
+ return VINF_BUFFER_UNDERFLOW;
+ }
+ else
+ {
+ memcpy(pvHash, &pThis->abState[pThis->offHash], cbHash);
+ return VERR_BUFFER_OVERFLOW;
+ }
+ }
+
+ return rtCrDigestSuccessWithDigestWarnings(pThis->pDesc);
+}
+
+
+RTDECL(bool) RTCrDigestMatch(RTCRDIGEST hDigest, void const *pvHash, size_t cbHash)
+{
+ PRTCRDIGESTINT pThis = hDigest;
+
+ int rc = RTCrDigestFinal(pThis, NULL, 0);
+ AssertRCReturn(rc, false);
+
+ AssertPtrReturn(pvHash, false);
+ return pThis->pDesc->cbHash == cbHash
+ && !memcmp(&pThis->abState[pThis->offHash], pvHash, cbHash);
+}
+
+
+RTDECL(uint8_t const *) RTCrDigestGetHash(RTCRDIGEST hDigest)
+{
+ PRTCRDIGESTINT pThis = hDigest;
+ AssertPtrReturn(pThis, NULL);
+ AssertReturn(pThis->u32Magic == RTCRDIGESTINT_MAGIC, NULL);
+
+ int rc = RTCrDigestFinal(pThis, NULL, 0);
+ AssertRCReturn(rc, NULL);
+
+ return &pThis->abState[pThis->offHash];
+}
+
+
+RTDECL(uint32_t) RTCrDigestGetHashSize(RTCRDIGEST hDigest)
+{
+ PRTCRDIGESTINT pThis = hDigest;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTCRDIGESTINT_MAGIC, 0);
+ if (pThis->pDesc->pfnGetHashSize)
+ {
+ uint32_t cbHash = pThis->pDesc->pfnGetHashSize(pThis->pvState);
+ Assert(cbHash <= pThis->pDesc->cbHash);
+ return cbHash;
+ }
+ return pThis->pDesc->cbHash;
+}
+
+
+RTDECL(uint64_t) RTCrDigestGetConsumedSize(RTCRDIGEST hDigest)
+{
+ PRTCRDIGESTINT pThis = hDigest;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTCRDIGESTINT_MAGIC, 0);
+ return pThis->cbConsumed;
+}
+
+
+RTDECL(bool) RTCrDigestIsFinalized(RTCRDIGEST hDigest)
+{
+ PRTCRDIGESTINT pThis = hDigest;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTCRDIGESTINT_MAGIC, false);
+ return pThis->uState == RTCRDIGEST_STATE_FINAL;
+}
+
+
+RTDECL(RTDIGESTTYPE) RTCrDigestGetType(RTCRDIGEST hDigest)
+{
+ PRTCRDIGESTINT pThis = hDigest;
+ AssertPtrReturn(pThis, RTDIGESTTYPE_INVALID);
+ AssertReturn(pThis->u32Magic == RTCRDIGESTINT_MAGIC, RTDIGESTTYPE_INVALID);
+
+ RTDIGESTTYPE enmType = pThis->pDesc->enmType;
+ if (pThis->pDesc->pfnGetDigestType)
+ enmType = pThis->pDesc->pfnGetDigestType(pThis->pvState);
+ return enmType;
+}
+
+
+RTDECL(const char *) RTCrDigestGetAlgorithmOid(RTCRDIGEST hDigest)
+{
+ return RTCrDigestTypeToAlgorithmOid(RTCrDigestGetType(hDigest));
+}
+
+
+RTDECL(uint32_t) RTCrDigestGetFlags(RTCRDIGEST hDigest)
+{
+ PRTCRDIGESTINT pThis = hDigest;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRDIGESTINT_MAGIC, UINT32_MAX);
+ return pThis->pDesc->fFlags;
+}
+
+
+RTDECL(const char *) RTCrDigestTypeToAlgorithmOid(RTDIGESTTYPE enmDigestType)
+{
+ switch (enmDigestType)
+ {
+ case RTDIGESTTYPE_MD2: return RTCRX509ALGORITHMIDENTIFIERID_MD2;
+ case RTDIGESTTYPE_MD4: return RTCRX509ALGORITHMIDENTIFIERID_MD4;
+ case RTDIGESTTYPE_MD5: return RTCRX509ALGORITHMIDENTIFIERID_MD5;
+ case RTDIGESTTYPE_SHA1: return RTCRX509ALGORITHMIDENTIFIERID_SHA1;
+ case RTDIGESTTYPE_SHA224: return RTCRX509ALGORITHMIDENTIFIERID_SHA224;
+ case RTDIGESTTYPE_SHA256: return RTCRX509ALGORITHMIDENTIFIERID_SHA256;
+ case RTDIGESTTYPE_SHA384: return RTCRX509ALGORITHMIDENTIFIERID_SHA384;
+ case RTDIGESTTYPE_SHA512: return RTCRX509ALGORITHMIDENTIFIERID_SHA512;
+ case RTDIGESTTYPE_SHA512T224: return RTCRX509ALGORITHMIDENTIFIERID_SHA512T224;
+ case RTDIGESTTYPE_SHA512T256: return RTCRX509ALGORITHMIDENTIFIERID_SHA512T256;
+ case RTDIGESTTYPE_SHA3_224: return RTCRX509ALGORITHMIDENTIFIERID_SHA3_224;
+ case RTDIGESTTYPE_SHA3_256: return RTCRX509ALGORITHMIDENTIFIERID_SHA3_256;
+ case RTDIGESTTYPE_SHA3_384: return RTCRX509ALGORITHMIDENTIFIERID_SHA3_384;
+ case RTDIGESTTYPE_SHA3_512: return RTCRX509ALGORITHMIDENTIFIERID_SHA3_512;
+ default: return NULL;
+ }
+}
+
+
+RTDECL(const char *) RTCrDigestTypeToName(RTDIGESTTYPE enmDigestType)
+{
+ switch (enmDigestType)
+ {
+ case RTDIGESTTYPE_CRC32: return "CRC32";
+ case RTDIGESTTYPE_CRC64: return "CRC64";
+ case RTDIGESTTYPE_MD2: return "MD2";
+ case RTDIGESTTYPE_MD4: return "MD4";
+ case RTDIGESTTYPE_MD5: return "MD5";
+ case RTDIGESTTYPE_SHA1: return "SHA-1";
+ case RTDIGESTTYPE_SHA224: return "SHA-224";
+ case RTDIGESTTYPE_SHA256: return "SHA-256";
+ case RTDIGESTTYPE_SHA384: return "SHA-384";
+ case RTDIGESTTYPE_SHA512: return "SHA-512";
+ case RTDIGESTTYPE_SHA512T224: return "SHA-512/224";
+ case RTDIGESTTYPE_SHA512T256: return "SHA-512/256";
+ case RTDIGESTTYPE_SHA3_224: return "SHA3-224";
+ case RTDIGESTTYPE_SHA3_256: return "SHA3-256";
+ case RTDIGESTTYPE_SHA3_384: return "SHA3-384";
+ case RTDIGESTTYPE_SHA3_512: return "SHA3-512";
+ default: return NULL;
+ }
+}
+
+
+RTDECL(uint32_t) RTCrDigestTypeToHashSize(RTDIGESTTYPE enmDigestType)
+{
+ switch (enmDigestType)
+ {
+ case RTDIGESTTYPE_CRC32: return 32 / 8;
+ case RTDIGESTTYPE_CRC64: return 64 / 8;
+ case RTDIGESTTYPE_MD2: return 128 / 8;
+ case RTDIGESTTYPE_MD4: return 128 / 8;
+ case RTDIGESTTYPE_MD5: return 128 / 8;
+ case RTDIGESTTYPE_SHA1: return 160 / 8;
+ case RTDIGESTTYPE_SHA224: return 224 / 8;
+ case RTDIGESTTYPE_SHA256: return 256 / 8;
+ case RTDIGESTTYPE_SHA384: return 384 / 8;
+ case RTDIGESTTYPE_SHA512: return 512 / 8;
+ case RTDIGESTTYPE_SHA512T224: return 224 / 8;
+ case RTDIGESTTYPE_SHA512T256: return 256 / 8;
+ case RTDIGESTTYPE_SHA3_224: return 224 / 8;
+ case RTDIGESTTYPE_SHA3_256: return 256 / 8;
+ case RTDIGESTTYPE_SHA3_384: return 384 / 8;
+ case RTDIGESTTYPE_SHA3_512: return 512 / 8;
+ default:
+ AssertFailed();
+ return 0;
+ }
+}
+
diff --git a/src/VBox/Runtime/common/crypto/digest-vfs.cpp b/src/VBox/Runtime/common/crypto/digest-vfs.cpp
new file mode 100644
index 00000000..029db793
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/digest-vfs.cpp
@@ -0,0 +1,80 @@
+/* $Id: digest-vfs.cpp $ */
+/** @file
+ * IPRT - Crypto - Cryptographic Hash / Message Digest API, VFS related interfaces
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/digest.h>
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/vfs.h>
+
+
+RTDECL(int) RTCrDigestUpdateFromVfsFile(RTCRDIGEST hDigest, RTVFSFILE hVfsFile, bool fRewindFile)
+{
+ int rc;
+ if (fRewindFile)
+ rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
+ else
+ rc = VINF_SUCCESS;
+ if (RT_SUCCESS(rc))
+ {
+ for (;;)
+ {
+ char abBuf[_16K];
+ size_t cbRead;
+ rc = RTVfsFileRead(hVfsFile, abBuf, sizeof(abBuf), &cbRead);
+ if (RT_SUCCESS(rc))
+ {
+ bool const fEof = rc == VINF_EOF;
+ rc = RTCrDigestUpdate(hDigest, abBuf, cbRead);
+ if (fEof || RT_FAILURE(rc))
+ break;
+ }
+ else
+ {
+ Assert(rc != VERR_EOF);
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/crypto/iprt-openssl.cpp b/src/VBox/Runtime/common/crypto/iprt-openssl.cpp
new file mode 100644
index 00000000..3bc2801c
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/iprt-openssl.cpp
@@ -0,0 +1,202 @@
+/* $Id: iprt-openssl.cpp $ */
+/** @file
+ * IPRT - Crypto - OpenSSL Helpers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+
+#ifdef IPRT_WITH_OPENSSL /* Whole file. */
+# include <iprt/err.h>
+# include <iprt/string.h>
+# include <iprt/mem.h>
+# include <iprt/asn1.h>
+# include <iprt/crypto/digest.h>
+# include <iprt/crypto/pkcs7.h>
+# include <iprt/crypto/spc.h>
+
+# include "internal/iprt-openssl.h"
+# include "internal/openssl-pre.h"
+# include <openssl/x509.h>
+# include <openssl/err.h>
+# include "internal/openssl-post.h"
+
+
+DECLHIDDEN(void) rtCrOpenSslInit(void)
+{
+ static bool s_fOssInitalized;
+ if (!s_fOssInitalized)
+ {
+ OpenSSL_add_all_algorithms();
+ ERR_load_ERR_strings();
+ ERR_load_crypto_strings();
+
+ /* Add some OIDs we might possibly want to use. */
+ static struct { const char *pszOid, *pszDesc; } const s_aOids[] =
+ {
+ { RTCRSPC_PE_IMAGE_HASHES_V1_OID, "Ms-SpcPeImagePageHashesV1" },
+ { RTCRSPC_PE_IMAGE_HASHES_V2_OID, "Ms-SpcPeImagePageHashesV2" },
+ { RTCRSPC_STMT_TYPE_INDIVIDUAL_CODE_SIGNING, "Ms-SpcIndividualCodeSigning" },
+ { RTCRSPCPEIMAGEDATA_OID, "Ms-SpcPeImageData" },
+ { RTCRSPCINDIRECTDATACONTENT_OID, "Ms-SpcIndirectDataContext" },
+ { RTCR_PKCS9_ID_MS_TIMESTAMP, "Ms-CounterSign" },
+ { RTCR_PKCS9_ID_MS_NESTED_SIGNATURE, "Ms-SpcNestedSignature" },
+ { RTCR_PKCS9_ID_MS_STATEMENT_TYPE, "Ms-SpcStatementType" },
+ { RTCR_PKCS9_ID_MS_SP_OPUS_INFO, "Ms-SpcOpusInfo" },
+ { "1.3.6.1.4.1.311.3.2.1", "Ms-SpcTimeStampRequest" }, /** @todo define */
+ { "1.3.6.1.4.1.311.10.1", "Ms-CertTrustList" }, /** @todo define */
+ };
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aOids); i++)
+ OBJ_create(s_aOids[i].pszOid, s_aOids[i].pszDesc, s_aOids[i].pszDesc);
+
+ s_fOssInitalized = true;
+ }
+}
+
+
+DECLHIDDEN(int) rtCrOpenSslErrInfoCallback(const char *pach, size_t cch, void *pvUser)
+{
+ PRTERRINFO pErrInfo = (PRTERRINFO)pvUser;
+ size_t cchAlready = pErrInfo->fFlags & RTERRINFO_FLAGS_SET ? strlen(pErrInfo->pszMsg) : 0;
+ if (cchAlready + 1 < pErrInfo->cbMsg)
+ RTStrCopyEx(pErrInfo->pszMsg + cchAlready, pErrInfo->cbMsg - cchAlready, pach, cch);
+ return -1;
+}
+
+
+DECLHIDDEN(int) rtCrOpenSslConvertX509Cert(void **ppvOsslCert, PCRTCRX509CERTIFICATE pCert, PRTERRINFO pErrInfo)
+{
+ const unsigned char *pabEncoded;
+ uint32_t cbEncoded;
+ void *pvFree;
+ int rc = RTAsn1EncodeQueryRawBits(RTCrX509Certificate_GetAsn1Core(pCert),
+ (const uint8_t **)&pabEncoded, &cbEncoded, &pvFree, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ X509 *pOsslCert = NULL;
+ X509 *pOsslCertRet = d2i_X509(&pOsslCert, &pabEncoded, cbEncoded);
+ RTMemTmpFree(pvFree);
+ if (pOsslCert != NULL && pOsslCertRet == pOsslCert)
+ {
+ *ppvOsslCert = pOsslCert;
+ return VINF_SUCCESS;
+ }
+ rc = RTErrInfoSet(pErrInfo, VERR_CR_X509_OSSL_D2I_FAILED, "d2i_X509");
+
+ }
+ *ppvOsslCert = NULL;
+ return rc;
+}
+
+
+DECLHIDDEN(void) rtCrOpenSslFreeConvertedX509Cert(void *pvOsslCert)
+{
+ X509_free((X509 *)pvOsslCert);
+}
+
+
+DECLHIDDEN(int) rtCrOpenSslAddX509CertToStack(void *pvOsslStack, PCRTCRX509CERTIFICATE pCert, PRTERRINFO pErrInfo)
+{
+ X509 *pOsslCert = NULL;
+ int rc = rtCrOpenSslConvertX509Cert((void **)&pOsslCert, pCert, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ if (sk_X509_push((STACK_OF(X509) *)pvOsslStack, pOsslCert))
+ rc = VINF_SUCCESS;
+ else
+ {
+ rtCrOpenSslFreeConvertedX509Cert(pOsslCert);
+ rc = RTErrInfoSet(pErrInfo, VERR_NO_MEMORY, "sk_X509_push");
+ }
+ }
+ return rc;
+}
+
+
+DECLHIDDEN(const void /*EVP_MD*/ *) rtCrOpenSslConvertDigestType(RTDIGESTTYPE enmDigestType, PRTERRINFO pErrInfo)
+{
+ const char *pszAlgoObjId = RTCrDigestTypeToAlgorithmOid(enmDigestType);
+ AssertReturnStmt(pszAlgoObjId, RTErrInfoSetF(pErrInfo, VERR_INVALID_PARAMETER, "Invalid type: %d", enmDigestType), NULL);
+
+ int iAlgoNid = OBJ_txt2nid(pszAlgoObjId);
+ AssertReturnStmt(iAlgoNid != NID_undef,
+ RTErrInfoSetF(pErrInfo, VERR_CR_DIGEST_OSSL_DIGEST_INIT_ERROR,
+ "OpenSSL does not know: %s (%s)", pszAlgoObjId, RTCrDigestTypeToName(enmDigestType)),
+ NULL);
+
+ const char *pszAlgoSn = OBJ_nid2sn(iAlgoNid);
+ const EVP_MD *pEvpMdType = EVP_get_digestbyname(pszAlgoSn);
+ AssertReturnStmt(pEvpMdType,
+ RTErrInfoSetF(pErrInfo, VERR_CR_DIGEST_OSSL_DIGEST_INIT_ERROR, "OpenSSL/EVP does not know: %d (%s; %s; %s)",
+ iAlgoNid, pszAlgoSn, pszAlgoSn, RTCrDigestTypeToName(enmDigestType)),
+ NULL);
+
+ return pEvpMdType;
+}
+
+DECLHIDDEN(int) rtCrOpenSslConvertPkcs7Attribute(void **ppvOsslAttrib, PCRTCRPKCS7ATTRIBUTE pAttrib, PRTERRINFO pErrInfo)
+{
+ const unsigned char *pabEncoded;
+ uint32_t cbEncoded;
+ void *pvFree;
+ int rc = RTAsn1EncodeQueryRawBits(RTCrPkcs7Attribute_GetAsn1Core(pAttrib),
+ (const uint8_t **)&pabEncoded, &cbEncoded, &pvFree, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ X509_ATTRIBUTE *pOsslAttrib = NULL;
+ X509_ATTRIBUTE *pOsslAttribRet = d2i_X509_ATTRIBUTE(&pOsslAttrib, &pabEncoded, cbEncoded);
+ RTMemTmpFree(pvFree);
+ if (pOsslAttrib != NULL && pOsslAttribRet == pOsslAttrib)
+ {
+ *ppvOsslAttrib = pOsslAttrib;
+ return VINF_SUCCESS;
+ }
+ rc = RTErrInfoSet(pErrInfo, VERR_CR_X509_OSSL_D2I_FAILED, "d2i_X509_ATTRIBUTE");
+ }
+ *ppvOsslAttrib = NULL;
+ return rc;
+}
+
+
+DECLHIDDEN(void) rtCrOpenSslFreeConvertedPkcs7Attribute(void *pvOsslAttrib)
+{
+ X509_ATTRIBUTE_free((X509_ATTRIBUTE *)pvOsslAttrib);
+}
+
+
+#endif /* IPRT_WITH_OPENSSL */
+
diff --git a/src/VBox/Runtime/common/crypto/key-create-rsa-openssl.cpp b/src/VBox/Runtime/common/crypto/key-create-rsa-openssl.cpp
new file mode 100644
index 00000000..44f265f6
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/key-create-rsa-openssl.cpp
@@ -0,0 +1,104 @@
+/* $Id: key-create-rsa-openssl.cpp $ */
+/** @file
+ * IPRT - Crypto - RSA Key Creation using OpenSSL.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/key.h>
+
+#ifdef IPRT_WITH_OPENSSL /* Whole file. */
+# include <iprt/err.h>
+# include <iprt/string.h>
+
+# include "internal/iprt-openssl.h"
+# include "internal/openssl-pre.h"
+# include <openssl/rsa.h>
+# include <openssl/err.h>
+# include "internal/openssl-post.h"
+
+# include "key-internal.h"
+
+
+
+RTDECL(int) RTCrKeyCreateNewRsa(PRTCRKEY phKey, uint32_t cBits, uint32_t uPubExp, uint32_t fFlags)
+{
+ AssertPtrReturn(phKey, VERR_INVALID_POINTER);
+ AssertMsgReturn(cBits >= 128 && cBits <= _64K, ("cBits=%u\n", cBits), VERR_OUT_OF_RANGE);
+ AssertReturn(uPubExp > 0, VERR_OUT_OF_RANGE);
+ AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+ rtCrOpenSslInit();
+
+ /*
+ * Do the key generation first.
+ */
+ int rc = VERR_NO_MEMORY;
+ RSA *pRsa = RSA_new();
+ if (pRsa)
+ {
+ BIGNUM *pPubExp = BN_new();
+ if (pPubExp)
+ {
+ if (BN_set_word(pPubExp, uPubExp) != 0)
+ {
+ if (RSA_generate_key_ex(pRsa, cBits, pPubExp, NULL))
+ {
+ /*
+ * Create a IPRT key for it by encoding it as a private key.
+ */
+ unsigned char *pbRsaPrivateKey = NULL;
+ int cbRsaPrivateKey = i2d_RSAPrivateKey(pRsa, &pbRsaPrivateKey);
+ if (cbRsaPrivateKey > 0)
+ {
+ rc = rtCrKeyCreateRsaPrivate(phKey, pbRsaPrivateKey, cbRsaPrivateKey, NULL, NULL);
+ OPENSSL_free(pbRsaPrivateKey);
+ }
+ /* else: VERR_NO_MEMORY */
+ }
+ else
+ rc = VERR_CR_KEY_GEN_FAILED_RSA;
+ }
+ BN_free(pPubExp);
+ }
+ RSA_free(pRsa);
+ }
+ return rc;
+}
+
+#endif /* IPRT_WITH_OPENSSL */
+
diff --git a/src/VBox/Runtime/common/crypto/key-file.cpp b/src/VBox/Runtime/common/crypto/key-file.cpp
new file mode 100644
index 00000000..62ea549f
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/key-file.cpp
@@ -0,0 +1,688 @@
+/* $Id: key-file.cpp $ */
+/** @file
+ * IPRT - Crypto - Cryptographic Keys, File I/O.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_CRYPTO
+#include "internal/iprt.h"
+#include <iprt/crypto/key.h>
+
+#include <iprt/alloca.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/memsafer.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/crypto/rsa.h>
+#include <iprt/crypto/pkcs8.h>
+#include <iprt/crypto/pkix.h>
+#include <iprt/crypto/x509.h>
+
+#include "internal/magics.h"
+#include "key-internal.h"
+
+#ifdef IPRT_WITH_OPENSSL
+# include "internal/iprt-openssl.h"
+# include "internal/openssl-pre.h"
+# include <openssl/err.h>
+# include <openssl/evp.h>
+# include <openssl/pkcs12.h>
+# include "internal/openssl-post.h"
+# ifndef OPENSSL_VERSION_NUMBER
+# error "Missing OPENSSL_VERSION_NUMBER!"
+# endif
+#endif
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+/** RSA public key marker words. */
+static RTCRPEMMARKERWORD const g_aWords_RsaPublicKey[] =
+{ { RT_STR_TUPLE("RSA") }, { RT_STR_TUPLE("PUBLIC") }, { RT_STR_TUPLE("KEY") } };
+/** Generic public key marker words. */
+static RTCRPEMMARKERWORD const g_aWords_PublicKey[] =
+{ { RT_STR_TUPLE("PUBLIC") }, { RT_STR_TUPLE("KEY") } };
+
+/** Public key markers. */
+RT_DECL_DATA_CONST(RTCRPEMMARKER const) g_aRTCrKeyPublicMarkers[] =
+{
+ { g_aWords_RsaPublicKey, RT_ELEMENTS(g_aWords_RsaPublicKey) },
+ { g_aWords_PublicKey, RT_ELEMENTS(g_aWords_PublicKey) },
+};
+/** Number of entries in g_aRTCrKeyPublicMarkers. */
+RT_DECL_DATA_CONST(uint32_t const) g_cRTCrKeyPublicMarkers = RT_ELEMENTS(g_aRTCrKeyPublicMarkers);
+
+
+/** RSA private key marker words. */
+static RTCRPEMMARKERWORD const g_aWords_RsaPrivateKey[] =
+{ { RT_STR_TUPLE("RSA") }, { RT_STR_TUPLE("PRIVATE") }, { RT_STR_TUPLE("KEY") } };
+/** Generic encrypted private key marker words. */
+static RTCRPEMMARKERWORD const g_aWords_EncryptedPrivateKey[] =
+{ { RT_STR_TUPLE("ENCRYPTED") }, { RT_STR_TUPLE("PRIVATE") }, { RT_STR_TUPLE("KEY") } };
+/** Generic private key marker words. */
+static RTCRPEMMARKERWORD const g_aWords_PrivateKey[] =
+{ { RT_STR_TUPLE("PRIVATE") }, { RT_STR_TUPLE("KEY") } };
+
+/** Private key markers. */
+RT_DECL_DATA_CONST(RTCRPEMMARKER const) g_aRTCrKeyPrivateMarkers[] =
+{
+ { g_aWords_RsaPrivateKey, RT_ELEMENTS(g_aWords_RsaPrivateKey) },
+ { g_aWords_EncryptedPrivateKey, RT_ELEMENTS(g_aWords_EncryptedPrivateKey) },
+ { g_aWords_PrivateKey, RT_ELEMENTS(g_aWords_PrivateKey) },
+};
+/** Number of entries in g_aRTCrKeyPrivateMarkers. */
+RT_DECL_DATA_CONST(uint32_t const) g_cRTCrKeyPrivateMarkers = RT_ELEMENTS(g_aRTCrKeyPrivateMarkers);
+
+
+/** Private and public key markers. */
+RT_DECL_DATA_CONST(RTCRPEMMARKER const) g_aRTCrKeyAllMarkers[] =
+{
+ { g_aWords_RsaPublicKey, RT_ELEMENTS(g_aWords_RsaPublicKey) },
+ { g_aWords_PublicKey, RT_ELEMENTS(g_aWords_PublicKey) },
+ { g_aWords_RsaPrivateKey, RT_ELEMENTS(g_aWords_RsaPrivateKey) },
+ { g_aWords_EncryptedPrivateKey, RT_ELEMENTS(g_aWords_EncryptedPrivateKey) },
+ { g_aWords_PrivateKey, RT_ELEMENTS(g_aWords_PrivateKey) },
+};
+/** Number of entries in g_aRTCrKeyAllMarkers. */
+RT_DECL_DATA_CONST(uint32_t const) g_cRTCrKeyAllMarkers = RT_ELEMENTS(g_aRTCrKeyAllMarkers);
+
+
+/**
+ * Creates a key from a raw PKCS\#8 PrivateKeyInfo structure.
+ *
+ * This is common code to both kKeyFormat_PrivateKeyInfo and
+ * kKeyFormat_EncryptedPrivateKeyInfo.
+ *
+ * @returns IPRT status code.
+ * @param phKey Where to return the key handle on success.
+ * @param pPrimaryCursor Cursor structure to use.
+ * @param pbRaw The raw PrivateKeyInfo bytes.
+ * @param cbRaw Size of the raw PrivateKeyInfo structure.
+ * @param pErrInfo Where to return additional error information.
+ * @param pszErrorTag What to tag the decoding with.
+ */
+static int rtCrKeyCreateFromPrivateKeyInfo(PRTCRKEY phKey, PRTASN1CURSORPRIMARY pPrimaryCursor,
+ uint8_t const *pbRaw, size_t cbRaw, PRTERRINFO pErrInfo, const char *pszErrorTag)
+
+{
+ RTCRPKCS8PRIVATEKEYINFO PrivateKeyInfo;
+ RT_ZERO(PrivateKeyInfo);
+ RTAsn1CursorInitPrimary(pPrimaryCursor, pbRaw, (uint32_t)cbRaw, pErrInfo, &g_RTAsn1DefaultAllocator,
+ RTASN1CURSOR_FLAGS_DER, pszErrorTag);
+ int rc = RTCrPkcs8PrivateKeyInfo_DecodeAsn1(&pPrimaryCursor->Cursor, 0, &PrivateKeyInfo,
+ pszErrorTag ? pszErrorTag : "PrivateKeyInfo");
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Load the private key according to it's algorithm.
+ * We currently only support RSA (pkcs1-RsaEncryption).
+ */
+ if (RTAsn1ObjId_CompareWithString(&PrivateKeyInfo.PrivateKeyAlgorithm.Algorithm, RTCRX509ALGORITHMIDENTIFIERID_RSA) == 0)
+ rc = rtCrKeyCreateRsaPrivate(phKey, PrivateKeyInfo.PrivateKey.Asn1Core.uData.pv,
+ PrivateKeyInfo.PrivateKey.Asn1Core.cb, pErrInfo, pszErrorTag);
+ else
+ rc = RTERRINFO_LOG_SET(pErrInfo, VERR_CR_KEY_FORMAT_NOT_SUPPORTED,
+ "Support for PKCS#8 PrivateKeyInfo for non-RSA keys is not yet implemented");
+ RTCrPkcs8PrivateKeyInfo_Delete(&PrivateKeyInfo);
+ }
+ return rc;
+}
+
+
+/**
+ * Decrypts a PEM message.
+ *
+ * @returns IPRT status code
+ * @param pEncryptedKey The encrypted private key information.
+ * @param pszPassword The password to use to decrypt the key text.
+ * @param ppbDecrypted Where to return the decrypted message. Free using RTMemSaferFree.
+ * @param pcbDecrypted Where to return the length of the decrypted message.
+ * @param pcbDecryptedAlloced Where to return the allocation size.
+ * @param pErrInfo Where to return additional error information.
+ */
+static int rtCrKeyDecryptPkcs8Info(PRTCRPKCS8ENCRYPTEDPRIVATEKEYINFO pEncryptedKey, const char *pszPassword,
+ uint8_t **ppbDecrypted, size_t *pcbDecrypted, size_t *pcbDecryptedAlloced, PRTERRINFO pErrInfo)
+{
+ /*
+ * Initialize return values.
+ */
+ *ppbDecrypted = NULL;
+ *pcbDecrypted = 0;
+ *pcbDecryptedAlloced = 0;
+
+ /*
+ * This operation requires a password.
+ */
+ if (!pszPassword)
+ return VERR_CR_KEY_ENCRYPTED;
+
+#ifdef IPRT_WITH_OPENSSL /** @todo abstract encryption & decryption. */
+
+ /*
+ * Query the EncryptionAlgorithm bytes so we can construction a X509_ALGOR
+ * for use in PKCS12_pbe_crypt.
+ */
+ void *pvAlgoFree = NULL;
+ const uint8_t *pbAlgoRaw = NULL;
+ uint32_t cbAlgoRaw = 0;
+ int rc = RTAsn1EncodeQueryRawBits(&pEncryptedKey->EncryptionAlgorithm.SeqCore.Asn1Core,
+ &pbAlgoRaw, &cbAlgoRaw, &pvAlgoFree, pErrInfo);
+ AssertRCReturn(rc, rc);
+
+ const unsigned char *puchAlgo = pbAlgoRaw;
+ X509_ALGOR *pOsslAlgoRet = NULL;
+ pOsslAlgoRet = d2i_X509_ALGOR(&pOsslAlgoRet, &puchAlgo, cbAlgoRaw);
+
+ RTMemTmpFree(pvAlgoFree);
+ if (pOsslAlgoRet)
+ {
+ /*
+ * Do the decryption (en_de = 0).
+ */
+ int cbDecrypted = 0;
+ unsigned char *puchDecrypted = NULL;
+ puchDecrypted = PKCS12_pbe_crypt(pOsslAlgoRet, pszPassword, (int)strlen(pszPassword),
+ (unsigned char *)pEncryptedKey->EncryptedData.Asn1Core.uData.puch, /* cast for v1.0.x */
+ (int)pEncryptedKey->EncryptedData.Asn1Core.cb,
+ &puchDecrypted, &cbDecrypted, 0 /*en_de*/);
+ if (puchDecrypted)
+ {
+ /*
+ * Transfer to a safer buffer and carefully wipe the OpenSSL buffer.
+ */
+ uint8_t *pbFinal = (uint8_t *)RTMemSaferAllocZ(cbDecrypted);
+ if (pbFinal)
+ {
+ memcpy(pbFinal, puchDecrypted, cbDecrypted);
+ *ppbDecrypted = pbFinal;
+ *pcbDecrypted = cbDecrypted;
+ *pcbDecryptedAlloced = cbDecrypted;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ RTMemWipeThoroughly(puchDecrypted, cbDecrypted, 3);
+ OPENSSL_free(puchDecrypted);
+ }
+ else
+ rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_KEY_DECRYPTION_FAILED,
+ "Incorrect password? d2i_X509_ALGOR failed (%u)", ERR_get_error());
+ X509_ALGOR_free(pOsslAlgoRet);
+ }
+ else
+ rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_PKIX_OSSL_D2I_PRIVATE_KEY_FAILED /* close enough */,
+ "d2i_X509_ALGOR failed (%u)", ERR_get_error());
+ return rc;
+
+#else
+ RT_NOREF(pEncryptedKey, pszPassword, pErrInfo);
+ return VERR_CR_KEY_DECRYPTION_NOT_SUPPORTED;
+#endif
+}
+
+
+/**
+ * Decrypts a PEM message.
+ *
+ * @returns IPRT status code
+ * @param pszDekInfo The decryption info. See RFC-1421 section 4.6.1.3
+ * as well as RFC-1423).
+ * @param pszPassword The password to use to decrypt the key text.
+ * @param pbEncrypted The encrypted key text.
+ * @param cbEncrypted The size of the encrypted text.
+ * @param ppbDecrypted Where to return the decrypted message. Free using RTMemSaferFree.
+ * @param pcbDecrypted Where to return the length of the decrypted message.
+ * @param pcbDecryptedAlloced Where to return the allocation size.
+ * @param pErrInfo Where to return additional error information.
+ */
+static int rtCrKeyDecryptPemMessage(const char *pszDekInfo, const char *pszPassword, uint8_t *pbEncrypted, size_t cbEncrypted,
+ uint8_t **ppbDecrypted, size_t *pcbDecrypted, size_t *pcbDecryptedAlloced, PRTERRINFO pErrInfo)
+{
+ /*
+ * Initialize return values.
+ */
+ *ppbDecrypted = NULL;
+ *pcbDecrypted = 0;
+ *pcbDecryptedAlloced = 0;
+
+ /*
+ * Parse the DEK-Info.
+ */
+ if (!pszDekInfo)
+ return VERR_CR_KEY_NO_DEK_INFO;
+
+ /* Find the end of the algorithm */
+ const char *pszParams = strchr(pszDekInfo, ',');
+ if (!pszParams)
+ pszParams = strchr(pszDekInfo, '\0');
+ size_t cchAlgo = pszParams - pszDekInfo;
+ while (cchAlgo > 0 && RT_C_IS_SPACE(pszDekInfo[cchAlgo - 1]))
+ cchAlgo--;
+
+ /* Copy it out and zero terminating it. */
+ char szAlgo[256];
+ if (cchAlgo >= sizeof(szAlgo))
+ return RTErrInfoSetF(pErrInfo, VERR_CR_KEY_DEK_INFO_TOO_LONG, "Algorithms list is too long (%s)", pszDekInfo);
+ memcpy(szAlgo, pszDekInfo, cchAlgo);
+ szAlgo[cchAlgo] = '\0';
+
+ /* Parameters. */
+ pszParams = RTStrStripL(*pszParams == ',' ? pszParams + 1 : pszParams);
+ size_t const cchParams = strlen(pszParams);
+
+ /*
+ * Do we support the cipher?
+ */
+#ifdef IPRT_WITH_OPENSSL /** @todo abstract encryption & decryption. */
+ const EVP_CIPHER *pCipher = EVP_get_cipherbyname(szAlgo);
+ if (!pCipher)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_KEY_UNSUPPORTED_CIPHER, "Unknown key cipher: %s (params: %s)", szAlgo, pszParams);
+
+ /* Decode the initialization vector if one is required. */
+ uint8_t *pbInitVector = NULL;
+ int const cbInitVector = EVP_CIPHER_iv_length(pCipher);
+ if (cbInitVector > 0)
+ {
+ if (*pszParams == '\0')
+ return RTErrInfoSetF(pErrInfo, VERR_CR_KEY_MISSING_CIPHER_PARAMS,
+ "Cipher '%s' expected %u bytes initialization vector, none found", cbInitVector, szAlgo);
+ if ((size_t)cbInitVector > cchParams / 2)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_KEY_TOO_SHORT_CIPHER_IV,
+ "Too short initialization vector for '%s', expected %u chars found only %u: %s",
+ szAlgo, cbInitVector * 2, cchParams, pszParams);
+ pbInitVector = (uint8_t *)alloca(cbInitVector);
+ int rc = RTStrConvertHexBytes(pszParams, pbInitVector, cbInitVector, 0 /*fFlags*/);
+ if ( RT_FAILURE(rc)
+ && rc != VERR_BUFFER_OVERFLOW /* openssl ignores this condition */)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_KEY_MALFORMED_CIPHER_IV,
+ "Malformed initialization vector for '%s': %s (rc=%Rrc)", szAlgo, pszParams, rc);
+ }
+ else if (*pszParams != '\0')
+ return RTErrInfoSetF(pErrInfo, VERR_CR_KEY_UNEXPECTED_CIPHER_PARAMS,
+ "Cipher '%s' expected no parameters, found: %s", szAlgo, pszParams);
+
+ /*
+ * Do we have a password? If so try decrypt the key.
+ */
+ if (!pszPassword)
+ return VERR_CR_KEY_ENCRYPTED;
+
+ unsigned char abKey[EVP_MAX_KEY_LENGTH * 2];
+ int cbKey = EVP_BytesToKey(pCipher, EVP_md5(), pbInitVector, (unsigned char const *)pszPassword, (int)strlen(pszPassword),
+ 1, abKey, NULL);
+ if (!cbKey)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_KEY_PASSWORD_ENCODING, "EVP_BytesToKey failed to encode password");
+
+ EVP_CIPHER_CTX *pCipherCtx = EVP_CIPHER_CTX_new();
+ if (!pCipherCtx)
+ return VERR_NO_MEMORY;
+
+ int rc;
+ if (EVP_DecryptInit_ex(pCipherCtx, pCipher, NULL /*pEngine*/, abKey, pbInitVector))
+ {
+ size_t cbDecryptedAlloced = cbEncrypted;
+ int cbDecrypted = (int)cbDecryptedAlloced;
+ uint8_t *pbDecrypted = (uint8_t *)RTMemSaferAllocZ(cbDecryptedAlloced);
+ if (pbDecrypted)
+ {
+ if (EVP_DecryptUpdate(pCipherCtx, pbDecrypted, &cbDecrypted, pbEncrypted, (int)cbEncrypted))
+ {
+ int cbFinal = (int)cbDecryptedAlloced - cbDecrypted;
+ if (EVP_DecryptFinal_ex(pCipherCtx, &pbDecrypted[cbDecrypted], &cbFinal))
+ {
+ cbDecrypted += cbFinal;
+ Assert((size_t)cbDecrypted <= cbDecryptedAlloced);
+
+ /*
+ * Done! Just set the return values.
+ */
+ *pcbDecrypted = cbDecrypted;
+ *pcbDecryptedAlloced = cbDecryptedAlloced;
+ *ppbDecrypted = pbDecrypted;
+ pbDecrypted = NULL;
+ rc = VINF_CR_KEY_WAS_DECRYPTED;
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_CR_KEY_DECRYPTION_FAILED,
+ "Incorrect password? EVP_DecryptFinal_ex failed for %s", pszDekInfo);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_CR_KEY_DECRYPTION_FAILED,
+ "Incorrect password? EVP_DecryptUpdate failed for %s", pszDekInfo);
+ if (pbDecrypted)
+ RTMemSaferFree(pbDecrypted, cbDecryptedAlloced);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_CR_KEY_OSSL_DECRYPT_INIT_ERROR, "EVP_DecryptInit_ex failed for %s", pszDekInfo);
+ EVP_CIPHER_CTX_free(pCipherCtx);
+ return rc;
+#else
+ RT_NOREF(pbEncrypted, cbEncrypted, pszPassword, pErrInfo, cchParams);
+ return VERR_CR_KEY_DECRYPTION_NOT_SUPPORTED;
+#endif
+}
+
+
+RTDECL(int) RTCrKeyCreateFromPemSection(PRTCRKEY phKey, PCRTCRPEMSECTION pSection, uint32_t fFlags, const char *pszPassword,
+ PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ AssertReturn(!(fFlags & (~RTCRKEYFROM_F_VALID_MASK | RTCRKEYFROM_F_ONLY_PEM)), VERR_INVALID_FLAGS);
+
+ AssertPtrReturn(phKey, VERR_INVALID_POINTER);
+ *phKey = NIL_RTCRKEY;
+ AssertPtrReturn(pSection, VERR_INVALID_POINTER);
+ NOREF(pszPassword);
+
+ /*
+ * If the source is PEM section, try identify the format from the markers.
+ */
+ enum
+ {
+ kKeyFormat_Unknown = 0,
+ kKeyFormat_RsaPrivateKey,
+ kKeyFormat_RsaEncryptedPrivateKey,
+ kKeyFormat_RsaPublicKey,
+ kKeyFormat_SubjectPublicKeyInfo,
+ kKeyFormat_PrivateKeyInfo,
+ kKeyFormat_EncryptedPrivateKeyInfo
+ } enmFormat = kKeyFormat_Unknown;
+ const char *pszDekInfo = NULL;
+ PCRTCRPEMMARKER pMarker = pSection->pMarker;
+ if (pMarker)
+ {
+ if ( pMarker->cWords == 3
+ && strcmp(pMarker->paWords[0].pszWord, "RSA") == 0
+ && strcmp(pMarker->paWords[2].pszWord, "KEY") == 0)
+ {
+ if (strcmp(pMarker->paWords[1].pszWord, "PUBLIC") == 0)
+ enmFormat = kKeyFormat_RsaPublicKey;
+ else if (strcmp(pMarker->paWords[1].pszWord, "PRIVATE") == 0)
+ {
+ enmFormat = kKeyFormat_RsaPrivateKey;
+
+ /* RSA PRIVATE KEY encryption is advertised thru PEM header fields.
+ We need the DEK field to decrypt the message (see RFC-1421 4.6.1.3). */
+ for (PCRTCRPEMFIELD pField = pSection->pFieldHead; pField; pField = pField->pNext)
+ {
+ if ( pField->cchName == sizeof("Proc-Type") - 1
+ && pField->cchValue >= sizeof("4,ENCRYPTED") - 1
+ && memcmp(pField->szName, RT_STR_TUPLE("Proc-Type")) == 0)
+ {
+ const char *pszValue = pField->pszValue;
+ if (*pszValue == '4')
+ {
+ do
+ pszValue++;
+ while (RT_C_IS_SPACE(*pszValue) || RT_C_IS_PUNCT(*pszValue));
+ if (strcmp(pszValue, "ENCRYPTED") == 0)
+ enmFormat = kKeyFormat_RsaEncryptedPrivateKey;
+ }
+ }
+ else if ( pField->cchName == sizeof("DEK-Info") - 1
+ && pField->cchValue > 0
+ && !pszDekInfo)
+ pszDekInfo = pField->pszValue;
+ }
+ }
+ else
+ AssertFailed();
+ }
+ else if ( pMarker->cWords == 2
+ && strcmp(pMarker->paWords[1].pszWord, "KEY") == 0)
+ {
+ if (strcmp(pMarker->paWords[0].pszWord, "PUBLIC") == 0)
+ enmFormat = kKeyFormat_SubjectPublicKeyInfo;
+ else if (strcmp(pMarker->paWords[0].pszWord, "PRIVATE") == 0)
+ enmFormat = kKeyFormat_PrivateKeyInfo;
+ else
+ AssertFailed();
+ }
+ else if ( pMarker->cWords == 3
+ && strcmp(pMarker->paWords[0].pszWord, "ENCRYPTED") == 0
+ && strcmp(pMarker->paWords[1].pszWord, "PRIVATE") == 0
+ && strcmp(pMarker->paWords[2].pszWord, "KEY") == 0)
+ enmFormat = kKeyFormat_EncryptedPrivateKeyInfo;
+ else
+ AssertFailed();
+ }
+
+ /*
+ * Try guess the format from the binary data if needed.
+ */
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ if ( enmFormat == kKeyFormat_Unknown
+ && pSection->cbData > 10)
+ {
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pSection->pbData, (uint32_t)pSection->cbData,
+ pErrInfo, &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "probing/0");
+
+ /*
+ * First the must be a sequence.
+ */
+ RTASN1CORE Tag;
+ int rc = RTAsn1CursorReadHdr(&PrimaryCursor.Cursor, &Tag, "#1");
+ if (RT_SUCCESS(rc) && Tag.uTag == ASN1_TAG_SEQUENCE)
+ {
+ RTASN1CURSOR Cursor2;
+ RTAsn1CursorInitSubFromCore(&PrimaryCursor.Cursor, &Tag, &Cursor2, "probing/1");
+ rc = RTAsn1CursorReadHdr(&Cursor2, &Tag, "#2");
+
+ /*
+ * SEQUENCE SubjectPublicKeyInfo.Algorithm?
+ */
+ if (RT_SUCCESS(rc) && Tag.uTag == ASN1_TAG_SEQUENCE)
+ {
+ RTASN1CURSOR Cursor3;
+ RTAsn1CursorInitSubFromCore(&Cursor2, &Tag, &Cursor3, "probing/2");
+ rc = RTAsn1CursorReadHdr(&Cursor3, &Tag, "#3");
+
+ /* SEQUENCE SubjectPublicKeyInfo.Algorithm.Algorithm? */
+ if (RT_SUCCESS(rc) && Tag.uTag == ASN1_TAG_OID)
+ enmFormat = kKeyFormat_SubjectPublicKeyInfo;
+ }
+ /*
+ * INTEGER PrivateKeyInfo.Version?
+ * INTEGER RsaPublicKey.Modulus?
+ * INTEGER RsaPrivateKey.Version?
+ */
+ else if (RT_SUCCESS(rc) && Tag.uTag == ASN1_TAG_INTEGER)
+ {
+ rc = RTAsn1CursorReadHdr(RTAsn1CursorSkip(&Cursor2, Tag.cb), &Tag, "#4");
+
+ /* OBJECT PrivateKeyInfo.privateKeyAlgorithm? */
+ if (RT_SUCCESS(rc) && Tag.uTag == ASN1_TAG_OID)
+ enmFormat = kKeyFormat_PrivateKeyInfo;
+ /* INTEGER RsaPublicKey.PublicExponent?
+ INTEGER RsaPrivateKey.Modulus? */
+ else if (RT_SUCCESS(rc) && Tag.uTag == ASN1_TAG_INTEGER)
+ {
+ /* RsaPublicKey.PublicExponent is at the end. */
+ if (RTAsn1CursorIsEnd(&Cursor2))
+ enmFormat = kKeyFormat_RsaPublicKey;
+ else
+ {
+ /* Check for INTEGER RsaPrivateKey.PublicExponent nad PrivateExponent before concluding. */
+ rc = RTAsn1CursorReadHdr(RTAsn1CursorSkip(&Cursor2, Tag.cb), &Tag, "#5");
+ if (RT_SUCCESS(rc) && Tag.uTag == ASN1_TAG_INTEGER)
+ {
+ rc = RTAsn1CursorReadHdr(RTAsn1CursorSkip(&Cursor2, Tag.cb), &Tag, "#6");
+ if (RT_SUCCESS(rc) && Tag.uTag == ASN1_TAG_INTEGER)
+ enmFormat = kKeyFormat_RsaPrivateKey;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (enmFormat == kKeyFormat_Unknown)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_KEY_UNKNOWN_TYPE,
+ "Unable to identify the key format (%.*Rhxs)", RT_MIN(16, pSection->cbData), pSection->pbData);
+
+ /*
+ * Do the reading.
+ */
+ int rc;
+ switch (enmFormat)
+ {
+ case kKeyFormat_RsaPublicKey:
+ rc = rtCrKeyCreateRsaPublic(phKey, pSection->pbData, (uint32_t)pSection->cbData, pErrInfo, pszErrorTag);
+ break;
+
+ case kKeyFormat_RsaPrivateKey:
+ rc = rtCrKeyCreateRsaPrivate(phKey, pSection->pbData, (uint32_t)pSection->cbData, pErrInfo, pszErrorTag);
+ break;
+
+ case kKeyFormat_RsaEncryptedPrivateKey:
+ {
+ uint8_t *pbDecrypted = NULL;
+ size_t cbDecrypted = 0;
+ size_t cbDecryptedAlloced = 0;
+ rc = rtCrKeyDecryptPemMessage(pszDekInfo, pszPassword, pSection->pbData, pSection->cbData,
+ &pbDecrypted, &cbDecrypted, &cbDecryptedAlloced, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ int rc2 = rtCrKeyCreateRsaPrivate(phKey, pbDecrypted, (uint32_t)cbDecrypted, pErrInfo, pszErrorTag);
+ if (rc2 != VINF_SUCCESS)
+ rc = rc2;
+ RTMemSaferFree(pbDecrypted, cbDecryptedAlloced);
+ }
+ break;
+ }
+
+ case kKeyFormat_SubjectPublicKeyInfo:
+ {
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pSection->pbData, (uint32_t)pSection->cbData,
+ pErrInfo, &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, pszErrorTag);
+ RTCRX509SUBJECTPUBLICKEYINFO SubjectPubKeyInfo;
+ RT_ZERO(SubjectPubKeyInfo);
+ rc = RTCrX509SubjectPublicKeyInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &SubjectPubKeyInfo, "SubjectPubKeyInfo");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrKeyCreateFromSubjectPublicKeyInfo(phKey, &SubjectPubKeyInfo, pErrInfo, pszErrorTag);
+ RTCrX509SubjectPublicKeyInfo_Delete(&SubjectPubKeyInfo);
+ }
+ break;
+ }
+
+ case kKeyFormat_PrivateKeyInfo:
+ rc = rtCrKeyCreateFromPrivateKeyInfo(phKey, &PrimaryCursor, pSection->pbData, pSection->cbData, pErrInfo, pszErrorTag);
+ break;
+
+ case kKeyFormat_EncryptedPrivateKeyInfo:
+ {
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pSection->pbData, (uint32_t)pSection->cbData,
+ pErrInfo, &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, pszErrorTag);
+ RTCRPKCS8ENCRYPTEDPRIVATEKEYINFO EncryptedPrivateKeyInfo;
+ RT_ZERO(EncryptedPrivateKeyInfo);
+ rc = RTCrPkcs8EncryptedPrivateKeyInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &EncryptedPrivateKeyInfo,
+ pszErrorTag ? pszErrorTag : "EncryptedPrivateKeyInfo");
+ if (RT_SUCCESS(rc))
+ {
+ uint8_t *pbDecrypted = NULL;
+ size_t cbDecrypted = 0;
+ size_t cbDecryptedAlloced = 0;
+ rc = rtCrKeyDecryptPkcs8Info(&EncryptedPrivateKeyInfo, pszPassword,
+ &pbDecrypted, &cbDecrypted, &cbDecryptedAlloced, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtCrKeyCreateFromPrivateKeyInfo(phKey, &PrimaryCursor, pbDecrypted, cbDecrypted, pErrInfo, pszErrorTag);
+
+ RTMemSaferFree(pbDecrypted, cbDecryptedAlloced);
+ }
+ RTCrPkcs8EncryptedPrivateKeyInfo_Delete(&EncryptedPrivateKeyInfo);
+ }
+ break;
+ }
+
+ default:
+ AssertFailedStmt(rc = VERR_INTERNAL_ERROR_4);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTCrKeyCreateFromBuffer(PRTCRKEY phKey, uint32_t fFlags, void const *pvSrc, size_t cbSrc, const char *pszPassword,
+ PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ AssertReturn(!(fFlags & ~RTCRKEYFROM_F_VALID_MASK), VERR_INVALID_FLAGS);
+ PCRTCRPEMSECTION pSectionHead;
+ int rc = RTCrPemParseContent(pvSrc, cbSrc, fFlags, g_aRTCrKeyAllMarkers, g_cRTCrKeyAllMarkers, &pSectionHead, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ if (pSectionHead)
+ {
+ rc = RTCrKeyCreateFromPemSection(phKey, pSectionHead, fFlags & ~RTCRKEYFROM_F_ONLY_PEM, pszPassword,
+ pErrInfo, pszErrorTag);
+ RTCrPemFreeSections(pSectionHead);
+ }
+ else
+ rc = rc != VINF_SUCCESS ? -rc : VERR_INTERNAL_ERROR_2;
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTCrKeyCreateFromFile(PRTCRKEY phKey, uint32_t fFlags, const char *pszFilename,
+ const char *pszPassword, PRTERRINFO pErrInfo)
+{
+ AssertReturn(!(fFlags & ~RTCRKEYFROM_F_VALID_MASK), VERR_INVALID_FLAGS);
+ PCRTCRPEMSECTION pSectionHead;
+ int rc = RTCrPemReadFile(pszFilename, fFlags, g_aRTCrKeyAllMarkers, g_cRTCrKeyAllMarkers, &pSectionHead, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ if (pSectionHead)
+ {
+ rc = RTCrKeyCreateFromPemSection(phKey, pSectionHead, fFlags & ~RTCRKEYFROM_F_ONLY_PEM, pszPassword,
+ pErrInfo, RTPathFilename(pszFilename));
+ RTCrPemFreeSections(pSectionHead);
+ }
+ else
+ rc = rc != VINF_SUCCESS ? -rc : VERR_INTERNAL_ERROR_2;
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/crypto/key-internal.h b/src/VBox/Runtime/common/crypto/key-internal.h
new file mode 100644
index 00000000..88455fd7
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/key-internal.h
@@ -0,0 +1,132 @@
+/* $Id: key-internal.h $ */
+/** @file
+ * IPRT - Crypto - Cryptographic Keys, Internal Header.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_common_crypto_key_internal_h
+#define IPRT_INCLUDED_SRC_common_crypto_key_internal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/crypto/key.h>
+#include <iprt/bignum.h>
+
+
+/**
+ * Cryptographic key - core bits.
+ */
+typedef struct RTCRKEYINT
+{
+ /** Magic value (RTCRKEYINT_MAGIC). */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** The key type. */
+ RTCRKEYTYPE enmType;
+ /** Flags, RTCRKEYINT_F_XXX. */
+ uint32_t fFlags;
+ /** Number of bits in the key. */
+ uint32_t cBits;
+
+ /** Type specific data. */
+ union
+ {
+ /** RTCRKEYTYPE_RSA_PRIVATE. */
+ struct
+ {
+ /** The modulus. */
+ RTBIGNUM Modulus;
+ /** The private exponent. */
+ RTBIGNUM PrivateExponent;
+ /** The public exponent. */
+ RTBIGNUM PublicExponent;
+ /** @todo add more bits as needed. */
+ } RsaPrivate;
+
+ /** RTCRKEYTYPE_RSA_PUBLIC. */
+ struct
+ {
+ /** The modulus. */
+ RTBIGNUM Modulus;
+ /** The exponent. */
+ RTBIGNUM Exponent;
+ } RsaPublic;
+
+ /** RTCRKEYTYPE_ECDSA_PUBLIC. */
+ struct
+ {
+ /** The named curve. */
+ RTASN1OBJID NamedCurve;
+ /** @todo ECPoint. */
+ } EcdsaPublic;
+ } u;
+
+#if defined(IPRT_WITH_OPENSSL)
+ /** Size of raw key copy. */
+ uint32_t cbEncoded;
+ /** Raw copy of the key, for openssl and such.
+ * If sensitive, this is a safer allocation, otherwise it follows the structure. */
+ uint8_t *pbEncoded;
+#endif
+} RTCRKEYINT;
+/** Pointer to a crypographic key. */
+typedef RTCRKEYINT *PRTCRKEYINT;
+/** Pointer to a const crypographic key. */
+typedef RTCRKEYINT const *PCRTCRKEYINT;
+
+
+
+/** @name RTCRKEYINT_F_XXX.
+ * @{ */
+/** Key contains sensitive information, so no unnecessary copies. */
+#define RTCRKEYINT_F_SENSITIVE UINT32_C(0x00000001)
+/** Set if private key bits are present. */
+#define RTCRKEYINT_F_PRIVATE UINT32_C(0x00000002)
+/** Set if public key bits are present. */
+#define RTCRKEYINT_F_PUBLIC UINT32_C(0x00000004)
+/** Set if the cbEncoded/pbEncoded members are present. */
+#define RTCRKEYINT_F_INCLUDE_ENCODED UINT32_C(0x00000008)
+/** @} */
+
+DECLHIDDEN(int) rtCrKeyCreateWorker(PRTCRKEYINT *ppThis, RTCRKEYTYPE enmType, uint32_t fFlags,
+ void const *pvEncoded, uint32_t cbEncoded);
+DECLHIDDEN(int) rtCrKeyCreateRsaPublic(PRTCRKEY phKey, const void *pvKeyBits, uint32_t cbKeyBits,
+ PRTERRINFO pErrInfo, const char *pszErrorTag);
+DECLHIDDEN(int) rtCrKeyCreateRsaPrivate(PRTCRKEY phKey, const void *pvKeyBits, uint32_t cbKeyBits,
+ PRTERRINFO pErrInfo, const char *pszErrorTag);
+DECLHIDDEN(int) rtCrKeyCreateEcdsaPublic(PRTCRKEY phKey, PCRTASN1DYNTYPE pParameters,
+ const void *pvKeyBits, uint32_t cbKeyBits, PRTERRINFO pErrInfo, const char *pszErrorTag);
+
+#endif /* !IPRT_INCLUDED_SRC_common_crypto_key_internal_h */
diff --git a/src/VBox/Runtime/common/crypto/key-openssl.cpp b/src/VBox/Runtime/common/crypto/key-openssl.cpp
new file mode 100644
index 00000000..c9f73335
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/key-openssl.cpp
@@ -0,0 +1,335 @@
+/* $Id: key-openssl.cpp $ */
+/** @file
+ * IPRT - Crypto - Cryptographic Keys, OpenSSL glue.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_CRYPTO
+#include "internal/iprt.h"
+#include <iprt/crypto/key.h>
+
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/crypto/digest.h>
+
+#ifdef IPRT_WITH_OPENSSL
+# include "internal/iprt-openssl.h"
+# include "internal/magics.h"
+# include "internal/openssl-pre.h"
+# include <openssl/evp.h>
+# include "internal/openssl-post.h"
+# ifndef OPENSSL_VERSION_NUMBER
+# error "Missing OPENSSL_VERSION_NUMBER!"
+# endif
+# if OPENSSL_VERSION_NUMBER < 0x30000000 || defined(LIBRESSL_VERSION_NUMBER)
+# include "openssl/x509.h"
+# include <iprt/crypto/x509.h>
+# endif
+
+# include "key-internal.h"
+
+
+/**
+ * Helper that loads key parameters and the actual key bits if present.
+ */
+static int rtCrKeyToOpenSslKeyLoad(RTCRKEY hKey, int idKeyType, EVP_PKEY **ppEvpNewKey, bool fNeedPublic, PRTERRINFO pErrInfo)
+{
+ int rc = VINF_SUCCESS;
+ if ( hKey->enmType == RTCRKEYTYPE_ECDSA_PUBLIC
+ || hKey->enmType == RTCRKEYTYPE_ECDSA_PRIVATE)
+ {
+# if OPENSSL_VERSION_NUMBER >= 0x30000000 && !defined(LIBRESSL_VERSION_NUMBER)
+ void *pvFree = NULL;
+ const uint8_t *pbRaw = NULL;
+ uint32_t cbRaw = 0;
+ if (hKey->enmType == RTCRKEYTYPE_ECDSA_PUBLIC)
+ rc = RTAsn1EncodeQueryRawBits(&hKey->u.EcdsaPublic.NamedCurve.Asn1Core, &pbRaw, &cbRaw, &pvFree, pErrInfo);
+ else
+ AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
+ if (RT_SUCCESS(rc))
+ {
+ const unsigned char *puchParams = pbRaw;
+ EVP_PKEY *pRet = d2i_KeyParams(idKeyType, ppEvpNewKey, &puchParams, cbRaw);
+ if (pRet != NULL && pRet == *ppEvpNewKey)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTERRINFO_LOG_SET(pErrInfo, VERR_CR_PKIX_OSSL_D2I_KEY_PARAMS_FAILED, "d2i_KeyParams failed");
+
+ RTMemTmpFree(pvFree);
+ }
+#else
+ /*
+ * Cannot find any real suitable alternative to d2i_KeyParams in pre-3.0.x
+ * OpenSSL, so decided to use d2i_PUBKEY instead. This means we need to
+ * encode the stuff a X.509 SubjectPublicKeyInfo ASN.1 sequence first.
+ */
+ if (hKey->enmType == RTCRKEYTYPE_ECDSA_PUBLIC)
+ {
+ RTCRX509SUBJECTPUBLICKEYINFO PubKeyInfo;
+ rc = RTCrX509SubjectPublicKeyInfo_Init(&PubKeyInfo, &g_RTAsn1DefaultAllocator);
+ AssertRCReturn(rc, rc);
+
+ rc = RTAsn1ObjId_SetFromString(&PubKeyInfo.Algorithm.Algorithm, RTCRX509ALGORITHMIDENTIFIERID_ECDSA,
+ &g_RTAsn1DefaultAllocator);
+ if (RT_SUCCESS(rc))
+ rc = RTAsn1DynType_SetToObjId(&PubKeyInfo.Algorithm.Parameters, &hKey->u.EcdsaPublic.NamedCurve,
+ &g_RTAsn1DefaultAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ RTAsn1BitString_Delete(&PubKeyInfo.SubjectPublicKey);
+ rc = RTAsn1BitString_InitWithData(&PubKeyInfo.SubjectPublicKey, hKey->pbEncoded, hKey->cbEncoded * 8,
+ &g_RTAsn1DefaultAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ /* Encode the whole shebang. */
+ void *pvFree = NULL;
+ const uint8_t *pbRaw = NULL;
+ uint32_t cbRaw = 0;
+ rc = RTAsn1EncodeQueryRawBits(&PubKeyInfo.SeqCore.Asn1Core, &pbRaw, &cbRaw, &pvFree, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+
+ const unsigned char *puchPubKey = pbRaw;
+ EVP_PKEY *pRet = d2i_PUBKEY(ppEvpNewKey, &puchPubKey, cbRaw);
+ if (pRet != NULL && pRet == *ppEvpNewKey)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTERRINFO_LOG_SET(pErrInfo, VERR_CR_PKIX_OSSL_D2I_KEY_PARAMS_FAILED, "d2i_KeyParams failed");
+ RTMemTmpFree(pvFree);
+ }
+ }
+ }
+ AssertRC(rc);
+ RTCrX509SubjectPublicKeyInfo_Delete(&PubKeyInfo);
+ return rc;
+ }
+ rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_OPENSSL_VERSION_TOO_OLD,
+ "OpenSSL version %#x is too old for IPRTs ECDSA code", OPENSSL_VERSION_NUMBER);
+ RT_NOREF(idKeyType, ppEvpNewKey);
+#endif
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Load the key into the structure.
+ */
+ const unsigned char *puchPublicKey = hKey->pbEncoded;
+ EVP_PKEY *pRet;
+ if (fNeedPublic)
+ pRet = d2i_PublicKey(idKeyType, ppEvpNewKey, &puchPublicKey, hKey->cbEncoded);
+ else
+ pRet = d2i_PrivateKey(idKeyType, ppEvpNewKey, &puchPublicKey, hKey->cbEncoded);
+ if (pRet != NULL && pRet == *ppEvpNewKey)
+ return VINF_SUCCESS;
+
+ /* Bail out: */
+ if (fNeedPublic)
+ rc = RTERRINFO_LOG_SET(pErrInfo, VERR_CR_PKIX_OSSL_D2I_PUBLIC_KEY_FAILED, "d2i_PublicKey failed");
+ else
+ rc = RTERRINFO_LOG_SET(pErrInfo, VERR_CR_PKIX_OSSL_D2I_PRIVATE_KEY_FAILED, "d2i_PrivateKey failed");
+ }
+ return rc;
+}
+
+
+/**
+ * Creates an OpenSSL key for the given IPRT one, returning the message digest
+ * algorithm if desired.
+ *
+ * @returns IRPT status code.
+ * @param hKey The key to convert to an OpenSSL key.
+ * @param fNeedPublic Set if we need the public side of the key.
+ * @param pszAlgoObjId Alogrithm stuff we currently need.
+ * @param ppEvpKey Where to return the pointer to the key structure.
+ * @param ppEvpMdType Where to optionally return the message digest type.
+ * @param pErrInfo Where to optionally return more error details.
+ */
+DECLHIDDEN(int) rtCrKeyToOpenSslKey(RTCRKEY hKey, bool fNeedPublic, void /*EVP_PKEY*/ **ppEvpKey, PRTERRINFO pErrInfo)
+{
+ *ppEvpKey = NULL;
+ AssertReturn(hKey->u32Magic == RTCRKEYINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(fNeedPublic == !(hKey->fFlags & RTCRKEYINT_F_PRIVATE), VERR_WRONG_TYPE);
+ AssertReturn(hKey->fFlags & RTCRKEYINT_F_INCLUDE_ENCODED, VERR_WRONG_TYPE); /* build misconfig */
+
+ rtCrOpenSslInit();
+
+ /*
+ * Translate the key type from IPRT to EVP speak.
+ */
+ int idKeyType;
+ switch (hKey->enmType)
+ {
+ case RTCRKEYTYPE_RSA_PRIVATE:
+ case RTCRKEYTYPE_RSA_PUBLIC:
+ idKeyType = EVP_PKEY_RSA;
+ break;
+
+ case RTCRKEYTYPE_ECDSA_PUBLIC:
+ case RTCRKEYTYPE_ECDSA_PRIVATE:
+ idKeyType = EVP_PKEY_EC;
+ break;
+
+ default:
+ return RTErrInfoSetF(pErrInfo, VERR_NOT_SUPPORTED, "Unsupported key type: %d", hKey->enmType);
+ }
+
+ /*
+ * Allocate a new key structure and set its type.
+ */
+ EVP_PKEY *pEvpNewKey = EVP_PKEY_new();
+ if (!pEvpNewKey)
+ return RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "EVP_PKEY_new/%d failed", idKeyType);
+
+ /*
+ * Load key parameters and the key into the EVP structure.
+ */
+ int rc = rtCrKeyToOpenSslKeyLoad(hKey, idKeyType, &pEvpNewKey, fNeedPublic, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ *ppEvpKey = pEvpNewKey;
+ return rc;
+ }
+ EVP_PKEY_free(pEvpNewKey);
+ return rc;
+}
+
+
+/**
+ * Creates an OpenSSL key for the given IPRT one, returning the message digest
+ * algorithm if desired.
+ *
+ * @returns IRPT status code.
+ * @param hKey The key to convert to an OpenSSL key.
+ * @param fNeedPublic Set if we need the public side of the key.
+ * @param pszAlgoObjId Alogrithm stuff we currently need.
+ * @param ppEvpKey Where to return the pointer to the key structure.
+ * @param ppEvpMdType Where to optionally return the message digest type.
+ * @param pErrInfo Where to optionally return more error details.
+ */
+DECLHIDDEN(int) rtCrKeyToOpenSslKeyEx(RTCRKEY hKey, bool fNeedPublic, const char *pszAlgoObjId,
+ void /*EVP_PKEY*/ **ppEvpKey, const void /*EVP_MD*/ **ppEvpMdType, PRTERRINFO pErrInfo)
+{
+ *ppEvpKey = NULL;
+ if (ppEvpMdType)
+ *ppEvpMdType = NULL;
+ AssertReturn(hKey->u32Magic == RTCRKEYINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(fNeedPublic == !(hKey->fFlags & RTCRKEYINT_F_PRIVATE), VERR_WRONG_TYPE);
+ AssertReturn(hKey->fFlags & RTCRKEYINT_F_INCLUDE_ENCODED, VERR_WRONG_TYPE); /* build misconfig */
+
+ rtCrOpenSslInit();
+
+ /*
+ * Translate algorithm object ID into stuff that OpenSSL wants.
+ */
+ int iAlgoNid = OBJ_txt2nid(pszAlgoObjId);
+ if (iAlgoNid == NID_undef)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_PKIX_OSSL_CIPHER_ALGO_NOT_KNOWN,
+ "Unknown public key algorithm [OpenSSL]: %s", pszAlgoObjId);
+ const char *pszAlgoSn = OBJ_nid2sn(iAlgoNid);
+
+# if OPENSSL_VERSION_NUMBER >= 0x10001000 && !defined(LIBRESSL_VERSION_NUMBER)
+ int idAlgoPkey = 0;
+ int idAlgoMd = 0;
+ if (!OBJ_find_sigid_algs(iAlgoNid, &idAlgoMd, &idAlgoPkey))
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_PKIX_OSSL_CIPHER_ALGO_NOT_KNOWN_EVP,
+ "OBJ_find_sigid_algs failed on %u (%s, %s)", iAlgoNid, pszAlgoSn, pszAlgoObjId);
+ if (ppEvpMdType)
+ {
+ const EVP_MD *pEvpMdType = EVP_get_digestbynid(idAlgoMd);
+ if (!pEvpMdType)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_PKIX_OSSL_CIPHER_ALGO_NOT_KNOWN_EVP,
+ "EVP_get_digestbynid failed on %d (%s, %s)", idAlgoMd, pszAlgoSn, pszAlgoObjId);
+ *ppEvpMdType = pEvpMdType;
+ }
+# else
+ const EVP_MD *pEvpMdType = EVP_get_digestbyname(pszAlgoSn);
+ if (!pEvpMdType)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_PKIX_OSSL_CIPHER_ALGO_NOT_KNOWN_EVP,
+ "EVP_get_digestbyname failed on %s (%s)", pszAlgoSn, pszAlgoObjId);
+ if (ppEvpMdType)
+ *ppEvpMdType = pEvpMdType;
+# endif
+
+ /*
+ * Allocate a new key structure and set its type.
+ */
+ EVP_PKEY *pEvpNewKey = EVP_PKEY_new();
+ if (!pEvpNewKey)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_NO_MEMORY, "EVP_PKEY_new(%d) failed", iAlgoNid);
+
+ int rc;
+# if OPENSSL_VERSION_NUMBER >= 0x10001000 && !defined(LIBRESSL_VERSION_NUMBER)
+ if (EVP_PKEY_set_type(pEvpNewKey, idAlgoPkey))
+ {
+ int idKeyType = EVP_PKEY_base_id(pEvpNewKey);
+# else
+ int idKeyType = pEvpNewKey->type = EVP_PKEY_type(pEvpMdType->required_pkey_type[0]);
+# endif
+ if (idKeyType != NID_undef)
+
+ {
+ /*
+ * Load key parameters and the key into the EVP structure.
+ */
+ rc = rtCrKeyToOpenSslKeyLoad(hKey, idKeyType, &pEvpNewKey, fNeedPublic, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ *ppEvpKey = pEvpNewKey;
+ return rc;
+ }
+ }
+ else
+# if OPENSSL_VERSION_NUMBER < 0x10001000 || defined(LIBRESSL_VERSION_NUMBER)
+ rc = RTERRINFO_LOG_SET(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR, "EVP_PKEY_type() failed");
+# else
+ rc = RTERRINFO_LOG_SET(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR, "EVP_PKEY_base_id() failed");
+ }
+ else
+ rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR,
+ "EVP_PKEY_set_type(%u) failed (sig algo %s)", idAlgoPkey, pszAlgoSn);
+# endif
+
+ EVP_PKEY_free(pEvpNewKey);
+ *ppEvpKey = NULL;
+ return rc;
+}
+
+#endif /* IPRT_WITH_OPENSSL */
+
diff --git a/src/VBox/Runtime/common/crypto/key.cpp b/src/VBox/Runtime/common/crypto/key.cpp
new file mode 100644
index 00000000..e3c68e75
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/key.cpp
@@ -0,0 +1,557 @@
+/* $Id: key.cpp $ */
+/** @file
+ * IPRT - Crypto - Cryptographic Keys.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_CRYPTO
+#include "internal/iprt.h"
+#include <iprt/crypto/key.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/memsafer.h>
+#include <iprt/string.h>
+#include <iprt/crypto/rsa.h>
+#include <iprt/crypto/pkix.h>
+
+#include "internal/magics.h"
+#include "key-internal.h"
+
+
+/**
+ * Internal crypto key instance creator.
+ *
+ * This does most of the common work, caller does the 'u' and cBits jobs.
+ *
+ * @returns IPRT status code.
+ * @param ppThis Where to return the key instance.
+ * @param enmType The key type.
+ * @param fFlags The key flags.
+ * @param pvEncoded The encoded key bits.
+ * @param cbEncoded The size of the encoded key bits (in bytes).
+ */
+DECLHIDDEN(int) rtCrKeyCreateWorker(PRTCRKEYINT *ppThis, RTCRKEYTYPE enmType, uint32_t fFlags,
+ void const *pvEncoded, uint32_t cbEncoded)
+{
+ PRTCRKEYINT pThis = (PRTCRKEYINT)RTMemAllocZ(sizeof(*pThis) + (fFlags & RTCRKEYINT_F_SENSITIVE ? 0 : cbEncoded));
+ if (pThis)
+ {
+ pThis->enmType = enmType;
+ pThis->fFlags = fFlags;
+#if defined(IPRT_WITH_OPENSSL)
+ pThis->fFlags |= RTCRKEYINT_F_INCLUDE_ENCODED;
+ pThis->cbEncoded = cbEncoded;
+ if (!(fFlags & RTCRKEYINT_F_SENSITIVE))
+ pThis->pbEncoded = (uint8_t *)(pThis + 1);
+ else
+ {
+ pThis->pbEncoded = (uint8_t *)RTMemSaferAllocZ(cbEncoded);
+ if (!pThis->pbEncoded)
+ {
+ RTMemFree(pThis);
+ return VERR_NO_MEMORY;
+ }
+ }
+ memcpy(pThis->pbEncoded, pvEncoded, cbEncoded);
+#else
+ RT_NOREF(pvEncoded, cbEncoded);
+#endif
+ pThis->cRefs = 1;
+ pThis->u32Magic = RTCRKEYINT_MAGIC;
+ *ppThis = pThis;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Creates an RSA public key from a DER encoded RTCRRSAPUBLICKEY blob.
+ *
+ * @returns IPRT status code.
+ * @param phKey Where to return the key handle.
+ * @param pvKeyBits The DER encoded RTCRRSAPUBLICKEY blob.
+ * @param cbKeyBits The size of the blob.
+ * @param pErrInfo Where to supply addition error details. Optional.
+ * @param pszErrorTag Error tag. Optional.
+ */
+DECLHIDDEN(int) rtCrKeyCreateRsaPublic(PRTCRKEY phKey, const void *pvKeyBits, uint32_t cbKeyBits,
+ PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ /*
+ * Decode the key data first since that's what's most likely to fail here.
+ */
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pvKeyBits, cbKeyBits, pErrInfo, &g_RTAsn1DefaultAllocator,
+ RTASN1CURSOR_FLAGS_DER, pszErrorTag ? pszErrorTag : "rsa");
+ RTCRRSAPUBLICKEY PublicKey;
+ RT_ZERO(PublicKey);
+ int rc = RTCrRsaPublicKey_DecodeAsn1(&PrimaryCursor.Cursor, 0, &PublicKey, pszErrorTag ? pszErrorTag : "PublicKey");
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a key instance for it.
+ */
+ PRTCRKEYINT pThis;
+ rc = rtCrKeyCreateWorker(&pThis, RTCRKEYTYPE_RSA_PUBLIC, RTCRKEYINT_F_PUBLIC, pvKeyBits, cbKeyBits);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTAsn1Integer_ToBigNum(&PublicKey.Modulus, &pThis->u.RsaPublic.Modulus, 0);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->cBits = RTBigNumBitWidth(&pThis->u.RsaPublic.Modulus);
+ rc = RTAsn1Integer_ToBigNum(&PublicKey.PublicExponent, &pThis->u.RsaPublic.Exponent, 0);
+ if (RT_SUCCESS(rc))
+ {
+
+ /* Done. */
+ RTAsn1VtDelete(&PublicKey.SeqCore.Asn1Core);
+ *phKey = pThis;
+ return VINF_SUCCESS;
+ }
+ }
+ RTCrKeyRelease(pThis);
+ }
+ RTAsn1VtDelete(&PublicKey.SeqCore.Asn1Core);
+ }
+ *phKey = NIL_RTCRKEY;
+ return rc;
+}
+
+
+/**
+ * Creates an EC (ECDSA) public key from a DER encoded RTCRX50-PUBLICKEY blob.
+ *
+ * @returns IPRT status code.
+ * @param phKey Where to return the key handle.
+ * @param pParameters The algorithm parameters, typically namedCurve OID.
+ * @param pvKeyBits The DER encoded RTCRRSAPUBLICKEY blob.
+ * @param cbKeyBits The size of the blob.
+ * @param pErrInfo Where to supply addition error details. Optional.
+ * @param pszErrorTag Error tag. Optional.
+ */
+DECLHIDDEN(int) rtCrKeyCreateEcdsaPublic(PRTCRKEY phKey, PCRTASN1DYNTYPE pParameters, const void *pvKeyBits, uint32_t cbKeyBits,
+ PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+#if 0
+ /*
+ * Decode the key data first since that's what's most likely to fail here.
+ */
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pvKeyBits, cbKeyBits, pErrInfo, &g_RTAsn1DefaultAllocator,
+ RTASN1CURSOR_FLAGS_DER, pszErrorTag ? pszErrorTag : "rsa");
+ RTCRRSAPUBLICKEY PublicKey;
+ RT_ZERO(PublicKey);
+ int rc = RTCrRsaPublicKey_DecodeAsn1(&PrimaryCursor.Cursor, 0, &PublicKey, pszErrorTag ? pszErrorTag : "PublicKey");
+#else
+ RT_NOREF(pszErrorTag);
+ int rc = VINF_SUCCESS;
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Check the parameter (see RTC-5480, section 2.1.1).
+ */
+ if ( pParameters
+ && pParameters->enmType == RTASN1TYPE_OBJID
+ && RTAsn1ObjId_IsPresent(&pParameters->u.ObjId) /* paranoia */)
+ {
+ /*
+ * Create a key instance for it.
+ */
+ PRTCRKEYINT pThis;
+ rc = rtCrKeyCreateWorker(&pThis, RTCRKEYTYPE_ECDSA_PUBLIC, RTCRKEYINT_F_PUBLIC, pvKeyBits, cbKeyBits);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTAsn1ObjId_Clone(&pThis->u.EcdsaPublic.NamedCurve, &pParameters->u.ObjId, &g_RTAsn1DefaultAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ /* Done. */
+#if 0
+ RTAsn1VtDelete(&PublicKey.SeqCore.Asn1Core);
+#endif
+ *phKey = pThis;
+ return VINF_SUCCESS;
+ }
+ RTCrKeyRelease(pThis);
+ }
+ }
+ else if (!pParameters || pParameters->enmType == RTASN1TYPE_NOT_PRESENT)
+ rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_KEY_ALGO_PARAMS_MISSING,
+ "%s: ECDSA public key expected a namedCurve parameter", pszErrorTag);
+ else if (pParameters->enmType == RTASN1TYPE_NULL)
+ rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_KEY_ALGO_PARAMS_UNKNOWN,
+ "%s: ECDSA public key expected a namedCurve parameter, found implicitCurve (NULL) instead",
+ pszErrorTag);
+ else
+ rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_KEY_ALGO_PARAMS_UNKNOWN,
+ "%s: ECDSA public key expected namedCurve parameter, found %d",
+ pszErrorTag, pParameters->enmType);
+#if 0
+ RTAsn1VtDelete(&PublicKey.SeqCore.Asn1Core);
+#endif
+ }
+ *phKey = NIL_RTCRKEY;
+ return rc;
+}
+
+
+RTDECL(int) RTCrKeyCreateFromPublicAlgorithmAndBits(PRTCRKEY phKey, PCRTASN1OBJID pAlgorithm, PCRTASN1DYNTYPE pParameters,
+ PCRTASN1BITSTRING pPublicKey, PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(phKey, VERR_INVALID_POINTER);
+ *phKey = NIL_RTCRKEY;
+
+ AssertPtrReturn(pAlgorithm, VERR_INVALID_POINTER);
+ AssertReturn(RTAsn1ObjId_IsPresent(pAlgorithm), VERR_INVALID_PARAMETER);
+
+ AssertPtrReturn(pPublicKey, VERR_INVALID_POINTER);
+ AssertReturn(RTAsn1BitString_IsPresent(pPublicKey), VERR_INVALID_PARAMETER);
+
+ /*
+ * Identify the key type from the algorithm ID
+ */
+ const char * const pszEncryptionOid = RTCrX509AlgorithmIdentifier_GetEncryptionOidFromOid(pAlgorithm->szObjId,
+ false /*fMustIncludeHash*/);
+ if (pszEncryptionOid)
+ {
+ if (strcmp(pszEncryptionOid, RTCRX509ALGORITHMIDENTIFIERID_RSA) == 0)
+ return rtCrKeyCreateRsaPublic(phKey,
+ RTASN1BITSTRING_GET_BIT0_PTR(pPublicKey),
+ RTASN1BITSTRING_GET_BYTE_SIZE(pPublicKey),
+ pErrInfo, pszErrorTag);
+ if (strcmp(pszEncryptionOid, RTCRX509ALGORITHMIDENTIFIERID_ECDSA) == 0)
+ return rtCrKeyCreateEcdsaPublic(phKey,
+ pParameters,
+ RTASN1BITSTRING_GET_BIT0_PTR(pPublicKey),
+ RTASN1BITSTRING_GET_BYTE_SIZE(pPublicKey),
+ pErrInfo, pszErrorTag);
+ }
+ Assert(pszEncryptionOid == NULL);
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_CIPHER_ALGO_NOT_KNOWN, "oid=%s", pAlgorithm->szObjId);
+}
+
+
+RTDECL(int) RTCrKeyCreateFromSubjectPublicKeyInfo(PRTCRKEY phKey, struct RTCRX509SUBJECTPUBLICKEYINFO const *pSrc,
+ PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ AssertPtrReturn(pSrc, VERR_INVALID_POINTER);
+ AssertReturn(RTCrX509SubjectPublicKeyInfo_IsPresent(pSrc), VERR_INVALID_PARAMETER);
+ return RTCrKeyCreateFromPublicAlgorithmAndBits(phKey, &pSrc->Algorithm.Algorithm, &pSrc->Algorithm.Parameters,
+ &pSrc->SubjectPublicKey, pErrInfo, pszErrorTag);
+}
+
+
+/**
+ * Creates an RSA private key from a DER encoded RTCRRSAPRIVATEKEY blob.
+ *
+ * @returns IPRT status code.
+ * @param phKey Where to return the key handle.
+ * @param pvKeyBits The DER encoded RTCRRSAPRIVATEKEY blob.
+ * @param cbKeyBits The size of the blob.
+ * @param pErrInfo Where to supply addition error details. Optional.
+ * @param pszErrorTag Error tag. Optional.
+ */
+DECLHIDDEN(int) rtCrKeyCreateRsaPrivate(PRTCRKEY phKey, const void *pvKeyBits, uint32_t cbKeyBits,
+ PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ /*
+ * Decode the key data first since that's what's most likely to fail here.
+ */
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pvKeyBits, cbKeyBits, pErrInfo, &g_RTAsn1SaferAllocator,
+ RTASN1CURSOR_FLAGS_DER, pszErrorTag ? pszErrorTag : "rsa");
+ RTCRRSAPRIVATEKEY PrivateKey;
+ RT_ZERO(PrivateKey);
+ int rc = RTCrRsaPrivateKey_DecodeAsn1(&PrimaryCursor.Cursor, 0, &PrivateKey, pszErrorTag ? pszErrorTag : "PrivateKey");
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a key instance for it.
+ */
+ PRTCRKEYINT pThis;
+ rc = rtCrKeyCreateWorker(&pThis, RTCRKEYTYPE_RSA_PRIVATE, RTCRKEYINT_F_PRIVATE | RTCRKEYINT_F_SENSITIVE,
+ pvKeyBits, cbKeyBits);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTAsn1Integer_ToBigNum(&PrivateKey.Modulus, &pThis->u.RsaPrivate.Modulus, 0);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->cBits = RTBigNumBitWidth(&pThis->u.RsaPrivate.Modulus);
+ rc = RTAsn1Integer_ToBigNum(&PrivateKey.PrivateExponent, &pThis->u.RsaPrivate.PrivateExponent, 0);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTAsn1Integer_ToBigNum(&PrivateKey.PublicExponent, &pThis->u.RsaPrivate.PublicExponent, 0);
+ if (RT_SUCCESS(rc))
+ {
+ /* Done. */
+ RTAsn1VtDelete(&PrivateKey.SeqCore.Asn1Core);
+ RTMemWipeThoroughly(&PrivateKey, sizeof(PrivateKey), 3);
+ *phKey = pThis;
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ RTCrKeyRelease(pThis);
+ }
+ RTAsn1VtDelete(&PrivateKey.SeqCore.Asn1Core);
+ RTMemWipeThoroughly(&PrivateKey, sizeof(PrivateKey), 3);
+ }
+ *phKey = NIL_RTCRKEY;
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTCrKeyRetain(RTCRKEY hKey)
+{
+ PRTCRKEYINT pThis = hKey;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRKEYINT_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ AssertMsg(cRefs > 1 && cRefs < 1024, ("%#x\n", cRefs));
+ return cRefs;
+}
+
+
+/**
+ * Destructor.
+ *
+ * @returns 0
+ * @param pThis The key to destroy.
+ */
+static int rtCrKeyDestroy(PRTCRKEYINT pThis)
+{
+ /* Invalidate the object. */
+ pThis->u32Magic = ~RTCRKEYINT_MAGIC;
+
+ /* Type specific cleanup. */
+ switch (pThis->enmType)
+ {
+ case RTCRKEYTYPE_RSA_PUBLIC:
+ RTBigNumDestroy(&pThis->u.RsaPublic.Modulus);
+ RTBigNumDestroy(&pThis->u.RsaPublic.Exponent);
+ break;
+
+ case RTCRKEYTYPE_RSA_PRIVATE:
+ RTBigNumDestroy(&pThis->u.RsaPrivate.Modulus);
+ RTBigNumDestroy(&pThis->u.RsaPrivate.PrivateExponent);
+ RTBigNumDestroy(&pThis->u.RsaPrivate.PublicExponent);
+ break;
+
+ case RTCRKEYTYPE_ECDSA_PUBLIC:
+ RTAsn1ObjId_Delete(&pThis->u.EcdsaPublic.NamedCurve);
+ break;
+
+ case RTCRKEYTYPE_ECDSA_PRIVATE: /* not yet implemented */
+ case RTCRKEYTYPE_INVALID:
+ case RTCRKEYTYPE_END:
+ case RTCRKEYTYPE_32BIT_HACK:
+ AssertFailed();
+ }
+ pThis->enmType = RTCRKEYTYPE_INVALID;
+
+#if defined(IPRT_WITH_OPENSSL)
+ /* Free the encoded form if sensitive (otherwise it follows pThis). */
+ if (pThis->pbEncoded)
+ {
+ if (pThis->fFlags & RTCRKEYINT_F_SENSITIVE)
+ RTMemSaferFree((uint8_t *)pThis->pbEncoded, pThis->cbEncoded);
+ else
+ Assert(pThis->pbEncoded == (uint8_t *)(pThis + 1));
+ pThis->pbEncoded = NULL;
+ }
+#endif
+
+ /* Finally, free the key object itself. */
+ RTMemFree(pThis);
+ return 0;
+}
+
+
+RTDECL(uint32_t) RTCrKeyRelease(RTCRKEY hKey)
+{
+ if (hKey == NIL_RTCRKEY)
+ return 0;
+ PRTCRKEYINT pThis = hKey;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRKEYINT_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ AssertMsg(cRefs < 1024, ("%#x\n", cRefs));
+ if (cRefs != 0)
+ return cRefs;
+ return rtCrKeyDestroy(pThis);
+}
+
+
+RTDECL(RTCRKEYTYPE) RTCrKeyGetType(RTCRKEY hKey)
+{
+ PRTCRKEYINT pThis = hKey;
+ AssertPtrReturn(pThis, RTCRKEYTYPE_INVALID);
+ AssertReturn(pThis->u32Magic == RTCRKEYINT_MAGIC, RTCRKEYTYPE_INVALID);
+ return pThis->enmType;
+}
+
+
+RTDECL(bool) RTCrKeyHasPrivatePart(RTCRKEY hKey)
+{
+ PRTCRKEYINT pThis = hKey;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTCRKEYINT_MAGIC, false);
+ return RT_BOOL(pThis->fFlags & RTCRKEYINT_F_PRIVATE);
+}
+
+
+RTDECL(bool) RTCrKeyHasPublicPart(RTCRKEY hKey)
+{
+ PRTCRKEYINT pThis = hKey;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTCRKEYINT_MAGIC, false);
+ return RT_BOOL(pThis->fFlags & RTCRKEYINT_F_PUBLIC);
+}
+
+
+RTDECL(uint32_t) RTCrKeyGetBitCount(RTCRKEY hKey)
+{
+ PRTCRKEYINT pThis = hKey;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTCRKEYINT_MAGIC, 0);
+ return pThis->cBits;
+}
+
+
+RTDECL(int) RTCrKeyQueryRsaModulus(RTCRKEY hKey, PRTBIGNUM pModulus)
+{
+ PRTCRKEYINT pThis = hKey;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRKEYINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->enmType == RTCRKEYTYPE_RSA_PRIVATE || pThis->enmType == RTCRKEYTYPE_RSA_PUBLIC, VERR_WRONG_TYPE);
+ AssertPtrReturn(pModulus, VERR_INVALID_POINTER);
+
+ if (pThis->enmType == RTCRKEYTYPE_RSA_PRIVATE)
+ return RTBigNumAssign(pModulus, &pThis->u.RsaPrivate.Modulus);
+ return RTBigNumAssign(pModulus, &pThis->u.RsaPublic.Modulus);
+}
+
+
+RTDECL(int) RTCrKeyQueryRsaPrivateExponent(RTCRKEY hKey, PRTBIGNUM pPrivateExponent)
+{
+ PRTCRKEYINT pThis = hKey;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRKEYINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->enmType == RTCRKEYTYPE_RSA_PRIVATE, VERR_WRONG_TYPE);
+ AssertPtrReturn(pPrivateExponent, VERR_INVALID_POINTER);
+
+ return RTBigNumAssign(pPrivateExponent, &pThis->u.RsaPrivate.PrivateExponent);
+}
+
+
+RTDECL(int) RTCrKeyVerifyParameterCompatibility(RTCRKEY hKey, PCRTASN1DYNTYPE pParameters, bool fForSignature,
+ PCRTASN1OBJID pAlgorithm, PRTERRINFO pErrInfo)
+{
+ PRTCRKEYINT pThis = hKey;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRKEYINT_MAGIC, VERR_INVALID_HANDLE);
+ RT_NOREF(pAlgorithm);
+
+ switch (pThis->enmType)
+ {
+ /*
+ * No parameters. Ignore NULL.
+ */
+ case RTCRKEYTYPE_RSA_PRIVATE:
+ case RTCRKEYTYPE_RSA_PUBLIC:
+ if ( !pParameters
+ || pParameters->enmType == RTASN1TYPE_NOT_PRESENT
+ || pParameters->enmType == RTASN1TYPE_NULL)
+ return VINF_SUCCESS;
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_KEY_ALGO_PARAMS_UNEXPECTED,
+ "RSA keys does not generally take parameters (enmType=%d)", pParameters->enmType);
+
+ /*
+ * ECDSA requires a parameter. Currently only the named curve choice is supported.
+ * RFC-3279 section 2.2.3 states that for ecdsa-with-SHA1 the parameter MUST be
+ * omitted. ASSUMING the same applies to the other ecdsa-with-xxxx variants.
+ */
+ case RTCRKEYTYPE_ECDSA_PUBLIC:
+ if (!fForSignature)
+ {
+ /* Key rules: Parameters required. */
+ if (pParameters)
+ {
+ if (pParameters->enmType == RTASN1TYPE_OBJID)
+ {
+ if (RTAsn1ObjId_Compare(&pParameters->u.ObjId, &pThis->u.EcdsaPublic.NamedCurve) == 0)
+ return VINF_SUCCESS;
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_KEY_ALGO_PARAMS_MISMATCH,
+ "ECDSA NamedCurve difference: %s, key uses %s",
+ pParameters->u.ObjId.szObjId, pThis->u.EcdsaPublic.NamedCurve.szObjId);
+ }
+ /* We don't implement the other variants. */
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_KEY_ALGO_PARAMS_UNKNOWN,
+ "Unexpected ECDSA parameter: enmType=%d", pParameters->enmType);
+ }
+ return RTERRINFO_LOG_SET(pErrInfo, VERR_CR_KEY_ALGO_PARAMS_MISSING, "ECDSA keys requires parameter(s)");
+ }
+
+ /* Hash+ecdsa parameter rules: No parameters */
+ if ( !pParameters
+ || pParameters->enmType == RTASN1TYPE_NOT_PRESENT
+ || pParameters->enmType == RTASN1TYPE_NULL)
+ return VINF_SUCCESS;
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_KEY_ALGO_PARAMS_UNEXPECTED,
+ "ECDSA signature should have no parameters (enmType=%d)", pParameters->enmType);
+
+ case RTCRKEYTYPE_ECDSA_PRIVATE:
+ AssertFailedReturn(VERR_NOT_IMPLEMENTED);
+
+ case RTCRKEYTYPE_INVALID:
+ case RTCRKEYTYPE_END:
+ case RTCRKEYTYPE_32BIT_HACK:
+ break;
+ }
+ AssertFailedReturn(VERR_INTERNAL_ERROR_5);
+}
+
diff --git a/src/VBox/Runtime/common/crypto/pemfile-read.cpp b/src/VBox/Runtime/common/crypto/pemfile-read.cpp
new file mode 100644
index 00000000..646fff84
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pemfile-read.cpp
@@ -0,0 +1,663 @@
+/* $Id: pemfile-read.cpp $ */
+/** @file
+ * IPRT - Crypto - PEM file reader.
+ *
+ * See RFC-1341 for the original ideas for the format, but keep in mind
+ * that the format was hijacked and put to different uses. We're aiming at
+ * dealing with the different uses rather than anything email related here.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pem.h>
+
+#include <iprt/asm.h>
+#include <iprt/base64.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/memsafer.h>
+#include <iprt/file.h>
+#include <iprt/string.h>
+
+
+
+/**
+ * Looks for a PEM-like marker.
+ *
+ * @returns true if found, false if not.
+ * @param pbContent Start of the content to search thru.
+ * @param cbContent The size of the content to search.
+ * @param offStart The offset into pbContent to start searching.
+ * @param pszLeadWord The lead word (BEGIN/END).
+ * @param cchLeadWord The length of the lead word.
+ * @param paMarkers Pointer to an array of markers.
+ * @param cMarkers Number of markers in the array.
+ * @param ppMatch Where to return the pointer to the matching
+ * marker. Optional.
+ * @param poffBegin Where to return the start offset of the marker.
+ * Optional.
+ * @param poffEnd Where to return the end offset of the marker
+ * (trailing whitespace and newlines will be
+ * skipped). Optional.
+ */
+static bool rtCrPemFindMarker(uint8_t const *pbContent, size_t cbContent, size_t offStart,
+ const char *pszLeadWord, size_t cchLeadWord, PCRTCRPEMMARKER paMarkers, size_t cMarkers,
+ PCRTCRPEMMARKER *ppMatch, size_t *poffBegin, size_t *poffEnd)
+{
+ /* Remember the start of the content for the purpose of calculating offsets. */
+ uint8_t const * const pbStart = pbContent;
+
+ /* Skip adhead by offStart */
+ if (offStart >= cbContent)
+ return false;
+ pbContent += offStart;
+ cbContent -= offStart;
+
+ /*
+ * Search the content.
+ */
+ while (cbContent > 6)
+ {
+ /*
+ * Look for dashes.
+ */
+ uint8_t const *pbStartSearch = pbContent;
+ pbContent = (uint8_t const *)memchr(pbContent, '-', cbContent);
+ if (!pbContent)
+ break;
+
+ cbContent -= pbContent - pbStartSearch;
+ if (cbContent < 6)
+ break;
+
+ /*
+ * There must be at least three to interest us.
+ */
+ if ( pbContent[1] == '-'
+ && pbContent[2] == '-')
+ {
+ unsigned cDashes = 3;
+ while (cDashes < cbContent && pbContent[cDashes] == '-')
+ cDashes++;
+
+ if (poffBegin)
+ *poffBegin = pbContent - pbStart;
+ cbContent -= cDashes;
+ pbContent += cDashes;
+
+ /*
+ * Match lead word.
+ */
+ if ( cbContent > cchLeadWord
+ && memcmp(pbContent, pszLeadWord, cchLeadWord) == 0
+ && RT_C_IS_BLANK(pbContent[cchLeadWord]) )
+ {
+ pbContent += cchLeadWord;
+ cbContent -= cchLeadWord;
+ while (cbContent > 0 && RT_C_IS_BLANK(*pbContent))
+ {
+ pbContent++;
+ cbContent--;
+ }
+
+ /*
+ * Match one of the specified markers.
+ */
+ uint8_t const *pbSavedContent = pbContent;
+ size_t const cbSavedContent = cbContent;
+ for (uint32_t iMarker = 0; iMarker < cMarkers; iMarker++)
+ {
+ pbContent = pbSavedContent;
+ cbContent = cbSavedContent;
+
+ uint32_t cWords = paMarkers[iMarker].cWords;
+ PCRTCRPEMMARKERWORD pWord = paMarkers[iMarker].paWords;
+ while (cWords > 0)
+ {
+ uint32_t const cchWord = pWord->cchWord;
+ if (cbContent <= cchWord)
+ break;
+ if (memcmp(pbContent, pWord->pszWord, cchWord))
+ break;
+ pbContent += cchWord;
+ cbContent -= cchWord;
+
+ if (!cbContent)
+ break;
+ if (RT_C_IS_BLANK(*pbContent))
+ do
+ {
+ pbContent++;
+ cbContent--;
+ } while (cbContent > 0 && RT_C_IS_BLANK(*pbContent));
+ else if (cWords > 1 || pbContent[0] != '-')
+ break;
+
+ cWords--;
+ if (cWords == 0)
+ {
+ /*
+ * If there are three or more dashes following now, we've got a hit.
+ */
+ if ( cbContent > 3
+ && pbContent[0] == '-'
+ && pbContent[1] == '-'
+ && pbContent[2] == '-')
+ {
+ cDashes = 3;
+ while (cDashes < cbContent && pbContent[cDashes] == '-')
+ cDashes++;
+ cbContent -= cDashes;
+ pbContent += cDashes;
+
+ /*
+ * Skip spaces and newline.
+ */
+ while (cbContent > 0 && RT_C_IS_SPACE(*pbContent))
+ pbContent++, cbContent--;
+ if (poffEnd)
+ *poffEnd = pbContent - pbStart;
+ if (ppMatch)
+ *ppMatch = &paMarkers[iMarker];
+ return true;
+ }
+ break;
+ }
+ pWord++;
+ } /* for each word in marker. */
+ } /* for each marker. */
+ }
+ }
+ else
+ {
+ pbContent++;
+ cbContent--;
+ }
+ }
+
+ return false;
+}
+
+
+static bool rtCrPemFindMarkerSection(uint8_t const *pbContent, size_t cbContent, size_t offStart,
+ PCRTCRPEMMARKER paMarkers, size_t cMarkers,
+ PCRTCRPEMMARKER *ppMatch, size_t *poffBegin, size_t *poffEnd, size_t *poffResume)
+{
+ /** @todo Detect BEGIN / END mismatch. */
+ PCRTCRPEMMARKER pMatch;
+ if (rtCrPemFindMarker(pbContent, cbContent, offStart, "BEGIN", 5, paMarkers, cMarkers,
+ &pMatch, NULL /*poffStart*/, poffBegin))
+ {
+ if (rtCrPemFindMarker(pbContent, cbContent, *poffBegin, "END", 3, pMatch, 1,
+ NULL /*ppMatch*/, poffEnd, poffResume))
+ {
+ *ppMatch = pMatch;
+ return true;
+ }
+ }
+ *ppMatch = NULL;
+ return false;
+}
+
+
+/**
+ * Parses any fields the message may contain.
+ *
+ * @retval VINF_SUCCESS
+ * @retval VERR_NO_MEMORY
+ * @retval VERR_CR_MALFORMED_PEM_HEADER
+ *
+ * @param pSection The current section, where we will attach a list of
+ * fields to the pFieldHead member.
+ * @param pbContent The content of the PEM message being parsed.
+ * @param cbContent The length of the PEM message.
+ * @param pcbFields Where to return the length of the header fields we found.
+ */
+static int rtCrPemProcessFields(PRTCRPEMSECTION pSection, uint8_t const *pbContent, size_t cbContent, size_t *pcbFields)
+{
+ uint8_t const * const pbContentStart = pbContent;
+
+ /*
+ * Work the encapulated header protion field by field.
+ *
+ * This is optional, so currently we don't throw errors here but leave that
+ * to when we work the text portion with the base64 decoder. Also, as a reader
+ * we don't go all pedanic on confirming to specification (RFC-1421), especially
+ * given that it's used for crypto certificates, keys and the like not email. :-)
+ */
+ PCRTCRPEMFIELD *ppNext = &pSection->pFieldHead;
+ while (cbContent > 0)
+ {
+ /* Just look for a colon first. */
+ const uint8_t *pbColon = (const uint8_t *)memchr(pbContent, ':', cbContent);
+ if (!pbColon)
+ break;
+ size_t offColon = pbColon - pbContent;
+
+ /* Check that the colon is within the first line. */
+ if (!memchr(pbContent, '\n', cbContent - offColon))
+ return VERR_CR_MALFORMED_PEM_HEADER;
+
+ /* Skip leading spaces (there shouldn't be any, but just in case). */
+ while (RT_C_IS_BLANK(*pbContent) && /*paranoia:*/ offColon > 0)
+ {
+ offColon--;
+ cbContent--;
+ pbContent++;
+ }
+
+ /* There shouldn't be any spaces before the colon, but just in case */
+ size_t cchName = offColon;
+ while (cchName > 0 && RT_C_IS_BLANK(pbContent[cchName - 1]))
+ cchName--;
+
+ /* Skip leading value spaces (there typically is at least one). */
+ size_t offValue = offColon + 1;
+ while (offValue < cbContent && RT_C_IS_BLANK(pbContent[offValue]))
+ offValue++;
+
+ /* Find the newline the field value ends with and where the next iteration should start later on. */
+ size_t cbLeft;
+ uint8_t const *pbNext = (uint8_t const *)memchr(&pbContent[offValue], '\n', cbContent - offValue);
+ while ( pbNext
+ && (cbLeft = pbNext - pbContent) < cbContent
+ && RT_C_IS_BLANK(pbNext[1]) /* next line must start with a space or tab */)
+ pbNext = (uint8_t const *)memchr(&pbNext[1], '\n', cbLeft - 1);
+
+ size_t cchValue;
+ if (pbNext)
+ {
+ cchValue = pbNext - &pbContent[offValue];
+ if (cchValue > 0 && pbNext[-1] == '\r')
+ cchValue--;
+ pbNext++;
+ }
+ else
+ {
+ cchValue = cbContent - offValue;
+ pbNext = &pbContent[cbContent];
+ }
+
+ /* Strip trailing spaces. */
+ while (cchValue > 0 && RT_C_IS_BLANK(pbContent[offValue + cchValue - 1]))
+ cchValue--;
+
+ /*
+ * Allocate a field instance.
+ *
+ * Note! We don't consider field data sensitive at the moment. This
+ * mainly because the fields are chiefly used to indicate the
+ * encryption parameters to the body.
+ */
+ PRTCRPEMFIELD pNewField = (PRTCRPEMFIELD)RTMemAllocZVar(sizeof(*pNewField) + cchName + 1 + cchValue + 1);
+ if (!pNewField)
+ return VERR_NO_MEMORY;
+ pNewField->cchName = cchName;
+ pNewField->cchValue = cchValue;
+ memcpy(pNewField->szName, pbContent, cchName);
+ pNewField->szName[cchName] = '\0';
+ char *pszDst = (char *)memcpy(&pNewField->szName[cchName + 1], &pbContent[offValue], cchValue);
+ pNewField->pszValue = pszDst;
+ pszDst[cchValue] = '\0';
+ pNewField->pNext = NULL;
+
+ *ppNext = pNewField;
+ ppNext = &pNewField->pNext;
+
+ /*
+ * Advance past the field.
+ */
+ cbContent -= pbNext - pbContent;
+ pbContent = pbNext;
+ }
+
+ /*
+ * Skip blank line(s) before the body.
+ */
+ while (cbContent >= 1)
+ {
+ size_t cbSkip;
+ if (pbContent[0] == '\n')
+ cbSkip = 1;
+ else if ( pbContent[0] == '\r'
+ && cbContent >= 2
+ && pbContent[1] == '\n')
+ cbSkip = 2;
+ else
+ break;
+ pbContent += cbSkip;
+ cbContent -= cbSkip;
+ }
+
+ *pcbFields = pbContent - pbContentStart;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Does the decoding of a PEM-like data blob after it has been located.
+ *
+ * @returns IPRT status ocde
+ * @param pbContent The start of the PEM-like content (text).
+ * @param cbContent The max size of the PEM-like content.
+ * @param fSensitive Set if the safer allocator should be used.
+ * @param ppvDecoded Where to return a heap block containing the
+ * decoded content.
+ * @param pcbDecoded Where to return the size of the decoded content.
+ */
+static int rtCrPemDecodeBase64(uint8_t const *pbContent, size_t cbContent, bool fSensitive,
+ void **ppvDecoded, size_t *pcbDecoded)
+{
+ ssize_t cbDecoded = RTBase64DecodedSizeEx((const char *)pbContent, cbContent, NULL);
+ if (cbDecoded < 0)
+ return VERR_INVALID_BASE64_ENCODING;
+
+ *pcbDecoded = cbDecoded;
+ void *pvDecoded = !fSensitive ? RTMemAlloc(cbDecoded) : RTMemSaferAllocZ(cbDecoded);
+ if (!pvDecoded)
+ return VERR_NO_MEMORY;
+
+ size_t cbActual;
+ int rc = RTBase64DecodeEx((const char *)pbContent, cbContent, pvDecoded, cbDecoded, &cbActual, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (cbActual == (size_t)cbDecoded)
+ {
+ *ppvDecoded = pvDecoded;
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_INTERNAL_ERROR_3;
+ }
+ if (!fSensitive)
+ RTMemFree(pvDecoded);
+ else
+ RTMemSaferFree(pvDecoded, cbDecoded);
+ return rc;
+}
+
+
+/**
+ * Checks if the content of a file looks to be binary or not.
+ *
+ * @returns true if likely to be binary, false if not binary.
+ * @param pbFile The file bytes to scan.
+ * @param cbFile The number of bytes.
+ * @param fFlags RTCRPEMREADFILE_F_XXX
+ */
+static bool rtCrPemIsBinaryBlob(uint8_t const *pbFile, size_t cbFile, uint32_t fFlags)
+{
+ if (fFlags & RTCRPEMREADFILE_F_ONLY_PEM)
+ return false;
+
+ /*
+ * Well formed PEM files should probably only contain 7-bit ASCII and
+ * restrict thenselfs to the following control characters:
+ * tab, newline, return, form feed
+ *
+ * However, if we want to read PEM files which contains human readable
+ * certificate details before or after each base-64 section, we can't stick
+ * to 7-bit ASCII. We could say it must be UTF-8, but that's probably to
+ * limited as well. So, we'll settle for detecting binary files by control
+ * characters alone (safe enough for DER encoded stuff, I think).
+ */
+ while (cbFile-- > 0)
+ {
+ uint8_t const b = *pbFile++;
+ if (b < 32 && b != '\t' && b != '\n' && b != '\r' && b != '\f')
+ {
+ /* Ignore EOT (4), SUB (26) and NUL (0) at the end of the file. */
+ if ( (b == 4 || b == 26)
+ && ( cbFile == 0
+ || ( cbFile == 1
+ && *pbFile == '\0')))
+ return false;
+
+ if (b == 0 && cbFile == 0)
+ return false;
+
+ return true;
+ }
+ }
+ return false;
+}
+
+
+RTDECL(int) RTCrPemFreeSections(PCRTCRPEMSECTION pSectionHead)
+{
+ while (pSectionHead != NULL)
+ {
+ PRTCRPEMSECTION pFree = (PRTCRPEMSECTION)pSectionHead;
+ pSectionHead = pSectionHead->pNext;
+ ASMCompilerBarrier(); /* paranoia */
+
+ if (pFree->pbData)
+ {
+ if (!pFree->fSensitive)
+ RTMemFree(pFree->pbData);
+ else
+ RTMemSaferFree(pFree->pbData, pFree->cbData);
+ pFree->pbData = NULL;
+ pFree->cbData = 0;
+ }
+
+ PRTCRPEMFIELD pField = (PRTCRPEMFIELD)pFree->pFieldHead;
+ if (pField)
+ {
+ pFree->pFieldHead = NULL;
+ do
+ {
+ PRTCRPEMFIELD pFreeField = pField;
+ pField = (PRTCRPEMFIELD)pField->pNext;
+ ASMCompilerBarrier(); /* paranoia */
+
+ pFreeField->pszValue = NULL;
+ RTMemFree(pFreeField);
+ } while (pField);
+ }
+
+ RTMemFree(pFree);
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCrPemParseContent(void const *pvContent, size_t cbContent, uint32_t fFlags,
+ PCRTCRPEMMARKER paMarkers, size_t cMarkers,
+ PCRTCRPEMSECTION *ppSectionHead, PRTERRINFO pErrInfo)
+{
+ RT_NOREF_PV(pErrInfo);
+
+ /*
+ * Input validation.
+ */
+ AssertPtr(ppSectionHead);
+ *ppSectionHead = NULL;
+ AssertReturn(cbContent, VINF_EOF);
+ AssertPtr(pvContent);
+ AssertPtr(paMarkers);
+ AssertReturn(!(fFlags & ~RTCRPEMREADFILE_F_VALID_MASK), VERR_INVALID_FLAGS);
+
+ /*
+ * Pre-allocate a section.
+ */
+ int rc = VINF_SUCCESS;
+ PRTCRPEMSECTION pSection = (PRTCRPEMSECTION)RTMemAllocZ(sizeof(*pSection));
+ if (pSection)
+ {
+ bool const fSensitive = RT_BOOL(fFlags & RTCRPEMREADFILE_F_SENSITIVE);
+
+ /*
+ * Try locate the first section.
+ */
+ uint8_t const *pbContent = (uint8_t const *)pvContent;
+ size_t offBegin, offEnd, offResume;
+ PCRTCRPEMMARKER pMatch;
+ if ( !rtCrPemIsBinaryBlob(pbContent, cbContent, fFlags)
+ && rtCrPemFindMarkerSection(pbContent, cbContent, 0 /*offStart*/, paMarkers, cMarkers,
+ &pMatch, &offBegin, &offEnd, &offResume) )
+ {
+ PCRTCRPEMSECTION *ppNext = ppSectionHead;
+ for (;;)
+ {
+ //pSection->pNext = NULL;
+ pSection->pMarker = pMatch;
+ //pSection->pbData = NULL;
+ //pSection->cbData = 0;
+ //pSection->pFieldHead = NULL;
+ pSection->fSensitive = fSensitive;
+
+ *ppNext = pSection;
+ ppNext = &pSection->pNext;
+
+ /*
+ * Decode the section.
+ */
+ size_t cbFields = 0;
+ int rc2 = rtCrPemProcessFields(pSection, pbContent + offBegin, offEnd - offBegin, &cbFields);
+ offBegin += cbFields;
+ if (RT_SUCCESS(rc2))
+ rc2 = rtCrPemDecodeBase64(pbContent + offBegin, offEnd - offBegin, fSensitive,
+ (void **)&pSection->pbData, &pSection->cbData);
+ if (RT_FAILURE(rc2))
+ {
+ pSection->pbData = NULL;
+ pSection->cbData = 0;
+ if ( rc2 == VERR_INVALID_BASE64_ENCODING
+ && (fFlags & RTCRPEMREADFILE_F_CONTINUE_ON_ENCODING_ERROR))
+ rc = -rc2;
+ else
+ {
+ rc = rc2;
+ break;
+ }
+ }
+
+ /*
+ * More sections?
+ */
+ if ( offResume + 12 >= cbContent
+ || offResume >= cbContent
+ || !rtCrPemFindMarkerSection(pbContent, cbContent, offResume, paMarkers, cMarkers,
+ &pMatch, &offBegin, &offEnd, &offResume) )
+ break; /* No. */
+
+ /* Ok, allocate a new record for it. */
+ pSection = (PRTCRPEMSECTION)RTMemAllocZ(sizeof(*pSection));
+ if (RT_UNLIKELY(!pSection))
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ RTCrPemFreeSections(*ppSectionHead);
+ }
+ else
+ {
+ if (!(fFlags & RTCRPEMREADFILE_F_ONLY_PEM))
+ {
+ /*
+ * No PEM section found. Return the whole file as one binary section.
+ */
+ //pSection->pNext = NULL;
+ //pSection->pMarker = NULL;
+ //pSection->pFieldHead = NULL;
+ pSection->cbData = cbContent;
+ pSection->fSensitive = fSensitive;
+ if (!fSensitive)
+ pSection->pbData = (uint8_t *)RTMemDup(pbContent, cbContent);
+ else
+ {
+ pSection->pbData = (uint8_t *)RTMemSaferAllocZ(cbContent);
+ if (pSection->pbData)
+ memcpy(pSection->pbData, pbContent, cbContent);
+ }
+ if (pSection->pbData)
+ {
+ *ppSectionHead = pSection;
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VWRN_NOT_FOUND;
+ RTMemFree(pSection);
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ *ppSectionHead = NULL;
+ return rc;
+}
+
+
+RTDECL(int) RTCrPemReadFile(const char *pszFilename, uint32_t fFlags, PCRTCRPEMMARKER paMarkers, size_t cMarkers,
+ PCRTCRPEMSECTION *ppSectionHead, PRTERRINFO pErrInfo)
+{
+ *ppSectionHead = NULL;
+ AssertReturn(!(fFlags & ~RTCRPEMREADFILE_F_VALID_MASK), VERR_INVALID_FLAGS);
+
+ size_t cbContent;
+ void *pvContent;
+ int rc = RTFileReadAllEx(pszFilename, 0, 64U*_1M, RTFILE_RDALL_O_DENY_WRITE, &pvContent, &cbContent);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrPemParseContent(pvContent, cbContent, fFlags, paMarkers, cMarkers, ppSectionHead, pErrInfo);
+ if (fFlags & RTCRPEMREADFILE_F_SENSITIVE)
+ RTMemWipeThoroughly(pvContent, cbContent, 3);
+ RTFileReadAllFree(pvContent, cbContent);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "RTFileReadAllEx failed with %Rrc on '%s'", rc, pszFilename);
+ return rc;
+}
+
+
+RTDECL(const char *) RTCrPemFindFirstSectionInContent(void const *pvContent, size_t cbContent,
+ PCRTCRPEMMARKER paMarkers, size_t cMarkers)
+{
+ size_t offBegin;
+ if (rtCrPemFindMarker((uint8_t *)pvContent, cbContent, 0, "BEGIN", 5, paMarkers, cMarkers, NULL, &offBegin, NULL))
+ return (const char *)pvContent + offBegin;
+ return NULL;
+}
+
diff --git a/src/VBox/Runtime/common/crypto/pemfile-write.cpp b/src/VBox/Runtime/common/crypto/pemfile-write.cpp
new file mode 100644
index 00000000..c37b653e
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pemfile-write.cpp
@@ -0,0 +1,266 @@
+/* $Id: pemfile-write.cpp $ */
+/** @file
+ * IPRT - Crypto - PEM file writer.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pem.h>
+
+#include <iprt/asn1.h>
+#include <iprt/base64.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include <iprt/vfs.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Used by rtCrPemWriteAsn1Callback to buffer data before outputting it as
+ * BASE64.
+ *
+ * An encoded line is 64 characters long plus a newline, covering 48 bytes
+ * of binary data. We want about 4KB of output:
+ * 4096 / 65 = 63.015384615384615384615384615385
+ * 64 * 65 + 1 = 4161 (0x1041)
+ */
+typedef struct PEMOUTPUTASN1
+{
+ size_t cbPending;
+ PFNRTSTROUTPUT pfnOutput;
+ void *pvUser;
+ size_t cchRet;
+ uint8_t abBlock[0x0c00];
+ char szBlock[0x1060];
+} PEMOUTPUTASN1;
+typedef PEMOUTPUTASN1 *PPEMOUTPUTASN1;
+
+
+
+RTDECL(size_t) RTCrPemWriteBlob(PFNRTSTROUTPUT pfnOutput, void *pvUser,
+ const void *pvContent, size_t cbContent, const char *pszMarker)
+{
+ /*
+ * -----BEGIN XXXXX-----
+ */
+ size_t cchRet = pfnOutput(pvUser, RT_STR_TUPLE("-----BEGIN "));
+ size_t const cchMarker = strlen(pszMarker);
+ cchRet += pfnOutput(pvUser, pszMarker, cchMarker);
+ cchRet += pfnOutput(pvUser, RT_STR_TUPLE("-----\n"));
+
+ /*
+ * base64 - in reasonably sized stack blocks.
+ * An encoded line is 64 characters long plus a newline, covering 48 bytes
+ * of binary data. We want about 4KB of output:
+ * 4096 / 65 = 63.015384615384615384615384615385
+ * 64 * 65 + 1 = 4161 (0x1041)
+ */
+ const size_t cbMaxBlock = 64 * 48;
+ while (cbContent > 0)
+ {
+ char szBlock[0x1060];
+ size_t cbBlock = RT_MIN(cbContent, cbMaxBlock);
+ size_t cchBlock = 0;
+ int rc = RTBase64EncodeEx(pvContent, cbBlock, RTBASE64_FLAGS_EOL_LF,
+ szBlock, sizeof(szBlock), &cchBlock);
+ AssertRC(rc);
+ szBlock[cchBlock++] = '\n';
+ szBlock[cchBlock] = '\0';
+
+ cchRet += pfnOutput(pvUser, szBlock, cchBlock);
+
+ pvContent = (uint8_t const *)pvContent + cbBlock;
+ cbContent -= cbBlock;
+ }
+
+ /*
+ * -----END XXXXX-----
+ */
+ cchRet += pfnOutput(pvUser, RT_STR_TUPLE("-----END "));
+ cchRet += pfnOutput(pvUser, pszMarker, cchMarker);
+ cchRet += pfnOutput(pvUser, RT_STR_TUPLE("-----\n"));
+
+ /* termination call */
+ cchRet += pfnOutput(pvUser, NULL, 0);
+
+ return cchRet;
+}
+
+
+RTDECL(ssize_t) RTCrPemWriteBlobToVfsIoStrm(RTVFSIOSTREAM hVfsIos, const void *pvContent, size_t cbContent, const char *pszMarker)
+{
+ VFSIOSTRMOUTBUF Buf;
+ VFSIOSTRMOUTBUF_INIT(&Buf, hVfsIos);
+ size_t cchRet = RTCrPemWriteBlob(RTVfsIoStrmStrOutputCallback, &Buf, pvContent, cbContent, pszMarker);
+ Assert(Buf.offBuf == 0);
+ return RT_SUCCESS(Buf.rc) ? (ssize_t)cchRet : Buf.rc;
+}
+
+
+RTDECL(ssize_t) RTCrPemWriteBlobToVfsFile(RTVFSFILE hVfsFile, const void *pvContent, size_t cbContent, const char *pszMarker)
+{
+ RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hVfsFile);
+ AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_INVALID_HANDLE);
+ ssize_t cchRet = RTCrPemWriteBlobToVfsIoStrm(hVfsIos, pvContent, cbContent, pszMarker);
+ RTVfsIoStrmRelease(hVfsIos);
+ return cchRet;
+}
+
+
+/** @callback_method_impl{FNRTASN1ENCODEWRITER} */
+static DECLCALLBACK(int) rtCrPemWriteAsn1Callback(const void *pvBuf, size_t cbToWrite, void *pvUser, PRTERRINFO pErrInfo)
+{
+ PPEMOUTPUTASN1 pThis = (PPEMOUTPUTASN1)pvUser;
+ AssertCompile((sizeof(pThis->abBlock) % 48) == 0);
+
+ while (cbToWrite > 0)
+ {
+ size_t offDst = pThis->cbPending;
+ AssertStmt(offDst <= sizeof(pThis->abBlock), offDst = sizeof(pThis->abBlock));
+ size_t cbDst = sizeof(pThis->abBlock) - offDst;
+ if (cbToWrite < cbDst)
+ {
+ /* Buffer not full: Append and return. */
+ memcpy(&pThis->abBlock[offDst], pvBuf, cbToWrite);
+ pThis->cbPending = offDst + cbToWrite;
+ break;
+ }
+
+ /* Fill the buffer and flush it: */
+ memcpy(&pThis->abBlock[offDst], pvBuf, cbDst);
+ Assert(offDst + cbDst == sizeof(pThis->abBlock));
+
+ size_t cchBlock = 0;
+ int rc = RTBase64EncodeEx(pThis->abBlock, sizeof(pThis->abBlock), RTBASE64_FLAGS_EOL_LF,
+ pThis->szBlock, sizeof(pThis->szBlock), &cchBlock);
+ AssertRC(rc);
+ pThis->szBlock[cchBlock++] = '\n';
+ pThis->szBlock[cchBlock] = '\0';
+
+ pThis->cchRet += pThis->pfnOutput(pThis->pvUser, pThis->szBlock, cchBlock);
+ pThis->cbPending = 0;
+
+ /* Advance. */
+ pvBuf = (uint8_t const *)pvBuf + cbDst;
+ cbToWrite -= cbDst;
+ }
+
+ RT_NOREF(pErrInfo);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(ssize_t) RTCrPemWriteAsn1(PFNRTSTROUTPUT pfnOutput, void *pvUser, PRTASN1CORE pRoot,
+ uint32_t fFlags, const char *pszMarker, PRTERRINFO pErrInfo)
+{
+ AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+ /*
+ * Prepare the ASN.1 data for DER encoding.
+ */
+ int rc = RTAsn1EncodePrepare(pRoot, RTASN1ENCODE_F_DER, NULL /*pcbEncoded*/, pErrInfo);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * -----BEGIN XXXXX-----
+ */
+ size_t cchRet = pfnOutput(pvUser, RT_STR_TUPLE("-----BEGIN "));
+ size_t const cchMarker = strlen(pszMarker);
+ cchRet += pfnOutput(pvUser, pszMarker, cchMarker);
+ cchRet += pfnOutput(pvUser, RT_STR_TUPLE("-----\n"));
+
+ /*
+ * BASE64
+ */
+ PEMOUTPUTASN1 This;
+ This.pfnOutput = pfnOutput;
+ This.pvUser = pvUser;
+ This.cchRet = 0;
+ This.cbPending = 0;
+ rc = RTAsn1EncodeWrite(pRoot, RTASN1ENCODE_F_DER, rtCrPemWriteAsn1Callback, &This, pErrInfo);
+ AssertRCReturn(rc, rc);
+ cchRet += This.cchRet;
+
+ Assert(This.cbPending <= sizeof(This.abBlock));
+ if (This.cbPending)
+ {
+ size_t cchBlock = 0;
+ rc = RTBase64EncodeEx(This.abBlock, This.cbPending, RTBASE64_FLAGS_EOL_LF,
+ This.szBlock, sizeof(This.szBlock), &cchBlock);
+ AssertRC(rc);
+ This.szBlock[cchBlock++] = '\n';
+ This.szBlock[cchBlock] = '\0';
+
+ cchRet += pfnOutput(pvUser, This.szBlock, cchBlock);
+ }
+
+ /*
+ * -----END XXXXX-----
+ */
+ cchRet += pfnOutput(pvUser, RT_STR_TUPLE("-----END "));
+ cchRet += pfnOutput(pvUser, pszMarker, cchMarker);
+ cchRet += pfnOutput(pvUser, RT_STR_TUPLE("-----\n"));
+
+ /* termination call */
+ cchRet += pfnOutput(pvUser, NULL, 0);
+
+ return cchRet;
+}
+
+
+RTDECL(ssize_t) RTCrPemWriteAsn1ToVfsIoStrm(RTVFSIOSTREAM hVfsIos, PRTASN1CORE pRoot,
+ uint32_t fFlags, const char *pszMarker, PRTERRINFO pErrInfo)
+{
+ VFSIOSTRMOUTBUF Buf;
+ VFSIOSTRMOUTBUF_INIT(&Buf, hVfsIos);
+ ssize_t cchRet = RTCrPemWriteAsn1(RTVfsIoStrmStrOutputCallback, &Buf, pRoot, fFlags, pszMarker, pErrInfo);
+ Assert(Buf.offBuf == 0);
+ return RT_SUCCESS(Buf.rc) ? (ssize_t)cchRet : Buf.rc;
+}
+
+
+RTDECL(ssize_t) RTCrPemWriteAsn1ToVfsFile(RTVFSFILE hVfsFile, PRTASN1CORE pRoot,
+ uint32_t fFlags, const char *pszMarker, PRTERRINFO pErrInfo)
+{
+ RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hVfsFile);
+ AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_INVALID_HANDLE);
+ ssize_t cchRet = RTCrPemWriteAsn1ToVfsIoStrm(hVfsIos, pRoot, fFlags, pszMarker, pErrInfo);
+ RTVfsIoStrmRelease(hVfsIos);
+ return cchRet;
+}
diff --git a/src/VBox/Runtime/common/crypto/pkcs7-asn1-decoder.cpp b/src/VBox/Runtime/common/crypto/pkcs7-asn1-decoder.cpp
new file mode 100644
index 00000000..93690fe8
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkcs7-asn1-decoder.cpp
@@ -0,0 +1,174 @@
+/* $Id: pkcs7-asn1-decoder.cpp $ */
+/** @file
+ * IPRT - Crypto - PKCS \#7, Decoder for ASN.1.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pkcs7.h>
+
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/crypto/spc.h>
+#include <iprt/crypto/tsp.h>
+
+#include "pkcs7-internal.h"
+
+
+/*
+ * PKCS #7 ContentInfo
+ */
+typedef enum RTCRPKCS7CONTENTINFOCHOICE
+{
+ RTCRPKCS7CONTENTINFOCHOICE_INVALID = 0,
+ RTCRPKCS7CONTENTINFOCHOICE_UNKNOWN,
+ RTCRPKCS7CONTENTINFOCHOICE_SIGNED_DATA,
+ RTCRPKCS7CONTENTINFOCHOICE_SPC_INDIRECT_DATA_CONTENT,
+ RTCRPKCS7CONTENTINFOCHOICE_TSP_TST_INFO,
+ RTCRPKCS7CONTENTINFOCHOICE_END,
+ RTCRPKCS7CONTENTINFOCHOICE_32BIT_HACK = 0x7fffffff
+} RTCRPKCS7CONTENTINFOCHOICE;
+
+static int rtCrPkcs7ContentInfo_DecodeExtra(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTCRPKCS7CONTENTINFO pThis,
+ const char *pszErrorTag)
+{
+ RT_NOREF_PV(fFlags); RT_NOREF_PV(pszErrorTag);
+ pThis->u.pCore = NULL;
+
+ /*
+ * Figure the type.
+ */
+ RTCRPKCS7CONTENTINFOCHOICE enmChoice;
+ size_t cbContent = 0;
+ if (RTAsn1ObjId_CompareWithString(&pThis->ContentType, RTCRPKCS7SIGNEDDATA_OID) == 0)
+ {
+ enmChoice = RTCRPKCS7CONTENTINFOCHOICE_SIGNED_DATA;
+ cbContent = sizeof(*pThis->u.pSignedData);
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pThis->ContentType, RTCRSPCINDIRECTDATACONTENT_OID) == 0)
+ {
+ enmChoice = RTCRPKCS7CONTENTINFOCHOICE_SPC_INDIRECT_DATA_CONTENT;
+ cbContent = sizeof(*pThis->u.pIndirectDataContent);
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pThis->ContentType, RTCRTSPTSTINFO_OID) == 0)
+ {
+ enmChoice = RTCRPKCS7CONTENTINFOCHOICE_TSP_TST_INFO;
+ cbContent = sizeof(*pThis->u.pTstInfo);
+ }
+ else
+ {
+ enmChoice = RTCRPKCS7CONTENTINFOCHOICE_UNKNOWN;
+ cbContent = 0;
+ }
+
+ int rc = VINF_SUCCESS;
+ if (enmChoice != RTCRPKCS7CONTENTINFOCHOICE_UNKNOWN)
+ {
+ /*
+ * Detect CMS octet string format and open the content cursor.
+ *
+ * Current we don't have any octent string content which, they're all
+ * sequences, which make detection so much simpler.
+ */
+ PRTASN1OCTETSTRING pOctetString = &pThis->Content;
+ RTASN1CURSOR ContentCursor;
+ rc = RTAsn1CursorInitSubFromCore(pCursor, &pThis->Content.Asn1Core, &ContentCursor, "Content");
+ if ( RT_SUCCESS(rc)
+ && RTAsn1CursorIsNextEx(&ContentCursor, ASN1_TAG_OCTET_STRING, ASN1_TAGFLAG_PRIMITIVE | ASN1_TAGCLASS_UNIVERSAL))
+ {
+ rc = RTAsn1MemAllocZ(&pThis->Content.EncapsulatedAllocation, (void **)&pThis->Content.pEncapsulated,
+ sizeof(*pOctetString));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->pCmsContent = pOctetString = (PRTASN1OCTETSTRING)pThis->Content.pEncapsulated;
+ rc = RTAsn1OctetString_DecodeAsn1(&ContentCursor, 0, pOctetString, "CmsContent");
+ if (RT_SUCCESS(rc))
+ rc = RTAsn1CursorCheckEnd(&ContentCursor);
+ if (RT_SUCCESS(rc))
+ rc = RTAsn1CursorInitSubFromCore(pCursor, &pOctetString->Asn1Core, &ContentCursor, "CmsContent");
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Allocate memory for the decoded content.
+ */
+ rc = RTAsn1MemAllocZ(&pOctetString->EncapsulatedAllocation, (void **)&pOctetString->pEncapsulated, cbContent);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->u.pCore = pOctetString->pEncapsulated;
+
+ /*
+ * Decode it.
+ */
+ switch (enmChoice)
+ {
+ case RTCRPKCS7CONTENTINFOCHOICE_SIGNED_DATA:
+ rc = RTCrPkcs7SignedData_DecodeAsn1(&ContentCursor, 0, pThis->u.pSignedData, "SignedData");
+ break;
+ case RTCRPKCS7CONTENTINFOCHOICE_SPC_INDIRECT_DATA_CONTENT:
+ rc = RTCrSpcIndirectDataContent_DecodeAsn1(&ContentCursor, 0, pThis->u.pIndirectDataContent,
+ "IndirectDataContent");
+ break;
+ case RTCRPKCS7CONTENTINFOCHOICE_TSP_TST_INFO:
+ rc = RTCrTspTstInfo_DecodeAsn1(&ContentCursor, 0, pThis->u.pTstInfo, "TstInfo");
+ break;
+ default:
+ AssertFailed();
+ rc = VERR_IPE_NOT_REACHED_DEFAULT_CASE;
+ break;
+ }
+ if (RT_SUCCESS(rc))
+ rc = RTAsn1CursorCheckOctStrEnd(&ContentCursor, &pThis->Content);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ RTAsn1MemFree(&pOctetString->EncapsulatedAllocation, pOctetString->pEncapsulated);
+ pOctetString->pEncapsulated = NULL;
+ pThis->u.pCore = NULL;
+ }
+ }
+ }
+ return rc;
+}
+
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-asn1-decoder.h>
+
diff --git a/src/VBox/Runtime/common/crypto/pkcs7-core.cpp b/src/VBox/Runtime/common/crypto/pkcs7-core.cpp
new file mode 100644
index 00000000..55f1e169
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkcs7-core.cpp
@@ -0,0 +1,250 @@
+/* $Id: pkcs7-core.cpp $ */
+/** @file
+ * IPRT - Crypto - PKCS \#7, Core APIs.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pkcs7.h>
+
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/crypto/digest.h>
+#include <iprt/crypto/tsp.h>
+
+#include "pkcs7-internal.h"
+
+/*
+ * PKCS #7 Attributes
+ */
+
+RTDECL(int) RTCrPkcs7Attributes_HashAttributes(PRTCRPKCS7ATTRIBUTES pAttributes, RTCRDIGEST hDigest, PRTERRINFO pErrInfo)
+{
+ /* ASSUMES that the attributes are encoded according to DER. */
+ uint8_t const *pbData;
+ uint32_t cbData;
+ void *pvFree = NULL;
+ int rc = RTAsn1EncodeQueryRawBits(RTCrPkcs7Attributes_GetAsn1Core(pAttributes),
+ &pbData, &cbData, &pvFree, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ uint8_t bSetOfTag = ASN1_TAG_SET | ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_CONSTRUCTED;
+ rc = RTCrDigestUpdate(hDigest, &bSetOfTag, sizeof(bSetOfTag)); /* Replace the implict tag with a SET-OF tag. */
+ if (RT_SUCCESS(rc))
+ rc = RTCrDigestUpdate(hDigest, pbData + sizeof(bSetOfTag), cbData - sizeof(bSetOfTag)); /* Skip the implicit tag. */
+ if (RT_SUCCESS(rc))
+ rc = RTCrDigestFinal(hDigest, NULL, 0);
+ else
+ RTErrInfoSet(pErrInfo, rc, "RTCrDigestUpdate failed");
+ RTMemTmpFree(pvFree);
+ }
+ return rc;
+}
+
+
+/*
+ * PKCS #7 SignerInfo
+ */
+
+RTDECL(PCRTASN1TIME) RTCrPkcs7SignerInfo_GetSigningTime(PCRTCRPKCS7SIGNERINFO pThis, PCRTCRPKCS7SIGNERINFO *ppSignerInfo)
+{
+ /*
+ * Check the immediate level, unless we're continuing a previous search.
+ * Note! We ASSUME a single signing time attribute, which simplifies the interface.
+ */
+ uint32_t cAttrsLeft;
+ PRTCRPKCS7ATTRIBUTE const *ppAttr;
+ if (!ppSignerInfo || *ppSignerInfo == NULL)
+ {
+ cAttrsLeft = pThis->AuthenticatedAttributes.cItems;
+ ppAttr = pThis->AuthenticatedAttributes.papItems;
+ while (cAttrsLeft-- > 0)
+ {
+ PCRTCRPKCS7ATTRIBUTE pAttr = *ppAttr;
+ if ( pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_SIGNING_TIME
+ && pAttr->uValues.pSigningTime->cItems > 0)
+ {
+ if (ppSignerInfo)
+ *ppSignerInfo = pThis;
+ return pAttr->uValues.pSigningTime->papItems[0];
+ }
+ ppAttr++;
+ }
+ }
+ else if (*ppSignerInfo == pThis)
+ *ppSignerInfo = NULL;
+
+ /*
+ * Check counter signatures.
+ */
+ cAttrsLeft = pThis->UnauthenticatedAttributes.cItems;
+ ppAttr = pThis->UnauthenticatedAttributes.papItems;
+ while (cAttrsLeft-- > 0)
+ {
+ PCRTCRPKCS7ATTRIBUTE pAttr = *ppAttr;
+ if (pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES)
+ {
+ uint32_t cSignatures = pAttr->uValues.pCounterSignatures->cItems;
+ PRTCRPKCS7SIGNERINFO *ppSignature = pAttr->uValues.pCounterSignatures->papItems;
+
+ /* Skip past the previous counter signature. */
+ if (ppSignerInfo && *ppSignerInfo != NULL)
+ while (cSignatures > 0)
+ {
+ cSignatures--;
+ if (*ppSignature == *ppSignerInfo)
+ {
+ *ppSignerInfo = NULL;
+ ppSignature++;
+ break;
+ }
+ ppSignature++;
+ }
+
+ /* Search the counter signatures (if any remaining). */
+ while (cSignatures-- > 0)
+ {
+ PCRTCRPKCS7SIGNERINFO pSignature = *ppSignature;
+ uint32_t cCounterAttrsLeft = pSignature->AuthenticatedAttributes.cItems;
+ PRTCRPKCS7ATTRIBUTE const *ppCounterAttr = pSignature->AuthenticatedAttributes.papItems;
+ while (cCounterAttrsLeft-- > 0)
+ {
+ PCRTCRPKCS7ATTRIBUTE pCounterAttr = *ppCounterAttr;
+ if ( pCounterAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_SIGNING_TIME
+ && pCounterAttr->uValues.pSigningTime->cItems > 0)
+ {
+ if (ppSignerInfo)
+ *ppSignerInfo = pSignature;
+ return pCounterAttr->uValues.pSigningTime->papItems[0];
+ }
+ ppCounterAttr++;
+ }
+ ppSignature++;
+ }
+ }
+ ppAttr++;
+ }
+
+ /*
+ * No signing timestamp found.
+ */
+ if (ppSignerInfo)
+ *ppSignerInfo = NULL;
+
+ return NULL;
+}
+
+
+RTDECL(PCRTASN1TIME) RTCrPkcs7SignerInfo_GetMsTimestamp(PCRTCRPKCS7SIGNERINFO pThis, PCRTCRPKCS7CONTENTINFO *ppContentInfoRet)
+{
+ /*
+ * Assume there is only one, so no need to enumerate anything here.
+ */
+ uint32_t cAttrsLeft = pThis->UnauthenticatedAttributes.cItems;
+ PRTCRPKCS7ATTRIBUTE const *ppAttr = pThis->UnauthenticatedAttributes.papItems;
+ while (cAttrsLeft-- > 0)
+ {
+ PCRTCRPKCS7ATTRIBUTE pAttr = *ppAttr;
+ if (pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_TIMESTAMP)
+ {
+ uint32_t cLeft = pAttr->uValues.pContentInfos->cItems;
+ PRTCRPKCS7CONTENTINFO const *ppContentInfo = pAttr->uValues.pContentInfos->papItems;
+ while (cLeft-- > 0)
+ {
+ PCRTCRPKCS7CONTENTINFO pContentInfo = *ppContentInfo;
+ if (RTAsn1ObjId_CompareWithString(&pContentInfo->ContentType, RTCRPKCS7SIGNEDDATA_OID) == 0)
+ {
+ if (RTAsn1ObjId_CompareWithString(&pContentInfo->u.pSignedData->ContentInfo.ContentType,
+ RTCRTSPTSTINFO_OID) == 0)
+ {
+ if (ppContentInfoRet)
+ *ppContentInfoRet = pContentInfo;
+ return &pContentInfo->u.pSignedData->ContentInfo.u.pTstInfo->GenTime;
+ }
+ }
+
+ pContentInfo++;
+ }
+ }
+ ppAttr++;
+ }
+
+ /*
+ * No signature was found.
+ */
+ if (ppContentInfoRet)
+ *ppContentInfoRet = NULL;
+
+ return NULL;
+}
+
+
+/*
+ * PKCS #7 ContentInfo.
+ */
+
+RTDECL(bool) RTCrPkcs7ContentInfo_IsSignedData(PCRTCRPKCS7CONTENTINFO pThis)
+{
+ return RTAsn1ObjId_CompareWithString(&pThis->ContentType, RTCRPKCS7SIGNEDDATA_OID) == 0;
+}
+
+
+/*
+ * Set of some kind of certificate supported by PKCS #7 or CMS.
+ */
+
+RTDECL(PCRTCRX509CERTIFICATE)
+RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(PCRTCRPKCS7SETOFCERTS pCertificates,
+ PCRTCRX509NAME pIssuer, PCRTASN1INTEGER pSerialNumber)
+{
+ for (uint32_t i = 0; i < pCertificates->cItems; i++)
+ {
+ PCRTCRPKCS7CERT pCert = pCertificates->papItems[i];
+ if ( pCert->enmChoice == RTCRPKCS7CERTCHOICE_X509
+ && RTCrX509Certificate_MatchIssuerAndSerialNumber(pCert->u.pX509Cert, pIssuer, pSerialNumber))
+ return pCert->u.pX509Cert;
+ }
+ return NULL;
+}
+
+
+/*
+ * Generate the standard core code.
+ */
+#include <iprt/asn1-generator-core.h>
+
diff --git a/src/VBox/Runtime/common/crypto/pkcs7-file.cpp b/src/VBox/Runtime/common/crypto/pkcs7-file.cpp
new file mode 100644
index 00000000..fbb25f01
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkcs7-file.cpp
@@ -0,0 +1,119 @@
+/* $Id: pkcs7-file.cpp $ */
+/** @file
+ * IPRT - Crypto - PKCS\#7/CMS, File related APIs.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pkcs7.h>
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/crypto/pem.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static RTCRPEMMARKERWORD const g_aWords_Cms[] = { { RT_STR_TUPLE("CMS") } };
+static RTCRPEMMARKERWORD const g_aWords_Pkcs7[] = { { RT_STR_TUPLE("PKCS7") } };
+/** X509 Certificate markers. */
+RT_DECL_DATA_CONST(RTCRPEMMARKER const) g_aRTCrPkcs7Markers[] =
+{
+ { g_aWords_Cms, RT_ELEMENTS(g_aWords_Cms) },
+ { g_aWords_Pkcs7, RT_ELEMENTS(g_aWords_Pkcs7) }
+};
+/** Number of entries in g_aRTCrPkcs7Markers. */
+RT_DECL_DATA_CONST(uint32_t const) g_cRTCrPkcs7Markers = RT_ELEMENTS(g_aRTCrPkcs7Markers);
+
+
+/** @name Flags for RTCrPkcs7ContentInfo_ReadFromBuffer
+ * @{ */
+/** Only allow PEM certificates, not binary ones.
+ * @sa RTCRPEMREADFILE_F_ONLY_PEM */
+#define RTCRPKCS7_READ_F_PEM_ONLY RT_BIT(1)
+/** @} */
+
+
+RTDECL(int) RTCrPkcs7_ReadFromBuffer(PRTCRPKCS7CONTENTINFO pContentInfo, const void *pvBuf, size_t cbBuf,
+ uint32_t fFlags, PCRTASN1ALLOCATORVTABLE pAllocator,
+ bool *pfCmsLabeled, PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ if (pfCmsLabeled)
+ *pfCmsLabeled = false;
+ AssertReturn(!(fFlags & ~RTCRX509CERT_READ_F_PEM_ONLY), VERR_INVALID_FLAGS);
+
+ PCRTCRPEMSECTION pSectionHead;
+ int rc = RTCrPemParseContent(pvBuf, cbBuf,
+ fFlags & RTCRX509CERT_READ_F_PEM_ONLY ? RTCRPEMREADFILE_F_ONLY_PEM : 0,
+ g_aRTCrPkcs7Markers, g_cRTCrPkcs7Markers,
+ &pSectionHead, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ if (pSectionHead)
+ {
+ if (pfCmsLabeled)
+ *pfCmsLabeled = pSectionHead->pMarker == &g_aRTCrPkcs7Markers[0];
+
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pSectionHead->pbData, (uint32_t)RT_MIN(pSectionHead->cbData, UINT32_MAX),
+ pErrInfo, pAllocator, RTASN1CURSOR_FLAGS_DER, pszErrorTag);
+
+ RTCRPKCS7CONTENTINFO TmpContentInfo;
+ rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &TmpContentInfo, "CI");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrPkcs7ContentInfo_CheckSanity(&TmpContentInfo, 0, pErrInfo, "CI");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrPkcs7ContentInfo_Clone(pContentInfo, &TmpContentInfo, pAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ if (pSectionHead->pNext || PrimaryCursor.Cursor.cbLeft)
+ rc = VINF_ASN1_MORE_DATA;
+ }
+ }
+ RTCrPkcs7ContentInfo_Delete(&TmpContentInfo);
+ }
+ RTCrPemFreeSections(pSectionHead);
+ }
+ else
+ rc = rc != VINF_SUCCESS ? -rc : VERR_INTERNAL_ERROR_2;
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/crypto/pkcs7-init.cpp b/src/VBox/Runtime/common/crypto/pkcs7-init.cpp
new file mode 100644
index 00000000..f40ad868
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkcs7-init.cpp
@@ -0,0 +1,62 @@
+/* $Id: pkcs7-init.cpp $ */
+/** @file
+ * IPRT - Crypto - PKCS \#7, Initialization API.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pkcs7.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include <iprt/uni.h>
+
+#include "pkcs7-internal.h"
+
+
+static int rtCrPkcs7ContentInfo_CloneExtra(PRTCRPKCS7CONTENTINFO pThis)
+{
+ pThis->u.pCore = pThis->Content.pEncapsulated;
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-init.h>
+
diff --git a/src/VBox/Runtime/common/crypto/pkcs7-internal.h b/src/VBox/Runtime/common/crypto/pkcs7-internal.h
new file mode 100644
index 00000000..78fba4b7
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkcs7-internal.h
@@ -0,0 +1,47 @@
+/* $Id: pkcs7-internal.h $ */
+/** @file
+ * IPRT - Crypto - PKCS \#7, Internal Header.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_common_crypto_pkcs7_internal_h
+#define IPRT_INCLUDED_SRC_common_crypto_pkcs7_internal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define RTASN1TMPL_TEMPLATE_FILE "../common/crypto/pkcs7-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+
+#endif /* !IPRT_INCLUDED_SRC_common_crypto_pkcs7_internal_h */
+
diff --git a/src/VBox/Runtime/common/crypto/pkcs7-sanity.cpp b/src/VBox/Runtime/common/crypto/pkcs7-sanity.cpp
new file mode 100644
index 00000000..dad218e9
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkcs7-sanity.cpp
@@ -0,0 +1,221 @@
+/* $Id: pkcs7-sanity.cpp $ */
+/** @file
+ * IPRT - Crypto - PKCS \#7, Sanity Checkers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pkcs7.h>
+
+#include <iprt/err.h>
+#include <iprt/string.h>
+
+//#include <iprt/stream.h>
+
+#include "pkcs7-internal.h"
+
+
+static int rtCrPkcs7SignedData_CheckSanityExtra(PCRTCRPKCS7SIGNEDDATA pSignedData, uint32_t fFlags,
+ PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ bool const fAuthenticode = RT_BOOL(fFlags & RTCRPKCS7SIGNEDDATA_SANITY_F_AUTHENTICODE);
+ RT_NOREF_PV(fFlags);
+
+ //RTAsn1Dump(&pSignedData->SeqCore.Asn1Core, 0, 0, RTAsn1DumpStrmPrintfV, g_pStdOut);
+
+ if ( RTAsn1Integer_UnsignedCompareWithU32(&pSignedData->Version, RTCRPKCS7SIGNEDDATA_V1) != 0
+ && RTAsn1Integer_UnsignedCompareWithU32(&pSignedData->Version, RTCRPKCS7SIGNEDDATA_V3) != 0
+ && RTAsn1Integer_UnsignedCompareWithU32(&pSignedData->Version, RTCRPKCS7SIGNEDDATA_V4) != 0
+ && RTAsn1Integer_UnsignedCompareWithU32(&pSignedData->Version, RTCRPKCS7SIGNEDDATA_V5) != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_SIGNED_DATA_VERSION, "SignedData version is %llu, expected %u",
+ pSignedData->Version.uValue.u, RTCRPKCS7SIGNEDDATA_V1);
+
+ /*
+ * DigestAlgorithms.
+ */
+ if (pSignedData->DigestAlgorithms.cItems == 0) /** @todo this might be too strict */
+ return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_SIGNED_DATA_NO_DIGEST_ALGOS, "SignedData.DigestAlgorithms is empty");
+ if (pSignedData->DigestAlgorithms.cItems != 1 && fAuthenticode)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_SPC_NOT_EXACTLY_ONE_DIGEST_ALGO,
+ "%s: SignedData.DigestAlgorithms has more than one algorithm (%u)",
+ pszErrorTag, pSignedData->DigestAlgorithms.cItems);
+
+ if (fFlags & RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH)
+ for (uint32_t i = 0; i < pSignedData->DigestAlgorithms.cItems; i++)
+ {
+ if ( RTCrX509AlgorithmIdentifier_GetDigestType(pSignedData->DigestAlgorithms.papItems[i],
+ true /*fPureDigestsOnly*/)
+ == RTDIGESTTYPE_INVALID)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_UNKNOWN_DIGEST_ALGORITHM,
+ "%s: SignedData.DigestAlgorithms[%i] is not known: %s",
+ pszErrorTag, i, pSignedData->DigestAlgorithms.papItems[i]->Algorithm.szObjId);
+ if ( pSignedData->DigestAlgorithms.papItems[i]->Parameters.enmType != RTASN1TYPE_NULL
+ && pSignedData->DigestAlgorithms.papItems[i]->Parameters.enmType != RTASN1TYPE_NOT_PRESENT)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_PARAMS_NOT_IMPL,
+ "%s: SignedData.DigestAlgorithms[%i] has parameters: tag=%u",
+ pszErrorTag, i, pSignedData->DigestAlgorithms.papItems[i]->Parameters.u.Core.uTag);
+ }
+
+ /*
+ * Certificates.
+ */
+ if ( (fFlags & RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT)
+ && pSignedData->Certificates.cItems == 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_NO_CERTIFICATES,
+ "%s: SignedData.Certifcates is empty, expected at least one certificate", pszErrorTag);
+
+ /*
+ * Crls.
+ */
+ if (fAuthenticode && RTAsn1Core_IsPresent(&pSignedData->Crls))
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_EXPECTED_NO_CRLS,
+ "%s: SignedData.Crls is not empty as expected for authenticode.", pszErrorTag);
+ /** @todo check Crls when they become important. */
+
+ /*
+ * SignerInfos.
+ */
+ if (pSignedData->SignerInfos.cItems == 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_NO_SIGNER_INFOS, "%s: SignedData.SignerInfos is empty?", pszErrorTag);
+ if (fAuthenticode && pSignedData->SignerInfos.cItems != 1)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_EXPECTED_ONE_SIGNER_INFO,
+ "%s: SignedData.SignerInfos should have one entry for authenticode: %u",
+ pszErrorTag, pSignedData->SignerInfos.cItems);
+
+ for (uint32_t i = 0; i < pSignedData->SignerInfos.cItems; i++)
+ {
+ PCRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[i];
+
+ if (RTAsn1Integer_UnsignedCompareWithU32(&pSignerInfo->Version, RTCRPKCS7SIGNERINFO_V1) != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_SIGNER_INFO_VERSION,
+ "%s: SignedData.SignerInfos[%u] version is %llu, expected %u",
+ pszErrorTag, i, pSignerInfo->Version.uValue.u, RTCRPKCS7SIGNERINFO_V1);
+
+ /* IssuerAndSerialNumber. */
+ int rc = RTCrX509Name_CheckSanity(&pSignerInfo->IssuerAndSerialNumber.Name, 0, pErrInfo,
+ "SignedData.SignerInfos[#].IssuerAndSerialNumber.Name");
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (pSignerInfo->IssuerAndSerialNumber.SerialNumber.Asn1Core.cb == 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_SIGNER_INFO_NO_ISSUER_SERIAL_NO,
+ "%s: SignedData.SignerInfos[%u].IssuerAndSerialNumber.SerialNumber is missing (zero length)",
+ pszErrorTag, i);
+
+ PCRTCRX509CERTIFICATE pCert;
+ pCert = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(&pSignedData->Certificates,
+ &pSignerInfo->IssuerAndSerialNumber.Name,
+ &pSignerInfo->IssuerAndSerialNumber.SerialNumber);
+ if (!pCert && (fFlags & RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT))
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_SIGNER_CERT_NOT_SHIPPED,
+ "%s: SignedData.SignerInfos[%u].IssuerAndSerialNumber not found in T0.Certificates",
+ pszErrorTag, i);
+
+ /* DigestAlgorithm */
+ uint32_t j = 0;
+ while ( j < pSignedData->DigestAlgorithms.cItems
+ && RTCrX509AlgorithmIdentifier_Compare(pSignedData->DigestAlgorithms.papItems[j],
+ &pSignerInfo->DigestAlgorithm) != 0)
+ j++;
+ if (j >= pSignedData->DigestAlgorithms.cItems)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_ALGO_NOT_FOUND_IN_LIST,
+ "%s: SignedData.SignerInfos[%u].DigestAlgorithm (%s) not found in SignedData.DigestAlgorithms",
+ pszErrorTag, i, pSignerInfo->DigestAlgorithm.Algorithm.szObjId);
+
+ /* Digest encryption algorithm. */
+#if 0 /** @todo Unimportant: Seen timestamp signatures specifying pkcs1-Sha256WithRsaEncryption in SignerInfo and just RSA in the certificate. Figure out how to compare the two. */
+ if ( pCert
+ && RTCrX509AlgorithmIdentifier_Compare(&pSignerInfo->DigestEncryptionAlgorithm,
+ &pCert->TbsCertificate.SubjectPublicKeyInfo.Algorithm) != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_SIGNER_INFO_DIGEST_ENCRYPT_MISMATCH,
+ "SignedData.SignerInfos[%u].DigestEncryptionAlgorithm (%s) mismatch with certificate (%s)",
+ i, pSignerInfo->DigestEncryptionAlgorithm.Algorithm.szObjId,
+ pCert->TbsCertificate.SubjectPublicKeyInfo.Algorithm.Algorithm.szObjId);
+#endif
+
+ /* Authenticated attributes we know. */
+ if (RTCrPkcs7Attributes_IsPresent(&pSignerInfo->AuthenticatedAttributes))
+ {
+ bool fFoundContentInfo = false;
+ bool fFoundMessageDigest = false;
+ for (j = 0; j < pSignerInfo->AuthenticatedAttributes.cItems; j++)
+ {
+ PCRTCRPKCS7ATTRIBUTE pAttrib = pSignerInfo->AuthenticatedAttributes.papItems[j];
+ if (RTAsn1ObjId_CompareWithString(&pAttrib->Type, RTCR_PKCS9_ID_CONTENT_TYPE_OID) == 0)
+ {
+ if (fFoundContentInfo)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_MISSING_CONTENT_TYPE_ATTRIB,
+ "%s: Multiple authenticated content-type attributes.", pszErrorTag);
+ fFoundContentInfo = true;
+ AssertReturn(pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_OBJ_IDS, VERR_INTERNAL_ERROR_3);
+ if (pAttrib->uValues.pObjIds->cItems != 1)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_BAD_CONTENT_TYPE_ATTRIB,
+ "%s: Expected exactly one value for content-type attrib, found: %u",
+ pszErrorTag, pAttrib->uValues.pObjIds->cItems);
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pAttrib->Type, RTCR_PKCS9_ID_MESSAGE_DIGEST_OID) == 0)
+ {
+ if (fFoundMessageDigest)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_MISSING_MESSAGE_DIGEST_ATTRIB,
+ "%s: Multiple authenticated message-digest attributes.", pszErrorTag);
+ fFoundMessageDigest = true;
+ AssertReturn(pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_OCTET_STRINGS, VERR_INTERNAL_ERROR_3);
+ if (pAttrib->uValues.pOctetStrings->cItems != 1)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_BAD_CONTENT_TYPE_ATTRIB,
+ "%s: Expected exactly one value for message-digest attrib, found: %u",
+ pszErrorTag, pAttrib->uValues.pOctetStrings->cItems);
+ }
+ }
+
+ if (!fFoundContentInfo)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_MISSING_CONTENT_TYPE_ATTRIB,
+ "%s: Missing authenticated content-type attribute.", pszErrorTag);
+ if (!fFoundMessageDigest)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_MISSING_MESSAGE_DIGEST_ATTRIB,
+ "%s: Missing authenticated message-digest attribute.", pszErrorTag);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-sanity.h>
+
diff --git a/src/VBox/Runtime/common/crypto/pkcs7-sign.cpp b/src/VBox/Runtime/common/crypto/pkcs7-sign.cpp
new file mode 100644
index 00000000..c1600d60
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkcs7-sign.cpp
@@ -0,0 +1,592 @@
+/* $Id: pkcs7-sign.cpp $ */
+/** @file
+ * IPRT - Crypto - PKCS \#7, Signing
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pkcs7.h>
+
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/crypto/digest.h>
+#include <iprt/crypto/key.h>
+#include <iprt/crypto/pkix.h>
+#include <iprt/crypto/store.h>
+#include <iprt/crypto/x509.h>
+
+#ifdef IPRT_WITH_OPENSSL
+# include "internal/iprt-openssl.h"
+# include "internal/openssl-pre.h"
+# include <openssl/asn1t.h>
+# include <openssl/pkcs7.h>
+# include <openssl/cms.h>
+# include <openssl/x509.h>
+# include <openssl/err.h>
+# include "internal/openssl-post.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * PKCS\#7 / CMS signing operation instance.
+ */
+typedef struct RTCRPKCS7SIGNINGJOBINT
+{
+ /** Magic value (RTCRPKCS7SIGNINGJOBINT). */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** RTCRPKCS7SIGN_F_XXX. */
+ uint64_t fFlags;
+ /** Set if finalized. */
+ bool fFinallized;
+
+ //....
+} RTCRPKCS7SIGNINGJOBINT;
+
+/** Magic value for RTCRPKCS7SIGNINGJOBINT (Jonathan Lethem). */
+#define RTCRPKCS7SIGNINGJOBINT_MAGIC UINT32_C(0x19640219)
+
+/** Handle to PKCS\#7/CMS signing operation. */
+typedef struct RTCRPKCS7SIGNINGJOBINT *RTCRPKCS7SIGNINGJOB;
+/** Pointer to a PKCS\#7/CMS signing operation handle. */
+typedef RTCRPKCS7SIGNINGJOB *PRTCRPKCS7SIGNINGJOB;
+
+//// CMS_sign
+//RTDECL(int) RTCrPkcs7Sign(PRTCRPKCS7SIGNINGJOB *phJob, uint64_t fFlags, PCRTCRX509CERTIFICATE pSigner, RTCRKEY hPrivateKey,
+// RTCRSTORE hAdditionalCerts,
+//
+
+#ifdef IPRT_WITH_OPENSSL
+
+static int rtCrPkcs7SimpleSignSignedDataDoV1TweakContent(PKCS7 *pOsslPkcs7, const char *pszContentId,
+ const void *pvData, size_t cbData,
+ PRTERRINFO pErrInfo)
+{
+ AssertReturn(pszContentId, RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_MISSING_CONTENT_TYPE_ATTRIB,
+ "RTCRPKCS7SIGN_SD_F_NO_DATA_ENCAP requires content type in additional attribs"));
+
+ /*
+ * Create a new inner PKCS#7 content container, forcing it to the 'other' type.
+ */
+ PKCS7 *pOsslInnerContent = PKCS7_new();
+ if (!pOsslInnerContent)
+ return RTErrInfoSet(pErrInfo, VERR_NO_MEMORY, "PKCS7_new failed");
+
+ /* Set the type. */
+ int rc;
+ pOsslInnerContent->type = OBJ_txt2obj(pszContentId, 1);
+ if (pOsslInnerContent->type)
+ {
+ /* Create a dynamic ASN1 type which we set to a sequence. */
+ ASN1_TYPE *pOsslOther = pOsslInnerContent->d.other = ASN1_TYPE_new();
+ if (pOsslOther)
+ {
+ pOsslOther->type = V_ASN1_SEQUENCE;
+
+ /* Create a string and put the data in it. */
+ ASN1_STRING *pOsslStr = pOsslOther->value.sequence = ASN1_STRING_new();
+ if (pOsslStr)
+ {
+ rc = ASN1_STRING_set(pOsslStr, pvData, (int)cbData); /* copies the buffer content */
+ if (rc > 0)
+ {
+ /*
+ * Set the content in the PKCS#7 signed data we're constructing.
+ * This consumes pOsslInnerContent on success.
+ */
+ rc = PKCS7_set_content(pOsslPkcs7, pOsslInnerContent);
+ if (rc > 0)
+ return VINF_SUCCESS;
+
+ /* failed */
+ rc = RTErrInfoSet(pErrInfo, VERR_NO_MEMORY, "PKCS7_set_content");
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "ASN1_STRING_set(,,%#x)", cbData);
+ }
+ else
+ rc = RTErrInfoSet(pErrInfo, VERR_NO_MEMORY, "ASN1_STRING_new");
+ }
+ else
+ rc = RTErrInfoSet(pErrInfo, VERR_NO_MEMORY, "ASN1_TYPE_new");
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "OBJ_txt2obj(%s, 1) failed", pszContentId);
+ PKCS7_free(pOsslInnerContent);
+ return rc;
+}
+
+
+static int rtCrPkcs7SimpleSignSignedDataDoV1TweakedFinal(PKCS7 *pOsslPkcs7, const char *pszContentId,
+ const void *pvData, size_t cbData, PRTERRINFO pErrInfo)
+{
+ AssertReturn(pszContentId, RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_MISSING_CONTENT_TYPE_ATTRIB,
+ "RTCRPKCS7SIGN_SD_F_NO_DATA_ENCAP requires content type in additional attribs"));
+
+ /*
+ * Prepare a BIO of what should be hashed with all the hashing filters attached.
+ */
+ BIO *pOsslBio = PKCS7_dataInit(pOsslPkcs7, NULL);
+ if (!pOsslBio)
+ return RTErrInfoSet(pErrInfo, VERR_CR_CIPHER_OSSL_ENCRYPT_FINAL_FAILED, "PKCS7_dataInit failed");
+
+ /*
+ * Now write the data.
+ *
+ * We must skip the outer wrapper here (see RTCrPkcs7VerifySignedData). This
+ * is probably a bit presumptive about what we're working on, so add an extra
+ * flag for this later.
+ */
+ uint8_t const *pbToWrite = (uint8_t const *)pvData;
+ size_t cbToWrite = cbData;
+
+ /** @todo add extra flag for this? */
+ RTASN1CURSORPRIMARY SkipCursor;
+ RTAsn1CursorInitPrimary(&SkipCursor, pvData, (uint32_t)cbData,
+ pErrInfo,&g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "skip");
+ RTASN1CORE SkipAsn1Core = { 0 };
+ int rc = RTAsn1CursorReadHdr(&SkipCursor.Cursor, &SkipAsn1Core, "skip-core");
+ if (RT_SUCCESS(rc))
+ {
+ pbToWrite += SkipAsn1Core.cbHdr;
+ cbToWrite -= SkipAsn1Core.cbHdr;
+
+ rc = BIO_write(pOsslBio, pbToWrite, (int)cbToWrite);
+ if (rc == (ssize_t)cbToWrite)
+ {
+ BIO_flush(pOsslBio); /** @todo error check this */
+ if (true)
+ {
+ /*
+ * Finalize the job - produce the signer info signatures and stuff.
+ */
+ rc = PKCS7_dataFinal(pOsslPkcs7, pOsslBio);
+ if (rc > 0)
+ {
+ /*
+ * Now tweak the content so we get the desired content type and
+ * no extra wrappers and stuff.
+ */
+ rc = rtCrPkcs7SimpleSignSignedDataDoV1TweakContent(pOsslPkcs7, pszContentId, pvData, cbData, pErrInfo);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_CR_CIPHER_OSSL_ENCRYPT_FINAL_FAILED, "PKCS7_dataFinal failed: %d", rc);
+ }
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_CR_CIPHER_OSSL_ENCRYPT_FINAL_FAILED,
+ "%zu byte data write failed: %d", cbToWrite, rc);
+ }
+ BIO_free_all(pOsslBio);
+ return rc;
+}
+
+
+static int rtCrPkcs7SimpleSignSignedDataDoV1AttribConversion(PKCS7_SIGNER_INFO *pSignerInfo,
+ PCRTCRPKCS7ATTRIBUTES pAdditionalAuthenticatedAttribs,
+ const char **ppszContentId, PRTERRINFO pErrInfo)
+{
+ int rc = VINF_SUCCESS;
+ *ppszContentId = NULL;
+
+ if (pAdditionalAuthenticatedAttribs)
+ {
+
+ /*
+ * Convert each attribute.
+ */
+ STACK_OF(X509_ATTRIBUTE) *pOsslAttributes = sk_X509_ATTRIBUTE_new_null();
+ for (uint32_t i = 0; i < pAdditionalAuthenticatedAttribs->cItems && RT_SUCCESS(rc); i++)
+ {
+ PCRTCRPKCS7ATTRIBUTE pAttrib = pAdditionalAuthenticatedAttribs->papItems[i];
+
+ /* Look out for content type, as we will probably need that for
+ RTCRPKCS7SIGN_SD_F_NO_DATA_ENCAP later. */
+ if ( pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_OBJ_IDS
+ && RTAsn1ObjId_CompareWithString(&pAttrib->Type, RTCR_PKCS9_ID_CONTENT_TYPE_OID) == 0)
+ {
+ AssertBreakStmt(!*ppszContentId, rc = VERR_CR_PKCS7_BAD_CONTENT_TYPE_ATTRIB);
+ AssertBreakStmt(pAttrib->uValues.pObjIds && pAttrib->uValues.pObjIds->cItems == 1,
+ rc = VERR_CR_PKCS7_BAD_CONTENT_TYPE_ATTRIB);
+ *ppszContentId = pAttrib->uValues.pObjIds->papItems[0]->szObjId;
+ }
+
+ /* The conversion (IPRT encode, OpenSSL decode). */
+ X509_ATTRIBUTE *pOsslAttrib;
+ rc = rtCrOpenSslConvertPkcs7Attribute((void **)&pOsslAttrib, pAttrib, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ if (!sk_X509_ATTRIBUTE_push(pOsslAttributes, pOsslAttrib))
+ rc = RTErrInfoSet(pErrInfo, VERR_NO_MEMORY, "sk_X509_ATTRIBUTE_push failed");
+ }
+ }
+
+ /*
+ * If we've successfully converted all the attributes, make a deep copy
+ * (waste of resource, but whatever) into the signer info we're working on.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ rc = PKCS7_set_signed_attributes(pSignerInfo, pOsslAttributes); /* deep copy */
+ if (rc <= 0)
+ rc = RTErrInfoSet(pErrInfo, VERR_NO_MEMORY, "PKCS7_set_signed_attributes failed");
+ }
+
+ /*
+ * Free the attributes (they were copied). Cannot use X509_ATTRIBUTE_pop_free as
+ * the callback causes Visual C++ to complain about exceptions on the callback.
+ */
+ for (int i = sk_X509_ATTRIBUTE_num(pOsslAttributes) - 1; i >= 0; i--)
+ X509_ATTRIBUTE_free(sk_X509_ATTRIBUTE_value(pOsslAttributes, i));
+ sk_X509_ATTRIBUTE_free(pOsslAttributes);
+ }
+ return rc;
+}
+
+static int rtCrPkcs7SimpleSignSignedDataDoV1(uint32_t fFlags, X509 *pOsslSigner, EVP_PKEY *pEvpPrivateKey,
+ BIO *pOsslData, const EVP_MD *pEvpMd, STACK_OF(X509) *pOsslAdditionalCerts,
+ PCRTCRPKCS7ATTRIBUTES pAdditionalAuthenticatedAttribs,
+ const void *pvData, size_t cbData,
+ BIO **ppOsslResult, PRTERRINFO pErrInfo)
+{
+ /*
+ * Use PKCS7_sign with PKCS7_PARTIAL to start a extended the signing process.
+ */
+ /* Create a ContentInfo we can modify using CMS_sign w/ CMS_PARTIAL. */
+ unsigned int fOsslSign = PKCS7_BINARY | PKCS7_PARTIAL;
+ if (fFlags & RTCRPKCS7SIGN_SD_F_DEATCHED)
+ fOsslSign |= PKCS7_DETACHED;
+ if (fFlags & RTCRPKCS7SIGN_SD_F_NO_SMIME_CAP)
+ fOsslSign |= PKCS7_NOSMIMECAP;
+ int rc = VINF_SUCCESS;
+ PKCS7 *pCms = PKCS7_sign(NULL, NULL, pOsslAdditionalCerts, NULL, fOsslSign);
+ if (pCms != NULL)
+ {
+ /*
+ * Add a signer.
+ */
+ PKCS7_SIGNER_INFO *pSignerInfo = PKCS7_sign_add_signer(pCms, pOsslSigner, pEvpPrivateKey, pEvpMd, fOsslSign);
+ if (pSignerInfo)
+ {
+ /*
+ * Add additional attributes to the signer.
+ */
+ const char *pszContentId = NULL;
+ rc = rtCrPkcs7SimpleSignSignedDataDoV1AttribConversion(pSignerInfo, pAdditionalAuthenticatedAttribs,
+ &pszContentId, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Finalized and actually sign the data.
+ */
+ bool const fTweaked = (fFlags & (RTCRPKCS7SIGN_SD_F_DEATCHED | RTCRPKCS7SIGN_SD_F_NO_DATA_ENCAP))
+ == RTCRPKCS7SIGN_SD_F_NO_DATA_ENCAP;
+ if (fTweaked)
+ rc = rtCrPkcs7SimpleSignSignedDataDoV1TweakedFinal(pCms, pszContentId, pvData, cbData, pErrInfo);
+ else
+ {
+ rc = PKCS7_final(pCms, pOsslData, fOsslSign);
+ if (rc > 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrInfoSet(pErrInfo, VERR_GENERAL_FAILURE, "PKCS7_final");
+ /** @todo maybe we want to use rtCrPkcs7SimpleSignSignedDataDoV1TweakContent
+ * for when the content type isn't 'data'... */
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get the output and copy it into the result buffer.
+ */
+ BIO *pOsslResult = BIO_new(BIO_s_mem());
+ if (pOsslResult)
+ {
+ rc = i2d_PKCS7_bio(pOsslResult, pCms);
+ if (rc > 0)
+ {
+ *ppOsslResult = pOsslResult;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rc = RTErrInfoSet(pErrInfo, VERR_GENERAL_FAILURE, "i2d_PKCS7_bio");
+ BIO_free(pOsslResult);
+ }
+ }
+ else
+ rc = RTErrInfoSet(pErrInfo, VERR_NO_MEMORY, "BIO_new/BIO_s_mem");
+ }
+ }
+ else
+ rc = RTErrInfoSet(pErrInfo, VERR_GENERAL_FAILURE, "PKCS7_sign_add_signer");
+ }
+ PKCS7_free(pCms);
+ }
+ else
+ rc = RTErrInfoSet(pErrInfo, VERR_GENERAL_FAILURE, "PKCS7_sign");
+ return rc;
+}
+
+
+static int rtCrPkcs7SimpleSignSignedDataDoDefault(uint32_t fFlags, X509 *pOsslSigner, EVP_PKEY *pEvpPrivateKey,
+ BIO *pOsslData, const EVP_MD *pEvpMd, STACK_OF(X509) *pOsslAdditionalCerts,
+ PCRTCRPKCS7ATTRIBUTES pAdditionalAuthenticatedAttribs,
+ BIO **ppOsslResult, PRTERRINFO pErrInfo)
+
+{
+ /*
+ * Use CMS_sign with CMS_PARTIAL to start a extended the signing process.
+ */
+ /* Create a ContentInfo we can modify using CMS_sign w/ CMS_PARTIAL. */
+ unsigned int fOsslSign = CMS_BINARY | CMS_PARTIAL;
+ if (fFlags & RTCRPKCS7SIGN_SD_F_DEATCHED)
+ fOsslSign |= CMS_DETACHED;
+ if (fFlags & RTCRPKCS7SIGN_SD_F_NO_SMIME_CAP)
+ fOsslSign |= CMS_NOSMIMECAP;
+ int rc = VINF_SUCCESS;
+ CMS_ContentInfo *pCms = CMS_sign(NULL, NULL, pOsslAdditionalCerts, NULL, fOsslSign);
+ if (pCms != NULL)
+ {
+ /*
+ * Set encapsulated content type if present in the auth attribs.
+ */
+ uint32_t iAuthAttrSkip = UINT32_MAX;
+ for (uint32_t i = 0; i < pAdditionalAuthenticatedAttribs->cItems && RT_SUCCESS(rc); i++)
+ {
+ PCRTCRPKCS7ATTRIBUTE pAttrib = pAdditionalAuthenticatedAttribs->papItems[i];
+ if ( pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_OBJ_IDS
+ && RTAsn1ObjId_CompareWithString(&pAttrib->Type, RTCR_PKCS9_ID_CONTENT_TYPE_OID) == 0)
+ {
+ AssertBreakStmt(pAttrib->uValues.pObjIds && pAttrib->uValues.pObjIds->cItems == 1,
+ rc = VERR_INTERNAL_ERROR_3);
+ PCRTASN1OBJID pObjId = pAttrib->uValues.pObjIds->papItems[0];
+ ASN1_OBJECT *pOsslObjId = OBJ_txt2obj(pObjId->szObjId, 0 /*no_name*/);
+ if (pOsslObjId)
+ {
+ rc = CMS_set1_eContentType(pCms, pOsslObjId);
+ ASN1_OBJECT_free(pOsslObjId);
+ if (rc < 0)
+ rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_GENERIC_ERROR,
+ "CMS_set1_eContentType(%s)", pObjId->szObjId);
+ }
+ else
+ rc = RTErrInfoSet(pErrInfo, VERR_NO_MEMORY, "OBJ_txt2obj");
+
+ iAuthAttrSkip = i;
+ break;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Add a signer.
+ */
+ CMS_SignerInfo *pSignerInfo = CMS_add1_signer(pCms, pOsslSigner, pEvpPrivateKey, pEvpMd, fOsslSign);
+ if (pSignerInfo)
+ {
+ /*
+ * Add additional attributes, skipping the content type if found above.
+ */
+ if (pAdditionalAuthenticatedAttribs)
+ for (uint32_t i = 0; i < pAdditionalAuthenticatedAttribs->cItems && RT_SUCCESS(rc); i++)
+ if (i != iAuthAttrSkip)
+ {
+ PCRTCRPKCS7ATTRIBUTE pAttrib = pAdditionalAuthenticatedAttribs->papItems[i];
+ X509_ATTRIBUTE *pOsslAttrib;
+ rc = rtCrOpenSslConvertPkcs7Attribute((void **)&pOsslAttrib, pAttrib, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = CMS_signed_add1_attr(pSignerInfo, pOsslAttrib);
+ rtCrOpenSslFreeConvertedPkcs7Attribute((void **)pOsslAttrib);
+ if (rc <= 0)
+ rc = RTErrInfoSet(pErrInfo, VERR_NO_MEMORY, "CMS_signed_add1_attr");
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Finalized and actually sign the data.
+ */
+ rc = CMS_final(pCms, pOsslData, NULL /*dcont*/, fOsslSign);
+ if (rc > 0)
+ {
+ /*
+ * Get the output and copy it into the result buffer.
+ */
+ BIO *pOsslResult = BIO_new(BIO_s_mem());
+ if (pOsslResult)
+ {
+ rc = i2d_CMS_bio(pOsslResult, pCms);
+ if (rc > 0)
+ {
+ *ppOsslResult = pOsslResult;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rc = RTErrInfoSet(pErrInfo, VERR_GENERAL_FAILURE, "i2d_CMS_bio");
+ BIO_free(pOsslResult);
+ }
+ }
+ else
+ rc = RTErrInfoSet(pErrInfo, VERR_NO_MEMORY, "BIO_new/BIO_s_mem");
+ }
+ else
+ rc = RTErrInfoSet(pErrInfo, VERR_GENERAL_FAILURE, "CMS_final");
+ }
+ }
+ else
+ rc = RTErrInfoSet(pErrInfo, VERR_GENERAL_FAILURE, "CMS_add1_signer");
+ }
+ CMS_ContentInfo_free(pCms);
+ }
+ else
+ rc = RTErrInfoSet(pErrInfo, VERR_GENERAL_FAILURE, "CMS_sign");
+ return rc;
+}
+
+#endif /* IPRT_WITH_OPENSSL */
+
+
+
+RTDECL(int) RTCrPkcs7SimpleSignSignedData(uint32_t fFlags, PCRTCRX509CERTIFICATE pSigner, RTCRKEY hPrivateKey,
+ void const *pvData, size_t cbData, RTDIGESTTYPE enmDigestType,
+ RTCRSTORE hAdditionalCerts, PCRTCRPKCS7ATTRIBUTES pAdditionalAuthenticatedAttribs,
+ void *pvResult, size_t *pcbResult, PRTERRINFO pErrInfo)
+{
+ size_t const cbResultBuf = *pcbResult;
+ *pcbResult = 0;
+ AssertReturn(!(fFlags & ~RTCRPKCS7SIGN_SD_F_VALID_MASK), VERR_INVALID_FLAGS);
+#ifdef IPRT_WITH_OPENSSL
+ AssertReturn((int)cbData >= 0 && (unsigned)cbData == cbData, VERR_TOO_MUCH_DATA);
+
+ /*
+ * Resolve the digest type.
+ */
+ const EVP_MD *pEvpMd = NULL;
+ if (enmDigestType != RTDIGESTTYPE_UNKNOWN)
+ {
+ pEvpMd = (const EVP_MD *)rtCrOpenSslConvertDigestType(enmDigestType, pErrInfo);
+ AssertReturn(pEvpMd, pErrInfo ? pErrInfo->rc : VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Convert the private key.
+ */
+ EVP_PKEY *pEvpPrivateKey = NULL;
+ int rc = rtCrKeyToOpenSslKey(hPrivateKey, false /*fNeedPublic*/, (void **)&pEvpPrivateKey, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Convert the signing certificate.
+ */
+ X509 *pOsslSigner = NULL;
+ rc = rtCrOpenSslConvertX509Cert((void **)&pOsslSigner, pSigner, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Convert any additional certificates.
+ */
+ STACK_OF(X509) *pOsslAdditionalCerts = NULL;
+ if (hAdditionalCerts != NIL_RTCRSTORE)
+ rc = RTCrStoreConvertToOpenSslCertStack(hAdditionalCerts, 0 /*fFlags*/, (void **)&pOsslAdditionalCerts, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a BIO for the data buffer.
+ */
+ BIO *pOsslData = BIO_new_mem_buf((void *)pvData, (int)cbData);
+ if (pOsslData)
+ {
+ /*
+ * Do the work.
+ */
+ BIO *pOsslResult = NULL;
+ if (!(fFlags & RTCRPKCS7SIGN_SD_F_USE_V1))
+ rc = rtCrPkcs7SimpleSignSignedDataDoDefault(fFlags, pOsslSigner, pEvpPrivateKey, pOsslData, pEvpMd,
+ pOsslAdditionalCerts, pAdditionalAuthenticatedAttribs,
+ &pOsslResult, pErrInfo);
+ else
+ rc = rtCrPkcs7SimpleSignSignedDataDoV1(fFlags, pOsslSigner, pEvpPrivateKey, pOsslData, pEvpMd,
+ pOsslAdditionalCerts, pAdditionalAuthenticatedAttribs,
+ pvData, cbData,
+ &pOsslResult, pErrInfo);
+ BIO_free(pOsslData);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Copy out the result.
+ */
+ BUF_MEM *pBuf = NULL;
+ rc = (int)BIO_get_mem_ptr(pOsslResult, &pBuf);
+ if (rc > 0)
+ {
+ AssertPtr(pBuf);
+ size_t const cbResult = pBuf->length;
+ if ( cbResultBuf >= cbResult
+ && pvResult != NULL)
+ {
+ memcpy(pvResult, pBuf->data, cbResult);
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ *pcbResult = cbResult;
+ }
+ else
+ rc = RTErrInfoSet(pErrInfo, VERR_GENERAL_FAILURE, "BIO_get_mem_ptr");
+ BIO_free(pOsslResult);
+ }
+ }
+ }
+ rtCrOpenSslFreeConvertedX509Cert(pOsslSigner);
+ }
+ EVP_PKEY_free(pEvpPrivateKey);
+ }
+ return rc;
+#else
+ RT_NOREF(fFlags, pSigner, hPrivateKey, pvData, cbData, enmDigestType, hAdditionalCerts, pAdditionalAuthenticatedAttribs,
+ pvResult, pErrInfo, cbResultBuf);
+ *pcbResult = 0;
+ return VERR_NOT_IMPLEMENTED;
+#endif
+}
+
diff --git a/src/VBox/Runtime/common/crypto/pkcs7-template.h b/src/VBox/Runtime/common/crypto/pkcs7-template.h
new file mode 100644
index 00000000..a0e64367
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkcs7-template.h
@@ -0,0 +1,236 @@
+/* $Id: pkcs7-template.h $ */
+/** @file
+ * IPRT - Crypto - PKCS \#7, Core APIs, Code Generator Template.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#define RTASN1TMPL_DECL RTDECL
+
+/*
+ * One PCKS #7 IssuerAndSerialNumber.
+ */
+#define RTASN1TMPL_TYPE RTCRPKCS7ISSUERANDSERIALNUMBER
+#define RTASN1TMPL_EXT_NAME RTCrPkcs7IssuerAndSerialNumber
+#define RTASN1TMPL_INT_NAME rtCrPkcs7IssuerAndSerialNumber
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( Name, RTCRX509NAME, RTCrX509Name);
+RTASN1TMPL_MEMBER( SerialNumber, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * One PCKS #7 Attribute.
+ */
+#define RTASN1TMPL_TYPE RTCRPKCS7ATTRIBUTE
+#define RTASN1TMPL_EXT_NAME RTCrPkcs7Attribute
+#define RTASN1TMPL_INT_NAME rtCrPkcs7Attribute
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( Type, RTASN1OBJID, RTAsn1ObjId);
+RTASN1TMPL_MEMBER_DYN_BEGIN(Type, RTCRPKCS7ATTRIBUTETYPE, enmType, Allocation);
+RTASN1TMPL_MEMBER_DYN( uValues, pOctetStrings, MessageDigest, RTASN1SETOFOCTETSTRINGS, RTAsn1SetOfOctetStrings, Allocation,
+ Type, enmType, RTCRPKCS7ATTRIBUTETYPE_OCTET_STRINGS, RTCR_PKCS9_ID_MESSAGE_DIGEST_OID);
+RTASN1TMPL_MEMBER_DYN( uValues, pObjIds, ContentType, RTASN1SETOFOBJIDS, RTAsn1SetOfObjIds, Allocation,
+ Type, enmType, RTCRPKCS7ATTRIBUTETYPE_OBJ_IDS, RTCR_PKCS9_ID_CONTENT_TYPE_OID);
+RTASN1TMPL_MEMBER_DYN( uValues, pCounterSignatures,CounterSignatures, RTCRPKCS7SIGNERINFOS, RTCrPkcs7SignerInfos, Allocation,
+ Type, enmType, RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES, RTCR_PKCS9_ID_COUNTER_SIGNATURE_OID);
+RTASN1TMPL_MEMBER_DYN( uValues, pSigningTime, SigningTime, RTASN1SETOFTIMES, RTAsn1SetOfTimes, Allocation,
+ Type, enmType, RTCRPKCS7ATTRIBUTETYPE_SIGNING_TIME, RTCR_PKCS9_ID_SIGNING_TIME_OID);
+RTASN1TMPL_MEMBER_DYN( uValues, pContentInfos, MsTimestamp, RTCRPKCS7SETOFCONTENTINFOS, RTCrPkcs7SetOfContentInfos, Allocation,
+ Type, enmType, RTCRPKCS7ATTRIBUTETYPE_MS_TIMESTAMP, RTCR_PKCS9_ID_MS_TIMESTAMP);
+RTASN1TMPL_MEMBER_DYN( uValues, pContentInfos, MsNestedSignature, RTCRPKCS7SETOFCONTENTINFOS, RTCrPkcs7SetOfContentInfos, Allocation,
+ Type, enmType, RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE, RTCR_PKCS9_ID_MS_NESTED_SIGNATURE);
+RTASN1TMPL_MEMBER_DYN( uValues, pObjIdSeqs, MsStatementType, RTASN1SETOFOBJIDSEQS, RTAsn1SetOfObjIdSeqs, Allocation,
+ Type, enmType, RTCRPKCS7ATTRIBUTETYPE_MS_STATEMENT_TYPE, RTCR_PKCS9_ID_MS_STATEMENT_TYPE);
+RTASN1TMPL_MEMBER_DYN( uValues, pOctetStrings, AppleMultiCdPlist, RTASN1SETOFOCTETSTRINGS, RTAsn1SetOfOctetStrings, Allocation,
+ Type, enmType, RTCRPKCS7ATTRIBUTETYPE_APPLE_MULTI_CD_PLIST, RTCR_PKCS9_ID_APPLE_MULTI_CD_PLIST);
+RTASN1TMPL_MEMBER_DYN_DEFAULT( uValues, pCores, RTASN1SETOFCORES, RTAsn1SetOfCores, Allocation,
+ Type, enmType, RTCRPKCS7ATTRIBUTETYPE_UNKNOWN);
+RTASN1TMPL_MEMBER_DYN_END(Type, RTCRPKCS7ATTRIBUTETYPE, enmType, Allocation);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * Set of PCKS #7 Attributes.
+ */
+#define RTASN1TMPL_TYPE RTCRPKCS7ATTRIBUTES
+#define RTASN1TMPL_EXT_NAME RTCrPkcs7Attributes
+#define RTASN1TMPL_INT_NAME rtCrPkcs7Attributes
+RTASN1TMPL_SET_OF(RTCRPKCS7ATTRIBUTE, RTCrPkcs7Attribute);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * One PCKS #7 SignerInfo.
+ */
+#define RTASN1TMPL_TYPE RTCRPKCS7SIGNERINFO
+#define RTASN1TMPL_EXT_NAME RTCrPkcs7SignerInfo
+#define RTASN1TMPL_INT_NAME rtCrPkcs7SignerInfo
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( Version, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( IssuerAndSerialNumber, RTCRPKCS7ISSUERANDSERIALNUMBER, RTCrPkcs7IssuerAndSerialNumber);
+RTASN1TMPL_MEMBER( DigestAlgorithm, RTCRX509ALGORITHMIDENTIFIER, RTCrX509AlgorithmIdentifier);
+RTASN1TMPL_MEMBER_OPT_ITAG( AuthenticatedAttributes, RTCRPKCS7ATTRIBUTES, RTCrPkcs7Attributes, 0);
+RTASN1TMPL_MEMBER( DigestEncryptionAlgorithm, RTCRX509ALGORITHMIDENTIFIER, RTCrX509AlgorithmIdentifier);
+RTASN1TMPL_MEMBER( EncryptedDigest, RTASN1OCTETSTRING, RTAsn1OctetString);
+RTASN1TMPL_MEMBER_OPT_ITAG( UnauthenticatedAttributes, RTCRPKCS7ATTRIBUTES, RTCrPkcs7Attributes, 1);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * Set of PCKS #7 SignerInfos.
+ */
+#define RTASN1TMPL_TYPE RTCRPKCS7SIGNERINFOS
+#define RTASN1TMPL_EXT_NAME RTCrPkcs7SignerInfos
+#define RTASN1TMPL_INT_NAME rtCrPkcs7SignerInfos
+RTASN1TMPL_SET_OF(RTCRPKCS7SIGNERINFO, RTCrPkcs7SignerInfo);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * PCKS #7 SignedData.
+ */
+#define RTASN1TMPL_TYPE RTCRPKCS7SIGNEDDATA
+#define RTASN1TMPL_EXT_NAME RTCrPkcs7SignedData
+#define RTASN1TMPL_INT_NAME rtCrPkcs7SignedData
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( Version, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( DigestAlgorithms, RTCRX509ALGORITHMIDENTIFIERS, RTCrX509AlgorithmIdentifiers);
+RTASN1TMPL_MEMBER( ContentInfo, RTCRPKCS7CONTENTINFO, RTCrPkcs7ContentInfo);
+RTASN1TMPL_MEMBER_OPT_ITAG( Certificates, RTCRPKCS7SETOFCERTS, RTCrPkcs7SetOfCerts, 0);
+RTASN1TMPL_MEMBER_OPT_ITAG( Crls, RTASN1CORE, RTAsn1Core, 1);
+RTASN1TMPL_MEMBER( SignerInfos, RTCRPKCS7SIGNERINFOS, RTCrPkcs7SignerInfos);
+RTASN1TMPL_EXEC_CHECK_SANITY( rc = rtCrPkcs7SignedData_CheckSanityExtra(pThis, fFlags, pErrInfo, pszErrorTag) ) /* no ; */
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * Set of PCKS #7 SignedData.
+ */
+#define RTASN1TMPL_TYPE RTCRPKCS7SETOFSIGNEDDATA
+#define RTASN1TMPL_EXT_NAME RTCrPkcs7SetOfSignedData
+#define RTASN1TMPL_INT_NAME rtCrPkcs7SetOfSignedData
+RTASN1TMPL_SET_OF(RTCRPKCS7SIGNEDDATA, RTCrPkcs7SignedData);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * PCKS #7 DigestInfo.
+ */
+#define RTASN1TMPL_TYPE RTCRPKCS7DIGESTINFO
+#define RTASN1TMPL_EXT_NAME RTCrPkcs7DigestInfo
+#define RTASN1TMPL_INT_NAME rtCrPkcs7DigestInfo
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( DigestAlgorithm, RTCRX509ALGORITHMIDENTIFIER, RTCrX509AlgorithmIdentifier);
+RTASN1TMPL_MEMBER( Digest, RTASN1OCTETSTRING, RTAsn1OctetString);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * PCKS #7 ContentInfo.
+ */
+#define RTASN1TMPL_TYPE RTCRPKCS7CONTENTINFO
+#define RTASN1TMPL_EXT_NAME RTCrPkcs7ContentInfo
+#define RTASN1TMPL_INT_NAME rtCrPkcs7ContentInfo
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( ContentType, RTASN1OBJID, RTAsn1ObjId);
+RTASN1TMPL_MEMBER_OPT_ITAG( Content, RTASN1OCTETSTRING, RTAsn1OctetString, 0);
+RTASN1TMPL_EXEC_DECODE( rc = rtCrPkcs7ContentInfo_DecodeExtra(pCursor, fFlags, pThis, pszErrorTag)) /* no ; */
+RTASN1TMPL_EXEC_CLONE( rc = rtCrPkcs7ContentInfo_CloneExtra(pThis) ) /* no ; */
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * Set of PCKS #7 ContentInfo.
+ */
+#define RTASN1TMPL_TYPE RTCRPKCS7SETOFCONTENTINFOS
+#define RTASN1TMPL_EXT_NAME RTCrPkcs7SetOfContentInfos
+#define RTASN1TMPL_INT_NAME rtCrPkcs7SetOfContentInfos
+RTASN1TMPL_SET_OF(RTCRPKCS7CONTENTINFO, RTCrPkcs7ContentInfo);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * One PKCS #7 ExtendedCertificateOrCertificate or a CMS CertificateChoices (sic).
+ */
+#define RTASN1TMPL_TYPE RTCRPKCS7CERT
+#define RTASN1TMPL_EXT_NAME RTCrPkcs7Cert
+#define RTASN1TMPL_INT_NAME rtCrPkcs7Cert
+RTASN1TMPL_BEGIN_PCHOICE();
+RTASN1TMPL_PCHOICE_ITAG_UC( ASN1_TAG_SEQUENCE, RTCRPKCS7CERTCHOICE_X509, u.pX509Cert, X509Cert, RTCRX509CERTIFICATE, RTCrX509Certificate);
+RTASN1TMPL_PCHOICE_ITAG( 0, RTCRPKCS7CERTCHOICE_EXTENDED_PKCS6, u.pExtendedCert, ExtendedCert, RTASN1CORE, RTAsn1Core);
+RTASN1TMPL_PCHOICE_ITAG( 1, RTCRPKCS7CERTCHOICE_AC_V1, u.pAcV1, AcV1, RTASN1CORE, RTAsn1Core);
+RTASN1TMPL_PCHOICE_ITAG( 2, RTCRPKCS7CERTCHOICE_AC_V2, u.pAcV2, AcV2, RTASN1CORE, RTAsn1Core);
+RTASN1TMPL_PCHOICE_ITAG( 3, RTCRPKCS7CERTCHOICE_OTHER, u.pOtherCert, OtherCert, RTASN1CORE, RTAsn1Core);
+RTASN1TMPL_END_PCHOICE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * Set of PKCS #7 ExtendedCertificateOrCertificate or a CMS CertificateChoices.
+ */
+#define RTASN1TMPL_TYPE RTCRPKCS7SETOFCERTS
+#define RTASN1TMPL_EXT_NAME RTCrPkcs7SetOfCerts
+#define RTASN1TMPL_INT_NAME rtCrPkcs7SetOfCerts
+RTASN1TMPL_SET_OF(RTCRPKCS7CERT, RTCrPkcs7Cert);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
diff --git a/src/VBox/Runtime/common/crypto/pkcs7-verify.cpp b/src/VBox/Runtime/common/crypto/pkcs7-verify.cpp
new file mode 100644
index 00000000..121ca442
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkcs7-verify.cpp
@@ -0,0 +1,866 @@
+/* $Id: pkcs7-verify.cpp $ */
+/** @file
+ * IPRT - Crypto - PKCS \#7, Verification
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pkcs7.h>
+
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/crypto/digest.h>
+#include <iprt/crypto/key.h>
+#include <iprt/crypto/pkix.h>
+#include <iprt/crypto/store.h>
+#include <iprt/crypto/x509.h>
+
+#ifdef IPRT_WITH_OPENSSL
+# include "internal/iprt-openssl.h"
+# include "internal/openssl-pre.h"
+# include <openssl/pkcs7.h>
+# include <openssl/x509.h>
+# include <openssl/err.h>
+# include "internal/openssl-post.h"
+#endif
+
+
+
+#ifdef IPRT_WITH_OPENSSL
+static int rtCrPkcs7VerifySignedDataUsingOpenSsl(PCRTCRPKCS7CONTENTINFO pContentInfo, uint32_t fFlags,
+ RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts,
+ void const *pvContent, size_t cbContent, PRTERRINFO pErrInfo)
+{
+ RT_NOREF_PV(fFlags);
+
+ /*
+ * Verify using OpenSSL. ERR_PUT_error
+ */
+ unsigned char const *pbRawContent;
+ uint32_t cbRawContent;
+ void *pvFree;
+ int rcOssl = RTAsn1EncodeQueryRawBits(RTCrPkcs7ContentInfo_GetAsn1Core(pContentInfo),
+ (const uint8_t **)&pbRawContent, &cbRawContent, &pvFree, pErrInfo);
+ AssertRCReturn(rcOssl, rcOssl);
+
+ PKCS7 *pOsslPkcs7 = NULL;
+ PKCS7 *pOsslPkcs7Ret = d2i_PKCS7(&pOsslPkcs7, &pbRawContent, cbRawContent);
+
+ RTMemTmpFree(pvFree);
+
+ if (pOsslPkcs7 != NULL && pOsslPkcs7Ret == pOsslPkcs7)
+ {
+ STACK_OF(X509) *pAddCerts = NULL;
+ if (hAdditionalCerts != NIL_RTCRSTORE)
+ rcOssl = RTCrStoreConvertToOpenSslCertStack(hAdditionalCerts, 0, (void **)&pAddCerts, pErrInfo);
+ else
+ {
+ pAddCerts = sk_X509_new_null();
+ rcOssl = RT_LIKELY(pAddCerts != NULL) ? VINF_SUCCESS : VERR_NO_MEMORY;
+ }
+ if (RT_SUCCESS(rcOssl))
+ {
+ PCRTCRPKCS7SETOFCERTS pCerts = &pContentInfo->u.pSignedData->Certificates;
+ for (uint32_t i = 0; i < pCerts->cItems; i++)
+ if (pCerts->papItems[i]->enmChoice == RTCRPKCS7CERTCHOICE_X509)
+ rtCrOpenSslAddX509CertToStack(pAddCerts, pCerts->papItems[i]->u.pX509Cert, NULL);
+
+ X509_STORE *pTrustedCerts = NULL;
+ if (hTrustedCerts != NIL_RTCRSTORE)
+ rcOssl = RTCrStoreConvertToOpenSslCertStore(hTrustedCerts, 0, (void **)&pTrustedCerts, pErrInfo);
+ if (RT_SUCCESS(rcOssl))
+ {
+ rtCrOpenSslInit();
+
+ BIO *pBioContent = BIO_new_mem_buf((void *)pvContent, (int)cbContent);
+ if (pBioContent)
+ {
+ uint32_t fOsslFlags = PKCS7_NOCHAIN;
+ fOsslFlags |= PKCS7_NOVERIFY; // temporary hack.
+ if (PKCS7_verify(pOsslPkcs7, pAddCerts, pTrustedCerts, pBioContent, NULL /*out*/, fOsslFlags))
+ rcOssl = VINF_SUCCESS;
+ else
+ {
+ rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_OSSL_VERIFY_FAILED, "PKCS7_verify failed: ");
+ if (pErrInfo)
+ ERR_print_errors_cb(rtCrOpenSslErrInfoCallback, pErrInfo);
+ }
+ BIO_free(pBioContent);
+ }
+ if (pTrustedCerts)
+ X509_STORE_free(pTrustedCerts);
+ }
+ else
+ rcOssl = RTErrInfoSet(pErrInfo, rcOssl, "RTCrStoreConvertToOpenSslCertStore failed");
+#include "internal/openssl-pre.h" /* Need to disable C5039 warning here. */
+ if (pAddCerts)
+ sk_X509_pop_free(pAddCerts, X509_free);
+#include "internal/openssl-post.h"
+ }
+ else
+ rcOssl = RTErrInfoSet(pErrInfo, rcOssl, "RTCrStoreConvertToOpenSslCertStack failed");
+ PKCS7_free(pOsslPkcs7);
+ }
+ else
+ {
+ rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_OSSL_D2I_FAILED, "d2i_PKCS7 failed");
+ if (pErrInfo)
+ ERR_print_errors_cb(rtCrOpenSslErrInfoCallback, pErrInfo);
+ }
+
+ return rcOssl;
+}
+#endif /* IPRT_WITH_OPENSSL */
+
+
+
+static int rtCrPkcs7VerifyCertUsageTimstamping(PCRTCRX509CERTIFICATE pCert, PRTERRINFO pErrInfo)
+{
+ if (!(pCert->TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_EXT_KEY_USAGE))
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_KEY_USAGE_MISMATCH, "No extended key usage certificate attribute.");
+ if (!(pCert->TbsCertificate.T3.fExtKeyUsage & (RTCRX509CERT_EKU_F_TIMESTAMPING | RTCRX509CERT_EKU_F_MS_TIMESTAMP_SIGNING)))
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_KEY_USAGE_MISMATCH, "fExtKeyUsage=%#x, missing %#x (time stamping)",
+ pCert->TbsCertificate.T3.fExtKeyUsage,
+ RTCRX509CERT_EKU_F_TIMESTAMPING | RTCRX509CERT_EKU_F_MS_TIMESTAMP_SIGNING);
+ return VINF_SUCCESS;
+}
+
+
+static int rtCrPkcs7VerifyCertUsageDigitalSignature(PCRTCRX509CERTIFICATE pCert, PRTERRINFO pErrInfo)
+{
+ if ( (pCert->TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_KEY_USAGE)
+ && !(pCert->TbsCertificate.T3.fKeyUsage & RTCRX509CERT_KEY_USAGE_F_DIGITAL_SIGNATURE))
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_KEY_USAGE_MISMATCH, "fKeyUsage=%#x, missing %#x",
+ pCert->TbsCertificate.T3.fKeyUsage, RTCRX509CERT_KEY_USAGE_F_DIGITAL_SIGNATURE);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{RTCRPKCS7VERIFYCERTCALLBACK,
+ * Default implementation that checks for the DigitalSignature KeyUsage bit.}
+ */
+RTDECL(int) RTCrPkcs7VerifyCertCallbackDefault(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths, uint32_t fFlags,
+ void *pvUser, PRTERRINFO pErrInfo)
+{
+ RT_NOREF_PV(hCertPaths); RT_NOREF_PV(pvUser);
+ int rc = VINF_SUCCESS;
+
+ if (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA)
+ rc = rtCrPkcs7VerifyCertUsageDigitalSignature(pCert, pErrInfo);
+
+ if ( (fFlags & RTCRPKCS7VCC_F_TIMESTAMP)
+ && RT_SUCCESS(rc))
+ rc = rtCrPkcs7VerifyCertUsageTimstamping(pCert, pErrInfo);
+
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{RTCRPKCS7VERIFYCERTCALLBACK,
+ * Standard code signing. Use this for Microsoft SPC.}
+ */
+RTDECL(int) RTCrPkcs7VerifyCertCallbackCodeSigning(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths, uint32_t fFlags,
+ void *pvUser, PRTERRINFO pErrInfo)
+{
+ RT_NOREF_PV(hCertPaths); RT_NOREF_PV(pvUser);
+ int rc = VINF_SUCCESS;
+ if (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA)
+ {
+ /*
+ * If KeyUsage is present it must include digital signature.
+ */
+ rc = rtCrPkcs7VerifyCertUsageDigitalSignature(pCert, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * The extended usage 'code signing' must be present.
+ */
+ if (!(pCert->TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_EXT_KEY_USAGE))
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_KEY_USAGE_MISMATCH, "No extended key usage certificate attribute.");
+ if (!(pCert->TbsCertificate.T3.fExtKeyUsage & RTCRX509CERT_EKU_F_CODE_SIGNING))
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_KEY_USAGE_MISMATCH, "fExtKeyUsage=%#RX64, missing CODE_SIGNING (%#RX64)",
+ pCert->TbsCertificate.T3.fExtKeyUsage, RTCRX509CERT_EKU_F_CODE_SIGNING);
+ }
+ }
+
+ /*
+ * Timestamping too?
+ */
+ if ( (fFlags & RTCRPKCS7VCC_F_TIMESTAMP)
+ && RT_SUCCESS(rc))
+ rc = rtCrPkcs7VerifyCertUsageTimstamping(pCert, pErrInfo);
+
+ return rc;
+}
+
+
+/**
+ * Deals with authenticated attributes.
+ *
+ * When authenticated attributes are present (checked by caller) we must:
+ * - fish out the content type and check it against the content inof,
+ * - fish out the message digest among and check it against *phDigest,
+ * - compute the message digest of the authenticated attributes and
+ * replace *phDigest with this for the signature verification.
+ *
+ * @returns IPRT status code.
+ * @param pSignerInfo The signer info being verified.
+ * @param pSignedData The signed data.
+ * @param phDigest On input this is the digest of the content. On
+ * output it will (on success) be a reference to
+ * the message digest of the authenticated
+ * attributes. The input reference is consumed.
+ * The caller shall release the output reference.
+ * @param fFlags Flags.
+ * @param pErrInfo Extended error info, optional.
+ */
+static int rtCrPkcs7VerifySignerInfoAuthAttribs(PCRTCRPKCS7SIGNERINFO pSignerInfo, PCRTCRPKCS7SIGNEDDATA pSignedData,
+ PRTCRDIGEST phDigest, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+ /*
+ * Scan the attributes and validate the two required attributes
+ * (RFC-2315, chapter 9.2, fourth bullet). Checking that we've got exactly
+ * one of each of them is checked by the santiy checker function, so we'll
+ * just assert that it did it's job here.
+ */
+ uint32_t cContentTypes = 0;
+ uint32_t cMessageDigests = 0;
+ uint32_t i = pSignerInfo->AuthenticatedAttributes.cItems;
+ while (i-- > 0)
+ {
+ PCRTCRPKCS7ATTRIBUTE pAttrib = pSignerInfo->AuthenticatedAttributes.papItems[i];
+
+ if (RTAsn1ObjId_CompareWithString(&pAttrib->Type, RTCR_PKCS9_ID_CONTENT_TYPE_OID) == 0)
+ {
+ AssertReturn(!cContentTypes, VERR_CR_PKCS7_INTERNAL_ERROR);
+ AssertReturn(pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_OBJ_IDS, VERR_CR_PKCS7_INTERNAL_ERROR);
+ AssertReturn(pAttrib->uValues.pObjIds->cItems == 1, VERR_CR_PKCS7_INTERNAL_ERROR);
+
+ if ( !(fFlags & RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE) /* See note about microsoft below. */
+ && RTAsn1ObjId_Compare(pAttrib->uValues.pObjIds->papItems[0], &pSignedData->ContentInfo.ContentType) != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_CONTENT_TYPE_ATTRIB_MISMATCH,
+ "Expected content-type %s, found %s", pAttrib->uValues.pObjIds->papItems[0]->szObjId,
+ pSignedData->ContentInfo.ContentType.szObjId);
+ cContentTypes++;
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pAttrib->Type, RTCR_PKCS9_ID_MESSAGE_DIGEST_OID) == 0)
+ {
+ AssertReturn(!cMessageDigests, VERR_CR_PKCS7_INTERNAL_ERROR);
+ AssertReturn(pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_OCTET_STRINGS, VERR_CR_PKCS7_INTERNAL_ERROR);
+ AssertReturn(pAttrib->uValues.pOctetStrings->cItems == 1, VERR_CR_PKCS7_INTERNAL_ERROR);
+
+ if (!RTCrDigestMatch(*phDigest,
+ pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pv,
+ pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb))
+ {
+ size_t cbHash = RTCrDigestGetHashSize(*phDigest);
+ if (cbHash != pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_MESSAGE_DIGEST_ATTRIB_MISMATCH,
+ "Authenticated message-digest attribute mismatch: cbHash=%#zx cbValue=%#x",
+ cbHash, pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb);
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_MESSAGE_DIGEST_ATTRIB_MISMATCH,
+ "Authenticated message-digest attribute mismatch (cbHash=%#zx):\n"
+ "signed: %.*Rhxs\n"
+ "our: %.*Rhxs\n",
+ cbHash,
+ cbHash, pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pv,
+ cbHash, RTCrDigestGetHash(*phDigest));
+ }
+ cMessageDigests++;
+ }
+ }
+
+ /*
+ * Full error reporting here as we don't currently extensively santiy check
+ * counter signatures.
+ * Note! Microsoft includes content info in their timestamp counter signatures,
+ * at least for vista, despite the RFC-3852 stating counter signatures
+ * "MUST NOT contain a content-type".
+ */
+ if (RT_UNLIKELY( cContentTypes != 1
+ && !(fFlags & RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE)))
+ return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_MISSING_CONTENT_TYPE_ATTRIB,
+ "Missing authenticated content-type attribute.");
+ if (RT_UNLIKELY(cMessageDigests != 1))
+ return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_MISSING_MESSAGE_DIGEST_ATTRIB,
+ "Missing authenticated message-digest attribute.");
+
+ /*
+ * Calculate the digest of the authenticated attributes for use in the
+ * signature validation.
+ */
+ if ( pSignerInfo->DigestAlgorithm.Parameters.enmType != RTASN1TYPE_NULL
+ && pSignerInfo->DigestAlgorithm.Parameters.enmType != RTASN1TYPE_NOT_PRESENT)
+ return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_DIGEST_PARAMS_NOT_IMPL, "Digest algorithm has unsupported parameters");
+
+ RTCRDIGEST hDigest;
+ int rc = RTCrDigestCreateByObjId(&hDigest, &pSignerInfo->DigestAlgorithm.Algorithm);
+ if (RT_SUCCESS(rc))
+ {
+ RTCrDigestRelease(*phDigest);
+ *phDigest = hDigest;
+
+ /** @todo The encoding step modifies the data, contradicting the const-ness
+ * of the parameter. */
+ rc = RTCrPkcs7Attributes_HashAttributes((PRTCRPKCS7ATTRIBUTES)&pSignerInfo->AuthenticatedAttributes, hDigest, pErrInfo);
+ }
+ return rc;
+}
+
+
+/**
+ * Find the handle to the digest given by the specified SignerInfo.
+ *
+ * @returns IPRT status code
+ * @param phDigest Where to return a referenced digest handle on
+ * success.
+ * @param pSignedData The signed data structure.
+ * @param pSignerInfo The signer info.
+ * @param pahDigests Array of content digests that runs parallel to
+ * pSignedData->DigestAlgorithms.
+ * @param pErrInfo Where to store additional error details,
+ * optional.
+ */
+static int rtCrPkcs7VerifyFindDigest(PRTCRDIGEST phDigest, PCRTCRPKCS7SIGNEDDATA pSignedData,
+ PCRTCRPKCS7SIGNERINFO pSignerInfo, PRTCRDIGEST pahDigests, PRTERRINFO pErrInfo)
+{
+ uint32_t iDigest = pSignedData->DigestAlgorithms.cItems;
+ while (iDigest-- > 0)
+ if (RTCrX509AlgorithmIdentifier_Compare(pSignedData->DigestAlgorithms.papItems[iDigest],
+ &pSignerInfo->DigestAlgorithm) == 0)
+ {
+ RTCRDIGEST hDigest = pahDigests[iDigest];
+ uint32_t cRefs = RTCrDigestRetain(hDigest);
+ AssertReturn(cRefs != UINT32_MAX, VERR_CR_PKCS7_INTERNAL_ERROR);
+ *phDigest = hDigest;
+ return VINF_SUCCESS;
+ }
+ *phDigest = NIL_RTCRDIGEST; /* Make gcc happy. */
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_ALGO_NOT_FOUND_IN_LIST,
+ "SignerInfo.DigestAlgorithm %s not found.",
+ pSignerInfo->DigestAlgorithm.Algorithm.szObjId);
+}
+
+
+/**
+ * Verifies one signature on a PKCS \#7 SignedData.
+ *
+ * @returns IPRT status code.
+ * @param pSignerInfo The signature.
+ * @param pSignedData The SignedData.
+ * @param hDigests The digest corresponding to
+ * pSignerInfo->DigestAlgorithm.
+ * @param fFlags Verification flags.
+ * @param hAdditionalCerts Store containing optional certificates,
+ * optional.
+ * @param hTrustedCerts Store containing trusted certificates, required.
+ * @param pValidationTime The time we're supposed to validate the
+ * certificates chains at.
+ * @param pfnVerifyCert Signing certificate verification callback.
+ * @param fVccFlags Signing certificate verification callback flags.
+ * @param pvUser Callback parameter.
+ * @param pErrInfo Where to store additional error details,
+ * optional.
+ */
+static int rtCrPkcs7VerifySignerInfo(PCRTCRPKCS7SIGNERINFO pSignerInfo, PCRTCRPKCS7SIGNEDDATA pSignedData,
+ RTCRDIGEST hDigest, uint32_t fFlags, RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts,
+ PCRTTIMESPEC pValidationTime, PFNRTCRPKCS7VERIFYCERTCALLBACK pfnVerifyCert,
+ uint32_t fVccFlags, void *pvUser, PRTERRINFO pErrInfo)
+{
+ /*
+ * Locate the certificate used for signing.
+ */
+ PCRTCRCERTCTX pSignerCertCtx = NULL;
+ PCRTCRX509CERTIFICATE pSignerCert = NULL;
+ if (hTrustedCerts != NIL_RTCRSTORE)
+ pSignerCertCtx = RTCrStoreCertByIssuerAndSerialNo(hTrustedCerts, &pSignerInfo->IssuerAndSerialNumber.Name,
+ &pSignerInfo->IssuerAndSerialNumber.SerialNumber);
+ if (!pSignerCertCtx && hAdditionalCerts != NIL_RTCRSTORE)
+ pSignerCertCtx = RTCrStoreCertByIssuerAndSerialNo(hAdditionalCerts, &pSignerInfo->IssuerAndSerialNumber.Name,
+ &pSignerInfo->IssuerAndSerialNumber.SerialNumber);
+ if (pSignerCertCtx)
+ pSignerCert = pSignerCertCtx->pCert;
+ else
+ {
+ pSignerCert = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(&pSignedData->Certificates,
+ &pSignerInfo->IssuerAndSerialNumber.Name,
+ &pSignerInfo->IssuerAndSerialNumber.SerialNumber);
+ if (!pSignerCert)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_SIGNED_DATA_CERT_NOT_FOUND,
+ "Certificate not found: serial=%.*Rhxs",
+ pSignerInfo->IssuerAndSerialNumber.SerialNumber.Asn1Core.cb,
+ pSignerInfo->IssuerAndSerialNumber.SerialNumber.Asn1Core.uData.pv);
+ }
+
+ /*
+ * Unless caller requesed all certificates to be trusted fully, we always
+ * pass it on to the certificate path builder so it can do the requested
+ * checks on trust anchors. (We didn't used to do this as the path
+ * builder could handle trusted targets. A benefit here is that
+ * pfnVerifyCert can assume a hCertPaths now, and get the validation time
+ * from it if it wants it.)
+ *
+ * If no valid paths are found, this step will fail.
+ */
+ int rc;
+ if (!(fFlags & RTCRPKCS7VERIFY_SD_F_TRUST_ALL_CERTS))
+ {
+ RTCRX509CERTPATHS hCertPaths;
+ rc = RTCrX509CertPathsCreate(&hCertPaths, pSignerCert);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrX509CertPathsSetValidTimeSpec(hCertPaths, pValidationTime);
+ if (hTrustedCerts != NIL_RTCRSTORE && RT_SUCCESS(rc))
+ rc = RTCrX509CertPathsSetTrustedStore(hCertPaths, hTrustedCerts);
+ if (hAdditionalCerts != NIL_RTCRSTORE && RT_SUCCESS(rc))
+ rc = RTCrX509CertPathsSetUntrustedStore(hCertPaths, hAdditionalCerts);
+ if (pSignedData->Certificates.cItems > 0 && RT_SUCCESS(rc))
+ rc = RTCrX509CertPathsSetUntrustedSet(hCertPaths, &pSignedData->Certificates);
+ if ((fFlags & RTCRPKCS7VERIFY_SD_F_CHECK_TRUST_ANCHORS) && RT_SUCCESS(rc))
+ rc = RTCrX509CertPathsSetTrustAnchorChecks(hCertPaths, true /*fEnable*/);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrX509CertPathsBuild(hCertPaths, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = RTCrX509CertPathsValidateAll(hCertPaths, NULL, pErrInfo);
+
+ /*
+ * Check that the certificate purpose and whatnot matches what
+ * is being signed.
+ */
+ if (RT_SUCCESS(rc))
+ rc = pfnVerifyCert(pSignerCert, hCertPaths, fVccFlags, pvUser, pErrInfo);
+ }
+ else
+ RTErrInfoSetF(pErrInfo, rc, "Error configuring path builder: %Rrc", rc);
+ RTCrX509CertPathsRelease(hCertPaths);
+ }
+ }
+ /*
+ * Check that the certificate purpose matches what is signed.
+ */
+ else
+ rc = pfnVerifyCert(pSignerCert, NIL_RTCRX509CERTPATHS, fVccFlags, pvUser, pErrInfo);
+
+ /*
+ * Reference the digest so we can safely replace with one on the
+ * authenticated attributes below.
+ */
+ if ( RT_SUCCESS(rc)
+ && RTCrDigestRetain(hDigest) != UINT32_MAX)
+ {
+ /*
+ * If there are authenticated attributes, we've got more work before we
+ * can verify the signature.
+ */
+ if ( RT_SUCCESS(rc)
+ && RTCrPkcs7Attributes_IsPresent(&pSignerInfo->AuthenticatedAttributes))
+ rc = rtCrPkcs7VerifySignerInfoAuthAttribs(pSignerInfo, pSignedData, &hDigest, fFlags, pErrInfo);
+
+ /*
+ * Verify the signature.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ RTCRKEY hKey;
+ rc = RTCrKeyCreateFromSubjectPublicKeyInfo(&hKey, &pSignerCert->TbsCertificate.SubjectPublicKeyInfo,
+ pErrInfo, "pkcs7");
+ if (RT_SUCCESS(rc))
+ {
+ RTCRPKIXSIGNATURE hSignature;
+ rc = RTCrPkixSignatureCreateByObjId(&hSignature, &pSignerInfo->DigestEncryptionAlgorithm.Algorithm,
+ hKey, &pSignerInfo->DigestEncryptionAlgorithm.Parameters, false /*fSigning*/);
+ RTCrKeyRelease(hKey);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo Check that DigestEncryptionAlgorithm is compatible with hSignature
+ * (this is not vital). */
+ rc = RTCrPkixSignatureVerifyOctetString(hSignature, hDigest, &pSignerInfo->EncryptedDigest);
+ if (RT_FAILURE(rc))
+ rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_SIGNATURE_VERIFICATION_FAILED,
+ "Signature verification failed: %Rrc", rc);
+ RTCrPkixSignatureRelease(hSignature);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "Failure to instantiate public key algorithm [IPRT]: %s (%s)",
+ pSignerCert->TbsCertificate.SubjectPublicKeyInfo.Algorithm.Algorithm.szObjId,
+ pSignerInfo->DigestEncryptionAlgorithm.Algorithm.szObjId);
+ }
+ }
+
+ RTCrDigestRelease(hDigest);
+ }
+ else if (RT_SUCCESS(rc))
+ rc = VERR_CR_PKCS7_INTERNAL_ERROR;
+ RTCrCertCtxRelease(pSignerCertCtx);
+ return rc;
+}
+
+
+/**
+ * Verifies a counter signature.
+ *
+ * @returns IPRT status code.
+ * @param pCounterSignerInfo The counter signature.
+ * @param pPrimarySignerInfo The primary signature (can be a counter
+ * signature too if nested).
+ * @param pSignedData The SignedData.
+ * @param fFlags Verification flags.
+ * @param hAdditionalCerts Store containing optional certificates,
+ * optional.
+ * @param hTrustedCerts Store containing trusted certificates, required.
+ * @param pValidationTime The time we're supposed to validate the
+ * certificates chains at.
+ * @param pfnVerifyCert Signing certificate verification callback.
+ * @param fVccFlags Signing certificate verification callback flags.
+ * @param pvUser Callback parameter.
+ * @param pErrInfo Where to store additional error details,
+ * optional.
+ */
+static int rtCrPkcs7VerifyCounterSignerInfo(PCRTCRPKCS7SIGNERINFO pCounterSignerInfo, PCRTCRPKCS7SIGNERINFO pPrimarySignerInfo,
+ PCRTCRPKCS7SIGNEDDATA pSignedData, uint32_t fFlags,
+ RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts, PCRTTIMESPEC pValidationTime,
+ PFNRTCRPKCS7VERIFYCERTCALLBACK pfnVerifyCert, uint32_t fVccFlags,
+ void *pvUser, PRTERRINFO pErrInfo)
+{
+ /*
+ * Calculate the digest we need to verify.
+ */
+ RTCRDIGEST hDigest;
+ int rc = RTCrDigestCreateByObjId(&hDigest, &pCounterSignerInfo->DigestAlgorithm.Algorithm);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrDigestUpdate(hDigest,
+ pPrimarySignerInfo->EncryptedDigest.Asn1Core.uData.pv,
+ pPrimarySignerInfo->EncryptedDigest.Asn1Core.cb);
+ if (RT_SUCCESS(rc))
+ rc = RTCrDigestFinal(hDigest, NULL, 0);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Pass it on to the common SignerInfo verifier function.
+ */
+ rc = rtCrPkcs7VerifySignerInfo(pCounterSignerInfo, pSignedData, hDigest,
+ fFlags | RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE,
+ hAdditionalCerts, hTrustedCerts, pValidationTime,
+ pfnVerifyCert, fVccFlags, pvUser, pErrInfo);
+
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_CALC_ERROR,
+ "Hashing for counter signature failed unexpectedly: %Rrc", rc);
+ RTCrDigestRelease(hDigest);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_CREATE_ERROR, "Error creating digest for '%s': %Rrc",
+ pCounterSignerInfo->DigestAlgorithm.Algorithm.szObjId, rc);
+
+ return rc;
+}
+
+
+/**
+ * Worker.
+ */
+static int rtCrPkcs7VerifySignedDataEx(PCRTCRPKCS7CONTENTINFO pContentInfo, uint32_t fFlags,
+ RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts,
+ PCRTTIMESPEC pValidationTime,
+ PFNRTCRPKCS7VERIFYCERTCALLBACK pfnVerifyCert, void *pvUser,
+ void const *pvContent, size_t cbContent, PRTERRINFO pErrInfo)
+{
+ /*
+ * Check and adjust the input.
+ */
+ if (pfnVerifyCert)
+ AssertPtrReturn(pfnVerifyCert, VERR_INVALID_POINTER);
+ else
+ pfnVerifyCert = RTCrPkcs7VerifyCertCallbackDefault;
+
+ if (!RTCrPkcs7ContentInfo_IsSignedData(pContentInfo))
+ return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_NOT_SIGNED_DATA, "Not PKCS #7 SignedData.");
+ PCRTCRPKCS7SIGNEDDATA pSignedData = pContentInfo->u.pSignedData;
+ int rc = RTCrPkcs7SignedData_CheckSanity(pSignedData, 0, pErrInfo, "");
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Hash the content info.
+ */
+ /* Check that there aren't too many or too few hash algorithms for our
+ implementation and purposes. */
+ RTCRDIGEST ahDigests[2];
+ uint32_t const cDigests = pSignedData->DigestAlgorithms.cItems;
+ if (!cDigests) /** @todo we might have to support this... */
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_NO_DIGEST_ALGORITHMS, "No digest algorithms");
+
+ if (cDigests > RT_ELEMENTS(ahDigests))
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_TOO_MANY_DIGEST_ALGORITHMS,
+ "Too many digest algorithm: cAlgorithms=%u", cDigests);
+
+ /* Create the message digest calculators. */
+ rc = VERR_CR_PKCS7_NO_DIGEST_ALGORITHMS;
+ uint32_t i;
+ for (i = 0; i < cDigests; i++)
+ {
+ rc = RTCrDigestCreateByObjId(&ahDigests[i], &pSignedData->DigestAlgorithms.papItems[i]->Algorithm);
+ if (RT_FAILURE(rc))
+ {
+ rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_CREATE_ERROR, "Error creating digest for '%s': %Rrc",
+ pSignedData->DigestAlgorithms.papItems[i]->Algorithm.szObjId, rc);
+ break;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /* Hash the content. */
+ for (i = 0; i < cDigests && RT_SUCCESS(rc); i++)
+ {
+ rc = RTCrDigestUpdate(ahDigests[i], pvContent, cbContent);
+ if (RT_SUCCESS(rc))
+ rc = RTCrDigestFinal(ahDigests[i], NULL, 0);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Validate the signed infos. The flags may select one particular entry.
+ */
+ RTTIMESPEC const GivenValidationTime = *pValidationTime;
+ uint32_t fPrimaryVccFlags = !(fFlags & RTCRPKCS7VERIFY_SD_F_USAGE_TIMESTAMPING)
+ ? RTCRPKCS7VCC_F_SIGNED_DATA : RTCRPKCS7VCC_F_TIMESTAMP;
+ uint32_t cItems = pSignedData->SignerInfos.cItems;
+ i = 0;
+ if (fFlags & RTCRPKCS7VERIFY_SD_F_HAS_SIGNER_INDEX)
+ {
+ i = (fFlags & RTCRPKCS7VERIFY_SD_F_SIGNER_INDEX_MASK) >> RTCRPKCS7VERIFY_SD_F_SIGNER_INDEX_SHIFT;
+ cItems = RT_MIN(cItems, i + 1);
+ }
+ rc = VERR_CR_PKCS7_NO_SIGNER_INFOS;
+ for (; i < cItems; i++)
+ {
+ PCRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[i];
+ RTCRDIGEST hThisDigest = NIL_RTCRDIGEST; /* (gcc maybe incredible stupid.) */
+ rc = rtCrPkcs7VerifyFindDigest(&hThisDigest, pSignedData, pSignerInfo, ahDigests, pErrInfo);
+ if (RT_FAILURE(rc))
+ break;
+
+ /*
+ * See if we can find a trusted signing time.
+ * (Note that while it would make sense splitting up this function,
+ * we need to carry a lot of arguments around, so better not.)
+ */
+ bool fDone = false;
+ PCRTCRPKCS7SIGNERINFO pSigningTimeSigner = NULL;
+ PCRTASN1TIME pSignedTime;
+ while ( !fDone
+ && (pSignedTime = RTCrPkcs7SignerInfo_GetSigningTime(pSignerInfo, &pSigningTimeSigner)) != NULL)
+ {
+ RTTIMESPEC ThisValidationTime;
+ if (RT_LIKELY(RTTimeImplode(&ThisValidationTime, &pSignedTime->Time)))
+ {
+ if (pSigningTimeSigner == pSignerInfo)
+ {
+ if (fFlags & RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY)
+ continue;
+ rc = rtCrPkcs7VerifySignerInfo(pSignerInfo, pSignedData, hThisDigest, fFlags,
+ hAdditionalCerts, hTrustedCerts, &ThisValidationTime,
+ pfnVerifyCert, fPrimaryVccFlags | RTCRPKCS7VCC_F_TIMESTAMP,
+ pvUser, pErrInfo);
+ }
+ else
+ {
+ rc = VINF_SUCCESS;
+ if (!(fFlags & RTCRPKCS7VERIFY_SD_F_USE_SIGNING_TIME_UNVERIFIED))
+ rc = rtCrPkcs7VerifyCounterSignerInfo(pSigningTimeSigner, pSignerInfo, pSignedData,
+ fFlags & ~RTCRPKCS7VERIFY_SD_F_UPDATE_VALIDATION_TIME,
+ hAdditionalCerts, hTrustedCerts, &ThisValidationTime,
+ pfnVerifyCert, RTCRPKCS7VCC_F_TIMESTAMP, pvUser, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = rtCrPkcs7VerifySignerInfo(pSignerInfo, pSignedData, hThisDigest, fFlags, hAdditionalCerts,
+ hTrustedCerts, &ThisValidationTime,
+ pfnVerifyCert, fPrimaryVccFlags, pvUser, pErrInfo);
+ }
+ fDone = RT_SUCCESS(rc)
+ || (fFlags & RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT);
+ if ((fFlags & RTCRPKCS7VERIFY_SD_F_UPDATE_VALIDATION_TIME) && fDone)
+ *(PRTTIMESPEC)pValidationTime = ThisValidationTime;
+ }
+ else
+ {
+ rc = RTErrInfoSet(pErrInfo, VERR_INTERNAL_ERROR_3, "RTTimeImplode failed");
+ fDone = true;
+ }
+ }
+
+ /*
+ * If not luck, check for microsoft timestamp counter signatures.
+ */
+ if (!fDone && !(fFlags & RTCRPKCS7VERIFY_SD_F_IGNORE_MS_TIMESTAMP))
+ {
+ PCRTCRPKCS7CONTENTINFO pSignedTimestamp = NULL;
+ pSignedTime = RTCrPkcs7SignerInfo_GetMsTimestamp(pSignerInfo, &pSignedTimestamp);
+ if (pSignedTime)
+ {
+ RTTIMESPEC ThisValidationTime;
+ if (RT_LIKELY(RTTimeImplode(&ThisValidationTime, &pSignedTime->Time)))
+ {
+ rc = VINF_SUCCESS;
+ if (!(fFlags & RTCRPKCS7VERIFY_SD_F_USE_MS_TIMESTAMP_UNVERIFIED))
+ rc = RTCrPkcs7VerifySignedData(pSignedTimestamp,
+ fFlags | RTCRPKCS7VERIFY_SD_F_IGNORE_MS_TIMESTAMP
+ | RTCRPKCS7VERIFY_SD_F_USAGE_TIMESTAMPING,
+ hAdditionalCerts, hTrustedCerts, &ThisValidationTime,
+ pfnVerifyCert, pvUser, pErrInfo);
+
+ if (RT_SUCCESS(rc))
+ rc = rtCrPkcs7VerifySignerInfo(pSignerInfo, pSignedData, hThisDigest, fFlags, hAdditionalCerts,
+ hTrustedCerts, &ThisValidationTime,
+ pfnVerifyCert, fPrimaryVccFlags, pvUser, pErrInfo);
+ fDone = RT_SUCCESS(rc)
+ || (fFlags & RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT);
+ if ((fFlags & RTCRPKCS7VERIFY_SD_F_UPDATE_VALIDATION_TIME) && fDone)
+ *(PRTTIMESPEC)pValidationTime = ThisValidationTime;
+ }
+ else
+ {
+ rc = RTErrInfoSet(pErrInfo, VERR_INTERNAL_ERROR_3, "RTTimeImplode failed");
+ fDone = true;
+ }
+
+ }
+ }
+
+ /*
+ * No valid signing time found, use the one specified instead.
+ */
+ if (!fDone)
+ rc = rtCrPkcs7VerifySignerInfo(pSignerInfo, pSignedData, hThisDigest, fFlags, hAdditionalCerts, hTrustedCerts,
+ &GivenValidationTime, pfnVerifyCert, fPrimaryVccFlags, pvUser, pErrInfo);
+ RTCrDigestRelease(hThisDigest);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_CALC_ERROR,
+ "Hashing content failed unexpectedly (i=%u): %Rrc", i, rc);
+
+ /* Clean up digests. */
+ i = cDigests;
+ }
+ while (i-- > 0)
+ {
+ int rc2 = RTCrDigestRelease(ahDigests[i]);
+ AssertRC(rc2);
+ }
+
+
+#ifdef IPRT_WITH_OPENSSL
+ /*
+ * Verify using OpenSSL and combine the results (should be identical).
+ */
+ /** @todo figure out how to verify MS timstamp signatures using OpenSSL. */
+ if (fFlags & RTCRPKCS7VERIFY_SD_F_USAGE_TIMESTAMPING)
+ return rc;
+ /** @todo figure out if we can verify just one signer info item using OpenSSL. */
+ if (!(fFlags & RTCRPKCS7VERIFY_SD_F_HAS_SIGNER_INDEX) && pSignedData->SignerInfos.cItems > 1)
+ return rc;
+
+ int rcOssl = rtCrPkcs7VerifySignedDataUsingOpenSsl(pContentInfo, fFlags, hAdditionalCerts, hTrustedCerts,
+ pvContent, cbContent, RT_SUCCESS(rc) ? pErrInfo : NULL);
+ if (RT_SUCCESS(rcOssl) && RT_SUCCESS(rc))
+ return rc;
+// AssertMsg(RT_FAILURE_NP(rcOssl) && RT_FAILURE_NP(rc), ("%Rrc, %Rrc\n", rcOssl, rc));
+ if (RT_FAILURE(rc))
+ return rc;
+ return rcOssl;
+#else
+ return rc;
+#endif
+}
+
+
+RTDECL(int) RTCrPkcs7VerifySignedData(PCRTCRPKCS7CONTENTINFO pContentInfo, uint32_t fFlags,
+ RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts,
+ PCRTTIMESPEC pValidationTime, PFNRTCRPKCS7VERIFYCERTCALLBACK pfnVerifyCert, void *pvUser,
+ PRTERRINFO pErrInfo)
+{
+ /*
+ * Find the content and pass it on to common worker.
+ */
+ if (!RTCrPkcs7ContentInfo_IsSignedData(pContentInfo))
+ return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_NOT_SIGNED_DATA, "Not PKCS #7 SignedData.");
+
+ /* Exactly what the content is, is for some stupid reason unnecessarily complicated. */
+ PCRTCRPKCS7SIGNEDDATA pSignedData = pContentInfo->u.pSignedData;
+ void const *pvContent = pSignedData->ContentInfo.Content.Asn1Core.uData.pv;
+ uint32_t cbContent = pSignedData->ContentInfo.Content.Asn1Core.cb;
+ if (pSignedData->ContentInfo.Content.pEncapsulated)
+ {
+ pvContent = pSignedData->ContentInfo.Content.pEncapsulated->uData.pv;
+ cbContent = pSignedData->ContentInfo.Content.pEncapsulated->cb;
+ }
+
+ return rtCrPkcs7VerifySignedDataEx(pContentInfo, fFlags, hAdditionalCerts, hTrustedCerts, pValidationTime,
+ pfnVerifyCert, pvUser, pvContent, cbContent, pErrInfo);
+}
+
+
+RTDECL(int) RTCrPkcs7VerifySignedDataWithExternalData(PCRTCRPKCS7CONTENTINFO pContentInfo, uint32_t fFlags,
+ RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts,
+ PCRTTIMESPEC pValidationTime,
+ PFNRTCRPKCS7VERIFYCERTCALLBACK pfnVerifyCert, void *pvUser,
+ void const *pvData, size_t cbData, PRTERRINFO pErrInfo)
+{
+ /*
+ * Require 'data' as inner content type.
+ */
+ if (!RTCrPkcs7ContentInfo_IsSignedData(pContentInfo))
+ return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_NOT_SIGNED_DATA, "Not PKCS #7 SignedData.");
+ PCRTCRPKCS7SIGNEDDATA pSignedData = pContentInfo->u.pSignedData;
+
+ if (RTAsn1ObjId_CompareWithString(&pSignedData->ContentInfo.ContentType, RTCR_PKCS7_DATA_OID) != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_NOT_DATA,
+ "The signedData content type is %s, expected 'data' (%s)",
+ pSignedData->ContentInfo.ContentType.szObjId, RTCR_PKCS7_DATA_OID);
+
+ return rtCrPkcs7VerifySignedDataEx(pContentInfo, fFlags, hAdditionalCerts, hTrustedCerts, pValidationTime,
+ pfnVerifyCert, pvUser, pvData, cbData, pErrInfo);
+}
+
diff --git a/src/VBox/Runtime/common/crypto/pkcs8-asn1-decoder.cpp b/src/VBox/Runtime/common/crypto/pkcs8-asn1-decoder.cpp
new file mode 100644
index 00000000..e6e8c400
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkcs8-asn1-decoder.cpp
@@ -0,0 +1,53 @@
+/* $Id: pkcs8-asn1-decoder.cpp $ */
+/** @file
+ * IPRT - Crypto - PKCS \#8, Decoder for ASN.1.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pkcs8.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+#include "pkcs8-internal.h"
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-asn1-decoder.h>
+
diff --git a/src/VBox/Runtime/common/crypto/pkcs8-core.cpp b/src/VBox/Runtime/common/crypto/pkcs8-core.cpp
new file mode 100644
index 00000000..bdf183c9
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkcs8-core.cpp
@@ -0,0 +1,54 @@
+/* $Id: pkcs8-core.cpp $ */
+/** @file
+ * IPRT - Crypto - PKCS \#8, Core APIs.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pkcs8.h>
+
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#include "pkcs8-internal.h"
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-core.h>
+
diff --git a/src/VBox/Runtime/common/crypto/pkcs8-init.cpp b/src/VBox/Runtime/common/crypto/pkcs8-init.cpp
new file mode 100644
index 00000000..a6c5cd1a
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkcs8-init.cpp
@@ -0,0 +1,53 @@
+/* $Id: pkcs8-init.cpp $ */
+/** @file
+ * IPRT - Crypto - PKCS \#8, Initialization API.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pkcs8.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+#include "pkcs8-internal.h"
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-init.h>
+
diff --git a/src/VBox/Runtime/common/crypto/pkcs8-internal.h b/src/VBox/Runtime/common/crypto/pkcs8-internal.h
new file mode 100644
index 00000000..e956e7f4
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkcs8-internal.h
@@ -0,0 +1,46 @@
+/* $Id: pkcs8-internal.h $ */
+/** @file
+ * IPRT - Crypto - PKCS \#8, Internal Header.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_common_crypto_pkcs8_internal_h
+#define IPRT_INCLUDED_SRC_common_crypto_pkcs8_internal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define RTASN1TMPL_TEMPLATE_FILE "../common/crypto/pkcs8-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+
+#endif /* !IPRT_INCLUDED_SRC_common_crypto_pkcs8_internal_h */
diff --git a/src/VBox/Runtime/common/crypto/pkcs8-sanity.cpp b/src/VBox/Runtime/common/crypto/pkcs8-sanity.cpp
new file mode 100644
index 00000000..78ca611c
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkcs8-sanity.cpp
@@ -0,0 +1,53 @@
+/* $Id: pkcs8-sanity.cpp $ */
+/** @file
+ * IPRT - Crypto - PKCS \#8, Sanity Checkers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pkcs8.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+#include "pkcs8-internal.h"
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-sanity.h>
+
diff --git a/src/VBox/Runtime/common/crypto/pkcs8-template.h b/src/VBox/Runtime/common/crypto/pkcs8-template.h
new file mode 100644
index 00000000..95d5ecc1
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkcs8-template.h
@@ -0,0 +1,69 @@
+/* $Id: pkcs8-template.h $ */
+/** @file
+ * IPRT - Crypto - PKCS \#8, Code Generator Template.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#define RTASN1TMPL_DECL RTDECL
+
+/*
+ * PKCS\#8 Private key info
+ */
+#define RTASN1TMPL_TYPE RTCRPKCS8PRIVATEKEYINFO
+#define RTASN1TMPL_EXT_NAME RTCrPkcs8PrivateKeyInfo
+#define RTASN1TMPL_INT_NAME RTCrPkcs8PrivateKeyInfo
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( Version, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( PrivateKeyAlgorithm, RTCRX509ALGORITHMIDENTIFIER, RTCrX509AlgorithmIdentifier);
+RTASN1TMPL_MEMBER( PrivateKey, RTASN1OCTETSTRING, RTAsn1OctetString);
+RTASN1TMPL_MEMBER_OPT_ITAG( Attributes, RTCRPKCS7ATTRIBUTES, RTCrPkcs7Attributes, 0);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * Encrypted private key info
+ */
+#define RTASN1TMPL_TYPE RTCRPKCS8ENCRYPTEDPRIVATEKEYINFO
+#define RTASN1TMPL_EXT_NAME RTCrPkcs8EncryptedPrivateKeyInfo
+#define RTASN1TMPL_INT_NAME rtCrPkcs8EncryptedPrivateKeyInfo
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( EncryptionAlgorithm, RTCRX509ALGORITHMIDENTIFIER, RTCrX509AlgorithmIdentifier);
+RTASN1TMPL_MEMBER( EncryptedData, RTASN1OCTETSTRING, RTAsn1OctetString);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
diff --git a/src/VBox/Runtime/common/crypto/pkix-sign.cpp b/src/VBox/Runtime/common/crypto/pkix-sign.cpp
new file mode 100644
index 00000000..8024b8cf
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkix-sign.cpp
@@ -0,0 +1,262 @@
+/* $Id: pkix-sign.cpp $ */
+/** @file
+ * IPRT - Crypto - Public Key Infrastructure API, Verification.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pkix.h>
+
+#include <iprt/alloca.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/crypto/digest.h>
+#include <iprt/crypto/key.h>
+
+#ifdef IPRT_WITH_OPENSSL
+# include "internal/iprt-openssl.h"
+# include "internal/openssl-pre.h"
+# include <openssl/evp.h>
+# include <openssl/rsa.h>
+# include "internal/openssl-post.h"
+# ifndef OPENSSL_VERSION_NUMBER
+# error "Missing OPENSSL_VERSION_NUMBER!"
+# endif
+#endif
+
+
+#if 0
+RTDECL(int) RTCrPkixPubKeySignData(PCRTASN1OBJID pAlgorithm, PCRTASN1DYNTYPE pParameters, PCRTASN1BITSTRING pPrivateKey,
+ PCRTASN1BITSTRING pSignatureValue, const void *pvData, size_t cbData, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate the digest related inputs.
+ */
+ AssertPtrReturn(pAlgorithm, VERR_INVALID_POINTER);
+ AssertReturn(RTAsn1ObjId_IsPresent(pAlgorithm), VERR_INVALID_POINTER);
+
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ AssertReturn(cbData > 0, VERR_INVALID_PARAMETER);
+
+ /*
+ * Digest the data and call the other API.
+ */
+ RTCRDIGEST hDigest;
+ int rc = RTCrDigestCreateByObjId(&hDigest, pAlgorithm);
+ if (RT_SUCCESS(rcIprt))
+ {
+ rc = RTCrDigestUpdate(hDigest, pvData, cbData);
+ if (RT_SUCCESS(rcIprt))
+ rc = RTCrPkixPubKeySignDigest(pAlgorithm, pParameters, pPrivateKey, pvSignedDigest, cbSignedDigest, hDigest, pErrInfo);
+ else
+ RTErrInfoSet(pErrInfo, rcIprt, "RTCrDigestUpdate failed");
+ RTCrDigestRelease(hDigest);
+ }
+ else
+ RTErrInfoSetF(pErrInfo, rcIprt, "Unknown digest algorithm [IPRT]: %s", pAlgorithm->szObjId);
+ return rc;
+}
+#endif
+
+
+RTDECL(int) RTCrPkixPubKeySignDigest(PCRTASN1OBJID pAlgorithm, RTCRKEY hPrivateKey, PCRTASN1DYNTYPE pParameters,
+ RTCRDIGEST hDigest, uint32_t fFlags,
+ void *pvSignature, size_t *pcbSignature, PRTERRINFO pErrInfo)
+{
+ /*
+ * Valid input.
+ */
+ AssertPtrReturn(pAlgorithm, VERR_INVALID_POINTER);
+ AssertReturn(RTAsn1ObjId_IsPresent(pAlgorithm), VERR_INVALID_POINTER);
+
+ if (pParameters)
+ {
+ AssertPtrReturn(pParameters, VERR_INVALID_POINTER);
+ if (pParameters->enmType == RTASN1TYPE_NULL)
+ pParameters = NULL;
+ }
+
+ AssertPtrReturn(hPrivateKey, VERR_INVALID_POINTER);
+ Assert(RTCrKeyHasPrivatePart(hPrivateKey));
+
+ AssertPtrReturn(pcbSignature, VERR_INVALID_PARAMETER);
+ size_t cbSignature = *pcbSignature;
+ if (cbSignature)
+ AssertPtrReturn(pvSignature, VERR_INVALID_POINTER);
+ else
+ pvSignature = NULL;
+
+ AssertPtrReturn(hDigest, VERR_INVALID_HANDLE);
+
+ AssertReturn(fFlags == 0, VERR_INVALID_FLAGS);
+
+ /*
+ * Parameters are not currently supported (openssl code path).
+ */
+ if (pParameters)
+ return RTErrInfoSet(pErrInfo, VERR_CR_PKIX_CIPHER_ALGO_PARAMS_NOT_IMPL,
+ "Cipher algorithm parameters are not yet supported.");
+
+ /*
+ * Sign using IPRT.
+ */
+ RTCRPKIXSIGNATURE hSignature;
+ int rcIprt = RTCrPkixSignatureCreateByObjId(&hSignature, pAlgorithm, hPrivateKey, pParameters, true /*fSigning*/);
+ if (RT_FAILURE(rcIprt))
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_CIPHER_ALGO_NOT_KNOWN,
+ "Unknown private key algorithm [IPRT %Rrc]: %s", rcIprt, pAlgorithm->szObjId);
+
+ rcIprt = RTCrPkixSignatureSign(hSignature, hDigest, pvSignature, pcbSignature);
+ if (RT_FAILURE(rcIprt))
+ RTErrInfoSet(pErrInfo, rcIprt, "RTCrPkixSignatureSign failed");
+
+ RTCrPkixSignatureRelease(hSignature);
+
+ /*
+ * Sign using OpenSSL EVP if we can.
+ */
+#if defined(IPRT_WITH_OPENSSL) \
+ && (OPENSSL_VERSION_NUMBER > 0x10000000L) /* 0.9.8 doesn't seem to have EVP_PKEY_CTX_set_signature_md. */
+
+ /* Make sure the algorithm includes the digest and isn't just RSA, ECDSA or similar. */
+ const char *pszAlgObjId = RTCrX509AlgorithmIdentifier_CombineEncryptionOidAndDigestOid(pAlgorithm->szObjId,
+ RTCrDigestGetAlgorithmOid(hDigest));
+ AssertMsgStmt(pszAlgObjId, ("enc=%s hash=%s\n", pAlgorithm->szObjId, RTCrDigestGetAlgorithmOid(hDigest)),
+ pszAlgObjId = RTCrDigestGetAlgorithmOid(hDigest));
+
+ /* Create an EVP private key. */
+ EVP_PKEY *pEvpPrivateKey = NULL;
+ const EVP_MD *pEvpMdType = NULL;
+ int rcOssl = rtCrKeyToOpenSslKeyEx(hPrivateKey, false /*fNeedPublic*/, pszAlgObjId,
+ (void **)&pEvpPrivateKey, (const void **)&pEvpMdType, pErrInfo);
+ if (RT_SUCCESS(rcOssl))
+ {
+ /* Create an EVP Private key context we can use to validate the digest. */
+ EVP_PKEY_CTX *pEvpPKeyCtx = EVP_PKEY_CTX_new(pEvpPrivateKey, NULL);
+ if (pEvpPKeyCtx)
+ {
+ rcOssl = EVP_PKEY_sign_init(pEvpPKeyCtx);
+ if (rcOssl > 0)
+ {
+ rcOssl = EVP_PKEY_CTX_set_rsa_padding(pEvpPKeyCtx, RSA_PKCS1_PADDING);
+ if (rcOssl > 0)
+ {
+ rcOssl = EVP_PKEY_CTX_set_signature_md(pEvpPKeyCtx, pEvpMdType);
+ if (rcOssl > 0)
+ {
+ /* Allocate a signature buffer. */
+ unsigned char *pbOsslSignature = NULL;
+ void *pvOsslSignatureFree = NULL;
+ size_t cbOsslSignature = cbSignature;
+ if (cbOsslSignature > 0)
+ {
+ if (cbOsslSignature < _1K)
+ pbOsslSignature = (unsigned char *)alloca(cbOsslSignature);
+ else
+ {
+ pbOsslSignature = (unsigned char *)RTMemTmpAlloc(cbOsslSignature);
+ pvOsslSignatureFree = pbOsslSignature;
+ }
+ }
+ if (cbOsslSignature == 0 || pbOsslSignature != NULL)
+ {
+ /* Get the digest from hDigest and sign it. */
+ rcOssl = EVP_PKEY_sign(pEvpPKeyCtx,
+ pbOsslSignature,
+ &cbOsslSignature,
+ (const unsigned char *)RTCrDigestGetHash(hDigest),
+ RTCrDigestGetHashSize(hDigest));
+ if (rcOssl > 0)
+ {
+ /* Compare the result. The memcmp assums no random padding bits. */
+ rcOssl = VINF_SUCCESS;
+ AssertMsgStmt(cbOsslSignature == *pcbSignature,
+ ("cbOsslSignature=%#x, iprt %#x\n", cbOsslSignature, *pcbSignature),
+ rcOssl = VERR_CR_PKIX_OSSL_VS_IPRT_SIGNATURE_SIZE);
+ AssertMsgStmt( pbOsslSignature == NULL
+ || rcOssl != VINF_SUCCESS
+ || memcmp(pbOsslSignature, pvSignature, cbOsslSignature) == 0,
+ ("OpenSSL: %.*Rhxs\n"
+ "IPRT: %.*Rhxs\n",
+ cbOsslSignature, pbOsslSignature, *pcbSignature, pvSignature),
+ rcOssl = VERR_CR_PKIX_OSSL_VS_IPRT_SIGNATURE);
+ if (!pbOsslSignature && rcOssl == VINF_SUCCESS)
+ rcOssl = VERR_BUFFER_OVERFLOW;
+ }
+ else
+ rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_SIGN_FINAL_FAILED,
+ "EVP_PKEY_sign failed (%d)", rcOssl);
+ if (pvOsslSignatureFree)
+ RTMemTmpFree(pvOsslSignatureFree);
+ }
+ else
+ rcOssl = VERR_NO_TMP_MEMORY;
+ }
+ else
+ rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR,
+ "EVP_PKEY_CTX_set_signature_md failed (%d)", rcOssl);
+ }
+ else
+ rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_RSA_PAD_ERROR,
+ "EVP_PKEY_CTX_set_rsa_padding failed (%d)", rcOssl);
+ }
+ else
+ rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR,
+ "EVP_PKEY_verify_init failed (%d)", rcOssl);
+ EVP_PKEY_CTX_free(pEvpPKeyCtx);
+ }
+ else
+ rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR, "EVP_PKEY_CTX_new failed");
+ EVP_PKEY_free(pEvpPrivateKey);
+ }
+
+ /*
+ * Check the result.
+ */
+ if ( (RT_SUCCESS(rcIprt) && RT_SUCCESS(rcOssl))
+ || (RT_FAILURE_NP(rcIprt) && RT_FAILURE_NP(rcOssl))
+ || (RT_SUCCESS(rcIprt) && rcOssl == VERR_CR_PKIX_OSSL_CIPHER_ALGO_NOT_KNOWN_EVP) )
+ return rcIprt;
+ AssertMsgFailed(("rcIprt=%Rrc rcOssl=%Rrc\n", rcIprt, rcOssl));
+ if (RT_FAILURE_NP(rcOssl))
+ return rcOssl;
+#endif /* IPRT_WITH_OPENSSL */
+
+ return rcIprt;
+}
+
diff --git a/src/VBox/Runtime/common/crypto/pkix-signature-builtin.cpp b/src/VBox/Runtime/common/crypto/pkix-signature-builtin.cpp
new file mode 100644
index 00000000..56c62064
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkix-signature-builtin.cpp
@@ -0,0 +1,153 @@
+/* $Id: pkix-signature-builtin.cpp $ */
+/** @file
+ * IPRT - Crypto - Public Key Signature Schemas, Built-in providers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pkix.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+#ifdef IPRT_WITH_OPENSSL
+# include "internal/iprt-openssl.h"
+# include "internal/openssl-pre.h"
+# include <openssl/evp.h>
+# include "internal/openssl-post.h"
+#endif
+
+#include "pkix-signature-builtin.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Array of built in message digest vtables.
+ */
+static PCRTCRPKIXSIGNATUREDESC const g_apPkixSignatureDescriptors[] =
+{
+ &g_rtCrPkixSigningHashWithRsaDesc,
+#ifdef IPRT_WITH_OPENSSL
+ &g_rtCrPkixSigningHashWithEcdsaDesc,
+#endif
+};
+
+
+
+PCRTCRPKIXSIGNATUREDESC RTCrPkixSignatureFindByObjIdString(const char *pszObjId, void **ppvOpaque)
+{
+ if (ppvOpaque)
+ *ppvOpaque = NULL;
+
+ /*
+ * Primary OIDs.
+ */
+ uint32_t i = RT_ELEMENTS(g_apPkixSignatureDescriptors);
+ while (i-- > 0)
+ if (strcmp(g_apPkixSignatureDescriptors[i]->pszObjId, pszObjId) == 0)
+ return g_apPkixSignatureDescriptors[i];
+
+ /*
+ * Alias OIDs.
+ */
+ i = RT_ELEMENTS(g_apPkixSignatureDescriptors);
+ while (i-- > 0)
+ {
+ const char * const *ppszAliases = g_apPkixSignatureDescriptors[i]->papszObjIdAliases;
+ if (ppszAliases)
+ for (; *ppszAliases; ppszAliases++)
+ if (strcmp(*ppszAliases, pszObjId) == 0)
+ return g_apPkixSignatureDescriptors[i];
+ }
+
+#if 0//def IPRT_WITH_OPENSSL
+ /*
+ * Try EVP and see if it knows the algorithm.
+ */
+ if (ppvOpaque)
+ {
+ rtCrOpenSslInit();
+ int iAlgoNid = OBJ_txt2nid(pszObjId);
+ if (iAlgoNid != NID_undef)
+ {
+ const char *pszAlogSn = OBJ_nid2sn(iAlgoNid);
+ const EVP_MD *pEvpMdType = EVP_get_digestbyname(pszAlogSn);
+ if (pEvpMdType)
+ {
+ /*
+ * Return the OpenSSL provider descriptor and the EVP_MD address.
+ */
+ Assert(pEvpMdType->md_size);
+ *ppvOpaque = (void *)pEvpMdType;
+ return &g_rtCrPkixSignatureOpenSslDesc;
+ }
+ }
+ }
+#endif
+ return NULL;
+}
+
+
+PCRTCRPKIXSIGNATUREDESC RTCrPkixSignatureFindByObjId(PCRTASN1OBJID pObjId, void **ppvOpaque)
+{
+ return RTCrPkixSignatureFindByObjIdString(pObjId->szObjId, ppvOpaque);
+}
+
+
+RTDECL(int) RTCrPkixSignatureCreateByObjIdString(PRTCRPKIXSIGNATURE phSignature, const char *pszObjId,
+ RTCRKEY hKey, PCRTASN1DYNTYPE pParams, bool fSigning)
+{
+ void *pvOpaque;
+ PCRTCRPKIXSIGNATUREDESC pDesc = RTCrPkixSignatureFindByObjIdString(pszObjId, &pvOpaque);
+ if (pDesc)
+ return RTCrPkixSignatureCreate(phSignature, pDesc, pvOpaque, fSigning, hKey, pParams);
+ return VERR_NOT_FOUND;
+}
+
+
+RTDECL(int) RTCrPkixSignatureCreateByObjId(PRTCRPKIXSIGNATURE phSignature, PCRTASN1OBJID pObjId,
+ RTCRKEY hKey, PCRTASN1DYNTYPE pParams, bool fSigning)
+{
+ void *pvOpaque;
+ PCRTCRPKIXSIGNATUREDESC pDesc = RTCrPkixSignatureFindByObjId(pObjId, &pvOpaque);
+ if (pDesc)
+ return RTCrPkixSignatureCreate(phSignature, pDesc, pvOpaque, fSigning, hKey, pParams);
+ return VERR_NOT_FOUND;
+}
+
diff --git a/src/VBox/Runtime/common/crypto/pkix-signature-builtin.h b/src/VBox/Runtime/common/crypto/pkix-signature-builtin.h
new file mode 100644
index 00000000..0d8f297c
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkix-signature-builtin.h
@@ -0,0 +1,51 @@
+/* $Id: pkix-signature-builtin.h $ */
+/** @file
+ * IPRT - Crypto - Public Key Signature Schemas, Built-in providers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_common_crypto_pkix_signature_builtin_h
+#define IPRT_INCLUDED_SRC_common_crypto_pkix_signature_builtin_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/crypto/pkix.h>
+
+extern DECL_HIDDEN_DATA(RTCRPKIXSIGNATUREDESC const) g_rtCrPkixSigningHashWithRsaDesc;
+#ifdef IPRT_WITH_OPENSSL
+extern DECL_HIDDEN_DATA(RTCRPKIXSIGNATUREDESC const) g_rtCrPkixSigningHashWithEcdsaDesc;
+#endif
+
+#endif /* !IPRT_INCLUDED_SRC_common_crypto_pkix_signature_builtin_h */
+
diff --git a/src/VBox/Runtime/common/crypto/pkix-signature-core.cpp b/src/VBox/Runtime/common/crypto/pkix-signature-core.cpp
new file mode 100644
index 00000000..ab755a17
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkix-signature-core.cpp
@@ -0,0 +1,294 @@
+/* $Id: pkix-signature-core.cpp $ */
+/** @file
+ * IPRT - Crypto - Public Key Signature Schema Algorithm, Core API.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pkix.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/crypto/digest.h>
+#include <iprt/crypto/key.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Generic public key signature scheme instance.
+ */
+typedef struct RTCRPKIXSIGNATUREINT
+{
+ /** Magic value (RTCRPKIXSIGNATUREINT_MAGIC). */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** Pointer to the message digest descriptor. */
+ PCRTCRPKIXSIGNATUREDESC pDesc;
+ /** Key being used (referrenced of course). */
+ RTCRKEY hKey;
+ /** The operation this instance is licensed for. */
+ bool fSigning;
+ /** State. */
+ uint32_t uState;
+
+ /** Opaque data specific to the message digest algorithm, size given by
+ * RTCRPKIXSIGNATUREDESC::cbState. */
+ uint8_t abState[1];
+} RTCRPKIXSIGNATUREINT;
+AssertCompileMemberAlignment(RTCRPKIXSIGNATUREINT, abState, 8);
+/** Pointer to a public key algorithm instance. */
+typedef RTCRPKIXSIGNATUREINT *PRTCRPKIXSIGNATUREINT;
+
+/** Magic value for RTCRPKIXSIGNATUREINT::u32Magic (Baley Withfield Diffie). */
+#define RTCRPKIXSIGNATUREINT_MAGIC UINT32_C(0x19440605)
+
+/** @name RTCRPKIXSIGNATUREINT::uState values.
+ * @{ */
+/** Ready to go. */
+#define RTCRPKIXSIGNATURE_STATE_READY UINT32_C(1)
+/** Need reset. */
+#define RTCRPKIXSIGNATURE_STATE_DONE UINT32_C(2)
+/** Busted state, can happen after re-init. */
+#define RTCRPKIXSIGNATURE_STATE_BUSTED UINT32_C(3)
+/** @} */
+
+
+
+RTDECL(int) RTCrPkixSignatureCreate(PRTCRPKIXSIGNATURE phSignature, PCRTCRPKIXSIGNATUREDESC pDesc, void *pvOpaque,
+ bool fSigning, RTCRKEY hKey, PCRTASN1DYNTYPE pParams)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(phSignature, VERR_INVALID_POINTER);
+ AssertPtrReturn(pDesc, VERR_INVALID_POINTER);
+ uint32_t cKeyRefs = RTCrKeyRetain(hKey);
+ AssertReturn(cKeyRefs != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ /*
+ * Instantiate the algorithm for the given operation.
+ */
+ int rc = VINF_SUCCESS;
+ PRTCRPKIXSIGNATUREINT pThis = (PRTCRPKIXSIGNATUREINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTCRPKIXSIGNATUREINT,
+ abState[pDesc->cbState]));
+ if (pThis)
+ {
+ pThis->u32Magic = RTCRPKIXSIGNATUREINT_MAGIC;
+ pThis->cRefs = 1;
+ pThis->pDesc = pDesc;
+ pThis->fSigning = fSigning;
+ pThis->uState = RTCRPKIXSIGNATURE_STATE_READY;
+ pThis->hKey = hKey;
+ if (pDesc->pfnInit)
+ rc = pDesc->pfnInit(pDesc, pThis->abState, pvOpaque, fSigning, hKey, pParams);
+ else
+ rc = RTCrKeyVerifyParameterCompatibility(hKey, pParams, true /*fForSignature*/,
+ NULL /*pAlgorithm*/, NULL /*pErrInfo*/);
+ if (RT_SUCCESS(rc))
+ {
+ *phSignature = pThis;
+ return VINF_SUCCESS;
+ }
+ pThis->u32Magic = 0;
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ RTCrKeyRelease(hKey);
+ return rc;
+
+}
+
+
+RTDECL(uint32_t) RTCrPkixSignatureRetain(RTCRPKIXSIGNATURE hSignature)
+{
+ PRTCRPKIXSIGNATUREINT pThis = hSignature;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRPKIXSIGNATUREINT_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs < 64);
+ return cRefs;
+}
+
+
+/**
+ * Destructor worker.
+ */
+static uint32_t rtCrPkixSignatureDestructor(PRTCRPKIXSIGNATUREINT pThis)
+{
+ pThis->u32Magic = ~RTCRPKIXSIGNATUREINT_MAGIC;
+ if (pThis->pDesc->pfnDelete)
+ pThis->pDesc->pfnDelete(pThis->pDesc, pThis->abState, pThis->fSigning);
+
+ RTCrKeyRelease(pThis->hKey);
+ pThis->hKey = NIL_RTCRKEY;
+
+ size_t cbToWipe = RT_UOFFSETOF_DYN(RTCRPKIXSIGNATUREINT, abState[pThis->pDesc->cbState]);
+ RTMemWipeThoroughly(pThis, cbToWipe, 6);
+
+ RTMemFree(pThis);
+ return 0;
+}
+
+
+RTDECL(uint32_t) RTCrPkixSignatureRelease(RTCRPKIXSIGNATURE hSignature)
+{
+ PRTCRPKIXSIGNATUREINT pThis = hSignature;
+ if (pThis == NIL_RTCRPKIXSIGNATURE)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRPKIXSIGNATUREINT_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ Assert(cRefs < 64);
+ if (!cRefs)
+ return rtCrPkixSignatureDestructor(pThis);
+ return cRefs;
+}
+
+
+/**
+ * Resets the signature provider instance prior to a new signing or verification
+ * opartion.
+ *
+ * @returns IPRT status code.
+ * @param pThis The instance to reset.
+ */
+static int rtCrPkxiSignatureReset(PRTCRPKIXSIGNATUREINT pThis)
+{
+ if (pThis->uState == RTCRPKIXSIGNATURE_STATE_DONE)
+ {
+ if (pThis->pDesc->pfnReset)
+ {
+ int rc = pThis->pDesc->pfnReset(pThis->pDesc, pThis->abState, pThis->fSigning);
+ if (RT_FAILURE(rc))
+ {
+ pThis->uState = RTCRPKIXSIGNATURE_STATE_BUSTED;
+ return rc;
+ }
+ }
+ pThis->uState = RTCRPKIXSIGNATURE_STATE_READY;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCrPkixSignatureVerify(RTCRPKIXSIGNATURE hSignature, RTCRDIGEST hDigest,
+ void const *pvSignature, size_t cbSignature)
+{
+ PRTCRPKIXSIGNATUREINT pThis = hSignature;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRPKIXSIGNATUREINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fSigning, VERR_INVALID_FUNCTION);
+ AssertReturn(pThis->uState == RTCRPKIXSIGNATURE_STATE_READY || pThis->uState == RTCRPKIXSIGNATURE_STATE_DONE, VERR_INVALID_STATE);
+
+ uint32_t cRefs = RTCrDigestRetain(hDigest);
+ AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ int rc = rtCrPkxiSignatureReset(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ rc = pThis->pDesc->pfnVerify(pThis->pDesc, pThis->abState, pThis->hKey, hDigest, pvSignature, cbSignature);
+ pThis->uState = RTCRPKIXSIGNATURE_STATE_DONE;
+ }
+
+ RTCrDigestRelease(hDigest);
+ return rc;
+}
+
+
+RTDECL(int) RTCrPkixSignatureVerifyBitString(RTCRPKIXSIGNATURE hSignature, RTCRDIGEST hDigest, PCRTASN1BITSTRING pSignature)
+{
+ /*
+ * Just unpack it and pass it on to the lower level API.
+ */
+ AssertPtrReturn(pSignature, VERR_INVALID_POINTER);
+ AssertReturn(RTAsn1BitString_IsPresent(pSignature), VERR_INVALID_PARAMETER);
+ uint32_t cbData = RTASN1BITSTRING_GET_BYTE_SIZE(pSignature);
+ void const *pvData = RTASN1BITSTRING_GET_BIT0_PTR(pSignature);
+ AssertPtrReturn(pvData, VERR_INVALID_PARAMETER);
+
+ return RTCrPkixSignatureVerify(hSignature, hDigest, pvData, cbData);
+}
+
+
+RTDECL(int) RTCrPkixSignatureVerifyOctetString(RTCRPKIXSIGNATURE hSignature, RTCRDIGEST hDigest, PCRTASN1OCTETSTRING pSignature)
+{
+ /*
+ * Just unpack it and pass it on to the lower level API.
+ */
+ AssertPtrReturn(pSignature, VERR_INVALID_POINTER);
+ AssertReturn(RTAsn1OctetString_IsPresent(pSignature), VERR_INVALID_PARAMETER);
+ uint32_t cbData = pSignature->Asn1Core.cb;
+ void const *pvData = pSignature->Asn1Core.uData.pv;
+ AssertPtrReturn(pvData, VERR_INVALID_PARAMETER);
+
+ return RTCrPkixSignatureVerify(hSignature, hDigest, pvData, cbData);
+}
+
+
+RTDECL(int) RTCrPkixSignatureSign(RTCRPKIXSIGNATURE hSignature, RTCRDIGEST hDigest,
+ void *pvSignature, size_t *pcbSignature)
+{
+ PRTCRPKIXSIGNATUREINT pThis = hSignature;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRPKIXSIGNATUREINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fSigning, VERR_INVALID_FUNCTION);
+ AssertReturn(pThis->uState == RTCRPKIXSIGNATURE_STATE_READY || pThis->uState == RTCRPKIXSIGNATURE_STATE_DONE, VERR_INVALID_STATE);
+
+ uint32_t cRefs = RTCrDigestRetain(hDigest);
+ AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ int rc = rtCrPkxiSignatureReset(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ rc = pThis->pDesc->pfnSign(pThis->pDesc, pThis->abState, pThis->hKey, hDigest, pvSignature, pcbSignature);
+ if (rc != VERR_BUFFER_OVERFLOW)
+ pThis->uState = RTCRPKIXSIGNATURE_STATE_DONE;
+ }
+
+ RTCrDigestRelease(hDigest);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/crypto/pkix-signature-ossl.cpp b/src/VBox/Runtime/common/crypto/pkix-signature-ossl.cpp
new file mode 100644
index 00000000..17af5a15
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkix-signature-ossl.cpp
@@ -0,0 +1,288 @@
+/* $Id: pkix-signature-ossl.cpp $ */
+/** @file
+ * IPRT - Crypto - Public Key Signature Schema Algorithm, ECDSA Providers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef IPRT_WITH_OPENSSL /* whole file */
+# define LOG_GROUP RTLOGGROUP_CRYPTO
+# include "internal/iprt.h"
+# include <iprt/crypto/rsa.h>
+
+# include <iprt/bignum.h>
+# include <iprt/err.h>
+# include <iprt/log.h>
+# include <iprt/mem.h>
+# include <iprt/string.h>
+# include <iprt/crypto/digest.h>
+# include <iprt/crypto/pkix.h>
+
+# include "internal/iprt-openssl.h"
+# include "internal/openssl-pre.h"
+# include <openssl/evp.h>
+# include "internal/openssl-post.h"
+# ifndef OPENSSL_VERSION_NUMBER
+# error "Missing OPENSSL_VERSION_NUMBER!"
+# endif
+
+# include "pkix-signature-builtin.h"
+# include "rsa-internal.h"
+# include "key-internal.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * OpenSSL EVP signature provider instance.
+ */
+typedef struct RTCRPKIXSIGNATUREOSSLEVP
+{
+ /** Set if we're signing, clear if verifying. */
+ bool fSigning;
+} RTCRPKIXSIGNATUREOSSLEVP;
+/** Pointer to an OpenSSL EVP signature provider instance. */
+typedef RTCRPKIXSIGNATUREOSSLEVP *PRTCRPKIXSIGNATUREOSSLEVP;
+
+
+
+/** @impl_interface_method{RTCRPKIXSIGNATUREDESC,pfnInit} */
+static DECLCALLBACK(int) rtCrPkixSignatureOsslEvp_Init(PCRTCRPKIXSIGNATUREDESC pDesc, void *pvState, void *pvOpaque,
+ bool fSigning, RTCRKEY hKey, PCRTASN1DYNTYPE pParams)
+{
+ RT_NOREF_PV(pvOpaque);
+
+ RTCRKEYTYPE enmKeyType = RTCrKeyGetType(hKey);
+ if (strcmp(pDesc->pszObjId, RTCR_X962_ECDSA_OID) == 0)
+ {
+ if (fSigning)
+ AssertReturn(enmKeyType == RTCRKEYTYPE_ECDSA_PRIVATE, VERR_CR_PKIX_NOT_ECDSA_PRIVATE_KEY);
+ else
+ AssertReturn(enmKeyType == RTCRKEYTYPE_ECDSA_PUBLIC, VERR_CR_PKIX_NOT_ECDSA_PUBLIC_KEY);
+ }
+ else if (strcmp(pDesc->pszObjId, RTCR_PKCS1_RSA_OID) == 0)
+ {
+ if (fSigning)
+ AssertReturn(enmKeyType == RTCRKEYTYPE_RSA_PRIVATE, VERR_CR_PKIX_NOT_RSA_PRIVATE_KEY);
+ else
+ AssertReturn(enmKeyType == RTCRKEYTYPE_RSA_PUBLIC, VERR_CR_PKIX_NOT_RSA_PUBLIC_KEY);
+ }
+ else
+ AssertFailedReturn(VERR_INTERNAL_ERROR_3);
+ int rc = RTCrKeyVerifyParameterCompatibility(hKey, pParams, true /*fForSignature*/, NULL /*pAlgorithm*/, NULL /*pErrInfo*/);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Locate the EVP_MD for the algorithm.
+ */
+ rtCrOpenSslInit();
+ int iAlgoNid = OBJ_txt2nid(pDesc->pszObjId);
+ if (iAlgoNid != NID_undef)
+ {
+ PRTCRPKIXSIGNATUREOSSLEVP pThis = (PRTCRPKIXSIGNATUREOSSLEVP)pvState;
+ pThis->fSigning = fSigning;
+ return VINF_SUCCESS;
+ }
+ return VERR_CR_PKIX_OSSL_CIPHER_ALGO_NOT_KNOWN_EVP;
+}
+
+
+/** @impl_interface_method{RTCRPKIXSIGNATUREDESC,pfnReset} */
+static DECLCALLBACK(int) rtCrPkixSignatureOsslEvp_Reset(PCRTCRPKIXSIGNATUREDESC pDesc, void *pvState, bool fSigning)
+{
+ PRTCRPKIXSIGNATUREOSSLEVP pThis = (PRTCRPKIXSIGNATUREOSSLEVP)pvState;
+ RT_NOREF_PV(fSigning); RT_NOREF_PV(pDesc);
+ Assert(pThis->fSigning == fSigning); NOREF(pThis);
+ return VINF_SUCCESS;
+}
+
+
+/** @impl_interface_method{RTCRPKIXSIGNATUREDESC,pfnDelete} */
+static DECLCALLBACK(void) rtCrPkixSignatureOsslEvp_Delete(PCRTCRPKIXSIGNATUREDESC pDesc, void *pvState, bool fSigning)
+{
+ PRTCRPKIXSIGNATUREOSSLEVP pThis = (PRTCRPKIXSIGNATUREOSSLEVP)pvState;
+ RT_NOREF_PV(fSigning); RT_NOREF_PV(pDesc);
+ Assert(pThis->fSigning == fSigning);
+ NOREF(pThis);
+}
+
+
+/** @impl_interface_method{RTCRPKIXSIGNATUREDESC,pfnVerify} */
+static DECLCALLBACK(int) rtCrPkixSignatureOsslEvp_Verify(PCRTCRPKIXSIGNATUREDESC pDesc, void *pvState, RTCRKEY hKey,
+ RTCRDIGEST hDigest, void const *pvSignature, size_t cbSignature)
+{
+ PRTCRPKIXSIGNATUREOSSLEVP pThis = (PRTCRPKIXSIGNATUREOSSLEVP)pvState;
+ Assert(!pThis->fSigning);
+ RT_NOREF_PV(pThis);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10000000
+ PRTERRINFO const pErrInfo = NULL;
+
+ /*
+ * Get the hash before we do anything that needs cleaning up.
+ */
+ int rc = RTCrDigestFinal(hDigest, NULL, 0);
+ AssertRCReturn(rc, rc);
+ uint8_t const * const pbDigest = RTCrDigestGetHash(hDigest);
+ AssertReturn(pbDigest, VERR_INTERNAL_ERROR_3);
+
+ uint32_t const cbDigest = RTCrDigestGetHashSize(hDigest);
+ AssertReturn(cbDigest > 0 && cbDigest < _16K, VERR_INTERNAL_ERROR_4);
+
+ /*
+ * Combine the encryption and digest algorithms.
+ */
+ const char * const pszDigestOid = RTCrDigestGetAlgorithmOid(hDigest);
+ const char * const pszEncryptedDigestOid
+ = RTCrX509AlgorithmIdentifier_CombineEncryptionOidAndDigestOid(pDesc->pszObjId, pszDigestOid);
+
+ /*
+ * Create an EVP public key from hKey and pszEncryptedDigestOid.
+ */
+ int const iAlgoNid = OBJ_txt2nid(pszEncryptedDigestOid);
+ if (iAlgoNid == NID_undef)
+ return VERR_CR_PKIX_OSSL_CIPHER_ALGO_NOT_KNOWN_EVP;
+
+ /* Create an EVP public key. */
+ EVP_PKEY *pEvpPublicKey = NULL;
+ const EVP_MD *pEvpMdType = NULL;
+ rc = rtCrKeyToOpenSslKeyEx(hKey, true /*fNeedPublic*/, pszEncryptedDigestOid,
+ (void **)&pEvpPublicKey, (const void **)&pEvpMdType, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+# if OPENSSL_VERSION_NUMBER >= 0x30000000 && !defined(LIBRESSL_VERSION_NUMBER)
+ EVP_PKEY_CTX * const pEvpPublickKeyCtx = EVP_PKEY_CTX_new_from_pkey(NULL, pEvpPublicKey, NULL);
+# else
+ EVP_PKEY_CTX * const pEvpPublickKeyCtx = EVP_PKEY_CTX_new(pEvpPublicKey, NULL);
+# endif
+ if (pEvpPublickKeyCtx)
+ {
+ rc = EVP_PKEY_verify_init(pEvpPublickKeyCtx);
+ if (rc > 0)
+ {
+ rc = EVP_PKEY_CTX_set_signature_md(pEvpPublickKeyCtx, pEvpMdType);
+ if (rc > 0)
+ {
+ rc = EVP_PKEY_verify(pEvpPublickKeyCtx, (const unsigned char *)pvSignature, cbSignature, pbDigest, cbDigest);
+ if (rc > 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_PKIX_OSSL_VERIFY_FINAL_FAILED,
+ "EVP_PKEY_verify failed (%d)", rc);
+ }
+ else
+ rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_PKIX_OSSL_CIPHER_ALOG_INIT_FAILED,
+ "EVP_PKEY_CTX_set_signature_md failed (%d)", rc);
+ }
+ else
+ rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_PKIX_OSSL_CIPHER_ALOG_INIT_FAILED,
+ "EVP_PKEY_verify_init failed (%d)", rc);
+ EVP_PKEY_CTX_free(pEvpPublickKeyCtx);
+ }
+ else
+ rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_CR_PKIX_OSSL_CIPHER_ALOG_INIT_FAILED,
+ "EVP_PKEY_CTX_new_from_pkey failed (%d)", rc);
+ EVP_PKEY_free(pEvpPublicKey);
+ }
+ return rc;
+
+#else
+ RT_NOREF(pDesc, pvState, hKey, hDigest, pvSignature, cbSignature);
+ return VERR_CR_OPENSSL_VERSION_TOO_OLD;
+#endif
+}
+
+
+/** @impl_interface_method{RTCRPKIXSIGNATUREDESC,pfnSign} */
+static DECLCALLBACK(int) rtCrPkixSignatureOsslEvp_Sign(PCRTCRPKIXSIGNATUREDESC pDesc, void *pvState, RTCRKEY hKey,
+ RTCRDIGEST hDigest, void *pvSignature, size_t *pcbSignature)
+{
+ PRTCRPKIXSIGNATUREOSSLEVP pThis = (PRTCRPKIXSIGNATUREOSSLEVP)pvState;
+ Assert(pThis->fSigning);
+ RT_NOREF(pThis, pDesc);
+
+#if 0
+ /*
+ * Get the key bits we need.
+ */
+ Assert(RTCrKeyGetType(hKey) == RTCRKEYTYPE_RSA_PRIVATE);
+ PRTBIGNUM pModulus = &hKey->u.RsaPrivate.Modulus;
+ PRTBIGNUM pExponent = &hKey->u.RsaPrivate.PrivateExponent;
+
+ return rc;
+#else
+ RT_NOREF(pDesc, pvState, hKey, hDigest, pvSignature, pcbSignature);
+ return VERR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/** ECDSA alias ODIs. */
+static const char * const g_apszHashWithEcdsaAliases[] =
+{
+ RTCR_X962_ECDSA_WITH_SHA1_OID,
+ RTCR_X962_ECDSA_WITH_SHA2_OID,
+ RTCR_X962_ECDSA_WITH_SHA224_OID,
+ RTCR_X962_ECDSA_WITH_SHA256_OID,
+ RTCR_X962_ECDSA_WITH_SHA384_OID,
+ RTCR_X962_ECDSA_WITH_SHA512_OID,
+ RTCR_NIST_SHA3_224_WITH_ECDSA_OID,
+ RTCR_NIST_SHA3_256_WITH_ECDSA_OID,
+ RTCR_NIST_SHA3_384_WITH_ECDSA_OID,
+ RTCR_NIST_SHA3_512_WITH_ECDSA_OID,
+ NULL
+};
+
+
+/** ECDSA descriptor. */
+DECL_HIDDEN_CONST(RTCRPKIXSIGNATUREDESC const) g_rtCrPkixSigningHashWithEcdsaDesc =
+{
+ "ECDSA",
+ RTCR_X962_ECDSA_OID,
+ g_apszHashWithEcdsaAliases,
+ sizeof(RTCRPKIXSIGNATUREOSSLEVP),
+ 0,
+ 0,
+ rtCrPkixSignatureOsslEvp_Init,
+ rtCrPkixSignatureOsslEvp_Reset,
+ rtCrPkixSignatureOsslEvp_Delete,
+ rtCrPkixSignatureOsslEvp_Verify,
+ rtCrPkixSignatureOsslEvp_Sign,
+};
+#endif /* IPRT_WITH_OPENSSL */
+
diff --git a/src/VBox/Runtime/common/crypto/pkix-signature-rsa.cpp b/src/VBox/Runtime/common/crypto/pkix-signature-rsa.cpp
new file mode 100644
index 00000000..b0b279fc
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkix-signature-rsa.cpp
@@ -0,0 +1,565 @@
+/* $Id: pkix-signature-rsa.cpp $ */
+/** @file
+ * IPRT - Crypto - Public Key Signature Schema Algorithm, RSA Providers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/rsa.h>
+
+#include <iprt/bignum.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/crypto/digest.h>
+#include <iprt/crypto/pkix.h>
+
+#include "rsa-internal.h"
+#include "pkix-signature-builtin.h"
+#include "key-internal.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * RSA signature provider instance.
+ */
+typedef struct RTCRPKIXSIGNATURERSA
+{
+ /** Set if we're signing, clear if verifying. */
+ bool fSigning;
+
+ /** Temporary big number for use when signing or verifiying. */
+ RTBIGNUM TmpBigNum1;
+ /** Temporary big number for use when signing or verifiying. */
+ RTBIGNUM TmpBigNum2;
+
+ /** Scratch space for decoding the key. */
+ union
+ {
+ /** Public key. */
+ RTCRRSAPUBLICKEY PublicKey;
+ /** Private key. */
+ RTCRRSAPRIVATEKEY PrivateKey;
+ /** Scratch area where we assemble the signature. */
+ uint8_t abSignature[RTCRRSA_MAX_MODULUS_BITS / 8 * 2];
+ } Scratch;
+} RTCRPKIXSIGNATURERSA;
+/** Pointer to an RSA signature provider instance. */
+typedef RTCRPKIXSIGNATURERSA *PRTCRPKIXSIGNATURERSA;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** @name Pre-encoded DigestInfo DER sequences.
+ * @{ */
+static const uint8_t g_abMd2[] =
+{/* { { 1.2.840.113549.2.2 (MD2), NULL }, hash octet-string } */
+ 0x30,0x20, 0x30,0x0c, 0x06,0x08,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x02, 0x05,0x00, 0x04,0x10
+};
+static const uint8_t g_abMd4[] =
+{/* { { 1.2.840.113549.2.4 (MD4), NULL }, hash octet-string } */
+ 0x30,0x20, 0x30,0x0c, 0x06,0x08,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x04, 0x05,0x00, 0x04,0x10
+};
+static const uint8_t g_abMd5[] =
+{/* { { 1.2.840.113549.2.5 (MD5), NULL }, hash octet-string } */
+ 0x30,0x20, 0x30,0x0c, 0x06,0x08,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x05, 0x05,0x00, 0x04,0x10
+};
+static const uint8_t g_abSha1[] =
+{/* { { 1.3.14.3.2.26 (SHA-1), NULL }, hash octet-string } */
+ 0x30,0x21, 0x30,0x09, 0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a, 0x05,0x00, 0x04,0x14
+};
+static const uint8_t g_abSha256[] =
+{/* { { 2.16.840.1.101.3.4.2.1 (SHA-256), NULL }, hash octet-string } */
+ 0x30,0x31, 0x30,0x0d, 0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01, 0x05,0x00, 0x04,0x20
+};
+static const uint8_t g_abSha384[] =
+{/* { { 2.16.840.1.101.3.4.2.2 (SHA-384), NULL }, hash octet-string } */
+ 0x30,0x41, 0x30,0x0d, 0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x02, 0x05,0x00, 0x04,0x30
+};
+static const uint8_t g_abSha512[] =
+{/* { { 2.16.840.1.101.3.4.2.3 (SHA-512), NULL }, hash octet-string } */
+ 0x30,0x51, 0x30,0x0d, 0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03, 0x05,0x00, 0x04,0x40
+};
+static const uint8_t g_abSha224[] =
+{/* { { 2.16.840.1.101.3.4.2.4 (SHA-224), NULL }, hash octet-string } */
+ 0x30,0x2d, 0x30,0x0d, 0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x04, 0x05,0x00, 0x04,0x1c
+};
+static const uint8_t g_abSha512t224[] =
+{/* { { 2.16.840.1.101.3.4.2.5 (SHA-512T224), NULL }, hash octet-string } */
+ 0x30,0x2d, 0x30,0x0d, 0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x05, 0x05,0x00, 0x04,0x1c
+};
+static const uint8_t g_abSha512t256[] =
+{/* { { 2.16.840.1.101.3.4.2.6 (SHA-512T256), NULL }, hash octet-string } */
+ 0x30,0x31, 0x30,0x0d, 0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x06, 0x05,0x00, 0x04,0x20
+};
+static const uint8_t g_abSha3t224[] =
+{/* { { 2.16.840.1.101.3.4.2.7 (SHA3-224), NULL }, hash octet-string } */
+ 0x30,0x2d, 0x30,0x0d, 0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x07, 0x05,0x00, 0x04,0x1c
+};
+static const uint8_t g_abSha3t256[] =
+{/* { { 2.16.840.1.101.3.4.2.8 (SHA3-256), NULL }, hash octet-string } */
+ 0x30,0x31, 0x30,0x0d, 0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x08, 0x05,0x00, 0x04,0x20
+};
+static const uint8_t g_abSha3t384[] =
+{/* { { 2.16.840.1.101.3.4.2.9 (SHA3-384), NULL }, hash octet-string } */
+ 0x30,0x41, 0x30,0x0d, 0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x09, 0x05,0x00, 0x04,0x30
+};
+static const uint8_t g_abSha3t512[] =
+{/* { { 2.16.840.1.101.3.4.2.10 (SHA3-512), NULL }, hash octet-string } */
+ 0x30,0x51, 0x30,0x0d, 0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x0a, 0x05,0x00, 0x04,0x40
+};
+/** @} */
+
+/** Lookup array for the pre-encoded DigestInfo DER sequences. */
+static struct
+{
+ RTDIGESTTYPE enmDigest;
+ const uint8_t *pb;
+ size_t cb;
+} const g_aDigestInfos[] =
+{
+ { RTDIGESTTYPE_SHA1, g_abSha1, sizeof(g_abSha1) },
+ { RTDIGESTTYPE_SHA256, g_abSha256, sizeof(g_abSha256) },
+ { RTDIGESTTYPE_SHA512, g_abSha512, sizeof(g_abSha512) },
+ { RTDIGESTTYPE_MD2, g_abMd2, sizeof(g_abMd2) },
+ { RTDIGESTTYPE_MD4, g_abMd4, sizeof(g_abMd4) },
+ { RTDIGESTTYPE_MD5, g_abMd5, sizeof(g_abMd5) },
+ { RTDIGESTTYPE_SHA384, g_abSha384, sizeof(g_abSha384) },
+ { RTDIGESTTYPE_SHA224, g_abSha224, sizeof(g_abSha224) },
+ { RTDIGESTTYPE_SHA512T224, g_abSha512t224, sizeof(g_abSha512t224)},
+ { RTDIGESTTYPE_SHA512T256, g_abSha512t256, sizeof(g_abSha512t256)},
+ { RTDIGESTTYPE_SHA3_224, g_abSha3t224, sizeof(g_abSha3t224) },
+ { RTDIGESTTYPE_SHA3_256, g_abSha3t256, sizeof(g_abSha3t256) },
+ { RTDIGESTTYPE_SHA3_384, g_abSha3t384, sizeof(g_abSha3t384) },
+ { RTDIGESTTYPE_SHA3_512, g_abSha3t512, sizeof(g_abSha3t512) },
+};
+
+
+/** @impl_interface_method{RTCRPKIXSIGNATUREDESC,pfnInit} */
+static DECLCALLBACK(int) rtCrPkixSignatureRsa_Init(PCRTCRPKIXSIGNATUREDESC pDesc, void *pvState, void *pvOpaque,
+ bool fSigning, RTCRKEY hKey, PCRTASN1DYNTYPE pParams)
+{
+ RT_NOREF_PV(pDesc); RT_NOREF_PV(pvState); RT_NOREF_PV(pvOpaque);
+
+ if ( !pParams
+ || pParams->enmType == RTASN1TYPE_NULL
+ || pParams->enmType == RTASN1TYPE_NOT_PRESENT)
+ { /* likely */ }
+ else
+ return VERR_CR_PKIX_SIGNATURE_TAKES_NO_PARAMETERS;
+
+ RTCRKEYTYPE enmKeyType = RTCrKeyGetType(hKey);
+ if (fSigning)
+ AssertReturn(enmKeyType == RTCRKEYTYPE_RSA_PRIVATE, VERR_CR_PKIX_NOT_RSA_PRIVATE_KEY);
+ else
+ AssertReturn(enmKeyType == RTCRKEYTYPE_RSA_PUBLIC, VERR_CR_PKIX_NOT_RSA_PUBLIC_KEY);
+
+ PRTCRPKIXSIGNATURERSA pThis = (PRTCRPKIXSIGNATURERSA)pvState;
+ pThis->fSigning = fSigning;
+
+ return VINF_SUCCESS;
+}
+
+
+/** @impl_interface_method{RTCRPKIXSIGNATUREDESC,pfnReset} */
+static DECLCALLBACK(int) rtCrPkixSignatureRsa_Reset(PCRTCRPKIXSIGNATUREDESC pDesc, void *pvState, bool fSigning)
+{
+ PRTCRPKIXSIGNATURERSA pThis = (PRTCRPKIXSIGNATURERSA)pvState;
+ RT_NOREF_PV(fSigning); RT_NOREF_PV(pDesc);
+ Assert(pThis->fSigning == fSigning); NOREF(pThis);
+ return VINF_SUCCESS;
+}
+
+
+/** @impl_interface_method{RTCRPKIXSIGNATUREDESC,pfnDelete} */
+static DECLCALLBACK(void) rtCrPkixSignatureRsa_Delete(PCRTCRPKIXSIGNATUREDESC pDesc, void *pvState, bool fSigning)
+{
+ PRTCRPKIXSIGNATURERSA pThis = (PRTCRPKIXSIGNATURERSA)pvState;
+ RT_NOREF_PV(fSigning); RT_NOREF_PV(pDesc);
+ Assert(pThis->fSigning == fSigning);
+ NOREF(pThis);
+}
+
+
+/**
+ * Common worker for rtCrPkixSignatureRsa_Verify and
+ * rtCrPkixSignatureRsa_Sign that encodes an EMSA-PKCS1-V1_5 signature in
+ * the scratch area.
+ *
+ * This function is referred to as EMSA-PKCS1-v1_5-ENCODE(M,k) in RFC-3447 and
+ * is described in section 9.2
+ *
+ * @returns IPRT status code.
+ * @param pThis The RSA signature provider instance.
+ * @param hDigest The digest which hash to turn into a signature.
+ * @param cbEncodedMsg The desired encoded message length.
+ * @param fNoDigestInfo If true, skip the DigestInfo and encode the digest
+ * without any prefix like described in v1.5 (RFC-2313)
+ * and observed with RSA+MD5 signed timestamps. If
+ * false, include the prefix like v2.0 (RFC-2437)
+ * describes in step in section 9.2.1
+ * (EMSA-PKCS1-v1_5)
+ *
+ * @remarks Must preserve informational status codes!
+ */
+static int rtCrPkixSignatureRsa_EmsaPkcs1V15Encode(PRTCRPKIXSIGNATURERSA pThis, RTCRDIGEST hDigest, size_t cbEncodedMsg,
+ bool fNoDigestInfo)
+{
+ AssertReturn(cbEncodedMsg * 2 <= sizeof(pThis->Scratch), VERR_CR_PKIX_INTERNAL_ERROR);
+
+ /*
+ * Figure out which hash and select the associate prebaked DigestInfo.
+ */
+ RTDIGESTTYPE const enmDigest = RTCrDigestGetType(hDigest);
+ AssertReturn(enmDigest != RTDIGESTTYPE_INVALID && enmDigest != RTDIGESTTYPE_UNKNOWN, VERR_CR_PKIX_UNKNOWN_DIGEST_TYPE);
+ uint8_t const *pbDigestInfoStart = NULL;
+ size_t cbDigestInfoStart = 0;
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_aDigestInfos); i++)
+ if (g_aDigestInfos[i].enmDigest == enmDigest)
+ {
+ pbDigestInfoStart = g_aDigestInfos[i].pb;
+ cbDigestInfoStart = g_aDigestInfos[i].cb;
+ break;
+ }
+ if (!pbDigestInfoStart)
+ return VERR_CR_PKIX_UNKNOWN_DIGEST_TYPE;
+
+ /*
+ * Get the hash size and verify that it matches what we've got in the
+ * precooked DigestInfo. ASSUMES less that 256 bytes of hash.
+ */
+ uint32_t const cbHash = RTCrDigestGetHashSize(hDigest);
+ AssertReturn(cbHash > 0 && cbHash < _16K, VERR_OUT_OF_RANGE);
+ AssertReturn(cbHash == pbDigestInfoStart[cbDigestInfoStart - 1], VERR_CR_PKIX_INTERNAL_ERROR);
+
+ if (fNoDigestInfo)
+ cbDigestInfoStart = 0;
+
+ if (cbDigestInfoStart + cbHash + 11 > cbEncodedMsg)
+ return VERR_CR_PKIX_HASH_TOO_LONG_FOR_KEY;
+
+ /*
+ * Encode the message the first part of the scratch area.
+ */
+ uint8_t *pbDst = &pThis->Scratch.abSignature[0];
+ pbDst[0] = 0x00;
+ pbDst[1] = 0x01; /* BT - block type, see RFC-2313. */
+ size_t cbFFs = cbEncodedMsg - cbHash - cbDigestInfoStart - 3;
+ memset(&pbDst[2], 0xff, cbFFs);
+ pbDst += cbFFs + 2;
+ *pbDst++ = 0x00;
+ memcpy(pbDst, pbDigestInfoStart, cbDigestInfoStart);
+ pbDst += cbDigestInfoStart;
+ /* Note! Must preserve informational status codes from this call . */
+ int rc = RTCrDigestFinal(hDigest, pbDst, cbHash);
+ if (RT_SUCCESS(rc))
+ {
+ pbDst += cbHash;
+ Assert((size_t)(pbDst - &pThis->Scratch.abSignature[0]) == cbEncodedMsg);
+ }
+ return rc;
+}
+
+
+
+/** @impl_interface_method{RTCRPKIXSIGNATUREDESC,pfnVerify} */
+static DECLCALLBACK(int) rtCrPkixSignatureRsa_Verify(PCRTCRPKIXSIGNATUREDESC pDesc, void *pvState, RTCRKEY hKey,
+ RTCRDIGEST hDigest, void const *pvSignature, size_t cbSignature)
+{
+ PRTCRPKIXSIGNATURERSA pThis = (PRTCRPKIXSIGNATURERSA)pvState;
+ RT_NOREF_PV(pDesc);
+ Assert(!pThis->fSigning);
+ if (cbSignature > sizeof(pThis->Scratch) / 2)
+ return VERR_CR_PKIX_SIGNATURE_TOO_LONG;
+
+ /*
+ * Get the key bits we need.
+ */
+ Assert(RTCrKeyGetType(hKey) == RTCRKEYTYPE_RSA_PUBLIC);
+ PRTBIGNUM pModulus = &hKey->u.RsaPublic.Modulus;
+ PRTBIGNUM pExponent = &hKey->u.RsaPublic.Exponent;
+
+ /*
+ * 8.2.2.1 - Length check. (RFC-3447)
+ */
+ if (cbSignature != RTBigNumByteWidth(pModulus))
+ return VERR_CR_PKIX_INVALID_SIGNATURE_LENGTH;
+
+ /*
+ * 8.2.2.2 - RSA verification / Decrypt the signature.
+ */
+ /* a) s = OS2IP(S) -- Convert signature to integer. */
+ int rc = RTBigNumInit(&pThis->TmpBigNum1, RTBIGNUMINIT_F_ENDIAN_BIG | RTBIGNUMINIT_F_UNSIGNED,
+ pvSignature, cbSignature);
+ if (RT_FAILURE(rc))
+ return rc;
+ /* b) RSAVP1 - 5.2.2.2: Range check (0 <= s < n). */
+ if (RTBigNumCompare(&pThis->TmpBigNum1, pModulus) < 0)
+ {
+ if (RTBigNumCompareWithU64(&pThis->TmpBigNum1, 0) >= 0)
+ {
+ /* b) RSAVP1 - 5.2.2.3: s^e mod n */
+ rc = RTBigNumInitZero(&pThis->TmpBigNum2, 0);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTBigNumModExp(&pThis->TmpBigNum2, &pThis->TmpBigNum1, pExponent, pModulus);
+ if (RT_SUCCESS(rc))
+ {
+ /* c) EM' = I2OSP(m, k) -- Convert the result to bytes. */
+ uint32_t cbDecrypted = RTBigNumByteWidth(&pThis->TmpBigNum2) + 1; /* 1 = leading zero byte */
+ if (cbDecrypted <= sizeof(pThis->Scratch) / 2)
+ {
+ uint8_t *pbDecrypted = &pThis->Scratch.abSignature[sizeof(pThis->Scratch) / 2];
+ rc = RTBigNumToBytesBigEndian(&pThis->TmpBigNum2, pbDecrypted, cbDecrypted);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * 8.2.2.3 - Build a hopefully identical signature using hDigest.
+ */
+ rc = rtCrPkixSignatureRsa_EmsaPkcs1V15Encode(pThis, hDigest, cbDecrypted, false /* fNoDigestInfo */);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * 8.2.2.4 - Compare the two.
+ */
+ if (memcmp(&pThis->Scratch.abSignature[0], pbDecrypted, cbDecrypted) == 0)
+ { /* No rc = VINF_SUCCESS here, mustpreserve informational status codes from digest. */ }
+ else
+ {
+ /*
+ * Try again without digestinfo. This style signing has been
+ * observed in Vista timestamp counter signatures (Thawte).
+ */
+ rc = rtCrPkixSignatureRsa_EmsaPkcs1V15Encode(pThis, hDigest, cbDecrypted,
+ true /* fNoDigestInfo */);
+ if (RT_SUCCESS(rc))
+ {
+ if (memcmp(&pThis->Scratch.abSignature[0], pbDecrypted, cbDecrypted) == 0)
+ { /* No rc = VINF_SUCCESS here, mustpreserve informational status codes from digest. */ }
+ else
+ rc = VERR_CR_PKIX_SIGNATURE_MISMATCH;
+ }
+ }
+ }
+ }
+ }
+ else
+ rc = VERR_CR_PKIX_SIGNATURE_TOO_LONG;
+ }
+ RTBigNumDestroy(&pThis->TmpBigNum2);
+ }
+ }
+ else
+ rc = VERR_CR_PKIX_SIGNATURE_NEGATIVE;
+ }
+ else
+ rc = VERR_CR_PKIX_SIGNATURE_GE_KEY;
+ RTBigNumDestroy(&pThis->TmpBigNum1);
+ return rc;
+}
+
+
+/** @impl_interface_method{RTCRPKIXSIGNATUREDESC,pfnSign} */
+static DECLCALLBACK(int) rtCrPkixSignatureRsa_Sign(PCRTCRPKIXSIGNATUREDESC pDesc, void *pvState, RTCRKEY hKey,
+ RTCRDIGEST hDigest, void *pvSignature, size_t *pcbSignature)
+{
+ PRTCRPKIXSIGNATURERSA pThis = (PRTCRPKIXSIGNATURERSA)pvState;
+ RT_NOREF_PV(pDesc);
+ Assert(pThis->fSigning);
+
+ /*
+ * Get the key bits we need.
+ */
+ Assert(RTCrKeyGetType(hKey) == RTCRKEYTYPE_RSA_PRIVATE);
+ PRTBIGNUM pModulus = &hKey->u.RsaPrivate.Modulus;
+ PRTBIGNUM pExponent = &hKey->u.RsaPrivate.PrivateExponent;
+
+ /*
+ * Calc signature length and return if destination buffer isn't big enough.
+ */
+ size_t const cbDst = *pcbSignature;
+ size_t const cbEncodedMsg = RTBigNumByteWidth(pModulus);
+ *pcbSignature = cbEncodedMsg;
+ if (cbEncodedMsg > sizeof(pThis->Scratch) / 2)
+ return VERR_CR_PKIX_SIGNATURE_TOO_LONG;
+ if (!pvSignature || cbDst < cbEncodedMsg)
+ return VERR_BUFFER_OVERFLOW;
+
+ /*
+ * 8.1.1.1 - EMSA-PSS encoding. (RFC-3447)
+ */
+ int rcRetSuccess;
+ int rc = rcRetSuccess = rtCrPkixSignatureRsa_EmsaPkcs1V15Encode(pThis, hDigest, cbEncodedMsg, false /* fNoDigestInfo */);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * 8.1.1.2 - RSA signature.
+ */
+ /* a) m = OS2IP(EM) -- Convert the encoded message (EM) to integer. */
+ rc = RTBigNumInit(&pThis->TmpBigNum1, RTBIGNUMINIT_F_ENDIAN_BIG | RTBIGNUMINIT_F_UNSIGNED,
+ pThis->Scratch.abSignature, cbEncodedMsg);
+ if (RT_SUCCESS(rc))
+ {
+ /* b) s = RSASP1(K, m = EM) - 5.2.1.1: Range check (0 <= m < n). */
+ if (RTBigNumCompare(&pThis->TmpBigNum1, pModulus) < 0)
+ {
+ /* b) s = RSAVP1(K, m = EM) - 5.2.1.2.a: s = m^d mod n */
+ rc = RTBigNumInitZero(&pThis->TmpBigNum2, 0);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTBigNumModExp(&pThis->TmpBigNum2, &pThis->TmpBigNum1, pExponent, pModulus);
+ if (RT_SUCCESS(rc))
+ {
+ /* c) S = I2OSP(s, k) -- Convert the result to bytes. */
+ rc = RTBigNumToBytesBigEndian(&pThis->TmpBigNum2, pvSignature, cbEncodedMsg);
+ AssertStmt(RT_SUCCESS(rc) || rc != VERR_BUFFER_OVERFLOW, rc = VERR_CR_PKIX_INTERNAL_ERROR);
+
+ /* Make sure we return the informational status code from the digest on success. */
+ if (rc == VINF_SUCCESS && rcRetSuccess != VINF_SUCCESS)
+ rc = rcRetSuccess;
+ }
+ RTBigNumDestroy(&pThis->TmpBigNum2);
+ }
+ }
+ else
+ rc = VERR_CR_PKIX_SIGNATURE_GE_KEY;
+ RTBigNumDestroy(&pThis->TmpBigNum1);
+ }
+ }
+ return rc;
+}
+
+
+
+
+/** RSA alias ODIs. */
+static const char * const g_apszHashWithRsaAliases[] =
+{
+ RTCR_PKCS1_MD2_WITH_RSA_OID,
+ RTCR_PKCS1_MD4_WITH_RSA_OID,
+ RTCR_PKCS1_MD5_WITH_RSA_OID,
+ RTCR_PKCS1_SHA1_WITH_RSA_OID,
+ RTCR_PKCS1_SHA256_WITH_RSA_OID,
+ RTCR_PKCS1_SHA384_WITH_RSA_OID,
+ RTCR_PKCS1_SHA512_WITH_RSA_OID,
+ RTCR_PKCS1_SHA224_WITH_RSA_OID,
+ RTCR_PKCS1_SHA512T224_WITH_RSA_OID,
+ RTCR_PKCS1_SHA512T256_WITH_RSA_OID,
+ RTCR_NIST_SHA3_224_WITH_RSA_OID,
+ RTCR_NIST_SHA3_256_WITH_RSA_OID,
+ RTCR_NIST_SHA3_384_WITH_RSA_OID,
+ RTCR_NIST_SHA3_512_WITH_RSA_OID,
+ /* Note: Note quite sure about these OIW oddballs. */
+ "1.3.14.3.2.11" /* OIW rsaSignature */,
+ "1.3.14.3.2.14" /* OIW mdc2WithRSASignature */,
+ "1.3.14.3.2.15" /* OIW shaWithRSASignature */,
+ "1.3.14.3.2.24" /* OIW md2WithRSASignature */,
+ "1.3.14.3.2.25" /* OIW md5WithRSASignature */,
+ "1.3.14.3.2.29" /* OIW sha1WithRSASignature */,
+ NULL
+};
+
+
+/** RSA descriptor. */
+DECL_HIDDEN_CONST(RTCRPKIXSIGNATUREDESC const) g_rtCrPkixSigningHashWithRsaDesc =
+{
+ "RSASSA-PKCS1-v1_5",
+ RTCR_PKCS1_RSA_OID,
+ g_apszHashWithRsaAliases,
+ sizeof(RTCRPKIXSIGNATURERSA),
+ 0,
+ 0,
+ rtCrPkixSignatureRsa_Init,
+ rtCrPkixSignatureRsa_Reset,
+ rtCrPkixSignatureRsa_Delete,
+ rtCrPkixSignatureRsa_Verify,
+ rtCrPkixSignatureRsa_Sign,
+};
+
+
+/**
+ * Worker for RTCrRsaPublicKey_CanHandleDigestType and
+ * RTCrRsaPrivateKey_CanHandleDigestType.
+ *
+ * We implement these two functions here because we've already got the
+ * DigestInfo sizes nicely lined up here.
+ */
+static bool rtCrRsa_CanHandleDigestType(int32_t cModulusBits, RTDIGESTTYPE enmDigestType, PRTERRINFO pErrInfo)
+{
+ /*
+ * ASSUME EMSA-PKCS1-v1_5 padding scheme (RFC-8017 section 9.2):
+ * - 11 byte padding prefix (00, 01, 8 times ff)
+ * - digest info der sequence for rsaWithXxxxEncryption
+ * - the hash value.
+ */
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_aDigestInfos); i++)
+ if (g_aDigestInfos[i].enmDigest == enmDigestType)
+ {
+ size_t const cbHash = RTCrDigestTypeToHashSize(enmDigestType);
+ AssertBreak(cbHash > 0);
+
+ size_t cbMsg = 11 + g_aDigestInfos[i].cb + cbHash;
+ if ((ssize_t)cbMsg <= cModulusBits / 8)
+ return true;
+ RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_INVALID_SIGNATURE_LENGTH, "cModulusBits=%d cbMsg=%u", cModulusBits, cbMsg);
+ return false;
+ }
+ RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_UNKNOWN_DIGEST_TYPE, "%s", RTCrDigestTypeToName(enmDigestType));
+ return false;
+}
+
+
+RTDECL(bool) RTCrRsaPublicKey_CanHandleDigestType(PCRTCRRSAPUBLICKEY pRsaPublicKey, RTDIGESTTYPE enmDigestType,
+ PRTERRINFO pErrInfo)
+{
+ if (RTCrRsaPublicKey_IsPresent(pRsaPublicKey))
+ return rtCrRsa_CanHandleDigestType(RTAsn1Integer_UnsignedLastBit(&pRsaPublicKey->Modulus) + 1, enmDigestType, pErrInfo);
+ return false;
+}
+
+
+RTDECL(bool) RTCrRsaPrivateKey_CanHandleDigestType(PCRTCRRSAPRIVATEKEY pRsaPrivateKey, RTDIGESTTYPE enmDigestType,
+ PRTERRINFO pErrInfo)
+{
+ if (RTCrRsaPrivateKey_IsPresent(pRsaPrivateKey))
+ return rtCrRsa_CanHandleDigestType(RTAsn1Integer_UnsignedLastBit(&pRsaPrivateKey->Modulus) + 1, enmDigestType, pErrInfo);
+ return false;
+}
+
diff --git a/src/VBox/Runtime/common/crypto/pkix-util.cpp b/src/VBox/Runtime/common/crypto/pkix-util.cpp
new file mode 100644
index 00000000..1198d0e0
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkix-util.cpp
@@ -0,0 +1,108 @@
+/* $Id: pkix-util.cpp $ */
+/** @file
+ * IPRT - Crypto - Public Key Infrastructure API, Utilities.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pkix.h>
+
+#include <iprt/asn1.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/crypto/rsa.h>
+
+
+RTDECL(const char *) RTCrPkixGetCiperOidFromSignatureAlgorithmOid(const char *pszSignatureOid)
+{
+ return RTCrX509AlgorithmIdentifier_GetEncryptionOidFromOid(pszSignatureOid, true /*fMustIncludeHash*/);
+}
+
+
+RTDECL(const char *) RTCrPkixGetCiperOidFromSignatureAlgorithm(PCRTASN1OBJID pAlgorithm)
+{
+ return RTCrX509AlgorithmIdentifier_GetEncryptionOidFromOid(pAlgorithm->szObjId, true /*fMustIncludeHash*/);
+}
+
+
+RTDECL(bool) RTCrPkixPubKeyCanHandleDigestType(PCRTCRX509SUBJECTPUBLICKEYINFO pPublicKeyInfo, RTDIGESTTYPE enmDigestType,
+ PRTERRINFO pErrInfo)
+{
+ bool fRc = false;
+ if (RTCrX509SubjectPublicKeyInfo_IsPresent(pPublicKeyInfo))
+ {
+ void const * const pvKeyBits = RTASN1BITSTRING_GET_BIT0_PTR(&pPublicKeyInfo->SubjectPublicKey);
+ uint32_t const cbKeyBits = RTASN1BITSTRING_GET_BYTE_SIZE(&pPublicKeyInfo->SubjectPublicKey);
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ union
+ {
+ RTCRRSAPUBLICKEY RsaPublicKey;
+ } u;
+
+ if (RTAsn1ObjId_CompareWithString(&pPublicKeyInfo->Algorithm.Algorithm, RTCR_PKCS1_RSA_OID) == 0)
+ {
+ /*
+ * RSA.
+ */
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pvKeyBits, cbKeyBits, pErrInfo, &g_RTAsn1DefaultAllocator,
+ RTASN1CURSOR_FLAGS_DER, "rsa");
+
+ RT_ZERO(u.RsaPublicKey);
+ int rc = RTCrRsaPublicKey_DecodeAsn1(&PrimaryCursor.Cursor, 0, &u.RsaPublicKey, "PublicKey");
+ if (RT_SUCCESS(rc))
+ fRc = RTCrRsaPublicKey_CanHandleDigestType(&u.RsaPublicKey, enmDigestType, pErrInfo);
+ RTCrRsaPublicKey_Delete(&u.RsaPublicKey);
+ }
+ else
+ {
+ /** @todo ECDSA when adding signing support. */
+ RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_CIPHER_ALGO_NOT_KNOWN, "%s", pPublicKeyInfo->Algorithm.Algorithm.szObjId);
+ AssertMsgFailed(("unknown key algorithm: %s\n", pPublicKeyInfo->Algorithm.Algorithm.szObjId));
+ fRc = true;
+ }
+ }
+ return fRc;
+}
+
+
+RTDECL(bool) RTCrPkixCanCertHandleDigestType(PCRTCRX509CERTIFICATE pCertificate, RTDIGESTTYPE enmDigestType, PRTERRINFO pErrInfo)
+{
+ if (RTCrX509Certificate_IsPresent(pCertificate))
+ return RTCrPkixPubKeyCanHandleDigestType(&pCertificate->TbsCertificate.SubjectPublicKeyInfo, enmDigestType, pErrInfo);
+ return false;
+}
+
diff --git a/src/VBox/Runtime/common/crypto/pkix-verify.cpp b/src/VBox/Runtime/common/crypto/pkix-verify.cpp
new file mode 100644
index 00000000..10c5bd61
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/pkix-verify.cpp
@@ -0,0 +1,329 @@
+/* $Id: pkix-verify.cpp $ */
+/** @file
+ * IPRT - Crypto - Public Key Infrastructure API, Verification.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/pkix.h>
+
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/crypto/digest.h>
+#include <iprt/crypto/key.h>
+
+#ifdef IPRT_WITH_OPENSSL
+# include "internal/iprt-openssl.h"
+# include "internal/openssl-pre.h"
+# include <openssl/evp.h>
+# include "internal/openssl-post.h"
+# ifndef OPENSSL_VERSION_NUMBER
+# error "Missing OPENSSL_VERSION_NUMBER!"
+# endif
+#endif
+
+
+
+RTDECL(int) RTCrPkixPubKeyVerifySignature(PCRTASN1OBJID pAlgorithm, RTCRKEY hPublicKey, PCRTASN1DYNTYPE pParameters,
+ PCRTASN1BITSTRING pSignatureValue, const void *pvData, size_t cbData,
+ PRTERRINFO pErrInfo)
+{
+ /*
+ * Valid input.
+ */
+ AssertPtrReturn(pAlgorithm, VERR_INVALID_POINTER);
+ AssertReturn(RTAsn1ObjId_IsPresent(pAlgorithm), VERR_INVALID_POINTER);
+
+ AssertPtrReturn(hPublicKey, VERR_INVALID_POINTER);
+ Assert(RTCrKeyHasPublicPart(hPublicKey));
+ RTCRKEYTYPE const enmKeyType = RTCrKeyGetType(hPublicKey);
+ AssertReturn(enmKeyType != RTCRKEYTYPE_INVALID, VERR_INVALID_HANDLE);
+
+ AssertPtrReturn(pSignatureValue, VERR_INVALID_POINTER);
+ AssertReturn(RTAsn1BitString_IsPresent(pSignatureValue), VERR_INVALID_POINTER);
+
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ AssertReturn(cbData > 0, VERR_INVALID_PARAMETER);
+
+ /*
+ * Verify that the parameters are compatible with the key. We ASSUME the
+ * parameters are for a hash+cryption combination, like those found in
+ * RTCRX509TBSCERTIFICATE::Signature. At present, these should NULL (or
+ * absent) for the two key types we support RSA & ECDSA, which is an
+ * ASSUMPTION by the OpenSSL code below.
+ */
+ int rcIprt = RTCrKeyVerifyParameterCompatibility(hPublicKey, pParameters, true /*fForSignature*/, pAlgorithm, pErrInfo);
+ AssertRCReturn(rcIprt, rcIprt);
+
+ /*
+ * Validate using IPRT.
+ */
+ RTCRPKIXSIGNATURE hSignature;
+ rcIprt = RTCrPkixSignatureCreateByObjId(&hSignature, pAlgorithm, hPublicKey, pParameters, false /*fSigning*/);
+ if (RT_FAILURE(rcIprt))
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_CIPHER_ALGO_NOT_KNOWN,
+ "Unknown public key algorithm [IPRT %Rrc]: %s", rcIprt, pAlgorithm->szObjId);
+
+ RTCRDIGEST hDigest;
+ rcIprt = RTCrDigestCreateByObjId(&hDigest, pAlgorithm);
+ if (RT_SUCCESS(rcIprt))
+ {
+ /* Calculate the digest. */
+ rcIprt = RTCrDigestUpdate(hDigest, pvData, cbData);
+ if (RT_SUCCESS(rcIprt))
+ {
+ rcIprt = RTCrPkixSignatureVerifyBitString(hSignature, hDigest, pSignatureValue);
+ if (RT_FAILURE(rcIprt))
+ RTErrInfoSet(pErrInfo, rcIprt, "RTCrPkixSignatureVerifyBitString failed");
+ }
+ else
+ RTErrInfoSet(pErrInfo, rcIprt, "RTCrDigestUpdate failed");
+ RTCrDigestRelease(hDigest);
+ }
+ else
+ RTErrInfoSetF(pErrInfo, rcIprt, "Unknown digest algorithm [IPRT]: %s", pAlgorithm->szObjId);
+ RTCrPkixSignatureRelease(hSignature);
+
+#ifdef IPRT_WITH_OPENSSL
+ /* We don't implement digest+cipher parameters in OpenSSL (or at all),
+ RTCrKeyVerifyParameterCompatibility should ensure we don't get here
+ (ASSUMING only RSA and ECDSA keys). But, just in case, bail out if we do. */
+ AssertReturn( !pParameters
+ || pParameters->enmType == RTASN1TYPE_NULL
+ || pParameters->enmType == RTASN1TYPE_NOT_PRESENT,
+ VERR_CR_PKIX_CIPHER_ALGO_PARAMS_NOT_IMPL);
+
+ /*
+ * Validate using OpenSSL EVP.
+ */
+ /* Create an EVP public key. */
+ EVP_PKEY *pEvpPublicKey = NULL;
+ const EVP_MD *pEvpMdType = NULL;
+ int rcOssl = rtCrKeyToOpenSslKeyEx(hPublicKey, true /*fNeedPublic*/, pAlgorithm->szObjId,
+ (void **)&pEvpPublicKey, (const void **)&pEvpMdType, pErrInfo);
+ if (RT_SUCCESS(rcOssl))
+ {
+ EVP_MD_CTX *pEvpMdCtx = EVP_MD_CTX_create();
+ if (pEvpMdCtx)
+ {
+ if (EVP_VerifyInit_ex(pEvpMdCtx, pEvpMdType, NULL /*engine*/))
+ {
+ /* Digest the data. */
+ EVP_VerifyUpdate(pEvpMdCtx, pvData, cbData);
+
+ /* Verify the signature. */
+ if (EVP_VerifyFinal(pEvpMdCtx,
+ RTASN1BITSTRING_GET_BIT0_PTR(pSignatureValue),
+ RTASN1BITSTRING_GET_BYTE_SIZE(pSignatureValue),
+ pEvpPublicKey) > 0)
+ rcOssl = VINF_SUCCESS;
+ else
+ rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKIX_OSSL_VERIFY_FINAL_FAILED, "EVP_VerifyFinal failed");
+
+ /* Cleanup and return: */
+ }
+ else
+ rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_CIPHER_ALOG_INIT_FAILED,
+ "EVP_VerifyInit_ex failed (algorithm type is %s)", pAlgorithm->szObjId);
+ EVP_MD_CTX_destroy(pEvpMdCtx);
+ }
+ else
+ rcOssl = RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "EVP_MD_CTX_create failed");
+ EVP_PKEY_free(pEvpPublicKey);
+ }
+
+ /*
+ * Check the result.
+ */
+ if ( (RT_SUCCESS(rcIprt) && RT_SUCCESS(rcOssl))
+ || (RT_FAILURE_NP(rcIprt) && RT_FAILURE_NP(rcOssl))
+ || (RT_SUCCESS(rcIprt) && rcOssl == VERR_CR_PKIX_OSSL_CIPHER_ALGO_NOT_KNOWN_EVP) )
+ return rcIprt;
+ AssertMsgFailed(("rcIprt=%Rrc rcOssl=%Rrc\n", rcIprt, rcOssl));
+ if (RT_FAILURE_NP(rcOssl))
+ return rcOssl;
+#endif /* IPRT_WITH_OPENSSL */
+
+ return rcIprt;
+}
+
+
+RTDECL(int) RTCrPkixPubKeyVerifySignedDigest(PCRTASN1OBJID pAlgorithm, RTCRKEY hPublicKey, PCRTASN1DYNTYPE pParameters,
+ void const *pvSignedDigest, size_t cbSignedDigest, RTCRDIGEST hDigest,
+ PRTERRINFO pErrInfo)
+{
+ /*
+ * Valid input.
+ */
+ AssertPtrReturn(pAlgorithm, VERR_INVALID_POINTER);
+ AssertReturn(RTAsn1ObjId_IsPresent(pAlgorithm), VERR_INVALID_POINTER);
+
+ AssertPtrReturn(hPublicKey, VERR_INVALID_POINTER);
+ Assert(RTCrKeyHasPublicPart(hPublicKey));
+ RTCRKEYTYPE const enmKeyType = RTCrKeyGetType(hPublicKey);
+ AssertReturn(enmKeyType != RTCRKEYTYPE_INVALID, VERR_INVALID_HANDLE);
+
+ AssertPtrReturn(pvSignedDigest, VERR_INVALID_POINTER);
+ AssertReturn(cbSignedDigest, VERR_INVALID_PARAMETER);
+
+ AssertPtrReturn(hDigest, VERR_INVALID_HANDLE);
+
+ /*
+ * Verify that the parameters are compatible with the key. We ASSUME the
+ * parameters are for a hash+cryption combination, like those found in
+ * RTCRX509TBSCERTIFICATE::Signature. At present, these should NULL (or
+ * absent) for the two key types we support RSA & ECDSA, which is an
+ * ASSUMPTION by the OpenSSL code below.
+ */
+ int rcIprt = RTCrKeyVerifyParameterCompatibility(hPublicKey, pParameters, true /*fForSignature*/, pAlgorithm, pErrInfo);
+ AssertRCReturn(rcIprt, rcIprt);
+
+ /*
+ * Validate using IPRT.
+ */
+ RTCRPKIXSIGNATURE hSignature;
+ rcIprt = RTCrPkixSignatureCreateByObjId(&hSignature, pAlgorithm, hPublicKey, pParameters, false /*fSigning*/);
+ if (RT_FAILURE(rcIprt))
+ return RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_CIPHER_ALGO_NOT_KNOWN,
+ "Unknown public key algorithm [IPRT %Rrc]: %s", rcIprt, pAlgorithm->szObjId);
+
+ rcIprt = RTCrPkixSignatureVerify(hSignature, hDigest, pvSignedDigest, cbSignedDigest);
+ if (RT_FAILURE(rcIprt))
+ RTErrInfoSet(pErrInfo, rcIprt, "RTCrPkixSignatureVerifyBitString failed");
+
+ RTCrPkixSignatureRelease(hSignature);
+
+#if defined(IPRT_WITH_OPENSSL) \
+ && (OPENSSL_VERSION_NUMBER > 0x10000000L) /* 0.9.8 doesn't seem to have EVP_PKEY_CTX_set_signature_md. */
+ /*
+ * Validate using OpenSSL EVP.
+ */
+ /* Make sure the algorithm includes the digest and isn't just RSA, ECDSA or similar. */
+ const char *pszAlgObjId = RTCrX509AlgorithmIdentifier_CombineEncryptionOidAndDigestOid(pAlgorithm->szObjId,
+ RTCrDigestGetAlgorithmOid(hDigest));
+ AssertMsgStmt(pszAlgObjId, ("enc=%s hash=%s\n", pAlgorithm->szObjId, RTCrDigestGetAlgorithmOid(hDigest)),
+ pszAlgObjId = RTCrDigestGetAlgorithmOid(hDigest));
+
+ /* We don't implement digest+cipher parameters in OpenSSL (or at all),
+ RTCrKeyVerifyParameterCompatibility should ensure we don't get here
+ (ASSUMING only RSA and ECDSA keys). But, just in case, bail out if we do. */
+ AssertReturn( !pParameters
+ || pParameters->enmType == RTASN1TYPE_NULL
+ || pParameters->enmType == RTASN1TYPE_NOT_PRESENT,
+ VERR_CR_PKIX_CIPHER_ALGO_PARAMS_NOT_IMPL);
+
+ /* Create an EVP public key. */
+ EVP_PKEY *pEvpPublicKey = NULL;
+ const EVP_MD *pEvpMdType = NULL;
+ int rcOssl = rtCrKeyToOpenSslKeyEx(hPublicKey, true /*fNeedPublic*/, pszAlgObjId,
+ (void **)&pEvpPublicKey, (const void **)&pEvpMdType, pErrInfo);
+ if (RT_SUCCESS(rcOssl))
+ {
+ /* Create an EVP public key context we can use to validate the digest. */
+ EVP_PKEY_CTX *pEvpPKeyCtx = EVP_PKEY_CTX_new(pEvpPublicKey, NULL);
+ if (pEvpPKeyCtx)
+ {
+ rcOssl = EVP_PKEY_verify_init(pEvpPKeyCtx);
+ if (rcOssl > 0)
+ {
+ rcOssl = EVP_PKEY_CTX_set_signature_md(pEvpPKeyCtx, pEvpMdType);
+ if (rcOssl > 0)
+ {
+ /* Get the digest from hDigest and verify it. */
+ rcOssl = EVP_PKEY_verify(pEvpPKeyCtx,
+ (uint8_t const *)pvSignedDigest,
+ cbSignedDigest,
+ RTCrDigestGetHash(hDigest),
+ RTCrDigestGetHashSize(hDigest));
+ if (rcOssl > 0)
+ rcOssl = VINF_SUCCESS;
+ else
+ rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_VERIFY_FINAL_FAILED,
+ "EVP_PKEY_verify failed (%d)", rcOssl);
+ /* Cleanup and return: */
+ }
+ else
+ rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR,
+ "EVP_PKEY_CTX_set_signature_md failed (%d)", rcOssl);
+ }
+ else
+ rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR,
+ "EVP_PKEY_verify_init failed (%d)", rcOssl);
+ EVP_PKEY_CTX_free(pEvpPKeyCtx);
+ }
+ else
+ rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR, "EVP_PKEY_CTX_new failed");
+ EVP_PKEY_free(pEvpPublicKey);
+ }
+
+ /*
+ * Check the result.
+ */
+ if ( (RT_SUCCESS(rcIprt) && RT_SUCCESS(rcOssl))
+ || (RT_FAILURE_NP(rcIprt) && RT_FAILURE_NP(rcOssl))
+ || (RT_SUCCESS(rcIprt) && rcOssl == VERR_CR_PKIX_OSSL_CIPHER_ALGO_NOT_KNOWN_EVP) )
+ return rcIprt;
+ AssertMsgFailed(("rcIprt=%Rrc rcOssl=%Rrc\n", rcIprt, rcOssl));
+ if (RT_FAILURE_NP(rcOssl))
+ return rcOssl;
+#endif /* IPRT_WITH_OPENSSL */
+
+ return rcIprt;
+}
+
+
+RTDECL(int) RTCrPkixPubKeyVerifySignedDigestByCertPubKeyInfo(PCRTCRX509SUBJECTPUBLICKEYINFO pCertPubKeyInfo,
+ void const *pvSignedDigest, size_t cbSignedDigest,
+ RTCRDIGEST hDigest, PRTERRINFO pErrInfo)
+{
+ RTCRKEY hPublicKey;
+ int rc = RTCrKeyCreateFromPublicAlgorithmAndBits(&hPublicKey, &pCertPubKeyInfo->Algorithm.Algorithm,
+ &pCertPubKeyInfo->Algorithm.Parameters,
+ &pCertPubKeyInfo->SubjectPublicKey, pErrInfo, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo r=bird (2023-07-06): This ASSUMES no digest+cipher parameters, which
+ * is the case for RSA and ECDSA. */
+ rc = RTCrPkixPubKeyVerifySignedDigest(&pCertPubKeyInfo->Algorithm.Algorithm, hPublicKey, NULL,
+ pvSignedDigest, cbSignedDigest, hDigest, pErrInfo);
+
+ uint32_t cRefs = RTCrKeyRelease(hPublicKey);
+ Assert(cRefs == 0); RT_NOREF(cRefs);
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/crypto/rc4-openssl.cpp b/src/VBox/Runtime/common/crypto/rc4-openssl.cpp
new file mode 100644
index 00000000..a32eb155
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/rc4-openssl.cpp
@@ -0,0 +1,75 @@
+/* $Id: rc4-openssl.cpp $ */
+/** @file
+ * IPRT - Crypto - Alleged RC4 via OpenSSL.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef IPRT_WITH_OPENSSL
+# include "internal/iprt.h"
+# include "internal/iprt-openssl.h"
+# include "internal/openssl-pre.h"
+# include <openssl/rc4.h>
+# include "internal/openssl-post.h"
+# include <iprt/crypto/rc4.h>
+
+# include <iprt/assert.h>
+# include <iprt/assertcompile.h>
+
+
+
+RTDECL(void) RTCrRc4SetKey(PRTCRRC4KEY pKey, size_t cbData, void const *pvData)
+{
+
+ AssertCompile(sizeof(RC4_INT) == 4 ? sizeof(pKey->au64Padding) == sizeof(pKey->Ossl) : sizeof(pKey->au64Padding) >= sizeof(pKey->Ossl));
+ Assert((int)cbData == (ssize_t)cbData);
+ AssertPtr(pKey);
+
+ RC4_set_key(&pKey->Ossl, (int)cbData, (const unsigned char *)pvData);
+}
+
+
+RTDECL(void) RTCrRc4(PRTCRRC4KEY pKey, size_t cbData, void const *pvDataIn, void *pvDataOut)
+{
+ AssertCompile(sizeof(RC4_INT) == 4 ? sizeof(pKey->au64Padding) == sizeof(pKey->Ossl) : sizeof(pKey->au64Padding) >= sizeof(pKey->Ossl));
+ Assert((int)cbData == (ssize_t)cbData);
+ AssertPtr(pKey);
+
+ RC4(&pKey->Ossl, (int)cbData, (const unsigned char *)pvDataIn, (unsigned char *)pvDataOut);
+}
+
+#endif /* IPRT_WITH_OPENSSL */
+
diff --git a/src/VBox/Runtime/common/crypto/rsa-asn1-decoder.cpp b/src/VBox/Runtime/common/crypto/rsa-asn1-decoder.cpp
new file mode 100644
index 00000000..9c127fb2
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/rsa-asn1-decoder.cpp
@@ -0,0 +1,53 @@
+/* $Id: rsa-asn1-decoder.cpp $ */
+/** @file
+ * IPRT - Crypto - RSA, Decoder for ASN.1.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/rsa.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+#include "rsa-internal.h"
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-asn1-decoder.h>
+
diff --git a/src/VBox/Runtime/common/crypto/rsa-core.cpp b/src/VBox/Runtime/common/crypto/rsa-core.cpp
new file mode 100644
index 00000000..c356b48c
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/rsa-core.cpp
@@ -0,0 +1,54 @@
+/* $Id: rsa-core.cpp $ */
+/** @file
+ * IPRT - Crypto - RSA, Core APIs.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/rsa.h>
+
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#include "rsa-internal.h"
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-core.h>
+
diff --git a/src/VBox/Runtime/common/crypto/rsa-init.cpp b/src/VBox/Runtime/common/crypto/rsa-init.cpp
new file mode 100644
index 00000000..2099b377
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/rsa-init.cpp
@@ -0,0 +1,53 @@
+/* $Id: rsa-init.cpp $ */
+/** @file
+ * IPRT - Crypto - RSA, Initialization API.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/rsa.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+#include "rsa-internal.h"
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-init.h>
+
diff --git a/src/VBox/Runtime/common/crypto/rsa-internal.h b/src/VBox/Runtime/common/crypto/rsa-internal.h
new file mode 100644
index 00000000..93f43a6f
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/rsa-internal.h
@@ -0,0 +1,50 @@
+/* $Id: rsa-internal.h $ */
+/** @file
+ * IPRT - Crypto - RSA, Internal Header.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_common_crypto_rsa_internal_h
+#define IPRT_INCLUDED_SRC_common_crypto_rsa_internal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/** The max number of bits we support in the modulus. */
+#define RTCRRSA_MAX_MODULUS_BITS 16384
+
+#define RTASN1TMPL_TEMPLATE_FILE "../common/crypto/rsa-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+
+#endif /* !IPRT_INCLUDED_SRC_common_crypto_rsa_internal_h */
+
diff --git a/src/VBox/Runtime/common/crypto/rsa-sanity.cpp b/src/VBox/Runtime/common/crypto/rsa-sanity.cpp
new file mode 100644
index 00000000..bf4cffaa
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/rsa-sanity.cpp
@@ -0,0 +1,53 @@
+/* $Id: rsa-sanity.cpp $ */
+/** @file
+ * IPRT - Crypto - RSA, Sanity Checkers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/rsa.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+#include "rsa-internal.h"
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-sanity.h>
+
diff --git a/src/VBox/Runtime/common/crypto/rsa-template.h b/src/VBox/Runtime/common/crypto/rsa-template.h
new file mode 100644
index 00000000..8fc21fed
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/rsa-template.h
@@ -0,0 +1,118 @@
+/* $Id: rsa-template.h $ */
+/** @file
+ * IPRT - Crypto - RSA, Code Generator Template.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#define RTASN1TMPL_DECL RTDECL
+
+/*
+ * RSA public key.
+ */
+#define RTASN1TMPL_TYPE RTCRRSAPUBLICKEY
+#define RTASN1TMPL_EXT_NAME RTCrRsaPublicKey
+#define RTASN1TMPL_INT_NAME rtCrRsaPublicKey
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( Modulus, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( PublicExponent, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * One RSA other prime info.
+ */
+#define RTASN1TMPL_TYPE RTCRRSAOTHERPRIMEINFO
+#define RTASN1TMPL_EXT_NAME RTCrRsaOtherPrimeInfo
+#define RTASN1TMPL_INT_NAME rtCrRsaOtherPrimeInfo
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( Prime, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( Exponent, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( Coefficient, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * Sequence of RSA other prime infos.
+ */
+#define RTASN1TMPL_TYPE RTCRRSAOTHERPRIMEINFOS
+#define RTASN1TMPL_EXT_NAME RTCrRsaOtherPrimeInfos
+#define RTASN1TMPL_INT_NAME rtCrRsaOtherPrimeInfos
+RTASN1TMPL_SEQ_OF(RTCRRSAOTHERPRIMEINFO, RTCrRsaOtherPrimeInfo);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * RSA private key.
+ */
+#define RTASN1TMPL_TYPE RTCRRSAPRIVATEKEY
+#define RTASN1TMPL_EXT_NAME RTCrRsaPrivateKey
+#define RTASN1TMPL_INT_NAME rtCrRsaPrivateKey
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( Version, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( Modulus, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( PublicExponent, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( PrivateExponent, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( Prime1, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( Prime2, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( Exponent1, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( Exponent2, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( Coefficient, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER_OPT_ITAG_EX( OtherPrimeInfos, RTCRRSAOTHERPRIMEINFOS, RTCrRsaOtherPrimeInfos, ASN1_TAG_SEQUENCE, RTASN1TMPL_ITAG_F_UC, RT_NOTHING);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * RSA Digest Info.
+ */
+#define RTASN1TMPL_TYPE RTCRRSADIGESTINFO
+#define RTASN1TMPL_EXT_NAME RTCrRsaDigestInfo
+#define RTASN1TMPL_INT_NAME rtCrRsaDigestInfo
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( DigestAlgorithm, RTCRX509ALGORITHMIDENTIFIER, RTCrX509AlgorithmIdentifier);
+RTASN1TMPL_MEMBER( Digest, RTASN1OCTETSTRING, RTAsn1OctetString);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
diff --git a/src/VBox/Runtime/common/crypto/spc-asn1-decoder.cpp b/src/VBox/Runtime/common/crypto/spc-asn1-decoder.cpp
new file mode 100644
index 00000000..134dd7cc
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/spc-asn1-decoder.cpp
@@ -0,0 +1,88 @@
+/* $Id: spc-asn1-decoder.cpp $ */
+/** @file
+ * IPRT - Crypto - Microsoft SPC / Authenticode, Decoder for ASN.1.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/spc.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+
+#include "spc-internal.h"
+
+
+/*
+ * One SPC Serialized Object Attribute.
+ */
+
+/** Decode the content of the octet string value if known. */
+static int rtCrSpcSerializedObject_DecodeMore(PRTASN1CURSOR pCursor, uint32_t fFlags,
+ PRTCRSPCSERIALIZEDOBJECT pThis, const char *pszErrorTag)
+
+{
+ RT_NOREF_PV(fFlags); RT_NOREF_PV(pszErrorTag);
+
+ int rc;
+ RTASN1CURSOR SubCursor;
+ if (RTUuidCompareStr(pThis->Uuid.Asn1Core.uData.pUuid, RTCRSPCSERIALIZEDOBJECT_UUID_STR) == 0)
+ {
+ rc = RTAsn1MemAllocZ(&pThis->SerializedData.EncapsulatedAllocation, (void **)&pThis->u.pData, sizeof(*pThis->u.pData));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->SerializedData.pEncapsulated = (PRTASN1CORE)pThis->u.pData;
+ pThis->enmType = RTCRSPCSERIALIZEDOBJECTTYPE_ATTRIBUTES;
+
+ rc = RTAsn1CursorInitSubFromCore(pCursor, &pThis->SerializedData.Asn1Core, &SubCursor, "SerializedData");
+ if (RT_SUCCESS(rc))
+ rc = RTCrSpcSerializedObjectAttributes_DecodeAsn1(&SubCursor, 0, pThis->u.pData, "SD");
+ }
+ }
+ else
+ return VINF_SUCCESS;
+ if (RT_SUCCESS(rc))
+ rc = RTAsn1CursorCheckEnd(&SubCursor);
+ return rc;
+}
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-asn1-decoder.h>
+
diff --git a/src/VBox/Runtime/common/crypto/spc-core.cpp b/src/VBox/Runtime/common/crypto/spc-core.cpp
new file mode 100644
index 00000000..dc33532d
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/spc-core.cpp
@@ -0,0 +1,94 @@
+/* $Id: spc-core.cpp $ */
+/** @file
+ * IPRT - Crypto - Microsoft SPC / Authenticode, Core APIs.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/spc.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+
+#include "spc-internal.h"
+
+
+RTDECL(int) RTCrSpcSerializedPageHashes_UpdateDerivedData(PRTCRSPCSERIALIZEDPAGEHASHES pThis)
+{
+ pThis->pData = (PCRTCRSPCPEIMAGEPAGEHASHES)pThis->RawData.Asn1Core.uData.pv;
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * SPC Indirect Data Content.
+ */
+
+RTDECL(PCRTCRSPCSERIALIZEDOBJECTATTRIBUTE)
+RTCrSpcIndirectDataContent_GetPeImageObjAttrib(PCRTCRSPCINDIRECTDATACONTENT pThis,
+ RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE enmType)
+{
+ if (pThis->Data.enmType == RTCRSPCAAOVTYPE_PE_IMAGE_DATA)
+ {
+ Assert(RTAsn1ObjId_CompareWithString(&pThis->Data.Type, RTCRSPCPEIMAGEDATA_OID) == 0);
+
+ if ( pThis->Data.uValue.pPeImage
+ && pThis->Data.uValue.pPeImage->T0.File.enmChoice == RTCRSPCLINKCHOICE_MONIKER
+ && RTCrSpcSerializedObject_IsPresent(pThis->Data.uValue.pPeImage->T0.File.u.pMoniker) )
+ {
+ if (pThis->Data.uValue.pPeImage->T0.File.u.pMoniker->enmType == RTCRSPCSERIALIZEDOBJECTTYPE_ATTRIBUTES)
+ {
+ Assert(RTUuidCompareStr(pThis->Data.uValue.pPeImage->T0.File.u.pMoniker->Uuid.Asn1Core.uData.pUuid,
+ RTCRSPCSERIALIZEDOBJECT_UUID_STR) == 0);
+ PCRTCRSPCSERIALIZEDOBJECTATTRIBUTES pData = pThis->Data.uValue.pPeImage->T0.File.u.pMoniker->u.pData;
+ if (pData)
+ for (uint32_t i = 0; i < pData->cItems; i++)
+ if (pData->papItems[i]->enmType == enmType)
+ return pData->papItems[i];
+ }
+ }
+ }
+ return NULL;
+}
+
+
+/*
+ * Generate the standard core code.
+ */
+#include <iprt/asn1-generator-core.h>
+
diff --git a/src/VBox/Runtime/common/crypto/spc-init.cpp b/src/VBox/Runtime/common/crypto/spc-init.cpp
new file mode 100644
index 00000000..1f80151b
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/spc-init.cpp
@@ -0,0 +1,55 @@
+/* $Id: spc-init.cpp $ */
+/** @file
+ * IPRT - Crypto - Microsoft SPC / Authenticode, Initialization API.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/spc.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+
+#include "spc-internal.h"
+
+
+/*
+ * Generate the standard core code.
+ */
+#include <iprt/asn1-generator-init.h>
+
diff --git a/src/VBox/Runtime/common/crypto/spc-internal.h b/src/VBox/Runtime/common/crypto/spc-internal.h
new file mode 100644
index 00000000..08f0f4cc
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/spc-internal.h
@@ -0,0 +1,47 @@
+/* $Id: spc-internal.h $ */
+/** @file
+ * IPRT - Crypto - Microsoft SPC / Authenticode, Internal Header.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_common_crypto_spc_internal_h
+#define IPRT_INCLUDED_SRC_common_crypto_spc_internal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define RTASN1TMPL_TEMPLATE_FILE "../common/crypto/spc-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+
+#endif /* !IPRT_INCLUDED_SRC_common_crypto_spc_internal_h */
+
diff --git a/src/VBox/Runtime/common/crypto/spc-sanity.cpp b/src/VBox/Runtime/common/crypto/spc-sanity.cpp
new file mode 100644
index 00000000..3c6aadbc
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/spc-sanity.cpp
@@ -0,0 +1,179 @@
+/* $Id: spc-sanity.cpp $ */
+/** @file
+ * IPRT - Crypto - Microsoft SPC / Authenticode, Sanity Checkers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/spc.h>
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/uuid.h>
+
+#include "spc-internal.h"
+
+
+RTDECL(int) RTCrSpcIndirectDataContent_CheckSanityEx(PCRTCRSPCINDIRECTDATACONTENT pIndData,
+ PCRTCRPKCS7SIGNEDDATA pSignedData,
+ uint32_t fFlags,
+ PRTERRINFO pErrInfo)
+{
+ /*
+ * Match up the digest algorithms (page 8, v1.0).
+ */
+ if (pSignedData->SignerInfos.cItems != 1)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_SPC_NOT_EXACTLY_ONE_SIGNER_INFOS,
+ "SpcIndirectDataContent expects SignedData to have exactly one SignerInfos entries, found: %u",
+ pSignedData->SignerInfos.cItems);
+ if (pSignedData->DigestAlgorithms.cItems != 1)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_SPC_NOT_EXACTLY_ONE_DIGEST_ALGO,
+ "SpcIndirectDataContent expects SignedData to have exactly one DigestAlgorithms entries, found: %u",
+ pSignedData->DigestAlgorithms.cItems);
+
+ if (RTCrX509AlgorithmIdentifier_Compare(&pIndData->DigestInfo.DigestAlgorithm, /** @todo not entirely sure about this check... */
+ &pSignedData->SignerInfos.papItems[0]->DigestAlgorithm) != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_SPC_SIGNED_IND_DATA_DIGEST_ALGO_MISMATCH,
+ "SpcIndirectDataContent DigestInfo and SignerInfos algorithms mismatch: %s vs %s",
+ pIndData->DigestInfo.DigestAlgorithm.Algorithm.szObjId,
+ pSignedData->SignerInfos.papItems[0]->DigestAlgorithm.Algorithm.szObjId);
+
+ if (RTCrX509AlgorithmIdentifier_Compare(&pIndData->DigestInfo.DigestAlgorithm,
+ pSignedData->DigestAlgorithms.papItems[0]) != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_SPC_IND_DATA_DIGEST_ALGO_NOT_IN_DIGEST_ALGOS,
+ "SpcIndirectDataContent DigestInfo and SignedData.DigestAlgorithms[0] mismatch: %s vs %s",
+ pIndData->DigestInfo.DigestAlgorithm.Algorithm.szObjId,
+ pSignedData->DigestAlgorithms.papItems[0]->Algorithm.szObjId);
+
+ if (fFlags & RTCRSPCINDIRECTDATACONTENT_SANITY_F_ONLY_KNOWN_HASH)
+ {
+ if ( RTCrX509AlgorithmIdentifier_GetDigestType(&pIndData->DigestInfo.DigestAlgorithm, true /*fPureDigestsOnly*/)
+ == RTDIGESTTYPE_INVALID)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_SPC_UNKNOWN_DIGEST_ALGO,
+ "SpcIndirectDataContent DigestAlgortihm is not known: %s",
+ pIndData->DigestInfo.DigestAlgorithm.Algorithm.szObjId);
+ }
+
+ uint32_t cbDigest = RTCrX509AlgorithmIdentifier_GetDigestSize(&pIndData->DigestInfo.DigestAlgorithm,
+ true /*fPureDigestsOnly*/);
+ if ( pIndData->DigestInfo.Digest.Asn1Core.cb != cbDigest
+ && (cbDigest != UINT32_MAX || (fFlags & RTCRSPCINDIRECTDATACONTENT_SANITY_F_ONLY_KNOWN_HASH)))
+ return RTErrInfoSetF(pErrInfo, VERR_CR_SPC_IND_DATA_DIGEST_SIZE_MISMATCH,
+ "SpcIndirectDataContent Digest size mismatch with algorithm: %u, expected %u (%s)",
+ pIndData->DigestInfo.Digest.Asn1Core.cb, cbDigest,
+ pIndData->DigestInfo.DigestAlgorithm.Algorithm.szObjId);
+
+ /*
+ * Data.
+ */
+ if (fFlags & RTCRSPCINDIRECTDATACONTENT_SANITY_F_PE_IMAGE)
+ {
+ if ( pIndData->Data.enmType != RTCRSPCAAOVTYPE_PE_IMAGE_DATA
+ || RTAsn1ObjId_CompareWithString(&pIndData->Data.Type, RTCRSPCPEIMAGEDATA_OID) != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_SPC_EXPECTED_PE_IMAGE_DATA,
+ "SpcIndirectDataContent.Data.Type is %s, expected %s (SpcPeImageData) [enmType=%d]",
+ pIndData->Data.Type.szObjId, RTCRSPCPEIMAGEDATA_OID, pIndData->Data.enmType);
+ if ( pIndData->Data.uValue.pPeImage
+ || !RTCrSpcPeImageData_IsPresent(pIndData->Data.uValue.pPeImage) )
+ return RTErrInfoSet(pErrInfo, VERR_CR_SPC_PEIMAGE_DATA_NOT_PRESENT,
+ "SpcIndirectDataContent.Data.uValue/PEImage is missing");
+
+ if ( pIndData->Data.uValue.pPeImage->T0.File.enmChoice == RTCRSPCLINKCHOICE_MONIKER
+ && RTCrSpcSerializedObject_IsPresent(pIndData->Data.uValue.pPeImage->T0.File.u.pMoniker) )
+ {
+ PCRTCRSPCSERIALIZEDOBJECT pObj = pIndData->Data.uValue.pPeImage->T0.File.u.pMoniker;
+
+ if (pObj->Uuid.Asn1Core.cb != sizeof(RTUUID))
+ return RTErrInfoSetF(pErrInfo, VERR_CR_SPC_BAD_MONIKER_UUID,
+ "SpcIndirectDataContent...MonikerT1.Uuid incorrect size: %u, expected %u.",
+ pObj->Uuid.Asn1Core.cb, sizeof(RTUUID));
+ if (RTUuidCompareStr(pObj->Uuid.Asn1Core.uData.pUuid, RTCRSPCSERIALIZEDOBJECT_UUID_STR) != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_SPC_UNKNOWN_MONIKER_UUID,
+ "SpcIndirectDataContent...MonikerT1.Uuid mismatch: %RTuuid, expected %s.",
+ pObj->Uuid.Asn1Core.uData.pUuid, RTCRSPCSERIALIZEDOBJECT_UUID_STR);
+
+ if (pObj->enmType != RTCRSPCSERIALIZEDOBJECTTYPE_ATTRIBUTES)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_SPC_BAD_MONIKER_CHOICE,
+ "SpcIndirectDataContent...pMoniker->enmType=%d, expected %d.",
+ pObj->enmType, RTCRSPCSERIALIZEDOBJECTTYPE_ATTRIBUTES);
+ if (!pObj->u.pData)
+ return RTErrInfoSet(pErrInfo, VERR_CR_SPC_MONIKER_BAD_DATA,
+ "SpcIndirectDataContent...pMoniker->pData is NULL.");
+
+ uint32_t cPageHashTabs = 0;
+ for (uint32_t i = 0; i < pObj->u.pData->cItems; i++)
+ {
+ PCRTCRSPCSERIALIZEDOBJECTATTRIBUTE pAttr = pObj->u.pData->papItems[i];
+ if ( RTAsn1ObjId_CompareWithString(&pAttr->Type, RTCRSPC_PE_IMAGE_HASHES_V1_OID) == 0
+ || RTAsn1ObjId_CompareWithString(&pAttr->Type, RTCRSPC_PE_IMAGE_HASHES_V2_OID) == 0 )
+ {
+ cPageHashTabs++;
+ AssertPtr(pAttr->u.pPageHashes->pData);
+ }
+ else
+ return RTErrInfoSetF(pErrInfo, VERR_CR_SPC_PEIMAGE_UNKNOWN_ATTRIBUTE,
+ "SpcIndirectDataContent...MonikerT1 unknown attribute %u: %s.",
+ i, pAttr->Type.szObjId);
+ }
+ if (cPageHashTabs > 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_SPC_PEIMAGE_MULTIPLE_HASH_TABS,
+ "SpcIndirectDataContent...MonikerT1 multiple page hash attributes (%u).", cPageHashTabs);
+
+ }
+ else if ( pIndData->Data.uValue.pPeImage->T0.File.enmChoice == RTCRSPCLINKCHOICE_FILE
+ && RTCrSpcString_IsPresent(&pIndData->Data.uValue.pPeImage->T0.File.u.pT2->File) )
+ {
+ /* Could check for "<<<Obsolete>>>" here, but it's really irrelevant. */
+ }
+ else if ( pIndData->Data.uValue.pPeImage->T0.File.enmChoice == RTCRSPCLINKCHOICE_URL
+ && RTAsn1String_IsPresent(pIndData->Data.uValue.pPeImage->T0.File.u.pUrl) )
+ return RTErrInfoSet(pErrInfo, VERR_CR_SPC_PEIMAGE_URL_UNEXPECTED,
+ "SpcIndirectDataContent.Data.uValue.pPeImage->File is an URL, expected object Moniker or File.");
+ else
+ return RTErrInfoSet(pErrInfo, VERR_CR_SPC_PEIMAGE_NO_CONTENT,
+ "SpcIndirectDataContent.Data.uValue.pPeImage->File has no content");
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * Generate the standard core code.
+ */
+#include <iprt/asn1-generator-sanity.h>
+
diff --git a/src/VBox/Runtime/common/crypto/spc-template.h b/src/VBox/Runtime/common/crypto/spc-template.h
new file mode 100644
index 00000000..510b66a6
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/spc-template.h
@@ -0,0 +1,198 @@
+/* $Id: spc-template.h $ */
+/** @file
+ * IPRT - Crypto - Microsoft SPC / Authenticode, Code Generator Template.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#define RTASN1TMPL_DECL RTDECL
+
+
+/*
+ * One SPC Serialized Page Hashes V2 Object.
+ */
+#define RTASN1TMPL_TYPE RTCRSPCSERIALIZEDPAGEHASHES
+#define RTASN1TMPL_EXT_NAME RTCrSpcSerializedPageHashes
+#define RTASN1TMPL_INT_NAME rtCrSpcSerializedPageHashes
+RTASN1TMPL_BEGIN_SETCORE();
+RTASN1TMPL_MEMBER( RawData, RTASN1OCTETSTRING, RTAsn1OctetString);
+RTASN1TMPL_EXEC_DECODE( rc = RTCrSpcSerializedPageHashes_UpdateDerivedData(pThis) ) /* no ; */
+RTASN1TMPL_EXEC_CLONE( rc = RTCrSpcSerializedPageHashes_UpdateDerivedData(pThis) ) /* no ; */
+RTASN1TMPL_END_SETCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * One SPC Serialized Object Attribute.
+ */
+#define RTASN1TMPL_TYPE RTCRSPCSERIALIZEDOBJECTATTRIBUTE
+#define RTASN1TMPL_EXT_NAME RTCrSpcSerializedObjectAttribute
+#define RTASN1TMPL_INT_NAME rtCrSpcSerializedObjectAttribute
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( Type, RTASN1OBJID, RTAsn1ObjId);
+RTASN1TMPL_MEMBER_DYN_BEGIN( Type, RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE, enmType, Allocation);
+RTASN1TMPL_MEMBER_DYN( u, pPageHashes, V1Hashes, RTCRSPCSERIALIZEDPAGEHASHES, RTCrSpcSerializedPageHashes, Allocation,
+ Type, enmType, RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1, RTCRSPC_PE_IMAGE_HASHES_V1_OID);
+RTASN1TMPL_MEMBER_DYN( u, pPageHashes, V2Hashes, RTCRSPCSERIALIZEDPAGEHASHES, RTCrSpcSerializedPageHashes, Allocation,
+ Type, enmType, RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V2, RTCRSPC_PE_IMAGE_HASHES_V2_OID);
+RTASN1TMPL_MEMBER_DYN_DEFAULT( u, pCore, RTASN1CORE, RTAsn1Core, Allocation,
+ Type, enmType, RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_UNKNOWN);
+RTASN1TMPL_MEMBER_DYN_END( Type, RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE, enmType, Allocation);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+/*
+ * Set of SPC Serialized Object Attributes.
+ */
+#define RTASN1TMPL_TYPE RTCRSPCSERIALIZEDOBJECTATTRIBUTES
+#define RTASN1TMPL_EXT_NAME RTCrSpcSerializedObjectAttributes
+#define RTASN1TMPL_INT_NAME rtCrSpcSerializedObjectAttributes
+RTASN1TMPL_SET_OF(RTCRSPCSERIALIZEDOBJECTATTRIBUTE, RTCrSpcSerializedObjectAttribute);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * One SPC Serialized Object.
+ */
+#define RTASN1TMPL_TYPE RTCRSPCSERIALIZEDOBJECT
+#define RTASN1TMPL_EXT_NAME RTCrSpcSerializedObject
+#define RTASN1TMPL_INT_NAME rtCrSpcSerializedObject
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER_EX( Uuid, RTASN1OCTETSTRING, RTAsn1OctetString,
+ RTASN1TMPL_MEMBER_CONSTR_MIN_MAX(Uuid, RTASN1OCTETSTRING, RTAsn1OctetString, 16, 16, RT_NOTHING));
+RTASN1TMPL_MEMBER( SerializedData, RTASN1OCTETSTRING, RTAsn1OctetString);
+RTASN1TMPL_EXEC_DECODE( rc = rtCrSpcSerializedObject_DecodeMore(pCursor, fFlags, pThis, pszErrorTag) ) /* no ; */
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * Choosy SPC strings.
+ */
+#define RTASN1TMPL_TYPE RTCRSPCSTRING
+#define RTASN1TMPL_EXT_NAME RTCrSpcString
+#define RTASN1TMPL_INT_NAME rtCrSpcString
+RTASN1TMPL_BEGIN_PCHOICE();
+RTASN1TMPL_PCHOICE_ITAG_CP( 0, RTCRSPCSTRINGCHOICE_UCS2, u.pUcs2, Ucs2, RTASN1STRING, RTAsn1BmpString);
+RTASN1TMPL_PCHOICE_ITAG_CP( 1, RTCRSPCSTRINGCHOICE_ASCII, u.pAscii, Ascii, RTASN1STRING, RTAsn1Ia5String);
+RTASN1TMPL_END_PCHOICE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * SPC Link.
+ */
+#define RTASN1TMPL_TYPE RTCRSPCLINK
+#define RTASN1TMPL_EXT_NAME RTCrSpcLink
+#define RTASN1TMPL_INT_NAME rtCrSpcLink
+RTASN1TMPL_BEGIN_PCHOICE();
+RTASN1TMPL_PCHOICE_ITAG_CP( 0, RTCRSPCLINKCHOICE_URL, u.pUrl, Url, RTASN1STRING, RTAsn1Ia5String);
+RTASN1TMPL_PCHOICE_ITAG( 1, RTCRSPCLINKCHOICE_MONIKER, u.pMoniker, Moniker, RTCRSPCSERIALIZEDOBJECT, RTCrSpcSerializedObject);
+RTASN1TMPL_PCHOICE_XTAG( 2, RTCRSPCLINKCHOICE_FILE, u.pT2, CtxTag2, File, RTCRSPCSTRING, RTCrSpcString);
+RTASN1TMPL_END_PCHOICE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * SPC PE Image Data.
+ *
+ * Note! This is not correctly declared in available specifications. The file
+ * member is tagged. Seeing the '--#public--' comment in the specs,
+ * one can't only guess that there are other alternatives in that part
+ * of the structure that microsoft does not wish to document.
+ */
+#define RTASN1TMPL_TYPE RTCRSPCPEIMAGEDATA
+#define RTASN1TMPL_EXT_NAME RTCrSpcPeImageData
+#define RTASN1TMPL_INT_NAME rtCrSpcPeImageData
+RTASN1TMPL_BEGIN_SEQCORE();
+/** @todo The flags defaults to includeResources. Could be expressed here rather
+ * than left to the user to deal with. */
+RTASN1TMPL_MEMBER_OPT_ITAG_EX( Flags, RTASN1BITSTRING, RTAsn1BitString, ASN1_TAG_BIT_STRING, RTASN1TMPL_ITAG_F_UP,
+ RTASN1TMPL_MEMBER_CONSTR_BITSTRING_MIN_MAX(Flags, 0, 3, RT_NOTHING));
+RTASN1TMPL_MEMBER_OPT_XTAG_EX( T0, CtxTag0, File, RTCRSPCLINK, RTCrSpcLink, 0, \
+ RTASN1TMPL_MEMBER_CONSTR_PRESENT(T0.File, RTCrSpcLink, RT_NOTHING));
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * SPC Attribute Type And Optional Value.
+ *
+ * Note! The value doesn't look very optional in available examples and specs.
+ * The available specs also claim there is an explicit 0 tag around the
+ * data, which isn't there is in signed executables. Gotta love Microsoft...
+ */
+#define RTASN1TMPL_TYPE RTCRSPCATTRIBUTETYPEANDOPTIONALVALUE
+#define RTASN1TMPL_EXT_NAME RTCrSpcAttributeTypeAndOptionalValue
+#define RTASN1TMPL_INT_NAME rtCrSpcAttributeTypeAndOptionalValue
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( Type, RTASN1OBJID, RTAsn1ObjId);
+RTASN1TMPL_MEMBER_DYN_BEGIN( Type, RTCRSPCAAOVTYPE, enmType, Allocation);
+RTASN1TMPL_MEMBER_DYN( uValue, pPeImage, PeImage, RTCRSPCPEIMAGEDATA, RTCrSpcPeImageData, Allocation,
+ Type, enmType, RTCRSPCAAOVTYPE_PE_IMAGE_DATA, RTCRSPCPEIMAGEDATA_OID);
+RTASN1TMPL_MEMBER_DYN_DEFAULT( uValue, pCore, RTASN1CORE, RTAsn1Core, Allocation,
+ Type, enmType, RTCRSPCAAOVTYPE_UNKNOWN);
+RTASN1TMPL_MEMBER_DYN_END( Type, RTCRSPCAAOVTYPE, enmType, Allocation);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * SPC Indirect Data Content.
+ */
+#define RTASN1TMPL_TYPE RTCRSPCINDIRECTDATACONTENT
+#define RTASN1TMPL_EXT_NAME RTCrSpcIndirectDataContent
+#define RTASN1TMPL_INT_NAME rtCrSpcIndirectDataContent
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( Data, RTCRSPCATTRIBUTETYPEANDOPTIONALVALUE, RTCrSpcAttributeTypeAndOptionalValue);
+RTASN1TMPL_MEMBER( DigestInfo, RTCRPKCS7DIGESTINFO, RTCrPkcs7DigestInfo);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
diff --git a/src/VBox/Runtime/common/crypto/ssl-openssl.cpp b/src/VBox/Runtime/common/crypto/ssl-openssl.cpp
new file mode 100644
index 00000000..1efafe74
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/ssl-openssl.cpp
@@ -0,0 +1,507 @@
+/** @file
+ * IPRT - Crypto - Secure Socket Layer (SSL) / Transport Security Layer (TLS).
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+#ifdef IPRT_WITH_OPENSSL /* whole file */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+# include "internal/iprt.h"
+# include <iprt/crypto/ssl.h>
+
+# include <iprt/asm.h>
+# include <iprt/assert.h>
+# include <iprt/err.h>
+# include <iprt/file.h>
+# include <iprt/mem.h>
+# include <iprt/string.h>
+
+# include "internal/magics.h"
+
+# include "internal/iprt-openssl.h"
+# include "internal/openssl-pre.h"
+# include <openssl/ssl.h>
+# include <openssl/tls1.h>
+# include "internal/openssl-post.h"
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+/**
+ * SSL instance data for OpenSSL.
+ */
+typedef struct RTCRSSLINT
+{
+ /** Magic value (RTCRSSLINT_MAGIC). */
+ uint32_t u32Magic;
+ /** Reference count. */
+ uint32_t volatile cRefs;
+ /** The SSL context. */
+ SSL_CTX *pCtx;
+} RTCRSSLINT;
+
+/**
+ * SSL session instance data for OpenSSL.
+ */
+typedef struct RTCRSSLSESSIONINT
+{
+ /** Magic value (RTCRSSLSESSIONINT_MAGIC). */
+ uint32_t u32Magic;
+ /** Reference count. */
+ uint32_t volatile cRefs;
+ /** RTCRSSLSESSION_F_XXX. */
+ uint32_t fFlags;
+
+ /** The SSL instance. */
+ SSL *pSsl;
+ /** The socket BIO instance. */
+ BIO *pBio;
+} RTCRSSLSESSIONINT;
+
+
+
+RTDECL(int) RTCrSslCreate(PRTCRSSL phSsl, uint32_t fFlags)
+{
+ AssertPtr(phSsl);
+ *phSsl = NIL_RTCRSSL;
+ AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+ SSL_library_init();
+
+ /*
+ * We aim at TLSv1 or higher here by default.
+ */
+# if OPENSSL_VERSION_NUMBER >= 0x10100000
+ const SSL_METHOD *pSslMethod = TLS_method();
+# elif OPENSSL_VERSION_NUMBER >= 0x10002000
+ const SSL_METHOD *pSslMethod = SSLv23_method();
+# elif OPENSSL_VERSION_NUMBER >= 0x10000000
+ const SSL_METHOD *pSslMethod = TLSv1_method();
+# else
+ SSL_METHOD *pSslMethod = TLSv1_method();
+# endif
+ if (pSslMethod)
+ {
+ RTCRSSLINT *pThis = (RTCRSSLINT *)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->pCtx = SSL_CTX_new(pSslMethod);
+ if (pThis->pCtx)
+ {
+ /* Help with above aim. */
+# if OPENSSL_VERSION_NUMBER >= 0x10100000
+# ifndef SSL_CTX_get_min_proto_version
+/* Some older OpenSSL 1.1.0 releases lack the getters, officially they were
+ * added with 1.1.1 but someone cherry picked them, just maybe too late. */
+# define SSL_CTX_get_min_proto_version(ctx) (0)
+# endif
+ if (SSL_CTX_get_min_proto_version(pThis->pCtx) < TLS1_VERSION)
+ SSL_CTX_set_min_proto_version(pThis->pCtx, TLS1_VERSION);
+# elif OPENSSL_VERSION_NUMBER >= 0x10002000
+ SSL_CTX_set_options(pThis->pCtx, SSL_OP_NO_SSLv2);
+ SSL_CTX_set_options(pThis->pCtx, SSL_OP_NO_SSLv3);
+# endif
+
+ /*
+ * Complete the instance and return it.
+ */
+ pThis->u32Magic = RTCRSSLINT_MAGIC;
+ pThis->cRefs = 1;
+
+ *phSsl = pThis;
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pThis);
+ }
+ return VERR_NO_MEMORY;
+ }
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(uint32_t) RTCrSslRetain(RTCRSSL hSsl)
+{
+ RTCRSSLINT *pThis = hSsl;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRSSLINT_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs > 1);
+ Assert(cRefs < 1024);
+ return cRefs;
+}
+
+
+/**
+ * Worker for RTCrSslRelease.
+ */
+static int rtCrSslDestroy(RTCRSSLINT *pThis)
+{
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTCRSSLINT_MAGIC);
+ SSL_CTX_free(pThis->pCtx);
+ pThis->pCtx = NULL;
+ RTMemFree(pThis);
+ return 0;
+}
+
+
+RTDECL(uint32_t) RTCrSslRelease(RTCRSSL hSsl)
+{
+ RTCRSSLINT *pThis = hSsl;
+ if (pThis == NIL_RTCRSSL)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRSSLINT_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ Assert(cRefs < 1024);
+ if (cRefs == 0)
+ return rtCrSslDestroy(pThis);
+ return cRefs;
+}
+
+
+RTDECL(int) RTCrSslSetCertificateFile(RTCRSSL hSsl, const char *pszFile, uint32_t fFlags)
+{
+ RTCRSSLINT *pThis = hSsl;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSSLINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!(fFlags & ~RTCRSSL_FILE_F_ASN1), VERR_INVALID_FLAGS);
+
+ int rcOssl = SSL_CTX_use_certificate_file(pThis->pCtx, pszFile,
+ RTCRSSL_FILE_F_ASN1 & fFlags ? SSL_FILETYPE_ASN1 : SSL_FILETYPE_PEM);
+ if (rcOssl != 0)
+ return VINF_SUCCESS;
+ return !pszFile || !*pszFile || !RTFileExists(pszFile) ? VERR_FILE_NOT_FOUND : VERR_OPEN_FAILED; /** @todo Better status codes */
+}
+
+
+RTDECL(int) RTCrSslSetPrivateKeyFile(RTCRSSL hSsl, const char *pszFile, uint32_t fFlags)
+{
+ RTCRSSLINT *pThis = hSsl;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSSLINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!(fFlags & ~RTCRSSL_FILE_F_ASN1), VERR_INVALID_FLAGS);
+
+ int rcOssl = SSL_CTX_use_PrivateKey_file(pThis->pCtx, pszFile,
+ RTCRSSL_FILE_F_ASN1 & fFlags ? SSL_FILETYPE_ASN1 : SSL_FILETYPE_PEM);
+ if (rcOssl != 0)
+ return VINF_SUCCESS;
+ return !pszFile || !*pszFile || !RTFileExists(pszFile) ? VERR_FILE_NOT_FOUND : VERR_OPEN_FAILED; /** @todo Better status codes */
+}
+
+
+RTDECL(int) RTCrSslLoadTrustedRootCerts(RTCRSSL hSsl, const char *pszFile, const char *pszDir)
+{
+ RTCRSSLINT *pThis = hSsl;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSSLINT_MAGIC, VERR_INVALID_HANDLE);
+
+ int rcOssl = SSL_CTX_load_verify_locations(pThis->pCtx, pszFile, pszDir);
+ if (rcOssl != 0)
+ return VINF_SUCCESS;
+
+ if (!pszFile || !*pszFile || !RTFileExists(pszFile))
+ return VERR_FILE_NOT_FOUND;
+ return VERR_OPEN_FAILED; /** @todo Better status codes */
+}
+
+
+RTDECL(int) RTCrSslSetNoPeerVerify(RTCRSSL hSsl)
+{
+ RTCRSSLINT *pThis = hSsl;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSSLINT_MAGIC, VERR_INVALID_HANDLE);
+
+ SSL_CTX_set_verify(pThis->pCtx, SSL_VERIFY_NONE, NULL);
+ return VINF_SUCCESS;
+}
+
+
+
+//RTDECL(int) RTCrSslCreateSession(RTCRSSL hSsl, RTSOCKET hSocket, uint32_t fFlags, PRTCRSSLSESSION phSslConn);
+
+RTDECL(int) RTCrSslCreateSessionForNativeSocket(RTCRSSL hSsl, RTHCINTPTR hNativeSocket, uint32_t fFlags,
+ PRTCRSSLSESSION phSslSession)
+{
+ /*
+ * Validate input.
+ */
+ *phSslSession = NIL_RTCRSSLSESSION;
+
+ RTCRSSLINT *pThis = hSsl;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSSLINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!(fFlags & ~RTCRSSLSESSION_F_NON_BLOCKING), VERR_INVALID_FLAGS);
+
+ /*
+ * Create a new session.
+ */
+ int rc = VERR_NO_MEMORY;
+ RTCRSSLSESSIONINT *pSession = (RTCRSSLSESSIONINT *)RTMemAllocZ(sizeof(*pSession));
+ if (pSession)
+ {
+ pSession->pSsl = SSL_new(pThis->pCtx);
+ if (pSession->pSsl)
+ {
+ /* Disable read-ahead if non-blocking socket relying on select/poll. */
+ if (fFlags & RTCRSSLSESSION_F_NON_BLOCKING)
+ SSL_set_read_ahead(pSession->pSsl, 0);
+
+ /* Create a wrapper for the socket handle. */
+ pSession->pBio = BIO_new_socket(hNativeSocket, BIO_NOCLOSE);
+ if (pSession->pBio)
+ {
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)) || LIBRESSL_VERSION_NUMBER >= 0x2070000f
+ BIO_up_ref(pSession->pBio); /* our reference. */
+# endif
+ SSL_set_bio(pSession->pSsl, pSession->pBio, pSession->pBio);
+
+ /*
+ * Done.
+ */
+ pSession->cRefs = 1;
+ pSession->u32Magic = RTCRSSLSESSIONINT_MAGIC;
+ *phSslSession = pSession;
+ return VINF_SUCCESS;
+ }
+
+ SSL_free(pSession->pSsl);
+ pSession->pSsl = NULL;
+ }
+ RTMemFree(pSession);
+ }
+ return rc;
+}
+
+
+/*********************************************************************************************************************************
+* Session implementation. *
+*********************************************************************************************************************************/
+
+RTDECL(uint32_t) RTCrSslSessionRetain(RTCRSSLSESSION hSslSession)
+{
+ RTCRSSLSESSIONINT *pThis = hSslSession;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRSSLSESSIONINT_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs > 1);
+ Assert(cRefs < 1024);
+ return cRefs;
+}
+
+
+/**
+ * Worker for RTCrSslRelease.
+ */
+static int rtCrSslSessionDestroy(RTCRSSLSESSIONINT *pThis)
+{
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTCRSSLSESSIONINT_MAGIC);
+ SSL_free(pThis->pSsl);
+ pThis->pSsl = NULL;
+# if (OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)) || LIBRESSL_VERSION_NUMBER >= 0x2070000f
+ BIO_free(pThis->pBio);
+# endif
+ pThis->pBio = NULL;
+ RTMemFree(pThis);
+ return 0;
+}
+
+
+RTDECL(uint32_t) RTCrSslSessionRelease(RTCRSSLSESSION hSslSession)
+{
+ RTCRSSLSESSIONINT *pThis = hSslSession;
+ if (pThis == NIL_RTCRSSLSESSION)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRSSLSESSIONINT_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ Assert(cRefs < 1024);
+ if (cRefs == 0)
+ return rtCrSslSessionDestroy(pThis);
+ return cRefs;
+}
+
+
+RTDECL(int) RTCrSslSessionAccept(RTCRSSLSESSION hSslSession, uint32_t fFlags)
+{
+ RTCRSSLSESSIONINT *pThis = hSslSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSSLSESSIONINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+ int rcOssl = SSL_accept(pThis->pSsl);
+ if (rcOssl > 0)
+ return VINF_SUCCESS;
+
+ /** @todo better status codes. */
+ if (BIO_should_retry(pThis->pBio))
+ return VERR_TRY_AGAIN;
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTCrSslSessionConnect(RTCRSSLSESSION hSslSession, uint32_t fFlags)
+{
+ RTCRSSLSESSIONINT *pThis = hSslSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSSLSESSIONINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+ int rcOssl = SSL_connect(pThis->pSsl);
+ if (rcOssl > 0)
+ return VINF_SUCCESS;
+
+ /** @todo better status codes. */
+ if (BIO_should_retry(pThis->pBio))
+ return VERR_TRY_AGAIN;
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(const char *) RTCrSslSessionGetVersion(RTCRSSLSESSION hSslSession)
+{
+ RTCRSSLSESSIONINT *pThis = hSslSession;
+ AssertPtrReturn(pThis, NULL);
+ AssertReturn(pThis->u32Magic == RTCRSSLSESSIONINT_MAGIC, NULL);
+
+ return SSL_get_version(pThis->pSsl);
+}
+
+
+RTDECL(int) RTCrSslSessionGetCertIssuerNameAsString(RTCRSSLSESSION hSslSession, char *pszBuf, size_t cbBuf, size_t *pcbActual)
+{
+ RTCRSSLSESSIONINT *pThis = hSslSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSSLSESSIONINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrNull(pszBuf);
+ AssertPtrNull(pcbActual);
+ if (pcbActual)
+ *pcbActual = 0;
+
+ /*
+ * Get and format the certificate issuer name.
+ */
+ int rc = VERR_NOT_AVAILABLE;
+ X509 *pCert = SSL_get_certificate(pThis->pSsl);
+ if (pCert)
+ {
+ X509_NAME *pIssuer = X509_get_issuer_name(pCert);
+ if (pIssuer)
+ {
+ char *pszSrc = X509_NAME_oneline(pIssuer, NULL, 0);
+ if (pszSrc)
+ {
+ /*
+ * Copy out the result and free it.
+ */
+ size_t cbNeeded = strlen(pszSrc) + 1;
+ if (pcbActual)
+ *pcbActual = cbNeeded;
+
+ if (pszBuf != NULL && cbBuf > 0)
+ {
+ if (cbBuf >= cbNeeded)
+ {
+ memcpy(pszBuf, pszSrc, cbNeeded);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ memcpy(pszBuf, pszSrc, cbBuf - 1);
+ pszBuf[cbBuf - 1] = '\0';
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ OPENSSL_free(pszSrc);
+ }
+ }
+ }
+ return rc;
+}
+
+
+RTDECL(bool) RTCrSslSessionPending(RTCRSSLSESSION hSslSession)
+{
+ RTCRSSLSESSIONINT *pThis = hSslSession;
+ AssertPtrReturn(pThis, true);
+ AssertReturn(pThis->u32Magic == RTCRSSLSESSIONINT_MAGIC, true);
+
+ return SSL_pending(pThis->pSsl) != 0;
+}
+
+
+RTDECL(ssize_t) RTCrSslSessionRead(RTCRSSLSESSION hSslSession, void *pvBuf, size_t cbToRead)
+{
+ RTCRSSLSESSIONINT *pThis = hSslSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSSLSESSIONINT_MAGIC, VERR_INVALID_HANDLE);
+
+ Assert((size_t)(int)cbToRead == cbToRead);
+
+ int cbActual = SSL_read(pThis->pSsl, pvBuf, (int)cbToRead);
+ if (cbActual > 0)
+ return cbActual;
+ if (BIO_should_retry(pThis->pBio))
+ return VERR_TRY_AGAIN;
+ return VERR_READ_ERROR; /** @todo better status codes. */
+}
+
+
+RTDECL(ssize_t) RTCrSslSessionWrite(RTCRSSLSESSION hSslSession, void const *pvBuf, size_t cbToWrite)
+{
+ RTCRSSLSESSIONINT *pThis = hSslSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSSLSESSIONINT_MAGIC, VERR_INVALID_HANDLE);
+
+ Assert((size_t)(int)cbToWrite == cbToWrite);
+ Assert(cbToWrite != 0 /* undefined behavior if zero */);
+
+ int cbActual = SSL_write(pThis->pSsl, pvBuf, (int)cbToWrite);
+ if (cbActual > 0)
+ return cbActual;
+ if (BIO_should_retry(pThis->pBio))
+ return VERR_TRY_AGAIN;
+ return VERR_WRITE_ERROR; /** @todo better status codes. */
+}
+
+#endif /* IPRT_WITH_OPENSSL */
+
diff --git a/src/VBox/Runtime/common/crypto/store-cert-add-basic.cpp b/src/VBox/Runtime/common/crypto/store-cert-add-basic.cpp
new file mode 100644
index 00000000..a98f75a0
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/store-cert-add-basic.cpp
@@ -0,0 +1,865 @@
+/* $Id: store-cert-add-basic.cpp $ */
+/** @file
+ * IPRT - Cryptographic (Certificate) Store, RTCrStoreCertAddFromDir.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/store.h>
+
+#include <iprt/assert.h>
+#include <iprt/crypto/pem.h>
+#include <iprt/dir.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/sha.h>
+#include <iprt/string.h>
+
+#include "x509-internal.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** BEGIN CERTIFICATE / END CERTIFICATE. */
+static RTCRPEMMARKERWORD const g_aWords_Certificate[] =
+{
+ { RT_STR_TUPLE("CERTIFICATE") }
+};
+
+/** BEGIN TRUSTED CERTIFICATE / END TRUSTED CERTIFICATE. */
+static RTCRPEMMARKERWORD const g_aWords_TrustedCertificate[] =
+{
+ { RT_STR_TUPLE("TRUSTED") },
+ { RT_STR_TUPLE("CERTIFICATE") }
+};
+
+/** BEGIN X509 CERTIFICATE / END X509 CERTIFICATE. (old) */
+static RTCRPEMMARKERWORD const g_aWords_X509Certificate[] =
+{
+ { RT_STR_TUPLE("X509") },
+ { RT_STR_TUPLE("CERTIFICATE") }
+};
+
+/**
+ * X509 Certificate markers.
+ *
+ * @remark See crypto/pem/pem.h in OpenSSL for a matching list.
+ */
+static RTCRPEMMARKER const g_aX509CertificateMarkers[3] =
+{
+ { g_aWords_Certificate, RT_ELEMENTS(g_aWords_Certificate) },
+ { g_aWords_TrustedCertificate, RT_ELEMENTS(g_aWords_TrustedCertificate) },
+ { g_aWords_X509Certificate, RT_ELEMENTS(g_aWords_X509Certificate) }
+};
+
+
+
+#ifdef RT_STRICT
+/**
+ * Checks if we've found all the certificates already.
+ *
+ * @returns true if all found, false if not.
+ * @param afFound Indicator array.
+ * @param cWanted Number of wanted certificates.
+ */
+DECLINLINE(bool) rtCrStoreAllDone(bool const *afFound, size_t cWanted)
+{
+ while (cWanted-- > 0)
+ if (!afFound[cWanted])
+ return false;
+ return true;
+}
+#endif /* RT_STRICT */
+
+
+/**
+ * Checks if the given certificate specs matches the given wanted poster.
+ *
+ * @returns true if match, false if not.
+ * @param pWanted The certificate wanted poster.
+ * @param cbEncoded The candidate certificate encoded size.
+ * @param paSha1 The candidate certificate SHA-1 fingerprint.
+ * @param paSha512 The candidate certificate SHA-512 fingerprint.
+ * @param pCert The decoded candidate certificate, optional. If not
+ * given the result will be uncertain.
+ */
+DECLINLINE(bool) rtCrStoreIsCertEqualToWanted(PCRTCRCERTWANTED pWanted,
+ size_t cbEncoded,
+ uint8_t const pabSha1[RTSHA1_HASH_SIZE],
+ uint8_t const pabSha512[RTSHA512_HASH_SIZE],
+ PCRTCRX509CERTIFICATE pCert)
+{
+ if ( pWanted->cbEncoded != cbEncoded
+ && pWanted->cbEncoded != 0)
+ return false;
+
+ if ( pWanted->fSha1Fingerprint
+ && memcmp(pWanted->abSha1, pabSha1, RTSHA1_HASH_SIZE) != 0)
+ return false;
+
+ if ( pWanted->fSha512Fingerprint
+ && memcmp(pWanted->abSha512, pabSha512, RTSHA512_HASH_SIZE) != 0)
+ return false;
+
+ if ( pWanted->pszSubject
+ && pCert
+ && !RTCrX509Name_MatchWithString(&pCert->TbsCertificate.Subject, pWanted->pszSubject))
+ return false;
+
+ return true;
+}
+
+
+/**
+ * Checks if a certificate is wanted.
+ *
+ * @returns true if match, false if not.
+ * @param paWanted The certificate wanted posters.
+ * @param cWanted The number of wanted posters.
+ * @param apfFound Found initicators running paralell to @a paWanted.
+ * @param cbEncoded The candidate certificate encoded size.
+ * @param paSha1 The candidate certificate SHA-1 fingerprint.
+ * @param paSha512 The candidate certificate SHA-512 fingerprint.
+ * @param pCert The decoded candidate certificate, optional. If not
+ * given the result will be uncertain.
+ */
+DECLINLINE(bool) rtCrStoreIsCertWanted(PCRTCRCERTWANTED paWanted, size_t cWanted, bool const *pafFound, size_t cbEncoded,
+ uint8_t const pabSha1[RTSHA1_HASH_SIZE], uint8_t const pabSha512[RTSHA512_HASH_SIZE],
+ PCRTCRX509CERTIFICATE pCert)
+{
+ for (size_t iCert = 0; iCert < cWanted; iCert++)
+ if (!pafFound[iCert])
+ if (rtCrStoreIsCertEqualToWanted(&paWanted[iCert], cbEncoded, pabSha1, pabSha512, pCert))
+ return true;
+ return false;
+}
+
+
+/**
+ * Marks a certificate as found after it has been added to the store.
+ *
+ * May actually mark several certificates as found if there are duplicates or
+ * ambiguities in the wanted list.
+ *
+ * @returns true if all have been found, false if more to search for.
+ *
+ * @param apfFound Found initicators running paralell to @a paWanted.
+ * This is what this function updates.
+ * @param paWanted The certificate wanted posters.
+ * @param cWanted The number of wanted posters.
+ * @param cbEncoded The candidate certificate encoded size.
+ * @param paSha1 The candidate certificate SHA-1 fingerprint.
+ * @param paSha512 The candidate certificate SHA-512 fingerprint.
+ * @param pCert The decoded candidate certificate, optional. If not
+ * given the result will be uncertain.
+ */
+static bool rtCrStoreMarkCertFound(bool *pafFound, PCRTCRCERTWANTED paWanted, size_t cWanted, size_t cbEncoded,
+ uint8_t const pabSha1[RTSHA1_HASH_SIZE], uint8_t const pabSha512[RTSHA512_HASH_SIZE],
+ PCRTCRX509CERTIFICATE pCert)
+{
+ size_t cFound = 0;
+ for (size_t iCert = 0; iCert < cWanted; iCert++)
+ if (pafFound[iCert])
+ cFound++;
+ else if (rtCrStoreIsCertEqualToWanted(&paWanted[iCert], cbEncoded, pabSha1, pabSha512, pCert))
+ {
+ pafFound[iCert] = true;
+ cFound++;
+ }
+ return cFound == cWanted;
+}
+
+
+RTDECL(int) RTCrStoreCertAddFromStore(RTCRSTORE hStore, uint32_t fFlags, RTCRSTORE hStoreSrc)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
+
+ /*
+ * Enumerate all the certificates in the source store, adding them to the destination.
+ */
+ RTCRSTORECERTSEARCH Search;
+ int rc = RTCrStoreCertFindAll(hStoreSrc, &Search);
+ if (RT_SUCCESS(rc))
+ {
+ PCRTCRCERTCTX pCertCtx;
+ while ((pCertCtx = RTCrStoreCertSearchNext(hStoreSrc, &Search)) != NULL)
+ {
+ int rc2 = RTCrStoreCertAddEncoded(hStore, pCertCtx->fFlags | (fFlags & RTCRCERTCTX_F_ADD_IF_NOT_FOUND),
+ pCertCtx->pabEncoded, pCertCtx->cbEncoded, NULL);
+ if (RT_FAILURE(rc2))
+ {
+ rc = rc2;
+ if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ break;
+ }
+ RTCrCertCtxRelease(pCertCtx);
+ }
+
+ int rc2 = RTCrStoreCertSearchDestroy(hStoreSrc, &Search); AssertRC(rc2);
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertAddFromStore);
+
+
+RTDECL(int) RTCrStoreCertAddWantedFromStore(RTCRSTORE hStore, uint32_t fFlags, RTCRSTORE hSrcStore,
+ PCRTCRCERTWANTED paWanted, size_t cWanted, bool *pafFound)
+{
+ /*
+ * Validate input a little.
+ */
+ AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
+ fFlags |= RTCRCERTCTX_F_ADD_IF_NOT_FOUND; /* forced */
+
+ AssertReturn(cWanted, VERR_NOT_FOUND);
+ for (uint32_t i = 0; i < cWanted; i++)
+ {
+ AssertReturn(!paWanted[i].pszSubject || *paWanted[i].pszSubject, VERR_INVALID_PARAMETER);
+ AssertReturn( paWanted[i].pszSubject
+ || paWanted[i].fSha1Fingerprint
+ || paWanted[i].fSha512Fingerprint,
+ VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Make sure we've got a result array.
+ */
+ bool *pafFoundFree = NULL;
+ if (!pafFound)
+ {
+ pafFound = pafFoundFree = (bool *)RTMemTmpAllocZ(sizeof(bool) * cWanted);
+ AssertReturn(pafFound, VERR_NO_TMP_MEMORY);
+ }
+
+ /*
+ * Enumerate the store entries.
+ */
+ RTCRSTORECERTSEARCH Search;
+ int rc = RTCrStoreCertFindAll(hSrcStore, &Search);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VWRN_NOT_FOUND;
+ PCRTCRCERTCTX pCertCtx;
+ while ((pCertCtx = RTCrStoreCertSearchNext(hSrcStore, &Search)) != NULL)
+ {
+ if ( (pCertCtx->fFlags & RTCRCERTCTX_F_ENC_MASK) == RTCRCERTCTX_F_ENC_X509_DER
+ && pCertCtx->cbEncoded > 0
+ && pCertCtx->pCert)
+ {
+ /*
+ * If the certificate is wanted, try add it to the store.
+ */
+ uint8_t abSha1[RTSHA1_HASH_SIZE];
+ RTSha1(pCertCtx->pabEncoded, pCertCtx->cbEncoded, abSha1);
+ uint8_t abSha512[RTSHA512_HASH_SIZE];
+ RTSha512(pCertCtx->pabEncoded, pCertCtx->cbEncoded, abSha512);
+ if (rtCrStoreIsCertWanted(paWanted, cWanted, pafFound, pCertCtx->cbEncoded, abSha1, abSha512, pCertCtx->pCert))
+ {
+ int rc2 = RTCrStoreCertAddEncoded(hStore,
+ RTCRCERTCTX_F_ENC_X509_DER | (fFlags & RTCRCERTCTX_F_ADD_IF_NOT_FOUND),
+ pCertCtx->pabEncoded, pCertCtx->cbEncoded, NULL /*pErrInfo*/);
+ if (RT_SUCCESS(rc2))
+ {
+ /*
+ * Mark it as found, stop if we've found all.
+ */
+ if (rtCrStoreMarkCertFound(pafFound, paWanted, cWanted,
+ pCertCtx->cbEncoded, abSha1, abSha512, pCertCtx->pCert))
+ {
+ if (RT_SUCCESS(rc))
+ rc = VINF_SUCCESS;
+ RTCrCertCtxRelease(pCertCtx);
+ break;
+ }
+ }
+ else
+ {
+ /*
+ * Some error adding the certificate. Since it cannot be anything with
+ * the encoding, it must be something with the store or resources, so
+ * always return the error status.
+ */
+ rc = rc2;
+ if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ {
+ RTCrCertCtxRelease(pCertCtx);
+ break;
+ }
+ }
+ }
+ }
+ RTCrCertCtxRelease(pCertCtx);
+ }
+ int rc2 = RTCrStoreCertSearchDestroy(hSrcStore, &Search);
+ AssertRC(rc2);
+ }
+
+ if (pafFoundFree)
+ RTMemTmpFree(pafFoundFree);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertAddWantedFromStore);
+
+
+RTDECL(int) RTCrStoreCertCheckWanted(RTCRSTORE hStore, PCRTCRCERTWANTED paWanted, size_t cWanted, bool *pafFound)
+{
+ /*
+ * Validate input a little.
+ */
+ AssertReturn(cWanted, VERR_NOT_FOUND);
+ for (uint32_t i = 0; i < cWanted; i++)
+ {
+ AssertReturn(!paWanted[i].pszSubject || *paWanted[i].pszSubject, VERR_INVALID_PARAMETER);
+ AssertReturn( paWanted[i].pszSubject
+ || paWanted[i].fSha1Fingerprint
+ || paWanted[i].fSha512Fingerprint,
+ VERR_INVALID_PARAMETER);
+ }
+ AssertPtrReturn(pafFound, VERR_INVALID_POINTER);
+
+ /*
+ * Clear the found array.
+ */
+ for (uint32_t iCert = 0; iCert < cWanted; iCert++)
+ pafFound[iCert] = false;
+
+ /*
+ * Enumerate the store entries.
+ */
+ RTCRSTORECERTSEARCH Search;
+ int rc = RTCrStoreCertFindAll(hStore, &Search);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VWRN_NOT_FOUND;
+ PCRTCRCERTCTX pCertCtx;
+ while ((pCertCtx = RTCrStoreCertSearchNext(hStore, &Search)) != NULL)
+ {
+ if ( (pCertCtx->fFlags & RTCRCERTCTX_F_ENC_MASK) == RTCRCERTCTX_F_ENC_X509_DER
+ && pCertCtx->cbEncoded > 0
+ && pCertCtx->pCert)
+ {
+ /*
+ * Hash it and check if it's wanted. Stop when we've found all.
+ */
+ uint8_t abSha1[RTSHA1_HASH_SIZE];
+ RTSha1(pCertCtx->pabEncoded, pCertCtx->cbEncoded, abSha1);
+ uint8_t abSha512[RTSHA512_HASH_SIZE];
+ RTSha512(pCertCtx->pabEncoded, pCertCtx->cbEncoded, abSha512);
+ if (rtCrStoreMarkCertFound(pafFound, paWanted, cWanted, pCertCtx->cbEncoded, abSha1, abSha512, pCertCtx->pCert))
+ {
+ rc = VINF_SUCCESS;
+ RTCrCertCtxRelease(pCertCtx);
+ break;
+ }
+ }
+ RTCrCertCtxRelease(pCertCtx);
+ }
+ int rc2 = RTCrStoreCertSearchDestroy(hStore, &Search);
+ AssertRC(rc2);
+ }
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertAddWantedFromStore);
+
+
+RTDECL(int) RTCrStoreCertAddFromFile(RTCRSTORE hStore, uint32_t fFlags, const char *pszFilename, PRTERRINFO pErrInfo)
+{
+ AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
+
+ size_t cbContent;
+ void *pvContent;
+ int rc = RTFileReadAllEx(pszFilename, 0, 64U*_1M, RTFILE_RDALL_O_DENY_WRITE, &pvContent, &cbContent);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Is it a java key store file?
+ */
+ if ( cbContent > 32
+ && ((uint32_t const *)pvContent)[0] == RT_H2BE_U32_C(UINT32_C(0xfeedfeed)) /* magic */
+ && ((uint32_t const *)pvContent)[1] == RT_H2BE_U32_C(UINT32_C(0x00000002)) /* version */ )
+ rc = RTCrStoreCertAddFromJavaKeyStoreInMem(hStore, fFlags, pvContent, cbContent, pszFilename, pErrInfo);
+ /*
+ * No assume PEM or DER encoded binary certificate.
+ */
+ else if (cbContent)
+ {
+ PCRTCRPEMSECTION pSectionHead;
+ rc = RTCrPemParseContent(pvContent, cbContent,
+ (fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)
+ ? RTCRPEMREADFILE_F_CONTINUE_ON_ENCODING_ERROR : 0,
+ g_aX509CertificateMarkers, RT_ELEMENTS(g_aX509CertificateMarkers),
+ &pSectionHead, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ PCRTCRPEMSECTION pCurSec = pSectionHead;
+ while (pCurSec)
+ {
+ int rc2 = RTCrStoreCertAddEncoded(hStore,
+ RTCRCERTCTX_F_ENC_X509_DER | (fFlags & RTCRCERTCTX_F_ADD_IF_NOT_FOUND),
+ pCurSec->pbData, pCurSec->cbData,
+ !RTErrInfoIsSet(pErrInfo) ? pErrInfo : NULL);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ {
+ rc = rc2;
+ if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ break;
+ }
+ pCurSec = pCurSec->pNext;
+ }
+
+ RTCrPemFreeSections(pSectionHead);
+ }
+ }
+ else /* Will happen if proxy not set / no connection available. */
+ rc = RTErrInfoSetF(pErrInfo, VERR_EOF, "Certificate '%s' is empty", pszFilename);
+ RTFileReadAllFree(pvContent, cbContent);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "RTFileReadAllEx failed with %Rrc on '%s'", rc, pszFilename);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertAddFromFile);
+
+
+RTDECL(int) RTCrStoreCertAddWantedFromFile(RTCRSTORE hStore, uint32_t fFlags, const char *pszFilename,
+ PCRTCRCERTWANTED paWanted, size_t cWanted, bool *pafFound, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate input a little.
+ */
+ AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
+ fFlags |= RTCRCERTCTX_F_ADD_IF_NOT_FOUND; /* forced */
+
+ AssertReturn(cWanted, VERR_NOT_FOUND);
+ for (uint32_t i = 0; i < cWanted; i++)
+ {
+ AssertReturn(!paWanted[i].pszSubject || *paWanted[i].pszSubject, VERR_INVALID_PARAMETER);
+ AssertReturn( paWanted[i].pszSubject
+ || paWanted[i].fSha1Fingerprint
+ || paWanted[i].fSha512Fingerprint,
+ VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Make sure we've got a result array.
+ */
+ bool *pafFoundFree = NULL;
+ if (!pafFound)
+ {
+ pafFound = pafFoundFree = (bool *)RTMemTmpAllocZ(sizeof(bool) * cWanted);
+ AssertReturn(pafFound, VERR_NO_TMP_MEMORY);
+ }
+
+ size_t cbContent;
+ void *pvContent;
+ int rc = RTFileReadAllEx(pszFilename, 0, 64U*_1M, RTFILE_RDALL_O_DENY_WRITE, &pvContent, &cbContent);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Is it a java key store file? If so, load it into a tmp store
+ * which we can search. Don't want to duplicate the JKS reader code.
+ */
+ if ( cbContent > 32
+ && ((uint32_t const *)pvContent)[0] == RT_H2BE_U32_C(UINT32_C(0xfeedfeed)) /* magic */
+ && ((uint32_t const *)pvContent)[1] == RT_H2BE_U32_C(UINT32_C(0x00000002)) /* version */ )
+ {
+ RTCRSTORE hTmpStore;
+ rc = RTCrStoreCreateInMem(&hTmpStore, 64);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrStoreCertAddFromJavaKeyStoreInMem(hStore, fFlags, pvContent, cbContent, pszFilename, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = RTCrStoreCertAddWantedFromStore(hStore, fFlags, hTmpStore, paWanted, cWanted, pafFound);
+ RTCrStoreRelease(hTmpStore);
+ }
+ else
+ rc = RTErrInfoSet(pErrInfo, rc, "Error creating temporary crypto store");
+ }
+ /*
+ * No assume PEM or DER encoded binary certificate. Inspect them one by one.
+ */
+ else if (cbContent)
+ {
+ PCRTCRPEMSECTION pSectionHead;
+ rc = RTCrPemParseContent(pvContent, cbContent,
+ (fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)
+ ? RTCRPEMREADFILE_F_CONTINUE_ON_ENCODING_ERROR : 0,
+ g_aX509CertificateMarkers, RT_ELEMENTS(g_aX509CertificateMarkers),
+ &pSectionHead, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VWRN_NOT_FOUND;
+ for (PCRTCRPEMSECTION pCurSec = pSectionHead; pCurSec; pCurSec = pCurSec->pNext)
+ {
+ if (!pCurSec->cbData)
+ continue;
+
+ /*
+ * See if this is a binary blob we might be interested in.
+ */
+ uint8_t abSha1[RTSHA1_HASH_SIZE];
+ RTSha1(pCurSec->pbData, pCurSec->cbData, abSha1);
+ uint8_t abSha512[RTSHA512_HASH_SIZE];
+ RTSha512(pCurSec->pbData, pCurSec->cbData, abSha512);
+ if (!rtCrStoreIsCertWanted(paWanted, cWanted, pafFound, pCurSec->cbData, abSha1, abSha512, NULL))
+ continue;
+
+ /*
+ * Decode the certificate so we can match the subject string.
+ */
+ RTASN1CURSORPRIMARY Cursor;
+ RTAsn1CursorInitPrimary(&Cursor, pCurSec->pbData, (uint32_t)pCurSec->cbData,
+ !RTErrInfoIsSet(pErrInfo) ? pErrInfo : NULL,
+ &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "InMem");
+ RTCRX509CERTIFICATE X509Cert;
+ int rc2 = RTCrX509Certificate_DecodeAsn1(&Cursor.Cursor, 0, &X509Cert, "Cert");
+ if (RT_SUCCESS(rc2))
+ {
+ rc2 = RTCrX509Certificate_CheckSanity(&X509Cert, 0, !RTErrInfoIsSet(pErrInfo) ? pErrInfo : NULL, "Cert");
+ if (RT_SUCCESS(rc2))
+ {
+ if (rtCrStoreIsCertWanted(paWanted, cWanted, pafFound, pCurSec->cbData, abSha1, abSha512, &X509Cert))
+ {
+ /*
+ * The certificate is wanted, now add it to the store.
+ */
+ rc2 = RTCrStoreCertAddEncoded(hStore,
+ RTCRCERTCTX_F_ENC_X509_DER
+ | (fFlags & RTCRCERTCTX_F_ADD_IF_NOT_FOUND),
+ pCurSec->pbData, pCurSec->cbData,
+ !RTErrInfoIsSet(pErrInfo) ? pErrInfo : NULL);
+ if (RT_SUCCESS(rc2))
+ {
+ /*
+ * Mark it as found, stop if we've found all.
+ */
+ if (rtCrStoreMarkCertFound(pafFound, paWanted, cWanted,
+ pCurSec->cbData, abSha1, abSha512, &X509Cert))
+ {
+ RTAsn1VtDelete(&X509Cert.SeqCore.Asn1Core);
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ }
+ }
+ else
+ Assert(!pErrInfo || RTErrInfoIsSet(pErrInfo));
+ RTAsn1VtDelete(&X509Cert.SeqCore.Asn1Core);
+ }
+ else if (!RTErrInfoIsSet(pErrInfo))
+ RTErrInfoSetF(pErrInfo, rc2, "RTCrX509Certificate_DecodeAsn1 failed");
+
+ /*
+ * Stop on error, if requested. Otherwise, let pErrInfo keep it.
+ */
+ if (RT_FAILURE(rc2) && !(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ {
+ rc = rc2;
+ break;
+ }
+ } /* For each PEM section. */
+
+ RTCrPemFreeSections(pSectionHead);
+ }
+ }
+ else /* Will happen if proxy not set / no connection available. */
+ rc = RTErrInfoSetF(pErrInfo, VERR_EOF, "Certificate '%s' is empty", pszFilename);
+ RTFileReadAllFree(pvContent, cbContent);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "RTFileReadAllEx failed with %Rrc on '%s'", rc, pszFilename);
+
+ if (pafFoundFree)
+ RTMemTmpFree(pafFoundFree);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertAddWantedFromFile);
+
+
+/**
+ * Checks if the directory entry matches the specified suffixes.
+ *
+ * @returns true on match, false on mismatch.
+ * @param pDirEntry The directory to check.
+ * @param paSuffixes The array of suffixes to match against.
+ * @param cSuffixes The number of suffixes in the array.
+ */
+DECLINLINE(bool) rtCrStoreIsSuffixMatch(PCRTDIRENTRY pDirEntry, PCRTSTRTUPLE paSuffixes, size_t cSuffixes)
+{
+ if (cSuffixes == 0)
+ return true;
+
+ size_t const cchName = pDirEntry->cbName;
+ size_t i = cSuffixes;
+ while (i-- > 0)
+ if ( cchName > paSuffixes[i].cch
+ && memcmp(&pDirEntry->szName[cchName - paSuffixes[i].cch], paSuffixes[i].psz, paSuffixes[i].cch) == 0)
+ return true;
+
+ return false;
+}
+
+
+RTDECL(int) RTCrStoreCertAddFromDir(RTCRSTORE hStore, uint32_t fFlags, const char *pszDir,
+ PCRTSTRTUPLE paSuffixes, size_t cSuffixes, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
+ size_t i = cSuffixes;
+ while (i-- > 0)
+ {
+ Assert(paSuffixes[i].cch > 0);
+ Assert(strlen(paSuffixes[i].psz) == paSuffixes[i].cch);
+ }
+
+ /*
+ * Prepare for constructing path to the files in the directory, so that we
+ * can open them.
+ */
+ char szPath[RTPATH_MAX];
+ int rc = RTStrCopy(szPath, sizeof(szPath), pszDir);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cchPath = RTPathEnsureTrailingSeparator(szPath, sizeof(szPath));
+ if (cchPath > 0)
+ {
+ size_t const cbMaxFilename = sizeof(szPath) - cchPath;
+
+ /*
+ * Enumerate the directory.
+ */
+ RTDIR hDir;
+ rc = RTDirOpen(&hDir, pszDir);
+ if (RT_SUCCESS(rc))
+ {
+ for (;;)
+ {
+ /* Read the next entry. */
+ union
+ {
+ RTDIRENTRY DirEntry;
+ uint8_t abPadding[RTPATH_MAX + sizeof(RTDIRENTRY)];
+ } u;
+ size_t cbBuf = sizeof(u);
+ int rc2 = RTDirRead(hDir, &u.DirEntry, &cbBuf);
+ if (RT_SUCCESS(rc2))
+ {
+ if ( ( u.DirEntry.enmType == RTDIRENTRYTYPE_FILE
+ || u.DirEntry.enmType == RTDIRENTRYTYPE_SYMLINK
+ || ( u.DirEntry.enmType == RTDIRENTRYTYPE_UNKNOWN
+ && !RTDirEntryIsStdDotLink(&u.DirEntry)) )
+ && rtCrStoreIsSuffixMatch(&u.DirEntry, paSuffixes, cSuffixes) )
+ {
+ if (u.DirEntry.cbName < cbMaxFilename)
+ {
+ memcpy(&szPath[cchPath], u.DirEntry.szName, u.DirEntry.cbName + 1);
+ rc2 = RTDirQueryUnknownType(szPath, true /*fFollowSymlinks*/, &u.DirEntry.enmType);
+ if ( RT_SUCCESS(rc2)
+ && u.DirEntry.enmType == RTDIRENTRYTYPE_FILE)
+ {
+ /*
+ * Add it.
+ */
+ rc2 = RTCrStoreCertAddFromFile(hStore, fFlags, szPath, pErrInfo);
+ if (RT_FAILURE(rc2))
+ {
+ rc = rc2;
+ if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ break;
+ }
+ }
+ }
+ else
+ {
+ rc = RTErrInfoAddF(pErrInfo, VERR_FILENAME_TOO_LONG,
+ " Too long filename (%u bytes)", u.DirEntry.cbName);
+ if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (rc2 != VERR_NO_MORE_FILES)
+ rc = RTErrInfoAddF(pErrInfo, rc2, " RTDirRead failed: %Rrc", rc2);
+ break;
+ }
+ }
+
+ RTDirClose(hDir);
+ }
+ else
+ rc = RTErrInfoAddF(pErrInfo, rc, " RTDirOpen('%s'): %Rrc", pszDir, rc);
+ }
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertAddFromDir);
+
+
+RTDECL(int) RTCrStoreCertAddWantedFromDir(RTCRSTORE hStore, uint32_t fFlags,
+ const char *pszDir, PCRTSTRTUPLE paSuffixes, size_t cSuffixes,
+ PCRTCRCERTWANTED paWanted, size_t cWanted, bool *pafFound, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate input a little.
+ */
+ AssertReturn(*pszDir, VERR_PATH_ZERO_LENGTH);
+ AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
+ fFlags |= RTCRCERTCTX_F_ADD_IF_NOT_FOUND; /* forced */
+
+ AssertReturn(cWanted, VERR_NOT_FOUND);
+ for (uint32_t i = 0; i < cWanted; i++)
+ {
+ AssertReturn(!paWanted[i].pszSubject || *paWanted[i].pszSubject, VERR_INVALID_PARAMETER);
+ AssertReturn( paWanted[i].pszSubject
+ || paWanted[i].fSha1Fingerprint
+ || paWanted[i].fSha512Fingerprint,
+ VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Prepare for constructing path to the files in the directory, so that we
+ * can open them.
+ */
+ char szPath[RTPATH_MAX];
+ int rc = RTStrCopy(szPath, sizeof(szPath), pszDir);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cchPath = RTPathEnsureTrailingSeparator(szPath, sizeof(szPath));
+ if (cchPath > 0)
+ {
+ size_t const cbMaxFilename = sizeof(szPath) - cchPath;
+
+ /*
+ * Enumerate the directory.
+ */
+ RTDIR hDir;
+ rc = RTDirOpen(&hDir, pszDir);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VWRN_NOT_FOUND;
+ for (;;)
+ {
+ /* Read the next entry. */
+ union
+ {
+ RTDIRENTRY DirEntry;
+ uint8_t abPadding[RTPATH_MAX + sizeof(RTDIRENTRY)];
+ } u;
+ size_t cbEntry = sizeof(u);
+ int rc2 = RTDirRead(hDir, &u.DirEntry, &cbEntry);
+ if (RT_SUCCESS(rc2))
+ {
+ if ( ( u.DirEntry.enmType == RTDIRENTRYTYPE_FILE
+ || u.DirEntry.enmType == RTDIRENTRYTYPE_SYMLINK
+ || ( u.DirEntry.enmType == RTDIRENTRYTYPE_UNKNOWN
+ && !RTDirEntryIsStdDotLink(&u.DirEntry)) )
+ && rtCrStoreIsSuffixMatch(&u.DirEntry, paSuffixes, cSuffixes) )
+ {
+ if (u.DirEntry.cbName < cbMaxFilename)
+ {
+ memcpy(&szPath[cchPath], u.DirEntry.szName, u.DirEntry.cbName);
+ szPath[cchPath + u.DirEntry.cbName] = '\0';
+ if (u.DirEntry.enmType != RTDIRENTRYTYPE_FILE)
+ RTDirQueryUnknownType(szPath, true /*fFollowSymlinks*/, &u.DirEntry.enmType);
+ if (u.DirEntry.enmType == RTDIRENTRYTYPE_FILE)
+ {
+ rc2 = RTCrStoreCertAddWantedFromFile(hStore, fFlags, szPath,
+ paWanted, cWanted, pafFound, pErrInfo);
+ if (rc2 == VINF_SUCCESS)
+ {
+ Assert(rtCrStoreAllDone(pafFound, cWanted));
+ if (RT_SUCCESS(rc))
+ rc = VINF_SUCCESS;
+ break;
+ }
+ if (RT_FAILURE(rc2) && !(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ {
+ rc = rc2;
+ break;
+ }
+ }
+ }
+ else
+ {
+ /*
+ * pErrInfo keeps the status code unless it's fatal.
+ */
+ RTErrInfoAddF(pErrInfo, VERR_FILENAME_TOO_LONG,
+ " Too long filename (%u bytes)", u.DirEntry.cbName);
+ if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ {
+ rc = VERR_FILENAME_TOO_LONG;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (rc2 != VERR_NO_MORE_FILES)
+ {
+ RTErrInfoAddF(pErrInfo, rc2, "RTDirRead failed: %Rrc", rc2);
+ if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ rc = rc2;
+ }
+ break;
+ }
+ }
+ RTDirClose(hDir);
+ }
+ }
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertAddWantedFromDir);
+
diff --git a/src/VBox/Runtime/common/crypto/store-inmem.cpp b/src/VBox/Runtime/common/crypto/store-inmem.cpp
new file mode 100644
index 00000000..7bc441ae
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/store-inmem.cpp
@@ -0,0 +1,466 @@
+/* $Id: store-inmem.cpp $ */
+/** @file
+ * IPRT - In Memory Cryptographic Certificate Store.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/store.h>
+
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#include "store-internal.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * A certificate entry in the in-memory store.
+ */
+typedef struct RTCRSTOREINMEMCERT
+{
+ /** The core certificate context. */
+ RTCRCERTCTXINT Core;
+ /** Internal copy of the flag (paranoia). */
+ uint32_t fFlags;
+ /** Decoded data. */
+ union
+ {
+ /** ASN.1 core structure for generic access. */
+ RTASN1CORE Asn1Core;
+ /** The decoded X.509 certificate (RTCRCERTCTX_F_ENC_X509_DER). */
+ RTCRX509CERTIFICATE X509Cert;
+ /** The decoded trust anchor info (RTCRCERTCTX_F_ENC_TAF_DER). */
+ RTCRTAFTRUSTANCHORINFO TaInfo;
+ } u;
+ /** Pointer to the store if still in it (no reference). */
+ struct RTCRSTOREINMEM *pStore;
+ /** The DER encoding of the certificate. */
+ uint8_t abEncoded[1];
+} RTCRSTOREINMEMCERT;
+AssertCompileMembersAtSameOffset(RTCRSTOREINMEMCERT, u.X509Cert.SeqCore.Asn1Core, RTCRSTOREINMEMCERT, u.Asn1Core);
+AssertCompileMembersAtSameOffset(RTCRSTOREINMEMCERT, u.TaInfo.SeqCore.Asn1Core, RTCRSTOREINMEMCERT, u.Asn1Core);
+/** Pointer to an in-memory store certificate entry. */
+typedef RTCRSTOREINMEMCERT *PRTCRSTOREINMEMCERT;
+
+
+/**
+ * The per instance data of a in-memory crypto store.
+ *
+ * Currently we ASSUME we don't need serialization. Add that when needed!
+ */
+typedef struct RTCRSTOREINMEM
+{
+ /** The number of certificates. */
+ uint32_t cCerts;
+ /** The max number of certificates papCerts can store before growing it. */
+ uint32_t cCertsAlloc;
+ /** Array of certificates. */
+ PRTCRSTOREINMEMCERT *papCerts;
+
+ /** Parent store. */
+ RTCRSTORE hParentStore;
+ /** The parent store callback table. */
+ PCRTCRSTOREPROVIDER pParentProvider;
+ /** The parent store provider callback argument. */
+ void *pvParentProvider;
+} RTCRSTOREINMEM;
+/** Pointer to an in-memory crypto store. */
+typedef RTCRSTOREINMEM *PRTCRSTOREINMEM;
+
+
+
+
+static DECLCALLBACK(void) rtCrStoreInMemCertEntry_Dtor(PRTCRCERTCTXINT pCertCtx)
+{
+ PRTCRSTOREINMEMCERT pEntry = (PRTCRSTOREINMEMCERT)pCertCtx;
+ AssertRelease(!pEntry->pStore);
+
+ pEntry->Core.pfnDtor = NULL;
+ RTAsn1VtDelete(&pEntry->u.Asn1Core);
+ RTMemFree(pEntry);
+}
+
+
+/**
+ * Internal method for allocating and initalizing a certificate entry in the
+ * in-memory store.
+ *
+ * @returns IPRT status code.
+ * @param pThis The in-memory store instance.
+ * @param fEnc RTCRCERTCTX_F_ENC_X509_DER or RTCRCERTCTX_F_ENC_TAF_DER.
+ * @param pbSrc The DER encoded X.509 certificate to add.
+ * @param cbSrc The size of the encoded certificate.
+ * @param pErrInfo Where to return extended error info. Optional.
+ * @param ppEntry Where to return the pointer to the new entry.
+ */
+static int rtCrStoreInMemCreateCertEntry(PRTCRSTOREINMEM pThis, uint32_t fEnc, uint8_t const *pbSrc, uint32_t cbSrc,
+ PRTERRINFO pErrInfo, PRTCRSTOREINMEMCERT *ppEntry)
+{
+ int rc;
+ PRTCRSTOREINMEMCERT pEntry = (PRTCRSTOREINMEMCERT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTCRSTOREINMEMCERT, abEncoded[cbSrc]));
+ if (pEntry)
+ {
+ memcpy(pEntry->abEncoded, pbSrc, cbSrc);
+ pEntry->Core.u32Magic = RTCRCERTCTXINT_MAGIC;
+ pEntry->Core.cRefs = 1;
+ pEntry->Core.pfnDtor = rtCrStoreInMemCertEntry_Dtor;
+ pEntry->Core.Public.fFlags = fEnc;
+ pEntry->Core.Public.cbEncoded = cbSrc;
+ pEntry->Core.Public.pabEncoded = &pEntry->abEncoded[0];
+ if (fEnc == RTCRCERTCTX_F_ENC_X509_DER)
+ {
+ pEntry->Core.Public.pCert = &pEntry->u.X509Cert;
+ pEntry->Core.Public.pTaInfo = NULL;
+ }
+ else
+ {
+ pEntry->Core.Public.pCert = NULL;
+ pEntry->Core.Public.pTaInfo = &pEntry->u.TaInfo;
+ }
+ pEntry->pStore = pThis;
+
+ RTASN1CURSORPRIMARY Cursor;
+ RTAsn1CursorInitPrimary(&Cursor, &pEntry->abEncoded[0], cbSrc, pErrInfo, &g_RTAsn1DefaultAllocator,
+ RTASN1CURSOR_FLAGS_DER, "InMem");
+ if (fEnc == RTCRCERTCTX_F_ENC_X509_DER)
+ rc = RTCrX509Certificate_DecodeAsn1(&Cursor.Cursor, 0, &pEntry->u.X509Cert, "Cert");
+ else
+ rc = RTCrTafTrustAnchorInfo_DecodeAsn1(&Cursor.Cursor, 0, &pEntry->u.TaInfo, "TaInfo");
+ if (RT_SUCCESS(rc))
+ {
+ if (fEnc == RTCRCERTCTX_F_ENC_X509_DER)
+ rc = RTCrX509Certificate_CheckSanity(&pEntry->u.X509Cert, 0, pErrInfo, "Cert");
+ else
+ rc = RTCrTafTrustAnchorInfo_CheckSanity(&pEntry->u.TaInfo, 0, pErrInfo, "TaInfo");
+ if (RT_SUCCESS(rc))
+ {
+ *ppEntry = pEntry;
+ return VINF_SUCCESS;
+ }
+
+ RTAsn1VtDelete(&pEntry->u.Asn1Core);
+ }
+ RTMemFree(pEntry);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+/**
+ * Grows the certificate pointer array to at least @a cMin entries.
+ *
+ * @returns IPRT status code.
+ * @param pThis The in-memory store instance.
+ * @param cMin The new minimum store size.
+ */
+static int rtCrStoreInMemGrow(PRTCRSTOREINMEM pThis, uint32_t cMin)
+{
+ AssertReturn(cMin <= _1M, VERR_OUT_OF_RANGE);
+ AssertReturn(cMin > pThis->cCertsAlloc, VERR_INTERNAL_ERROR_3);
+
+ if (cMin < 64)
+ cMin = RT_ALIGN_32(cMin, 8);
+ else
+ cMin = RT_ALIGN_32(cMin, 32);
+
+ void *pv = RTMemRealloc(pThis->papCerts, cMin * sizeof(pThis->papCerts[0]));
+ if (pv)
+ {
+ pThis->papCerts = (PRTCRSTOREINMEMCERT *)pv;
+ for (uint32_t i = pThis->cCertsAlloc; i < cMin; i++)
+ pThis->papCerts[i] = NULL;
+ pThis->cCertsAlloc = cMin;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+
+/** @interface_method_impl{RTCRSTOREPROVIDER,pfnDestroyStore} */
+static DECLCALLBACK(void) rtCrStoreInMem_DestroyStore(void *pvProvider)
+{
+ PRTCRSTOREINMEM pThis = (PRTCRSTOREINMEM)pvProvider;
+
+ while (pThis->cCerts > 0)
+ {
+ uint32_t i = --pThis->cCerts;
+ PRTCRSTOREINMEMCERT pEntry = pThis->papCerts[i];
+ pThis->papCerts[i] = NULL;
+ AssertPtr(pEntry);
+
+ pEntry->pStore = NULL;
+ RTCrCertCtxRelease(&pEntry->Core.Public);
+ }
+
+ RTMemFree(pThis->papCerts);
+ pThis->papCerts = NULL;
+
+ if (pThis->hParentStore != NIL_RTCRSTORE)
+ {
+ RTCrStoreRelease(pThis->hParentStore);
+ pThis->hParentStore = NIL_RTCRSTORE;
+ }
+
+ RTMemFree(pThis);
+}
+
+
+/** @interface_method_impl{RTCRSTOREPROVIDER,pfnCertCtxQueryPrivateKey} */
+static DECLCALLBACK(int) rtCrStoreInMem_CertCtxQueryPrivateKey(void *pvProvider, PRTCRCERTCTXINT pCertCtx,
+ uint8_t *pbKey, size_t cbKey, size_t *pcbKeyRet)
+{
+ RT_NOREF_PV(pvProvider); RT_NOREF_PV(pCertCtx); RT_NOREF_PV(pbKey); RT_NOREF_PV(cbKey); RT_NOREF_PV(pcbKeyRet);
+ //PRTCRSTOREINMEM pThis = (PRTCRSTOREINMEM)pvProvider;
+ return VERR_NOT_FOUND;
+}
+
+
+/** @interface_method_impl{RTCRSTOREPROVIDER,pfnCertFindAll} */
+static DECLCALLBACK(int) rtCrStoreInMem_CertFindAll(void *pvProvider, PRTCRSTORECERTSEARCH pSearch)
+{
+ pSearch->auOpaque[0] = ~(uintptr_t)pvProvider;
+ pSearch->auOpaque[1] = 0;
+ pSearch->auOpaque[2] = ~(uintptr_t)0; /* For the front-end API. */
+ pSearch->auOpaque[3] = ~(uintptr_t)0; /* For the front-end API. */
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTCRSTOREPROVIDER,pfnCertSearchNext} */
+static DECLCALLBACK(PCRTCRCERTCTX) rtCrStoreInMem_CertSearchNext(void *pvProvider, PRTCRSTORECERTSEARCH pSearch)
+{
+ PRTCRSTOREINMEM pThis = (PRTCRSTOREINMEM)pvProvider;
+ if (pSearch->auOpaque[0] == ~(uintptr_t)pvProvider)
+ {
+ uintptr_t i = pSearch->auOpaque[1];
+ if (i < pThis->cCerts)
+ {
+ pSearch->auOpaque[1] = i + 1;
+ PRTCRCERTCTXINT pCertCtx = &pThis->papCerts[i]->Core;
+ ASMAtomicIncU32(&pCertCtx->cRefs);
+ return &pCertCtx->Public;
+ }
+
+ /* Do we have a parent store to search? */
+ if (pThis->hParentStore == NIL_RTCRSTORE)
+ return NULL; /* no */
+ if ( !pThis->pParentProvider->pfnCertFindAll
+ || !pThis->pParentProvider->pfnCertSearchNext)
+ return NULL;
+
+ RTCRSTORECERTSEARCH const SavedSearch = *pSearch;
+ int rc = pThis->pParentProvider->pfnCertFindAll(pThis->pvParentProvider, pSearch);
+ AssertRCReturnStmt(rc, *pSearch = SavedSearch, NULL);
+
+ /* Restore the store.cpp specifics: */
+ AssertCompile(RT_ELEMENTS(SavedSearch.auOpaque) == 4);
+ pSearch->auOpaque[2] = SavedSearch.auOpaque[2];
+ pSearch->auOpaque[3] = SavedSearch.auOpaque[3];
+ }
+
+ AssertReturn(pThis->pParentProvider, NULL);
+ AssertReturn(pThis->pParentProvider->pfnCertSearchNext, NULL);
+ return pThis->pParentProvider->pfnCertSearchNext(pThis->pvParentProvider, pSearch);
+}
+
+
+/** @interface_method_impl{RTCRSTOREPROVIDER,pfnCertSearchDestroy} */
+static DECLCALLBACK(void) rtCrStoreInMem_CertSearchDestroy(void *pvProvider, PRTCRSTORECERTSEARCH pSearch)
+{
+ PRTCRSTOREINMEM pThis = (PRTCRSTOREINMEM)pvProvider;
+ if (pSearch->auOpaque[0] == ~(uintptr_t)pvProvider)
+ {
+ pSearch->auOpaque[0] = 0;
+ pSearch->auOpaque[1] = 0;
+ pSearch->auOpaque[2] = 0;
+ pSearch->auOpaque[3] = 0;
+ }
+ else
+ {
+ AssertReturnVoid(pThis->pParentProvider);
+ AssertReturnVoid(pThis->pParentProvider->pfnCertSearchDestroy);
+ pThis->pParentProvider->pfnCertSearchDestroy(pThis->pvParentProvider, pSearch);
+ }
+}
+
+
+/** @interface_method_impl{RTCRSTOREPROVIDER,pfnCertSearchDestroy} */
+static DECLCALLBACK(int) rtCrStoreInMem_CertAddEncoded(void *pvProvider, uint32_t fFlags,
+ uint8_t const *pbEncoded, uint32_t cbEncoded, PRTERRINFO pErrInfo)
+{
+ PRTCRSTOREINMEM pThis = (PRTCRSTOREINMEM)pvProvider;
+ int rc;
+
+ AssertMsgReturn( (fFlags & RTCRCERTCTX_F_ENC_MASK) == RTCRCERTCTX_F_ENC_X509_DER
+ || (fFlags & RTCRCERTCTX_F_ENC_MASK) == RTCRCERTCTX_F_ENC_TAF_DER
+ , ("Only X.509 and TAF DER are supported: %#x\n", fFlags), VERR_INVALID_FLAGS);
+
+ /*
+ * Check for duplicates if specified.
+ */
+ if (fFlags & RTCRCERTCTX_F_ADD_IF_NOT_FOUND)
+ {
+ uint32_t iCert = pThis->cCerts;
+ while (iCert-- > 0)
+ {
+ PRTCRSTOREINMEMCERT pCert = pThis->papCerts[iCert];
+ if ( pCert->Core.Public.cbEncoded == cbEncoded
+ && pCert->Core.Public.fFlags == (fFlags & RTCRCERTCTX_F_ENC_MASK)
+ && memcmp(pCert->Core.Public.pabEncoded, pbEncoded, cbEncoded) == 0)
+ return VWRN_ALREADY_EXISTS;
+ }
+ }
+
+ /*
+ * Add it.
+ */
+ if (pThis->cCerts + 1 <= pThis->cCertsAlloc)
+ { /* likely */ }
+ else
+ {
+ rc = rtCrStoreInMemGrow(pThis, pThis->cCerts + 1);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ rc = rtCrStoreInMemCreateCertEntry(pThis, fFlags & RTCRCERTCTX_F_ENC_MASK, pbEncoded, cbEncoded,
+ pErrInfo, &pThis->papCerts[pThis->cCerts]);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->cCerts++;
+ return VINF_SUCCESS;
+ }
+ return rc;
+}
+
+
+/**
+ * In-memory store provider.
+ */
+static RTCRSTOREPROVIDER const g_rtCrStoreInMemProvider =
+{
+ "in-memory",
+ rtCrStoreInMem_DestroyStore,
+ rtCrStoreInMem_CertCtxQueryPrivateKey,
+ rtCrStoreInMem_CertFindAll,
+ rtCrStoreInMem_CertSearchNext,
+ rtCrStoreInMem_CertSearchDestroy,
+ rtCrStoreInMem_CertAddEncoded,
+ NULL,
+ 42
+};
+
+
+/**
+ * Common worker for RTCrStoreCreateInMem and future constructors...
+ *
+ * @returns IPRT status code.
+ * @param ppStore Where to return the store instance.
+ * @param hParentStore Optional parent store. Consums reference on
+ * success.
+ */
+static int rtCrStoreInMemCreateInternal(PRTCRSTOREINMEM *ppStore, RTCRSTORE hParentStore)
+{
+ PRTCRSTOREINMEM pStore = (PRTCRSTOREINMEM)RTMemAlloc(sizeof(*pStore));
+ if (pStore)
+ {
+ pStore->cCerts = 0;
+ pStore->cCertsAlloc = 0;
+ pStore->papCerts = NULL;
+ pStore->hParentStore = hParentStore;
+ pStore->pParentProvider = NULL;
+ pStore->pvParentProvider = NULL;
+ *ppStore = pStore;
+ if (hParentStore == NIL_RTCRSTORE)
+ return VINF_SUCCESS;
+ if (~(uintptr_t)hParentStore != ~(uintptr_t)pStore)
+ {
+ pStore->pParentProvider = rtCrStoreGetProvider(hParentStore, &pStore->pvParentProvider);
+ if (pStore->pParentProvider)
+ return VINF_SUCCESS;
+ AssertFailed();
+ }
+ RTMemFree(pStore);
+ }
+ *ppStore = NULL; /* shut up gcc-maybe-pita warning. */
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(int) RTCrStoreCreateInMemEx(PRTCRSTORE phStore, uint32_t cSizeHint, RTCRSTORE hParentStore)
+{
+ if (hParentStore != NIL_RTCRSTORE)
+ {
+ uint32_t cRefs = RTCrStoreRetain(hParentStore);
+ AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
+ }
+
+ PRTCRSTOREINMEM pStore;
+ int rc = rtCrStoreInMemCreateInternal(&pStore, hParentStore);
+ if (RT_SUCCESS(rc))
+ {
+ if (cSizeHint)
+ rc = rtCrStoreInMemGrow(pStore, RT_MIN(cSizeHint, 512));
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtCrStoreCreate(&g_rtCrStoreInMemProvider, pStore, phStore);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ }
+ RTMemFree(pStore);
+ }
+
+ RTCrStoreRelease(hParentStore);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCreateInMemEx);
+
+
+RTDECL(int) RTCrStoreCreateInMem(PRTCRSTORE phStore, uint32_t cSizeHint)
+{
+ return RTCrStoreCreateInMemEx(phStore, cSizeHint, NIL_RTCRSTORE);
+}
+RT_EXPORT_SYMBOL(RTCrStoreCreateInMem);
+
diff --git a/src/VBox/Runtime/common/crypto/store-internal.h b/src/VBox/Runtime/common/crypto/store-internal.h
new file mode 100644
index 00000000..83628271
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/store-internal.h
@@ -0,0 +1,177 @@
+/* $Id: store-internal.h $ */
+/** @file
+ * IPRT - Cryptographic Store, Internal Header.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_common_crypto_store_internal_h
+#define IPRT_INCLUDED_SRC_common_crypto_store_internal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+
+/**
+ * Internal certificate context.
+ *
+ * In addition to the externally visible structure (RTCRCERTCTX) this has the
+ * reference counter and store reference. (This structure may again be part of
+ * a larger structure internal to the store, depending on the source store.)
+ */
+typedef struct RTCRCERTCTXINT
+{
+ /** Magic number (RTCRCERTCTXINT_MAGIC). */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /**
+ * Destructor that gets called with cRefs reaches zero.
+ * @param pCertCtx The internal certificate context.
+ */
+ DECLCALLBACKMEMBER(void, pfnDtor,(struct RTCRCERTCTXINT *pCertCtx));
+ /** The public store context. */
+ RTCRCERTCTX Public;
+} RTCRCERTCTXINT;
+/** Pointer to an internal certificate context. */
+typedef RTCRCERTCTXINT *PRTCRCERTCTXINT;
+
+/** Magic value for RTCRCERTCTXINT::u32Magic (Alan Mathison Turing). */
+#define RTCRCERTCTXINT_MAGIC UINT32_C(0x19120623)
+/** Dead magic value for RTCRCERTCTXINT::u32Magic. */
+#define RTCRCERTCTXINT_MAGIC_DEAD UINT32_C(0x19540607)
+
+
+/**
+ * IPRT Cryptographic Store Provider.
+ *
+ * @remarks This is a very incomplete sketch.
+ */
+typedef struct RTCRSTOREPROVIDER
+{
+ /** The provider name. */
+ const char *pszName;
+
+ /**
+ * Called to destroy an open store.
+ *
+ * @param pvProvider The provider specific data.
+ */
+ DECLCALLBACKMEMBER(void, pfnDestroyStore,(void *pvProvider));
+
+ /**
+ * Queries the private key.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_NOT_FOUND if not private key.
+ * @retval VERR_ACCESS_DENIED if the private key isn't allowed to leave the
+ * store. One would then have to use the pfnCertCtxSign method.
+ *
+ * @param pvProvider The provider specific data.
+ * @param pCertCtx The internal certificate context.
+ * @param pbKey Where to return the key bytes.
+ * @param cbKey The size of the buffer @a pbKey points to.
+ * @param pcbKeyRet Where to return the size of the returned key.
+ */
+ DECLCALLBACKMEMBER(int, pfnCertCtxQueryPrivateKey,(void *pvProvider, PRTCRCERTCTXINT pCertCtx,
+ uint8_t *pbKey, size_t cbKey, size_t *pcbKeyRet));
+
+ /**
+ * Open an enumeration of all certificates.
+ *
+ * @returns IPRT status code
+ * @param pvProvider The provider specific data.
+ * @param pSearch Pointer to opaque search state structure. The
+ * provider should initalize this on success.
+ */
+ DECLCALLBACKMEMBER(int, pfnCertFindAll,(void *pvProvider, PRTCRSTORECERTSEARCH pSearch));
+
+ /**
+ * Get the next certificate.
+ *
+ * @returns Reference to the next certificate context (must be released by
+ * caller). NULL if no more certificates in the search result.
+ * @param pvProvider The provider specific data.
+ * @param pSearch Pointer to opaque search state structure.
+ */
+ DECLCALLBACKMEMBER(PCRTCRCERTCTX, pfnCertSearchNext,(void *pvProvider, PRTCRSTORECERTSEARCH pSearch));
+
+ /**
+ * Closes a certficate search state.
+ *
+ * @param pvProvider The provider specific data.
+ * @param pSearch Pointer to opaque search state structure to destroy.
+ */
+ DECLCALLBACKMEMBER(void, pfnCertSearchDestroy,(void *pvProvider, PRTCRSTORECERTSEARCH pSearch));
+
+ /**
+ * Adds a certificate to the store.
+ *
+ * @returns IPRT status code.
+ * @retval VWRN_ALREADY_EXISTS if the certificate is already present and
+ * RTCRCERTCTX_F_ADD_IF_NOT_FOUND was specified.
+ * @param pvProvider The provider specific data.
+ * @param fFlags RTCRCERTCTX_F_XXX.
+ * @param pbEncoded The encoded certificate bytes.
+ * @param cbEncoded The size of the encoded certificate.
+ * @param pErrInfo Where to store extended error info. Optional.
+ */
+ DECLCALLBACKMEMBER(int, pfnCertAddEncoded,(void *pvProvider, uint32_t fFlags, uint8_t const *pbEncoded, uint32_t cbEncoded,
+ PRTERRINFO pErrInfo));
+
+
+ /* Optional: */
+
+ /**
+ * Find all certficates matching a given issuer and serial number.
+ *
+ * (Usually only one result.)
+ *
+ * @returns IPRT status code
+ * @param pvProvider The provider specific data.
+ * @param phSearch Pointer to a provider specific search handle.
+ */
+ DECLCALLBACKMEMBER(int, pfnCertFindByIssuerAndSerialNo,(void *pvProvider, PCRTCRX509NAME pIssuer, PCRTASN1INTEGER pSerialNo,
+ PRTCRSTORECERTSEARCH phSearch));
+ /** Non-zero end marker. */
+ uintptr_t uEndMarker;
+} RTCRSTOREPROVIDER;
+
+/** Pointer to a store provider call table. */
+typedef RTCRSTOREPROVIDER const *PCRTCRSTOREPROVIDER;
+
+
+DECLHIDDEN(int) rtCrStoreCreate(PCRTCRSTOREPROVIDER pProvider, void *pvProvider, PRTCRSTORE phStore);
+DECLHIDDEN(PCRTCRSTOREPROVIDER) rtCrStoreGetProvider(RTCRSTORE hStore, void **ppvProvider);
+
+#endif /* !IPRT_INCLUDED_SRC_common_crypto_store_internal_h */
+
diff --git a/src/VBox/Runtime/common/crypto/store.cpp b/src/VBox/Runtime/common/crypto/store.cpp
new file mode 100644
index 00000000..edf80c9c
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/store.cpp
@@ -0,0 +1,571 @@
+/* $Id: store.cpp $ */
+/** @file
+ * IPRT - Cryptographic (Certificate) Store.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/store.h>
+
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#include <iprt/crypto/pkcs7.h>
+#include <iprt/crypto/x509.h>
+
+#ifdef IPRT_WITH_OPENSSL
+# include "internal/openssl-pre.h"
+# include <openssl/x509.h>
+# include "internal/openssl-post.h"
+#endif
+
+#include "store-internal.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Internal representation of a (certificate,++) store.
+ */
+typedef struct RTCRSTOREINT
+{
+ /** Magic number (RTCRSTOREINT_MAGIC). */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** Pointer to the store provider. */
+ PCRTCRSTOREPROVIDER pProvider;
+ /** Provider specific data. */
+ void *pvProvider;
+} RTCRSTOREINT;
+/** Pointer to the internal representation of a store. */
+typedef RTCRSTOREINT *PRTCRSTOREINT;
+
+/** Magic value for RTCRSTOREINT::u32Magic (Alfred Dillwyn "Dilly" Knox). */
+#define RTCRSTOREINT_MAGIC UINT32_C(0x18840723)
+/** Dead magic value for RTCRSTOREINT::u32Magic. */
+#define RTCRSTOREINT_MAGIC_DEAD UINT32_C(0x19430227)
+
+
+
+/**
+ * Internal method a store provider uses to create a store handle.
+ *
+ * @returns IPRT status code
+ * @param pProvider Pointer to the store provider callback table.
+ * @param pvProvider Pointer to the provider specific instance data.
+ * @param phStore Where to return the store handle.
+ */
+DECLHIDDEN(int) rtCrStoreCreate(PCRTCRSTOREPROVIDER pProvider, void *pvProvider, PRTCRSTORE phStore)
+{
+ PRTCRSTOREINT pThis = (PRTCRSTOREINT)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->pvProvider = pvProvider;
+ pThis->pProvider = pProvider;
+ pThis->cRefs = 1;
+ pThis->u32Magic = RTCRSTOREINT_MAGIC;
+ *phStore = pThis;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * For the parent forwarding of the in-memory store.
+ */
+DECLHIDDEN(PCRTCRSTOREPROVIDER) rtCrStoreGetProvider(RTCRSTORE hStore, void **ppvProvider)
+{
+ PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
+ AssertPtrReturn(pThis, NULL);
+ AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, NULL);
+ *ppvProvider = pThis->pvProvider;
+ return pThis->pProvider;
+}
+
+
+RTDECL(uint32_t) RTCrStoreRetain(RTCRSTORE hStore)
+{
+ PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, UINT32_MAX);
+
+ uint32_t cRet = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRet < 8192);
+ return cRet;
+}
+
+
+RTDECL(uint32_t) RTCrStoreRelease(RTCRSTORE hStore)
+{
+ if (hStore == NIL_RTCRSTORE)
+ return 0;
+
+ PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, UINT32_MAX);
+
+ uint32_t cStore = ASMAtomicDecU32(&pThis->cRefs);
+ if (!cStore)
+ {
+ ASMAtomicWriteU32(&pThis->u32Magic, RTCRSTOREINT_MAGIC_DEAD);
+ pThis->pProvider->pfnDestroyStore(pThis->pvProvider);
+ RTMemFree(pThis);
+ }
+ return cStore;
+}
+
+
+RTDECL(PCRTCRCERTCTX) RTCrStoreCertByIssuerAndSerialNo(RTCRSTORE hStore, PCRTCRX509NAME pIssuer, PCRTASN1INTEGER pSerialNo)
+{
+ PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
+ AssertPtrReturn(pThis, NULL);
+ AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, NULL);
+ AssertPtrReturn(pIssuer, NULL);
+
+ int rc;
+ RTCRSTORECERTSEARCH Search;
+ if (pThis->pProvider->pfnCertFindByIssuerAndSerialNo)
+ rc = pThis->pProvider->pfnCertFindByIssuerAndSerialNo(pThis->pvProvider, pIssuer, pSerialNo, &Search);
+ else
+ rc = pThis->pProvider->pfnCertFindAll(pThis->pvProvider, &Search);
+
+ PCRTCRCERTCTX pCertCtx = NULL;
+ if (RT_SUCCESS(rc))
+ {
+ for (;;)
+ {
+ pCertCtx = pThis->pProvider->pfnCertSearchNext(pThis->pvProvider, &Search);
+ if (!pCertCtx)
+ break;
+
+ if ( pCertCtx->pCert
+ && RTCrX509Certificate_MatchIssuerAndSerialNumber(pCertCtx->pCert, pIssuer, pSerialNo))
+ break;
+ RTCrCertCtxRelease(pCertCtx);
+ }
+
+ pThis->pProvider->pfnCertSearchDestroy(pThis->pvProvider, &Search);
+ }
+ else
+ AssertMsg(rc == VERR_NOT_FOUND, ("%Rrc\n", rc));
+ return pCertCtx;
+}
+
+
+RTDECL(int) RTCrStoreCertAddEncoded(RTCRSTORE hStore, uint32_t fFlags, void const *pvSrc, size_t cbSrc, PRTERRINFO pErrInfo)
+{
+ PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvSrc, VERR_INVALID_POINTER);
+ AssertReturn(cbSrc > 16 && cbSrc < _1M, VERR_OUT_OF_RANGE);
+ AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ENC_MASK)), VERR_INVALID_FLAGS);
+ AssertMsgReturn( (fFlags & RTCRCERTCTX_F_ENC_MASK) == RTCRCERTCTX_F_ENC_X509_DER
+ || (fFlags & RTCRCERTCTX_F_ENC_MASK) == RTCRCERTCTX_F_ENC_TAF_DER
+ , ("Only X.509 and TAF DER supported: %#x\n", fFlags), VERR_INVALID_FLAGS);
+
+ int rc;
+ if (pThis->pProvider->pfnCertAddEncoded)
+ rc = pThis->pProvider->pfnCertAddEncoded(pThis->pvProvider, fFlags, (uint8_t const *)pvSrc, (uint32_t)cbSrc, pErrInfo);
+ else
+ rc = VERR_WRITE_PROTECT;
+
+ return rc;
+}
+
+
+RTDECL(int) RTCrStoreCertAddX509(RTCRSTORE hStore, uint32_t fFlags, PRTCRX509CERTIFICATE pCertificate, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate.
+ */
+ PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, VERR_INVALID_HANDLE);
+
+ AssertPtrReturn(pCertificate, VERR_INVALID_POINTER);
+ AssertReturn(RTCrX509Certificate_IsPresent(pCertificate), VERR_INVALID_PARAMETER);
+ int rc = RTCrX509Certificate_CheckSanity(pCertificate, 0, pErrInfo, "Cert");
+ AssertRCReturn(rc, rc);
+
+ AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ENC_MASK)), VERR_INVALID_FLAGS);
+ AssertCompile(RTCRCERTCTX_F_ENC_X509_DER == 0);
+ AssertMsgReturn((fFlags & RTCRCERTCTX_F_ENC_MASK) == RTCRCERTCTX_F_ENC_X509_DER,
+ ("Invalid encoding: %#x\n", fFlags), VERR_INVALID_FLAGS);
+
+ /*
+ * Encode and add it using pfnCertAddEncoded.
+ */
+ if (pThis->pProvider->pfnCertAddEncoded)
+ {
+ PRTASN1CORE pCore = RTCrX509Certificate_GetAsn1Core(pCertificate);
+ uint32_t cbEncoded = 0;
+ rc = RTAsn1EncodePrepare(pCore, RTASN1ENCODE_F_DER, &cbEncoded, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ uint8_t * const pbEncoded = (uint8_t *)RTMemTmpAllocZ(cbEncoded);
+ if (pbEncoded)
+ {
+ rc = RTAsn1EncodeToBuffer(pCore, RTASN1ENCODE_F_DER, pbEncoded, cbEncoded, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = pThis->pProvider->pfnCertAddEncoded(pThis->pvProvider, fFlags, pbEncoded, cbEncoded, pErrInfo);
+ RTMemTmpFree(pbEncoded);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ }
+ }
+ else
+ rc = VERR_WRITE_PROTECT;
+
+ return rc;
+}
+
+
+RTDECL(int) RTCrStoreCertAddPkcs7(RTCRSTORE hStore, uint32_t fFlags, PRTCRPKCS7CERT pCertificate, PRTERRINFO pErrInfo)
+{
+ AssertPtrReturn(pCertificate, VERR_INVALID_POINTER);
+ AssertReturn(RTCrPkcs7Cert_IsPresent(pCertificate), VERR_INVALID_PARAMETER);
+ switch (pCertificate->enmChoice)
+ {
+ case RTCRPKCS7CERTCHOICE_X509:
+ return RTCrStoreCertAddX509(hStore, fFlags, pCertificate->u.pX509Cert, pErrInfo);
+
+ case RTCRPKCS7CERTCHOICE_EXTENDED_PKCS6:
+ return RTErrInfoSetF(pErrInfo, VERR_NOT_IMPLEMENTED, "RTCrStoreCertAddPkcs7 does not implement EXTENDED_PKCS6");
+ case RTCRPKCS7CERTCHOICE_AC_V1:
+ return RTErrInfoSetF(pErrInfo, VERR_NOT_IMPLEMENTED, "RTCrStoreCertAddPkcs7 does not implement AC_V1");
+ case RTCRPKCS7CERTCHOICE_AC_V2:
+ return RTErrInfoSetF(pErrInfo, VERR_NOT_IMPLEMENTED, "RTCrStoreCertAddPkcs7 does not implement AC_V2");
+ case RTCRPKCS7CERTCHOICE_OTHER:
+ return RTErrInfoSetF(pErrInfo, VERR_NOT_IMPLEMENTED, "RTCrStoreCertAddPkcs7 does not implement OTHER");
+ case RTCRPKCS7CERTCHOICE_END:
+ case RTCRPKCS7CERTCHOICE_INVALID:
+ case RTCRPKCS7CERTCHOICE_32BIT_HACK:
+ break;
+ /* no default */
+ }
+ return RTErrInfoSetF(pErrInfo, VERR_INVALID_PARAMETER, "Invalid RTCRPKCS7CERT enmChoice value: %d", pCertificate->enmChoice);
+}
+
+
+/*
+ * Searching.
+ * Searching.
+ * Searching.
+ */
+
+RTDECL(int) RTCrStoreCertFindAll(RTCRSTORE hStore, PRTCRSTORECERTSEARCH pSearch)
+{
+ PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pSearch, VERR_INVALID_POINTER);
+
+ return pThis->pProvider->pfnCertFindAll(pThis->pvProvider, pSearch);
+}
+
+
+/** Indicator for RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280 searches
+ * implemented by this front-end code. */
+#define RTCRSTORECERTSEARCH_BY_SUBECT_OR_ALT_SUBJECT_BY_RFC5280 UINT32_C(0x5be9145d)
+
+RTDECL(int) RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(RTCRSTORE hStore, PCRTCRX509NAME pSubject,
+ PRTCRSTORECERTSEARCH pSearch)
+{
+ PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pSearch, VERR_INVALID_POINTER);
+
+ int rc = pThis->pProvider->pfnCertFindAll(pThis->pvProvider, pSearch);
+ if (RT_SUCCESS(rc))
+ {
+ pSearch->auOpaque[2] = RTCRSTORECERTSEARCH_BY_SUBECT_OR_ALT_SUBJECT_BY_RFC5280;
+ pSearch->auOpaque[3] = (uintptr_t)pSubject;
+ }
+ return rc;
+}
+
+
+RTDECL(PCRTCRCERTCTX) RTCrStoreCertSearchNext(RTCRSTORE hStore, PRTCRSTORECERTSEARCH pSearch)
+{
+ PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
+ AssertPtrReturn(pThis, NULL);
+ AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, NULL);
+ AssertPtrReturn(pSearch, NULL);
+
+ PCRTCRCERTCTX pRet;
+ switch (pSearch->auOpaque[2])
+ {
+ default:
+ pRet = pThis->pProvider->pfnCertSearchNext(pThis->pvProvider, pSearch);
+ break;
+
+ case RTCRSTORECERTSEARCH_BY_SUBECT_OR_ALT_SUBJECT_BY_RFC5280:
+ {
+ PCRTCRX509NAME pSubject = (PCRTCRX509NAME)pSearch->auOpaque[3];
+ AssertPtrReturn(pSubject, NULL);
+
+ for (;;)
+ {
+ pRet = pThis->pProvider->pfnCertSearchNext(pThis->pvProvider, pSearch);
+ if (!pRet)
+ break;
+ if (pRet->pCert)
+ {
+ if (RTCrX509Certificate_MatchSubjectOrAltSubjectByRfc5280(pRet->pCert, pSubject))
+ break;
+ }
+ else if (pRet->pTaInfo)
+ {
+ if ( RTCrTafCertPathControls_IsPresent(&pRet->pTaInfo->CertPath)
+ && RTCrX509Name_MatchByRfc5280(&pRet->pTaInfo->CertPath.TaName, pSubject))
+ break;
+ }
+ RTCrCertCtxRelease(pRet);
+ }
+ break;
+ }
+ }
+ return pRet;
+}
+
+
+RTDECL(int) RTCrStoreCertSearchDestroy(RTCRSTORE hStore, PRTCRSTORECERTSEARCH pSearch)
+{
+ PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, VERR_INVALID_HANDLE);
+ if (pSearch)
+ {
+ AssertPtrReturn(pSearch, VERR_INVALID_POINTER);
+ pThis->pProvider->pfnCertSearchDestroy(pThis->pvProvider, pSearch);
+ }
+ return VINF_SUCCESS;
+}
+
+
+
+RTDECL(uint32_t) RTCrStoreCertCount(RTCRSTORE hStore)
+{
+ PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, UINT32_MAX);
+
+ RTCRSTORECERTSEARCH Search;
+ int rc = pThis->pProvider->pfnCertFindAll(pThis->pvProvider, &Search);
+ AssertRCReturn(rc, UINT32_MAX);
+
+
+ uint32_t cCerts = 0;
+ PCRTCRCERTCTX pCur;
+ while ((pCur = pThis->pProvider->pfnCertSearchNext(pThis->pvProvider, &Search)) != NULL)
+ {
+ RTCrCertCtxRelease(pCur);
+ cCerts++;
+ }
+
+ return cCerts;
+}
+
+
+#ifdef IPRT_WITH_OPENSSL
+/*
+ * OpenSSL helper.
+ * OpenSSL helper.
+ * OpenSSL helper.
+ */
+
+RTDECL(int) RTCrStoreConvertToOpenSslCertStore(RTCRSTORE hStore, uint32_t fFlags, void **ppvOpenSslStore, PRTERRINFO pErrInfo)
+{
+ RT_NOREF(pErrInfo);
+ PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, VERR_INVALID_HANDLE);
+ RT_NOREF_PV(fFlags);
+
+ /*
+ * Use the pfnCertFindAll method to add all certificates to the store we're returning.
+ */
+ int rc;
+ X509_STORE *pOsslStore = X509_STORE_new();
+ if (pOsslStore)
+ {
+ RTCRSTORECERTSEARCH Search;
+ rc = pThis->pProvider->pfnCertFindAll(pThis->pvProvider, &Search);
+ if (RT_SUCCESS(rc))
+ {
+ do
+ {
+ PCRTCRCERTCTX pCertCtx = pThis->pProvider->pfnCertSearchNext(pThis->pvProvider, &Search);
+ if (!pCertCtx)
+ break;
+
+ if ( (pCertCtx->fFlags & RTCRCERTCTX_F_ENC_MASK) == RTCRCERTCTX_F_ENC_X509_DER
+ && pCertCtx->cbEncoded > 0)
+ {
+ X509 *pOsslCert = NULL;
+ const unsigned char *pabEncoded = (const unsigned char *)pCertCtx->pabEncoded;
+ if (d2i_X509(&pOsslCert, &pabEncoded, pCertCtx->cbEncoded) == pOsslCert && pOsslCert != NULL)
+ {
+ if (!X509_STORE_add_cert(pOsslStore, pOsslCert))
+ rc = VERR_NO_MEMORY;
+ X509_free(pOsslCert);
+ }
+ }
+
+ RTCrCertCtxRelease(pCertCtx);
+ } while (RT_SUCCESS(rc));
+
+ pThis->pProvider->pfnCertSearchDestroy(pThis->pvProvider, &Search);
+ if (RT_SUCCESS(rc))
+ {
+ *ppvOpenSslStore = pOsslStore;
+ return VINF_SUCCESS;
+ }
+ }
+ X509_STORE_free(pOsslStore);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+RTDECL(int) RTCrStoreConvertToOpenSslCertStack(RTCRSTORE hStore, uint32_t fFlags, void **ppvOpenSslStack, PRTERRINFO pErrInfo)
+{
+ RT_NOREF(pErrInfo);
+ PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, VERR_INVALID_HANDLE);
+ RT_NOREF_PV(fFlags);
+
+ /*
+ * Use the pfnCertFindAll method to add all certificates to the store we're returning.
+ */
+ int rc;
+ STACK_OF(X509) *pOsslStack = sk_X509_new_null();
+ if (pOsslStack)
+ {
+ RTCRSTORECERTSEARCH Search;
+ rc = pThis->pProvider->pfnCertFindAll(pThis->pvProvider, &Search);
+ if (RT_SUCCESS(rc))
+ {
+ do
+ {
+ PCRTCRCERTCTX pCertCtx = pThis->pProvider->pfnCertSearchNext(pThis->pvProvider, &Search);
+ if (!pCertCtx)
+ break;
+
+ if ( (pCertCtx->fFlags & RTCRCERTCTX_F_ENC_MASK) == RTCRCERTCTX_F_ENC_X509_DER
+ && pCertCtx->cbEncoded > 0)
+ {
+ X509 *pOsslCert = NULL;
+ const unsigned char *pabEncoded = (const unsigned char *)pCertCtx->pabEncoded;
+ if (d2i_X509(&pOsslCert, &pabEncoded, pCertCtx->cbEncoded) == pOsslCert && pOsslCert != NULL)
+ {
+ if (!sk_X509_push(pOsslStack, pOsslCert))
+ {
+ rc = VERR_NO_MEMORY;
+ X509_free(pOsslCert);
+ }
+ }
+ }
+
+ RTCrCertCtxRelease(pCertCtx);
+ } while (RT_SUCCESS(rc));
+
+ pThis->pProvider->pfnCertSearchDestroy(pThis->pvProvider, &Search);
+ if (RT_SUCCESS(rc))
+ {
+ *ppvOpenSslStack = pOsslStack;
+ return VINF_SUCCESS;
+ }
+ }
+#include "internal/openssl-pre.h" /* Need to disable C5039 warning here. */
+ sk_X509_pop_free(pOsslStack, X509_free);
+#include "internal/openssl-post.h"
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+#endif /* IPRT_WITH_OPENSSL */
+
+
+/*
+ * Certificate context.
+ * Certificate context.
+ * Certificate context.
+ */
+
+
+RTDECL(uint32_t) RTCrCertCtxRetain(PCRTCRCERTCTX pCertCtx)
+{
+ AssertPtrReturn(pCertCtx, UINT32_MAX);
+ PRTCRCERTCTXINT pThis = RT_FROM_MEMBER(pCertCtx, RTCRCERTCTXINT, Public);
+ AssertReturn(pThis->u32Magic == RTCRCERTCTXINT_MAGIC, UINT32_MAX);
+ uint32_t cRet = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRet < 64);
+ return cRet;
+}
+
+
+RTDECL(uint32_t) RTCrCertCtxRelease(PCRTCRCERTCTX pCertCtx)
+{
+ if (!pCertCtx)
+ return 0;
+
+ AssertPtrReturn(pCertCtx, UINT32_MAX);
+ PRTCRCERTCTXINT pThis = RT_FROM_MEMBER(pCertCtx, RTCRCERTCTXINT, Public);
+ AssertReturn(pThis->u32Magic == RTCRCERTCTXINT_MAGIC, UINT32_MAX);
+ uint32_t cRet = ASMAtomicDecU32(&pThis->cRefs);
+ if (!cRet)
+ {
+ ASMAtomicWriteU32(&pThis->u32Magic, RTCRCERTCTXINT_MAGIC_DEAD);
+ pThis->pfnDtor(pThis);
+ }
+ return cRet;
+}
+
diff --git a/src/VBox/Runtime/common/crypto/taf-asn1-decoder.cpp b/src/VBox/Runtime/common/crypto/taf-asn1-decoder.cpp
new file mode 100644
index 00000000..51b2d521
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/taf-asn1-decoder.cpp
@@ -0,0 +1,54 @@
+/* $Id: taf-asn1-decoder.cpp $ */
+/** @file
+ * IPRT - Crypto - Trust Anchor Format (RFC-5914), Decoder for ASN.1.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/taf.h>
+
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#include "taf-internal.h"
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-asn1-decoder.h>
+
diff --git a/src/VBox/Runtime/common/crypto/taf-core.cpp b/src/VBox/Runtime/common/crypto/taf-core.cpp
new file mode 100644
index 00000000..03627c07
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/taf-core.cpp
@@ -0,0 +1,51 @@
+/* $Id: taf-core.cpp $ */
+/** @file
+ * IPRT - Crypto - Trust Anchor Format (RFC-5914), Core API.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/taf.h>
+
+#include "taf-internal.h"
+
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-core.h>
+
diff --git a/src/VBox/Runtime/common/crypto/taf-init.cpp b/src/VBox/Runtime/common/crypto/taf-init.cpp
new file mode 100644
index 00000000..822f5fd6
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/taf-init.cpp
@@ -0,0 +1,54 @@
+/* $Id: taf-init.cpp $ */
+/** @file
+ * IPRT - Crypto - Trust Anchor Format (RFC-5914), Initialization API.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/taf.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include <iprt/crypto/x509.h>
+
+#include "taf-internal.h"
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-init.h>
+
diff --git a/src/VBox/Runtime/common/crypto/taf-internal.h b/src/VBox/Runtime/common/crypto/taf-internal.h
new file mode 100644
index 00000000..084905ed
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/taf-internal.h
@@ -0,0 +1,47 @@
+/* $Id: taf-internal.h $ */
+/** @file
+ * IPRT - Crypto - TAF, Internal Header.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_common_crypto_taf_internal_h
+#define IPRT_INCLUDED_SRC_common_crypto_taf_internal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define RTASN1TMPL_TEMPLATE_FILE "../common/crypto/taf-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+
+#endif /* !IPRT_INCLUDED_SRC_common_crypto_taf_internal_h */
+
diff --git a/src/VBox/Runtime/common/crypto/taf-sanity.cpp b/src/VBox/Runtime/common/crypto/taf-sanity.cpp
new file mode 100644
index 00000000..f23ce203
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/taf-sanity.cpp
@@ -0,0 +1,52 @@
+/* $Id: taf-sanity.cpp $ */
+/** @file
+ * IPRT - Crypto - Trust Anchor Format (RFC-5914), Sanity Checkers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/taf.h>
+
+#include <iprt/errcore.h>
+
+#include "taf-internal.h"
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-sanity.h>
+
diff --git a/src/VBox/Runtime/common/crypto/taf-template.h b/src/VBox/Runtime/common/crypto/taf-template.h
new file mode 100644
index 00000000..3b35aeb3
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/taf-template.h
@@ -0,0 +1,104 @@
+/* $Id: taf-template.h $ */
+/** @file
+ * IPRT - Crypto - Trust Anchor Format (RFC-5914), Code Generator Template.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#define RTASN1TMPL_DECL RTDECL
+
+/*
+ * CertPathControls (not sequence-/set-of).
+ */
+#define RTASN1TMPL_TYPE RTCRTAFCERTPATHCONTROLS
+#define RTASN1TMPL_EXT_NAME RTCrTafCertPathControls
+#define RTASN1TMPL_INT_NAME rtCrTafCertPathControls
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( TaName, RTCRX509NAME, RTCrX509Name);
+RTASN1TMPL_MEMBER_OPT_ITAG( Certificate, RTCRX509CERTIFICATE, RTCrX509Certificate, 0);
+RTASN1TMPL_MEMBER_OPT_ITAG( PolicySet, RTCRX509CERTIFICATEPOLICIES, RTCrX509CertificatePolicies, 1);
+RTASN1TMPL_MEMBER_OPT_ITAG_BITSTRING(PolicyFlags, 3 /* max bits */, 2);
+RTASN1TMPL_MEMBER_OPT_ITAG( NameConstr, RTCRX509NAMECONSTRAINTS, RTCrX509NameConstraints, 3);
+RTASN1TMPL_MEMBER_OPT_ITAG_EX( PathLenConstraint, RTASN1INTEGER, RTAsn1Integer, 4, RTASN1TMPL_ITAG_F_CP, RT_NOTHING);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * TrustAnchorInfo.
+ */
+#define RTASN1TMPL_TYPE RTCRTAFTRUSTANCHORINFO
+#define RTASN1TMPL_EXT_NAME RTCrTafTrustAnchorInfo
+#define RTASN1TMPL_INT_NAME rtCrTafTrustAnchorInfo
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER_DEF_ITAG_UP( Version, RTASN1INTEGER, RTAsn1Integer, ASN1_TAG_INTEGER, RTCRTAFTRUSTANCHORINFO_V1);
+RTASN1TMPL_MEMBER( PubKey, RTCRX509SUBJECTPUBLICKEYINFO, RTCrX509SubjectPublicKeyInfo);
+RTASN1TMPL_MEMBER( KeyIdentifier, RTASN1OCTETSTRING, RTAsn1OctetString);
+RTASN1TMPL_MEMBER_OPT_UTF8_STRING_EX(TaTitle, RTASN1TMPL_MEMBER_CONSTR_MIN_MAX(TaTitleLangTag, RTASN1STRING, RTAsn1String, 1, 64, RT_NOTHING));
+RTASN1TMPL_MEMBER_OPT_ITAG_EX( CertPath, RTCRTAFCERTPATHCONTROLS, RTCrTafCertPathControls, ASN1_TAG_SEQUENCE, RTASN1TMPL_ITAG_F_UC, RT_NOTHING);
+RTASN1TMPL_MEMBER_OPT_XTAG( T1, CtxTag1, Exts, RTCRX509EXTENSIONS, RTCrX509Extensions, 1);
+RTASN1TMPL_MEMBER_OPT_UTF8_STRING_EX(TaTitleLangTag, RTASN1TMPL_MEMBER_CONSTR_MIN_MAX(TaTitleLangTag, RTASN1STRING, RTAsn1String, 2, 4, RT_NOTHING));
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * TrustAnchorChoice.
+ */
+#define RTASN1TMPL_TYPE RTCRTAFTRUSTANCHORCHOICE
+#define RTASN1TMPL_EXT_NAME RTCrTafTrustAnchorChoice
+#define RTASN1TMPL_INT_NAME rtCrTafTrustAnchorChoice
+RTASN1TMPL_BEGIN_PCHOICE();
+RTASN1TMPL_PCHOICE_ITAG(ASN1_TAG_SEQUENCE, RTCRTAFTRUSTANCHORCHOICEVAL_CERTIFICATE, u.pCertificate, Certificate, RTCRX509CERTIFICATE, RTCrX509Certificate);
+RTASN1TMPL_PCHOICE_XTAG(1, RTCRTAFTRUSTANCHORCHOICEVAL_TBS_CERTIFICATE, u.pT1, CtxTag1, TbsCert, RTCRX509TBSCERTIFICATE, RTCrX509TbsCertificate);
+RTASN1TMPL_PCHOICE_XTAG(2, RTCRTAFTRUSTANCHORCHOICEVAL_TRUST_ANCHOR_INFO, u.pT2, CtxTag2, TaInfo, RTCRTAFTRUSTANCHORINFO, RTCrTafTrustAnchorInfo);
+RTASN1TMPL_END_PCHOICE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * TrustAnchorList
+ */
+#define RTASN1TMPL_TYPE RTCRTAFTRUSTANCHORLIST
+#define RTASN1TMPL_EXT_NAME RTCrTafTrustAnchorList
+#define RTASN1TMPL_INT_NAME rtCrTafTrustAnchorList
+RTASN1TMPL_SEQ_OF(RTCRTAFTRUSTANCHORCHOICE, RTCrTafTrustAnchorChoice);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
diff --git a/src/VBox/Runtime/common/crypto/tsp-asn1-decoder.cpp b/src/VBox/Runtime/common/crypto/tsp-asn1-decoder.cpp
new file mode 100644
index 00000000..b0e7a808
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/tsp-asn1-decoder.cpp
@@ -0,0 +1,51 @@
+/* $Id: tsp-asn1-decoder.cpp $ */
+/** @file
+ * IPRT - Crypto - Time-Stamp Protocol (RFC-3161), Decoder for ASN.1.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/tsp.h>
+
+#include "tsp-internal.h"
+
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-asn1-decoder.h>
+
diff --git a/src/VBox/Runtime/common/crypto/tsp-core.cpp b/src/VBox/Runtime/common/crypto/tsp-core.cpp
new file mode 100644
index 00000000..ee0c5329
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/tsp-core.cpp
@@ -0,0 +1,51 @@
+/* $Id: tsp-core.cpp $ */
+/** @file
+ * IPRT - Crypto - Time-Stamp Protocol (RFC-3161), Core API.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/tsp.h>
+
+#include "tsp-internal.h"
+
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-core.h>
+
diff --git a/src/VBox/Runtime/common/crypto/tsp-init.cpp b/src/VBox/Runtime/common/crypto/tsp-init.cpp
new file mode 100644
index 00000000..4d19c09d
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/tsp-init.cpp
@@ -0,0 +1,51 @@
+/* $Id: tsp-init.cpp $ */
+/** @file
+ * IPRT - Crypto - Time-Stamp Protocol (RFC-3161), Initialization API.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/tsp.h>
+
+#include "tsp-internal.h"
+
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-init.h>
+
diff --git a/src/VBox/Runtime/common/crypto/tsp-internal.h b/src/VBox/Runtime/common/crypto/tsp-internal.h
new file mode 100644
index 00000000..34797c8b
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/tsp-internal.h
@@ -0,0 +1,47 @@
+/* $Id: tsp-internal.h $ */
+/** @file
+ * IPRT - Crypto - TSP, Internal Header.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_common_crypto_tsp_internal_h
+#define IPRT_INCLUDED_SRC_common_crypto_tsp_internal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define RTASN1TMPL_TEMPLATE_FILE "../common/crypto/tsp-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+
+#endif /* !IPRT_INCLUDED_SRC_common_crypto_tsp_internal_h */
+
diff --git a/src/VBox/Runtime/common/crypto/tsp-sanity.cpp b/src/VBox/Runtime/common/crypto/tsp-sanity.cpp
new file mode 100644
index 00000000..2444733c
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/tsp-sanity.cpp
@@ -0,0 +1,51 @@
+/* $Id: tsp-sanity.cpp $ */
+/** @file
+ * IPRT - Crypto - Time-Stamp Protocol (RFC-3161), Sanity Checkers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/tsp.h>
+
+#include "tsp-internal.h"
+
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-sanity.h>
+
diff --git a/src/VBox/Runtime/common/crypto/tsp-template.h b/src/VBox/Runtime/common/crypto/tsp-template.h
new file mode 100644
index 00000000..ab288873
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/tsp-template.h
@@ -0,0 +1,113 @@
+/* $Id: tsp-template.h $ */
+/** @file
+ * IPRT - Crypto - Time-Stamp Protocol (RFC-3161), Code Generator Template.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#define RTASN1TMPL_DECL RTDECL
+
+/*
+ * MessageImprint
+ */
+#define RTASN1TMPL_TYPE RTCRTSPMESSAGEIMPRINT
+#define RTASN1TMPL_EXT_NAME RTCrTspMessageImprint
+#define RTASN1TMPL_INT_NAME rtCrTspMessageImprint
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( HashAlgorithm, RTCRX509ALGORITHMIDENTIFIER, RTCrX509AlgorithmIdentifier);
+RTASN1TMPL_MEMBER( HashedMessage, RTASN1OCTETSTRING, RTAsn1OctetString);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+/*
+ * TimeStampReq
+ */
+
+/*
+ * PKIStatusInfo
+ */
+
+/*
+ * TimeStampResp
+ */
+
+/*
+ * Accuracy
+ *
+ * Note! Capping second accuracy at an hour to reduce chance exploiting this
+ * field to tinker with a signed structure. The RFC does not specify
+ * any upper limit.
+ *
+ * Note! Allowing a zero value for the 'millis' field because we've seen symantec
+ * return that when 'micros' is present. The RFC seems to want the TSA to
+ * omit the field if its value is zero.
+ */
+#define RTASN1TMPL_TYPE RTCRTSPACCURACY
+#define RTASN1TMPL_EXT_NAME RTCrTspAccuracy
+#define RTASN1TMPL_INT_NAME rtCrTspAccuracy
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER_OPT_ITAG_EX( Seconds, RTASN1INTEGER, RTAsn1Integer, ASN1_TAG_INTEGER, RTASN1TMPL_ITAG_F_UP,
+ RTASN1TMPL_MEMBER_CONSTR_U64_MIN_MAX(Seconds, 0, 3600, RT_NOTHING));
+RTASN1TMPL_MEMBER_OPT_ITAG_EX( Millis, RTASN1INTEGER, RTAsn1Integer, 0, RTASN1TMPL_ITAG_F_CP,
+ RTASN1TMPL_MEMBER_CONSTR_U64_MIN_MAX(Millis, 0, 999, RT_NOTHING));
+RTASN1TMPL_MEMBER_OPT_ITAG_EX( Micros, RTASN1INTEGER, RTAsn1Integer, 1, RTASN1TMPL_ITAG_F_CP,
+ RTASN1TMPL_MEMBER_CONSTR_U64_MIN_MAX(Micros, 1, 999, RT_NOTHING));
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * TSTInfo
+ */
+#define RTASN1TMPL_TYPE RTCRTSPTSTINFO
+#define RTASN1TMPL_EXT_NAME RTCrTspTstInfo
+#define RTASN1TMPL_INT_NAME rtCrTspTstInfo
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( Version, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( Policy, RTASN1OBJID, RTAsn1ObjId);
+RTASN1TMPL_MEMBER( MessageImprint, RTCRTSPMESSAGEIMPRINT, RTCrTspMessageImprint);
+RTASN1TMPL_MEMBER( SerialNumber, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( GenTime, RTASN1TIME, RTAsn1GeneralizedTime);
+RTASN1TMPL_MEMBER_OPT_ITAG_UC( Accuracy, RTCRTSPACCURACY, RTCrTspAccuracy, ASN1_TAG_SEQUENCE);
+RTASN1TMPL_MEMBER_DEF_ITAG_UP( Ordering, RTASN1BOOLEAN, RTAsn1Boolean, ASN1_TAG_BOOLEAN, 0 /*False*/);
+RTASN1TMPL_MEMBER_OPT_ITAG_UP( Nonce, RTASN1INTEGER, RTAsn1Integer, ASN1_TAG_INTEGER);
+RTASN1TMPL_MEMBER_OPT_XTAG( T0, CtxTag0, Tsa, RTCRX509GENERALNAME, RTCrX509GeneralName, 0);
+RTASN1TMPL_MEMBER_OPT_ITAG( Extensions, RTCRX509EXTENSION, RTCrX509Extension, 1);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
diff --git a/src/VBox/Runtime/common/crypto/x509-asn1-decoder.cpp b/src/VBox/Runtime/common/crypto/x509-asn1-decoder.cpp
new file mode 100644
index 00000000..a510e130
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/x509-asn1-decoder.cpp
@@ -0,0 +1,230 @@
+/* $Id: x509-asn1-decoder.cpp $ */
+/** @file
+ * IPRT - Crypto - X.509, Decoder for ASN.1.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/x509.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+#include "x509-internal.h"
+
+
+/*
+ * One X.509 Extension.
+ */
+RTDECL(int) RTCrX509Extension_ExtnValue_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags,
+ PRTCRX509EXTENSION pThis, const char *pszErrorTag)
+{
+ RT_NOREF_PV(fFlags); RT_NOREF_PV(pszErrorTag);
+
+ pThis->enmValue = RTCRX509EXTENSIONVALUE_UNKNOWN;
+
+ /*
+ * Decode the encapsulated extension bytes if know the format.
+ */
+ RTASN1CURSOR ValueCursor;
+ int rc = RTAsn1CursorInitSubFromCore(pCursor, &pThis->ExtnValue.Asn1Core, &ValueCursor, "ExtnValue");
+ if (RT_FAILURE(rc))
+ return rc;
+ pCursor = &ValueCursor;
+
+ if (RTAsn1ObjId_CompareWithString(&pThis->ExtnId, RTCRX509_ID_CE_AUTHORITY_KEY_IDENTIFIER_OID) == 0)
+ {
+ /* 4.2.1.1 Authority Key Identifier */
+ PRTCRX509AUTHORITYKEYIDENTIFIER pThat;
+ rc = RTAsn1MemAllocZ(&pThis->ExtnValue.EncapsulatedAllocation, (void **)&pThat, sizeof(*pThat));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->ExtnValue.pEncapsulated = &pThat->SeqCore.Asn1Core;
+ pThis->enmValue = RTCRX509EXTENSIONVALUE_AUTHORITY_KEY_IDENTIFIER;
+ rc = RTCrX509AuthorityKeyIdentifier_DecodeAsn1(&ValueCursor, 0, pThat, "AuthorityKeyIdentifier");
+ }
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pThis->ExtnId, RTCRX509_ID_CE_OLD_AUTHORITY_KEY_IDENTIFIER_OID) == 0)
+ {
+ /* Old and obsolete version of the above, still found in microsoft certificates. */
+ PRTCRX509OLDAUTHORITYKEYIDENTIFIER pThat;
+ rc = RTAsn1MemAllocZ(&pThis->ExtnValue.EncapsulatedAllocation, (void **)&pThat, sizeof(*pThat));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->ExtnValue.pEncapsulated = &pThat->SeqCore.Asn1Core;
+ pThis->enmValue = RTCRX509EXTENSIONVALUE_OLD_AUTHORITY_KEY_IDENTIFIER;
+ rc = RTCrX509OldAuthorityKeyIdentifier_DecodeAsn1(&ValueCursor, 0, pThat, "OldAuthorityKeyIdentifier");
+ }
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pThis->ExtnId, RTCRX509_ID_CE_SUBJECT_KEY_IDENTIFIER_OID) == 0)
+ {
+ /* 4.2.1.2 Subject Key Identifier */
+ PRTASN1OCTETSTRING pThat;
+ rc = RTAsn1MemAllocZ(&pThis->ExtnValue.EncapsulatedAllocation, (void **)&pThat, sizeof(*pThat));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->ExtnValue.pEncapsulated = &pThat->Asn1Core;
+ pThis->enmValue = RTCRX509EXTENSIONVALUE_OCTET_STRING;
+ rc = RTAsn1CursorGetOctetString(&ValueCursor, 0, pThat, "SubjectKeyIdentifier");
+ }
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pThis->ExtnId, RTCRX509_ID_CE_KEY_USAGE_OID) == 0)
+ {
+ /* 4.2.1.3 Key Usage */
+ PRTASN1BITSTRING pThat;
+ rc = RTAsn1MemAllocZ(&pThis->ExtnValue.EncapsulatedAllocation, (void **)&pThat, sizeof(*pThat));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->ExtnValue.pEncapsulated = &pThat->Asn1Core;
+ pThis->enmValue = RTCRX509EXTENSIONVALUE_BIT_STRING;
+ rc = RTAsn1CursorGetBitStringEx(&ValueCursor, 0, 9, pThat, "KeyUsage");
+ }
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pThis->ExtnId, RTCRX509_ID_CE_CERTIFICATE_POLICIES_OID) == 0)
+ {
+ /* 4.2.1.4 Certificate Policies */
+ PRTCRX509CERTIFICATEPOLICIES pThat;
+ rc = RTAsn1MemAllocZ(&pThis->ExtnValue.EncapsulatedAllocation, (void **)&pThat, sizeof(*pThat));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->ExtnValue.pEncapsulated = &pThat->SeqCore.Asn1Core;
+ pThis->enmValue = RTCRX509EXTENSIONVALUE_CERTIFICATE_POLICIES;
+ rc = RTCrX509CertificatePolicies_DecodeAsn1(&ValueCursor, 0, pThat, "CertPolicies");
+ }
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pThis->ExtnId, RTCRX509_ID_CE_POLICY_MAPPINGS_OID) == 0)
+ {
+ /* 4.2.1.5 Policy Mappings */
+ PRTCRX509POLICYMAPPINGS pThat;
+ rc = RTAsn1MemAllocZ(&pThis->ExtnValue.EncapsulatedAllocation, (void **)&pThat, sizeof(*pThat));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->ExtnValue.pEncapsulated = &pThat->SeqCore.Asn1Core;
+ pThis->enmValue = RTCRX509EXTENSIONVALUE_POLICY_MAPPINGS;
+ rc = RTCrX509PolicyMappings_DecodeAsn1(&ValueCursor, 0, pThat, "PolicyMapppings");
+ }
+ }
+ else if ( RTAsn1ObjId_CompareWithString(&pThis->ExtnId, RTCRX509_ID_CE_SUBJECT_ALT_NAME_OID) == 0
+ || RTAsn1ObjId_CompareWithString(&pThis->ExtnId, RTCRX509_ID_CE_ISSUER_ALT_NAME_OID) == 0)
+ {
+ /* 4.2.1.6 Subject Alternative Name / 4.2.1.7 Issuer Alternative Name */
+ PRTCRX509GENERALNAMES pThat;
+ rc = RTAsn1MemAllocZ(&pThis->ExtnValue.EncapsulatedAllocation, (void **)&pThat, sizeof(*pThat));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->ExtnValue.pEncapsulated = &pThat->SeqCore.Asn1Core;
+ pThis->enmValue = RTCRX509EXTENSIONVALUE_GENERAL_NAMES;
+ rc = RTCrX509GeneralNames_DecodeAsn1(&ValueCursor, 0, pThat, "AltName");
+ }
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pThis->ExtnId, RTCRX509_ID_CE_BASIC_CONSTRAINTS_OID) == 0)
+ {
+ /* 4.2.1.9 Basic Constraints */
+ PRTCRX509BASICCONSTRAINTS pThat;
+ rc = RTAsn1MemAllocZ(&pThis->ExtnValue.EncapsulatedAllocation, (void **)&pThat, sizeof(*pThat));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->ExtnValue.pEncapsulated = &pThat->SeqCore.Asn1Core;
+ pThis->enmValue = RTCRX509EXTENSIONVALUE_BASIC_CONSTRAINTS;
+ rc = RTCrX509BasicConstraints_DecodeAsn1(&ValueCursor, 0, pThat, "BasicConstraints");
+ }
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pThis->ExtnId, RTCRX509_ID_CE_NAME_CONSTRAINTS_OID) == 0)
+ {
+ /* 4.2.1.10 Name Constraints */
+ PRTCRX509NAMECONSTRAINTS pThat;
+ rc = RTAsn1MemAllocZ(&pThis->ExtnValue.EncapsulatedAllocation, (void **)&pThat, sizeof(*pThat));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->ExtnValue.pEncapsulated = &pThat->SeqCore.Asn1Core;
+ pThis->enmValue = RTCRX509EXTENSIONVALUE_NAME_CONSTRAINTS;
+ rc = RTCrX509NameConstraints_DecodeAsn1(&ValueCursor, 0, pThat, "NameConstraints");
+ }
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pThis->ExtnId, RTCRX509_ID_CE_POLICY_CONSTRAINTS_OID) == 0)
+ {
+ /* 4.2.1.11 Policy Constraints */
+ PRTCRX509POLICYCONSTRAINTS pThat;
+ rc = RTAsn1MemAllocZ(&pThis->ExtnValue.EncapsulatedAllocation, (void **)&pThat, sizeof(*pThat));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->ExtnValue.pEncapsulated = &pThat->SeqCore.Asn1Core;
+ pThis->enmValue = RTCRX509EXTENSIONVALUE_POLICY_CONSTRAINTS;
+ rc = RTCrX509PolicyConstraints_DecodeAsn1(&ValueCursor, 0, pThat, "PolicyConstraints");
+ }
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pThis->ExtnId, RTCRX509_ID_CE_EXT_KEY_USAGE_OID) == 0)
+ {
+ /* 4.2.1.12 Extended Key Usage */
+ PRTASN1SEQOFOBJIDS pThat;
+ rc = RTAsn1MemAllocZ(&pThis->ExtnValue.EncapsulatedAllocation, (void **)&pThat, sizeof(*pThat));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->ExtnValue.pEncapsulated = &pThat->SeqCore.Asn1Core;
+ pThis->enmValue = RTCRX509EXTENSIONVALUE_SEQ_OF_OBJ_IDS;
+ rc = RTAsn1SeqOfObjIds_DecodeAsn1(&ValueCursor, 0, pThat, "ExKeyUsage");
+ }
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pThis->ExtnId, RTCRX509_ID_CE_EXT_KEY_USAGE_OID) == 0)
+ {
+ /* 4.2.1.14 Inhibit anyPolicy */
+ PRTASN1INTEGER pThat;
+ rc = RTAsn1MemAllocZ(&pThis->ExtnValue.EncapsulatedAllocation, (void **)&pThat, sizeof(*pThat));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->ExtnValue.pEncapsulated = &pThat->Asn1Core;
+ pThis->enmValue = RTCRX509EXTENSIONVALUE_INTEGER;
+ rc = RTAsn1CursorGetInteger(&ValueCursor, 0, pThat, "InhibitAnyPolicy");
+ }
+ }
+ else
+ return VINF_SUCCESS;
+
+ if (RT_SUCCESS(rc))
+ rc = RTAsn1CursorCheckEnd(&ValueCursor);
+
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ return rc;
+}
+
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-asn1-decoder.h>
+
diff --git a/src/VBox/Runtime/common/crypto/x509-certpaths.cpp b/src/VBox/Runtime/common/crypto/x509-certpaths.cpp
new file mode 100644
index 00000000..1ef8cc5c
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/x509-certpaths.cpp
@@ -0,0 +1,3017 @@
+/* $Id: x509-certpaths.cpp $ */
+/** @file
+ * IPRT - Crypto - X.509, Simple Certificate Path Builder & Validator.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_CRYPTO
+#include "internal/iprt.h"
+#include <iprt/crypto/x509.h>
+
+#include <iprt/asm.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/list.h>
+#include <iprt/log.h>
+#include <iprt/time.h>
+#include <iprt/crypto/applecodesign.h> /* critical extension OIDs */
+#include <iprt/crypto/pkcs7.h> /* PCRTCRPKCS7SETOFCERTS */
+#include <iprt/crypto/store.h>
+
+#include "x509-internal.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * X.509 certificate path node.
+ */
+typedef struct RTCRX509CERTPATHNODE
+{
+ /** Sibling list entry. */
+ RTLISTNODE SiblingEntry;
+ /** List of children or leaf list entry. */
+ RTLISTANCHOR ChildListOrLeafEntry;
+ /** Pointer to the parent node. NULL for root. */
+ struct RTCRX509CERTPATHNODE *pParent;
+
+ /** The distance between this node and the target. */
+ uint32_t uDepth : 8;
+ /** Indicates the source of this certificate. */
+ uint32_t uSrc : 3;
+ /** Set if this is a leaf node. */
+ uint32_t fLeaf : 1;
+ /** Makes sure it's a 32-bit bitfield. */
+ uint32_t uReserved : 20;
+
+ /** Leaf only: The result of the last path vertification. */
+ int rcVerify;
+
+ /** Pointer to the certificate. This can be NULL only for trust anchors. */
+ PCRTCRX509CERTIFICATE pCert;
+
+ /** If the certificate or trust anchor was obtained from a store, this is the
+ * associated certificate context (referenced of course). This is used to
+ * access the trust anchor information, if present.
+ *
+ * (If this is NULL it's from a certificate array or some such given directly to
+ * the path building code. It's assumed the caller doesn't free these until the
+ * path validation/whatever is done with and the paths destroyed.) */
+ PCRTCRCERTCTX pCertCtx;
+} RTCRX509CERTPATHNODE;
+/** Pointer to a X.509 path node. */
+typedef RTCRX509CERTPATHNODE *PRTCRX509CERTPATHNODE;
+
+/** @name RTCRX509CERTPATHNODE::uSrc values.
+ * The trusted and untrusted sources ordered in priority order, where higher
+ * number means high priority in case of duplicates.
+ * @{ */
+#define RTCRX509CERTPATHNODE_SRC_NONE 0
+#define RTCRX509CERTPATHNODE_SRC_TARGET 1
+#define RTCRX509CERTPATHNODE_SRC_UNTRUSTED_SET 2
+#define RTCRX509CERTPATHNODE_SRC_UNTRUSTED_ARRAY 3
+#define RTCRX509CERTPATHNODE_SRC_UNTRUSTED_STORE 4
+#define RTCRX509CERTPATHNODE_SRC_TRUSTED_STORE 5
+#define RTCRX509CERTPATHNODE_SRC_TRUSTED_CERT 6
+#define RTCRX509CERTPATHNODE_SRC_IS_TRUSTED(uSrc) ((uSrc) >= RTCRX509CERTPATHNODE_SRC_TRUSTED_STORE)
+/** @} */
+
+
+/**
+ * Policy tree node.
+ */
+typedef struct RTCRX509CERTPATHSPOLICYNODE
+{
+ /** Sibling list entry. */
+ RTLISTNODE SiblingEntry;
+ /** Tree depth list entry. */
+ RTLISTNODE DepthEntry;
+ /** List of children or leaf list entry. */
+ RTLISTANCHOR ChildList;
+ /** Pointer to the parent. */
+ struct RTCRX509CERTPATHSPOLICYNODE *pParent;
+
+ /** The policy object ID. */
+ PCRTASN1OBJID pValidPolicy;
+
+ /** Optional sequence of policy qualifiers. */
+ PCRTCRX509POLICYQUALIFIERINFOS pPolicyQualifiers;
+
+ /** The first policy ID in the exepcted policy set. */
+ PCRTASN1OBJID pExpectedPolicyFirst;
+ /** Set if we've already mapped pExpectedPolicyFirst. */
+ bool fAlreadyMapped;
+ /** Number of additional items in the expected policy set. */
+ uint32_t cMoreExpectedPolicySet;
+ /** Additional items in the expected policy set. */
+ PCRTASN1OBJID *papMoreExpectedPolicySet;
+} RTCRX509CERTPATHSPOLICYNODE;
+/** Pointer to a policy tree node. */
+typedef RTCRX509CERTPATHSPOLICYNODE *PRTCRX509CERTPATHSPOLICYNODE;
+
+
+/**
+ * Path builder and validator instance.
+ *
+ * The path builder creates a tree of certificates by forward searching from the
+ * end-entity towards a trusted source. The leaf nodes are inserted into list
+ * ordered by the source of the leaf certificate and the path length (i.e. tree
+ * depth).
+ *
+ * The path validator works the tree from the leaf end and validates each
+ * potential path found by the builder. It is generally happy with one working
+ * path, but may be told to verify all of them.
+ */
+typedef struct RTCRX509CERTPATHSINT
+{
+ /** Magic number. */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+
+ /** @name Input
+ * @{ */
+ /** The target certificate (end entity) to build a trusted path for. */
+ PCRTCRX509CERTIFICATE pTarget;
+
+ /** Lone trusted certificate. */
+ PCRTCRX509CERTIFICATE pTrustedCert;
+ /** Store of trusted certificates. */
+ RTCRSTORE hTrustedStore;
+
+ /** Store of untrusted certificates. */
+ RTCRSTORE hUntrustedStore;
+ /** Array of untrusted certificates, typically from the protocol. */
+ PCRTCRX509CERTIFICATE paUntrustedCerts;
+ /** Number of entries in paUntrusted. */
+ uint32_t cUntrustedCerts;
+ /** Set of untrusted PKCS \#7 / CMS certificatess. */
+ PCRTCRPKCS7SETOFCERTS pUntrustedCertsSet;
+
+ /** UTC time we're going to validate the path at, requires
+ * RTCRX509CERTPATHSINT_F_VALID_TIME to be set. */
+ RTTIMESPEC ValidTime;
+ /** Number of policy OIDs in the user initial policy set, 0 means anyPolicy. */
+ uint32_t cInitialUserPolicySet;
+ /** The user initial policy set. As with all other user provided data, we
+ * assume it's immutable and remains valid for the usage period of the path
+ * builder & validator. */
+ PCRTASN1OBJID *papInitialUserPolicySet;
+ /** Number of certificates before the user wants an explicit policy result.
+ * Set to UINT32_MAX no explicit policy restriction required by the user. */
+ uint32_t cInitialExplicitPolicy;
+ /** Number of certificates before the user wants policy mapping to be
+ * inhibited. Set to UINT32_MAX if no initial policy mapping inhibition
+ * desired by the user. */
+ uint32_t cInitialPolicyMappingInhibit;
+ /** Number of certificates before the user wants the anyPolicy to be rejected.
+ * Set to UINT32_MAX no explicit policy restriction required by the user. */
+ uint32_t cInitialInhibitAnyPolicy;
+ /** Initial name restriction: Permitted subtrees. */
+ PCRTCRX509GENERALSUBTREES pInitialPermittedSubtrees;
+ /** Initial name restriction: Excluded subtrees. */
+ PCRTCRX509GENERALSUBTREES pInitialExcludedSubtrees;
+
+ /** Flags RTCRX509CERTPATHSINT_F_XXX. */
+ uint32_t fFlags;
+ /** @} */
+
+ /** Sticky status for remembering allocation errors and the like. */
+ int32_t rc;
+ /** Where to store extended error info (optional). */
+ PRTERRINFO pErrInfo;
+
+ /** @name Path Builder Output
+ * @{ */
+ /** Pointer to the root of the tree. This will always be non-NULL after path
+ * building and thus can be reliably used to tell if path building has taken
+ * place or not. */
+ PRTCRX509CERTPATHNODE pRoot;
+ /** List of working leaf tree nodes. */
+ RTLISTANCHOR LeafList;
+ /** The number of paths (leafs). */
+ uint32_t cPaths;
+ /** @} */
+
+ /** Path Validator State. */
+ struct
+ {
+ /** Number of nodes in the certificate path we're validating (aka 'n'). */
+ uint32_t cNodes;
+ /** The current node (0 being the trust anchor). */
+ uint32_t iNode;
+
+ /** The root node of the valid policy tree. */
+ PRTCRX509CERTPATHSPOLICYNODE pValidPolicyTree;
+ /** An array of length cNodes + 1 which tracks all nodes at the given (index)
+ * tree depth via the RTCRX509CERTPATHSPOLICYNODE::DepthEntry member. */
+ PRTLISTANCHOR paValidPolicyDepthLists;
+
+ /** Number of entries in paPermittedSubtrees (name constraints).
+ * If zero, no permitted name constrains currently in effect. */
+ uint32_t cPermittedSubtrees;
+ /** The allocated size of papExcludedSubtrees */
+ uint32_t cPermittedSubtreesAlloc;
+ /** Array of permitted subtrees we've collected so far (name constraints). */
+ PCRTCRX509GENERALSUBTREE *papPermittedSubtrees;
+ /** Set if we end up with an empty set after calculating a name constraints
+ * union. */
+ bool fNoPermittedSubtrees;
+
+ /** Number of entries in paExcludedSubtrees (name constraints).
+ * If zero, no excluded name constrains currently in effect. */
+ uint32_t cExcludedSubtrees;
+ /** Array of excluded subtrees we've collected so far (name constraints). */
+ PCRTCRX509GENERALSUBTREES *papExcludedSubtrees;
+
+ /** Number of non-self-issued certificates to be processed before a non-NULL
+ * paValidPolicyTree is required. */
+ uint32_t cExplicitPolicy;
+ /** Number of non-self-issued certificates to be processed we stop processing
+ * policy mapping extensions. */
+ uint32_t cInhibitPolicyMapping;
+ /** Number of non-self-issued certificates to be processed before a the
+ * anyPolicy is rejected. */
+ uint32_t cInhibitAnyPolicy;
+ /** Number of non-self-issued certificates we're allowed to process. */
+ uint32_t cMaxPathLength;
+
+ /** The working issuer name. */
+ PCRTCRX509NAME pWorkingIssuer;
+ /** The working public key algorithm ID. */
+ PCRTASN1OBJID pWorkingPublicKeyAlgorithm;
+ /** The working public key algorithm parameters. */
+ PCRTASN1DYNTYPE pWorkingPublicKeyParameters;
+ /** A bit string containing the public key. */
+ PCRTASN1BITSTRING pWorkingPublicKey;
+ } v;
+
+ /** An object identifier initialized to anyPolicy. */
+ RTASN1OBJID AnyPolicyObjId;
+
+ /** Temporary scratch space. */
+ char szTmp[1024];
+} RTCRX509CERTPATHSINT;
+typedef RTCRX509CERTPATHSINT *PRTCRX509CERTPATHSINT;
+
+/** Magic value for RTCRX509CERTPATHSINT::u32Magic (Bruce Schneier). */
+#define RTCRX509CERTPATHSINT_MAGIC UINT32_C(0x19630115)
+
+/** @name RTCRX509CERTPATHSINT_F_XXX - Certificate path build flags.
+ * @{ */
+#define RTCRX509CERTPATHSINT_F_VALID_TIME RT_BIT_32(0)
+#define RTCRX509CERTPATHSINT_F_ELIMINATE_UNTRUSTED_PATHS RT_BIT_32(1)
+/** Whether checking the trust anchor signature (if self signed) and
+ * that it is valid at the verification time, also require it to be a CA if not
+ * leaf node. */
+#define RTCRX509CERTPATHSINT_F_CHECK_TRUST_ANCHOR RT_BIT_32(2)
+#define RTCRX509CERTPATHSINT_F_VALID_MASK UINT32_C(0x00000007)
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void rtCrX509CertPathsDestroyTree(PRTCRX509CERTPATHSINT pThis);
+static void rtCrX509CpvCleanup(PRTCRX509CERTPATHSINT pThis);
+
+
+/** @name Path Builder and Validator Config APIs
+ * @{
+ */
+
+RTDECL(int) RTCrX509CertPathsCreate(PRTCRX509CERTPATHS phCertPaths, PCRTCRX509CERTIFICATE pTarget)
+{
+ AssertPtrReturn(phCertPaths, VERR_INVALID_POINTER);
+
+ PRTCRX509CERTPATHSINT pThis = (PRTCRX509CERTPATHSINT)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ int rc = RTAsn1ObjId_InitFromString(&pThis->AnyPolicyObjId, RTCRX509_ID_CE_CP_ANY_POLICY_OID, &g_RTAsn1DefaultAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->u32Magic = RTCRX509CERTPATHSINT_MAGIC;
+ pThis->cRefs = 1;
+ pThis->pTarget = pTarget;
+ pThis->hTrustedStore = NIL_RTCRSTORE;
+ pThis->hUntrustedStore = NIL_RTCRSTORE;
+ pThis->cInitialExplicitPolicy = UINT32_MAX;
+ pThis->cInitialPolicyMappingInhibit = UINT32_MAX;
+ pThis->cInitialInhibitAnyPolicy = UINT32_MAX;
+ pThis->rc = VINF_SUCCESS;
+ RTListInit(&pThis->LeafList);
+ *phCertPaths = pThis;
+ return VINF_SUCCESS;
+ }
+ return rc;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(uint32_t) RTCrX509CertPathsRetain(RTCRX509CERTPATHS hCertPaths)
+{
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs > 0 && cRefs < 64);
+ return cRefs;
+}
+
+
+RTDECL(uint32_t) RTCrX509CertPathsRelease(RTCRX509CERTPATHS hCertPaths)
+{
+ uint32_t cRefs;
+ if (hCertPaths != NIL_RTCRX509CERTPATHS)
+ {
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, UINT32_MAX);
+
+ cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ Assert(cRefs < 64);
+ if (!cRefs)
+ {
+ /*
+ * No more references, destroy the whole thing.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTCRX509CERTPATHSINT_MAGIC);
+
+ /* config */
+ pThis->pTarget = NULL; /* Referencing user memory. */
+ pThis->pTrustedCert = NULL; /* Referencing user memory. */
+ RTCrStoreRelease(pThis->hTrustedStore);
+ pThis->hTrustedStore = NIL_RTCRSTORE;
+ RTCrStoreRelease(pThis->hUntrustedStore);
+ pThis->hUntrustedStore = NIL_RTCRSTORE;
+ pThis->paUntrustedCerts = NULL; /* Referencing user memory. */
+ pThis->pUntrustedCertsSet = NULL; /* Referencing user memory. */
+ pThis->papInitialUserPolicySet = NULL; /* Referencing user memory. */
+ pThis->pInitialPermittedSubtrees = NULL; /* Referencing user memory. */
+ pThis->pInitialExcludedSubtrees = NULL; /* Referencing user memory. */
+
+ /* builder */
+ rtCrX509CertPathsDestroyTree(pThis);
+
+ /* validator */
+ rtCrX509CpvCleanup(pThis);
+
+ /* misc */
+ RTAsn1VtDelete(&pThis->AnyPolicyObjId.Asn1Core);
+
+ /* Finally, the instance itself. */
+ RTMemFree(pThis);
+ }
+ }
+ else
+ cRefs = 0;
+ return cRefs;
+}
+
+
+
+RTDECL(int) RTCrX509CertPathsSetTrustedStore(RTCRX509CERTPATHS hCertPaths, RTCRSTORE hTrustedStore)
+{
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->pRoot == NULL, VERR_WRONG_ORDER);
+
+ if (pThis->hTrustedStore != NIL_RTCRSTORE)
+ {
+ RTCrStoreRelease(pThis->hTrustedStore);
+ pThis->hTrustedStore = NIL_RTCRSTORE;
+ }
+ if (hTrustedStore != NIL_RTCRSTORE)
+ {
+ AssertReturn(RTCrStoreRetain(hTrustedStore) != UINT32_MAX, VERR_INVALID_HANDLE);
+ pThis->hTrustedStore = hTrustedStore;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCrX509CertPathsSetUntrustedStore(RTCRX509CERTPATHS hCertPaths, RTCRSTORE hUntrustedStore)
+{
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->pRoot == NULL, VERR_WRONG_ORDER);
+
+ if (pThis->hUntrustedStore != NIL_RTCRSTORE)
+ {
+ RTCrStoreRelease(pThis->hUntrustedStore);
+ pThis->hUntrustedStore = NIL_RTCRSTORE;
+ }
+ if (hUntrustedStore != NIL_RTCRSTORE)
+ {
+ AssertReturn(RTCrStoreRetain(hUntrustedStore) != UINT32_MAX, VERR_INVALID_HANDLE);
+ pThis->hUntrustedStore = hUntrustedStore;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCrX509CertPathsSetUntrustedArray(RTCRX509CERTPATHS hCertPaths, PCRTCRX509CERTIFICATE paCerts, uint32_t cCerts)
+{
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
+
+ pThis->paUntrustedCerts = paCerts;
+ pThis->cUntrustedCerts = cCerts;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCrX509CertPathsSetUntrustedSet(RTCRX509CERTPATHS hCertPaths, PCRTCRPKCS7SETOFCERTS pSetOfCerts)
+{
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
+
+ pThis->pUntrustedCertsSet = pSetOfCerts;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCrX509CertPathsSetValidTime(RTCRX509CERTPATHS hCertPaths, PCRTTIME pTime)
+{
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
+
+ /* Allow this after building paths, as it's only used during verification. */
+
+ if (pTime)
+ {
+ if (RTTimeImplode(&pThis->ValidTime, pTime))
+ return VERR_INVALID_PARAMETER;
+ pThis->fFlags |= RTCRX509CERTPATHSINT_F_VALID_TIME;
+ }
+ else
+ pThis->fFlags &= ~RTCRX509CERTPATHSINT_F_VALID_TIME;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCrX509CertPathsSetValidTimeSpec(RTCRX509CERTPATHS hCertPaths, PCRTTIMESPEC pTimeSpec)
+{
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
+
+ /* Allow this after building paths, as it's only used during verification. */
+
+ if (pTimeSpec)
+ {
+ pThis->ValidTime = *pTimeSpec;
+ pThis->fFlags |= RTCRX509CERTPATHSINT_F_VALID_TIME;
+ }
+ else
+ pThis->fFlags &= ~RTCRX509CERTPATHSINT_F_VALID_TIME;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCrX509CertPathsSetTrustAnchorChecks(RTCRX509CERTPATHS hCertPaths, bool fEnable)
+{
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
+
+ if (fEnable)
+ pThis->fFlags |= RTCRX509CERTPATHSINT_F_CHECK_TRUST_ANCHOR;
+ else
+ pThis->fFlags &= ~RTCRX509CERTPATHSINT_F_CHECK_TRUST_ANCHOR;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCrX509CertPathsCreateEx(PRTCRX509CERTPATHS phCertPaths, PCRTCRX509CERTIFICATE pTarget, RTCRSTORE hTrustedStore,
+ RTCRSTORE hUntrustedStore, PCRTCRX509CERTIFICATE paUntrustedCerts, uint32_t cUntrustedCerts,
+ PCRTTIMESPEC pValidTime)
+{
+ int rc = RTCrX509CertPathsCreate(phCertPaths, pTarget);
+ if (RT_SUCCESS(rc))
+ {
+ PRTCRX509CERTPATHSINT pThis = *phCertPaths;
+
+ rc = RTCrX509CertPathsSetTrustedStore(pThis, hTrustedStore);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrX509CertPathsSetUntrustedStore(pThis, hUntrustedStore);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrX509CertPathsSetUntrustedArray(pThis, paUntrustedCerts, cUntrustedCerts);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrX509CertPathsSetValidTimeSpec(pThis, pValidTime);
+ if (RT_SUCCESS(rc))
+ {
+ return VINF_SUCCESS;
+ }
+ }
+ RTCrStoreRelease(pThis->hUntrustedStore);
+ }
+ RTCrStoreRelease(pThis->hTrustedStore);
+ }
+ RTMemFree(pThis);
+ *phCertPaths = NIL_RTCRX509CERTPATHS;
+ }
+ return rc;
+}
+
+/** @} */
+
+
+
+/** @name Path Builder and Validator Common Utility Functions.
+ * @{
+ */
+
+/**
+ * Checks if the certificate is self-issued.
+ *
+ * @returns true / false.
+ * @param pNode The path node to check..
+ */
+static bool rtCrX509CertPathsIsSelfIssued(PRTCRX509CERTPATHNODE pNode)
+{
+ return pNode->pCert
+ && RTCrX509Name_MatchByRfc5280(&pNode->pCert->TbsCertificate.Subject, &pNode->pCert->TbsCertificate.Issuer);
+}
+
+/**
+ * Helper for checking whether a certificate is in the trusted store or not.
+ */
+static bool rtCrX509CertPathsIsCertInStore(PRTCRX509CERTPATHNODE pNode, RTCRSTORE hStore)
+{
+ bool fRc = false;
+ PCRTCRCERTCTX pCertCtx = RTCrStoreCertByIssuerAndSerialNo(hStore, &pNode->pCert->TbsCertificate.Issuer,
+ &pNode->pCert->TbsCertificate.SerialNumber);
+ if (pCertCtx)
+ {
+ if (pCertCtx->pCert)
+ fRc = RTCrX509Certificate_Compare(pCertCtx->pCert, pNode->pCert) == 0;
+ RTCrCertCtxRelease(pCertCtx);
+ }
+ return fRc;
+}
+
+/** @} */
+
+
+
+/** @name Path Builder Functions.
+ * @{
+ */
+
+static PRTCRX509CERTPATHNODE rtCrX509CertPathsNewNode(PRTCRX509CERTPATHSINT pThis)
+{
+ PRTCRX509CERTPATHNODE pNode = (PRTCRX509CERTPATHNODE)RTMemAllocZ(sizeof(*pNode));
+ if (RT_LIKELY(pNode))
+ {
+ RTListInit(&pNode->SiblingEntry);
+ RTListInit(&pNode->ChildListOrLeafEntry);
+ pNode->rcVerify = VERR_CR_X509_NOT_VERIFIED;
+
+ return pNode;
+ }
+
+ pThis->rc = RTErrInfoSet(pThis->pErrInfo, VERR_NO_MEMORY, "No memory for path node");
+ return NULL;
+}
+
+
+static void rtCrX509CertPathsDestroyNode(PRTCRX509CERTPATHNODE pNode)
+{
+ if (pNode->pCertCtx)
+ {
+ RTCrCertCtxRelease(pNode->pCertCtx);
+ pNode->pCertCtx = NULL;
+ }
+ RT_ZERO(*pNode);
+ RTMemFree(pNode);
+}
+
+
+static void rtCrX509CertPathsAddIssuer(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pParent,
+ PCRTCRX509CERTIFICATE pCert, PCRTCRCERTCTX pCertCtx, uint8_t uSrc)
+{
+ /*
+ * Check if we've seen this certificate already in the current path or
+ * among the already gathered issuers.
+ */
+ if (pCert)
+ {
+ /* No duplicate certificates in the path. */
+ PRTCRX509CERTPATHNODE pTmpNode = pParent;
+ while (pTmpNode)
+ {
+ Assert(pTmpNode->pCert);
+ if ( pTmpNode->pCert == pCert
+ || RTCrX509Certificate_Compare(pTmpNode->pCert, pCert) == 0)
+ {
+ /* If target and the source it trusted, upgrade the source so we can successfully verify single node 'paths'. */
+ if ( RTCRX509CERTPATHNODE_SRC_IS_TRUSTED(uSrc)
+ && pTmpNode == pParent
+ && pTmpNode->uSrc == RTCRX509CERTPATHNODE_SRC_TARGET)
+ {
+ AssertReturnVoid(!pTmpNode->pParent);
+ pTmpNode->uSrc = uSrc;
+ }
+ return;
+ }
+ pTmpNode = pTmpNode->pParent;
+ }
+
+ /* No duplicate tree branches. */
+ RTListForEach(&pParent->ChildListOrLeafEntry, pTmpNode, RTCRX509CERTPATHNODE, SiblingEntry)
+ {
+ if (RTCrX509Certificate_Compare(pTmpNode->pCert, pCert) == 0)
+ return;
+ }
+ }
+ else
+ Assert(pCertCtx);
+
+ /*
+ * Reference the context core before making the allocation.
+ */
+ if (pCertCtx)
+ AssertReturnVoidStmt(RTCrCertCtxRetain(pCertCtx) != UINT32_MAX,
+ pThis->rc = RTErrInfoSetF(pThis->pErrInfo, VERR_CR_X509_CPB_BAD_CERT_CTX,
+ "Bad pCertCtx=%p", pCertCtx));
+
+ /*
+ * We haven't see it, append it as a child.
+ */
+ PRTCRX509CERTPATHNODE pNew = rtCrX509CertPathsNewNode(pThis);
+ if (pNew)
+ {
+ pNew->pParent = pParent;
+ pNew->pCert = pCert;
+ pNew->pCertCtx = pCertCtx;
+ pNew->uSrc = uSrc;
+ pNew->uDepth = pParent->uDepth + 1;
+ RTListAppend(&pParent->ChildListOrLeafEntry, &pNew->SiblingEntry);
+ Log2Func(("pNew=%p uSrc=%u uDepth=%u\n", pNew, uSrc, pNew->uDepth));
+ }
+ else
+ RTCrCertCtxRelease(pCertCtx);
+}
+
+
+static void rtCrX509CertPathsGetIssuersFromStore(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode,
+ PCRTCRX509NAME pIssuer, RTCRSTORE hStore, uint8_t uSrc)
+{
+ RTCRSTORECERTSEARCH Search;
+ int rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(hStore, pIssuer, &Search);
+ if (RT_SUCCESS(rc))
+ {
+ PCRTCRCERTCTX pCertCtx;
+ while ((pCertCtx = RTCrStoreCertSearchNext(hStore, &Search)) != NULL)
+ {
+ if ( pCertCtx->pCert
+ || ( RTCRX509CERTPATHNODE_SRC_IS_TRUSTED(uSrc)
+ && pCertCtx->pTaInfo) )
+ rtCrX509CertPathsAddIssuer(pThis, pNode, pCertCtx->pCert, pCertCtx, uSrc);
+ RTCrCertCtxRelease(pCertCtx);
+ }
+ RTCrStoreCertSearchDestroy(hStore, &Search);
+ }
+}
+
+
+static void rtCrX509CertPathsGetIssuers(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
+{
+ Assert(RTListIsEmpty(&pNode->ChildListOrLeafEntry));
+ Assert(!pNode->fLeaf);
+ Assert(pNode->pCert);
+
+ /*
+ * Don't recurse infintely.
+ */
+ if (RT_UNLIKELY(pNode->uDepth >= 50))
+ return;
+
+ PCRTCRX509NAME const pIssuer = &pNode->pCert->TbsCertificate.Issuer;
+#if defined(LOG_ENABLED) && defined(IN_RING3)
+ if (LogIs2Enabled())
+ {
+ char szIssuer[128] = {0};
+ RTCrX509Name_FormatAsString(pIssuer, szIssuer, sizeof(szIssuer), NULL);
+ char szSubject[128] = {0};
+ RTCrX509Name_FormatAsString(&pNode->pCert->TbsCertificate.Subject, szSubject, sizeof(szSubject), NULL);
+ Log2Func(("pNode=%p uSrc=%u uDepth=%u Issuer='%s' (Subject='%s')\n", pNode, pNode->uSrc, pNode->uDepth, szIssuer, szSubject));
+ }
+#endif
+
+ /*
+ * Trusted certificate.
+ */
+ if ( pThis->pTrustedCert
+ && RTCrX509Certificate_MatchSubjectOrAltSubjectByRfc5280(pThis->pTrustedCert, pIssuer))
+ rtCrX509CertPathsAddIssuer(pThis, pNode, pThis->pTrustedCert, NULL, RTCRX509CERTPATHNODE_SRC_TRUSTED_CERT);
+
+ /*
+ * Trusted certificate store.
+ */
+ if (pThis->hTrustedStore != NIL_RTCRSTORE)
+ rtCrX509CertPathsGetIssuersFromStore(pThis, pNode, pIssuer, pThis->hTrustedStore,
+ RTCRX509CERTPATHNODE_SRC_TRUSTED_STORE);
+
+ /*
+ * Untrusted store.
+ */
+ if (pThis->hUntrustedStore != NIL_RTCRSTORE)
+ rtCrX509CertPathsGetIssuersFromStore(pThis, pNode, pIssuer, pThis->hTrustedStore,
+ RTCRX509CERTPATHNODE_SRC_UNTRUSTED_STORE);
+
+ /*
+ * Untrusted array.
+ */
+ if (pThis->paUntrustedCerts)
+ for (uint32_t i = 0; i < pThis->cUntrustedCerts; i++)
+ if (RTCrX509Certificate_MatchSubjectOrAltSubjectByRfc5280(&pThis->paUntrustedCerts[i], pIssuer))
+ rtCrX509CertPathsAddIssuer(pThis, pNode, &pThis->paUntrustedCerts[i], NULL,
+ RTCRX509CERTPATHNODE_SRC_UNTRUSTED_ARRAY);
+
+ /** @todo Rainy day: Should abstract the untrusted array and set so we don't get
+ * unnecessary PKCS7/CMS header dependencies. */
+
+ /*
+ * Untrusted set.
+ */
+ if (pThis->pUntrustedCertsSet)
+ {
+ uint32_t const cCerts = pThis->pUntrustedCertsSet->cItems;
+ PRTCRPKCS7CERT const *papCerts = pThis->pUntrustedCertsSet->papItems;
+ for (uint32_t i = 0; i < cCerts; i++)
+ {
+ PCRTCRPKCS7CERT pCert = papCerts[i];
+ if ( pCert->enmChoice == RTCRPKCS7CERTCHOICE_X509
+ && RTCrX509Certificate_MatchSubjectOrAltSubjectByRfc5280(pCert->u.pX509Cert, pIssuer))
+ rtCrX509CertPathsAddIssuer(pThis, pNode, pCert->u.pX509Cert, NULL, RTCRX509CERTPATHNODE_SRC_UNTRUSTED_SET);
+ }
+ }
+}
+
+
+static PRTCRX509CERTPATHNODE rtCrX509CertPathsGetNextRightUp(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
+{
+ for (;;)
+ {
+ /* The root node has no siblings. */
+ PRTCRX509CERTPATHNODE pParent = pNode->pParent;
+ if (!pNode->pParent)
+ return NULL;
+
+ /* Try go to the right. */
+ PRTCRX509CERTPATHNODE pNext = RTListGetNext(&pParent->ChildListOrLeafEntry, pNode, RTCRX509CERTPATHNODE, SiblingEntry);
+ if (pNext)
+ return pNext;
+
+ /* Up. */
+ pNode = pParent;
+ }
+
+ RT_NOREF_PV(pThis);
+}
+
+
+static PRTCRX509CERTPATHNODE rtCrX509CertPathsEliminatePath(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
+{
+ for (;;)
+ {
+ Assert(RTListIsEmpty(&pNode->ChildListOrLeafEntry));
+
+ /* Don't remove the root node. */
+ PRTCRX509CERTPATHNODE pParent = pNode->pParent;
+ if (!pParent)
+ return NULL;
+
+ /* Before removing and deleting the node check if there is sibling
+ right to it that we should continue processing from. */
+ PRTCRX509CERTPATHNODE pNext = RTListGetNext(&pParent->ChildListOrLeafEntry, pNode, RTCRX509CERTPATHNODE, SiblingEntry);
+ RTListNodeRemove(&pNode->SiblingEntry);
+ rtCrX509CertPathsDestroyNode(pNode);
+
+ if (pNext)
+ return pNext;
+
+ /* If the parent node cannot be removed, do a normal get-next-rigth-up
+ to find the continuation point for the tree loop. */
+ if (!RTListIsEmpty(&pParent->ChildListOrLeafEntry))
+ return rtCrX509CertPathsGetNextRightUp(pThis, pParent);
+
+ pNode = pParent;
+ }
+}
+
+
+/**
+ * Destroys the whole path tree.
+ *
+ * @param pThis The path builder and verifier instance.
+ */
+static void rtCrX509CertPathsDestroyTree(PRTCRX509CERTPATHSINT pThis)
+{
+ PRTCRX509CERTPATHNODE pNode, pNextLeaf;
+ RTListForEachSafe(&pThis->LeafList, pNode, pNextLeaf, RTCRX509CERTPATHNODE, ChildListOrLeafEntry)
+ {
+ RTListNodeRemove(&pNode->ChildListOrLeafEntry);
+ RTListInit(&pNode->ChildListOrLeafEntry);
+
+ for (;;)
+ {
+ PRTCRX509CERTPATHNODE pParent = pNode->pParent;
+
+ RTListNodeRemove(&pNode->SiblingEntry);
+ rtCrX509CertPathsDestroyNode(pNode);
+
+ if (!pParent)
+ {
+ pThis->pRoot = NULL;
+ break;
+ }
+
+ if (!RTListIsEmpty(&pParent->ChildListOrLeafEntry))
+ break;
+
+ pNode = pParent;
+ }
+ }
+ Assert(!pThis->pRoot);
+}
+
+
+/**
+ * Adds a leaf node.
+ *
+ * This should normally be a trusted certificate, but the caller can also
+ * request the incomplete paths, in which case this will be an untrusted
+ * certificate.
+ *
+ * @returns Pointer to the next node in the tree to process.
+ * @param pThis The path builder instance.
+ * @param pNode The leaf node.
+ */
+static PRTCRX509CERTPATHNODE rtCrX509CertPathsAddLeaf(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
+{
+ pNode->fLeaf = true;
+
+ /*
+ * Priority insert by source and depth.
+ */
+ PRTCRX509CERTPATHNODE pCurLeaf;
+ RTListForEach(&pThis->LeafList, pCurLeaf, RTCRX509CERTPATHNODE, ChildListOrLeafEntry)
+ {
+ if ( pNode->uSrc > pCurLeaf->uSrc
+ || ( pNode->uSrc == pCurLeaf->uSrc
+ && pNode->uDepth < pCurLeaf->uDepth) )
+ {
+ RTListNodeInsertBefore(&pCurLeaf->ChildListOrLeafEntry, &pNode->ChildListOrLeafEntry);
+ pThis->cPaths++;
+ return rtCrX509CertPathsGetNextRightUp(pThis, pNode);
+ }
+ }
+
+ RTListAppend(&pThis->LeafList, &pNode->ChildListOrLeafEntry);
+ pThis->cPaths++;
+ return rtCrX509CertPathsGetNextRightUp(pThis, pNode);
+}
+
+
+
+RTDECL(int) RTCrX509CertPathsBuild(RTCRX509CERTPATHS hCertPaths, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate the input.
+ */
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!(pThis->fFlags & ~RTCRX509CERTPATHSINT_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn( (pThis->paUntrustedCerts == NULL && pThis->cUntrustedCerts == 0)
+ || (pThis->paUntrustedCerts != NULL && pThis->cUntrustedCerts > 0),
+ VERR_INVALID_PARAMETER);
+ AssertReturn(RTListIsEmpty(&pThis->LeafList), VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->pRoot == NULL, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->rc == VINF_SUCCESS, pThis->rc);
+ AssertPtrReturn(pThis->pTarget, VERR_INVALID_PARAMETER);
+ Assert(RT_SUCCESS(RTCrX509Certificate_CheckSanity(pThis->pTarget, 0, NULL, NULL)));
+
+ /*
+ * Set up the target.
+ */
+ PRTCRX509CERTPATHNODE pCur;
+ pThis->pRoot = pCur = rtCrX509CertPathsNewNode(pThis);
+ if (pThis->pRoot)
+ {
+ pCur->pCert = pThis->pTarget;
+ pCur->uDepth = 0;
+ pCur->uSrc = RTCRX509CERTPATHNODE_SRC_TARGET;
+
+ /* Check if the target is trusted and do the upgrade (this is outside the RFC,
+ but this simplifies the path validator usage a lot (less work for the caller)). */
+ if ( pThis->pTrustedCert
+ && RTCrX509Certificate_Compare(pThis->pTrustedCert, pCur->pCert) == 0)
+ pCur->uSrc = RTCRX509CERTPATHNODE_SRC_TRUSTED_CERT;
+ else if ( pThis->hTrustedStore != NIL_RTCRSTORE
+ && rtCrX509CertPathsIsCertInStore(pCur, pThis->hTrustedStore))
+ pCur->uSrc = RTCRX509CERTPATHNODE_SRC_TRUSTED_STORE;
+
+ pThis->pErrInfo = pErrInfo;
+
+ /*
+ * The tree construction loop.
+ * Walks down, up, and right as the tree is constructed.
+ */
+ do
+ {
+ /*
+ * Check for the two leaf cases first.
+ */
+ if (RTCRX509CERTPATHNODE_SRC_IS_TRUSTED(pCur->uSrc))
+ pCur = rtCrX509CertPathsAddLeaf(pThis, pCur);
+#if 0 /* This isn't right.*/
+ else if (rtCrX509CertPathsIsSelfIssued(pCur))
+ {
+ if (pThis->fFlags & RTCRX509CERTPATHSINT_F_ELIMINATE_UNTRUSTED_PATHS)
+ pCur = rtCrX509CertPathsEliminatePath(pThis, pCur);
+ else
+ pCur = rtCrX509CertPathsAddLeaf(pThis, pCur);
+ }
+#endif
+ /*
+ * Not a leaf, find all potential issuers and decend into these.
+ */
+ else
+ {
+ rtCrX509CertPathsGetIssuers(pThis, pCur);
+ if (RT_FAILURE(pThis->rc))
+ break;
+
+ if (!RTListIsEmpty(&pCur->ChildListOrLeafEntry))
+ pCur = RTListGetFirst(&pCur->ChildListOrLeafEntry, RTCRX509CERTPATHNODE, SiblingEntry);
+ else if (pThis->fFlags & RTCRX509CERTPATHSINT_F_ELIMINATE_UNTRUSTED_PATHS)
+ pCur = rtCrX509CertPathsEliminatePath(pThis, pCur);
+ else
+ pCur = rtCrX509CertPathsAddLeaf(pThis, pCur);
+ }
+ if (pCur)
+ Log2(("RTCrX509CertPathsBuild: pCur=%p fLeaf=%d pParent=%p pNext=%p pPrev=%p\n",
+ pCur, pCur->fLeaf, pCur->pParent,
+ pCur->pParent ? RTListGetNext(&pCur->pParent->ChildListOrLeafEntry, pCur, RTCRX509CERTPATHNODE, SiblingEntry) : NULL,
+ pCur->pParent ? RTListGetPrev(&pCur->pParent->ChildListOrLeafEntry, pCur, RTCRX509CERTPATHNODE, SiblingEntry) : NULL));
+ } while (pCur);
+
+ pThis->pErrInfo = NULL;
+ if (RT_SUCCESS(pThis->rc))
+ return VINF_SUCCESS;
+ }
+ else
+ Assert(RT_FAILURE_NP(pThis->rc));
+ return pThis->rc;
+}
+
+
+/**
+ * Looks up path by leaf/path index.
+ *
+ * @returns Pointer to the leaf node of the path.
+ * @param pThis The path builder & validator instance.
+ * @param iPath The oridnal of the path to get.
+ */
+static PRTCRX509CERTPATHNODE rtCrX509CertPathsGetLeafByIndex(PRTCRX509CERTPATHSINT pThis, uint32_t iPath)
+{
+ Assert(iPath < pThis->cPaths);
+
+ uint32_t iCurPath = 0;
+ PRTCRX509CERTPATHNODE pCurLeaf;
+ RTListForEach(&pThis->LeafList, pCurLeaf, RTCRX509CERTPATHNODE, ChildListOrLeafEntry)
+ {
+ if (iCurPath == iPath)
+ return pCurLeaf;
+ iCurPath++;
+ }
+
+ AssertFailedReturn(NULL);
+}
+
+
+static void rtDumpPrintf(PFNRTDUMPPRINTFV pfnPrintfV, void *pvUser, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ pfnPrintfV(pvUser, pszFormat, va);
+ va_end(va);
+}
+
+
+static void rtDumpIndent(PFNRTDUMPPRINTFV pfnPrintfV, void *pvUser, uint32_t cchSpaces, const char *pszFormat, ...)
+{
+ static const char s_szSpaces[] = " ";
+ while (cchSpaces > 0)
+ {
+ uint32_t cchBurst = RT_MIN(sizeof(s_szSpaces) - 1, cchSpaces);
+ rtDumpPrintf(pfnPrintfV, pvUser, &s_szSpaces[sizeof(s_szSpaces) - cchBurst - 1]);
+ cchSpaces -= cchBurst;
+ }
+
+ va_list va;
+ va_start(va, pszFormat);
+ pfnPrintfV(pvUser, pszFormat, va);
+ va_end(va);
+}
+
+/** @name X.500 attribute types
+ * See RFC-4519 among others.
+ * @{ */
+#define RTCRX500_ID_AT_OBJECT_CLASS_OID "2.5.4.0"
+#define RTCRX500_ID_AT_ALIASED_ENTRY_NAME_OID "2.5.4.1"
+#define RTCRX500_ID_AT_KNOWLDGEINFORMATION_OID "2.5.4.2"
+#define RTCRX500_ID_AT_COMMON_NAME_OID "2.5.4.3"
+#define RTCRX500_ID_AT_SURNAME_OID "2.5.4.4"
+#define RTCRX500_ID_AT_SERIAL_NUMBER_OID "2.5.4.5"
+#define RTCRX500_ID_AT_COUNTRY_NAME_OID "2.5.4.6"
+#define RTCRX500_ID_AT_LOCALITY_NAME_OID "2.5.4.7"
+#define RTCRX500_ID_AT_STATE_OR_PROVINCE_NAME_OID "2.5.4.8"
+#define RTCRX500_ID_AT_STREET_ADDRESS_OID "2.5.4.9"
+#define RTCRX500_ID_AT_ORGANIZATION_NAME_OID "2.5.4.10"
+#define RTCRX500_ID_AT_ORGANIZATION_UNIT_NAME_OID "2.5.4.11"
+#define RTCRX500_ID_AT_TITLE_OID "2.5.4.12"
+#define RTCRX500_ID_AT_DESCRIPTION_OID "2.5.4.13"
+#define RTCRX500_ID_AT_SEARCH_GUIDE_OID "2.5.4.14"
+#define RTCRX500_ID_AT_BUSINESS_CATEGORY_OID "2.5.4.15"
+#define RTCRX500_ID_AT_POSTAL_ADDRESS_OID "2.5.4.16"
+#define RTCRX500_ID_AT_POSTAL_CODE_OID "2.5.4.17"
+#define RTCRX500_ID_AT_POST_OFFICE_BOX_OID "2.5.4.18"
+#define RTCRX500_ID_AT_PHYSICAL_DELIVERY_OFFICE_NAME_OID "2.5.4.19"
+#define RTCRX500_ID_AT_TELEPHONE_NUMBER_OID "2.5.4.20"
+#define RTCRX500_ID_AT_TELEX_NUMBER_OID "2.5.4.21"
+#define RTCRX500_ID_AT_TELETEX_TERMINAL_IDENTIFIER_OID "2.5.4.22"
+#define RTCRX500_ID_AT_FACIMILE_TELEPHONE_NUMBER_OID "2.5.4.23"
+#define RTCRX500_ID_AT_X121_ADDRESS_OID "2.5.4.24"
+#define RTCRX500_ID_AT_INTERNATIONAL_ISDN_NUMBER_OID "2.5.4.25"
+#define RTCRX500_ID_AT_REGISTERED_ADDRESS_OID "2.5.4.26"
+#define RTCRX500_ID_AT_DESTINATION_INDICATOR_OID "2.5.4.27"
+#define RTCRX500_ID_AT_PREFERRED_DELIVERY_METHOD_OID "2.5.4.28"
+#define RTCRX500_ID_AT_PRESENTATION_ADDRESS_OID "2.5.4.29"
+#define RTCRX500_ID_AT_SUPPORTED_APPLICATION_CONTEXT_OID "2.5.4.30"
+#define RTCRX500_ID_AT_MEMBER_OID "2.5.4.31"
+#define RTCRX500_ID_AT_OWNER_OID "2.5.4.32"
+#define RTCRX500_ID_AT_ROLE_OCCUPANT_OID "2.5.4.33"
+#define RTCRX500_ID_AT_SEE_ALSO_OID "2.5.4.34"
+#define RTCRX500_ID_AT_USER_PASSWORD_OID "2.5.4.35"
+#define RTCRX500_ID_AT_USER_CERTIFICATE_OID "2.5.4.36"
+#define RTCRX500_ID_AT_CA_CERTIFICATE_OID "2.5.4.37"
+#define RTCRX500_ID_AT_AUTHORITY_REVOCATION_LIST_OID "2.5.4.38"
+#define RTCRX500_ID_AT_CERTIFICATE_REVOCATION_LIST_OID "2.5.4.39"
+#define RTCRX500_ID_AT_CROSS_CERTIFICATE_PAIR_OID "2.5.4.40"
+#define RTCRX500_ID_AT_NAME_OID "2.5.4.41"
+#define RTCRX500_ID_AT_GIVEN_NAME_OID "2.5.4.42"
+#define RTCRX500_ID_AT_INITIALS_OID "2.5.4.43"
+#define RTCRX500_ID_AT_GENERATION_QUALIFIER_OID "2.5.4.44"
+#define RTCRX500_ID_AT_UNIQUE_IDENTIFIER_OID "2.5.4.45"
+#define RTCRX500_ID_AT_DN_QUALIFIER_OID "2.5.4.46"
+#define RTCRX500_ID_AT_ENHANCHED_SEARCH_GUIDE_OID "2.5.4.47"
+#define RTCRX500_ID_AT_PROTOCOL_INFORMATION_OID "2.5.4.48"
+#define RTCRX500_ID_AT_DISTINGUISHED_NAME_OID "2.5.4.49"
+#define RTCRX500_ID_AT_UNIQUE_MEMBER_OID "2.5.4.50"
+#define RTCRX500_ID_AT_HOUSE_IDENTIFIER_OID "2.5.4.51"
+#define RTCRX500_ID_AT_SUPPORTED_ALGORITHMS_OID "2.5.4.52"
+#define RTCRX500_ID_AT_DELTA_REVOCATION_LIST_OID "2.5.4.53"
+#define RTCRX500_ID_AT_ATTRIBUTE_CERTIFICATE_OID "2.5.4.58"
+#define RTCRX500_ID_AT_PSEUDONYM_OID "2.5.4.65"
+/** @} */
+
+
+static void rtCrX509NameDump(PCRTCRX509NAME pName, PFNRTDUMPPRINTFV pfnPrintfV, void *pvUser)
+{
+ for (uint32_t i = 0; i < pName->cItems; i++)
+ {
+ PCRTCRX509RELATIVEDISTINGUISHEDNAME const pRdn = pName->papItems[i];
+ for (uint32_t j = 0; j < pRdn->cItems; j++)
+ {
+ PRTCRX509ATTRIBUTETYPEANDVALUE pAttrib = pRdn->papItems[j];
+
+ const char *pszType = RTCrX509Name_GetShortRdn(&pAttrib->Type);
+ if (!pszType)
+ pszType = pAttrib->Type.szObjId;
+ rtDumpPrintf(pfnPrintfV, pvUser, "/%s=", pszType);
+ if (pAttrib->Value.enmType == RTASN1TYPE_STRING)
+ {
+ if (pAttrib->Value.u.String.pszUtf8)
+ rtDumpPrintf(pfnPrintfV, pvUser, "%s", pAttrib->Value.u.String.pszUtf8);
+ else
+ {
+ const char *pch = pAttrib->Value.u.String.Asn1Core.uData.pch;
+ uint32_t cch = pAttrib->Value.u.String.Asn1Core.cb;
+ int rc = RTStrValidateEncodingEx(pch, cch, 0);
+ if (RT_SUCCESS(rc) && cch)
+ rtDumpPrintf(pfnPrintfV, pvUser, "%.*s", (size_t)cch, pch);
+ else
+ while (cch > 0)
+ {
+ if (RT_C_IS_PRINT(*pch))
+ rtDumpPrintf(pfnPrintfV, pvUser, "%c", *pch);
+ else
+ rtDumpPrintf(pfnPrintfV, pvUser, "\\x%02x", *pch);
+ cch--;
+ pch++;
+ }
+ }
+ }
+ else
+ rtDumpPrintf(pfnPrintfV, pvUser, "<not-string: uTag=%#x>", pAttrib->Value.u.Core.uTag);
+ }
+ }
+}
+
+
+static const char *rtCrX509CertPathsNodeGetSourceName(PRTCRX509CERTPATHNODE pNode)
+{
+ switch (pNode->uSrc)
+ {
+ case RTCRX509CERTPATHNODE_SRC_TARGET: return "target";
+ case RTCRX509CERTPATHNODE_SRC_UNTRUSTED_SET: return "untrusted_set";
+ case RTCRX509CERTPATHNODE_SRC_UNTRUSTED_ARRAY: return "untrusted_array";
+ case RTCRX509CERTPATHNODE_SRC_UNTRUSTED_STORE: return "untrusted_store";
+ case RTCRX509CERTPATHNODE_SRC_TRUSTED_STORE: return "trusted_store";
+ case RTCRX509CERTPATHNODE_SRC_TRUSTED_CERT: return "trusted_cert";
+ default: return "invalid";
+ }
+}
+
+
+static void rtCrX509CertPathsDumpOneWorker(PRTCRX509CERTPATHSINT pThis, uint32_t iPath, PRTCRX509CERTPATHNODE pCurLeaf,
+ uint32_t uVerbosity, PFNRTDUMPPRINTFV pfnPrintfV, void *pvUser)
+{
+ RT_NOREF_PV(pThis);
+ rtDumpPrintf(pfnPrintfV, pvUser, "Path #%u: %s, %u deep, rcVerify=%Rrc\n",
+ iPath, RTCRX509CERTPATHNODE_SRC_IS_TRUSTED(pCurLeaf->uSrc) ? "trusted" : "untrusted", pCurLeaf->uDepth,
+ pCurLeaf->rcVerify);
+
+ for (uint32_t iIndent = 2; pCurLeaf; iIndent += 2, pCurLeaf = pCurLeaf->pParent)
+ {
+ if (pCurLeaf->pCert)
+ {
+ rtDumpIndent(pfnPrintfV, pvUser, iIndent, "Issuer : ");
+ rtCrX509NameDump(&pCurLeaf->pCert->TbsCertificate.Issuer, pfnPrintfV, pvUser);
+ rtDumpPrintf(pfnPrintfV, pvUser, "\n");
+
+ rtDumpIndent(pfnPrintfV, pvUser, iIndent, "Subject: ");
+ rtCrX509NameDump(&pCurLeaf->pCert->TbsCertificate.Subject, pfnPrintfV, pvUser);
+ rtDumpPrintf(pfnPrintfV, pvUser, "\n");
+
+ if (uVerbosity >= 4)
+ RTAsn1Dump(&pCurLeaf->pCert->SeqCore.Asn1Core, 0, iIndent, pfnPrintfV, pvUser);
+ else if (uVerbosity >= 3)
+ RTAsn1Dump(&pCurLeaf->pCert->TbsCertificate.T3.Extensions.SeqCore.Asn1Core, 0, iIndent, pfnPrintfV, pvUser);
+
+ rtDumpIndent(pfnPrintfV, pvUser, iIndent, "Valid : %s thru %s\n",
+ RTTimeToString(&pCurLeaf->pCert->TbsCertificate.Validity.NotBefore.Time,
+ pThis->szTmp, sizeof(pThis->szTmp) / 2),
+ RTTimeToString(&pCurLeaf->pCert->TbsCertificate.Validity.NotAfter.Time,
+ &pThis->szTmp[sizeof(pThis->szTmp) / 2], sizeof(pThis->szTmp) / 2) );
+ }
+ else
+ {
+ Assert(pCurLeaf->pCertCtx); Assert(pCurLeaf->pCertCtx->pTaInfo);
+ rtDumpIndent(pfnPrintfV, pvUser, iIndent, "Subject: ");
+ rtCrX509NameDump(&pCurLeaf->pCertCtx->pTaInfo->CertPath.TaName, pfnPrintfV, pvUser);
+
+ if (uVerbosity >= 4)
+ RTAsn1Dump(&pCurLeaf->pCertCtx->pTaInfo->SeqCore.Asn1Core, 0, iIndent, pfnPrintfV, pvUser);
+ }
+
+ const char *pszSrc = rtCrX509CertPathsNodeGetSourceName(pCurLeaf);
+ rtDumpIndent(pfnPrintfV, pvUser, iIndent, "Source : %s\n", pszSrc);
+ }
+}
+
+
+RTDECL(int) RTCrX509CertPathsDumpOne(RTCRX509CERTPATHS hCertPaths, uint32_t iPath, uint32_t uVerbosity,
+ PFNRTDUMPPRINTFV pfnPrintfV, void *pvUser)
+{
+ /*
+ * Validate the input.
+ */
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pfnPrintfV, VERR_INVALID_POINTER);
+ int rc;
+ if (iPath < pThis->cPaths)
+ {
+ PRTCRX509CERTPATHNODE pLeaf = rtCrX509CertPathsGetLeafByIndex(pThis, iPath);
+ if (pLeaf)
+ {
+ rtCrX509CertPathsDumpOneWorker(pThis, iPath, pLeaf, uVerbosity, pfnPrintfV, pvUser);
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_CR_X509_CERTPATHS_INTERNAL_ERROR;
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ return rc;
+}
+
+
+RTDECL(int) RTCrX509CertPathsDumpAll(RTCRX509CERTPATHS hCertPaths, uint32_t uVerbosity, PFNRTDUMPPRINTFV pfnPrintfV, void *pvUser)
+{
+ /*
+ * Validate the input.
+ */
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pfnPrintfV, VERR_INVALID_POINTER);
+
+ /*
+ * Dump all the paths.
+ */
+ rtDumpPrintf(pfnPrintfV, pvUser, "%u paths, rc=%Rrc\n", pThis->cPaths, pThis->rc);
+ uint32_t iPath = 0;
+ PRTCRX509CERTPATHNODE pCurLeaf, pNextLeaf;
+ RTListForEachSafe(&pThis->LeafList, pCurLeaf, pNextLeaf, RTCRX509CERTPATHNODE, ChildListOrLeafEntry)
+ {
+ rtCrX509CertPathsDumpOneWorker(pThis, iPath, pCurLeaf, uVerbosity, pfnPrintfV, pvUser);
+ iPath++;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/** @} */
+
+
+/** @name Path Validator Functions.
+ * @{
+ */
+
+
+static void *rtCrX509CpvAllocZ(PRTCRX509CERTPATHSINT pThis, size_t cb, const char *pszWhat)
+{
+ void *pv = RTMemAllocZ(cb);
+ if (!pv)
+ pThis->rc = RTErrInfoSetF(pThis->pErrInfo, VERR_NO_MEMORY, "Failed to allocate %zu bytes for %s", cb, pszWhat);
+ return pv;
+}
+
+
+DECL_NO_INLINE(static, bool) rtCrX509CpvFailed(PRTCRX509CERTPATHSINT pThis, int rc, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ pThis->rc = RTErrInfoSetV(pThis->pErrInfo, rc, pszFormat, va);
+ va_end(va);
+ return false;
+}
+
+
+/**
+ * Adds a sequence of excluded sub-trees.
+ *
+ * Don't waste time optimizing the output if this is supposed to be a union.
+ * Unless the path is very long, it's a lot more work to optimize and the result
+ * will be the same anyway.
+ *
+ * @returns success indicator.
+ * @param pThis The validator instance.
+ * @param pSubtrees The sequence of sub-trees to add.
+ */
+static bool rtCrX509CpvAddExcludedSubtrees(PRTCRX509CERTPATHSINT pThis, PCRTCRX509GENERALSUBTREES pSubtrees)
+{
+ if (((pThis->v.cExcludedSubtrees + 1) & 0xf) == 0)
+ {
+ void *pvNew = RTMemRealloc(pThis->v.papExcludedSubtrees,
+ (pThis->v.cExcludedSubtrees + 16) * sizeof(pThis->v.papExcludedSubtrees[0]));
+ if (RT_UNLIKELY(!pvNew))
+ return rtCrX509CpvFailed(pThis, VERR_NO_MEMORY, "Error growing subtrees pointer array to %u elements",
+ pThis->v.cExcludedSubtrees + 16);
+ pThis->v.papExcludedSubtrees = (PCRTCRX509GENERALSUBTREES *)pvNew;
+ }
+ pThis->v.papExcludedSubtrees[pThis->v.cExcludedSubtrees] = pSubtrees;
+ pThis->v.cExcludedSubtrees++;
+ return true;
+}
+
+
+/**
+ * Checks if a sub-tree is according to RFC-5280.
+ *
+ * @returns Success indiciator.
+ * @param pThis The validator instance.
+ * @param pSubtree The subtree to check.
+ */
+static bool rtCrX509CpvCheckSubtreeValidity(PRTCRX509CERTPATHSINT pThis, PCRTCRX509GENERALSUBTREE pSubtree)
+{
+ if ( pSubtree->Base.enmChoice <= RTCRX509GENERALNAMECHOICE_INVALID
+ || pSubtree->Base.enmChoice >= RTCRX509GENERALNAMECHOICE_END)
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_UNEXP_GENERAL_SUBTREE_CHOICE,
+ "Unexpected GeneralSubtree choice %#x", pSubtree->Base.enmChoice);
+
+ if (RTAsn1Integer_UnsignedCompareWithU32(&pSubtree->Minimum, 0) != 0)
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_UNEXP_GENERAL_SUBTREE_MIN,
+ "Unexpected GeneralSubtree Minimum value: %#llx",
+ pSubtree->Minimum.uValue);
+
+ if (RTAsn1Integer_IsPresent(&pSubtree->Maximum))
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_UNEXP_GENERAL_SUBTREE_MAX,
+ "Unexpected GeneralSubtree Maximum value: %#llx",
+ pSubtree->Maximum.uValue);
+
+ return true;
+}
+
+
+/**
+ * Grows the array of permitted sub-trees.
+ *
+ * @returns success indiciator.
+ * @param pThis The validator instance.
+ * @param cAdding The number of subtrees we should grow by
+ * (relative to the current number of valid
+ * entries).
+ */
+static bool rtCrX509CpvGrowPermittedSubtrees(PRTCRX509CERTPATHSINT pThis, uint32_t cAdding)
+{
+ uint32_t cNew = RT_ALIGN_32(pThis->v.cPermittedSubtrees + cAdding, 16);
+ if (cNew > pThis->v.cPermittedSubtreesAlloc)
+ {
+ if (cNew >= _4K)
+ return rtCrX509CpvFailed(pThis, VERR_NO_MEMORY, "Too many permitted subtrees: %u (cur %u)",
+ cNew, pThis->v.cPermittedSubtrees);
+ void *pvNew = RTMemRealloc(pThis->v.papPermittedSubtrees, cNew * sizeof(pThis->v.papPermittedSubtrees[0]));
+ if (RT_UNLIKELY(!pvNew))
+ return rtCrX509CpvFailed(pThis, VERR_NO_MEMORY, "Error growing subtrees pointer array from %u to %u elements",
+ pThis->v.cPermittedSubtreesAlloc, cNew);
+ pThis->v.papPermittedSubtrees = (PCRTCRX509GENERALSUBTREE *)pvNew;
+ }
+ return true;
+}
+
+
+/**
+ * Adds a sequence of permitted sub-trees.
+ *
+ * We store reference to each individual sub-tree because we must support
+ * intersection calculation.
+ *
+ * @returns success indiciator.
+ * @param pThis The validator instance.
+ * @param cSubtrees The number of sub-trees to add.
+ * @param papSubtrees Array of sub-trees to add.
+ */
+static bool rtCrX509CpvAddPermittedSubtrees(PRTCRX509CERTPATHSINT pThis, uint32_t cSubtrees,
+ PRTCRX509GENERALSUBTREE const *papSubtrees)
+{
+ /*
+ * If the array is empty, assume no permitted names.
+ */
+ if (!cSubtrees)
+ {
+ pThis->v.fNoPermittedSubtrees = true;
+ return true;
+ }
+
+ /*
+ * Grow the array if necessary.
+ */
+ if (!rtCrX509CpvGrowPermittedSubtrees(pThis, cSubtrees))
+ return false;
+
+ /*
+ * Append each subtree to the array.
+ */
+ uint32_t iDst = pThis->v.cPermittedSubtrees;
+ for (uint32_t iSrc = 0; iSrc < cSubtrees; iSrc++)
+ {
+ if (!rtCrX509CpvCheckSubtreeValidity(pThis, papSubtrees[iSrc]))
+ return false;
+ pThis->v.papPermittedSubtrees[iDst] = papSubtrees[iSrc];
+ iDst++;
+ }
+ pThis->v.cPermittedSubtrees = iDst;
+
+ return true;
+}
+
+
+/**
+ * Adds a one permitted sub-tree.
+ *
+ * We store reference to each individual sub-tree because we must support
+ * intersection calculation.
+ *
+ * @returns success indiciator.
+ * @param pThis The validator instance.
+ * @param pSubtree Array of sub-trees to add.
+ */
+static bool rtCrX509CpvAddPermittedSubtree(PRTCRX509CERTPATHSINT pThis, PCRTCRX509GENERALSUBTREE pSubtree)
+{
+ return rtCrX509CpvAddPermittedSubtrees(pThis, 1, (PRTCRX509GENERALSUBTREE const *)&pSubtree);
+}
+
+
+/**
+ * Calculates the intersection between @a pSubtrees and the current permitted
+ * sub-trees.
+ *
+ * @returns Success indicator.
+ * @param pThis The validator instance.
+ * @param pSubtrees The sub-tree sequence to intersect with.
+ */
+static bool rtCrX509CpvIntersectionPermittedSubtrees(PRTCRX509CERTPATHSINT pThis, PCRTCRX509GENERALSUBTREES pSubtrees)
+{
+ /*
+ * Deal with special cases first.
+ */
+ if (pThis->v.fNoPermittedSubtrees)
+ {
+ Assert(pThis->v.cPermittedSubtrees == 0);
+ return true;
+ }
+
+ uint32_t cRight = pSubtrees->cItems;
+ PRTCRX509GENERALSUBTREE const *papRight = pSubtrees->papItems;
+ if (cRight == 0)
+ {
+ pThis->v.cPermittedSubtrees = 0;
+ pThis->v.fNoPermittedSubtrees = true;
+ return true;
+ }
+
+ uint32_t cLeft = pThis->v.cPermittedSubtrees;
+ PCRTCRX509GENERALSUBTREE *papLeft = pThis->v.papPermittedSubtrees;
+ if (!cLeft) /* first name constraint, no initial constraint */
+ return rtCrX509CpvAddPermittedSubtrees(pThis, cRight, papRight);
+
+ /*
+ * Create a new array with the intersection, freeing the old (left) array
+ * once we're done.
+ */
+ bool afRightTags[RTCRX509GENERALNAMECHOICE_END] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ pThis->v.cPermittedSubtrees = 0;
+ pThis->v.cPermittedSubtreesAlloc = 0;
+ pThis->v.papPermittedSubtrees = NULL;
+
+ for (uint32_t iRight = 0; iRight < cRight; iRight++)
+ {
+ if (!rtCrX509CpvCheckSubtreeValidity(pThis, papRight[iRight]))
+ return false;
+
+ RTCRX509GENERALNAMECHOICE const enmRightChoice = papRight[iRight]->Base.enmChoice;
+ afRightTags[enmRightChoice] = true;
+
+ bool fHaveRight = false;
+ for (uint32_t iLeft = 0; iLeft < cLeft; iLeft++)
+ if (papLeft[iLeft]->Base.enmChoice == enmRightChoice)
+ {
+ if (RTCrX509GeneralSubtree_Compare(papLeft[iLeft], papRight[iRight]) == 0)
+ {
+ if (!fHaveRight)
+ {
+ fHaveRight = true;
+ rtCrX509CpvAddPermittedSubtree(pThis, papLeft[iLeft]);
+ }
+ }
+ else if (RTCrX509GeneralSubtree_ConstraintMatch(papLeft[iLeft], papRight[iRight]))
+ {
+ if (!fHaveRight)
+ {
+ fHaveRight = true;
+ rtCrX509CpvAddPermittedSubtree(pThis, papRight[iRight]);
+ }
+ }
+ else if (RTCrX509GeneralSubtree_ConstraintMatch(papRight[iRight], papLeft[iLeft]))
+ rtCrX509CpvAddPermittedSubtree(pThis, papLeft[iLeft]);
+ }
+ }
+
+ /*
+ * Add missing types not specified in the right set.
+ */
+ for (uint32_t iLeft = 0; iLeft < cLeft; iLeft++)
+ if (!afRightTags[papLeft[iLeft]->Base.enmChoice])
+ rtCrX509CpvAddPermittedSubtree(pThis, papLeft[iLeft]);
+
+ /*
+ * If we ended up with an empty set, no names are permitted any more.
+ */
+ if (pThis->v.cPermittedSubtrees == 0)
+ pThis->v.fNoPermittedSubtrees = true;
+
+ RTMemFree(papLeft);
+ return RT_SUCCESS(pThis->rc);
+}
+
+
+/**
+ * Check if the given X.509 name is permitted by current name constraints.
+ *
+ * @returns true is permitteded, false if not (caller set error info).
+ * @param pThis The validator instance.
+ * @param pName The name to match.
+ */
+static bool rtCrX509CpvIsNamePermitted(PRTCRX509CERTPATHSINT pThis, PCRTCRX509NAME pName)
+{
+ uint32_t i = pThis->v.cPermittedSubtrees;
+ if (i == 0)
+ return !pThis->v.fNoPermittedSubtrees;
+
+ while (i-- > 0)
+ {
+ PCRTCRX509GENERALSUBTREE pConstraint = pThis->v.papPermittedSubtrees[i];
+ if ( RTCRX509GENERALNAME_IS_DIRECTORY_NAME(&pConstraint->Base)
+ && RTCrX509Name_ConstraintMatch(&pConstraint->Base.u.pT4->DirectoryName, pName))
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Check if the given X.509 general name is permitted by current name
+ * constraints.
+ *
+ * @returns true is permitteded, false if not (caller sets error info).
+ * @param pThis The validator instance.
+ * @param pGeneralName The name to match.
+ */
+static bool rtCrX509CpvIsGeneralNamePermitted(PRTCRX509CERTPATHSINT pThis, PCRTCRX509GENERALNAME pGeneralName)
+{
+ uint32_t i = pThis->v.cPermittedSubtrees;
+ if (i == 0)
+ return !pThis->v.fNoPermittedSubtrees;
+
+ while (i-- > 0)
+ if (RTCrX509GeneralName_ConstraintMatch(&pThis->v.papPermittedSubtrees[i]->Base, pGeneralName))
+ return true;
+ return false;
+}
+
+
+/**
+ * Check if the given X.509 name is excluded by current name constraints.
+ *
+ * @returns true if excluded (caller sets error info), false if not explicitly
+ * excluded.
+ * @param pThis The validator instance.
+ * @param pName The name to match.
+ */
+static bool rtCrX509CpvIsNameExcluded(PRTCRX509CERTPATHSINT pThis, PCRTCRX509NAME pName)
+{
+ uint32_t i = pThis->v.cExcludedSubtrees;
+ while (i-- > 0)
+ {
+ PCRTCRX509GENERALSUBTREES pSubTrees = pThis->v.papExcludedSubtrees[i];
+ uint32_t j = pSubTrees->cItems;
+ while (j-- > 0)
+ {
+ PCRTCRX509GENERALSUBTREE const pSubTree = pSubTrees->papItems[j];
+ if ( RTCRX509GENERALNAME_IS_DIRECTORY_NAME(&pSubTree->Base)
+ && RTCrX509Name_ConstraintMatch(&pSubTree->Base.u.pT4->DirectoryName, pName))
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/**
+ * Check if the given X.509 general name is excluded by current name
+ * constraints.
+ *
+ * @returns true if excluded (caller sets error info), false if not explicitly
+ * excluded.
+ * @param pThis The validator instance.
+ * @param pGeneralName The name to match.
+ */
+static bool rtCrX509CpvIsGeneralNameExcluded(PRTCRX509CERTPATHSINT pThis, PCRTCRX509GENERALNAME pGeneralName)
+{
+ uint32_t i = pThis->v.cExcludedSubtrees;
+ while (i-- > 0)
+ {
+ PCRTCRX509GENERALSUBTREES pSubTrees = pThis->v.papExcludedSubtrees[i];
+ uint32_t j = pSubTrees->cItems;
+ while (j-- > 0)
+ if (RTCrX509GeneralName_ConstraintMatch(&pSubTrees->papItems[j]->Base, pGeneralName))
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Creates a new node and inserts it.
+ *
+ * @param pThis The path builder & validator instance.
+ * @param pParent The parent node. NULL for the root node.
+ * @param iDepth The tree depth to insert at.
+ * @param pValidPolicy The valid policy of the new node.
+ * @param pQualifiers The qualifiers of the new node.
+ * @param pExpectedPolicy The (first) expected polcy of the new node.
+ */
+static bool rtCrX509CpvPolicyTreeInsertNew(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHSPOLICYNODE pParent, uint32_t iDepth,
+ PCRTASN1OBJID pValidPolicy, PCRTCRX509POLICYQUALIFIERINFOS pQualifiers,
+ PCRTASN1OBJID pExpectedPolicy)
+{
+ Assert(iDepth <= pThis->v.cNodes);
+
+ PRTCRX509CERTPATHSPOLICYNODE pNode;
+ pNode = (PRTCRX509CERTPATHSPOLICYNODE)rtCrX509CpvAllocZ(pThis, sizeof(*pNode), "policy tree node");
+ if (pNode)
+ {
+ pNode->pParent = pParent;
+ if (pParent)
+ RTListAppend(&pParent->ChildList, &pNode->SiblingEntry);
+ else
+ {
+ Assert(pThis->v.pValidPolicyTree == NULL);
+ pThis->v.pValidPolicyTree = pNode;
+ RTListInit(&pNode->SiblingEntry);
+ }
+ RTListInit(&pNode->ChildList);
+ RTListAppend(&pThis->v.paValidPolicyDepthLists[iDepth], &pNode->DepthEntry);
+
+ pNode->pValidPolicy = pValidPolicy;
+ pNode->pPolicyQualifiers = pQualifiers;
+ pNode->pExpectedPolicyFirst = pExpectedPolicy;
+ pNode->cMoreExpectedPolicySet = 0;
+ pNode->papMoreExpectedPolicySet = NULL;
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Unlinks and frees a node in the valid policy tree.
+ *
+ * @param pThis The path builder & validator instance.
+ * @param pNode The node to destroy.
+ */
+static void rtCrX509CpvPolicyTreeDestroyNode(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHSPOLICYNODE pNode)
+{
+ Assert(RTListIsEmpty(&pNode->ChildList));
+ if (pNode->pParent)
+ RTListNodeRemove(&pNode->SiblingEntry);
+ else
+ pThis->v.pValidPolicyTree = NULL;
+ RTListNodeRemove(&pNode->DepthEntry);
+ pNode->pParent = NULL;
+
+ if (pNode->papMoreExpectedPolicySet)
+ {
+ RTMemFree(pNode->papMoreExpectedPolicySet);
+ pNode->papMoreExpectedPolicySet = NULL;
+ }
+ RTMemFree(pNode);
+}
+
+
+/**
+ * Unlinks and frees a sub-tree in the valid policy tree.
+ *
+ * @param pThis The path builder & validator instance.
+ * @param pNode The node that is the root of the subtree.
+ */
+static void rtCrX509CpvPolicyTreeDestroySubtree(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHSPOLICYNODE pNode)
+{
+ if (!RTListIsEmpty(&pNode->ChildList))
+ {
+ PRTCRX509CERTPATHSPOLICYNODE pCur = pNode;
+ do
+ {
+ Assert(!RTListIsEmpty(&pCur->ChildList));
+
+ /* Decend until we find a leaf. */
+ do
+ pCur = RTListGetFirst(&pCur->ChildList, RTCRX509CERTPATHSPOLICYNODE, SiblingEntry);
+ while (!RTListIsEmpty(&pCur->ChildList));
+
+ /* Remove it and all leafy siblings. */
+ PRTCRX509CERTPATHSPOLICYNODE pParent = pCur->pParent;
+ do
+ {
+ Assert(pCur != pNode);
+ rtCrX509CpvPolicyTreeDestroyNode(pThis, pCur);
+ pCur = RTListGetFirst(&pParent->ChildList, RTCRX509CERTPATHSPOLICYNODE, SiblingEntry);
+ if (!pCur)
+ {
+ pCur = pParent;
+ pParent = pParent->pParent;
+ }
+ } while (RTListIsEmpty(&pCur->ChildList) && pCur != pNode);
+ } while (pCur != pNode);
+ }
+
+ rtCrX509CpvPolicyTreeDestroyNode(pThis, pNode);
+}
+
+
+
+/**
+ * Destroys the entire policy tree.
+ *
+ * @param pThis The path builder & validator instance.
+ */
+static void rtCrX509CpvPolicyTreeDestroy(PRTCRX509CERTPATHSINT pThis)
+{
+ uint32_t i = pThis->v.cNodes + 1;
+ while (i-- > 0)
+ {
+ PRTCRX509CERTPATHSPOLICYNODE pCur, pNext;
+ RTListForEachSafe(&pThis->v.paValidPolicyDepthLists[i], pCur, pNext, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
+ {
+ rtCrX509CpvPolicyTreeDestroyNode(pThis, pCur);
+ }
+ }
+}
+
+
+/**
+ * Removes all leaf nodes at level @a iDepth and above.
+ *
+ * @param pThis The path builder & validator instance.
+ * @param iDepth The depth to start pruning at.
+ */
+static void rtCrX509CpvPolicyTreePrune(PRTCRX509CERTPATHSINT pThis, uint32_t iDepth)
+{
+ do
+ {
+ PRTLISTANCHOR pList = &pThis->v.paValidPolicyDepthLists[iDepth];
+ PRTCRX509CERTPATHSPOLICYNODE pCur, pNext;
+ RTListForEachSafe(pList, pCur, pNext, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
+ {
+ if (RTListIsEmpty(&pCur->ChildList))
+ rtCrX509CpvPolicyTreeDestroyNode(pThis, pCur);
+ }
+
+ } while (iDepth-- > 0);
+}
+
+
+/**
+ * Checks if @a pPolicy is the valid policy of a child of @a pNode.
+ *
+ * @returns true if in child node, false if not.
+ * @param pNode The node which children to check.
+ * @param pPolicy The valid policy to look for among the children.
+ */
+static bool rtCrX509CpvPolicyTreeIsChild(PRTCRX509CERTPATHSPOLICYNODE pNode, PCRTASN1OBJID pPolicy)
+{
+ PRTCRX509CERTPATHSPOLICYNODE pChild;
+ RTListForEach(&pNode->ChildList, pChild, RTCRX509CERTPATHSPOLICYNODE, SiblingEntry)
+ {
+ if (RTAsn1ObjId_Compare(pChild->pValidPolicy, pPolicy) == 0)
+ return true;
+ }
+ return true;
+}
+
+
+/**
+ * Prunes the valid policy tree according to the specified user policy set.
+ *
+ * @returns Pointer to the policy object from @a papPolicies if found, NULL if
+ * no match.
+ * @param pObjId The object ID to locate at match in the set.
+ * @param cPolicies The number of policies in @a papPolicies.
+ * @param papPolicies The policy set to search.
+ */
+static PCRTASN1OBJID rtCrX509CpvFindObjIdInPolicySet(PCRTASN1OBJID pObjId, uint32_t cPolicies, PCRTASN1OBJID *papPolicies)
+{
+ uint32_t i = cPolicies;
+ while (i-- > 0)
+ if (RTAsn1ObjId_Compare(pObjId, papPolicies[i]) == 0)
+ return papPolicies[i];
+ return NULL;
+}
+
+
+/**
+ * Prunes the valid policy tree according to the specified user policy set.
+ *
+ * @returns success indicator (allocates memory)
+ * @param pThis The path builder & validator instance.
+ * @param cPolicies The number of policies in @a papPolicies.
+ * @param papPolicies The user initial policies.
+ */
+static bool rtCrX509CpvPolicyTreeIntersect(PRTCRX509CERTPATHSINT pThis, uint32_t cPolicies, PCRTASN1OBJID *papPolicies)
+{
+ /*
+ * 4.1.6.g.i - NULL tree remains NULL.
+ */
+ if (!pThis->v.pValidPolicyTree)
+ return true;
+
+ /*
+ * 4.1.6.g.ii - If the user set includes anyPolicy, the whole tree is the
+ * result of the intersection.
+ */
+ uint32_t i = cPolicies;
+ while (i-- > 0)
+ if (RTAsn1ObjId_CompareWithString(papPolicies[i], RTCRX509_ID_CE_CP_ANY_POLICY_OID) == 0)
+ return true;
+
+ /*
+ * 4.1.6.g.iii - Complicated.
+ */
+ PRTCRX509CERTPATHSPOLICYNODE pCur, pNext;
+ PRTLISTANCHOR pList;
+
+ /* 1 & 2: Delete nodes which parent has valid policy == anyPolicy and which
+ valid policy is neither anyPolicy nor a member of papszPolicies.
+ While doing so, construct a set of unused user policies that
+ we'll replace anyPolicy nodes with in step 3. */
+ uint32_t cPoliciesLeft = 0;
+ PCRTASN1OBJID *papPoliciesLeft = NULL;
+ if (cPolicies)
+ {
+ papPoliciesLeft = (PCRTASN1OBJID *)rtCrX509CpvAllocZ(pThis, cPolicies * sizeof(papPoliciesLeft[0]), "papPoliciesLeft");
+ if (!papPoliciesLeft)
+ return false;
+ for (i = 0; i < cPolicies; i++)
+ papPoliciesLeft[i] = papPolicies[i];
+ }
+
+ for (uint32_t iDepth = 1; iDepth <= pThis->v.cNodes; iDepth++)
+ {
+ pList = &pThis->v.paValidPolicyDepthLists[iDepth];
+ RTListForEachSafe(pList, pCur, pNext, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
+ {
+ Assert(pCur->pParent);
+ if ( RTAsn1ObjId_CompareWithString(pCur->pParent->pValidPolicy, RTCRX509_ID_CE_CP_ANY_POLICY_OID) == 0
+ && RTAsn1ObjId_CompareWithString(pCur->pValidPolicy, RTCRX509_ID_CE_CP_ANY_POLICY_OID) != 0)
+ {
+ PCRTASN1OBJID pFound = rtCrX509CpvFindObjIdInPolicySet(pCur->pValidPolicy, cPolicies, papPolicies);
+ if (!pFound)
+ rtCrX509CpvPolicyTreeDestroySubtree(pThis, pCur);
+ else
+ for (i = 0; i < cPoliciesLeft; i++)
+ if (papPoliciesLeft[i] == pFound)
+ {
+ cPoliciesLeft--;
+ if (i < cPoliciesLeft)
+ papPoliciesLeft[i] = papPoliciesLeft[cPoliciesLeft];
+ papPoliciesLeft[cPoliciesLeft] = NULL;
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * 4.1.5.g.iii.3 - Replace anyPolicy nodes on the final tree depth with
+ * the policies in papPoliciesLeft.
+ */
+ pList = &pThis->v.paValidPolicyDepthLists[pThis->v.cNodes];
+ RTListForEachSafe(pList, pCur, pNext, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
+ {
+ if (RTAsn1ObjId_CompareWithString(pCur->pValidPolicy, RTCRX509_ID_CE_CP_ANY_POLICY_OID) == 0)
+ {
+ for (i = 0; i < cPoliciesLeft; i++)
+ rtCrX509CpvPolicyTreeInsertNew(pThis, pCur->pParent, pThis->v.cNodes - 1,
+ papPoliciesLeft[i], pCur->pPolicyQualifiers, papPoliciesLeft[i]);
+ rtCrX509CpvPolicyTreeDestroyNode(pThis, pCur);
+ }
+ }
+
+ RTMemFree(papPoliciesLeft);
+
+ /*
+ * 4.1.5.g.iii.4 - Prune the tree
+ */
+ rtCrX509CpvPolicyTreePrune(pThis, pThis->v.cNodes - 1);
+
+ return RT_SUCCESS(pThis->rc);
+}
+
+
+
+/**
+ * Frees the path validator state.
+ *
+ * @param pThis The path builder & validator instance.
+ */
+static void rtCrX509CpvCleanup(PRTCRX509CERTPATHSINT pThis)
+{
+ /*
+ * Destroy the policy tree and all its nodes. We do this from the bottom
+ * up via the depth lists, saving annoying tree traversal.
+ */
+ if (pThis->v.paValidPolicyDepthLists)
+ {
+ rtCrX509CpvPolicyTreeDestroy(pThis);
+
+ RTMemFree(pThis->v.paValidPolicyDepthLists);
+ pThis->v.paValidPolicyDepthLists = NULL;
+ }
+
+ Assert(pThis->v.pValidPolicyTree == NULL);
+ pThis->v.pValidPolicyTree = NULL;
+
+ /*
+ * Destroy the name constraint arrays.
+ */
+ if (pThis->v.papPermittedSubtrees)
+ {
+ RTMemFree(pThis->v.papPermittedSubtrees);
+ pThis->v.papPermittedSubtrees = NULL;
+ }
+ pThis->v.cPermittedSubtrees = 0;
+ pThis->v.cPermittedSubtreesAlloc = 0;
+ pThis->v.fNoPermittedSubtrees = false;
+
+ if (pThis->v.papExcludedSubtrees)
+ {
+ RTMemFree(pThis->v.papExcludedSubtrees);
+ pThis->v.papExcludedSubtrees = NULL;
+ }
+ pThis->v.cExcludedSubtrees = 0;
+
+ /*
+ * Clear other pointers.
+ */
+ pThis->v.pWorkingIssuer = NULL;
+ pThis->v.pWorkingPublicKey = NULL;
+ pThis->v.pWorkingPublicKeyAlgorithm = NULL;
+ pThis->v.pWorkingPublicKeyParameters = NULL;
+}
+
+
+/**
+ * Initializes the state.
+ *
+ * Caller must check pThis->rc.
+ *
+ * @param pThis The path builder & validator instance.
+ * @param pTrustAnchor The trust anchor node for the path that we're about
+ * to validate.
+ */
+static void rtCrX509CpvInit(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pTrustAnchor)
+{
+ rtCrX509CpvCleanup(pThis);
+
+ /*
+ * The node count does not include the trust anchor.
+ */
+ pThis->v.cNodes = pTrustAnchor->uDepth;
+
+ /*
+ * Valid policy tree starts with an anyPolicy node.
+ */
+ uint32_t i = pThis->v.cNodes + 1;
+ pThis->v.paValidPolicyDepthLists = (PRTLISTANCHOR)rtCrX509CpvAllocZ(pThis, i * sizeof(RTLISTANCHOR),
+ "paValidPolicyDepthLists");
+ if (RT_UNLIKELY(!pThis->v.paValidPolicyDepthLists))
+ return;
+ while (i-- > 0)
+ RTListInit(&pThis->v.paValidPolicyDepthLists[i]);
+
+ if (!rtCrX509CpvPolicyTreeInsertNew(pThis, NULL, 0 /* iDepth*/, &pThis->AnyPolicyObjId, NULL, &pThis->AnyPolicyObjId))
+ return;
+ Assert(!RTListIsEmpty(&pThis->v.paValidPolicyDepthLists[0])); Assert(pThis->v.pValidPolicyTree);
+
+ /*
+ * Name constrains.
+ */
+ if (pThis->pInitialPermittedSubtrees)
+ rtCrX509CpvAddPermittedSubtrees(pThis, pThis->pInitialPermittedSubtrees->cItems,
+ pThis->pInitialPermittedSubtrees->papItems);
+ if (pThis->pInitialExcludedSubtrees)
+ rtCrX509CpvAddExcludedSubtrees(pThis, pThis->pInitialExcludedSubtrees);
+
+ /*
+ * Counters.
+ */
+ pThis->v.cExplicitPolicy = pThis->cInitialExplicitPolicy;
+ pThis->v.cInhibitPolicyMapping = pThis->cInitialPolicyMappingInhibit;
+ pThis->v.cInhibitAnyPolicy = pThis->cInitialInhibitAnyPolicy;
+ pThis->v.cMaxPathLength = pThis->v.cNodes;
+
+ /*
+ * Certificate info from the trust anchor.
+ */
+ if (pTrustAnchor->pCert)
+ {
+ PCRTCRX509TBSCERTIFICATE const pTbsCert = &pTrustAnchor->pCert->TbsCertificate;
+ pThis->v.pWorkingIssuer = &pTbsCert->Subject;
+ pThis->v.pWorkingPublicKey = &pTbsCert->SubjectPublicKeyInfo.SubjectPublicKey;
+ pThis->v.pWorkingPublicKeyAlgorithm = &pTbsCert->SubjectPublicKeyInfo.Algorithm.Algorithm;
+ pThis->v.pWorkingPublicKeyParameters = &pTbsCert->SubjectPublicKeyInfo.Algorithm.Parameters;
+ }
+ else
+ {
+ Assert(pTrustAnchor->pCertCtx); Assert(pTrustAnchor->pCertCtx->pTaInfo);
+
+ PCRTCRTAFTRUSTANCHORINFO const pTaInfo = pTrustAnchor->pCertCtx->pTaInfo;
+ pThis->v.pWorkingIssuer = &pTaInfo->CertPath.TaName;
+ pThis->v.pWorkingPublicKey = &pTaInfo->PubKey.SubjectPublicKey;
+ pThis->v.pWorkingPublicKeyAlgorithm = &pTaInfo->PubKey.Algorithm.Algorithm;
+ pThis->v.pWorkingPublicKeyParameters = &pTaInfo->PubKey.Algorithm.Parameters;
+ }
+ if ( !RTASN1CORE_IS_PRESENT(&pThis->v.pWorkingPublicKeyParameters->u.Core)
+ || pThis->v.pWorkingPublicKeyParameters->enmType == RTASN1TYPE_NULL)
+ pThis->v.pWorkingPublicKeyParameters = NULL;
+}
+
+
+/**
+ * This does basic trust anchor checks (similar to 6.1.3.a) before starting on
+ * the RFC-5280 algorithm.
+ */
+static bool rtCrX509CpvMaybeCheckTrustAnchor(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pTrustAnchor)
+{
+ /*
+ * This is optional (not part of RFC-5280) and we need a full certificate
+ * structure to do it.
+ */
+ if (!(pThis->fFlags & RTCRX509CERTPATHSINT_F_CHECK_TRUST_ANCHOR))
+ return true;
+
+ PCRTCRX509CERTIFICATE const pCert = pTrustAnchor->pCert;
+ if (!pCert)
+ return true;
+
+ /*
+ * Verify the certificate signature if self-signed.
+ */
+ if (RTCrX509Certificate_IsSelfSigned(pCert))
+ {
+ int rc = RTCrX509Certificate_VerifySignature(pCert, pThis->v.pWorkingPublicKeyAlgorithm,
+ pThis->v.pWorkingPublicKeyParameters, pThis->v.pWorkingPublicKey,
+ pThis->pErrInfo);
+ if (RT_FAILURE(rc))
+ {
+ pThis->rc = rc;
+ return false;
+ }
+ }
+
+ /*
+ * Verify that the certificate is valid at the specified time.
+ */
+ AssertCompile(sizeof(pThis->szTmp) >= 36 * 3);
+ if ( (pThis->fFlags & RTCRX509CERTPATHSINT_F_VALID_TIME)
+ && !RTCrX509Validity_IsValidAtTimeSpec(&pCert->TbsCertificate.Validity, &pThis->ValidTime))
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NOT_VALID_AT_TIME,
+ "Certificate is not valid (ValidTime=%s Validity=[%s...%s])",
+ RTTimeSpecToString(&pThis->ValidTime, &pThis->szTmp[0], 36),
+ RTTimeToString(&pCert->TbsCertificate.Validity.NotBefore.Time, &pThis->szTmp[36], 36),
+ RTTimeToString(&pCert->TbsCertificate.Validity.NotAfter.Time, &pThis->szTmp[2*36], 36) );
+
+ /*
+ * Verified that the certficiate is not revoked.
+ */
+ /** @todo rainy day. */
+
+ /*
+ * If non-leaf certificate CA must be set, if basic constraints are present.
+ */
+ if (pTrustAnchor->pParent)
+ {
+ if (RTAsn1Integer_UnsignedCompareWithU32(&pTrustAnchor->pCert->TbsCertificate.T0.Version, RTCRX509TBSCERTIFICATE_V3) != 0)
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NOT_V3_CERT,
+ "Only version 3 TA certificates are supported (Version=%llu)",
+ pTrustAnchor->pCert->TbsCertificate.T0.Version.uValue);
+ PCRTCRX509BASICCONSTRAINTS pBasicConstraints = pTrustAnchor->pCert->TbsCertificate.T3.pBasicConstraints;
+ if (pBasicConstraints && !pBasicConstraints->CA.fValue)
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NOT_CA_CERT,
+ "Trust anchor certificate is not marked as a CA");
+ }
+
+ return true;
+}
+
+
+/**
+ * Step 6.1.3.a.
+ */
+static bool rtCrX509CpvCheckBasicCertInfo(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
+{
+ /*
+ * 6.1.3.a.1 - Verify the certificate signature.
+ */
+ int rc = RTCrX509Certificate_VerifySignature(pNode->pCert, pThis->v.pWorkingPublicKeyAlgorithm,
+ pThis->v.pWorkingPublicKeyParameters, pThis->v.pWorkingPublicKey,
+ pThis->pErrInfo);
+ if (RT_FAILURE(rc))
+ {
+ pThis->rc = rc;
+ return false;
+ }
+
+ /*
+ * 6.1.3.a.2 - Verify that the certificate is valid at the specified time.
+ */
+ AssertCompile(sizeof(pThis->szTmp) >= 36 * 3);
+ if ( (pThis->fFlags & RTCRX509CERTPATHSINT_F_VALID_TIME)
+ && !RTCrX509Validity_IsValidAtTimeSpec(&pNode->pCert->TbsCertificate.Validity, &pThis->ValidTime))
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NOT_VALID_AT_TIME,
+ "Certificate is not valid (ValidTime=%s Validity=[%s...%s])",
+ RTTimeSpecToString(&pThis->ValidTime, &pThis->szTmp[0], 36),
+ RTTimeToString(&pNode->pCert->TbsCertificate.Validity.NotBefore.Time, &pThis->szTmp[36], 36),
+ RTTimeToString(&pNode->pCert->TbsCertificate.Validity.NotAfter.Time, &pThis->szTmp[2*36], 36) );
+
+ /*
+ * 6.1.3.a.3 - Verified that the certficiate is not revoked.
+ */
+ /** @todo rainy day. */
+
+ /*
+ * 6.1.3.a.4 - Check the issuer name.
+ */
+ if (!RTCrX509Name_MatchByRfc5280(&pNode->pCert->TbsCertificate.Issuer, pThis->v.pWorkingIssuer))
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_ISSUER_MISMATCH, "Issuer mismatch");
+
+ return true;
+}
+
+
+/**
+ * Step 6.1.3.b-c.
+ */
+static bool rtCrX509CpvCheckNameConstraints(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
+{
+ if (pThis->v.fNoPermittedSubtrees)
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NO_PERMITTED_NAMES, "No permitted subtrees");
+
+ if ( pNode->pCert->TbsCertificate.Subject.cItems > 0
+ && ( !rtCrX509CpvIsNamePermitted(pThis, &pNode->pCert->TbsCertificate.Subject)
+ || rtCrX509CpvIsNameExcluded(pThis, &pNode->pCert->TbsCertificate.Subject)) )
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NAME_NOT_PERMITTED,
+ "Subject name is not permitted by current name constraints");
+
+ PCRTCRX509GENERALNAMES pAltSubjectName = pNode->pCert->TbsCertificate.T3.pAltSubjectName;
+ if (pAltSubjectName)
+ {
+ uint32_t i = pAltSubjectName->cItems;
+ while (i-- > 0)
+ if ( !rtCrX509CpvIsGeneralNamePermitted(pThis, pAltSubjectName->papItems[i])
+ || rtCrX509CpvIsGeneralNameExcluded(pThis, pAltSubjectName->papItems[i]))
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_ALT_NAME_NOT_PERMITTED,
+ "Alternative name #%u is is not permitted by current name constraints", i);
+ }
+
+ return true;
+}
+
+
+/**
+ * Step 6.1.3.d-f.
+ */
+static bool rtCrX509CpvWorkValidPolicyTree(PRTCRX509CERTPATHSINT pThis, uint32_t iDepth, PRTCRX509CERTPATHNODE pNode,
+ bool fSelfIssued)
+{
+ PCRTCRX509CERTIFICATEPOLICIES pPolicies = pNode->pCert->TbsCertificate.T3.pCertificatePolicies;
+ if (pPolicies)
+ {
+ /*
+ * 6.1.3.d.1 - Work the certiciate policies into the tree.
+ */
+ PRTCRX509CERTPATHSPOLICYNODE pCur;
+ PRTLISTANCHOR pListAbove = &pThis->v.paValidPolicyDepthLists[iDepth - 1];
+ uint32_t iAnyPolicy = UINT32_MAX;
+ uint32_t i = pPolicies->cItems;
+ while (i-- > 0)
+ {
+ PCRTCRX509POLICYQUALIFIERINFOS const pQualifiers = &pPolicies->papItems[i]->PolicyQualifiers;
+ PCRTASN1OBJID const pIdP = &pPolicies->papItems[i]->PolicyIdentifier;
+ if (RTAsn1ObjId_CompareWithString(pIdP, RTCRX509_ID_CE_CP_ANY_POLICY_OID) == 0)
+ {
+ iAnyPolicy++;
+ continue;
+ }
+
+ /*
+ * 6.1.3.d.1.i - Create children for matching policies.
+ */
+ uint32_t cMatches = 0;
+ RTListForEach(pListAbove, pCur, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
+ {
+ bool fMatch = RTAsn1ObjId_Compare(pCur->pExpectedPolicyFirst, pIdP) == 0;
+ if (!fMatch && pCur->cMoreExpectedPolicySet)
+ for (uint32_t j = 0; !fMatch && j < pCur->cMoreExpectedPolicySet; j++)
+ fMatch = RTAsn1ObjId_Compare(pCur->papMoreExpectedPolicySet[j], pIdP) == 0;
+ if (fMatch)
+ {
+ if (!rtCrX509CpvPolicyTreeInsertNew(pThis, pCur, iDepth, pIdP, pQualifiers, pIdP))
+ return false;
+ cMatches++;
+ }
+ }
+
+ /*
+ * 6.1.3.d.1.ii - If no matches above do the same for anyPolicy
+ * nodes, only match with valid policy this time.
+ */
+ if (cMatches == 0)
+ {
+ RTListForEach(pListAbove, pCur, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
+ {
+ if (RTAsn1ObjId_CompareWithString(pCur->pExpectedPolicyFirst, RTCRX509_ID_CE_CP_ANY_POLICY_OID) == 0)
+ {
+ if (!rtCrX509CpvPolicyTreeInsertNew(pThis, pCur, iDepth, pIdP, pQualifiers, pIdP))
+ return false;
+ }
+ }
+ }
+ }
+
+ /*
+ * 6.1.3.d.2 - If anyPolicy present, make sure all expected policies
+ * are propagated to the current depth.
+ */
+ if ( iAnyPolicy < pPolicies->cItems
+ && ( pThis->v.cInhibitAnyPolicy > 0
+ || (pNode->pParent && fSelfIssued) ) )
+ {
+ PCRTCRX509POLICYQUALIFIERINFOS pApQ = &pPolicies->papItems[iAnyPolicy]->PolicyQualifiers;
+ RTListForEach(pListAbove, pCur, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
+ {
+ if (!rtCrX509CpvPolicyTreeIsChild(pCur, pCur->pExpectedPolicyFirst))
+ rtCrX509CpvPolicyTreeInsertNew(pThis, pCur, iDepth, pCur->pExpectedPolicyFirst, pApQ,
+ pCur->pExpectedPolicyFirst);
+ for (uint32_t j = 0; j < pCur->cMoreExpectedPolicySet; j++)
+ if (!rtCrX509CpvPolicyTreeIsChild(pCur, pCur->papMoreExpectedPolicySet[j]))
+ rtCrX509CpvPolicyTreeInsertNew(pThis, pCur, iDepth, pCur->papMoreExpectedPolicySet[j], pApQ,
+ pCur->papMoreExpectedPolicySet[j]);
+ }
+ }
+ /*
+ * 6.1.3.d.3 - Prune the tree.
+ */
+ else
+ rtCrX509CpvPolicyTreePrune(pThis, iDepth - 1);
+ }
+ else
+ {
+ /*
+ * 6.1.3.e - No policy extension present, set tree to NULL.
+ */
+ rtCrX509CpvPolicyTreeDestroy(pThis);
+ }
+
+ /*
+ * 6.1.3.f - NULL tree check.
+ */
+ if ( pThis->v.pValidPolicyTree == NULL
+ && pThis->v.cExplicitPolicy == 0)
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NO_VALID_POLICY,
+ "An explicit policy is called for but the valid policy tree is NULL.");
+ return RT_SUCCESS(pThis->rc);
+}
+
+
+/**
+ * Step 6.1.4.a-b.
+ */
+static bool rtCrX509CpvSoakUpPolicyMappings(PRTCRX509CERTPATHSINT pThis, uint32_t iDepth,
+ PCRTCRX509POLICYMAPPINGS pPolicyMappings)
+{
+ /*
+ * 6.1.4.a - The anyPolicy is not allowed in policy mappings as it would
+ * allow an evil intermediate certificate to expand the policy
+ * scope of a certiciate chain without regard to upstream.
+ */
+ uint32_t i = pPolicyMappings->cItems;
+ while (i-- > 0)
+ {
+ PCRTCRX509POLICYMAPPING const pOne = pPolicyMappings->papItems[i];
+ if (RTAsn1ObjId_CompareWithString(&pOne->IssuerDomainPolicy, RTCRX509_ID_CE_CP_ANY_POLICY_OID) == 0)
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_INVALID_POLICY_MAPPING,
+ "Invalid policy mapping %#u: IssuerDomainPolicy is anyPolicy.", i);
+
+ if (RTAsn1ObjId_CompareWithString(&pOne->SubjectDomainPolicy, RTCRX509_ID_CE_CP_ANY_POLICY_OID) == 0)
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_INVALID_POLICY_MAPPING,
+ "Invalid policy mapping %#u: SubjectDomainPolicy is anyPolicy.", i);
+ }
+
+ PRTCRX509CERTPATHSPOLICYNODE pCur, pNext;
+ if (pThis->v.cInhibitPolicyMapping > 0)
+ {
+ /*
+ * 6.1.4.b.1 - Do the policy mapping.
+ */
+ i = pPolicyMappings->cItems;
+ while (i-- > 0)
+ {
+ PCRTCRX509POLICYMAPPING const pOne = pPolicyMappings->papItems[i];
+
+ uint32_t cFound = 0;
+ RTListForEach(&pThis->v.paValidPolicyDepthLists[iDepth], pCur, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
+ {
+ if (RTAsn1ObjId_Compare(pCur->pValidPolicy, &pOne->IssuerDomainPolicy))
+ {
+ if (!pCur->fAlreadyMapped)
+ {
+ pCur->fAlreadyMapped = true;
+ pCur->pExpectedPolicyFirst = &pOne->SubjectDomainPolicy;
+ }
+ else
+ {
+ uint32_t iExpected = pCur->cMoreExpectedPolicySet;
+ void *pvNew = RTMemRealloc(pCur->papMoreExpectedPolicySet,
+ sizeof(pCur->papMoreExpectedPolicySet[0]) * (iExpected + 1));
+ if (!pvNew)
+ return rtCrX509CpvFailed(pThis, VERR_NO_MEMORY,
+ "Error growing papMoreExpectedPolicySet array (cur %u, depth %u)",
+ pCur->cMoreExpectedPolicySet, iDepth);
+ pCur->papMoreExpectedPolicySet = (PCRTASN1OBJID *)pvNew;
+ pCur->papMoreExpectedPolicySet[iExpected] = &pOne->SubjectDomainPolicy;
+ pCur->cMoreExpectedPolicySet = iExpected + 1;
+ }
+ cFound++;
+ }
+ }
+
+ /*
+ * If no mapping took place, look for an anyPolicy node.
+ */
+ if (!cFound)
+ {
+ RTListForEach(&pThis->v.paValidPolicyDepthLists[iDepth], pCur, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
+ {
+ if (RTAsn1ObjId_CompareWithString(pCur->pValidPolicy, RTCRX509_ID_CE_CP_ANY_POLICY_OID) == 0)
+ {
+ if (!rtCrX509CpvPolicyTreeInsertNew(pThis, pCur->pParent, iDepth,
+ &pOne->IssuerDomainPolicy,
+ pCur->pPolicyQualifiers,
+ &pOne->SubjectDomainPolicy))
+ return false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /*
+ * 6.1.4.b.2 - Remove matching policies from the tree if mapping is
+ * inhibited and prune the tree.
+ */
+ uint32_t cRemoved = 0;
+ i = pPolicyMappings->cItems;
+ while (i-- > 0)
+ {
+ PCRTCRX509POLICYMAPPING const pOne = pPolicyMappings->papItems[i];
+ RTListForEachSafe(&pThis->v.paValidPolicyDepthLists[iDepth], pCur, pNext, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
+ {
+ if (RTAsn1ObjId_Compare(pCur->pValidPolicy, &pOne->IssuerDomainPolicy))
+ {
+ rtCrX509CpvPolicyTreeDestroyNode(pThis, pCur);
+ cRemoved++;
+ }
+ }
+ }
+ if (cRemoved)
+ rtCrX509CpvPolicyTreePrune(pThis, iDepth - 1);
+ }
+
+ return true;
+}
+
+
+/**
+ * Step 6.1.4.d-f & 6.1.5.c-e.
+ */
+static void rtCrX509CpvSetWorkingPublicKeyInfo(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
+{
+ PCRTCRX509TBSCERTIFICATE const pTbsCert = &pNode->pCert->TbsCertificate;
+
+ /*
+ * 6.1.4.d - The public key.
+ */
+ pThis->v.pWorkingPublicKey = &pTbsCert->SubjectPublicKeyInfo.SubjectPublicKey;
+
+ /*
+ * 6.1.4.e - The public key parameters. Use new ones if present, keep old
+ * if the algorithm remains the same.
+ */
+ if ( RTASN1CORE_IS_PRESENT(&pTbsCert->SubjectPublicKeyInfo.Algorithm.Parameters.u.Core)
+ && pTbsCert->SubjectPublicKeyInfo.Algorithm.Parameters.enmType != RTASN1TYPE_NULL)
+ pThis->v.pWorkingPublicKeyParameters = &pTbsCert->SubjectPublicKeyInfo.Algorithm.Parameters;
+ else if ( pThis->v.pWorkingPublicKeyParameters
+ && RTAsn1ObjId_Compare(pThis->v.pWorkingPublicKeyAlgorithm, &pTbsCert->SubjectPublicKeyInfo.Algorithm.Algorithm) != 0)
+ pThis->v.pWorkingPublicKeyParameters = NULL;
+
+ /*
+ * 6.1.4.f - The public algorithm.
+ */
+ pThis->v.pWorkingPublicKeyAlgorithm = &pTbsCert->SubjectPublicKeyInfo.Algorithm.Algorithm;
+}
+
+
+/**
+ * Step 6.1.4.g.
+ */
+static bool rtCrX509CpvSoakUpNameConstraints(PRTCRX509CERTPATHSINT pThis, PCRTCRX509NAMECONSTRAINTS pNameConstraints)
+{
+ if (pNameConstraints->T0.PermittedSubtrees.cItems > 0)
+ if (!rtCrX509CpvIntersectionPermittedSubtrees(pThis, &pNameConstraints->T0.PermittedSubtrees))
+ return false;
+
+ if (pNameConstraints->T1.ExcludedSubtrees.cItems > 0)
+ if (!rtCrX509CpvAddExcludedSubtrees(pThis, &pNameConstraints->T1.ExcludedSubtrees))
+ return false;
+
+ return true;
+}
+
+
+/**
+ * Step 6.1.4.i.
+ */
+static bool rtCrX509CpvSoakUpPolicyConstraints(PRTCRX509CERTPATHSINT pThis, PCRTCRX509POLICYCONSTRAINTS pPolicyConstraints)
+{
+ if (RTAsn1Integer_IsPresent(&pPolicyConstraints->RequireExplicitPolicy))
+ {
+ if (RTAsn1Integer_UnsignedCompareWithU32(&pPolicyConstraints->RequireExplicitPolicy, pThis->v.cExplicitPolicy) < 0)
+ pThis->v.cExplicitPolicy = pPolicyConstraints->RequireExplicitPolicy.uValue.s.Lo;
+ }
+
+ if (RTAsn1Integer_IsPresent(&pPolicyConstraints->InhibitPolicyMapping))
+ {
+ if (RTAsn1Integer_UnsignedCompareWithU32(&pPolicyConstraints->InhibitPolicyMapping, pThis->v.cInhibitPolicyMapping) < 0)
+ pThis->v.cInhibitPolicyMapping = pPolicyConstraints->InhibitPolicyMapping.uValue.s.Lo;
+ }
+ return true;
+}
+
+
+/**
+ * Step 6.1.4.j.
+ */
+static bool rtCrX509CpvSoakUpInhibitAnyPolicy(PRTCRX509CERTPATHSINT pThis, PCRTASN1INTEGER pInhibitAnyPolicy)
+{
+ if (RTAsn1Integer_UnsignedCompareWithU32(pInhibitAnyPolicy, pThis->v.cInhibitAnyPolicy) < 0)
+ pThis->v.cInhibitAnyPolicy = pInhibitAnyPolicy->uValue.s.Lo;
+ return true;
+}
+
+
+/**
+ * Steps 6.1.4.k, 6.1.4.l, 6.1.4.m, and 6.1.4.n.
+ */
+static bool rtCrX509CpvCheckAndSoakUpBasicConstraintsAndKeyUsage(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode,
+ bool fSelfIssued)
+{
+ /* 6.1.4.k - If basic constraints present, CA must be set. */
+ if (RTAsn1Integer_UnsignedCompareWithU32(&pNode->pCert->TbsCertificate.T0.Version, RTCRX509TBSCERTIFICATE_V3) != 0)
+ {
+ /* Note! Add flags if support for older certificates is needed later. */
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NOT_V3_CERT,
+ "Only version 3 certificates are supported (Version=%llu)",
+ pNode->pCert->TbsCertificate.T0.Version.uValue);
+ }
+ PCRTCRX509BASICCONSTRAINTS pBasicConstraints = pNode->pCert->TbsCertificate.T3.pBasicConstraints;
+ if (pBasicConstraints)
+ {
+ if (!pBasicConstraints->CA.fValue)
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NOT_CA_CERT,
+ "Intermediate certificate (#%u) is not marked as a CA", pThis->v.iNode);
+ }
+
+ /* 6.1.4.l - Work cMaxPathLength. */
+ if (!fSelfIssued)
+ {
+ if (pThis->v.cMaxPathLength > 0)
+ pThis->v.cMaxPathLength--;
+ else
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_MAX_PATH_LENGTH,
+ "Hit max path length at node #%u", pThis->v.iNode);
+ }
+
+ /* 6.1.4.m - Update cMaxPathLength if basic constrain field is present and smaller. */
+ if (pBasicConstraints)
+ {
+ if (RTAsn1Integer_IsPresent(&pBasicConstraints->PathLenConstraint))
+ if (RTAsn1Integer_UnsignedCompareWithU32(&pBasicConstraints->PathLenConstraint, pThis->v.cMaxPathLength) < 0)
+ pThis->v.cMaxPathLength = pBasicConstraints->PathLenConstraint.uValue.s.Lo;
+ }
+
+ /* 6.1.4.n - Require keyCertSign in key usage if the extension is present. */
+ PCRTCRX509TBSCERTIFICATE const pTbsCert = &pNode->pCert->TbsCertificate;
+ if ( (pTbsCert->T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_KEY_USAGE)
+ && !(pTbsCert->T3.fKeyUsage & RTCRX509CERT_KEY_USAGE_F_KEY_CERT_SIGN))
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_MISSING_KEY_CERT_SIGN,
+ "Node #%u does not have KeyCertSign set (keyUsage=%#x)",
+ pThis->v.iNode, pTbsCert->T3.fKeyUsage);
+
+ return true;
+}
+
+
+/**
+ * Step 6.1.4.o - check out critical extensions.
+ */
+static bool rtCrX509CpvCheckCriticalExtensions(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
+{
+ uint32_t cLeft = pNode->pCert->TbsCertificate.T3.Extensions.cItems;
+ PRTCRX509EXTENSION const *ppCur = pNode->pCert->TbsCertificate.T3.Extensions.papItems;
+ while (cLeft-- > 0)
+ {
+ PCRTCRX509EXTENSION const pCur = *ppCur;
+ if (pCur->Critical.fValue)
+ {
+ if ( RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_KEY_USAGE_OID) != 0
+ && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_SUBJECT_ALT_NAME_OID) != 0
+ && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_ISSUER_ALT_NAME_OID) != 0
+ && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_BASIC_CONSTRAINTS_OID) != 0
+ && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_NAME_CONSTRAINTS_OID) != 0
+ && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_CERTIFICATE_POLICIES_OID) != 0
+ && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_POLICY_MAPPINGS_OID) != 0
+ && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_POLICY_CONSTRAINTS_OID) != 0
+ && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_EXT_KEY_USAGE_OID) != 0
+ && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_INHIBIT_ANY_POLICY_OID) != 0
+ && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCR_APPLE_CS_DEVID_APPLICATION_OID) != 0
+ && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCR_APPLE_CS_DEVID_INSTALLER_OID) != 0
+ && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCR_APPLE_CS_DEVID_KEXT_OID) != 0
+ && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCR_APPLE_CS_DEVID_IPHONE_SW_DEV_OID) != 0
+ && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCR_APPLE_CS_DEVID_MAC_SW_DEV_OID) != 0
+ )
+ {
+ /* @bugref{10130}: An IntelGraphicsPE2021 cert issued by iKG_AZSKGFDCS has a critical subjectKeyIdentifier
+ which we quietly ignore here. RFC-5280 conforming CAs should not mark this as critical.
+ On an end entity this extension can have relevance to path construction. */
+ if ( pNode->uSrc == RTCRX509CERTPATHNODE_SRC_TARGET
+ && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_SUBJECT_KEY_IDENTIFIER_OID) == 0)
+ LogFunc(("Ignoring non-standard subjectKeyIdentifier on target certificate.\n"));
+ else
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_UNKNOWN_CRITICAL_EXTENSION,
+ "Node #%u has an unknown critical extension: %s",
+ pThis->v.iNode, pCur->ExtnId.szObjId);
+ }
+ }
+
+ ppCur++;
+ }
+
+ return true;
+}
+
+
+/**
+ * Step 6.1.5 - The wrapping up.
+ */
+static bool rtCrX509CpvWrapUp(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
+{
+ Assert(!pNode->pParent); Assert(pThis->pTarget == pNode->pCert);
+
+ /*
+ * 6.1.5.a - Decrement explicit policy.
+ */
+ if (pThis->v.cExplicitPolicy > 0)
+ pThis->v.cExplicitPolicy--;
+
+ /*
+ * 6.1.5.b - Policy constraints and explicit policy.
+ */
+ PCRTCRX509POLICYCONSTRAINTS pPolicyConstraints = pNode->pCert->TbsCertificate.T3.pPolicyConstraints;
+ if ( pPolicyConstraints
+ && RTAsn1Integer_IsPresent(&pPolicyConstraints->RequireExplicitPolicy)
+ && RTAsn1Integer_UnsignedCompareWithU32(&pPolicyConstraints->RequireExplicitPolicy, 0) == 0)
+ pThis->v.cExplicitPolicy = 0;
+
+ /*
+ * 6.1.5.c-e - Update working public key info.
+ */
+ rtCrX509CpvSetWorkingPublicKeyInfo(pThis, pNode);
+
+ /*
+ * 6.1.5.f - Critical extensions.
+ */
+ if (!rtCrX509CpvCheckCriticalExtensions(pThis, pNode))
+ return false;
+
+ /*
+ * 6.1.5.g - Calculate the intersection between the user initial policy set
+ * and the valid policy tree.
+ */
+ rtCrX509CpvPolicyTreeIntersect(pThis, pThis->cInitialUserPolicySet, pThis->papInitialUserPolicySet);
+
+ if ( pThis->v.cExplicitPolicy == 0
+ && pThis->v.pValidPolicyTree == NULL)
+ return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NO_VALID_POLICY, "No valid policy (wrap-up).");
+
+ return true;
+}
+
+
+/**
+ * Worker that validates one path.
+ *
+ * This implements the algorithm in RFC-5280, section 6.1, with exception of
+ * the CRL checks in 6.1.3.a.3.
+ *
+ * @returns success indicator.
+ * @param pThis The path builder & validator instance.
+ * @param pTrustAnchor The trust anchor node.
+ */
+static bool rtCrX509CpvOneWorker(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pTrustAnchor)
+{
+ /*
+ * Init.
+ */
+ rtCrX509CpvInit(pThis, pTrustAnchor);
+ if (RT_SUCCESS(pThis->rc))
+ {
+ /*
+ * Maybe do some trust anchor checks.
+ */
+ if (!rtCrX509CpvMaybeCheckTrustAnchor(pThis, pTrustAnchor))
+ {
+ AssertStmt(RT_FAILURE_NP(pThis->rc), pThis->rc = VERR_CR_X509_CERTPATHS_INTERNAL_ERROR);
+ return false;
+ }
+
+ /*
+ * Special case, target certificate is trusted.
+ */
+ if (!pTrustAnchor->pParent)
+ return true; /* rtCrX509CpvWrapUp should not be needed here. */
+
+ /*
+ * Normal processing.
+ */
+ PRTCRX509CERTPATHNODE pNode = pTrustAnchor->pParent;
+ uint32_t iNode = pThis->v.iNode = 1; /* We count to cNode (inclusive). Same a validation tree depth. */
+ while (pNode && RT_SUCCESS(pThis->rc))
+ {
+ /*
+ * Basic certificate processing.
+ */
+ if (!rtCrX509CpvCheckBasicCertInfo(pThis, pNode)) /* Step 6.1.3.a */
+ break;
+
+ bool const fSelfIssued = rtCrX509CertPathsIsSelfIssued(pNode);
+ if (!fSelfIssued || !pNode->pParent) /* Step 6.1.3.b-c */
+ if (!rtCrX509CpvCheckNameConstraints(pThis, pNode))
+ break;
+
+ if (!rtCrX509CpvWorkValidPolicyTree(pThis, iNode, pNode, fSelfIssued)) /* Step 6.1.3.d-f */
+ break;
+
+ /*
+ * If it's the last certificate in the path, do wrap-ups.
+ */
+ if (!pNode->pParent) /* Step 6.1.5 */
+ {
+ Assert(iNode == pThis->v.cNodes);
+ if (!rtCrX509CpvWrapUp(pThis, pNode))
+ break;
+ AssertRCBreak(pThis->rc);
+ return true;
+ }
+
+ /*
+ * Preparations for the next certificate.
+ */
+ PCRTCRX509TBSCERTIFICATE const pTbsCert = &pNode->pCert->TbsCertificate;
+ if ( pTbsCert->T3.pPolicyMappings
+ && !rtCrX509CpvSoakUpPolicyMappings(pThis, iNode, pTbsCert->T3.pPolicyMappings)) /* Step 6.1.4.a-b */
+ break;
+
+ pThis->v.pWorkingIssuer = &pTbsCert->Subject; /* Step 6.1.4.c */
+
+ rtCrX509CpvSetWorkingPublicKeyInfo(pThis, pNode); /* Step 6.1.4.d-f */
+
+ if ( pTbsCert->T3.pNameConstraints /* Step 6.1.4.g */
+ && !rtCrX509CpvSoakUpNameConstraints(pThis, pTbsCert->T3.pNameConstraints))
+ break;
+
+ if (!fSelfIssued) /* Step 6.1.4.h */
+ {
+ if (pThis->v.cExplicitPolicy > 0)
+ pThis->v.cExplicitPolicy--;
+ if (pThis->v.cInhibitPolicyMapping > 0)
+ pThis->v.cInhibitPolicyMapping--;
+ if (pThis->v.cInhibitAnyPolicy > 0)
+ pThis->v.cInhibitAnyPolicy--;
+ }
+
+ if ( pTbsCert->T3.pPolicyConstraints /* Step 6.1.4.j */
+ && !rtCrX509CpvSoakUpPolicyConstraints(pThis, pTbsCert->T3.pPolicyConstraints))
+ break;
+
+ if ( pTbsCert->T3.pInhibitAnyPolicy /* Step 6.1.4.j */
+ && !rtCrX509CpvSoakUpInhibitAnyPolicy(pThis, pTbsCert->T3.pInhibitAnyPolicy))
+ break;
+
+ if (!rtCrX509CpvCheckAndSoakUpBasicConstraintsAndKeyUsage(pThis, pNode, fSelfIssued)) /* Step 6.1.4.k-n */
+ break;
+
+ if (!rtCrX509CpvCheckCriticalExtensions(pThis, pNode)) /* Step 6.1.4.o */
+ break;
+
+ /*
+ * Advance to the next certificate.
+ */
+ pNode = pNode->pParent;
+ pThis->v.iNode = ++iNode;
+ }
+ AssertStmt(RT_FAILURE_NP(pThis->rc), pThis->rc = VERR_CR_X509_CERTPATHS_INTERNAL_ERROR);
+ }
+ return false;
+}
+
+
+RTDECL(int) RTCrX509CertPathsValidateOne(RTCRX509CERTPATHS hCertPaths, uint32_t iPath, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate the input.
+ */
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!(pThis->fFlags & ~RTCRX509CERTPATHSINT_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pThis->pTarget, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pThis->pRoot, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->rc == VINF_SUCCESS, VERR_INVALID_PARAMETER);
+
+ /*
+ * Locate the path and validate it.
+ */
+ int rc;
+ if (iPath < pThis->cPaths)
+ {
+ PRTCRX509CERTPATHNODE pLeaf = rtCrX509CertPathsGetLeafByIndex(pThis, iPath);
+ if (pLeaf)
+ {
+ if (RTCRX509CERTPATHNODE_SRC_IS_TRUSTED(pLeaf->uSrc))
+ {
+ pThis->pErrInfo = pErrInfo;
+ rtCrX509CpvOneWorker(pThis, pLeaf);
+ pThis->pErrInfo = NULL;
+ rc = pThis->rc;
+ pThis->rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_CR_X509_NO_TRUST_ANCHOR, "Path #%u is does not have a trust anchor: uSrc=%s",
+ iPath, rtCrX509CertPathsNodeGetSourceName(pLeaf));
+ pLeaf->rcVerify = rc;
+ }
+ else
+ rc = VERR_CR_X509_CERTPATHS_INTERNAL_ERROR;
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ return rc;
+}
+
+
+RTDECL(int) RTCrX509CertPathsValidateAll(RTCRX509CERTPATHS hCertPaths, uint32_t *pcValidPaths, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate the input.
+ */
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!(pThis->fFlags & ~RTCRX509CERTPATHSINT_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pThis->pTarget, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pThis->pRoot, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->rc == VINF_SUCCESS, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pcValidPaths, VERR_INVALID_POINTER);
+
+ /*
+ * Validate the paths.
+ */
+ pThis->pErrInfo = pErrInfo;
+
+ int rcLastFailure = VINF_SUCCESS;
+ uint32_t cValidPaths = 0;
+ PRTCRX509CERTPATHNODE pCurLeaf;
+ RTListForEach(&pThis->LeafList, pCurLeaf, RTCRX509CERTPATHNODE, ChildListOrLeafEntry)
+ {
+ if (RTCRX509CERTPATHNODE_SRC_IS_TRUSTED(pCurLeaf->uSrc))
+ {
+ rtCrX509CpvOneWorker(hCertPaths, pCurLeaf);
+ if (RT_SUCCESS(pThis->rc))
+ cValidPaths++;
+ else
+ rcLastFailure = pThis->rc;
+ pCurLeaf->rcVerify = pThis->rc;
+ pThis->rc = VINF_SUCCESS;
+ }
+ else
+ pCurLeaf->rcVerify = VERR_CR_X509_NO_TRUST_ANCHOR;
+ }
+
+ pThis->pErrInfo = NULL;
+
+ if (pcValidPaths)
+ *pcValidPaths = cValidPaths;
+ if (cValidPaths > 0)
+ return VINF_SUCCESS;
+ if (RT_SUCCESS_NP(rcLastFailure))
+ return RTErrInfoSetF(pErrInfo, VERR_CR_X509_CPV_NO_TRUSTED_PATHS,
+ "None of the %u path(s) have a trust anchor.", pThis->cPaths);
+ return rcLastFailure;
+}
+
+
+RTDECL(uint32_t) RTCrX509CertPathsGetPathCount(RTCRX509CERTPATHS hCertPaths)
+{
+ /*
+ * Validate the input.
+ */
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, UINT32_MAX);
+ AssertPtrReturn(pThis->pRoot, UINT32_MAX);
+
+ /*
+ * Return data.
+ */
+ return pThis->cPaths;
+}
+
+
+RTDECL(int) RTCrX509CertPathsQueryPathInfo(RTCRX509CERTPATHS hCertPaths, uint32_t iPath,
+ bool *pfTrusted, uint32_t *pcNodes, PCRTCRX509NAME *ppSubject,
+ PCRTCRX509SUBJECTPUBLICKEYINFO *ppPublicKeyInfo,
+ PCRTCRX509CERTIFICATE *ppCert, PCRTCRCERTCTX *ppCertCtx,
+ int *prcVerify)
+{
+ /*
+ * Validate the input.
+ */
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pThis->pRoot, VERR_WRONG_ORDER);
+ AssertReturn(iPath < pThis->cPaths, VERR_NOT_FOUND);
+
+ /*
+ * Get the data.
+ */
+ PRTCRX509CERTPATHNODE pLeaf = rtCrX509CertPathsGetLeafByIndex(pThis, iPath);
+ AssertReturn(pLeaf, VERR_CR_X509_INTERNAL_ERROR);
+
+ if (pfTrusted)
+ *pfTrusted = RTCRX509CERTPATHNODE_SRC_IS_TRUSTED(pLeaf->uSrc);
+
+ if (pcNodes)
+ *pcNodes = pLeaf->uDepth + 1; /* Includes both trust anchor and target. */
+
+ if (ppSubject)
+ *ppSubject = pLeaf->pCert ? &pLeaf->pCert->TbsCertificate.Subject : &pLeaf->pCertCtx->pTaInfo->CertPath.TaName;
+
+ if (ppPublicKeyInfo)
+ *ppPublicKeyInfo = pLeaf->pCert ? &pLeaf->pCert->TbsCertificate.SubjectPublicKeyInfo : &pLeaf->pCertCtx->pTaInfo->PubKey;
+
+ if (ppCert)
+ *ppCert = pLeaf->pCert;
+
+ if (ppCertCtx)
+ {
+ if (pLeaf->pCertCtx)
+ {
+ uint32_t cRefs = RTCrCertCtxRetain(pLeaf->pCertCtx);
+ AssertReturn(cRefs != UINT32_MAX, VERR_CR_X509_INTERNAL_ERROR);
+ }
+ *ppCertCtx = pLeaf->pCertCtx;
+ }
+
+ if (prcVerify)
+ *prcVerify = pLeaf->rcVerify;
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(uint32_t) RTCrX509CertPathsGetPathLength(RTCRX509CERTPATHS hCertPaths, uint32_t iPath)
+{
+ /*
+ * Validate the input.
+ */
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, UINT32_MAX);
+ AssertPtrReturn(pThis->pRoot, UINT32_MAX);
+ AssertReturn(iPath < pThis->cPaths, UINT32_MAX);
+
+ /*
+ * Get the data.
+ */
+ PRTCRX509CERTPATHNODE pLeaf = rtCrX509CertPathsGetLeafByIndex(pThis, iPath);
+ AssertReturn(pLeaf, UINT32_MAX);
+ return pLeaf->uDepth + 1;
+}
+
+
+RTDECL(int) RTCrX509CertPathsGetPathVerifyResult(RTCRX509CERTPATHS hCertPaths, uint32_t iPath)
+{
+ /*
+ * Validate the input.
+ */
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pThis->pRoot, VERR_WRONG_ORDER);
+ AssertReturn(iPath < pThis->cPaths, VERR_NOT_FOUND);
+
+ /*
+ * Get the data.
+ */
+ PRTCRX509CERTPATHNODE pLeaf = rtCrX509CertPathsGetLeafByIndex(pThis, iPath);
+ AssertReturn(pLeaf, VERR_CR_X509_INTERNAL_ERROR);
+
+ return pLeaf->rcVerify;
+}
+
+
+static PRTCRX509CERTPATHNODE rtCrX509CertPathsGetPathNodeByIndexes(PRTCRX509CERTPATHSINT pThis, uint32_t iPath, uint32_t iNode)
+{
+ PRTCRX509CERTPATHNODE pNode = rtCrX509CertPathsGetLeafByIndex(pThis, iPath);
+ Assert(pNode);
+ if (pNode)
+ {
+ if (iNode <= pNode->uDepth)
+ {
+ uint32_t uCertDepth = pNode->uDepth - iNode;
+ while (pNode->uDepth > uCertDepth)
+ pNode = pNode->pParent;
+ Assert(pNode);
+ Assert(pNode && pNode->uDepth == uCertDepth);
+ return pNode;
+ }
+ }
+
+ return NULL;
+}
+
+
+RTDECL(PCRTCRX509CERTIFICATE) RTCrX509CertPathsGetPathNodeCert(RTCRX509CERTPATHS hCertPaths, uint32_t iPath, uint32_t iNode)
+{
+ /*
+ * Validate the input.
+ */
+ PRTCRX509CERTPATHSINT pThis = hCertPaths;
+ AssertPtrReturn(pThis, NULL);
+ AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, NULL);
+ AssertPtrReturn(pThis->pRoot, NULL);
+ AssertReturn(iPath < pThis->cPaths, NULL);
+
+ /*
+ * Get the data.
+ */
+ PRTCRX509CERTPATHNODE pNode = rtCrX509CertPathsGetPathNodeByIndexes(pThis, iPath, iNode);
+ if (pNode)
+ return pNode->pCert;
+ return NULL;
+}
+
+
+/** @} */
+
diff --git a/src/VBox/Runtime/common/crypto/x509-core.cpp b/src/VBox/Runtime/common/crypto/x509-core.cpp
new file mode 100644
index 00000000..7dfcfc14
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/x509-core.cpp
@@ -0,0 +1,1934 @@
+/* $Id: x509-core.cpp $ */
+/** @file
+ * IPRT - Crypto - X.509, Core APIs.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/x509.h>
+
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/uni.h>
+#include <iprt/crypto/pkix.h>
+#ifdef RT_STRICT
+# include <iprt/crypto/digest.h>
+#endif
+
+#include "x509-internal.h"
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#ifdef RT_STRICT
+static void rtCrX509AlgorithmIdentifier_AssertTableSanityAndMore(void);
+#endif
+
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-core.h>
+
+
+/*
+ * X.509 Validity.
+ */
+
+RTDECL(bool) RTCrX509Validity_IsValidAtTimeSpec(PCRTCRX509VALIDITY pThis, PCRTTIMESPEC pTimeSpec)
+{
+ if (RTAsn1Time_CompareWithTimeSpec(&pThis->NotBefore, pTimeSpec) > 0)
+ return false;
+ if (RTAsn1Time_CompareWithTimeSpec(&pThis->NotAfter, pTimeSpec) < 0)
+ return false;
+ return true;
+}
+
+
+/*
+ * One X.509 Algorithm Identifier.
+ */
+
+/**
+ * String table with the encryption OIDs (used by g_aSignatureOidInfo).
+ */
+static const char * const g_apszEncryptionOids[] =
+{
+ NULL,
+#define IDX_ENCRYPTION_NIL 0
+ RTCR_X962_ECDSA_OID,
+#define IDX_ENCRYPTION_ECDSA 1
+ RTCR_PKCS1_RSA_OID,
+#define IDX_ENCRYPTION_RSA 2
+};
+
+/**
+ * Information about an algorithm identifier.
+ */
+typedef struct RTCRX509ALGORITHIDENTIFIERINTERNALINFO
+{
+ /** The signature OID. */
+ const char *pszSignatureOid;
+ /** Index into g_apszEncryptionOids of the encryption ODI.
+ * This is IDX_ENCRYPTION_NIL for hashes. */
+ uint8_t idxEncryption;
+ /** The message digest type specified by the OID.
+ * This is set to RTDIGESTTYPE_INVALID in two cases:
+ * 1. Pure encryption algorithm OID (cBitsDigest also zero).
+ * 2. The hash is so esoteric that IPRT doesn't support it. */
+ uint8_t enmDigestType;
+ /** The digest size in bits.
+ * This is ZERO if the OID does not include an hash. */
+ uint16_t cBitsDigest;
+} RTCRX509ALGORITHIDENTIFIERINTERNALINFO;
+typedef RTCRX509ALGORITHIDENTIFIERINTERNALINFO const *PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO;
+
+/**
+ * Signature to encryption OID.
+ *
+ * @note This is sorted to allow binary searching.
+ * @note This origins in pkix-utils.cpp, which is why it uses the other set of
+ * OID defines.
+ */
+static RTCRX509ALGORITHIDENTIFIERINTERNALINFO const g_aSignatureOidInfo[] =
+{
+ { /*1.0.10118.3.0.55*/ RTCRX509ALGORITHMIDENTIFIERID_WHIRLPOOL, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_INVALID, 512, },
+
+ { /*1.2.840.10045.2.1 */ RTCR_X962_ECDSA_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_INVALID, 0, },
+ { /*1.2.840.10045.4.1 */ RTCR_X962_ECDSA_WITH_SHA1_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA1, 160, },
+ { /*1.2.840.10045.4.3.1 */ RTCR_X962_ECDSA_WITH_SHA224_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA224, 224, },
+ { /*1.2.840.10045.4.3.2 */ RTCR_X962_ECDSA_WITH_SHA256_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA256, 256, },
+ { /*1.2.840.10045.4.3.3 */ RTCR_X962_ECDSA_WITH_SHA384_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA384, 384, },
+ { /*1.2.840.10045.4.3.4 */ RTCR_X962_ECDSA_WITH_SHA512_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA512, 512, },
+
+ { /*1.2.840.113549.1.1.1 */ RTCR_PKCS1_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_INVALID, 0, },
+ { /*1.2.840.113549.1.1.11*/ RTCR_PKCS1_SHA256_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA256, 256, },
+ { /*1.2.840.113549.1.1.12*/ RTCR_PKCS1_SHA384_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA384, 384, },
+ { /*1.2.840.113549.1.1.13*/ RTCR_PKCS1_SHA512_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA512, 512, },
+ { /*1.2.840.113549.1.1.14*/ RTCR_PKCS1_SHA224_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA224, 224, },
+ { /*1.2.840.113549.1.1.15*/ RTCR_PKCS1_SHA512T224_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA512T224, 224, },
+ { /*1.2.840.113549.1.1.16*/ RTCR_PKCS1_SHA512T256_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA512T256, 256, },
+ { /*1.2.840.113549.1.1.2*/ RTCR_PKCS1_MD2_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_MD2, 128, },
+ { /*1.2.840.113549.1.1.3*/ RTCR_PKCS1_MD4_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_MD4, 128, },
+ { /*1.2.840.113549.1.1.4*/ RTCR_PKCS1_MD5_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_MD5, 128, },
+ { /*1.2.840.113549.1.1.5*/ RTCR_PKCS1_SHA1_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA1, 160, },
+
+ { /*1.2.840.113549.2.2*/ RTCRX509ALGORITHMIDENTIFIERID_MD2, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_MD2, 128, },
+ { /*1.2.840.113549.2.4*/ RTCRX509ALGORITHMIDENTIFIERID_MD4, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_MD4, 128, },
+ { /*1.2.840.113549.2.5*/ RTCRX509ALGORITHMIDENTIFIERID_MD5, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_MD5, 128, },
+
+ /* oddballs for which we don't support the padding (skip?): */
+ //{ "1.3.14.3.2.11" /*rsaSignature*/, IDX_ENCRYPTION_RSA/*?*/, RTDIGESTTYPE_INVALID, 0, },
+ { "1.3.14.3.2.14" /*mdc2WithRSASignature w/ 9796-2 padding*/, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_INVALID, 0, },
+ //{ "1.3.14.3.2.15" /*sha0WithRSASignature w/ 9796-2 padding*/, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_INVALID, 160, },
+ { "1.3.14.3.2.24" /*md2WithRSASignature w/ 9796-2 padding*/, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_MD2, 128, },
+ { "1.3.14.3.2.25" /*md5WithRSASignature w/ 9796-2 padding*/, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_MD5, 128, },
+ { /*1.3.14.3.2.26*/ RTCRX509ALGORITHMIDENTIFIERID_SHA1, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA1, 160, },
+ { "1.3.14.3.2.29" /*sha1WithRSAEncryption (obsolete?)*/, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA1, 160, },
+
+ { /*2.16.840.1.101.3.4.2.1*/ RTCRX509ALGORITHMIDENTIFIERID_SHA256, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA256, 256, },
+ { /*2.16.840.1.101.3.4.2.10*/ RTCRX509ALGORITHMIDENTIFIERID_SHA3_512, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA3_512, 512, },
+ { /*2.16.840.1.101.3.4.2.2*/ RTCRX509ALGORITHMIDENTIFIERID_SHA384, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA384, 384, },
+ { /*2.16.840.1.101.3.4.2.3*/ RTCRX509ALGORITHMIDENTIFIERID_SHA512, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA512, 512, },
+ { /*2.16.840.1.101.3.4.2.4*/ RTCRX509ALGORITHMIDENTIFIERID_SHA224, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA224, 224, },
+ { /*2.16.840.1.101.3.4.2.5*/ RTCRX509ALGORITHMIDENTIFIERID_SHA512T224,IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA512T224, 224, },
+ { /*2.16.840.1.101.3.4.2.6*/ RTCRX509ALGORITHMIDENTIFIERID_SHA512T256,IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA512T256, 256, },
+ { /*2.16.840.1.101.3.4.2.7*/ RTCRX509ALGORITHMIDENTIFIERID_SHA3_224, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA3_224, 224, },
+ { /*2.16.840.1.101.3.4.2.8*/ RTCRX509ALGORITHMIDENTIFIERID_SHA3_256, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA3_256, 256, },
+ { /*2.16.840.1.101.3.4.2.9*/ RTCRX509ALGORITHMIDENTIFIERID_SHA3_384, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA3_384, 384, },
+
+ { /*2.16.840.1.101.3.4.3.10*/ RTCR_NIST_SHA3_256_WITH_ECDSA_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA3_256, 256, },
+ { /*2.16.840.1.101.3.4.3.11*/ RTCR_NIST_SHA3_384_WITH_ECDSA_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA3_384, 384, },
+ { /*2.16.840.1.101.3.4.3.12*/ RTCR_NIST_SHA3_512_WITH_ECDSA_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA3_512, 512, },
+ { /*2.16.840.1.101.3.4.3.13*/ RTCR_NIST_SHA3_224_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA3_224, 224, },
+ { /*2.16.840.1.101.3.4.3.14*/ RTCR_NIST_SHA3_256_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA3_256, 256, },
+ { /*2.16.840.1.101.3.4.3.15*/ RTCR_NIST_SHA3_384_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA3_384, 384, },
+ { /*2.16.840.1.101.3.4.3.16*/ RTCR_NIST_SHA3_512_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA3_512, 512, },
+ { /*2.16.840.1.101.3.4.3.9*/ RTCR_NIST_SHA3_224_WITH_ECDSA_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA3_224, 224, },
+};
+
+/**
+ * Encryption and digest combining.
+ *
+ * This is a subset of g_aSignatureOidInfo.
+ * @todo Organize this more efficiently...
+ */
+typedef struct RTCRX509ALGORITHIDENTIFIERCOMBINING
+{
+ const char *pszDigestOid;
+ const char *pszEncryptedDigestOid;
+} RTCRX509ALGORITHIDENTIFIERCOMBINING;
+typedef RTCRX509ALGORITHIDENTIFIERCOMBINING const *PCRTCRX509ALGORITHIDENTIFIERCOMBINING;
+
+#undef MY_COMBINE
+#define MY_COMBINE(a_Encrypt, a_Digest) \
+ { RTCRX509ALGORITHMIDENTIFIERID_ ## a_Digest, \
+ RTCRX509ALGORITHMIDENTIFIERID_ ## a_Digest ## _WITH_ ## a_Encrypt }
+
+/** Digest and encryption combinations for ECDSA. */
+static RTCRX509ALGORITHIDENTIFIERCOMBINING const g_aDigestAndEncryptionEcdsa[] =
+{
+ MY_COMBINE(ECDSA, SHA1),
+ MY_COMBINE(ECDSA, SHA224),
+ MY_COMBINE(ECDSA, SHA256),
+ MY_COMBINE(ECDSA, SHA384),
+ MY_COMBINE(ECDSA, SHA512),
+ MY_COMBINE(ECDSA, SHA3_224),
+ MY_COMBINE(ECDSA, SHA3_256),
+ MY_COMBINE(ECDSA, SHA3_384),
+ MY_COMBINE(ECDSA, SHA3_512),
+};
+
+/** Digest and encryption combinations for RSA. */
+static RTCRX509ALGORITHIDENTIFIERCOMBINING const g_aDigestAndEncryptionRsa[] =
+{
+ MY_COMBINE(RSA, SHA1),
+ MY_COMBINE(RSA, SHA256),
+ MY_COMBINE(RSA, SHA512),
+ MY_COMBINE(RSA, SHA384),
+ MY_COMBINE(RSA, MD5),
+ MY_COMBINE(RSA, MD2),
+ MY_COMBINE(RSA, MD4),
+ MY_COMBINE(RSA, SHA224),
+ MY_COMBINE(RSA, SHA512T224),
+ MY_COMBINE(RSA, SHA512T256),
+ MY_COMBINE(RSA, SHA3_224),
+ MY_COMBINE(RSA, SHA3_256),
+ MY_COMBINE(RSA, SHA3_384),
+ MY_COMBINE(RSA, SHA3_512),
+};
+
+#undef MY_COMBINE
+
+/**
+ * Table running parallel to g_apszEncryptionOids.
+ */
+static struct
+{
+ PCRTCRX509ALGORITHIDENTIFIERCOMBINING paCombinations;
+ size_t cCombinations;
+} const g_aDigestAndEncryption[] =
+{
+ /* [IDX_ENCRYPTION_NIL] = */ { NULL, 0 },
+ /* [IDX_ENCRYPTION_ECDSA] = */ { &g_aDigestAndEncryptionEcdsa[0], RT_ELEMENTS(g_aDigestAndEncryptionEcdsa) },
+ /* [IDX_ENCRYPTION_RSA] = */ { &g_aDigestAndEncryptionRsa[0], RT_ELEMENTS(g_aDigestAndEncryptionRsa) },
+};
+AssertCompile(IDX_ENCRYPTION_NIL == 0 && IDX_ENCRYPTION_ECDSA == 1 && IDX_ENCRYPTION_RSA == 2);
+
+
+/**
+ * Looks up info we've got on a algorithm identifier.
+ */
+static PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO rtCrX509AlgorithmIdentifier_LookupInfoByOid(const char *pszSignatureOid)
+{
+#ifdef RT_STRICT
+ /*
+ * Do internal santiy checking on first call.
+ */
+ static bool volatile s_fChecked = false;
+ if (RT_LIKELY(s_fChecked))
+ { /* likely */ }
+ else
+ {
+ s_fChecked = true; /* Must be set before the call, as the callee will calls us again. */
+ rtCrX509AlgorithmIdentifier_AssertTableSanityAndMore();
+ }
+#endif
+
+ /*
+ * Do a binary search of g_aSignatureOidInfo.
+ */
+ size_t iFirst = 0;
+ size_t iEnd = RT_ELEMENTS(g_aSignatureOidInfo);
+ for (;;)
+ {
+ size_t const i = iFirst + (iEnd - iFirst) / 2;
+ int const iDiff = strcmp(pszSignatureOid, g_aSignatureOidInfo[i].pszSignatureOid);
+ if (iDiff < 0)
+ {
+ if (i > iFirst)
+ iEnd = i;
+ else
+ return NULL;
+ }
+ else if (iDiff > 0)
+ {
+ if (i + 1 < iEnd)
+ iFirst = i + 1;
+ else
+ return NULL;
+ }
+ else
+ return &g_aSignatureOidInfo[i];
+ }
+}
+
+#ifdef RT_STRICT
+/**
+ * Check that the g_aSignatureOidInfo and g_aDigestAndEncryption makes sense and
+ * matches up with one another and other IPRT information sources.
+ */
+static void rtCrX509AlgorithmIdentifier_AssertTableSanityAndMore(void)
+{
+ /* Check that binary searching work and that digest info matches up: */
+ for (size_t i = 1; i < RT_ELEMENTS(g_aSignatureOidInfo); i++)
+ Assert(strcmp(g_aSignatureOidInfo[i].pszSignatureOid, g_aSignatureOidInfo[i - 1].pszSignatureOid) > 0);
+ for (size_t i = 0; i < RT_ELEMENTS(g_aSignatureOidInfo); i++)
+ {
+ PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pInfo
+ = rtCrX509AlgorithmIdentifier_LookupInfoByOid(g_aSignatureOidInfo[i].pszSignatureOid);
+ Assert(pInfo && pInfo->pszSignatureOid == g_aSignatureOidInfo[i].pszSignatureOid);
+
+ /* If the digest type is RTDIGESTTYPE_INVALID, we must have an pure encryption entry or an obscure hash function. */
+ if (g_aSignatureOidInfo[i].enmDigestType != RTDIGESTTYPE_INVALID)
+ Assert( RTCrDigestTypeToHashSize((RTDIGESTTYPE)g_aSignatureOidInfo[i].enmDigestType) * 8
+ == g_aSignatureOidInfo[i].cBitsDigest);
+ else
+ Assert(g_aSignatureOidInfo[i].cBitsDigest == 0 || g_aSignatureOidInfo[i].idxEncryption == IDX_ENCRYPTION_NIL);
+
+# ifdef IN_RING3
+ /* Check with the RTCrDigestFindByObjIdString API: */
+ RTDIGESTTYPE enmDigestType2 = (RTDIGESTTYPE)g_aSignatureOidInfo[i].enmDigestType;
+# if defined(IPRT_WITHOUT_DIGEST_MD2) || defined(IPRT_WITHOUT_DIGEST_MD4) || defined(IPRT_WITHOUT_DIGEST_MD5) \
+|| defined(IPRT_WITHOUT_SHA512T224) || defined(IPRT_WITHOUT_SHA512T256) || defined(IPRT_WITHOUT_SHA3)
+ switch (enmDigestType2)
+ {
+ default: break;
+# ifdef IPRT_WITHOUT_DIGEST_MD2
+ case RTDIGESTTYPE_MD2:
+# endif
+# ifdef IPRT_WITHOUT_DIGEST_MD4
+ case RTDIGESTTYPE_MD4:
+# endif
+# ifdef IPRT_WITHOUT_DIGEST_MD5
+ case RTDIGESTTYPE_MD5:
+# endif
+# ifdef IPRT_WITHOUT_SHA512T224
+ case RTDIGESTTYPE_SHA512T224:
+# endif
+# ifdef IPRT_WITHOUT_SHA512T256
+ case RTDIGESTTYPE_SHA512T256:
+# endif
+# ifdef IPRT_WITHOUT_SHA3
+ case RTDIGESTTYPE_SHA3_224:
+ case RTDIGESTTYPE_SHA3_256:
+ case RTDIGESTTYPE_SHA3_384:
+ case RTDIGESTTYPE_SHA3_512:
+# endif
+ enmDigestType2 = RTDIGESTTYPE_INVALID;
+ break;
+ }
+# endif
+ PCRTCRDIGESTDESC const pDigestDesc = RTCrDigestFindByObjIdString(g_aSignatureOidInfo[i].pszSignatureOid,
+ NULL /*ppvOpaque*/);
+ if (pDigestDesc)
+ {
+ AssertMsg(pDigestDesc->enmType == enmDigestType2,
+ ("%s pDigestDesc=%s enmDigestType2=%s\n", g_aSignatureOidInfo[i].pszSignatureOid,
+ RTCrDigestTypeToName(pDigestDesc->enmType), RTCrDigestTypeToName(enmDigestType2)));
+ Assert(pDigestDesc->cbHash * 8 == g_aSignatureOidInfo[i].cBitsDigest);
+ }
+ else
+ AssertMsg(enmDigestType2 == RTDIGESTTYPE_INVALID,
+ ("%s enmDigestType2=%s\n", g_aSignatureOidInfo[i].pszSignatureOid, RTCrDigestTypeToName(enmDigestType2)));
+# endif /* IN_RING3 */
+
+
+# ifdef IN_RING3
+ /* Look it up the encryption descriptor. */
+ const char * const pszCheckEncryptId = g_apszEncryptionOids[g_aSignatureOidInfo[i].idxEncryption];
+ PCRTCRPKIXSIGNATUREDESC const pSigDesc = RTCrPkixSignatureFindByObjIdString(g_aSignatureOidInfo[i].pszSignatureOid,
+ NULL /*ppvOpaque*/);
+ if (pSigDesc)
+ Assert(pszCheckEncryptId && strcmp(pSigDesc->pszObjId, pszCheckEncryptId) == 0);
+# ifdef IPRT_WITH_OPENSSL /* No ECDSA implementation w/o OpenSSL at the moment. */
+ else
+ AssertMsg(!pSigDesc && pInfo->idxEncryption == IDX_ENCRYPTION_NIL, ("%s\n", g_aSignatureOidInfo[i].pszSignatureOid));
+# endif
+# endif /* IN_RING3 */
+ }
+
+ /*
+ * Check that everything in g_aDigestAndEncryption is resolvable here and that the info matches up.
+ */
+ for (size_t idxEncryption = IDX_ENCRYPTION_NIL; idxEncryption < RT_ELEMENTS(g_aDigestAndEncryption); idxEncryption++)
+ {
+ PCRTCRX509ALGORITHIDENTIFIERCOMBINING const paCombinations = g_aDigestAndEncryption[idxEncryption].paCombinations;
+ size_t const cCombinations = g_aDigestAndEncryption[idxEncryption].cCombinations;
+ for (size_t i = 0; i < cCombinations; i++)
+ {
+ PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pInfo
+ = rtCrX509AlgorithmIdentifier_LookupInfoByOid(paCombinations[i].pszEncryptedDigestOid);
+ AssertContinue(pInfo);
+ Assert(strcmp(paCombinations[i].pszEncryptedDigestOid, pInfo->pszSignatureOid) == 0);
+ Assert(pInfo->idxEncryption == idxEncryption);
+ Assert(strcmp(paCombinations[i].pszDigestOid,
+ RTCrDigestTypeToAlgorithmOid((RTDIGESTTYPE)pInfo->enmDigestType)) == 0);
+ }
+ }
+}
+#endif /* RT_STRICT */
+
+
+RTDECL(RTDIGESTTYPE) RTCrX509AlgorithmIdentifier_GetDigestType(PCRTCRX509ALGORITHMIDENTIFIER pThis, bool fPureDigestsOnly)
+{
+ AssertPtrReturn(pThis, RTDIGESTTYPE_INVALID);
+ PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pInfo = rtCrX509AlgorithmIdentifier_LookupInfoByOid(pThis->Algorithm.szObjId);
+ return pInfo && (!fPureDigestsOnly || pInfo->idxEncryption == IDX_ENCRYPTION_NIL)
+ ? (RTDIGESTTYPE)pInfo->enmDigestType : RTDIGESTTYPE_INVALID;
+}
+
+
+RTDECL(uint32_t) RTCrX509AlgorithmIdentifier_GetDigestSize(PCRTCRX509ALGORITHMIDENTIFIER pThis, bool fPureDigestsOnly)
+{
+ AssertPtrReturn(pThis, UINT32_MAX);
+ PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pInfo = rtCrX509AlgorithmIdentifier_LookupInfoByOid(pThis->Algorithm.szObjId);
+ return pInfo && (!fPureDigestsOnly || pInfo->idxEncryption == IDX_ENCRYPTION_NIL)
+ ? pInfo->cBitsDigest / 8 : UINT32_MAX;
+}
+
+
+RTDECL(int) RTCrX509AlgorithmIdentifier_CompareWithString(PCRTCRX509ALGORITHMIDENTIFIER pThis, const char *pszObjId)
+{
+ return strcmp(pThis->Algorithm.szObjId, pszObjId);
+}
+
+
+RTDECL(int) RTCrX509AlgorithmIdentifier_CompareDigestOidAndEncryptedDigestOid(const char *pszDigestOid,
+ const char *pszEncryptedDigestOid)
+{
+ /*
+ * Lookup the digest and encrypted digest OIDs.
+ */
+ PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pDigest = rtCrX509AlgorithmIdentifier_LookupInfoByOid(pszDigestOid);
+ AssertMsgReturn(pDigest, ("pszDigestOid=%s\n", pszDigestOid), -1);
+ AssertMsgReturn(pDigest->idxEncryption == IDX_ENCRYPTION_NIL, ("pszDigestOid=%s\n", pszDigestOid), -1);
+ AssertMsgReturn(pDigest->cBitsDigest != 0, ("pszDigestOid=%s\n", pszDigestOid), -1);
+
+ PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pEncrypt = rtCrX509AlgorithmIdentifier_LookupInfoByOid(pszEncryptedDigestOid);
+ AssertMsgReturn(pEncrypt, ("pszEncryptedDigestOid=%s\n", pszEncryptedDigestOid), 1);
+ AssertMsgReturn(pEncrypt->idxEncryption != IDX_ENCRYPTION_NIL, ("pszEncryptedDigestOid=%s\n", pszEncryptedDigestOid), 1);
+ AssertMsgReturn(pEncrypt->enmDigestType != RTDIGESTTYPE_INVALID, ("pszEncryptedDigestOid=%s\n", pszEncryptedDigestOid), 1);
+
+ return pDigest->enmDigestType == pEncrypt->enmDigestType ? 0 : 1;
+}
+
+RTDECL(int) RTCrX509AlgorithmIdentifier_CompareDigestAndEncryptedDigest(PCRTCRX509ALGORITHMIDENTIFIER pDigest,
+ PCRTCRX509ALGORITHMIDENTIFIER pEncryptedDigest)
+{
+ return RTCrX509AlgorithmIdentifier_CompareDigestOidAndEncryptedDigestOid(pDigest->Algorithm.szObjId,
+ pEncryptedDigest->Algorithm.szObjId);
+}
+
+
+RTDECL(const char *) RTCrX509AlgorithmIdentifier_CombineEncryptionOidAndDigestOid(const char *pszEncryptionOid,
+ const char *pszDigestOid)
+{
+ /*
+ * We can look up the two OIDs and see what they actually are.
+ */
+ /* The digest OID should be a pure hash algorithm, however we also accept
+ the already combined algorithm. */
+ PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pDigest = rtCrX509AlgorithmIdentifier_LookupInfoByOid(pszDigestOid);
+ AssertReturn(pDigest, NULL);
+ AssertReturn(pDigest->enmDigestType != RTDIGESTTYPE_INVALID, NULL);
+
+ /* The encryption OID should be a pure encryption algorithm, however we
+ also accept the already combined algorithm. */
+ PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pEncrypt = rtCrX509AlgorithmIdentifier_LookupInfoByOid(pszEncryptionOid);
+ AssertReturn(pEncrypt, NULL);
+ uint8_t const idxEncryption = pEncrypt->idxEncryption;
+ AssertReturn(idxEncryption != IDX_ENCRYPTION_NIL, NULL);
+ Assert(idxEncryption < RT_ELEMENTS(g_aDigestAndEncryption));
+
+ /* Is the encryption OID purely encryption? */
+ if (pEncrypt->cBitsDigest == 0)
+ {
+ Assert(pEncrypt->enmDigestType == RTDIGESTTYPE_INVALID);
+
+ /* Identify the slice of the table related to this encryption OID: */
+ PCRTCRX509ALGORITHIDENTIFIERCOMBINING const paCombinations = g_aDigestAndEncryption[idxEncryption].paCombinations;
+ size_t const cCombinations = g_aDigestAndEncryption[idxEncryption].cCombinations;
+
+ /* Is the digest OID purely a digest? */
+ if (pDigest->idxEncryption == IDX_ENCRYPTION_NIL)
+ {
+ for (size_t i = 0; i < cCombinations; i++)
+ if (!strcmp(pszDigestOid, paCombinations[i].pszDigestOid))
+ return paCombinations[i].pszEncryptedDigestOid;
+ AssertMsgFailed(("enc=%s hash=%s\n", pszEncryptionOid, pszDigestOid));
+ }
+ else
+ {
+ /* No, it's a combined one. */
+ for (size_t i = 0; i < cCombinations; i++)
+ if (!strcmp(pszDigestOid, paCombinations[i].pszEncryptedDigestOid))
+ return paCombinations[i].pszEncryptedDigestOid;
+ AssertMsgFailed(("enc=%s hash+enc=%s\n", pszEncryptionOid, pszDigestOid));
+ }
+ }
+ /* The digest OID purely a digest? */
+ else if (pDigest->idxEncryption == IDX_ENCRYPTION_NIL)
+ {
+ /* Check that it's for the same hash before returning it. */
+ Assert(pEncrypt->enmDigestType != RTDIGESTTYPE_INVALID);
+ if (pEncrypt->enmDigestType == pDigest->enmDigestType)
+ return pEncrypt->pszSignatureOid;
+ AssertMsgFailed(("enc+hash=%s hash=%s\n", pszEncryptionOid, pszDigestOid));
+ }
+ /* Both the digest and encryption OIDs are combined ones, so they have to
+ be the same entry then or they cannot be combined. */
+ else if (pDigest == pEncrypt)
+ return pEncrypt->pszSignatureOid;
+ else
+ AssertMsgFailed(("enc+hash=%s hash+enc=%s\n", pszEncryptionOid, pszDigestOid));
+
+ return NULL;
+}
+
+
+RTDECL(const char *) RTCrX509AlgorithmIdentifier_CombineEncryptionAndDigest(PCRTCRX509ALGORITHMIDENTIFIER pEncryption,
+ PCRTCRX509ALGORITHMIDENTIFIER pDigest)
+{
+ return RTCrX509AlgorithmIdentifier_CombineEncryptionOidAndDigestOid(pEncryption->Algorithm.szObjId,
+ pDigest->Algorithm.szObjId);
+}
+
+
+RTDECL(const char *) RTCrX509AlgorithmIdentifier_GetEncryptionOid(PCRTCRX509ALGORITHMIDENTIFIER pThis, bool fMustIncludeHash)
+{
+ AssertPtrReturn(pThis, NULL);
+ PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pInfo = rtCrX509AlgorithmIdentifier_LookupInfoByOid(pThis->Algorithm.szObjId);
+ if (pInfo && (!fMustIncludeHash || pInfo->enmDigestType != RTDIGESTTYPE_INVALID))
+ return g_apszEncryptionOids[pInfo->idxEncryption];
+ return NULL;
+}
+
+
+RTDECL(const char *) RTCrX509AlgorithmIdentifier_GetEncryptionOidFromOid(const char *pszAlgorithmOid, bool fMustIncludeHash)
+{
+ AssertPtrReturn(pszAlgorithmOid, NULL);
+ PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pInfo = rtCrX509AlgorithmIdentifier_LookupInfoByOid(pszAlgorithmOid);
+ if (pInfo && (!fMustIncludeHash || pInfo->enmDigestType != RTDIGESTTYPE_INVALID))
+ return g_apszEncryptionOids[pInfo->idxEncryption];
+ return NULL;
+}
+
+
+/*
+ * Set of X.509 Algorithm Identifiers.
+ */
+
+
+/*
+ * One X.509 AttributeTypeAndValue.
+ */
+
+
+/*
+ * Set of X.509 AttributeTypeAndValues / X.509 RelativeDistinguishedName.
+ */
+
+/**
+ * Slow code path of rtCrX509CanNameIsNothing.
+ *
+ * @returns true if @uc maps to nothing, false if not.
+ * @param uc The unicode code point.
+ */
+static bool rtCrX509CanNameIsNothingSlow(RTUNICP uc)
+{
+ switch (uc)
+ {
+ /* 2.2 Map - Paragraph 1: */
+ case 0x00ad:
+ case 0x1806:
+ case 0x034f:
+ case 0x180b: case 0x180c: case 0x180d:
+
+ case 0xfe00: case 0xfe01: case 0xfe02: case 0xfe03:
+ case 0xfe04: case 0xfe05: case 0xfe06: case 0xfe07:
+ case 0xfe08: case 0xfe09: case 0xfe0a: case 0xfe0b:
+ case 0xfe0c: case 0xfe0d: case 0xfe0e: case 0xfe0f:
+
+ case 0xfffc:
+
+ /* 2.2 Map - Paragraph 3 (control code/function): */
+ case 0x0000: case 0x0001: case 0x0002: case 0x0003:
+ case 0x0004: case 0x0005: case 0x0006: case 0x0007:
+ case 0x0008:
+
+ case 0x000e: case 0x000f:
+ case 0x0010: case 0x0011: case 0x0012: case 0x0013:
+ case 0x0014: case 0x0015: case 0x0016: case 0x0017:
+ case 0x0018: case 0x0019: case 0x001a: case 0x001b:
+ case 0x001c: case 0x001d: case 0x001e: case 0x001f:
+
+ case 0x007f:
+ case 0x0080: case 0x0081: case 0x0082: case 0x0083:
+ case 0x0084: /*case 0x0085:*/ case 0x0086: case 0x0087:
+ case 0x0088: case 0x0089: case 0x008a: case 0x008b:
+ case 0x008c: case 0x008d: case 0x008e: case 0x008f:
+ case 0x0090: case 0x0091: case 0x0092: case 0x0093:
+ case 0x0094: case 0x0095: case 0x0096: case 0x0097:
+ case 0x0098: case 0x0099: case 0x009a: case 0x009b:
+ case 0x009c: case 0x009d: case 0x009e: case 0x009f:
+
+ case 0x06dd:
+ case 0x070f:
+ case 0x180e:
+ case 0x200c: case 0x200d: case 0x200e: case 0x200f:
+ case 0x202a: case 0x202b: case 0x202c: case 0x202d: case 0x202e:
+ case 0x2060: case 0x2061: case 0x2062: case 0x2063:
+ case 0x206a: case 0x206b: case 0x206c: case 0x206d: case 0x206e: case 0x206f:
+ case 0xfeff:
+ case 0xfff9: case 0xfffa: case 0xfffb:
+ case 0x1d173: case 0x1d174: case 0x1d175: case 0x1d176: case 0x1d177: case 0x1d178: case 0x1d179: case 0x1d17a:
+ case 0xe0001:
+ case 0xe0020: case 0xe0021: case 0xe0022: case 0xe0023:
+ case 0xe0024: case 0xe0025: case 0xe0026: case 0xe0027:
+ case 0xe0028: case 0xe0029: case 0xe002a: case 0xe002b:
+ case 0xe002c: case 0xe002d: case 0xe002e: case 0xe002f:
+ case 0xe0030: case 0xe0031: case 0xe0032: case 0xe0033:
+ case 0xe0034: case 0xe0035: case 0xe0036: case 0xe0037:
+ case 0xe0038: case 0xe0039: case 0xe003a: case 0xe003b:
+ case 0xe003c: case 0xe003d: case 0xe003e: case 0xe003f:
+ case 0xe0040: case 0xe0041: case 0xe0042: case 0xe0043:
+ case 0xe0044: case 0xe0045: case 0xe0046: case 0xe0047:
+ case 0xe0048: case 0xe0049: case 0xe004a: case 0xe004b:
+ case 0xe004c: case 0xe004d: case 0xe004e: case 0xe004f:
+ case 0xe0050: case 0xe0051: case 0xe0052: case 0xe0053:
+ case 0xe0054: case 0xe0055: case 0xe0056: case 0xe0057:
+ case 0xe0058: case 0xe0059: case 0xe005a: case 0xe005b:
+ case 0xe005c: case 0xe005d: case 0xe005e: case 0xe005f:
+ case 0xe0060: case 0xe0061: case 0xe0062: case 0xe0063:
+ case 0xe0064: case 0xe0065: case 0xe0066: case 0xe0067:
+ case 0xe0068: case 0xe0069: case 0xe006a: case 0xe006b:
+ case 0xe006c: case 0xe006d: case 0xe006e: case 0xe006f:
+ case 0xe0070: case 0xe0071: case 0xe0072: case 0xe0073:
+ case 0xe0074: case 0xe0075: case 0xe0076: case 0xe0077:
+ case 0xe0078: case 0xe0079: case 0xe007a: case 0xe007b:
+ case 0xe007c: case 0xe007d: case 0xe007e: case 0xe007f:
+
+ /* 2.2 Map - Paragraph 4. */
+ case 0x200b:
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Checks if @a uc maps to nothing according to mapping rules of RFC-5280 and
+ * RFC-4518.
+ *
+ * @returns true if @uc maps to nothing, false if not.
+ * @param uc The unicode code point.
+ */
+DECLINLINE(bool) rtCrX509CanNameIsNothing(RTUNICP uc)
+{
+ if (uc > 0x001f && uc < 0x00ad)
+ return false;
+ return rtCrX509CanNameIsNothingSlow(uc);
+}
+
+
+/**
+ * Slow code path of rtCrX509CanNameIsSpace.
+ *
+ * @returns true if space, false if not.
+ * @param uc The unicode code point.
+ */
+static bool rtCrX509CanNameIsSpaceSlow(RTUNICP uc)
+{
+ switch (uc)
+ {
+ /* 2.2 Map - Paragraph 2. */
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ case 0x20:
+ case 0x0085:
+ case 0x00a0:
+ case 0x1680:
+ case 0x2000: case 0x2001: case 0x2002: case 0x2003:
+ case 0x2004: case 0x2005: case 0x2006: case 0x2007:
+ case 0x2008: case 0x2009: case 0x200a:
+ case 0x2028: case 0x2029:
+ case 0x202f:
+ case 0x205f:
+ case 0x3000:
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Checks if @a uc is a space character according to the mapping rules of
+ * RFC-5280 and RFC-4518.
+ *
+ * @returns true if space, false if not.
+ * @param uc The unicode code point.
+ */
+DECLINLINE(bool) rtCrX509CanNameIsSpace(RTUNICP uc)
+{
+ if (uc < 0x0085)
+ {
+ if (uc > 0x0020)
+ return false;
+ if (uc == 0x0020) /* space */
+ return true;
+ }
+ return rtCrX509CanNameIsSpaceSlow(uc);
+}
+
+
+static const char *rtCrX509CanNameStripLeft(const char *psz, size_t *pcch)
+{
+ /*
+ * Return space when we've encountered the first non-space-non-nothing code point.
+ */
+ const char * const pszStart = psz;
+ const char *pszPrev;
+ for (;;)
+ {
+ pszPrev = psz;
+ RTUNICP uc;
+ int rc = RTStrGetCpEx(&psz, &uc);
+ AssertRCBreak(rc);
+ if (!uc)
+ {
+ if ((uintptr_t)(pszPrev - pszStart) >= *pcch)
+ break;
+ /* NUL inside the string, maps to nothing => ignore it. */
+ }
+ else if (!rtCrX509CanNameIsSpace(uc) && !rtCrX509CanNameIsNothing(uc))
+ break;
+ }
+ *pcch -= (size_t)(pszPrev - pszStart);
+ return pszPrev;
+}
+
+
+static RTUNICP rtCrX509CanNameGetNextCpWithMappingSlowSpace(const char **ppsz, size_t *pcch)
+{
+ /*
+ * Return space when we've encountered the first non-space-non-nothing code point.
+ */
+ RTUNICP uc;
+ const char *psz = *ppsz;
+ const char * const pszStart = psz;
+ const char *pszPrev;
+ for (;;)
+ {
+ pszPrev = psz;
+ int rc = RTStrGetCpEx(&psz, &uc);
+ AssertRCBreakStmt(rc, uc = 0x20);
+ if (!uc)
+ {
+ if ((uintptr_t)(pszPrev - pszStart) >= *pcch)
+ {
+ uc = 0; /* End of string: Ignore trailing spaces. */
+ break;
+ }
+ /* NUL inside the string, maps to nothing => ignore it. */
+ }
+ else if (!rtCrX509CanNameIsSpace(uc) && !rtCrX509CanNameIsNothing(uc))
+ {
+ uc = 0x20; /* Return space before current char. */
+ break;
+ }
+ }
+
+ *ppsz = pszPrev;
+ *pcch -= (size_t)(pszPrev - pszStart);
+ return uc;
+}
+
+
+DECLINLINE(RTUNICP) rtCrX509CanNameGetNextCpIgnoreNul(const char **ppsz, size_t *pcch)
+{
+ while (*pcch > 0)
+ {
+ const char *psz = *ppsz;
+ RTUNICP uc = (RTUNICP)*psz;
+ if (uc < 0x80)
+ {
+ *pcch -= 1;
+ *ppsz = psz + 1;
+ }
+ else
+ {
+ int rc = RTStrGetCpEx(ppsz, &uc);
+ AssertRCReturn(rc, uc);
+ size_t cchCp = (size_t)(*ppsz - psz);
+ AssertReturn(cchCp <= *pcch, 0);
+ *pcch -= cchCp;
+ }
+ if (uc != 0)
+ return uc;
+ }
+ return 0;
+}
+
+
+static RTUNICP rtCrX509CanNameGetNextCpWithMappingSlowNothing(const char **ppsz, size_t *pcch)
+{
+ /*
+ * Return first code point which doesn't map to nothing. If we encounter
+ * a space, we defer to the mapping-after-space routine above.
+ */
+ for (;;)
+ {
+ RTUNICP uc = rtCrX509CanNameGetNextCpIgnoreNul(ppsz, pcch);
+ if (rtCrX509CanNameIsSpace(uc))
+ return rtCrX509CanNameGetNextCpWithMappingSlowSpace(ppsz, pcch);
+ if (!rtCrX509CanNameIsNothing(uc) || uc == 0)
+ return uc;
+ }
+}
+
+
+DECLINLINE(RTUNICP) rtCrX509CanNameGetNextCpWithMapping(const char **ppsz, size_t *pcch)
+{
+ RTUNICP uc = rtCrX509CanNameGetNextCpIgnoreNul(ppsz, pcch);
+ if (uc)
+ {
+ if (!rtCrX509CanNameIsSpace(uc))
+ {
+ if (!rtCrX509CanNameIsNothing(uc))
+ return uc;
+ return rtCrX509CanNameGetNextCpWithMappingSlowNothing(ppsz, pcch);
+ }
+ return rtCrX509CanNameGetNextCpWithMappingSlowSpace(ppsz, pcch);
+ }
+ return uc;
+}
+
+
+RTDECL(bool) RTCrX509AttributeTypeAndValue_MatchAsRdnByRfc5280(PCRTCRX509ATTRIBUTETYPEANDVALUE pLeft,
+ PCRTCRX509ATTRIBUTETYPEANDVALUE pRight)
+{
+ if (RTAsn1ObjId_Compare(&pLeft->Type, &pRight->Type) == 0)
+ {
+ /*
+ * Try for perfect match in case we get luck.
+ */
+#ifdef DEBUG_bird /* Want to test the complicated code path first */
+ if (pLeft->Value.enmType != RTASN1TYPE_STRING || pRight->Value.enmType != RTASN1TYPE_STRING)
+#endif
+ if (RTAsn1DynType_Compare(&pLeft->Value, &pRight->Value) == 0)
+ return true;
+
+ /*
+ * If both are string types, we can compare them according to RFC-5280.
+ */
+ if ( pLeft->Value.enmType == RTASN1TYPE_STRING
+ && pRight->Value.enmType == RTASN1TYPE_STRING)
+ {
+ size_t cchLeft;
+ const char *pszLeft;
+ int rc = RTAsn1String_QueryUtf8(&pLeft->Value.u.String, &pszLeft, &cchLeft);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cchRight;
+ const char *pszRight;
+ rc = RTAsn1String_QueryUtf8(&pRight->Value.u.String, &pszRight, &cchRight);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Perform a simplified RFC-5280 comparsion.
+ * The algorithm as be relaxed on the following counts:
+ * 1. No unicode normalization.
+ * 2. Prohibited characters not checked for.
+ * 3. Bidirectional characters are not ignored.
+ */
+ pszLeft = rtCrX509CanNameStripLeft(pszLeft, &cchLeft);
+ pszRight = rtCrX509CanNameStripLeft(pszRight, &cchRight);
+ while (*pszLeft && *pszRight)
+ {
+ RTUNICP ucLeft = rtCrX509CanNameGetNextCpWithMapping(&pszLeft, &cchLeft);
+ RTUNICP ucRight = rtCrX509CanNameGetNextCpWithMapping(&pszRight, &cchRight);
+ if (ucLeft != ucRight)
+ {
+ ucLeft = RTUniCpToLower(ucLeft);
+ ucRight = RTUniCpToLower(ucRight);
+ if (ucLeft != ucRight)
+ return false;
+ }
+ }
+
+ return cchRight == 0 && cchLeft == 0;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
+RTDECL(bool) RTCrX509RelativeDistinguishedName_MatchByRfc5280(PCRTCRX509RELATIVEDISTINGUISHEDNAME pLeft,
+ PCRTCRX509RELATIVEDISTINGUISHEDNAME pRight)
+{
+ /*
+ * No match if the attribute count differs.
+ */
+ uint32_t const cItems = pLeft->cItems;
+ if (cItems == pRight->cItems)
+ {
+ /*
+ * Compare each attribute, but don't insist on the same order nor
+ * bother checking for duplicates (too complicated).
+ */
+ for (uint32_t iLeft = 0; iLeft < cItems; iLeft++)
+ {
+ PCRTCRX509ATTRIBUTETYPEANDVALUE pLeftAttr = pLeft->papItems[iLeft];
+ bool fFound = false;
+ for (uint32_t iRight = 0; iRight < cItems; iRight++)
+ if (RTCrX509AttributeTypeAndValue_MatchAsRdnByRfc5280(pLeftAttr, pRight->papItems[iRight]))
+ {
+ fFound = true;
+ break;
+ }
+ if (!fFound)
+ return false;
+ }
+ return true;
+ }
+ return false;
+
+}
+
+
+/*
+ * X.509 Name.
+ */
+
+RTDECL(bool) RTCrX509Name_MatchByRfc5280(PCRTCRX509NAME pLeft, PCRTCRX509NAME pRight)
+{
+ uint32_t const cItems = pLeft->cItems;
+ if (cItems == pRight->cItems)
+ {
+ /* Require exact order. */
+ for (uint32_t iRdn = 0; iRdn < cItems; iRdn++)
+ if (!RTCrX509RelativeDistinguishedName_MatchByRfc5280(pLeft->papItems[iRdn], pRight->papItems[iRdn]))
+ return false;
+ return true;
+ }
+ return false;
+}
+
+
+RTDECL(bool) RTCrX509Name_ConstraintMatch(PCRTCRX509NAME pConstraint, PCRTCRX509NAME pName)
+{
+ /*
+ * Check that the constraint is a prefix of the name. This means that
+ * the name must have at least as many components and the constraint.
+ */
+ if (pName->cItems >= pConstraint->cItems)
+ {
+ /*
+ * Parallel crawl of the two RDNs arrays.
+ */
+ for (uint32_t i = 0; pConstraint->cItems; i++)
+ {
+ PCRTCRX509RELATIVEDISTINGUISHEDNAME pConstrRdns = pConstraint->papItems[i];
+ PCRTCRX509RELATIVEDISTINGUISHEDNAME pNameRdns = pName->papItems[i];
+
+ /*
+ * Walk the constraint attribute & value array.
+ */
+ for (uint32_t iConstrAttrib = 0; iConstrAttrib < pConstrRdns->cItems; iConstrAttrib++)
+ {
+ PCRTCRX509ATTRIBUTETYPEANDVALUE pConstrAttrib = pConstrRdns->papItems[iConstrAttrib];
+
+ /*
+ * Find matching attribute & value in the name.
+ */
+ bool fFound = false;
+ for (uint32_t iNameAttrib = 0; iNameAttrib < pNameRdns->cItems; iNameAttrib++)
+ if (RTCrX509AttributeTypeAndValue_MatchAsRdnByRfc5280(pConstrAttrib, pNameRdns->papItems[iNameAttrib]))
+ {
+ fFound = true;
+ break;
+ }
+ if (fFound)
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Mapping between X.500 object IDs and short and long names.
+ *
+ * See RFC-1327, RFC-4519 ...
+ */
+static struct
+{
+ const char *pszOid;
+ const char *pszShortNm;
+ size_t cchShortNm;
+ const char *pszLongNm;
+} const g_aRdnMap[] =
+{
+ { "0.9.2342.19200300.100.1.1", RT_STR_TUPLE("uid"), "userid" },
+ { "0.9.2342.19200300.100.1.3", RT_STR_TUPLE("Mail"), "Rfc822Mailbox" },
+ { "0.9.2342.19200300.100.1.25", RT_STR_TUPLE("DC"), "DomainComponent" },
+ { "1.2.840.113549.1.9.1", RT_STR_TUPLE("Email") /*nonstandard*/,"EmailAddress" },
+ { "1.3.6.1.4.1.311.60.2.1.1", RT_STR_TUPLE("JdxIncL") /*nonstd*/, "JdxOfIncLocalityName" },
+ { "1.3.6.1.4.1.311.60.2.1.2", RT_STR_TUPLE("JdxIncST") /*nonstd*/, "JdxOfIncStateOrProvinceName" },
+ { "1.3.6.1.4.1.311.60.2.1.3", RT_STR_TUPLE("JdxIncC") /*nonstd*/, "JdxOfIncCountryName" },
+ { "2.5.4.3", RT_STR_TUPLE("CN"), "CommonName" },
+ { "2.5.4.4", RT_STR_TUPLE("SN"), "Surname" },
+ { "2.5.4.5", RT_STR_TUPLE("SRN") /*nonstandard*/, "SerialNumber" },
+ { "2.5.4.6", RT_STR_TUPLE("C"), "CountryName" },
+ { "2.5.4.7", RT_STR_TUPLE("L"), "LocalityName" },
+ { "2.5.4.8", RT_STR_TUPLE("ST"), "StateOrProviceName" },
+ { "2.5.4.9", RT_STR_TUPLE("street"), "Street" },
+ { "2.5.4.10", RT_STR_TUPLE("O"), "OrganizationName" },
+ { "2.5.4.11", RT_STR_TUPLE("OU"), "OrganizationalUnitName" },
+ { "2.5.4.12", RT_STR_TUPLE("title"), "Title" },
+ { "2.5.4.13", RT_STR_TUPLE("desc"), "Description" },
+ { "2.5.4.15", RT_STR_TUPLE("BC") /*nonstandard*/, "BusinessCategory" },
+ { "2.5.4.17", RT_STR_TUPLE("ZIP") /*nonstandard*/, "PostalCode" },
+ { "2.5.4.18", RT_STR_TUPLE("POBox") /*nonstandard*/,"PostOfficeBox" },
+ { "2.5.4.20", RT_STR_TUPLE("PN") /*nonstandard*/, "TelephoneNumber" },
+ { "2.5.4.33", RT_STR_TUPLE("RO") /*nonstandard*/, "RoleOccupant" },
+ { "2.5.4.34", RT_STR_TUPLE("SA") /*nonstandard*/, "StreetAddress" },
+ { "2.5.4.41", RT_STR_TUPLE("N") /*nonstandard*/, "Name" },
+ { "2.5.4.42", RT_STR_TUPLE("GN"), "GivenName" },
+ { "2.5.4.43", RT_STR_TUPLE("I") /*nonstandard*/, "Initials" },
+ { "2.5.4.44", RT_STR_TUPLE("GQ") /*nonstandard*/, "GenerationQualifier" },
+ { "2.5.4.46", RT_STR_TUPLE("DNQ") /*nonstandard*/, "DNQualifier" },
+ { "2.5.4.51", RT_STR_TUPLE("HID") /*nonstandard*/, "HouseIdentifier" },
+};
+
+
+RTDECL(const char *) RTCrX509Name_GetShortRdn(PCRTASN1OBJID pRdnId)
+{
+ uint32_t iName = RT_ELEMENTS(g_aRdnMap);
+ while (iName-- > 0)
+ if (RTAsn1ObjId_CompareWithString(pRdnId, g_aRdnMap[iName].pszOid) == 0)
+ return g_aRdnMap[iName].pszShortNm;
+ return NULL;
+}
+
+
+RTDECL(bool) RTCrX509Name_MatchWithString(PCRTCRX509NAME pThis, const char *pszString)
+{
+ /* Keep track of the string length. */
+ size_t cchString = strlen(pszString);
+
+ /*
+ * The usual double loop for walking the components.
+ */
+ for (uint32_t i = 0; i < pThis->cItems; i++)
+ {
+ PCRTCRX509RELATIVEDISTINGUISHEDNAME pRdn = pThis->papItems[i];
+ for (uint32_t j = 0; j < pRdn->cItems; j++)
+ {
+ PCRTCRX509ATTRIBUTETYPEANDVALUE pComponent = pRdn->papItems[j];
+
+ /*
+ * Must be a string.
+ */
+ if (pComponent->Value.enmType != RTASN1TYPE_STRING)
+ return false;
+
+ /*
+ * Look up the component name prefix and check whether it's also in the string.
+ */
+ uint32_t iName = RT_ELEMENTS(g_aRdnMap);
+ while (iName-- > 0)
+ if (RTAsn1ObjId_CompareWithString(&pComponent->Type, g_aRdnMap[iName].pszOid) == 0)
+ break;
+ AssertMsgReturn(iName != UINT32_MAX, ("Please extend g_aRdnMap with '%s'.\n", pComponent->Type.szObjId), false);
+
+ if ( strncmp(pszString, g_aRdnMap[iName].pszShortNm, g_aRdnMap[iName].cchShortNm) != 0
+ || pszString[g_aRdnMap[iName].cchShortNm] != '=')
+ return false;
+
+ pszString += g_aRdnMap[iName].cchShortNm + 1;
+ cchString -= g_aRdnMap[iName].cchShortNm + 1;
+
+ /*
+ * Compare the component string.
+ */
+ size_t cchComponent;
+ int rc = RTAsn1String_QueryUtf8Len(&pComponent->Value.u.String, &cchComponent);
+ AssertRCReturn(rc, false);
+
+ if (cchComponent > cchString)
+ return false;
+ if (RTAsn1String_CompareWithString(&pComponent->Value.u.String, pszString, cchComponent) != 0)
+ return false;
+
+ cchString -= cchComponent;
+ pszString += cchComponent;
+
+ /*
+ * Check separator comma + space and skip extra spaces before the next component.
+ */
+ if (cchString)
+ {
+ if (pszString[0] != ',')
+ return false;
+ if (pszString[1] != ' ' && pszString[1] != '\t')
+ return false;
+ pszString += 2;
+ cchString -= 2;
+
+ while (*pszString == ' ' || *pszString == '\t')
+ {
+ pszString++;
+ cchString--;
+ }
+ }
+ }
+ }
+
+ /*
+ * If we got thru the whole name and the whole string, we're good.
+ */
+ return *pszString == '\0';
+}
+
+
+RTDECL(int) RTCrX509Name_FormatAsString(PCRTCRX509NAME pThis, char *pszBuf, size_t cbBuf, size_t *pcbActual)
+{
+ /*
+ * The usual double loop for walking the components.
+ */
+ size_t off = 0;
+ int rc = VINF_SUCCESS;
+ for (uint32_t i = 0; i < pThis->cItems; i++)
+ {
+ PCRTCRX509RELATIVEDISTINGUISHEDNAME pRdn = pThis->papItems[i];
+ for (uint32_t j = 0; j < pRdn->cItems; j++)
+ {
+ PCRTCRX509ATTRIBUTETYPEANDVALUE pComponent = pRdn->papItems[j];
+
+ /*
+ * Must be a string.
+ */
+ if (pComponent->Value.enmType != RTASN1TYPE_STRING)
+ return VERR_CR_X509_NAME_NOT_STRING;
+
+ /*
+ * Look up the component name prefix.
+ */
+ uint32_t iName = RT_ELEMENTS(g_aRdnMap);
+ while (iName-- > 0)
+ if (RTAsn1ObjId_CompareWithString(&pComponent->Type, g_aRdnMap[iName].pszOid) == 0)
+ break;
+ AssertMsgReturn(iName != UINT32_MAX, ("Please extend g_aRdnMap with '%s'.\n", pComponent->Type.szObjId),
+ VERR_CR_X509_NAME_MISSING_RDN_MAP_ENTRY);
+
+ /*
+ * Append the prefix.
+ */
+ if (off)
+ {
+ if (off + 2 < cbBuf)
+ {
+ pszBuf[off] = ',';
+ pszBuf[off + 1] = ' ';
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ off += 2;
+ }
+
+ if (off + g_aRdnMap[iName].cchShortNm + 1 < cbBuf)
+ {
+ memcpy(&pszBuf[off], g_aRdnMap[iName].pszShortNm, g_aRdnMap[iName].cchShortNm);
+ pszBuf[off + g_aRdnMap[iName].cchShortNm] = '=';
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ off += g_aRdnMap[iName].cchShortNm + 1;
+
+ /*
+ * Add the component string.
+ */
+ const char *pszUtf8;
+ size_t cchUtf8;
+ int rc2 = RTAsn1String_QueryUtf8(&pComponent->Value.u.String, &pszUtf8, &cchUtf8);
+ AssertRCReturn(rc2, rc2);
+ if (off + cchUtf8 < cbBuf)
+ memcpy(&pszBuf[off], pszUtf8, cchUtf8);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ off += cchUtf8;
+ }
+ }
+
+ if (pcbActual)
+ *pcbActual = off + 1;
+ if (off < cbBuf)
+ pszBuf[off] = '\0';
+ return rc;
+}
+
+
+
+/*
+ * One X.509 GeneralName.
+ */
+
+/**
+ * Name constraint matching (RFC-5280): DNS Name.
+ *
+ * @returns true on match, false on mismatch.
+ * @param pConstraint The constraint name.
+ * @param pName The name to match against the constraint.
+ */
+static bool rtCrX509GeneralName_ConstraintMatchDnsName(PCRTCRX509GENERALNAME pConstraint, PCRTCRX509GENERALNAME pName)
+{
+ /*
+ * Empty constraint string is taken to match everything.
+ */
+ if (pConstraint->u.pT2_DnsName->Asn1Core.cb == 0)
+ return true;
+
+ /*
+ * Get the UTF-8 strings for the two.
+ */
+ size_t cchConstraint;
+ char const *pszConstraint;
+ int rc = RTAsn1String_QueryUtf8(pConstraint->u.pT2_DnsName, &pszConstraint, &cchConstraint);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cchFull;
+ char const *pszFull;
+ rc = RTAsn1String_QueryUtf8(pName->u.pT2_DnsName, &pszFull, &cchFull);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * No match if the constraint is longer.
+ */
+ if (cchConstraint > cchFull)
+ return false;
+
+ /*
+ * No match if the constraint and name tail doesn't match
+ * in a case-insensitive compare.
+ */
+ size_t offFull = cchFull - cchConstraint;
+ if (RTStrICmp(&pszFull[offFull], pszConstraint) != 0)
+ return false;
+ if (!offFull)
+ return true;
+
+ /*
+ * The matching constraint must be delimited by a dot in the full
+ * name. There seems to be some discussion whether ".oracle.com"
+ * should match "www..oracle.com". This implementation does choose
+ * to not succeed in that case.
+ */
+ if ((pszFull[offFull - 1] == '.') ^ (pszFull[offFull] == '.'))
+ return true;
+
+ return false;
+ }
+ }
+
+ /* fall back. */
+ return RTCrX509GeneralName_Compare(pConstraint, pName) == 0;
+}
+
+
+/**
+ * Name constraint matching (RFC-5280): RFC-822 (email).
+ *
+ * @returns true on match, false on mismatch.
+ * @param pConstraint The constraint name.
+ * @param pName The name to match against the constraint.
+ */
+static bool rtCrX509GeneralName_ConstraintMatchRfc822Name(PCRTCRX509GENERALNAME pConstraint, PCRTCRX509GENERALNAME pName)
+{
+ /*
+ * Empty constraint string is taken to match everything.
+ */
+ if (pConstraint->u.pT1_Rfc822->Asn1Core.cb == 0)
+ return true;
+
+ /*
+ * Get the UTF-8 strings for the two.
+ */
+ size_t cchConstraint;
+ char const *pszConstraint;
+ int rc = RTAsn1String_QueryUtf8(pConstraint->u.pT1_Rfc822, &pszConstraint, &cchConstraint);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cchFull;
+ char const *pszFull;
+ rc = RTAsn1String_QueryUtf8(pName->u.pT1_Rfc822, &pszFull, &cchFull);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * No match if the constraint is longer.
+ */
+ if (cchConstraint > cchFull)
+ return false;
+
+ /*
+ * A lone dot matches everything.
+ */
+ if (cchConstraint == 1 && *pszConstraint == '.')
+ return true;
+
+ /*
+ * If there is a '@' in the constraint, the entire address must match.
+ */
+ const char *pszConstraintAt = (const char *)memchr(pszConstraint, '@', cchConstraint);
+ if (pszConstraintAt)
+ return cchConstraint == cchFull && RTStrICmp(pszConstraint, pszFull) == 0;
+
+ /*
+ * No match if the constraint and name tail doesn't match
+ * in a case-insensitive compare.
+ */
+ size_t offFull = cchFull - cchConstraint;
+ if (RTStrICmp(&pszFull[offFull], pszConstraint) != 0)
+ return false;
+
+ /*
+ * If the constraint starts with a dot, we're supposed to be
+ * satisfied with a tail match.
+ */
+ /** @todo Check if this should match even if offFull == 0. */
+ if (*pszConstraint == '.')
+ return true;
+
+ /*
+ * Otherwise, we require a hostname match and thus expect an '@'
+ * immediatly preceding the constraint match.
+ */
+ if (pszFull[offFull - 1] == '@')
+ return true;
+
+ return false;
+ }
+ }
+
+ /* fall back. */
+ return RTCrX509GeneralName_Compare(pConstraint, pName) == 0;
+}
+
+
+/**
+ * Extracts the hostname from an URI.
+ *
+ * @returns true if successfully extract, false if no hostname present.
+ * @param pszUri The URI.
+ * @param pchHostName .
+ * @param pcchHostName .
+ */
+static bool rtCrX509GeneralName_ExtractHostName(const char *pszUri, const char **pchHostName, size_t *pcchHostName)
+{
+ /*
+ * Skip the schema name.
+ */
+ const char *pszStart = strchr(pszUri, ':');
+ while (pszStart && (pszStart[1] != '/' || pszStart[2] != '/'))
+ pszStart = strchr(pszStart + 1, ':');
+ if (pszStart)
+ {
+ pszStart += 3;
+
+ /*
+ * The name ends with the first slash or ":port".
+ */
+ const char *pszEnd = strchr(pszStart, '/');
+ if (!pszEnd)
+ pszEnd = strchr(pszStart, '\0');
+ if (memchr(pszStart, ':', (size_t)(pszEnd - pszStart)))
+ do
+ pszEnd--;
+ while (*pszEnd != ':');
+ if (pszEnd != pszStart)
+ {
+ /*
+ * Drop access credentials at the front of the string if present.
+ */
+ const char *pszAt = (const char *)memchr(pszStart, '@', (size_t)(pszEnd - pszStart));
+ if (pszAt)
+ pszStart = pszAt + 1;
+
+ /*
+ * If there is still some string left, that's the host name.
+ */
+ if (pszEnd != pszStart)
+ {
+ *pcchHostName = (size_t)(pszEnd - pszStart);
+ *pchHostName = pszStart;
+ return true;
+ }
+ }
+ }
+
+ *pcchHostName = 0;
+ *pchHostName = NULL;
+ return false;
+}
+
+
+/**
+ * Name constraint matching (RFC-5280): URI.
+ *
+ * @returns true on match, false on mismatch.
+ * @param pConstraint The constraint name.
+ * @param pName The name to match against the constraint.
+ */
+static bool rtCrX509GeneralName_ConstraintMatchUri(PCRTCRX509GENERALNAME pConstraint, PCRTCRX509GENERALNAME pName)
+{
+ /*
+ * Empty constraint string is taken to match everything.
+ */
+ if (pConstraint->u.pT6_Uri->Asn1Core.cb == 0)
+ return true;
+
+ /*
+ * Get the UTF-8 strings for the two.
+ */
+ size_t cchConstraint;
+ char const *pszConstraint;
+ int rc = RTAsn1String_QueryUtf8(pConstraint->u.pT6_Uri, &pszConstraint, &cchConstraint);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cchFull;
+ char const *pszFull;
+ rc = RTAsn1String_QueryUtf8(pName->u.pT6_Uri, &pszFull, &cchFull);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Isolate the hostname in the name.
+ */
+ size_t cchHostName;
+ const char *pchHostName;
+ if (rtCrX509GeneralName_ExtractHostName(pszFull, &pchHostName, &cchHostName))
+ {
+ /*
+ * Domain constraint.
+ */
+ if (*pszConstraint == '.')
+ {
+ if (cchHostName >= cchConstraint)
+ {
+ size_t offHostName = cchHostName - cchConstraint;
+ if (RTStrICmp(&pchHostName[offHostName], pszConstraint) == 0)
+ {
+ /* "http://www..oracle.com" does not match ".oracle.com".
+ It's debatable whether "http://.oracle.com/" should match. */
+ if ( !offHostName
+ || pchHostName[offHostName - 1] != '.')
+ return true;
+ }
+ }
+ }
+ /*
+ * Host name constraint. Full match required.
+ */
+ else if ( cchHostName == cchConstraint
+ && RTStrNICmp(pchHostName, pszConstraint, cchHostName) == 0)
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /* fall back. */
+ return RTCrX509GeneralName_Compare(pConstraint, pName) == 0;
+}
+
+
+/**
+ * Name constraint matching (RFC-5280): IP address.
+ *
+ * @returns true on match, false on mismatch.
+ * @param pConstraint The constraint name.
+ * @param pName The name to match against the constraint.
+ */
+static bool rtCrX509GeneralName_ConstraintMatchIpAddress(PCRTCRX509GENERALNAME pConstraint, PCRTCRX509GENERALNAME pName)
+{
+ uint8_t const *pbConstraint = pConstraint->u.pT7_IpAddress->Asn1Core.uData.pu8;
+ uint8_t const *pbFull = pName->u.pT7_IpAddress->Asn1Core.uData.pu8;
+
+ /*
+ * IPv4.
+ */
+ if ( pConstraint->u.pT7_IpAddress->Asn1Core.cb == 8 /* ip+netmask*/
+ && pName->u.pT7_IpAddress->Asn1Core.cb == 4) /* ip */
+ return ((pbFull[0] ^ pbConstraint[0]) & pbConstraint[4]) == 0
+ && ((pbFull[1] ^ pbConstraint[1]) & pbConstraint[5]) == 0
+ && ((pbFull[2] ^ pbConstraint[2]) & pbConstraint[6]) == 0
+ && ((pbFull[3] ^ pbConstraint[3]) & pbConstraint[7]) == 0;
+
+ /*
+ * IPv6.
+ */
+ if ( pConstraint->u.pT7_IpAddress->Asn1Core.cb == 32 /* ip+netmask*/
+ && pName->u.pT7_IpAddress->Asn1Core.cb == 16) /* ip */
+ {
+ for (uint32_t i = 0; i < 16; i++)
+ if (((pbFull[i] ^ pbConstraint[i]) & pbConstraint[i + 16]) != 0)
+ return false;
+ return true;
+ }
+
+ return RTCrX509GeneralName_Compare(pConstraint, pName) == 0;
+}
+
+
+RTDECL(bool) RTCrX509GeneralName_ConstraintMatch(PCRTCRX509GENERALNAME pConstraint, PCRTCRX509GENERALNAME pName)
+{
+ if (pConstraint->enmChoice == pName->enmChoice)
+ {
+ if (RTCRX509GENERALNAME_IS_DIRECTORY_NAME(pConstraint))
+ return RTCrX509Name_ConstraintMatch(&pConstraint->u.pT4->DirectoryName, &pName->u.pT4->DirectoryName);
+
+ if (RTCRX509GENERALNAME_IS_DNS_NAME(pConstraint))
+ return rtCrX509GeneralName_ConstraintMatchDnsName(pConstraint, pName);
+
+ if (RTCRX509GENERALNAME_IS_RFC822_NAME(pConstraint))
+ return rtCrX509GeneralName_ConstraintMatchRfc822Name(pConstraint, pName);
+
+ if (RTCRX509GENERALNAME_IS_URI(pConstraint))
+ return rtCrX509GeneralName_ConstraintMatchUri(pConstraint, pName);
+
+ if (RTCRX509GENERALNAME_IS_IP_ADDRESS(pConstraint))
+ return rtCrX509GeneralName_ConstraintMatchIpAddress(pConstraint, pName);
+
+ AssertFailed();
+ return RTCrX509GeneralName_Compare(pConstraint, pName) == 0;
+ }
+ return false;
+}
+
+
+/*
+ * Sequence of X.509 GeneralNames.
+ */
+
+
+/*
+ * X.509 UniqueIdentifier.
+ */
+
+
+/*
+ * X.509 SubjectPublicKeyInfo.
+ */
+
+
+/*
+ * X.509 AuthorityKeyIdentifier (IPRT representation).
+ */
+
+
+/*
+ * One X.509 PolicyQualifierInfo.
+ */
+
+
+/*
+ * Sequence of X.509 PolicyQualifierInfo.
+ */
+
+
+/*
+ * One X.509 PolicyInformation.
+ */
+
+
+/*
+ * Sequence of X.509 CertificatePolicies.
+ */
+
+
+/*
+ * One X.509 PolicyMapping (IPRT representation).
+ */
+
+
+/*
+ * Sequence of X.509 PolicyMappings (IPRT representation).
+ */
+
+
+/*
+ * X.509 BasicConstraints (IPRT representation).
+ */
+
+
+/*
+ * X.509 GeneralSubtree (IPRT representation).
+ */
+
+
+RTDECL(bool) RTCrX509GeneralSubtree_ConstraintMatch(PCRTCRX509GENERALSUBTREE pConstraint, PCRTCRX509GENERALSUBTREE pName)
+{
+ return RTCrX509GeneralName_ConstraintMatch(&pConstraint->Base, &pName->Base);
+}
+
+
+/*
+ * Sequence of X.509 GeneralSubtrees (IPRT representation).
+ */
+
+
+/*
+ * X.509 NameConstraints (IPRT representation).
+ */
+
+
+/*
+ * X.509 PolicyConstraints (IPRT representation).
+ */
+
+
+/*
+ * One X.509 Extension.
+ */
+
+
+/*
+ * Sequence of X.509 Extensions.
+ */
+
+
+/*
+ * X.509 TbsCertificate.
+ */
+
+static void rtCrx509TbsCertificate_AddKeyUsageFlags(PRTCRX509TBSCERTIFICATE pThis, PCRTCRX509EXTENSION pExtension)
+{
+ AssertReturnVoid(pExtension->enmValue == RTCRX509EXTENSIONVALUE_BIT_STRING);
+ /* 3 = 1 byte for unused bit count, followed by one or two bytes containing actual bits. RFC-5280 defines bits 0 thru 8. */
+ AssertReturnVoid(pExtension->ExtnValue.pEncapsulated->cb <= 3);
+ pThis->T3.fKeyUsage |= (uint32_t)RTAsn1BitString_GetAsUInt64((PCRTASN1BITSTRING)pExtension->ExtnValue.pEncapsulated);
+}
+
+
+static void rtCrx509TbsCertificate_AddExtKeyUsageFlags(PRTCRX509TBSCERTIFICATE pThis, PCRTCRX509EXTENSION pExtension)
+{
+ AssertReturnVoid(pExtension->enmValue == RTCRX509EXTENSIONVALUE_SEQ_OF_OBJ_IDS);
+ PCRTASN1SEQOFOBJIDS pObjIds = (PCRTASN1SEQOFOBJIDS)pExtension->ExtnValue.pEncapsulated;
+ uint32_t i = pObjIds->cItems;
+ while (i-- > 0)
+ {
+
+ if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_ANY_EXTENDED_KEY_USAGE_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_ANY;
+ else if (RTAsn1ObjId_StartsWith(pObjIds->papItems[i], RTCRX509_ID_KP_OID))
+ {
+ if (RTAsn1ObjIdCountComponents(pObjIds->papItems[i]) == 9)
+ switch (RTAsn1ObjIdGetLastComponentsAsUInt32(pObjIds->papItems[i]))
+ {
+ case 1: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_SERVER_AUTH; break;
+ case 2: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_CLIENT_AUTH; break;
+ case 3: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_CODE_SIGNING; break;
+ case 4: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_EMAIL_PROTECTION; break;
+ case 5: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_IPSEC_END_SYSTEM; break;
+ case 6: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_IPSEC_TUNNEL; break;
+ case 7: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_IPSEC_USER; break;
+ case 8: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_TIMESTAMPING; break;
+ case 9: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OCSP_SIGNING; break;
+ case 10: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_DVCS; break;
+ case 11: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_SBGP_CERT_AA_SERVICE_AUTH; break;
+ case 13: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_EAP_OVER_PPP; break;
+ case 14: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_EAP_OVER_LAN; break;
+ default: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OTHER; break;
+ }
+ else
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OTHER;
+ }
+ else if (RTAsn1ObjId_StartsWith(pObjIds->papItems[i], RTCRX509_APPLE_EKU_APPLE_EXTENDED_KEY_USAGE_OID))
+ {
+ if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_APPLE_EKU_CODE_SIGNING_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_CODE_SIGNING;
+ else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_APPLE_EKU_CODE_SIGNING_DEVELOPMENT_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_CODE_SIGNING_DEVELOPMENT;
+ else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_APPLE_EKU_SOFTWARE_UPDATE_SIGNING_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_SOFTWARE_UPDATE_SIGNING;
+ else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_APPLE_EKU_CODE_SIGNING_THRID_PARTY_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_CODE_SIGNING_THIRD_PARTY;
+ else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_APPLE_EKU_RESOURCE_SIGNING_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_RESOURCE_SIGNING;
+ else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_APPLE_EKU_SYSTEM_IDENTITY_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_SYSTEM_IDENTITY;
+ else
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OTHER;
+ }
+ else if (RTAsn1ObjId_StartsWith(pObjIds->papItems[i], "1.3.6.1.4.1.311"))
+ {
+ if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_TIMESTAMP_SIGNING_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_TIMESTAMP_SIGNING;
+ else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_WHQL_CRYPTO_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_WHQL_CRYPTO;
+ else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_ATTEST_WHQL_CRYPTO_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_ATTEST_WHQL_CRYPTO;
+ else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_NT5_CRYPTO_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_NT5_CRYPTO;
+ else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_OEM_WHQL_CRYPTO_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_OEM_WHQL_CRYPTO;
+ else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_EMBEDDED_NT_CRYPTO_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_EMBEDDED_NT_CRYPTO;
+ else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_KERNEL_MODE_CODE_SIGNING_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_KERNEL_MODE_CODE_SIGNING;
+ else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_LIFETIME_SIGNING_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_LIFETIME_SIGNING;
+ else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_DRM_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_DRM;
+ else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_DRM_INDIVIDUALIZATION_OID) == 0)
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_DRM_INDIVIDUALIZATION;
+ else
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OTHER;
+ }
+ else
+ pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OTHER;
+ }
+}
+
+
+/**
+ * (Re-)Process the certificate extensions.
+ *
+ * Will fail if duplicate extensions are encountered.
+ *
+ * @returns IPRT status code.
+ * @param pThis The to-be-signed certificate part.
+ * @param pErrInfo Where to return extended error details,
+ * optional.
+ */
+RTDECL(int) RTCrX509TbsCertificate_ReprocessExtensions(PRTCRX509TBSCERTIFICATE pThis, PRTERRINFO pErrInfo)
+{
+ /*
+ * Clear all variables we will set.
+ */
+ pThis->T3.fFlags = 0;
+ pThis->T3.fKeyUsage = 0;
+ pThis->T3.fExtKeyUsage = 0;
+ pThis->T3.pAuthorityKeyIdentifier = NULL;
+ pThis->T3.pSubjectKeyIdentifier = NULL;
+ pThis->T3.pAltSubjectName = NULL;
+ pThis->T3.pAltIssuerName = NULL;
+ pThis->T3.pCertificatePolicies = NULL;
+ pThis->T3.pPolicyMappings = NULL;
+ pThis->T3.pBasicConstraints = NULL;
+ pThis->T3.pNameConstraints = NULL;
+ pThis->T3.pPolicyConstraints = NULL;
+ pThis->T3.pInhibitAnyPolicy = NULL;
+
+#define CHECK_SET_PRESENT_RET_ON_DUP(a_pThis, a_pErrInfo, a_fPresentFlag) \
+ do { \
+ if ((a_pThis)->T3.fFlags & (a_fPresentFlag)) \
+ return RTErrInfoSet(a_pErrInfo, VERR_CR_X509_TBSCERT_DUPLICATE_EXTENSION, \
+ "Duplicate extension " #a_fPresentFlag); \
+ (a_pThis)->T3.fFlags |= (a_fPresentFlag); \
+ } while (0)
+
+ /*
+ * Process all the extensions.
+ */
+ for (uint32_t i = 0; i < pThis->T3.Extensions.cItems; i++)
+ {
+ PCRTASN1OBJID pExtnId = &pThis->T3.Extensions.papItems[i]->ExtnId;
+ PCRTASN1OCTETSTRING pExtValue = &pThis->T3.Extensions.papItems[i]->ExtnValue;
+ if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_KEY_USAGE_OID) == 0)
+ {
+ CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_KEY_USAGE);
+ rtCrx509TbsCertificate_AddKeyUsageFlags(pThis, pThis->T3.Extensions.papItems[i]);
+ Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_BIT_STRING);
+ }
+ else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_EXT_KEY_USAGE_OID) == 0)
+ {
+ CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_EXT_KEY_USAGE);
+ rtCrx509TbsCertificate_AddExtKeyUsageFlags(pThis, pThis->T3.Extensions.papItems[i]);
+ Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_SEQ_OF_OBJ_IDS);
+ }
+ else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_AUTHORITY_KEY_IDENTIFIER_OID) == 0)
+ {
+ CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_AUTHORITY_KEY_IDENTIFIER);
+ pThis->T3.pAuthorityKeyIdentifier = (PCRTCRX509AUTHORITYKEYIDENTIFIER)pExtValue->pEncapsulated;
+ Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_AUTHORITY_KEY_IDENTIFIER);
+ }
+ else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_OLD_AUTHORITY_KEY_IDENTIFIER_OID) == 0)
+ {
+ CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_OLD_AUTHORITY_KEY_IDENTIFIER);
+ pThis->T3.pOldAuthorityKeyIdentifier = (PCRTCRX509OLDAUTHORITYKEYIDENTIFIER)pExtValue->pEncapsulated;
+ Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_OLD_AUTHORITY_KEY_IDENTIFIER);
+ }
+ else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_SUBJECT_KEY_IDENTIFIER_OID) == 0)
+ {
+ CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_SUBJECT_KEY_IDENTIFIER);
+ pThis->T3.pSubjectKeyIdentifier = (PCRTASN1OCTETSTRING)pExtValue->pEncapsulated;
+ Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_OCTET_STRING);
+ }
+ else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_SUBJECT_ALT_NAME_OID) == 0)
+ {
+ CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_SUBJECT_ALT_NAME);
+ pThis->T3.pAltSubjectName = (PCRTCRX509GENERALNAMES)pExtValue->pEncapsulated;
+ Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_GENERAL_NAMES);
+ }
+ else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_ISSUER_ALT_NAME_OID) == 0)
+ {
+ CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_ISSUER_ALT_NAME);
+ pThis->T3.pAltIssuerName = (PCRTCRX509GENERALNAMES)pExtValue->pEncapsulated;
+ Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_GENERAL_NAMES);
+ }
+ else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_CERTIFICATE_POLICIES_OID) == 0)
+ {
+ CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_CERTIFICATE_POLICIES);
+ pThis->T3.pCertificatePolicies = (PCRTCRX509CERTIFICATEPOLICIES)pExtValue->pEncapsulated;
+ Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_CERTIFICATE_POLICIES);
+ }
+ else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_POLICY_MAPPINGS_OID) == 0)
+ {
+ CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_POLICY_MAPPINGS);
+ pThis->T3.pPolicyMappings = (PCRTCRX509POLICYMAPPINGS)pExtValue->pEncapsulated;
+ Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_POLICY_MAPPINGS);
+ }
+ else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_BASIC_CONSTRAINTS_OID) == 0)
+ {
+ CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_BASIC_CONSTRAINTS);
+ pThis->T3.pBasicConstraints = (PCRTCRX509BASICCONSTRAINTS)pExtValue->pEncapsulated;
+ Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_BASIC_CONSTRAINTS);
+ }
+ else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_NAME_CONSTRAINTS_OID) == 0)
+ {
+ CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_NAME_CONSTRAINTS);
+ pThis->T3.pNameConstraints = (PCRTCRX509NAMECONSTRAINTS)pExtValue->pEncapsulated;
+ Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_NAME_CONSTRAINTS);
+ }
+ else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_POLICY_CONSTRAINTS_OID) == 0)
+ {
+ CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_POLICY_CONSTRAINTS);
+ pThis->T3.pPolicyConstraints = (PCRTCRX509POLICYCONSTRAINTS)pExtValue->pEncapsulated;
+ Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_POLICY_CONSTRAINTS);
+ }
+ else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_INHIBIT_ANY_POLICY_OID) == 0)
+ {
+ CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_INHIBIT_ANY_POLICY);
+ pThis->T3.pInhibitAnyPolicy = (PCRTASN1INTEGER)pExtValue->pEncapsulated;
+ Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_INTEGER);
+ }
+ else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_ACCEPTABLE_CERT_POLICIES_OID) == 0)
+ pThis->T3.fFlags |= RTCRX509TBSCERTIFICATE_F_PRESENT_ACCEPTABLE_CERT_POLICIES;
+ else
+ pThis->T3.fFlags |= RTCRX509TBSCERTIFICATE_F_PRESENT_OTHER;
+ }
+
+ if (!pThis->T3.fFlags)
+ pThis->T3.fFlags |= RTCRX509TBSCERTIFICATE_F_PRESENT_NONE;
+
+#undef CHECK_SET_PRESENT_RET_ON_DUP
+ return VINF_SUCCESS;
+}
+
+
+
+/*
+ * One X.509 Certificate.
+ */
+
+RTDECL(bool) RTCrX509Certificate_MatchIssuerAndSerialNumber(PCRTCRX509CERTIFICATE pCertificate,
+ PCRTCRX509NAME pIssuer, PCRTASN1INTEGER pSerialNumber)
+{
+ if ( RTAsn1Integer_UnsignedCompare(&pCertificate->TbsCertificate.SerialNumber, pSerialNumber) == 0
+ && RTCrX509Name_Compare(&pCertificate->TbsCertificate.Issuer, pIssuer) == 0)
+ return true;
+ return false;
+}
+
+
+RTDECL(bool) RTCrX509Certificate_MatchSubjectOrAltSubjectByRfc5280(PCRTCRX509CERTIFICATE pThis, PCRTCRX509NAME pName)
+{
+ if (RTCrX509Name_MatchByRfc5280(&pThis->TbsCertificate.Subject, pName))
+ return true;
+
+ if (RTCrX509Extensions_IsPresent(&pThis->TbsCertificate.T3.Extensions))
+ for (uint32_t i = 0; i < pThis->TbsCertificate.T3.Extensions.cItems; i++)
+ {
+ PCRTCRX509EXTENSION pExt = pThis->TbsCertificate.T3.Extensions.papItems[i];
+ if ( pExt->enmValue == RTCRX509EXTENSIONVALUE_GENERAL_NAMES
+ && RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCRX509_ID_CE_SUBJECT_ALT_NAME_OID))
+ {
+ PCRTCRX509GENERALNAMES pGeneralNames = (PCRTCRX509GENERALNAMES)pExt->ExtnValue.pEncapsulated;
+ for (uint32_t j = 0; j < pGeneralNames->cItems; j++)
+ if ( RTCRX509GENERALNAME_IS_DIRECTORY_NAME(pGeneralNames->papItems[j])
+ && RTCrX509Name_MatchByRfc5280(&pGeneralNames->papItems[j]->u.pT4->DirectoryName, pName))
+ return true;
+ }
+ }
+ return false;
+}
+
+
+RTDECL(bool) RTCrX509Certificate_IsSelfSigned(PCRTCRX509CERTIFICATE pCertificate)
+{
+ if (RTCrX509Certificate_IsPresent(pCertificate))
+ {
+ return RTCrX509Name_MatchByRfc5280(&pCertificate->TbsCertificate.Subject,
+ &pCertificate->TbsCertificate.Issuer);
+ }
+ return false;
+}
+
+
+/*
+ * Set of X.509 Certificates.
+ */
+
+RTDECL(PCRTCRX509CERTIFICATE)
+RTCrX509Certificates_FindByIssuerAndSerialNumber(PCRTCRX509CERTIFICATES pCertificates,
+ PCRTCRX509NAME pIssuer, PCRTASN1INTEGER pSerialNumber)
+{
+ for (uint32_t i = 0; i < pCertificates->cItems; i++)
+ if (RTCrX509Certificate_MatchIssuerAndSerialNumber(pCertificates->papItems[i], pIssuer, pSerialNumber))
+ return pCertificates->papItems[i];
+ return NULL;
+}
+
diff --git a/src/VBox/Runtime/common/crypto/x509-file.cpp b/src/VBox/Runtime/common/crypto/x509-file.cpp
new file mode 100644
index 00000000..71863f73
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/x509-file.cpp
@@ -0,0 +1,174 @@
+/* $Id: x509-file.cpp $ */
+/** @file
+ * IPRT - Crypto - X.509, File related APIs.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/x509.h>
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/path.h>
+#include <iprt/crypto/pem.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static RTCRPEMMARKERWORD const g_aWords_Certificate[] = { { RT_STR_TUPLE("CERTIFICATE") } };
+/** X509 Certificate markers. */
+RT_DECL_DATA_CONST(RTCRPEMMARKER const) g_aRTCrX509CertificateMarkers[] =
+{
+ { g_aWords_Certificate, RT_ELEMENTS(g_aWords_Certificate) }
+};
+/** Number of entries in g_aRTCrX509CertificateMarkers. */
+RT_DECL_DATA_CONST(uint32_t const) g_cRTCrX509CertificateMarkers = RT_ELEMENTS(g_aRTCrX509CertificateMarkers);
+
+
+RTDECL(int) RTCrX509Certificate_ReadFromFile(PRTCRX509CERTIFICATE pCertificate, const char *pszFilename, uint32_t fFlags,
+ PCRTASN1ALLOCATORVTABLE pAllocator, PRTERRINFO pErrInfo)
+{
+ AssertReturn(!(fFlags & ~RTCRX509CERT_READ_F_PEM_ONLY), VERR_INVALID_FLAGS);
+ PCRTCRPEMSECTION pSectionHead;
+ int rc = RTCrPemReadFile(pszFilename,
+ fFlags & RTCRX509CERT_READ_F_PEM_ONLY ? RTCRPEMREADFILE_F_ONLY_PEM : 0,
+ g_aRTCrX509CertificateMarkers, g_cRTCrX509CertificateMarkers,
+ &pSectionHead, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ if (pSectionHead)
+ {
+ RTCRX509CERTIFICATE TmpCert;
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pSectionHead->pbData, (uint32_t)RT_MIN(pSectionHead->cbData, UINT32_MAX),
+ pErrInfo, pAllocator, RTASN1CURSOR_FLAGS_DER, RTPathFilename(pszFilename));
+ rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, &TmpCert, "Cert");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrX509Certificate_CheckSanity(&TmpCert, 0, pErrInfo, "Cert");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrX509Certificate_Clone(pCertificate, &TmpCert, pAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ if (pSectionHead->pNext || PrimaryCursor.Cursor.cbLeft)
+ rc = VINF_ASN1_MORE_DATA;
+ }
+ }
+ RTCrX509Certificate_Delete(&TmpCert);
+ }
+ RTCrPemFreeSections(pSectionHead);
+ }
+ else
+ rc = rc != VINF_SUCCESS ? -rc : VERR_INTERNAL_ERROR_2;
+
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTCrX509Certificate_ReadFromBuffer(PRTCRX509CERTIFICATE pCertificate, const void *pvBuf, size_t cbBuf,
+ uint32_t fFlags, PCRTASN1ALLOCATORVTABLE pAllocator,
+ PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ AssertReturn(!(fFlags & ~RTCRX509CERT_READ_F_PEM_ONLY), VERR_INVALID_FLAGS);
+ PCRTCRPEMSECTION pSectionHead;
+ int rc = RTCrPemParseContent(pvBuf, cbBuf,
+ fFlags & RTCRX509CERT_READ_F_PEM_ONLY ? RTCRPEMREADFILE_F_ONLY_PEM : 0,
+ g_aRTCrX509CertificateMarkers, g_cRTCrX509CertificateMarkers,
+ &pSectionHead, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ if (pSectionHead)
+ {
+ RTCRX509CERTIFICATE TmpCert;
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pSectionHead->pbData, (uint32_t)RT_MIN(pSectionHead->cbData, UINT32_MAX),
+ pErrInfo, pAllocator, RTASN1CURSOR_FLAGS_DER, pszErrorTag);
+ rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, &TmpCert, "Cert");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrX509Certificate_CheckSanity(&TmpCert, 0, pErrInfo, "Cert");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrX509Certificate_Clone(pCertificate, &TmpCert, pAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ if (pSectionHead->pNext || PrimaryCursor.Cursor.cbLeft)
+ rc = VINF_ASN1_MORE_DATA;
+ }
+ }
+ RTCrX509Certificate_Delete(&TmpCert);
+ }
+ RTCrPemFreeSections(pSectionHead);
+ }
+ else
+ rc = rc != VINF_SUCCESS ? -rc : VERR_INTERNAL_ERROR_2;
+ }
+ return rc;
+}
+
+
+
+#if 0
+RTDECL(int) RTCrX509Certificates_ReadFromFile(const char *pszFilename, uint32_t fFlags,
+ PRTCRX509CERTIFICATES pCertificates, PRTERRINFO pErrInfo)
+{
+ AssertReturn(!(fFlags & ~RTCRX509CERT_READ_F_PEM_ONLY), VERR_INVALID_FLAGS);
+ PCRTCRPEMSECTION pSectionHead;
+ int rc = RTCrPemReadFile(pszFilename,
+ fFlags & RTCRX509CERT_READ_F_PEM_ONLY ? RTCRPEMREADFILE_F_ONLY_PEM : 0,
+ g_aRTCrX509CertificateMarkers, g_cRTCrX509CertificateMarkers,
+ &pSectionHead, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ pCertificates->Allocation
+
+ PCRTCRPEMSECTION pCurSec = pSectionHead;
+ while (pCurSec)
+ {
+
+ pCurSec = pCurSec->pNext;
+ }
+
+ RTCrPemFreeSections(pSectionHead);
+ }
+ return rc;
+}
+#endif
+
diff --git a/src/VBox/Runtime/common/crypto/x509-init.cpp b/src/VBox/Runtime/common/crypto/x509-init.cpp
new file mode 100644
index 00000000..b30a1945
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/x509-init.cpp
@@ -0,0 +1,88 @@
+/* $Id: x509-init.cpp $ */
+/** @file
+ * IPRT - Crypto - X.509, Initialization API.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/x509.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include <iprt/uni.h>
+
+#include "x509-internal.h"
+
+
+static int rtCrX509Extension_ExtnValue_Clone(PRTCRX509EXTENSION pThis, PCRTCRX509EXTENSION pSrc)
+{
+ pThis->enmValue = pSrc->enmValue;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCrX509Name_RecodeAsUtf8(PRTCRX509NAME pThis, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ uint32_t cRdns = pThis->cItems;
+ PRTCRX509RELATIVEDISTINGUISHEDNAME *ppRdn = pThis->papItems;
+ while (cRdns-- > 0)
+ {
+ PRTCRX509RELATIVEDISTINGUISHEDNAME const pRdn = *ppRdn;
+ uint32_t cAttribs = pRdn->cItems;
+ PRTCRX509ATTRIBUTETYPEANDVALUE *ppAttrib = pRdn->papItems;
+ while (cAttribs-- > 0)
+ {
+ PRTCRX509ATTRIBUTETYPEANDVALUE const pAttrib = *ppAttrib;
+ if (pAttrib->Value.enmType == RTASN1TYPE_STRING)
+ {
+ int rc = RTAsn1String_RecodeAsUtf8(&pAttrib->Value.u.String, pAllocator);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ ppAttrib++;
+ }
+ ppRdn++;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-init.h>
+
diff --git a/src/VBox/Runtime/common/crypto/x509-internal.h b/src/VBox/Runtime/common/crypto/x509-internal.h
new file mode 100644
index 00000000..550892ad
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/x509-internal.h
@@ -0,0 +1,47 @@
+/* $Id: x509-internal.h $ */
+/** @file
+ * IPRT - Crypto - X.509, Internal Header.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_common_crypto_x509_internal_h
+#define IPRT_INCLUDED_SRC_common_crypto_x509_internal_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define RTASN1TMPL_TEMPLATE_FILE "../common/crypto/x509-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+
+#endif /* !IPRT_INCLUDED_SRC_common_crypto_x509_internal_h */
+
diff --git a/src/VBox/Runtime/common/crypto/x509-sanity.cpp b/src/VBox/Runtime/common/crypto/x509-sanity.cpp
new file mode 100644
index 00000000..843ac996
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/x509-sanity.cpp
@@ -0,0 +1,171 @@
+/* $Id: x509-sanity.cpp $ */
+/** @file
+ * IPRT - Crypto - X.509, Sanity Checkers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/x509.h>
+
+#include <iprt/err.h>
+#include <iprt/string.h>
+
+#include "x509-internal.h"
+
+
+
+static int rtCrX509Validity_CheckSanityExtra(PCRTCRX509VALIDITY pThis, uint32_t fFlags, PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ RT_NOREF_PV(fFlags);
+
+ if (RTAsn1Time_Compare(&pThis->NotBefore, &pThis->NotAfter) > 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_X509_VALIDITY_SWAPPED, "%s: NotBefore is after NotAfter", pszErrorTag);
+ /** @todo check tag constraints? */
+ return VINF_SUCCESS;
+}
+
+
+static int rtCrX509Name_CheckSanityExtra(PCRTCRX509NAME pThis, uint32_t fFlags, PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ RT_NOREF_PV(fFlags);
+
+ if (pThis->cItems == 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_X509_NAME_EMPTY_SET, "%s: Has no components.", pszErrorTag);
+
+ for (uint32_t i = 0; i < pThis->cItems; i++)
+ {
+ PCRTCRX509RELATIVEDISTINGUISHEDNAME const pRdn = pThis->papItems[i];
+ if (pRdn->cItems == 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_X509_NAME_EMPTY_SUB_SET,
+ "%s: Items[%u] has no sub components.", pszErrorTag, i);
+
+ for (uint32_t j = 0; j < pRdn->cItems; j++)
+ {
+ PCRTCRX509ATTRIBUTETYPEANDVALUE const pAttr = pRdn->papItems[j];
+
+ if (pAttr->Value.enmType != RTASN1TYPE_STRING)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_X509_NAME_NOT_STRING,
+ "%s: Items[%u].paItems[%u].enmType is %d instead of string (%d).",
+ pszErrorTag, i, j, pAttr->Value.enmType, RTASN1TYPE_STRING);
+ if (pAttr->Value.u.String.Asn1Core.cb == 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_X509_NAME_EMPTY_STRING,
+ "%s: Items[%u].paItems[%u] is an empty string", pszErrorTag, i, j);
+ switch (pAttr->Value.u.String.Asn1Core.uTag)
+ {
+ case ASN1_TAG_PRINTABLE_STRING:
+ case ASN1_TAG_UTF8_STRING:
+ break;
+ case ASN1_TAG_T61_STRING:
+ case ASN1_TAG_UNIVERSAL_STRING:
+ case ASN1_TAG_BMP_STRING:
+ break;
+ case ASN1_TAG_IA5_STRING: /* Used by "Microsoft Root Certificate Authority" in the "com" part of the Issuer. */
+ break;
+ default:
+ return RTErrInfoSetF(pErrInfo, VERR_CR_X509_INVALID_NAME_STRING_TAG,
+ "%s: Items[%u].paItems[%u] invalid string type: %u", pszErrorTag, i, j,
+ pAttr->Value.u.String.Asn1Core.uTag);
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+static int rtCrX509SubjectPublicKeyInfo_CheckSanityExtra(PCRTCRX509SUBJECTPUBLICKEYINFO pThis, uint32_t fFlags,
+ PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ RT_NOREF_PV(fFlags);
+ if (pThis->SubjectPublicKey.cBits <= 32)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_X509_PUBLIC_KEY_TOO_SMALL,
+ "%s: SubjectPublicKey is too small, only %u bits", pszErrorTag, pThis->SubjectPublicKey.cBits);
+ return VINF_SUCCESS;
+}
+
+
+static int rtCrX509TbsCertificate_CheckSanityExtra(PCRTCRX509TBSCERTIFICATE pThis, uint32_t fFlags,
+ PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ RT_NOREF_PV(fFlags);
+
+ if ( RTAsn1Integer_IsPresent(&pThis->T0.Version)
+ && RTAsn1Integer_UnsignedCompareWithU32(&pThis->T0.Version, RTCRX509TBSCERTIFICATE_V1) != 0
+ && RTAsn1Integer_UnsignedCompareWithU32(&pThis->T0.Version, RTCRX509TBSCERTIFICATE_V2) != 0
+ && RTAsn1Integer_UnsignedCompareWithU32(&pThis->T0.Version, RTCRX509TBSCERTIFICATE_V3) != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_X509_TBSCERT_UNSUPPORTED_VERSION,
+ "%s: Unknown Version number: %llu",
+ pszErrorTag, pThis->T0.Version.uValue.u);
+
+ if ( pThis->SerialNumber.Asn1Core.cb < 1
+ || pThis->SerialNumber.Asn1Core.cb > 1024)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_X509_TBSCERT_SERIAL_NUMBER_OUT_OF_BOUNDS,
+ "%s: Bad SerialNumber length: %u", pszErrorTag, pThis->SerialNumber.Asn1Core.cb);
+
+ if ( ( RTAsn1BitString_IsPresent(&pThis->T1.IssuerUniqueId)
+ || RTAsn1BitString_IsPresent(&pThis->T2.SubjectUniqueId))
+ && RTAsn1Integer_UnsignedCompareWithU32(&pThis->T0.Version, RTCRX509TBSCERTIFICATE_V2) < 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_X509_TBSCERT_UNIQUE_IDS_REQ_V2,
+ "%s: IssuerUniqueId and SubjectUniqueId requires version 2", pszErrorTag);
+
+ if ( RTCrX509Extensions_IsPresent(&pThis->T3.Extensions)
+ && RTAsn1Integer_UnsignedCompareWithU32(&pThis->T0.Version, RTCRX509TBSCERTIFICATE_V3) < 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_X509_TBSCERT_EXTS_REQ_V3, "%s: Extensions requires version 3", pszErrorTag);
+
+ return VINF_SUCCESS;
+}
+
+
+static int rtCrX509Certificate_CheckSanityExtra(PCRTCRX509CERTIFICATE pThis, uint32_t fFlags,
+ PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ RT_NOREF_PV(fFlags);
+
+ if (RTCrX509AlgorithmIdentifier_Compare(&pThis->SignatureAlgorithm, &pThis->TbsCertificate.Signature) != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_X509_CERT_TBS_SIGN_ALGO_MISMATCH,
+ "%s: SignatureAlgorithm (%s) does not match TbsCertificate.Signature (%s).", pszErrorTag,
+ pThis->SignatureAlgorithm.Algorithm.szObjId,
+ pThis->TbsCertificate.Signature.Algorithm.szObjId);
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * Generate the code.
+ */
+#include <iprt/asn1-generator-sanity.h>
+
diff --git a/src/VBox/Runtime/common/crypto/x509-template.h b/src/VBox/Runtime/common/crypto/x509-template.h
new file mode 100644
index 00000000..9ab3fa51
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/x509-template.h
@@ -0,0 +1,468 @@
+/* $Id: x509-template.h $ */
+/** @file
+ * IPRT - Crypto - X.509, Code Generator Template.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#define RTASN1TMPL_DECL RTDECL
+
+/*
+ * X.509 Validity.
+ */
+#define RTASN1TMPL_TYPE RTCRX509VALIDITY
+#define RTASN1TMPL_EXT_NAME RTCrX509Validity
+#define RTASN1TMPL_INT_NAME rtCrX509Validity
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( NotBefore, RTASN1TIME, RTAsn1Time);
+RTASN1TMPL_MEMBER( NotAfter, RTASN1TIME, RTAsn1Time);
+RTASN1TMPL_EXEC_CHECK_SANITY( rc = rtCrX509Validity_CheckSanityExtra(pThis, fFlags, pErrInfo, pszErrorTag) )
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * One X.509 Algorithm Identifier.
+ */
+#define RTASN1TMPL_TYPE RTCRX509ALGORITHMIDENTIFIER
+#define RTASN1TMPL_EXT_NAME RTCrX509AlgorithmIdentifier
+#define RTASN1TMPL_INT_NAME rtCrX509AlgorithmIdentifier
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( Algorithm, RTASN1OBJID, RTAsn1ObjId);
+RTASN1TMPL_MEMBER_OPT_ANY( Parameters, RTASN1DYNTYPE, RTAsn1DynType);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * Set of X.509 Algorithm Identifiers.
+ */
+#define RTASN1TMPL_TYPE RTCRX509ALGORITHMIDENTIFIERS
+#define RTASN1TMPL_EXT_NAME RTCrX509AlgorithmIdentifiers
+#define RTASN1TMPL_INT_NAME rtCrX509AlgorithmIdentifiers
+RTASN1TMPL_SET_OF(RTCRX509ALGORITHMIDENTIFIER, RTCrX509AlgorithmIdentifier);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * One X.509 AttributeTypeAndValue.
+ */
+#define RTASN1TMPL_TYPE RTCRX509ATTRIBUTETYPEANDVALUE
+#define RTASN1TMPL_EXT_NAME RTCrX509AttributeTypeAndValue
+#define RTASN1TMPL_INT_NAME rtCrX509AttributeTypeAndValue
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( Type, RTASN1OBJID, RTAsn1ObjId);
+RTASN1TMPL_MEMBER( Value, RTASN1DYNTYPE, RTAsn1DynType);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * Set of X.509 AttributeTypeAndValues / X.509 RelativeDistinguishedName.
+ */
+#define RTASN1TMPL_TYPE RTCRX509ATTRIBUTETYPEANDVALUES
+#define RTASN1TMPL_EXT_NAME RTCrX509AttributeTypeAndValues
+#define RTASN1TMPL_INT_NAME rtCrX509AttributeTypeAndValues
+RTASN1TMPL_SET_OF(RTCRX509ATTRIBUTETYPEANDVALUE, RTCrX509AttributeTypeAndValue);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+/*
+ * X.509 Name.
+ */
+#define RTASN1TMPL_TYPE RTCRX509NAME
+#define RTASN1TMPL_EXT_NAME RTCrX509Name
+#define RTASN1TMPL_INT_NAME rtCrX509Name
+#undef RTASN1TMPL_SET_SEQ_EXEC_CHECK_SANITY
+#define RTASN1TMPL_SET_SEQ_EXEC_CHECK_SANITY() rc = rtCrX509Name_CheckSanityExtra(pThis, fFlags, pErrInfo, pszErrorTag)
+RTASN1TMPL_SEQ_OF(RTCRX509RELATIVEDISTINGUISHEDNAME, RTCrX509RelativeDistinguishedName);
+#undef RTASN1TMPL_SET_SEQ_EXEC_CHECK_SANITY
+#define RTASN1TMPL_SET_SEQ_EXEC_CHECK_SANITY() do { } while (0)
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+/*
+ * One X.509 OtherName.
+ * Note! This is simplified and might not work correctly for all types with
+ * non-DER compatible encodings.
+ */
+#define RTASN1TMPL_TYPE RTCRX509OTHERNAME
+#define RTASN1TMPL_EXT_NAME RTCrX509OtherName
+#define RTASN1TMPL_INT_NAME rtCrX509OtherName
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( TypeId, RTASN1OBJID, RTAsn1ObjId);
+RTASN1TMPL_MEMBER( Value, RTASN1DYNTYPE, RTAsn1DynType);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * One X.509 GeneralName.
+ * Note! This is simplified and might not work correctly for all types with
+ * non-DER compatible encodings.
+ */
+#define RTASN1TMPL_TYPE RTCRX509GENERALNAME
+#define RTASN1TMPL_EXT_NAME RTCrX509GeneralName
+#define RTASN1TMPL_INT_NAME rtCrX509GeneralName
+RTASN1TMPL_BEGIN_PCHOICE();
+RTASN1TMPL_PCHOICE_ITAG( 0, RTCRX509GENERALNAMECHOICE_OTHER_NAME, u.pT0_OtherName, OtherName, RTCRX509OTHERNAME, RTCrX509OtherName);
+RTASN1TMPL_PCHOICE_ITAG_CP( 1, RTCRX509GENERALNAMECHOICE_RFC822_NAME, u.pT1_Rfc822, Rfc822, RTASN1STRING, RTAsn1Ia5String);
+RTASN1TMPL_PCHOICE_ITAG_CP( 2, RTCRX509GENERALNAMECHOICE_DNS_NAME, u.pT2_DnsName, DnsType, RTASN1STRING, RTAsn1Ia5String);
+RTASN1TMPL_PCHOICE_XTAG( 3, RTCRX509GENERALNAMECHOICE_X400_ADDRESS, u.pT3, CtxTag3, X400Address, RTASN1DYNTYPE, RTAsn1DynType); /** @todo */
+RTASN1TMPL_PCHOICE_XTAG( 4, RTCRX509GENERALNAMECHOICE_DIRECTORY_NAME, u.pT4, CtxTag4, DirectoryName, RTCRX509NAME, RTCrX509Name);
+RTASN1TMPL_PCHOICE_XTAG( 5, RTCRX509GENERALNAMECHOICE_EDI_PARTY_NAME, u.pT5, CtxTag5, EdiPartyName, RTASN1DYNTYPE, RTAsn1DynType); /** @todo */
+RTASN1TMPL_PCHOICE_ITAG_CP( 6, RTCRX509GENERALNAMECHOICE_URI, u.pT6_Uri, Uri, RTASN1STRING, RTAsn1Ia5String);
+RTASN1TMPL_PCHOICE_ITAG_CP( 7, RTCRX509GENERALNAMECHOICE_IP_ADDRESS, u.pT7_IpAddress, IpAddress, RTASN1OCTETSTRING, RTAsn1OctetString); /** @todo Constraints */
+RTASN1TMPL_PCHOICE_ITAG_CP( 8, RTCRX509GENERALNAMECHOICE_REGISTERED_ID, u.pT8_RegisteredId,RegisteredId,RTASN1OBJID, RTAsn1ObjId);
+RTASN1TMPL_END_PCHOICE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * Sequence of X.509 GeneralNames.
+ */
+#define RTASN1TMPL_TYPE RTCRX509GENERALNAMES
+#define RTASN1TMPL_EXT_NAME RTCrX509GeneralNames
+#define RTASN1TMPL_INT_NAME rtCrX509GeneralNames
+RTASN1TMPL_SEQ_OF(RTCRX509GENERALNAME, RTCrX509GeneralName);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * X.509 UniqueIdentifier - RTASN1BITSTRING alias.
+ */
+
+
+/*
+ * X.509 SubjectPublicKeyInfo.
+ */
+#define RTASN1TMPL_TYPE RTCRX509SUBJECTPUBLICKEYINFO
+#define RTASN1TMPL_EXT_NAME RTCrX509SubjectPublicKeyInfo
+#define RTASN1TMPL_INT_NAME rtCrX509SubjectPublicKeyInfo
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( Algorithm, RTCRX509ALGORITHMIDENTIFIER, RTCrX509AlgorithmIdentifier);
+RTASN1TMPL_MEMBER( SubjectPublicKey, RTASN1BITSTRING, RTAsn1BitString);
+RTASN1TMPL_EXEC_CHECK_SANITY( rc = rtCrX509SubjectPublicKeyInfo_CheckSanityExtra(pThis, fFlags, pErrInfo, pszErrorTag) )
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * X.509 AuthorityKeyIdentifier (IPRT representation).
+ */
+#define RTASN1TMPL_TYPE RTCRX509AUTHORITYKEYIDENTIFIER
+#define RTASN1TMPL_EXT_NAME RTCrX509AuthorityKeyIdentifier
+#define RTASN1TMPL_INT_NAME rtCrX509AuthorityKeyIdentifier
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER_OPT_ITAG_CP( KeyIdentifier, RTASN1OCTETSTRING, RTAsn1OctetString, 0);
+RTASN1TMPL_MEMBER_OPT_ITAG( AuthorityCertIssuer, RTCRX509GENERALNAMES, RTCrX509GeneralNames, 1);
+RTASN1TMPL_MEMBER_OPT_ITAG_CP( AuthorityCertSerialNumber, RTASN1INTEGER, RTAsn1Integer, 2);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * X.509 OldAuthorityKeyIdentifier (IPRT representation).
+ */
+#define RTASN1TMPL_TYPE RTCRX509OLDAUTHORITYKEYIDENTIFIER
+#define RTASN1TMPL_EXT_NAME RTCrX509OldAuthorityKeyIdentifier
+#define RTASN1TMPL_INT_NAME rtCrX509OldAuthorityKeyIdentifier
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER_OPT_ITAG_CP( KeyIdentifier, RTASN1OCTETSTRING, RTAsn1OctetString, 0);
+RTASN1TMPL_MEMBER_OPT_XTAG( T1, CtxTag1, AuthorityCertIssuer, RTCRX509NAME, RTCrX509Name, 1);
+RTASN1TMPL_MEMBER_OPT_ITAG_CP( AuthorityCertSerialNumber, RTASN1INTEGER, RTAsn1Integer, 2);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * One X.509 PolicyQualifierInfo.
+ */
+#define RTASN1TMPL_TYPE RTCRX509POLICYQUALIFIERINFO
+#define RTASN1TMPL_EXT_NAME RTCrX509PolicyQualifierInfo
+#define RTASN1TMPL_INT_NAME rtCrX509PolicyQualifierInfo
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( PolicyQualifierId, RTASN1OBJID, RTAsn1ObjId);
+RTASN1TMPL_MEMBER( Qualifier, RTASN1DYNTYPE, RTAsn1DynType);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * Sequence of X.509 PolicyQualifierInfo.
+ */
+#define RTASN1TMPL_TYPE RTCRX509POLICYQUALIFIERINFOS
+#define RTASN1TMPL_EXT_NAME RTCrX509PolicyQualifierInfos
+#define RTASN1TMPL_INT_NAME rtCrX509PolicyQualifierInfos
+RTASN1TMPL_SEQ_OF(RTCRX509POLICYQUALIFIERINFO, RTCrX509PolicyQualifierInfo);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * One X.509 PolicyInformation.
+ */
+#define RTASN1TMPL_TYPE RTCRX509POLICYINFORMATION
+#define RTASN1TMPL_EXT_NAME RTCrX509PolicyInformation
+#define RTASN1TMPL_INT_NAME rtCrX509PolicyInformation
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( PolicyIdentifier, RTASN1OBJID, RTAsn1ObjId);
+RTASN1TMPL_MEMBER_OPT_ITAG_UC( PolicyQualifiers, RTCRX509POLICYQUALIFIERINFOS, RTCrX509PolicyQualifierInfos, ASN1_TAG_SEQUENCE);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * Sequence of X.509 CertificatePolicies.
+ */
+#define RTASN1TMPL_TYPE RTCRX509CERTIFICATEPOLICIES
+#define RTASN1TMPL_EXT_NAME RTCrX509CertificatePolicies
+#define RTASN1TMPL_INT_NAME rtCrX509CertificatePolicies
+RTASN1TMPL_SEQ_OF(RTCRX509POLICYINFORMATION, RTCrX509PolicyInformation);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * One X.509 PolicyMapping (IPRT representation).
+ */
+#define RTASN1TMPL_TYPE RTCRX509POLICYMAPPING
+#define RTASN1TMPL_EXT_NAME RTCrX509PolicyMapping
+#define RTASN1TMPL_INT_NAME rtCrX509PolicyMapping
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( IssuerDomainPolicy, RTASN1OBJID, RTAsn1ObjId);
+RTASN1TMPL_MEMBER( SubjectDomainPolicy, RTASN1OBJID, RTAsn1ObjId);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * Sequence of X.509 PolicyMappings (IPRT representation).
+ */
+#define RTASN1TMPL_TYPE RTCRX509POLICYMAPPINGS
+#define RTASN1TMPL_EXT_NAME RTCrX509PolicyMappings
+#define RTASN1TMPL_INT_NAME rtCrX509PolicyMappings
+RTASN1TMPL_SEQ_OF(RTCRX509POLICYMAPPING, RTCrX509PolicyMapping);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * X.509 BasicConstraints (IPRT representation).
+ */
+#define RTASN1TMPL_TYPE RTCRX509BASICCONSTRAINTS
+#define RTASN1TMPL_EXT_NAME RTCrX509BasicConstraints
+#define RTASN1TMPL_INT_NAME rtCrX509BasicConstraints
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER_DEF_ITAG_UP( CA, RTASN1BOOLEAN, RTAsn1Boolean, ASN1_TAG_BOOLEAN, false);
+RTASN1TMPL_MEMBER_OPT_ITAG_UP( PathLenConstraint, RTASN1INTEGER, RTAsn1Integer, ASN1_TAG_INTEGER);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * X.509 GeneralSubtree (IPRT representation).
+ */
+#define RTASN1TMPL_TYPE RTCRX509GENERALSUBTREE
+#define RTASN1TMPL_EXT_NAME RTCrX509GeneralSubtree
+#define RTASN1TMPL_INT_NAME rtCrX509GeneralSubtree
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( Base, RTCRX509GENERALNAME, RTCrX509GeneralName);
+RTASN1TMPL_MEMBER_DEF_ITAG_UP( Minimum, RTASN1INTEGER, RTAsn1Integer, ASN1_TAG_INTEGER, 0);
+RTASN1TMPL_MEMBER_OPT_ITAG_UP( Maximum, RTASN1INTEGER, RTAsn1Integer, ASN1_TAG_INTEGER);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+
+/*
+ * Sequence of X.509 GeneralSubtrees (IPRT representation).
+ */
+#define RTASN1TMPL_TYPE RTCRX509GENERALSUBTREES
+#define RTASN1TMPL_EXT_NAME RTCrX509GeneralSubtrees
+#define RTASN1TMPL_INT_NAME rtCrX509GeneralSubtrees
+RTASN1TMPL_SEQ_OF(RTCRX509GENERALSUBTREE, RTCrX509GeneralSubtree);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * X.509 NameConstraints (IPRT representation).
+ */
+#define RTASN1TMPL_TYPE RTCRX509NAMECONSTRAINTS
+#define RTASN1TMPL_EXT_NAME RTCrX509NameConstraints
+#define RTASN1TMPL_INT_NAME rtCrX509NameConstraints
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER_OPT_XTAG( T0, CtxTag0, PermittedSubtrees, RTCRX509GENERALSUBTREES, RTCrX509GeneralSubtrees, 0);
+RTASN1TMPL_MEMBER_OPT_XTAG( T1, CtxTag1, ExcludedSubtrees, RTCRX509GENERALSUBTREES, RTCrX509GeneralSubtrees, 1);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * X.509 PolicyConstraints (IPRT representation).
+ */
+#define RTASN1TMPL_TYPE RTCRX509POLICYCONSTRAINTS
+#define RTASN1TMPL_EXT_NAME RTCrX509PolicyConstraints
+#define RTASN1TMPL_INT_NAME rtCrX509PolicyConstraints
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER_OPT_ITAG_CP( RequireExplicitPolicy, RTASN1INTEGER, RTAsn1Integer, 0);
+RTASN1TMPL_MEMBER_OPT_ITAG_CP( InhibitPolicyMapping, RTASN1INTEGER, RTAsn1Integer, 1);
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * One X.509 Extension.
+ */
+#define RTASN1TMPL_TYPE RTCRX509EXTENSION
+#define RTASN1TMPL_EXT_NAME RTCrX509Extension
+#define RTASN1TMPL_INT_NAME rtCrX509Extension
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( ExtnId, RTASN1OBJID, RTAsn1ObjId);
+RTASN1TMPL_MEMBER_DEF_ITAG_UP( Critical, RTASN1BOOLEAN, RTAsn1Boolean, ASN1_TAG_BOOLEAN, false);
+RTASN1TMPL_MEMBER( ExtnValue, RTASN1OCTETSTRING, RTAsn1OctetString);
+RTASN1TMPL_EXEC_DECODE(rc = RTCrX509Extension_ExtnValue_DecodeAsn1(pCursor, fFlags, pThis, "ExtnValue"))
+RTASN1TMPL_EXEC_CLONE( rc = rtCrX509Extension_ExtnValue_Clone(pThis, pSrc))
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * Sequence of X.509 Extensions.
+ */
+#define RTASN1TMPL_TYPE RTCRX509EXTENSIONS
+#define RTASN1TMPL_EXT_NAME RTCrX509Extensions
+#define RTASN1TMPL_INT_NAME rtCrX509Extensions
+RTASN1TMPL_SEQ_OF(RTCRX509EXTENSION, RTCrX509Extension);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * X.509 TbsCertificate.
+ */
+#define RTASN1TMPL_TYPE RTCRX509TBSCERTIFICATE
+#define RTASN1TMPL_EXT_NAME RTCrX509TbsCertificate
+#define RTASN1TMPL_INT_NAME rtCrX509TbsCertificate
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER_OPT_XTAG( T0, CtxTag0, Version, RTASN1INTEGER, RTAsn1Integer, 0);
+RTASN1TMPL_MEMBER( SerialNumber, RTASN1INTEGER, RTAsn1Integer);
+RTASN1TMPL_MEMBER( Signature, RTCRX509ALGORITHMIDENTIFIER, RTCrX509AlgorithmIdentifier);
+RTASN1TMPL_MEMBER( Issuer, RTCRX509NAME, RTCrX509Name);
+RTASN1TMPL_MEMBER( Validity, RTCRX509VALIDITY, RTCrX509Validity);
+RTASN1TMPL_MEMBER( Subject, RTCRX509NAME, RTCrX509Name);
+RTASN1TMPL_MEMBER( SubjectPublicKeyInfo, RTCRX509SUBJECTPUBLICKEYINFO, RTCrX509SubjectPublicKeyInfo);
+RTASN1TMPL_MEMBER_OPT_XTAG( T1, CtxTag1, IssuerUniqueId, RTCRX509UNIQUEIDENTIFIER, RTCrX509UniqueIdentifier, 1);
+RTASN1TMPL_MEMBER_OPT_XTAG( T2, CtxTag2, SubjectUniqueId, RTCRX509UNIQUEIDENTIFIER, RTCrX509UniqueIdentifier, 2);
+RTASN1TMPL_MEMBER_OPT_XTAG( T3, CtxTag3, Extensions, RTCRX509EXTENSIONS, RTCrX509Extensions, 3);
+RTASN1TMPL_EXEC_DECODE( rc = RTCrX509TbsCertificate_ReprocessExtensions(pThis, pCursor->pPrimary->pErrInfo) )
+RTASN1TMPL_EXEC_CLONE( rc = RTCrX509TbsCertificate_ReprocessExtensions(pThis, NULL) )
+RTASN1TMPL_EXEC_CHECK_SANITY( rc = rtCrX509TbsCertificate_CheckSanityExtra(pThis, fFlags, pErrInfo, pszErrorTag) )
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * One X.509 Certificate.
+ */
+#define RTASN1TMPL_TYPE RTCRX509CERTIFICATE
+#define RTASN1TMPL_EXT_NAME RTCrX509Certificate
+#define RTASN1TMPL_INT_NAME rtCrX509Certificate
+RTASN1TMPL_BEGIN_SEQCORE();
+RTASN1TMPL_MEMBER( TbsCertificate, RTCRX509TBSCERTIFICATE, RTCrX509TbsCertificate);
+RTASN1TMPL_MEMBER( SignatureAlgorithm, RTCRX509ALGORITHMIDENTIFIER, RTCrX509AlgorithmIdentifier);
+RTASN1TMPL_MEMBER( SignatureValue, RTASN1BITSTRING, RTAsn1BitString);
+RTASN1TMPL_EXEC_CHECK_SANITY( rc = rtCrX509Certificate_CheckSanityExtra(pThis, fFlags, pErrInfo, pszErrorTag) )
+RTASN1TMPL_END_SEQCORE();
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+/*
+ * Set of X.509 Certificates.
+ */
+/** @todo Microsoft Hacks. ExtendedCertificates. */
+#define RTASN1TMPL_TYPE RTCRX509CERTIFICATES
+#define RTASN1TMPL_EXT_NAME RTCrX509Certificates
+#define RTASN1TMPL_INT_NAME rtCrX509Certificates
+RTASN1TMPL_SET_OF(RTCRX509CERTIFICATE, RTCrX509Certificate);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
diff --git a/src/VBox/Runtime/common/crypto/x509-verify.cpp b/src/VBox/Runtime/common/crypto/x509-verify.cpp
new file mode 100644
index 00000000..e2b42c08
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/x509-verify.cpp
@@ -0,0 +1,131 @@
+/* $Id: x509-verify.cpp $ */
+/** @file
+ * IPRT - Crypto - X.509, Signature verification.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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, in version 3 of the
+ * License.
+ *
+ * This program 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, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/x509.h>
+#include <iprt/crypto/pkix.h>
+#include <iprt/crypto/key.h>
+
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+
+RTDECL(int) RTCrX509Certificate_VerifySignature(PCRTCRX509CERTIFICATE pThis, PCRTASN1OBJID pAlgorithm,
+ PCRTASN1DYNTYPE pParameters, PCRTASN1BITSTRING pPublicKey,
+ PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate the input a little.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertReturn(RTCrX509Certificate_IsPresent(pThis), VERR_INVALID_PARAMETER);
+
+ AssertPtrReturn(pAlgorithm, VERR_INVALID_POINTER);
+ AssertReturn(RTAsn1ObjId_IsPresent(pAlgorithm), VERR_INVALID_POINTER);
+
+ AssertPtrReturn(pPublicKey, VERR_INVALID_POINTER);
+ AssertReturn(RTAsn1BitString_IsPresent(pPublicKey), VERR_INVALID_POINTER);
+
+ /*
+ * Check if the algorithm matches.
+ */
+ const char * const pszCipherOid = RTCrX509AlgorithmIdentifier_GetEncryptionOid(&pThis->SignatureAlgorithm,
+ true /*fMustIncludeHash*/);
+ if (!pszCipherOid)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_X509_UNKNOWN_CERT_SIGN_ALGO,
+ "Certificate signature algorithm not known: %s",
+ pThis->SignatureAlgorithm.Algorithm.szObjId);
+
+ if (RTAsn1ObjId_CompareWithString(pAlgorithm, pszCipherOid) != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_CR_X509_CERT_SIGN_ALGO_MISMATCH,
+ "Certificate signature cipher algorithm mismatch: cert uses %s (%s) while key uses %s",
+ pszCipherOid, pThis->SignatureAlgorithm.Algorithm.szObjId, pAlgorithm->szObjId);
+
+ /*
+ * Wrap up the public key.
+ */
+ RTCRKEY hPubKey;
+ int rc = RTCrKeyCreateFromPublicAlgorithmAndBits(&hPubKey, pAlgorithm, pParameters, pPublicKey, pErrInfo, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Here we should recode the to-be-signed part as DER, but we'll ASSUME
+ * that it's already in DER encoding and only does this if there the
+ * encoded bits are missing.
+ */
+ const uint8_t *pbRaw;
+ uint32_t cbRaw;
+ void *pvFree = NULL;
+ rc = RTAsn1EncodeQueryRawBits(RTCrX509TbsCertificate_GetAsn1Core(&pThis->TbsCertificate), &pbRaw, &cbRaw, &pvFree, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrPkixPubKeyVerifySignature(&pThis->SignatureAlgorithm.Algorithm, hPubKey, &pThis->SignatureAlgorithm.Parameters,
+ &pThis->SignatureValue, pbRaw, cbRaw, pErrInfo);
+ RTMemTmpFree(pvFree);
+ }
+
+ /* Free the public key. */
+ uint32_t cRefs = RTCrKeyRelease(hPubKey);
+ Assert(cRefs == 0); NOREF(cRefs);
+
+ return rc;
+}
+
+
+RTDECL(int) RTCrX509Certificate_VerifySignatureSelfSigned(PCRTCRX509CERTIFICATE pThis, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate the input a little.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertReturn(RTCrX509Certificate_IsPresent(pThis), VERR_INVALID_PARAMETER);
+
+ /*
+ * Call generic verification function.
+ */
+ PCRTCRX509TBSCERTIFICATE const pTbsCert = &pThis->TbsCertificate;
+ return RTCrX509Certificate_VerifySignature(pThis,
+ &pTbsCert->SubjectPublicKeyInfo.Algorithm.Algorithm,
+ &pTbsCert->SubjectPublicKeyInfo.Algorithm.Parameters,
+ &pTbsCert->SubjectPublicKeyInfo.SubjectPublicKey, pErrInfo);
+}
+