summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/crypto/x509-core.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/crypto/x509-core.cpp')
-rw-r--r--src/VBox/Runtime/common/crypto/x509-core.cpp1934
1 files changed, 1934 insertions, 0 deletions
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;
+}
+