VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/crypto/x509-core.cpp

Last change on this file was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 76.6 KB
Line 
1/* $Id: x509-core.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - Crypto - X.509, Core APIs.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include "internal/iprt.h"
42#include <iprt/crypto/x509.h>
43
44#include <iprt/err.h>
45#include <iprt/string.h>
46#include <iprt/uni.h>
47#include <iprt/crypto/pkix.h>
48#ifdef RT_STRICT
49# include <iprt/crypto/digest.h>
50#endif
51
52#include "x509-internal.h"
53
54
55/*********************************************************************************************************************************
56* Internal Functions *
57*********************************************************************************************************************************/
58#ifdef RT_STRICT
59static void rtCrX509AlgorithmIdentifier_AssertTableSanityAndMore(void);
60#endif
61
62
63/*
64 * Generate the code.
65 */
66#include <iprt/asn1-generator-core.h>
67
68
69/*
70 * X.509 Validity.
71 */
72
73RTDECL(bool) RTCrX509Validity_IsValidAtTimeSpec(PCRTCRX509VALIDITY pThis, PCRTTIMESPEC pTimeSpec)
74{
75 if (RTAsn1Time_CompareWithTimeSpec(&pThis->NotBefore, pTimeSpec) > 0)
76 return false;
77 if (RTAsn1Time_CompareWithTimeSpec(&pThis->NotAfter, pTimeSpec) < 0)
78 return false;
79 return true;
80}
81
82
83/*
84 * One X.509 Algorithm Identifier.
85 */
86
87/**
88 * String table with the encryption OIDs (used by g_aSignatureOidInfo).
89 */
90static const char * const g_apszEncryptionOids[] =
91{
92 NULL,
93#define IDX_ENCRYPTION_NIL 0
94 RTCR_X962_ECDSA_OID,
95#define IDX_ENCRYPTION_ECDSA 1
96 RTCR_PKCS1_RSA_OID,
97#define IDX_ENCRYPTION_RSA 2
98};
99
100/**
101 * Information about an algorithm identifier.
102 */
103typedef struct RTCRX509ALGORITHIDENTIFIERINTERNALINFO
104{
105 /** The signature OID. */
106 const char *pszSignatureOid;
107 /** Index into g_apszEncryptionOids of the encryption ODI.
108 * This is IDX_ENCRYPTION_NIL for hashes. */
109 uint8_t idxEncryption;
110 /** The message digest type specified by the OID.
111 * This is set to RTDIGESTTYPE_INVALID in two cases:
112 * 1. Pure encryption algorithm OID (cBitsDigest also zero).
113 * 2. The hash is so esoteric that IPRT doesn't support it. */
114 uint8_t enmDigestType;
115 /** The digest size in bits.
116 * This is ZERO if the OID does not include an hash. */
117 uint16_t cBitsDigest;
118} RTCRX509ALGORITHIDENTIFIERINTERNALINFO;
119typedef RTCRX509ALGORITHIDENTIFIERINTERNALINFO const *PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO;
120
121/**
122 * Signature to encryption OID.
123 *
124 * @note This is sorted to allow binary searching.
125 * @note This origins in pkix-utils.cpp, which is why it uses the other set of
126 * OID defines.
127 */
128static RTCRX509ALGORITHIDENTIFIERINTERNALINFO const g_aSignatureOidInfo[] =
129{
130 { /*1.0.10118.3.0.55*/ RTCRX509ALGORITHMIDENTIFIERID_WHIRLPOOL, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_INVALID, 512, },
131
132 { /*1.2.840.10045.2.1 */ RTCR_X962_ECDSA_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_INVALID, 0, },
133 { /*1.2.840.10045.4.1 */ RTCR_X962_ECDSA_WITH_SHA1_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA1, 160, },
134 { /*1.2.840.10045.4.3.1 */ RTCR_X962_ECDSA_WITH_SHA224_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA224, 224, },
135 { /*1.2.840.10045.4.3.2 */ RTCR_X962_ECDSA_WITH_SHA256_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA256, 256, },
136 { /*1.2.840.10045.4.3.3 */ RTCR_X962_ECDSA_WITH_SHA384_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA384, 384, },
137 { /*1.2.840.10045.4.3.4 */ RTCR_X962_ECDSA_WITH_SHA512_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA512, 512, },
138
139 { /*1.2.840.113549.1.1.1 */ RTCR_PKCS1_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_INVALID, 0, },
140 { /*1.2.840.113549.1.1.11*/ RTCR_PKCS1_SHA256_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA256, 256, },
141 { /*1.2.840.113549.1.1.12*/ RTCR_PKCS1_SHA384_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA384, 384, },
142 { /*1.2.840.113549.1.1.13*/ RTCR_PKCS1_SHA512_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA512, 512, },
143 { /*1.2.840.113549.1.1.14*/ RTCR_PKCS1_SHA224_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA224, 224, },
144 { /*1.2.840.113549.1.1.15*/ RTCR_PKCS1_SHA512T224_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA512T224, 224, },
145 { /*1.2.840.113549.1.1.16*/ RTCR_PKCS1_SHA512T256_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA512T256, 256, },
146 { /*1.2.840.113549.1.1.2*/ RTCR_PKCS1_MD2_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_MD2, 128, },
147 { /*1.2.840.113549.1.1.3*/ RTCR_PKCS1_MD4_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_MD4, 128, },
148 { /*1.2.840.113549.1.1.4*/ RTCR_PKCS1_MD5_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_MD5, 128, },
149 { /*1.2.840.113549.1.1.5*/ RTCR_PKCS1_SHA1_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA1, 160, },
150
151 { /*1.2.840.113549.2.2*/ RTCRX509ALGORITHMIDENTIFIERID_MD2, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_MD2, 128, },
152 { /*1.2.840.113549.2.4*/ RTCRX509ALGORITHMIDENTIFIERID_MD4, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_MD4, 128, },
153 { /*1.2.840.113549.2.5*/ RTCRX509ALGORITHMIDENTIFIERID_MD5, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_MD5, 128, },
154
155 /* oddballs for which we don't support the padding (skip?): */
156 //{ "1.3.14.3.2.11" /*rsaSignature*/, IDX_ENCRYPTION_RSA/*?*/, RTDIGESTTYPE_INVALID, 0, },
157 { "1.3.14.3.2.14" /*mdc2WithRSASignature w/ 9796-2 padding*/, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_INVALID, 0, },
158 //{ "1.3.14.3.2.15" /*sha0WithRSASignature w/ 9796-2 padding*/, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_INVALID, 160, },
159 { "1.3.14.3.2.24" /*md2WithRSASignature w/ 9796-2 padding*/, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_MD2, 128, },
160 { "1.3.14.3.2.25" /*md5WithRSASignature w/ 9796-2 padding*/, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_MD5, 128, },
161 { /*1.3.14.3.2.26*/ RTCRX509ALGORITHMIDENTIFIERID_SHA1, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA1, 160, },
162 { "1.3.14.3.2.29" /*sha1WithRSAEncryption (obsolete?)*/, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA1, 160, },
163
164 { /*2.16.840.1.101.3.4.2.1*/ RTCRX509ALGORITHMIDENTIFIERID_SHA256, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA256, 256, },
165 { /*2.16.840.1.101.3.4.2.10*/ RTCRX509ALGORITHMIDENTIFIERID_SHA3_512, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA3_512, 512, },
166 { /*2.16.840.1.101.3.4.2.2*/ RTCRX509ALGORITHMIDENTIFIERID_SHA384, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA384, 384, },
167 { /*2.16.840.1.101.3.4.2.3*/ RTCRX509ALGORITHMIDENTIFIERID_SHA512, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA512, 512, },
168 { /*2.16.840.1.101.3.4.2.4*/ RTCRX509ALGORITHMIDENTIFIERID_SHA224, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA224, 224, },
169 { /*2.16.840.1.101.3.4.2.5*/ RTCRX509ALGORITHMIDENTIFIERID_SHA512T224,IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA512T224, 224, },
170 { /*2.16.840.1.101.3.4.2.6*/ RTCRX509ALGORITHMIDENTIFIERID_SHA512T256,IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA512T256, 256, },
171 { /*2.16.840.1.101.3.4.2.7*/ RTCRX509ALGORITHMIDENTIFIERID_SHA3_224, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA3_224, 224, },
172 { /*2.16.840.1.101.3.4.2.8*/ RTCRX509ALGORITHMIDENTIFIERID_SHA3_256, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA3_256, 256, },
173 { /*2.16.840.1.101.3.4.2.9*/ RTCRX509ALGORITHMIDENTIFIERID_SHA3_384, IDX_ENCRYPTION_NIL, RTDIGESTTYPE_SHA3_384, 384, },
174
175 { /*2.16.840.1.101.3.4.3.10*/ RTCR_NIST_SHA3_256_WITH_ECDSA_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA3_256, 256, },
176 { /*2.16.840.1.101.3.4.3.11*/ RTCR_NIST_SHA3_384_WITH_ECDSA_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA3_384, 384, },
177 { /*2.16.840.1.101.3.4.3.12*/ RTCR_NIST_SHA3_512_WITH_ECDSA_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA3_512, 512, },
178 { /*2.16.840.1.101.3.4.3.13*/ RTCR_NIST_SHA3_224_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA3_224, 224, },
179 { /*2.16.840.1.101.3.4.3.14*/ RTCR_NIST_SHA3_256_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA3_256, 256, },
180 { /*2.16.840.1.101.3.4.3.15*/ RTCR_NIST_SHA3_384_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA3_384, 384, },
181 { /*2.16.840.1.101.3.4.3.16*/ RTCR_NIST_SHA3_512_WITH_RSA_OID, IDX_ENCRYPTION_RSA, RTDIGESTTYPE_SHA3_512, 512, },
182 { /*2.16.840.1.101.3.4.3.9*/ RTCR_NIST_SHA3_224_WITH_ECDSA_OID, IDX_ENCRYPTION_ECDSA, RTDIGESTTYPE_SHA3_224, 224, },
183};
184
185/**
186 * Encryption and digest combining.
187 *
188 * This is a subset of g_aSignatureOidInfo.
189 * @todo Organize this more efficiently...
190 */
191typedef struct RTCRX509ALGORITHIDENTIFIERCOMBINING
192{
193 const char *pszDigestOid;
194 const char *pszEncryptedDigestOid;
195} RTCRX509ALGORITHIDENTIFIERCOMBINING;
196typedef RTCRX509ALGORITHIDENTIFIERCOMBINING const *PCRTCRX509ALGORITHIDENTIFIERCOMBINING;
197
198#undef MY_COMBINE
199#define MY_COMBINE(a_Encrypt, a_Digest) \
200 { RTCRX509ALGORITHMIDENTIFIERID_ ## a_Digest, \
201 RTCRX509ALGORITHMIDENTIFIERID_ ## a_Digest ## _WITH_ ## a_Encrypt }
202
203/** Digest and encryption combinations for ECDSA. */
204static RTCRX509ALGORITHIDENTIFIERCOMBINING const g_aDigestAndEncryptionEcdsa[] =
205{
206 MY_COMBINE(ECDSA, SHA1),
207 MY_COMBINE(ECDSA, SHA224),
208 MY_COMBINE(ECDSA, SHA256),
209 MY_COMBINE(ECDSA, SHA384),
210 MY_COMBINE(ECDSA, SHA512),
211 MY_COMBINE(ECDSA, SHA3_224),
212 MY_COMBINE(ECDSA, SHA3_256),
213 MY_COMBINE(ECDSA, SHA3_384),
214 MY_COMBINE(ECDSA, SHA3_512),
215};
216
217/** Digest and encryption combinations for RSA. */
218static RTCRX509ALGORITHIDENTIFIERCOMBINING const g_aDigestAndEncryptionRsa[] =
219{
220 MY_COMBINE(RSA, SHA1),
221 MY_COMBINE(RSA, SHA256),
222 MY_COMBINE(RSA, SHA512),
223 MY_COMBINE(RSA, SHA384),
224 MY_COMBINE(RSA, MD5),
225 MY_COMBINE(RSA, MD2),
226 MY_COMBINE(RSA, MD4),
227 MY_COMBINE(RSA, SHA224),
228 MY_COMBINE(RSA, SHA512T224),
229 MY_COMBINE(RSA, SHA512T256),
230 MY_COMBINE(RSA, SHA3_224),
231 MY_COMBINE(RSA, SHA3_256),
232 MY_COMBINE(RSA, SHA3_384),
233 MY_COMBINE(RSA, SHA3_512),
234};
235
236#undef MY_COMBINE
237
238/**
239 * Table running parallel to g_apszEncryptionOids.
240 */
241static struct
242{
243 PCRTCRX509ALGORITHIDENTIFIERCOMBINING paCombinations;
244 size_t cCombinations;
245} const g_aDigestAndEncryption[] =
246{
247 /* [IDX_ENCRYPTION_NIL] = */ { NULL, 0 },
248 /* [IDX_ENCRYPTION_ECDSA] = */ { &g_aDigestAndEncryptionEcdsa[0], RT_ELEMENTS(g_aDigestAndEncryptionEcdsa) },
249 /* [IDX_ENCRYPTION_RSA] = */ { &g_aDigestAndEncryptionRsa[0], RT_ELEMENTS(g_aDigestAndEncryptionRsa) },
250};
251AssertCompile(IDX_ENCRYPTION_NIL == 0 && IDX_ENCRYPTION_ECDSA == 1 && IDX_ENCRYPTION_RSA == 2);
252
253
254/**
255 * Looks up info we've got on a algorithm identifier.
256 */
257static PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO rtCrX509AlgorithmIdentifier_LookupInfoByOid(const char *pszSignatureOid)
258{
259#ifdef RT_STRICT
260 /*
261 * Do internal santiy checking on first call.
262 */
263 static bool volatile s_fChecked = false;
264 if (RT_LIKELY(s_fChecked))
265 { /* likely */ }
266 else
267 {
268 s_fChecked = true; /* Must be set before the call, as the callee will calls us again. */
269 rtCrX509AlgorithmIdentifier_AssertTableSanityAndMore();
270 }
271#endif
272
273 /*
274 * Do a binary search of g_aSignatureOidInfo.
275 */
276 size_t iFirst = 0;
277 size_t iEnd = RT_ELEMENTS(g_aSignatureOidInfo);
278 for (;;)
279 {
280 size_t const i = iFirst + (iEnd - iFirst) / 2;
281 int const iDiff = strcmp(pszSignatureOid, g_aSignatureOidInfo[i].pszSignatureOid);
282 if (iDiff < 0)
283 {
284 if (i > iFirst)
285 iEnd = i;
286 else
287 return NULL;
288 }
289 else if (iDiff > 0)
290 {
291 if (i + 1 < iEnd)
292 iFirst = i + 1;
293 else
294 return NULL;
295 }
296 else
297 return &g_aSignatureOidInfo[i];
298 }
299}
300
301#ifdef RT_STRICT
302/**
303 * Check that the g_aSignatureOidInfo and g_aDigestAndEncryption makes sense and
304 * matches up with one another and other IPRT information sources.
305 */
306static void rtCrX509AlgorithmIdentifier_AssertTableSanityAndMore(void)
307{
308 /* Check that binary searching work and that digest info matches up: */
309 for (size_t i = 1; i < RT_ELEMENTS(g_aSignatureOidInfo); i++)
310 Assert(strcmp(g_aSignatureOidInfo[i].pszSignatureOid, g_aSignatureOidInfo[i - 1].pszSignatureOid) > 0);
311 for (size_t i = 0; i < RT_ELEMENTS(g_aSignatureOidInfo); i++)
312 {
313 PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pInfo
314 = rtCrX509AlgorithmIdentifier_LookupInfoByOid(g_aSignatureOidInfo[i].pszSignatureOid);
315 Assert(pInfo && pInfo->pszSignatureOid == g_aSignatureOidInfo[i].pszSignatureOid);
316
317 /* If the digest type is RTDIGESTTYPE_INVALID, we must have an pure encryption entry or an obscure hash function. */
318 if (g_aSignatureOidInfo[i].enmDigestType != RTDIGESTTYPE_INVALID)
319 Assert( RTCrDigestTypeToHashSize((RTDIGESTTYPE)g_aSignatureOidInfo[i].enmDigestType) * 8
320 == g_aSignatureOidInfo[i].cBitsDigest);
321 else
322 Assert(g_aSignatureOidInfo[i].cBitsDigest == 0 || g_aSignatureOidInfo[i].idxEncryption == IDX_ENCRYPTION_NIL);
323
324# ifdef IN_RING3
325 /* Check with the RTCrDigestFindByObjIdString API: */
326 RTDIGESTTYPE enmDigestType2 = (RTDIGESTTYPE)g_aSignatureOidInfo[i].enmDigestType;
327# if defined(IPRT_WITHOUT_DIGEST_MD2) || defined(IPRT_WITHOUT_DIGEST_MD4) || defined(IPRT_WITHOUT_DIGEST_MD5) \
328|| defined(IPRT_WITHOUT_SHA512T224) || defined(IPRT_WITHOUT_SHA512T256) || defined(IPRT_WITHOUT_SHA3)
329 switch (enmDigestType2)
330 {
331 default: break;
332# ifdef IPRT_WITHOUT_DIGEST_MD2
333 case RTDIGESTTYPE_MD2:
334# endif
335# ifdef IPRT_WITHOUT_DIGEST_MD4
336 case RTDIGESTTYPE_MD4:
337# endif
338# ifdef IPRT_WITHOUT_DIGEST_MD5
339 case RTDIGESTTYPE_MD5:
340# endif
341# ifdef IPRT_WITHOUT_SHA512T224
342 case RTDIGESTTYPE_SHA512T224:
343# endif
344# ifdef IPRT_WITHOUT_SHA512T256
345 case RTDIGESTTYPE_SHA512T256:
346# endif
347# ifdef IPRT_WITHOUT_SHA3
348 case RTDIGESTTYPE_SHA3_224:
349 case RTDIGESTTYPE_SHA3_256:
350 case RTDIGESTTYPE_SHA3_384:
351 case RTDIGESTTYPE_SHA3_512:
352# endif
353 enmDigestType2 = RTDIGESTTYPE_INVALID;
354 break;
355 }
356# endif
357 PCRTCRDIGESTDESC const pDigestDesc = RTCrDigestFindByObjIdString(g_aSignatureOidInfo[i].pszSignatureOid,
358 NULL /*ppvOpaque*/);
359 if (pDigestDesc)
360 {
361 AssertMsg(pDigestDesc->enmType == enmDigestType2,
362 ("%s pDigestDesc=%s enmDigestType2=%s\n", g_aSignatureOidInfo[i].pszSignatureOid,
363 RTCrDigestTypeToName(pDigestDesc->enmType), RTCrDigestTypeToName(enmDigestType2)));
364 Assert(pDigestDesc->cbHash * 8 == g_aSignatureOidInfo[i].cBitsDigest);
365 }
366 else
367 AssertMsg(enmDigestType2 == RTDIGESTTYPE_INVALID,
368 ("%s enmDigestType2=%s\n", g_aSignatureOidInfo[i].pszSignatureOid, RTCrDigestTypeToName(enmDigestType2)));
369# endif /* IN_RING3 */
370
371
372# ifdef IN_RING3
373 /* Look it up the encryption descriptor. */
374 const char * const pszCheckEncryptId = g_apszEncryptionOids[g_aSignatureOidInfo[i].idxEncryption];
375 PCRTCRPKIXSIGNATUREDESC const pSigDesc = RTCrPkixSignatureFindByObjIdString(g_aSignatureOidInfo[i].pszSignatureOid,
376 NULL /*ppvOpaque*/);
377 if (pSigDesc)
378 Assert(pszCheckEncryptId && strcmp(pSigDesc->pszObjId, pszCheckEncryptId) == 0);
379# ifdef IPRT_WITH_OPENSSL /* No ECDSA implementation w/o OpenSSL at the moment. */
380 else
381 AssertMsg(!pSigDesc && pInfo->idxEncryption == IDX_ENCRYPTION_NIL, ("%s\n", g_aSignatureOidInfo[i].pszSignatureOid));
382# endif
383# endif /* IN_RING3 */
384 }
385
386 /*
387 * Check that everything in g_aDigestAndEncryption is resolvable here and that the info matches up.
388 */
389 for (size_t idxEncryption = IDX_ENCRYPTION_NIL; idxEncryption < RT_ELEMENTS(g_aDigestAndEncryption); idxEncryption++)
390 {
391 PCRTCRX509ALGORITHIDENTIFIERCOMBINING const paCombinations = g_aDigestAndEncryption[idxEncryption].paCombinations;
392 size_t const cCombinations = g_aDigestAndEncryption[idxEncryption].cCombinations;
393 for (size_t i = 0; i < cCombinations; i++)
394 {
395 PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pInfo
396 = rtCrX509AlgorithmIdentifier_LookupInfoByOid(paCombinations[i].pszEncryptedDigestOid);
397 AssertContinue(pInfo);
398 Assert(strcmp(paCombinations[i].pszEncryptedDigestOid, pInfo->pszSignatureOid) == 0);
399 Assert(pInfo->idxEncryption == idxEncryption);
400 Assert(strcmp(paCombinations[i].pszDigestOid,
401 RTCrDigestTypeToAlgorithmOid((RTDIGESTTYPE)pInfo->enmDigestType)) == 0);
402 }
403 }
404}
405#endif /* RT_STRICT */
406
407
408RTDECL(RTDIGESTTYPE) RTCrX509AlgorithmIdentifier_GetDigestType(PCRTCRX509ALGORITHMIDENTIFIER pThis, bool fPureDigestsOnly)
409{
410 AssertPtrReturn(pThis, RTDIGESTTYPE_INVALID);
411 PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pInfo = rtCrX509AlgorithmIdentifier_LookupInfoByOid(pThis->Algorithm.szObjId);
412 return pInfo && (!fPureDigestsOnly || pInfo->idxEncryption == IDX_ENCRYPTION_NIL)
413 ? (RTDIGESTTYPE)pInfo->enmDigestType : RTDIGESTTYPE_INVALID;
414}
415
416
417RTDECL(uint32_t) RTCrX509AlgorithmIdentifier_GetDigestSize(PCRTCRX509ALGORITHMIDENTIFIER pThis, bool fPureDigestsOnly)
418{
419 AssertPtrReturn(pThis, UINT32_MAX);
420 PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pInfo = rtCrX509AlgorithmIdentifier_LookupInfoByOid(pThis->Algorithm.szObjId);
421 return pInfo && (!fPureDigestsOnly || pInfo->idxEncryption == IDX_ENCRYPTION_NIL)
422 ? pInfo->cBitsDigest / 8 : UINT32_MAX;
423}
424
425
426RTDECL(int) RTCrX509AlgorithmIdentifier_CompareWithString(PCRTCRX509ALGORITHMIDENTIFIER pThis, const char *pszObjId)
427{
428 return strcmp(pThis->Algorithm.szObjId, pszObjId);
429}
430
431
432RTDECL(int) RTCrX509AlgorithmIdentifier_CompareDigestOidAndEncryptedDigestOid(const char *pszDigestOid,
433 const char *pszEncryptedDigestOid)
434{
435 /*
436 * Lookup the digest and encrypted digest OIDs.
437 */
438 PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pDigest = rtCrX509AlgorithmIdentifier_LookupInfoByOid(pszDigestOid);
439 AssertMsgReturn(pDigest, ("pszDigestOid=%s\n", pszDigestOid), -1);
440 AssertMsgReturn(pDigest->idxEncryption == IDX_ENCRYPTION_NIL, ("pszDigestOid=%s\n", pszDigestOid), -1);
441 AssertMsgReturn(pDigest->cBitsDigest != 0, ("pszDigestOid=%s\n", pszDigestOid), -1);
442
443 PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pEncrypt = rtCrX509AlgorithmIdentifier_LookupInfoByOid(pszEncryptedDigestOid);
444 AssertMsgReturn(pEncrypt, ("pszEncryptedDigestOid=%s\n", pszEncryptedDigestOid), 1);
445 AssertMsgReturn(pEncrypt->idxEncryption != IDX_ENCRYPTION_NIL, ("pszEncryptedDigestOid=%s\n", pszEncryptedDigestOid), 1);
446 AssertMsgReturn(pEncrypt->enmDigestType != RTDIGESTTYPE_INVALID, ("pszEncryptedDigestOid=%s\n", pszEncryptedDigestOid), 1);
447
448 return pDigest->enmDigestType == pEncrypt->enmDigestType ? 0 : 1;
449}
450
451RTDECL(int) RTCrX509AlgorithmIdentifier_CompareDigestAndEncryptedDigest(PCRTCRX509ALGORITHMIDENTIFIER pDigest,
452 PCRTCRX509ALGORITHMIDENTIFIER pEncryptedDigest)
453{
454 return RTCrX509AlgorithmIdentifier_CompareDigestOidAndEncryptedDigestOid(pDigest->Algorithm.szObjId,
455 pEncryptedDigest->Algorithm.szObjId);
456}
457
458
459RTDECL(const char *) RTCrX509AlgorithmIdentifier_CombineEncryptionOidAndDigestOid(const char *pszEncryptionOid,
460 const char *pszDigestOid)
461{
462 /*
463 * We can look up the two OIDs and see what they actually are.
464 */
465 /* The digest OID should be a pure hash algorithm, however we also accept
466 the already combined algorithm. */
467 PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pDigest = rtCrX509AlgorithmIdentifier_LookupInfoByOid(pszDigestOid);
468 AssertReturn(pDigest, NULL);
469 AssertReturn(pDigest->enmDigestType != RTDIGESTTYPE_INVALID, NULL);
470
471 /* The encryption OID should be a pure encryption algorithm, however we
472 also accept the already combined algorithm. */
473 PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pEncrypt = rtCrX509AlgorithmIdentifier_LookupInfoByOid(pszEncryptionOid);
474 AssertReturn(pEncrypt, NULL);
475 uint8_t const idxEncryption = pEncrypt->idxEncryption;
476 AssertReturn(idxEncryption != IDX_ENCRYPTION_NIL, NULL);
477 Assert(idxEncryption < RT_ELEMENTS(g_aDigestAndEncryption));
478
479 /* Is the encryption OID purely encryption? */
480 if (pEncrypt->cBitsDigest == 0)
481 {
482 Assert(pEncrypt->enmDigestType == RTDIGESTTYPE_INVALID);
483
484 /* Identify the slice of the table related to this encryption OID: */
485 PCRTCRX509ALGORITHIDENTIFIERCOMBINING const paCombinations = g_aDigestAndEncryption[idxEncryption].paCombinations;
486 size_t const cCombinations = g_aDigestAndEncryption[idxEncryption].cCombinations;
487
488 /* Is the digest OID purely a digest? */
489 if (pDigest->idxEncryption == IDX_ENCRYPTION_NIL)
490 {
491 for (size_t i = 0; i < cCombinations; i++)
492 if (!strcmp(pszDigestOid, paCombinations[i].pszDigestOid))
493 return paCombinations[i].pszEncryptedDigestOid;
494 AssertMsgFailed(("enc=%s hash=%s\n", pszEncryptionOid, pszDigestOid));
495 }
496 else
497 {
498 /* No, it's a combined one. */
499 for (size_t i = 0; i < cCombinations; i++)
500 if (!strcmp(pszDigestOid, paCombinations[i].pszEncryptedDigestOid))
501 return paCombinations[i].pszEncryptedDigestOid;
502 AssertMsgFailed(("enc=%s hash+enc=%s\n", pszEncryptionOid, pszDigestOid));
503 }
504 }
505 /* The digest OID purely a digest? */
506 else if (pDigest->idxEncryption == IDX_ENCRYPTION_NIL)
507 {
508 /* Check that it's for the same hash before returning it. */
509 Assert(pEncrypt->enmDigestType != RTDIGESTTYPE_INVALID);
510 if (pEncrypt->enmDigestType == pDigest->enmDigestType)
511 return pEncrypt->pszSignatureOid;
512 AssertMsgFailed(("enc+hash=%s hash=%s\n", pszEncryptionOid, pszDigestOid));
513 }
514 /* Both the digest and encryption OIDs are combined ones, so they have to
515 be the same entry then or they cannot be combined. */
516 else if (pDigest == pEncrypt)
517 return pEncrypt->pszSignatureOid;
518 else
519 AssertMsgFailed(("enc+hash=%s hash+enc=%s\n", pszEncryptionOid, pszDigestOid));
520
521 return NULL;
522}
523
524
525RTDECL(const char *) RTCrX509AlgorithmIdentifier_CombineEncryptionAndDigest(PCRTCRX509ALGORITHMIDENTIFIER pEncryption,
526 PCRTCRX509ALGORITHMIDENTIFIER pDigest)
527{
528 return RTCrX509AlgorithmIdentifier_CombineEncryptionOidAndDigestOid(pEncryption->Algorithm.szObjId,
529 pDigest->Algorithm.szObjId);
530}
531
532
533RTDECL(const char *) RTCrX509AlgorithmIdentifier_GetEncryptionOid(PCRTCRX509ALGORITHMIDENTIFIER pThis, bool fMustIncludeHash)
534{
535 AssertPtrReturn(pThis, NULL);
536 PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pInfo = rtCrX509AlgorithmIdentifier_LookupInfoByOid(pThis->Algorithm.szObjId);
537 if (pInfo && (!fMustIncludeHash || pInfo->enmDigestType != RTDIGESTTYPE_INVALID))
538 return g_apszEncryptionOids[pInfo->idxEncryption];
539 return NULL;
540}
541
542
543RTDECL(const char *) RTCrX509AlgorithmIdentifier_GetEncryptionOidFromOid(const char *pszAlgorithmOid, bool fMustIncludeHash)
544{
545 AssertPtrReturn(pszAlgorithmOid, NULL);
546 PCRTCRX509ALGORITHIDENTIFIERINTERNALINFO const pInfo = rtCrX509AlgorithmIdentifier_LookupInfoByOid(pszAlgorithmOid);
547 if (pInfo && (!fMustIncludeHash || pInfo->enmDigestType != RTDIGESTTYPE_INVALID))
548 return g_apszEncryptionOids[pInfo->idxEncryption];
549 return NULL;
550}
551
552
553/*
554 * Set of X.509 Algorithm Identifiers.
555 */
556
557
558/*
559 * One X.509 AttributeTypeAndValue.
560 */
561
562
563/*
564 * Set of X.509 AttributeTypeAndValues / X.509 RelativeDistinguishedName.
565 */
566
567/**
568 * Slow code path of rtCrX509CanNameIsNothing.
569 *
570 * @returns true if @uc maps to nothing, false if not.
571 * @param uc The unicode code point.
572 */
573static bool rtCrX509CanNameIsNothingSlow(RTUNICP uc)
574{
575 switch (uc)
576 {
577 /* 2.2 Map - Paragraph 1: */
578 case 0x00ad:
579 case 0x1806:
580 case 0x034f:
581 case 0x180b: case 0x180c: case 0x180d:
582
583 case 0xfe00: case 0xfe01: case 0xfe02: case 0xfe03:
584 case 0xfe04: case 0xfe05: case 0xfe06: case 0xfe07:
585 case 0xfe08: case 0xfe09: case 0xfe0a: case 0xfe0b:
586 case 0xfe0c: case 0xfe0d: case 0xfe0e: case 0xfe0f:
587
588 case 0xfffc:
589
590 /* 2.2 Map - Paragraph 3 (control code/function): */
591 case 0x0000: case 0x0001: case 0x0002: case 0x0003:
592 case 0x0004: case 0x0005: case 0x0006: case 0x0007:
593 case 0x0008:
594
595 case 0x000e: case 0x000f:
596 case 0x0010: case 0x0011: case 0x0012: case 0x0013:
597 case 0x0014: case 0x0015: case 0x0016: case 0x0017:
598 case 0x0018: case 0x0019: case 0x001a: case 0x001b:
599 case 0x001c: case 0x001d: case 0x001e: case 0x001f:
600
601 case 0x007f:
602 case 0x0080: case 0x0081: case 0x0082: case 0x0083:
603 case 0x0084: /*case 0x0085:*/ case 0x0086: case 0x0087:
604 case 0x0088: case 0x0089: case 0x008a: case 0x008b:
605 case 0x008c: case 0x008d: case 0x008e: case 0x008f:
606 case 0x0090: case 0x0091: case 0x0092: case 0x0093:
607 case 0x0094: case 0x0095: case 0x0096: case 0x0097:
608 case 0x0098: case 0x0099: case 0x009a: case 0x009b:
609 case 0x009c: case 0x009d: case 0x009e: case 0x009f:
610
611 case 0x06dd:
612 case 0x070f:
613 case 0x180e:
614 case 0x200c: case 0x200d: case 0x200e: case 0x200f:
615 case 0x202a: case 0x202b: case 0x202c: case 0x202d: case 0x202e:
616 case 0x2060: case 0x2061: case 0x2062: case 0x2063:
617 case 0x206a: case 0x206b: case 0x206c: case 0x206d: case 0x206e: case 0x206f:
618 case 0xfeff:
619 case 0xfff9: case 0xfffa: case 0xfffb:
620 case 0x1d173: case 0x1d174: case 0x1d175: case 0x1d176: case 0x1d177: case 0x1d178: case 0x1d179: case 0x1d17a:
621 case 0xe0001:
622 case 0xe0020: case 0xe0021: case 0xe0022: case 0xe0023:
623 case 0xe0024: case 0xe0025: case 0xe0026: case 0xe0027:
624 case 0xe0028: case 0xe0029: case 0xe002a: case 0xe002b:
625 case 0xe002c: case 0xe002d: case 0xe002e: case 0xe002f:
626 case 0xe0030: case 0xe0031: case 0xe0032: case 0xe0033:
627 case 0xe0034: case 0xe0035: case 0xe0036: case 0xe0037:
628 case 0xe0038: case 0xe0039: case 0xe003a: case 0xe003b:
629 case 0xe003c: case 0xe003d: case 0xe003e: case 0xe003f:
630 case 0xe0040: case 0xe0041: case 0xe0042: case 0xe0043:
631 case 0xe0044: case 0xe0045: case 0xe0046: case 0xe0047:
632 case 0xe0048: case 0xe0049: case 0xe004a: case 0xe004b:
633 case 0xe004c: case 0xe004d: case 0xe004e: case 0xe004f:
634 case 0xe0050: case 0xe0051: case 0xe0052: case 0xe0053:
635 case 0xe0054: case 0xe0055: case 0xe0056: case 0xe0057:
636 case 0xe0058: case 0xe0059: case 0xe005a: case 0xe005b:
637 case 0xe005c: case 0xe005d: case 0xe005e: case 0xe005f:
638 case 0xe0060: case 0xe0061: case 0xe0062: case 0xe0063:
639 case 0xe0064: case 0xe0065: case 0xe0066: case 0xe0067:
640 case 0xe0068: case 0xe0069: case 0xe006a: case 0xe006b:
641 case 0xe006c: case 0xe006d: case 0xe006e: case 0xe006f:
642 case 0xe0070: case 0xe0071: case 0xe0072: case 0xe0073:
643 case 0xe0074: case 0xe0075: case 0xe0076: case 0xe0077:
644 case 0xe0078: case 0xe0079: case 0xe007a: case 0xe007b:
645 case 0xe007c: case 0xe007d: case 0xe007e: case 0xe007f:
646
647 /* 2.2 Map - Paragraph 4. */
648 case 0x200b:
649 return true;
650 }
651 return false;
652}
653
654
655/**
656 * Checks if @a uc maps to nothing according to mapping rules of RFC-5280 and
657 * RFC-4518.
658 *
659 * @returns true if @uc maps to nothing, false if not.
660 * @param uc The unicode code point.
661 */
662DECLINLINE(bool) rtCrX509CanNameIsNothing(RTUNICP uc)
663{
664 if (uc > 0x001f && uc < 0x00ad)
665 return false;
666 return rtCrX509CanNameIsNothingSlow(uc);
667}
668
669
670/**
671 * Slow code path of rtCrX509CanNameIsSpace.
672 *
673 * @returns true if space, false if not.
674 * @param uc The unicode code point.
675 */
676static bool rtCrX509CanNameIsSpaceSlow(RTUNICP uc)
677{
678 switch (uc)
679 {
680 /* 2.2 Map - Paragraph 2. */
681 case 0x09:
682 case 0x0a:
683 case 0x0b:
684 case 0x0c:
685 case 0x0d:
686 case 0x20:
687 case 0x0085:
688 case 0x00a0:
689 case 0x1680:
690 case 0x2000: case 0x2001: case 0x2002: case 0x2003:
691 case 0x2004: case 0x2005: case 0x2006: case 0x2007:
692 case 0x2008: case 0x2009: case 0x200a:
693 case 0x2028: case 0x2029:
694 case 0x202f:
695 case 0x205f:
696 case 0x3000:
697 return true;
698 }
699 return false;
700}
701
702
703/**
704 * Checks if @a uc is a space character according to the mapping rules of
705 * RFC-5280 and RFC-4518.
706 *
707 * @returns true if space, false if not.
708 * @param uc The unicode code point.
709 */
710DECLINLINE(bool) rtCrX509CanNameIsSpace(RTUNICP uc)
711{
712 if (uc < 0x0085)
713 {
714 if (uc > 0x0020)
715 return false;
716 if (uc == 0x0020) /* space */
717 return true;
718 }
719 return rtCrX509CanNameIsSpaceSlow(uc);
720}
721
722
723static const char *rtCrX509CanNameStripLeft(const char *psz, size_t *pcch)
724{
725 /*
726 * Return space when we've encountered the first non-space-non-nothing code point.
727 */
728 const char * const pszStart = psz;
729 const char *pszPrev;
730 for (;;)
731 {
732 pszPrev = psz;
733 RTUNICP uc;
734 int rc = RTStrGetCpEx(&psz, &uc);
735 AssertRCBreak(rc);
736 if (!uc)
737 {
738 if ((uintptr_t)(pszPrev - pszStart) >= *pcch)
739 break;
740 /* NUL inside the string, maps to nothing => ignore it. */
741 }
742 else if (!rtCrX509CanNameIsSpace(uc) && !rtCrX509CanNameIsNothing(uc))
743 break;
744 }
745 *pcch -= (size_t)(pszPrev - pszStart);
746 return pszPrev;
747}
748
749
750static RTUNICP rtCrX509CanNameGetNextCpWithMappingSlowSpace(const char **ppsz, size_t *pcch)
751{
752 /*
753 * Return space when we've encountered the first non-space-non-nothing code point.
754 */
755 RTUNICP uc;
756 const char *psz = *ppsz;
757 const char * const pszStart = psz;
758 const char *pszPrev;
759 for (;;)
760 {
761 pszPrev = psz;
762 int rc = RTStrGetCpEx(&psz, &uc);
763 AssertRCBreakStmt(rc, uc = 0x20);
764 if (!uc)
765 {
766 if ((uintptr_t)(pszPrev - pszStart) >= *pcch)
767 {
768 uc = 0; /* End of string: Ignore trailing spaces. */
769 break;
770 }
771 /* NUL inside the string, maps to nothing => ignore it. */
772 }
773 else if (!rtCrX509CanNameIsSpace(uc) && !rtCrX509CanNameIsNothing(uc))
774 {
775 uc = 0x20; /* Return space before current char. */
776 break;
777 }
778 }
779
780 *ppsz = pszPrev;
781 *pcch -= (size_t)(pszPrev - pszStart);
782 return uc;
783}
784
785
786DECLINLINE(RTUNICP) rtCrX509CanNameGetNextCpIgnoreNul(const char **ppsz, size_t *pcch)
787{
788 while (*pcch > 0)
789 {
790 const char *psz = *ppsz;
791 RTUNICP uc = (RTUNICP)*psz;
792 if (uc < 0x80)
793 {
794 *pcch -= 1;
795 *ppsz = psz + 1;
796 }
797 else
798 {
799 int rc = RTStrGetCpEx(ppsz, &uc);
800 AssertRCReturn(rc, uc);
801 size_t cchCp = (size_t)(*ppsz - psz);
802 AssertReturn(cchCp <= *pcch, 0);
803 *pcch -= cchCp;
804 }
805 if (uc != 0)
806 return uc;
807 }
808 return 0;
809}
810
811
812static RTUNICP rtCrX509CanNameGetNextCpWithMappingSlowNothing(const char **ppsz, size_t *pcch)
813{
814 /*
815 * Return first code point which doesn't map to nothing. If we encounter
816 * a space, we defer to the mapping-after-space routine above.
817 */
818 for (;;)
819 {
820 RTUNICP uc = rtCrX509CanNameGetNextCpIgnoreNul(ppsz, pcch);
821 if (rtCrX509CanNameIsSpace(uc))
822 return rtCrX509CanNameGetNextCpWithMappingSlowSpace(ppsz, pcch);
823 if (!rtCrX509CanNameIsNothing(uc) || uc == 0)
824 return uc;
825 }
826}
827
828
829DECLINLINE(RTUNICP) rtCrX509CanNameGetNextCpWithMapping(const char **ppsz, size_t *pcch)
830{
831 RTUNICP uc = rtCrX509CanNameGetNextCpIgnoreNul(ppsz, pcch);
832 if (uc)
833 {
834 if (!rtCrX509CanNameIsSpace(uc))
835 {
836 if (!rtCrX509CanNameIsNothing(uc))
837 return uc;
838 return rtCrX509CanNameGetNextCpWithMappingSlowNothing(ppsz, pcch);
839 }
840 return rtCrX509CanNameGetNextCpWithMappingSlowSpace(ppsz, pcch);
841 }
842 return uc;
843}
844
845
846RTDECL(bool) RTCrX509AttributeTypeAndValue_MatchAsRdnByRfc5280(PCRTCRX509ATTRIBUTETYPEANDVALUE pLeft,
847 PCRTCRX509ATTRIBUTETYPEANDVALUE pRight)
848{
849 if (RTAsn1ObjId_Compare(&pLeft->Type, &pRight->Type) == 0)
850 {
851 /*
852 * Try for perfect match in case we get luck.
853 */
854#ifdef DEBUG_bird /* Want to test the complicated code path first */
855 if (pLeft->Value.enmType != RTASN1TYPE_STRING || pRight->Value.enmType != RTASN1TYPE_STRING)
856#endif
857 if (RTAsn1DynType_Compare(&pLeft->Value, &pRight->Value) == 0)
858 return true;
859
860 /*
861 * If both are string types, we can compare them according to RFC-5280.
862 */
863 if ( pLeft->Value.enmType == RTASN1TYPE_STRING
864 && pRight->Value.enmType == RTASN1TYPE_STRING)
865 {
866 size_t cchLeft;
867 const char *pszLeft;
868 int rc = RTAsn1String_QueryUtf8(&pLeft->Value.u.String, &pszLeft, &cchLeft);
869 if (RT_SUCCESS(rc))
870 {
871 size_t cchRight;
872 const char *pszRight;
873 rc = RTAsn1String_QueryUtf8(&pRight->Value.u.String, &pszRight, &cchRight);
874 if (RT_SUCCESS(rc))
875 {
876 /*
877 * Perform a simplified RFC-5280 comparsion.
878 * The algorithm as be relaxed on the following counts:
879 * 1. No unicode normalization.
880 * 2. Prohibited characters not checked for.
881 * 3. Bidirectional characters are not ignored.
882 */
883 pszLeft = rtCrX509CanNameStripLeft(pszLeft, &cchLeft);
884 pszRight = rtCrX509CanNameStripLeft(pszRight, &cchRight);
885 while (*pszLeft && *pszRight)
886 {
887 RTUNICP ucLeft = rtCrX509CanNameGetNextCpWithMapping(&pszLeft, &cchLeft);
888 RTUNICP ucRight = rtCrX509CanNameGetNextCpWithMapping(&pszRight, &cchRight);
889 if (ucLeft != ucRight)
890 {
891 ucLeft = RTUniCpToLower(ucLeft);
892 ucRight = RTUniCpToLower(ucRight);
893 if (ucLeft != ucRight)
894 return false;
895 }
896 }
897
898 return cchRight == 0 && cchLeft == 0;
899 }
900 }
901 }
902 }
903 return false;
904}
905
906
907RTDECL(bool) RTCrX509RelativeDistinguishedName_MatchByRfc5280(PCRTCRX509RELATIVEDISTINGUISHEDNAME pLeft,
908 PCRTCRX509RELATIVEDISTINGUISHEDNAME pRight)
909{
910 /*
911 * No match if the attribute count differs.
912 */
913 uint32_t const cItems = pLeft->cItems;
914 if (cItems == pRight->cItems)
915 {
916 /*
917 * Compare each attribute, but don't insist on the same order nor
918 * bother checking for duplicates (too complicated).
919 */
920 for (uint32_t iLeft = 0; iLeft < cItems; iLeft++)
921 {
922 PCRTCRX509ATTRIBUTETYPEANDVALUE pLeftAttr = pLeft->papItems[iLeft];
923 bool fFound = false;
924 for (uint32_t iRight = 0; iRight < cItems; iRight++)
925 if (RTCrX509AttributeTypeAndValue_MatchAsRdnByRfc5280(pLeftAttr, pRight->papItems[iRight]))
926 {
927 fFound = true;
928 break;
929 }
930 if (!fFound)
931 return false;
932 }
933 return true;
934 }
935 return false;
936
937}
938
939
940/*
941 * X.509 Name.
942 */
943
944RTDECL(bool) RTCrX509Name_MatchByRfc5280(PCRTCRX509NAME pLeft, PCRTCRX509NAME pRight)
945{
946 uint32_t const cItems = pLeft->cItems;
947 if (cItems == pRight->cItems)
948 {
949 /* Require exact order. */
950 for (uint32_t iRdn = 0; iRdn < cItems; iRdn++)
951 if (!RTCrX509RelativeDistinguishedName_MatchByRfc5280(pLeft->papItems[iRdn], pRight->papItems[iRdn]))
952 return false;
953 return true;
954 }
955 return false;
956}
957
958
959RTDECL(bool) RTCrX509Name_ConstraintMatch(PCRTCRX509NAME pConstraint, PCRTCRX509NAME pName)
960{
961 /*
962 * Check that the constraint is a prefix of the name. This means that
963 * the name must have at least as many components and the constraint.
964 */
965 if (pName->cItems >= pConstraint->cItems)
966 {
967 /*
968 * Parallel crawl of the two RDNs arrays.
969 */
970 for (uint32_t i = 0; pConstraint->cItems; i++)
971 {
972 PCRTCRX509RELATIVEDISTINGUISHEDNAME pConstrRdns = pConstraint->papItems[i];
973 PCRTCRX509RELATIVEDISTINGUISHEDNAME pNameRdns = pName->papItems[i];
974
975 /*
976 * Walk the constraint attribute & value array.
977 */
978 for (uint32_t iConstrAttrib = 0; iConstrAttrib < pConstrRdns->cItems; iConstrAttrib++)
979 {
980 PCRTCRX509ATTRIBUTETYPEANDVALUE pConstrAttrib = pConstrRdns->papItems[iConstrAttrib];
981
982 /*
983 * Find matching attribute & value in the name.
984 */
985 bool fFound = false;
986 for (uint32_t iNameAttrib = 0; iNameAttrib < pNameRdns->cItems; iNameAttrib++)
987 if (RTCrX509AttributeTypeAndValue_MatchAsRdnByRfc5280(pConstrAttrib, pNameRdns->papItems[iNameAttrib]))
988 {
989 fFound = true;
990 break;
991 }
992 if (fFound)
993 return false;
994 }
995 }
996 return true;
997 }
998 return false;
999}
1000
1001
1002/**
1003 * Mapping between X.500 object IDs and short and long names.
1004 *
1005 * See RFC-1327, RFC-4519 ...
1006 */
1007static struct
1008{
1009 const char *pszOid;
1010 const char *pszShortNm;
1011 size_t cchShortNm;
1012 const char *pszLongNm;
1013} const g_aRdnMap[] =
1014{
1015 { "0.9.2342.19200300.100.1.1", RT_STR_TUPLE("uid"), "userid" },
1016 { "0.9.2342.19200300.100.1.3", RT_STR_TUPLE("Mail"), "Rfc822Mailbox" },
1017 { "0.9.2342.19200300.100.1.25", RT_STR_TUPLE("DC"), "DomainComponent" },
1018 { "1.2.840.113549.1.9.1", RT_STR_TUPLE("Email") /*nonstandard*/,"EmailAddress" },
1019 { "1.3.6.1.4.1.311.60.2.1.1", RT_STR_TUPLE("JdxIncL") /*nonstd*/, "JdxOfIncLocalityName" },
1020 { "1.3.6.1.4.1.311.60.2.1.2", RT_STR_TUPLE("JdxIncST") /*nonstd*/, "JdxOfIncStateOrProvinceName" },
1021 { "1.3.6.1.4.1.311.60.2.1.3", RT_STR_TUPLE("JdxIncC") /*nonstd*/, "JdxOfIncCountryName" },
1022 { "2.5.4.3", RT_STR_TUPLE("CN"), "CommonName" },
1023 { "2.5.4.4", RT_STR_TUPLE("SN"), "Surname" },
1024 { "2.5.4.5", RT_STR_TUPLE("SRN") /*nonstandard*/, "SerialNumber" },
1025 { "2.5.4.6", RT_STR_TUPLE("C"), "CountryName" },
1026 { "2.5.4.7", RT_STR_TUPLE("L"), "LocalityName" },
1027 { "2.5.4.8", RT_STR_TUPLE("ST"), "StateOrProviceName" },
1028 { "2.5.4.9", RT_STR_TUPLE("street"), "Street" },
1029 { "2.5.4.10", RT_STR_TUPLE("O"), "OrganizationName" },
1030 { "2.5.4.11", RT_STR_TUPLE("OU"), "OrganizationalUnitName" },
1031 { "2.5.4.12", RT_STR_TUPLE("title"), "Title" },
1032 { "2.5.4.13", RT_STR_TUPLE("desc"), "Description" },
1033 { "2.5.4.15", RT_STR_TUPLE("BC") /*nonstandard*/, "BusinessCategory" },
1034 { "2.5.4.17", RT_STR_TUPLE("ZIP") /*nonstandard*/, "PostalCode" },
1035 { "2.5.4.18", RT_STR_TUPLE("POBox") /*nonstandard*/,"PostOfficeBox" },
1036 { "2.5.4.20", RT_STR_TUPLE("PN") /*nonstandard*/, "TelephoneNumber" },
1037 { "2.5.4.33", RT_STR_TUPLE("RO") /*nonstandard*/, "RoleOccupant" },
1038 { "2.5.4.34", RT_STR_TUPLE("SA") /*nonstandard*/, "StreetAddress" },
1039 { "2.5.4.41", RT_STR_TUPLE("N") /*nonstandard*/, "Name" },
1040 { "2.5.4.42", RT_STR_TUPLE("GN"), "GivenName" },
1041 { "2.5.4.43", RT_STR_TUPLE("I") /*nonstandard*/, "Initials" },
1042 { "2.5.4.44", RT_STR_TUPLE("GQ") /*nonstandard*/, "GenerationQualifier" },
1043 { "2.5.4.46", RT_STR_TUPLE("DNQ") /*nonstandard*/, "DNQualifier" },
1044 { "2.5.4.51", RT_STR_TUPLE("HID") /*nonstandard*/, "HouseIdentifier" },
1045};
1046
1047
1048RTDECL(const char *) RTCrX509Name_GetShortRdn(PCRTASN1OBJID pRdnId)
1049{
1050 uint32_t iName = RT_ELEMENTS(g_aRdnMap);
1051 while (iName-- > 0)
1052 if (RTAsn1ObjId_CompareWithString(pRdnId, g_aRdnMap[iName].pszOid) == 0)
1053 return g_aRdnMap[iName].pszShortNm;
1054 return NULL;
1055}
1056
1057
1058RTDECL(bool) RTCrX509Name_MatchWithString(PCRTCRX509NAME pThis, const char *pszString)
1059{
1060 /* Keep track of the string length. */
1061 size_t cchString = strlen(pszString);
1062
1063 /*
1064 * The usual double loop for walking the components.
1065 */
1066 for (uint32_t i = 0; i < pThis->cItems; i++)
1067 {
1068 PCRTCRX509RELATIVEDISTINGUISHEDNAME pRdn = pThis->papItems[i];
1069 for (uint32_t j = 0; j < pRdn->cItems; j++)
1070 {
1071 PCRTCRX509ATTRIBUTETYPEANDVALUE pComponent = pRdn->papItems[j];
1072
1073 /*
1074 * Must be a string.
1075 */
1076 if (pComponent->Value.enmType != RTASN1TYPE_STRING)
1077 return false;
1078
1079 /*
1080 * Look up the component name prefix and check whether it's also in the string.
1081 */
1082 uint32_t iName = RT_ELEMENTS(g_aRdnMap);
1083 while (iName-- > 0)
1084 if (RTAsn1ObjId_CompareWithString(&pComponent->Type, g_aRdnMap[iName].pszOid) == 0)
1085 break;
1086 AssertMsgReturn(iName != UINT32_MAX, ("Please extend g_aRdnMap with '%s'.\n", pComponent->Type.szObjId), false);
1087
1088 if ( strncmp(pszString, g_aRdnMap[iName].pszShortNm, g_aRdnMap[iName].cchShortNm) != 0
1089 || pszString[g_aRdnMap[iName].cchShortNm] != '=')
1090 return false;
1091
1092 pszString += g_aRdnMap[iName].cchShortNm + 1;
1093 cchString -= g_aRdnMap[iName].cchShortNm + 1;
1094
1095 /*
1096 * Compare the component string.
1097 */
1098 size_t cchComponent;
1099 int rc = RTAsn1String_QueryUtf8Len(&pComponent->Value.u.String, &cchComponent);
1100 AssertRCReturn(rc, false);
1101
1102 if (cchComponent > cchString)
1103 return false;
1104 if (RTAsn1String_CompareWithString(&pComponent->Value.u.String, pszString, cchComponent) != 0)
1105 return false;
1106
1107 cchString -= cchComponent;
1108 pszString += cchComponent;
1109
1110 /*
1111 * Check separator comma + space and skip extra spaces before the next component.
1112 */
1113 if (cchString)
1114 {
1115 if (pszString[0] != ',')
1116 return false;
1117 if (pszString[1] != ' ' && pszString[1] != '\t')
1118 return false;
1119 pszString += 2;
1120 cchString -= 2;
1121
1122 while (*pszString == ' ' || *pszString == '\t')
1123 {
1124 pszString++;
1125 cchString--;
1126 }
1127 }
1128 }
1129 }
1130
1131 /*
1132 * If we got thru the whole name and the whole string, we're good.
1133 */
1134 return *pszString == '\0';
1135}
1136
1137
1138RTDECL(int) RTCrX509Name_FormatAsString(PCRTCRX509NAME pThis, char *pszBuf, size_t cbBuf, size_t *pcbActual)
1139{
1140 /*
1141 * The usual double loop for walking the components.
1142 */
1143 size_t off = 0;
1144 int rc = VINF_SUCCESS;
1145 for (uint32_t i = 0; i < pThis->cItems; i++)
1146 {
1147 PCRTCRX509RELATIVEDISTINGUISHEDNAME pRdn = pThis->papItems[i];
1148 for (uint32_t j = 0; j < pRdn->cItems; j++)
1149 {
1150 PCRTCRX509ATTRIBUTETYPEANDVALUE pComponent = pRdn->papItems[j];
1151
1152 /*
1153 * Must be a string.
1154 */
1155 if (pComponent->Value.enmType != RTASN1TYPE_STRING)
1156 return VERR_CR_X509_NAME_NOT_STRING;
1157
1158 /*
1159 * Look up the component name prefix.
1160 */
1161 uint32_t iName = RT_ELEMENTS(g_aRdnMap);
1162 while (iName-- > 0)
1163 if (RTAsn1ObjId_CompareWithString(&pComponent->Type, g_aRdnMap[iName].pszOid) == 0)
1164 break;
1165 AssertMsgReturn(iName != UINT32_MAX, ("Please extend g_aRdnMap with '%s'.\n", pComponent->Type.szObjId),
1166 VERR_CR_X509_NAME_MISSING_RDN_MAP_ENTRY);
1167
1168 /*
1169 * Append the prefix.
1170 */
1171 if (off)
1172 {
1173 if (off + 2 < cbBuf)
1174 {
1175 pszBuf[off] = ',';
1176 pszBuf[off + 1] = ' ';
1177 }
1178 else
1179 rc = VERR_BUFFER_OVERFLOW;
1180 off += 2;
1181 }
1182
1183 if (off + g_aRdnMap[iName].cchShortNm + 1 < cbBuf)
1184 {
1185 memcpy(&pszBuf[off], g_aRdnMap[iName].pszShortNm, g_aRdnMap[iName].cchShortNm);
1186 pszBuf[off + g_aRdnMap[iName].cchShortNm] = '=';
1187 }
1188 else
1189 rc = VERR_BUFFER_OVERFLOW;
1190 off += g_aRdnMap[iName].cchShortNm + 1;
1191
1192 /*
1193 * Add the component string.
1194 */
1195 const char *pszUtf8;
1196 size_t cchUtf8;
1197 int rc2 = RTAsn1String_QueryUtf8(&pComponent->Value.u.String, &pszUtf8, &cchUtf8);
1198 AssertRCReturn(rc2, rc2);
1199 if (off + cchUtf8 < cbBuf)
1200 memcpy(&pszBuf[off], pszUtf8, cchUtf8);
1201 else
1202 rc = VERR_BUFFER_OVERFLOW;
1203 off += cchUtf8;
1204 }
1205 }
1206
1207 if (pcbActual)
1208 *pcbActual = off + 1;
1209 if (off < cbBuf)
1210 pszBuf[off] = '\0';
1211 return rc;
1212}
1213
1214
1215
1216/*
1217 * One X.509 GeneralName.
1218 */
1219
1220/**
1221 * Name constraint matching (RFC-5280): DNS Name.
1222 *
1223 * @returns true on match, false on mismatch.
1224 * @param pConstraint The constraint name.
1225 * @param pName The name to match against the constraint.
1226 */
1227static bool rtCrX509GeneralName_ConstraintMatchDnsName(PCRTCRX509GENERALNAME pConstraint, PCRTCRX509GENERALNAME pName)
1228{
1229 /*
1230 * Empty constraint string is taken to match everything.
1231 */
1232 if (pConstraint->u.pT2_DnsName->Asn1Core.cb == 0)
1233 return true;
1234
1235 /*
1236 * Get the UTF-8 strings for the two.
1237 */
1238 size_t cchConstraint;
1239 char const *pszConstraint;
1240 int rc = RTAsn1String_QueryUtf8(pConstraint->u.pT2_DnsName, &pszConstraint, &cchConstraint);
1241 if (RT_SUCCESS(rc))
1242 {
1243 size_t cchFull;
1244 char const *pszFull;
1245 rc = RTAsn1String_QueryUtf8(pName->u.pT2_DnsName, &pszFull, &cchFull);
1246 if (RT_SUCCESS(rc))
1247 {
1248 /*
1249 * No match if the constraint is longer.
1250 */
1251 if (cchConstraint > cchFull)
1252 return false;
1253
1254 /*
1255 * No match if the constraint and name tail doesn't match
1256 * in a case-insensitive compare.
1257 */
1258 size_t offFull = cchFull - cchConstraint;
1259 if (RTStrICmp(&pszFull[offFull], pszConstraint) != 0)
1260 return false;
1261 if (!offFull)
1262 return true;
1263
1264 /*
1265 * The matching constraint must be delimited by a dot in the full
1266 * name. There seems to be some discussion whether ".oracle.com"
1267 * should match "www..oracle.com". This implementation does choose
1268 * to not succeed in that case.
1269 */
1270 if ((pszFull[offFull - 1] == '.') ^ (pszFull[offFull] == '.'))
1271 return true;
1272
1273 return false;
1274 }
1275 }
1276
1277 /* fall back. */
1278 return RTCrX509GeneralName_Compare(pConstraint, pName) == 0;
1279}
1280
1281
1282/**
1283 * Name constraint matching (RFC-5280): RFC-822 (email).
1284 *
1285 * @returns true on match, false on mismatch.
1286 * @param pConstraint The constraint name.
1287 * @param pName The name to match against the constraint.
1288 */
1289static bool rtCrX509GeneralName_ConstraintMatchRfc822Name(PCRTCRX509GENERALNAME pConstraint, PCRTCRX509GENERALNAME pName)
1290{
1291 /*
1292 * Empty constraint string is taken to match everything.
1293 */
1294 if (pConstraint->u.pT1_Rfc822->Asn1Core.cb == 0)
1295 return true;
1296
1297 /*
1298 * Get the UTF-8 strings for the two.
1299 */
1300 size_t cchConstraint;
1301 char const *pszConstraint;
1302 int rc = RTAsn1String_QueryUtf8(pConstraint->u.pT1_Rfc822, &pszConstraint, &cchConstraint);
1303 if (RT_SUCCESS(rc))
1304 {
1305 size_t cchFull;
1306 char const *pszFull;
1307 rc = RTAsn1String_QueryUtf8(pName->u.pT1_Rfc822, &pszFull, &cchFull);
1308 if (RT_SUCCESS(rc))
1309 {
1310 /*
1311 * No match if the constraint is longer.
1312 */
1313 if (cchConstraint > cchFull)
1314 return false;
1315
1316 /*
1317 * A lone dot matches everything.
1318 */
1319 if (cchConstraint == 1 && *pszConstraint == '.')
1320 return true;
1321
1322 /*
1323 * If there is a '@' in the constraint, the entire address must match.
1324 */
1325 const char *pszConstraintAt = (const char *)memchr(pszConstraint, '@', cchConstraint);
1326 if (pszConstraintAt)
1327 return cchConstraint == cchFull && RTStrICmp(pszConstraint, pszFull) == 0;
1328
1329 /*
1330 * No match if the constraint and name tail doesn't match
1331 * in a case-insensitive compare.
1332 */
1333 size_t offFull = cchFull - cchConstraint;
1334 if (RTStrICmp(&pszFull[offFull], pszConstraint) != 0)
1335 return false;
1336
1337 /*
1338 * If the constraint starts with a dot, we're supposed to be
1339 * satisfied with a tail match.
1340 */
1341 /** @todo Check if this should match even if offFull == 0. */
1342 if (*pszConstraint == '.')
1343 return true;
1344
1345 /*
1346 * Otherwise, we require a hostname match and thus expect an '@'
1347 * immediatly preceding the constraint match.
1348 */
1349 if (pszFull[offFull - 1] == '@')
1350 return true;
1351
1352 return false;
1353 }
1354 }
1355
1356 /* fall back. */
1357 return RTCrX509GeneralName_Compare(pConstraint, pName) == 0;
1358}
1359
1360
1361/**
1362 * Extracts the hostname from an URI.
1363 *
1364 * @returns true if successfully extract, false if no hostname present.
1365 * @param pszUri The URI.
1366 * @param pchHostName .
1367 * @param pcchHostName .
1368 */
1369static bool rtCrX509GeneralName_ExtractHostName(const char *pszUri, const char **pchHostName, size_t *pcchHostName)
1370{
1371 /*
1372 * Skip the schema name.
1373 */
1374 const char *pszStart = strchr(pszUri, ':');
1375 while (pszStart && (pszStart[1] != '/' || pszStart[2] != '/'))
1376 pszStart = strchr(pszStart + 1, ':');
1377 if (pszStart)
1378 {
1379 pszStart += 3;
1380
1381 /*
1382 * The name ends with the first slash or ":port".
1383 */
1384 const char *pszEnd = strchr(pszStart, '/');
1385 if (!pszEnd)
1386 pszEnd = strchr(pszStart, '\0');
1387 if (memchr(pszStart, ':', (size_t)(pszEnd - pszStart)))
1388 do
1389 pszEnd--;
1390 while (*pszEnd != ':');
1391 if (pszEnd != pszStart)
1392 {
1393 /*
1394 * Drop access credentials at the front of the string if present.
1395 */
1396 const char *pszAt = (const char *)memchr(pszStart, '@', (size_t)(pszEnd - pszStart));
1397 if (pszAt)
1398 pszStart = pszAt + 1;
1399
1400 /*
1401 * If there is still some string left, that's the host name.
1402 */
1403 if (pszEnd != pszStart)
1404 {
1405 *pcchHostName = (size_t)(pszEnd - pszStart);
1406 *pchHostName = pszStart;
1407 return true;
1408 }
1409 }
1410 }
1411
1412 *pcchHostName = 0;
1413 *pchHostName = NULL;
1414 return false;
1415}
1416
1417
1418/**
1419 * Name constraint matching (RFC-5280): URI.
1420 *
1421 * @returns true on match, false on mismatch.
1422 * @param pConstraint The constraint name.
1423 * @param pName The name to match against the constraint.
1424 */
1425static bool rtCrX509GeneralName_ConstraintMatchUri(PCRTCRX509GENERALNAME pConstraint, PCRTCRX509GENERALNAME pName)
1426{
1427 /*
1428 * Empty constraint string is taken to match everything.
1429 */
1430 if (pConstraint->u.pT6_Uri->Asn1Core.cb == 0)
1431 return true;
1432
1433 /*
1434 * Get the UTF-8 strings for the two.
1435 */
1436 size_t cchConstraint;
1437 char const *pszConstraint;
1438 int rc = RTAsn1String_QueryUtf8(pConstraint->u.pT6_Uri, &pszConstraint, &cchConstraint);
1439 if (RT_SUCCESS(rc))
1440 {
1441 size_t cchFull;
1442 char const *pszFull;
1443 rc = RTAsn1String_QueryUtf8(pName->u.pT6_Uri, &pszFull, &cchFull);
1444 if (RT_SUCCESS(rc))
1445 {
1446 /*
1447 * Isolate the hostname in the name.
1448 */
1449 size_t cchHostName;
1450 const char *pchHostName;
1451 if (rtCrX509GeneralName_ExtractHostName(pszFull, &pchHostName, &cchHostName))
1452 {
1453 /*
1454 * Domain constraint.
1455 */
1456 if (*pszConstraint == '.')
1457 {
1458 if (cchHostName >= cchConstraint)
1459 {
1460 size_t offHostName = cchHostName - cchConstraint;
1461 if (RTStrICmp(&pchHostName[offHostName], pszConstraint) == 0)
1462 {
1463 /* "http://www..oracle.com" does not match ".oracle.com".
1464 It's debatable whether "http://.oracle.com/" should match. */
1465 if ( !offHostName
1466 || pchHostName[offHostName - 1] != '.')
1467 return true;
1468 }
1469 }
1470 }
1471 /*
1472 * Host name constraint. Full match required.
1473 */
1474 else if ( cchHostName == cchConstraint
1475 && RTStrNICmp(pchHostName, pszConstraint, cchHostName) == 0)
1476 return true;
1477 }
1478 return false;
1479 }
1480 }
1481
1482 /* fall back. */
1483 return RTCrX509GeneralName_Compare(pConstraint, pName) == 0;
1484}
1485
1486
1487/**
1488 * Name constraint matching (RFC-5280): IP address.
1489 *
1490 * @returns true on match, false on mismatch.
1491 * @param pConstraint The constraint name.
1492 * @param pName The name to match against the constraint.
1493 */
1494static bool rtCrX509GeneralName_ConstraintMatchIpAddress(PCRTCRX509GENERALNAME pConstraint, PCRTCRX509GENERALNAME pName)
1495{
1496 uint8_t const *pbConstraint = pConstraint->u.pT7_IpAddress->Asn1Core.uData.pu8;
1497 uint8_t const *pbFull = pName->u.pT7_IpAddress->Asn1Core.uData.pu8;
1498
1499 /*
1500 * IPv4.
1501 */
1502 if ( pConstraint->u.pT7_IpAddress->Asn1Core.cb == 8 /* ip+netmask*/
1503 && pName->u.pT7_IpAddress->Asn1Core.cb == 4) /* ip */
1504 return ((pbFull[0] ^ pbConstraint[0]) & pbConstraint[4]) == 0
1505 && ((pbFull[1] ^ pbConstraint[1]) & pbConstraint[5]) == 0
1506 && ((pbFull[2] ^ pbConstraint[2]) & pbConstraint[6]) == 0
1507 && ((pbFull[3] ^ pbConstraint[3]) & pbConstraint[7]) == 0;
1508
1509 /*
1510 * IPv6.
1511 */
1512 if ( pConstraint->u.pT7_IpAddress->Asn1Core.cb == 32 /* ip+netmask*/
1513 && pName->u.pT7_IpAddress->Asn1Core.cb == 16) /* ip */
1514 {
1515 for (uint32_t i = 0; i < 16; i++)
1516 if (((pbFull[i] ^ pbConstraint[i]) & pbConstraint[i + 16]) != 0)
1517 return false;
1518 return true;
1519 }
1520
1521 return RTCrX509GeneralName_Compare(pConstraint, pName) == 0;
1522}
1523
1524
1525RTDECL(bool) RTCrX509GeneralName_ConstraintMatch(PCRTCRX509GENERALNAME pConstraint, PCRTCRX509GENERALNAME pName)
1526{
1527 if (pConstraint->enmChoice == pName->enmChoice)
1528 {
1529 if (RTCRX509GENERALNAME_IS_DIRECTORY_NAME(pConstraint))
1530 return RTCrX509Name_ConstraintMatch(&pConstraint->u.pT4->DirectoryName, &pName->u.pT4->DirectoryName);
1531
1532 if (RTCRX509GENERALNAME_IS_DNS_NAME(pConstraint))
1533 return rtCrX509GeneralName_ConstraintMatchDnsName(pConstraint, pName);
1534
1535 if (RTCRX509GENERALNAME_IS_RFC822_NAME(pConstraint))
1536 return rtCrX509GeneralName_ConstraintMatchRfc822Name(pConstraint, pName);
1537
1538 if (RTCRX509GENERALNAME_IS_URI(pConstraint))
1539 return rtCrX509GeneralName_ConstraintMatchUri(pConstraint, pName);
1540
1541 if (RTCRX509GENERALNAME_IS_IP_ADDRESS(pConstraint))
1542 return rtCrX509GeneralName_ConstraintMatchIpAddress(pConstraint, pName);
1543
1544 AssertFailed();
1545 return RTCrX509GeneralName_Compare(pConstraint, pName) == 0;
1546 }
1547 return false;
1548}
1549
1550
1551/*
1552 * Sequence of X.509 GeneralNames.
1553 */
1554
1555
1556/*
1557 * X.509 UniqueIdentifier.
1558 */
1559
1560
1561/*
1562 * X.509 SubjectPublicKeyInfo.
1563 */
1564
1565
1566/*
1567 * X.509 AuthorityKeyIdentifier (IPRT representation).
1568 */
1569
1570
1571/*
1572 * One X.509 PolicyQualifierInfo.
1573 */
1574
1575
1576/*
1577 * Sequence of X.509 PolicyQualifierInfo.
1578 */
1579
1580
1581/*
1582 * One X.509 PolicyInformation.
1583 */
1584
1585
1586/*
1587 * Sequence of X.509 CertificatePolicies.
1588 */
1589
1590
1591/*
1592 * One X.509 PolicyMapping (IPRT representation).
1593 */
1594
1595
1596/*
1597 * Sequence of X.509 PolicyMappings (IPRT representation).
1598 */
1599
1600
1601/*
1602 * X.509 BasicConstraints (IPRT representation).
1603 */
1604
1605
1606/*
1607 * X.509 GeneralSubtree (IPRT representation).
1608 */
1609
1610
1611RTDECL(bool) RTCrX509GeneralSubtree_ConstraintMatch(PCRTCRX509GENERALSUBTREE pConstraint, PCRTCRX509GENERALSUBTREE pName)
1612{
1613 return RTCrX509GeneralName_ConstraintMatch(&pConstraint->Base, &pName->Base);
1614}
1615
1616
1617/*
1618 * Sequence of X.509 GeneralSubtrees (IPRT representation).
1619 */
1620
1621
1622/*
1623 * X.509 NameConstraints (IPRT representation).
1624 */
1625
1626
1627/*
1628 * X.509 PolicyConstraints (IPRT representation).
1629 */
1630
1631
1632/*
1633 * One X.509 Extension.
1634 */
1635
1636
1637/*
1638 * Sequence of X.509 Extensions.
1639 */
1640
1641
1642/*
1643 * X.509 TbsCertificate.
1644 */
1645
1646static void rtCrx509TbsCertificate_AddKeyUsageFlags(PRTCRX509TBSCERTIFICATE pThis, PCRTCRX509EXTENSION pExtension)
1647{
1648 AssertReturnVoid(pExtension->enmValue == RTCRX509EXTENSIONVALUE_BIT_STRING);
1649 /* 3 = 1 byte for unused bit count, followed by one or two bytes containing actual bits. RFC-5280 defines bits 0 thru 8. */
1650 AssertReturnVoid(pExtension->ExtnValue.pEncapsulated->cb <= 3);
1651 pThis->T3.fKeyUsage |= (uint32_t)RTAsn1BitString_GetAsUInt64((PCRTASN1BITSTRING)pExtension->ExtnValue.pEncapsulated);
1652}
1653
1654
1655static void rtCrx509TbsCertificate_AddExtKeyUsageFlags(PRTCRX509TBSCERTIFICATE pThis, PCRTCRX509EXTENSION pExtension)
1656{
1657 AssertReturnVoid(pExtension->enmValue == RTCRX509EXTENSIONVALUE_SEQ_OF_OBJ_IDS);
1658 PCRTASN1SEQOFOBJIDS pObjIds = (PCRTASN1SEQOFOBJIDS)pExtension->ExtnValue.pEncapsulated;
1659 uint32_t i = pObjIds->cItems;
1660 while (i-- > 0)
1661 {
1662
1663 if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_ANY_EXTENDED_KEY_USAGE_OID) == 0)
1664 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_ANY;
1665 else if (RTAsn1ObjId_StartsWith(pObjIds->papItems[i], RTCRX509_ID_KP_OID))
1666 {
1667 if (RTAsn1ObjIdCountComponents(pObjIds->papItems[i]) == 9)
1668 switch (RTAsn1ObjIdGetLastComponentsAsUInt32(pObjIds->papItems[i]))
1669 {
1670 case 1: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_SERVER_AUTH; break;
1671 case 2: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_CLIENT_AUTH; break;
1672 case 3: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_CODE_SIGNING; break;
1673 case 4: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_EMAIL_PROTECTION; break;
1674 case 5: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_IPSEC_END_SYSTEM; break;
1675 case 6: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_IPSEC_TUNNEL; break;
1676 case 7: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_IPSEC_USER; break;
1677 case 8: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_TIMESTAMPING; break;
1678 case 9: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OCSP_SIGNING; break;
1679 case 10: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_DVCS; break;
1680 case 11: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_SBGP_CERT_AA_SERVICE_AUTH; break;
1681 case 13: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_EAP_OVER_PPP; break;
1682 case 14: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_EAP_OVER_LAN; break;
1683 default: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OTHER; break;
1684 }
1685 else
1686 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OTHER;
1687 }
1688 else if (RTAsn1ObjId_StartsWith(pObjIds->papItems[i], RTCRX509_APPLE_EKU_APPLE_EXTENDED_KEY_USAGE_OID))
1689 {
1690 if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_APPLE_EKU_CODE_SIGNING_OID) == 0)
1691 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_CODE_SIGNING;
1692 else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_APPLE_EKU_CODE_SIGNING_DEVELOPMENT_OID) == 0)
1693 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_CODE_SIGNING_DEVELOPMENT;
1694 else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_APPLE_EKU_SOFTWARE_UPDATE_SIGNING_OID) == 0)
1695 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_SOFTWARE_UPDATE_SIGNING;
1696 else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_APPLE_EKU_CODE_SIGNING_THRID_PARTY_OID) == 0)
1697 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_CODE_SIGNING_THIRD_PARTY;
1698 else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_APPLE_EKU_RESOURCE_SIGNING_OID) == 0)
1699 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_RESOURCE_SIGNING;
1700 else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_APPLE_EKU_SYSTEM_IDENTITY_OID) == 0)
1701 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_SYSTEM_IDENTITY;
1702 else
1703 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OTHER;
1704 }
1705 else if (RTAsn1ObjId_StartsWith(pObjIds->papItems[i], "1.3.6.1.4.1.311"))
1706 {
1707 if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_TIMESTAMP_SIGNING_OID) == 0)
1708 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_TIMESTAMP_SIGNING;
1709 else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_WHQL_CRYPTO_OID) == 0)
1710 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_WHQL_CRYPTO;
1711 else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_ATTEST_WHQL_CRYPTO_OID) == 0)
1712 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_ATTEST_WHQL_CRYPTO;
1713 else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_NT5_CRYPTO_OID) == 0)
1714 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_NT5_CRYPTO;
1715 else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_OEM_WHQL_CRYPTO_OID) == 0)
1716 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_OEM_WHQL_CRYPTO;
1717 else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_EMBEDDED_NT_CRYPTO_OID) == 0)
1718 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_EMBEDDED_NT_CRYPTO;
1719 else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_KERNEL_MODE_CODE_SIGNING_OID) == 0)
1720 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_KERNEL_MODE_CODE_SIGNING;
1721 else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_LIFETIME_SIGNING_OID) == 0)
1722 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_LIFETIME_SIGNING;
1723 else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_DRM_OID) == 0)
1724 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_DRM;
1725 else if (RTAsn1ObjId_CompareWithString(pObjIds->papItems[i], RTCRX509_MS_EKU_DRM_INDIVIDUALIZATION_OID) == 0)
1726 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_DRM_INDIVIDUALIZATION;
1727 else
1728 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OTHER;
1729 }
1730 else
1731 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OTHER;
1732 }
1733}
1734
1735
1736/**
1737 * (Re-)Process the certificate extensions.
1738 *
1739 * Will fail if duplicate extensions are encountered.
1740 *
1741 * @returns IPRT status code.
1742 * @param pThis The to-be-signed certificate part.
1743 * @param pErrInfo Where to return extended error details,
1744 * optional.
1745 */
1746RTDECL(int) RTCrX509TbsCertificate_ReprocessExtensions(PRTCRX509TBSCERTIFICATE pThis, PRTERRINFO pErrInfo)
1747{
1748 /*
1749 * Clear all variables we will set.
1750 */
1751 pThis->T3.fFlags = 0;
1752 pThis->T3.fKeyUsage = 0;
1753 pThis->T3.fExtKeyUsage = 0;
1754 pThis->T3.pAuthorityKeyIdentifier = NULL;
1755 pThis->T3.pSubjectKeyIdentifier = NULL;
1756 pThis->T3.pAltSubjectName = NULL;
1757 pThis->T3.pAltIssuerName = NULL;
1758 pThis->T3.pCertificatePolicies = NULL;
1759 pThis->T3.pPolicyMappings = NULL;
1760 pThis->T3.pBasicConstraints = NULL;
1761 pThis->T3.pNameConstraints = NULL;
1762 pThis->T3.pPolicyConstraints = NULL;
1763 pThis->T3.pInhibitAnyPolicy = NULL;
1764
1765#define CHECK_SET_PRESENT_RET_ON_DUP(a_pThis, a_pErrInfo, a_fPresentFlag) \
1766 do { \
1767 if ((a_pThis)->T3.fFlags & (a_fPresentFlag)) \
1768 return RTErrInfoSet(a_pErrInfo, VERR_CR_X509_TBSCERT_DUPLICATE_EXTENSION, \
1769 "Duplicate extension " #a_fPresentFlag); \
1770 (a_pThis)->T3.fFlags |= (a_fPresentFlag); \
1771 } while (0)
1772
1773 /*
1774 * Process all the extensions.
1775 */
1776 for (uint32_t i = 0; i < pThis->T3.Extensions.cItems; i++)
1777 {
1778 PCRTASN1OBJID pExtnId = &pThis->T3.Extensions.papItems[i]->ExtnId;
1779 PCRTASN1OCTETSTRING pExtValue = &pThis->T3.Extensions.papItems[i]->ExtnValue;
1780 if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_KEY_USAGE_OID) == 0)
1781 {
1782 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_KEY_USAGE);
1783 rtCrx509TbsCertificate_AddKeyUsageFlags(pThis, pThis->T3.Extensions.papItems[i]);
1784 Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_BIT_STRING);
1785 }
1786 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_EXT_KEY_USAGE_OID) == 0)
1787 {
1788 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_EXT_KEY_USAGE);
1789 rtCrx509TbsCertificate_AddExtKeyUsageFlags(pThis, pThis->T3.Extensions.papItems[i]);
1790 Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_SEQ_OF_OBJ_IDS);
1791 }
1792 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_AUTHORITY_KEY_IDENTIFIER_OID) == 0)
1793 {
1794 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_AUTHORITY_KEY_IDENTIFIER);
1795 pThis->T3.pAuthorityKeyIdentifier = (PCRTCRX509AUTHORITYKEYIDENTIFIER)pExtValue->pEncapsulated;
1796 Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_AUTHORITY_KEY_IDENTIFIER);
1797 }
1798 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_OLD_AUTHORITY_KEY_IDENTIFIER_OID) == 0)
1799 {
1800 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_OLD_AUTHORITY_KEY_IDENTIFIER);
1801 pThis->T3.pOldAuthorityKeyIdentifier = (PCRTCRX509OLDAUTHORITYKEYIDENTIFIER)pExtValue->pEncapsulated;
1802 Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_OLD_AUTHORITY_KEY_IDENTIFIER);
1803 }
1804 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_SUBJECT_KEY_IDENTIFIER_OID) == 0)
1805 {
1806 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_SUBJECT_KEY_IDENTIFIER);
1807 pThis->T3.pSubjectKeyIdentifier = (PCRTASN1OCTETSTRING)pExtValue->pEncapsulated;
1808 Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_OCTET_STRING);
1809 }
1810 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_SUBJECT_ALT_NAME_OID) == 0)
1811 {
1812 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_SUBJECT_ALT_NAME);
1813 pThis->T3.pAltSubjectName = (PCRTCRX509GENERALNAMES)pExtValue->pEncapsulated;
1814 Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_GENERAL_NAMES);
1815 }
1816 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_ISSUER_ALT_NAME_OID) == 0)
1817 {
1818 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_ISSUER_ALT_NAME);
1819 pThis->T3.pAltIssuerName = (PCRTCRX509GENERALNAMES)pExtValue->pEncapsulated;
1820 Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_GENERAL_NAMES);
1821 }
1822 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_CERTIFICATE_POLICIES_OID) == 0)
1823 {
1824 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_CERTIFICATE_POLICIES);
1825 pThis->T3.pCertificatePolicies = (PCRTCRX509CERTIFICATEPOLICIES)pExtValue->pEncapsulated;
1826 Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_CERTIFICATE_POLICIES);
1827 }
1828 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_POLICY_MAPPINGS_OID) == 0)
1829 {
1830 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_POLICY_MAPPINGS);
1831 pThis->T3.pPolicyMappings = (PCRTCRX509POLICYMAPPINGS)pExtValue->pEncapsulated;
1832 Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_POLICY_MAPPINGS);
1833 }
1834 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_BASIC_CONSTRAINTS_OID) == 0)
1835 {
1836 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_BASIC_CONSTRAINTS);
1837 pThis->T3.pBasicConstraints = (PCRTCRX509BASICCONSTRAINTS)pExtValue->pEncapsulated;
1838 Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_BASIC_CONSTRAINTS);
1839 }
1840 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_NAME_CONSTRAINTS_OID) == 0)
1841 {
1842 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_NAME_CONSTRAINTS);
1843 pThis->T3.pNameConstraints = (PCRTCRX509NAMECONSTRAINTS)pExtValue->pEncapsulated;
1844 Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_NAME_CONSTRAINTS);
1845 }
1846 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_POLICY_CONSTRAINTS_OID) == 0)
1847 {
1848 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_POLICY_CONSTRAINTS);
1849 pThis->T3.pPolicyConstraints = (PCRTCRX509POLICYCONSTRAINTS)pExtValue->pEncapsulated;
1850 Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_POLICY_CONSTRAINTS);
1851 }
1852 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_INHIBIT_ANY_POLICY_OID) == 0)
1853 {
1854 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_INHIBIT_ANY_POLICY);
1855 pThis->T3.pInhibitAnyPolicy = (PCRTASN1INTEGER)pExtValue->pEncapsulated;
1856 Assert(pThis->T3.Extensions.papItems[i]->enmValue == RTCRX509EXTENSIONVALUE_INTEGER);
1857 }
1858 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_ACCEPTABLE_CERT_POLICIES_OID) == 0)
1859 pThis->T3.fFlags |= RTCRX509TBSCERTIFICATE_F_PRESENT_ACCEPTABLE_CERT_POLICIES;
1860 else
1861 pThis->T3.fFlags |= RTCRX509TBSCERTIFICATE_F_PRESENT_OTHER;
1862 }
1863
1864 if (!pThis->T3.fFlags)
1865 pThis->T3.fFlags |= RTCRX509TBSCERTIFICATE_F_PRESENT_NONE;
1866
1867#undef CHECK_SET_PRESENT_RET_ON_DUP
1868 return VINF_SUCCESS;
1869}
1870
1871
1872
1873/*
1874 * One X.509 Certificate.
1875 */
1876
1877RTDECL(bool) RTCrX509Certificate_MatchIssuerAndSerialNumber(PCRTCRX509CERTIFICATE pCertificate,
1878 PCRTCRX509NAME pIssuer, PCRTASN1INTEGER pSerialNumber)
1879{
1880 if ( RTAsn1Integer_UnsignedCompare(&pCertificate->TbsCertificate.SerialNumber, pSerialNumber) == 0
1881 && RTCrX509Name_Compare(&pCertificate->TbsCertificate.Issuer, pIssuer) == 0)
1882 return true;
1883 return false;
1884}
1885
1886
1887RTDECL(bool) RTCrX509Certificate_MatchSubjectOrAltSubjectByRfc5280(PCRTCRX509CERTIFICATE pThis, PCRTCRX509NAME pName)
1888{
1889 if (RTCrX509Name_MatchByRfc5280(&pThis->TbsCertificate.Subject, pName))
1890 return true;
1891
1892 if (RTCrX509Extensions_IsPresent(&pThis->TbsCertificate.T3.Extensions))
1893 for (uint32_t i = 0; i < pThis->TbsCertificate.T3.Extensions.cItems; i++)
1894 {
1895 PCRTCRX509EXTENSION pExt = pThis->TbsCertificate.T3.Extensions.papItems[i];
1896 if ( pExt->enmValue == RTCRX509EXTENSIONVALUE_GENERAL_NAMES
1897 && RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCRX509_ID_CE_SUBJECT_ALT_NAME_OID))
1898 {
1899 PCRTCRX509GENERALNAMES pGeneralNames = (PCRTCRX509GENERALNAMES)pExt->ExtnValue.pEncapsulated;
1900 for (uint32_t j = 0; j < pGeneralNames->cItems; j++)
1901 if ( RTCRX509GENERALNAME_IS_DIRECTORY_NAME(pGeneralNames->papItems[j])
1902 && RTCrX509Name_MatchByRfc5280(&pGeneralNames->papItems[j]->u.pT4->DirectoryName, pName))
1903 return true;
1904 }
1905 }
1906 return false;
1907}
1908
1909
1910RTDECL(bool) RTCrX509Certificate_IsSelfSigned(PCRTCRX509CERTIFICATE pCertificate)
1911{
1912 if (RTCrX509Certificate_IsPresent(pCertificate))
1913 {
1914 return RTCrX509Name_MatchByRfc5280(&pCertificate->TbsCertificate.Subject,
1915 &pCertificate->TbsCertificate.Issuer);
1916 }
1917 return false;
1918}
1919
1920
1921/*
1922 * Set of X.509 Certificates.
1923 */
1924
1925RTDECL(PCRTCRX509CERTIFICATE)
1926RTCrX509Certificates_FindByIssuerAndSerialNumber(PCRTCRX509CERTIFICATES pCertificates,
1927 PCRTCRX509NAME pIssuer, PCRTASN1INTEGER pSerialNumber)
1928{
1929 for (uint32_t i = 0; i < pCertificates->cItems; i++)
1930 if (RTCrX509Certificate_MatchIssuerAndSerialNumber(pCertificates->papItems[i], pIssuer, pSerialNumber))
1931 return pCertificates->papItems[i];
1932 return NULL;
1933}
1934
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette