VirtualBox

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

Last change on this file since 51856 was 51856, checked in by vboxsync, 11 years ago

Added the odd sha-2 algorithms to RTCrDigest and extended the testcases to cover them. Added VBOX_WITH_ALT_HASH_CODE for avoiding the OpenSSL code even for VBoxRT.dll/so/dylib.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.0 KB
Line 
1/* $Id: x509-core.cpp 51856 2014-07-03 18:39:21Z vboxsync $ */
2/** @file
3 * IPRT - Crypto - X.509, Core APIs.
4 */
5
6/*
7 * Copyright (C) 2006-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include "internal/iprt.h"
32#include <iprt/crypto/x509.h>
33
34#include <iprt/err.h>
35#include <iprt/string.h>
36#include <iprt/uni.h>
37
38#include "x509-internal.h"
39
40
41/*
42 * Generate the code.
43 */
44#include <iprt/asn1-generator-core.h>
45
46
47/*
48 * X.509 Validity.
49 */
50
51RTDECL(bool) RTCrX509Validity_IsValidAtTimeSpec(PCRTCRX509VALIDITY pThis, PCRTTIMESPEC pTimeSpec)
52{
53 if (RTAsn1Time_CompareWithTimeSpec(&pThis->NotBefore, pTimeSpec) > 0)
54 return false;
55 if (RTAsn1Time_CompareWithTimeSpec(&pThis->NotAfter, pTimeSpec) < 0)
56 return false;
57 return true;
58}
59
60
61/*
62 * One X.509 Algorithm Identifier.
63 */
64
65RTDECL(RTDIGESTTYPE) RTCrX509AlgorithmIdentifier_QueryDigestType(PCRTCRX509ALGORITHMIDENTIFIER pThis)
66{
67 AssertPtrReturn(pThis, RTDIGESTTYPE_INVALID);
68 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_MD5))
69 return RTDIGESTTYPE_MD5;
70 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA1))
71 return RTDIGESTTYPE_SHA1;
72 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA256))
73 return RTDIGESTTYPE_SHA256;
74 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA512))
75 return RTDIGESTTYPE_SHA512;
76
77 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA384))
78 return RTDIGESTTYPE_SHA384;
79 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA224))
80 return RTDIGESTTYPE_SHA224;
81 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA512T224))
82 return RTDIGESTTYPE_SHA512T224;
83 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA512T256))
84 return RTDIGESTTYPE_SHA512T256;
85 return RTDIGESTTYPE_INVALID;
86}
87
88
89RTDECL(uint32_t) RTCrX509AlgorithmIdentifier_QueryDigestSize(PCRTCRX509ALGORITHMIDENTIFIER pThis)
90{
91 AssertPtrReturn(pThis, UINT32_MAX);
92
93 /* common */
94 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_MD5))
95 return 128 / 8;
96 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA1))
97 return 160 / 8;
98 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA256))
99 return 256 / 8;
100 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA512))
101 return 512 / 8;
102
103 /* Less common. */
104 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_MD2))
105 return 128 / 8;
106 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_MD4))
107 return 128 / 8;
108 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA384))
109 return 384 / 8;
110 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA224))
111 return 224 / 8;
112 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA512T224))
113 return 224 / 8;
114 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA512T256))
115 return 256 / 8;
116 if (!strcmp(pThis->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_WHIRLPOOL))
117 return 512 / 8;
118
119 return UINT32_MAX;
120}
121
122
123RTDECL(int) RTCrX509AlgorithmIdentifier_CompareWithString(PCRTCRX509ALGORITHMIDENTIFIER pThis, const char *pszObjId)
124{
125 return strcmp(pThis->Algorithm.szObjId, pszObjId);
126}
127
128
129RTDECL(int) RTCrX509AlgorithmIdentifier_CompareDigestAndEncryptedDigest(PCRTCRX509ALGORITHMIDENTIFIER pDigest,
130 PCRTCRX509ALGORITHMIDENTIFIER pEncryptedDigest)
131{
132 /* common */
133 if (!strcmp(pDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_MD5))
134 {
135 if (!strcmp(pEncryptedDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_MD5_WITH_RSA))
136 return 0;
137 }
138 else if (!strcmp(pDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA1))
139 {
140 if (!strcmp(pEncryptedDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA1_WITH_RSA))
141 return 0;
142 }
143 else if (!strcmp(pDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA256))
144 {
145 if (!strcmp(pEncryptedDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA256_WITH_RSA))
146 return 0;
147 }
148 else if (!strcmp(pDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA512))
149 {
150 if (!strcmp(pEncryptedDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA512_WITH_RSA))
151 return 0;
152 }
153 /* Less common. */
154 else if (!strcmp(pDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_MD2))
155 {
156 if (!strcmp(pEncryptedDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_MD2_WITH_RSA))
157 return 0;
158 }
159 else if (!strcmp(pDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_MD4))
160 {
161 if (!strcmp(pEncryptedDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_MD4_WITH_RSA))
162 return 0;
163 }
164 else if (!strcmp(pDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA384))
165 {
166 if (!strcmp(pEncryptedDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA384_WITH_RSA))
167 return 0;
168 }
169 else if (!strcmp(pDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA224))
170 {
171 if (!strcmp(pEncryptedDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_SHA224_WITH_RSA))
172 return 0;
173 }
174 else if (!strcmp(pDigest->Algorithm.szObjId, RTCRX509ALGORITHMIDENTIFIERID_WHIRLPOOL))
175 {
176 /* ?? */
177 }
178 else
179 return -1;
180 return 1;
181}
182
183
184/*
185 * Set of X.509 Algorithm Identifiers.
186 */
187
188
189/*
190 * One X.509 AttributeTypeAndValue.
191 */
192
193
194/*
195 * Set of X.509 AttributeTypeAndValues / X.509 RelativeDistinguishedName.
196 */
197
198/**
199 * Slow code path of rtCrX509CanNameIsNothing.
200 *
201 * @returns true if @uc maps to nothing, false if not.
202 * @param uc The unicode code point.
203 */
204static bool rtCrX509CanNameIsNothingSlow(RTUNICP uc)
205{
206 switch (uc)
207 {
208 /* 2.2 Map - Paragraph 1: */
209 case 0x00ad:
210 case 0x1806:
211 case 0x034f:
212 case 0x180b: case 0x180c: case 0x180d:
213
214 case 0xfe00: case 0xfe01: case 0xfe02: case 0xfe03:
215 case 0xfe04: case 0xfe05: case 0xfe06: case 0xfe07:
216 case 0xfe08: case 0xfe09: case 0xfe0a: case 0xfe0b:
217 case 0xfe0c: case 0xfe0d: case 0xfe0e: case 0xfe0f:
218
219 case 0xfffc:
220
221 /* 2.2 Map - Paragraph 3 (control code/function): */
222 case 0x0000: case 0x0001: case 0x0002: case 0x0003:
223 case 0x0004: case 0x0005: case 0x0006: case 0x0007:
224 case 0x0008:
225
226 case 0x000e: case 0x000f:
227 case 0x0010: case 0x0011: case 0x0012: case 0x0013:
228 case 0x0014: case 0x0015: case 0x0016: case 0x0017:
229 case 0x0018: case 0x0019: case 0x001a: case 0x001b:
230 case 0x001c: case 0x001d: case 0x001e: case 0x001f:
231
232 case 0x007f:
233 case 0x0080: case 0x0081: case 0x0082: case 0x0083:
234 case 0x0084: /*case 0x0085:*/ case 0x0086: case 0x0087:
235 case 0x0088: case 0x0089: case 0x008a: case 0x008b:
236 case 0x008c: case 0x008d: case 0x008e: case 0x008f:
237 case 0x0090: case 0x0091: case 0x0092: case 0x0093:
238 case 0x0094: case 0x0095: case 0x0096: case 0x0097:
239 case 0x0098: case 0x0099: case 0x009a: case 0x009b:
240 case 0x009c: case 0x009d: case 0x009e: case 0x009f:
241
242 case 0x06dd:
243 case 0x070f:
244 case 0x180e:
245 case 0x200c: case 0x200d: case 0x200e: case 0x200f:
246 case 0x202a: case 0x202b: case 0x202c: case 0x202d: case 0x202e:
247 case 0x2060: case 0x2061: case 0x2062: case 0x2063:
248 case 0x206a: case 0x206b: case 0x206c: case 0x206d: case 0x206e: case 0x206f:
249 case 0xfeff:
250 case 0xfff9: case 0xfffa: case 0xfffb:
251 case 0x1d173: case 0x1d174: case 0x1d175: case 0x1d176: case 0x1d177: case 0x1d178: case 0x1d179: case 0x1d17a:
252 case 0xe0001:
253 case 0xe0020: case 0xe0021: case 0xe0022: case 0xe0023:
254 case 0xe0024: case 0xe0025: case 0xe0026: case 0xe0027:
255 case 0xe0028: case 0xe0029: case 0xe002a: case 0xe002b:
256 case 0xe002c: case 0xe002d: case 0xe002e: case 0xe002f:
257 case 0xe0030: case 0xe0031: case 0xe0032: case 0xe0033:
258 case 0xe0034: case 0xe0035: case 0xe0036: case 0xe0037:
259 case 0xe0038: case 0xe0039: case 0xe003a: case 0xe003b:
260 case 0xe003c: case 0xe003d: case 0xe003e: case 0xe003f:
261 case 0xe0040: case 0xe0041: case 0xe0042: case 0xe0043:
262 case 0xe0044: case 0xe0045: case 0xe0046: case 0xe0047:
263 case 0xe0048: case 0xe0049: case 0xe004a: case 0xe004b:
264 case 0xe004c: case 0xe004d: case 0xe004e: case 0xe004f:
265 case 0xe0050: case 0xe0051: case 0xe0052: case 0xe0053:
266 case 0xe0054: case 0xe0055: case 0xe0056: case 0xe0057:
267 case 0xe0058: case 0xe0059: case 0xe005a: case 0xe005b:
268 case 0xe005c: case 0xe005d: case 0xe005e: case 0xe005f:
269 case 0xe0060: case 0xe0061: case 0xe0062: case 0xe0063:
270 case 0xe0064: case 0xe0065: case 0xe0066: case 0xe0067:
271 case 0xe0068: case 0xe0069: case 0xe006a: case 0xe006b:
272 case 0xe006c: case 0xe006d: case 0xe006e: case 0xe006f:
273 case 0xe0070: case 0xe0071: case 0xe0072: case 0xe0073:
274 case 0xe0074: case 0xe0075: case 0xe0076: case 0xe0077:
275 case 0xe0078: case 0xe0079: case 0xe007a: case 0xe007b:
276 case 0xe007c: case 0xe007d: case 0xe007e: case 0xe007f:
277
278 /* 2.2 Map - Paragraph 4. */
279 case 0x200b:
280 return true;
281 }
282 return false;
283}
284
285
286/**
287 * Checks if @a uc maps to nothing according to mapping rules of RFC-5280 and
288 * RFC-4518.
289 *
290 * @returns true if @uc maps to nothing, false if not.
291 * @param uc The unicode code point.
292 */
293DECLINLINE(bool) rtCrX509CanNameIsNothing(RTUNICP uc)
294{
295 if (uc > 0x001f && uc < 0x00ad)
296 return false;
297 return rtCrX509CanNameIsNothingSlow(uc);
298}
299
300
301/**
302 * Slow code path of rtCrX509CanNameIsSpace.
303 *
304 * @returns true if space, false if not.
305 * @param uc The unicode code point.
306 */
307static bool rtCrX509CanNameIsSpaceSlow(RTUNICP uc)
308{
309 switch (uc)
310 {
311 /* 2.2 Map - Paragraph 2. */
312 case 0x09:
313 case 0x0a:
314 case 0x0b:
315 case 0x0c:
316 case 0x0d:
317 case 0x20:
318 case 0x0085:
319 case 0x00a0:
320 case 0x1680:
321 case 0x2000: case 0x2001: case 0x2002: case 0x2003:
322 case 0x2004: case 0x2005: case 0x2006: case 0x2007:
323 case 0x2008: case 0x2009: case 0x200a:
324 case 0x2028: case 0x2029:
325 case 0x202f:
326 case 0x205f:
327 case 0x3000:
328 return true;
329 }
330 return false;
331}
332
333
334/**
335 * Checks if @a uc is a space character according to the mapping rules of
336 * RFC-5280 and RFC-4518.
337 *
338 * @returns true if space, false if not.
339 * @param uc The unicode code point.
340 */
341DECLINLINE(bool) rtCrX509CanNameIsSpace(RTUNICP uc)
342{
343 if (uc < 0x0085)
344 {
345 if (uc > 0x0020)
346 return false;
347 if (uc == 0x0020) /* space */
348 return true;
349 }
350 return rtCrX509CanNameIsSpaceSlow(uc);
351}
352
353
354static const char *rtCrX509CanNameStripLeft(const char *psz, size_t *pcch)
355{
356 /*
357 * Return space when we've encountered the first non-space-non-nothing code point.
358 */
359 const char * const pszStart = psz;
360 const char *pszPrev;
361 for (;;)
362 {
363 pszPrev = psz;
364 RTUNICP uc;
365 int rc = RTStrGetCpEx(&psz, &uc);
366 AssertRCBreak(rc);
367 if (!uc)
368 {
369 if ((uintptr_t)(pszPrev - pszStart) >= *pcch)
370 break;
371 /* NUL inside the string, maps to nothing => ignore it. */
372 }
373 else if (!rtCrX509CanNameIsSpace(uc) && !rtCrX509CanNameIsNothing(uc))
374 break;
375 }
376 *pcch -= pszPrev - pszStart;
377 return pszPrev;
378}
379
380
381static RTUNICP rtCrX509CanNameGetNextCpWithMappingSlowSpace(const char **ppsz, size_t *pcch)
382{
383 /*
384 * Return space when we've encountered the first non-space-non-nothing code point.
385 */
386 RTUNICP uc;
387 const char *psz = *ppsz;
388 const char * const pszStart = psz;
389 const char *pszPrev;
390 for (;;)
391 {
392 pszPrev = psz;
393 int rc = RTStrGetCpEx(&psz, &uc);
394 AssertRCBreakStmt(rc, uc = 0x20);
395 if (!uc)
396 {
397 if ((uintptr_t)(pszPrev - pszStart) >= *pcch)
398 {
399 uc = 0; /* End of string: Ignore trailing spaces. */
400 break;
401 }
402 /* NUL inside the string, maps to nothing => ignore it. */
403 }
404 else if (!rtCrX509CanNameIsSpace(uc) && !rtCrX509CanNameIsNothing(uc))
405 {
406 uc = 0x20; /* Return space before current char. */
407 break;
408 }
409 }
410
411 *ppsz = pszPrev;
412 *pcch -= pszPrev - pszStart;
413 return uc;
414}
415
416
417DECLINLINE(RTUNICP) rtCrX509CanNameGetNextCpIgnoreNul(const char **ppsz, size_t *pcch)
418{
419 while (*pcch > 0)
420 {
421 const char *psz = *ppsz;
422 RTUNICP uc = *psz;
423 if (uc < 0x80)
424 {
425 *pcch -= 1;
426 *ppsz = psz + 1;
427 }
428 else
429 {
430 int rc = RTStrGetCpEx(ppsz, &uc);
431 AssertRCReturn(rc, uc);
432 size_t cchCp = *ppsz - psz;
433 AssertReturn(cchCp <= *pcch, 0);
434 *pcch -= cchCp;
435 }
436 if (uc != 0)
437 return uc;
438 }
439 return 0;
440}
441
442
443static RTUNICP rtCrX509CanNameGetNextCpWithMappingSlowNothing(const char **ppsz, size_t *pcch)
444{
445 /*
446 * Return first code point which doesn't map to nothing. If we encounter
447 * a space, we defer to the mapping-after-space routine above.
448 */
449 for (;;)
450 {
451 RTUNICP uc = rtCrX509CanNameGetNextCpIgnoreNul(ppsz, pcch);
452 if (rtCrX509CanNameIsSpace(uc))
453 return rtCrX509CanNameGetNextCpWithMappingSlowSpace(ppsz, pcch);
454 if (!rtCrX509CanNameIsNothing(uc) || uc == 0)
455 return uc;
456 }
457}
458
459
460DECLINLINE(RTUNICP) rtCrX509CanNameGetNextCpWithMapping(const char **ppsz, size_t *pcch)
461{
462 RTUNICP uc = rtCrX509CanNameGetNextCpIgnoreNul(ppsz, pcch);
463 if (uc)
464 {
465 if (!rtCrX509CanNameIsSpace(uc))
466 {
467 if (!rtCrX509CanNameIsNothing(uc))
468 return uc;
469 return rtCrX509CanNameGetNextCpWithMappingSlowNothing(ppsz, pcch);
470 }
471 return rtCrX509CanNameGetNextCpWithMappingSlowSpace(ppsz, pcch);
472 }
473 return uc;
474}
475
476
477RTDECL(bool) RTCrX509AttributeTypeAndValue_MatchAsRdnByRfc5280(PCRTCRX509ATTRIBUTETYPEANDVALUE pLeft,
478 PCRTCRX509ATTRIBUTETYPEANDVALUE pRight)
479{
480 if (RTAsn1ObjId_Compare(&pLeft->Type, &pRight->Type) == 0)
481 {
482 /*
483 * Try for perfect match in case we get luck.
484 */
485#ifdef DEBUG_bird /* Want to test the complicated code path first */
486 if (pLeft->Value.enmType != RTASN1TYPE_STRING || pRight->Value.enmType != RTASN1TYPE_STRING)
487#endif
488 if (RTAsn1DynType_Compare(&pLeft->Value, &pRight->Value) == 0)
489 return true;
490
491 /*
492 * If both are string types, we can compare them according to RFC-5280.
493 */
494 if ( pLeft->Value.enmType == RTASN1TYPE_STRING
495 && pRight->Value.enmType == RTASN1TYPE_STRING)
496 {
497 size_t cchLeft;
498 const char *pszLeft;
499 int rc = RTAsn1String_QueryUtf8(&pLeft->Value.u.String, &pszLeft, &cchLeft);
500 if (RT_SUCCESS(rc))
501 {
502 size_t cchRight;
503 const char *pszRight;
504 rc = RTAsn1String_QueryUtf8(&pRight->Value.u.String, &pszRight, &cchRight);
505 if (RT_SUCCESS(rc))
506 {
507 /*
508 * Perform a simplified RFC-5280 comparsion.
509 * The algorithm as be relaxed on the following counts:
510 * 1. No unicode normalization.
511 * 2. Prohibited characters not checked for.
512 * 3. Bidirectional characters are not ignored.
513 */
514 pszLeft = rtCrX509CanNameStripLeft(pszLeft, &cchLeft);
515 pszRight = rtCrX509CanNameStripLeft(pszRight, &cchRight);
516 while (*pszLeft && *pszRight)
517 {
518 RTUNICP ucLeft = rtCrX509CanNameGetNextCpWithMapping(&pszLeft, &cchLeft);
519 RTUNICP ucRight = rtCrX509CanNameGetNextCpWithMapping(&pszRight, &cchRight);
520 if (ucLeft != ucRight)
521 {
522 ucLeft = RTUniCpToLower(ucLeft);
523 ucRight = RTUniCpToLower(ucRight);
524 if (ucLeft != ucRight)
525 return false;
526 }
527 }
528
529 return cchRight == 0 && cchLeft == 0;
530 }
531 }
532 }
533 }
534 return false;
535}
536
537
538RTDECL(bool) RTCrX509RelativeDistinguishedName_MatchByRfc5280(PCRTCRX509RELATIVEDISTINGUISHEDNAME pLeft,
539 PCRTCRX509RELATIVEDISTINGUISHEDNAME pRight)
540{
541 /*
542 * No match if the attribute count differs.
543 */
544 uint32_t const cItems = pLeft->cItems;
545 if (cItems == pRight->cItems)
546 {
547 /*
548 * Compare each attribute, but don't insist on the same order nor
549 * bother checking for duplicates (too complicated).
550 */
551 for (uint32_t iLeft = 0; iLeft < cItems; iLeft++)
552 {
553 PCRTCRX509ATTRIBUTETYPEANDVALUE pLeftAttr = &pLeft->paItems[iLeft];
554 bool fFound = false;
555 for (uint32_t iRight = 0; iRight < cItems; iRight++)
556 if (RTCrX509AttributeTypeAndValue_MatchAsRdnByRfc5280(pLeftAttr, &pRight->paItems[iRight]))
557 {
558 fFound = true;
559 break;
560 }
561 if (!fFound)
562 return false;
563 }
564 return true;
565 }
566 return false;
567
568}
569
570
571/*
572 * X.509 Name.
573 */
574
575RTDECL(bool) RTCrX509Name_MatchByRfc5280(PCRTCRX509NAME pLeft, PCRTCRX509NAME pRight)
576{
577 uint32_t const cItems = pLeft->cItems;
578 if (cItems == pRight->cItems)
579 {
580 /* Require exact order. */
581 for (uint32_t iRdn = 0; iRdn < cItems; iRdn++)
582 if (!RTCrX509RelativeDistinguishedName_MatchByRfc5280(&pLeft->paItems[iRdn], &pRight->paItems[iRdn]))
583 return false;
584 return true;
585 }
586 return false;
587}
588
589
590RTDECL(bool) RTCrX509Name_ConstraintMatch(PCRTCRX509NAME pConstraint, PCRTCRX509NAME pName)
591{
592 /*
593 * Check that the constraint is a prefix of the name. This means that
594 * the name must have at least as many components and the constraint.
595 */
596 if (pName->cItems >= pConstraint->cItems)
597 {
598 /*
599 * Parallel crawl of the two RDNs arrays.
600 */
601 for (uint32_t i = 0; pConstraint->cItems; i++)
602 {
603 PCRTCRX509RELATIVEDISTINGUISHEDNAME pConstrRdns = &pConstraint->paItems[i];
604 PCRTCRX509RELATIVEDISTINGUISHEDNAME pNameRdns = &pName->paItems[i];
605
606 /*
607 * Walk the constraint attribute & value array.
608 */
609 for (uint32_t iConstrAttrib = 0; iConstrAttrib < pConstrRdns->cItems; iConstrAttrib++)
610 {
611 PCRTCRX509ATTRIBUTETYPEANDVALUE pConstrAttrib = &pConstrRdns->paItems[iConstrAttrib];
612
613 /*
614 * Find matching attribute & value in the name.
615 */
616 bool fFound = false;
617 for (uint32_t iNameAttrib = 0; iNameAttrib < pNameRdns->cItems; iNameAttrib++)
618 if (RTCrX509AttributeTypeAndValue_MatchAsRdnByRfc5280(pConstrAttrib, &pNameRdns->paItems[iNameAttrib]))
619 {
620 fFound = true;
621 break;
622 }
623 if (fFound)
624 return false;
625 }
626 }
627 return true;
628 }
629 return false;
630}
631
632
633/**
634 * Mapping between X.500 object IDs and short and long names.
635 *
636 * See RFC-1327, ...
637 */
638static struct
639{
640 const char *pszOid;
641 const char *pszShortNm;
642 size_t cchShortNm;
643 const char *pszLongNm;
644} const g_aRdnMap[] =
645{
646 { "0.9.2342.19200300.100.1.25", RT_STR_TUPLE("DC"), "DomainComponent" },
647 { "1.2.840.113549.1.9.1", RT_STR_TUPLE("Email"), "EmailAddress" },
648 { "2.5.4.3", RT_STR_TUPLE("CN"), "CommonName" },
649 { "2.5.4.4", RT_STR_TUPLE("S"), "Surname" },
650 { "2.5.4.6", RT_STR_TUPLE("C"), "CountryName" },
651 { "2.5.4.7", RT_STR_TUPLE("L"), "LocalityName" },
652 { "2.5.4.8", RT_STR_TUPLE("ST"), "StatOrProviceName" },
653 { "2.5.4.10", RT_STR_TUPLE("O"), "OrganizationName" },
654 { "2.5.4.11", RT_STR_TUPLE("OU"), "OrganizationUnitName" },
655 { "2.5.4.42", RT_STR_TUPLE("G"), "GivenName" },
656 { "2.5.4.43", RT_STR_TUPLE("I"), "Initials" },
657 { "2.5.4.44", RT_STR_TUPLE("GQ"), "GenerationQualifier" },
658};
659
660
661RTDECL(bool) RTCrX509Name_MatchWithString(PCRTCRX509NAME pThis, const char *pszString)
662{
663 /* Keep track of the string length. */
664 size_t cchString = strlen(pszString);
665
666 /*
667 * The usual double loop for walking the components.
668 */
669 for (uint32_t i = 0; i < pThis->cItems; i++)
670 {
671 PCRTCRX509RELATIVEDISTINGUISHEDNAME pRdn = &pThis->paItems[i];
672 for (uint32_t j = 0; j < pRdn->cItems; j++)
673 {
674 PCRTCRX509ATTRIBUTETYPEANDVALUE pComponent = &pRdn->paItems[j];
675
676 /*
677 * Must be a string.
678 */
679 if (pComponent->Value.enmType != RTASN1TYPE_STRING)
680 return false;
681
682 /*
683 * Look up the component name prefix and check whether it's also in the string.
684 */
685 uint32_t iName = RT_ELEMENTS(g_aRdnMap);
686 while (iName-- > 0)
687 if (RTAsn1ObjId_CompareWithString(&pComponent->Type, g_aRdnMap[iName].pszOid) == 0)
688 break;
689 AssertMsgReturn(iName != UINT32_MAX, ("Please extend g_aRdnMap with '%s'.\n", pComponent->Type.szObjId), false);
690
691 if ( strncmp(pszString, g_aRdnMap[iName].pszShortNm, g_aRdnMap[iName].cchShortNm) != 0
692 || pszString[g_aRdnMap[iName].cchShortNm] != '=')
693 return false;
694
695 pszString += g_aRdnMap[iName].cchShortNm + 1;
696 cchString -= g_aRdnMap[iName].cchShortNm + 1;
697
698 /*
699 * Compare the component string.
700 */
701 size_t cchComponent;
702 int rc = RTAsn1String_QueryUtf8Len(&pComponent->Value.u.String, &cchComponent);
703 AssertRCReturn(rc, false);
704
705 if (cchComponent > cchString)
706 return false;
707 if (RTAsn1String_CompareWithString(&pComponent->Value.u.String, pszString, cchComponent) != 0)
708 return false;
709
710 cchString -= cchComponent;
711 pszString += cchComponent;
712
713 /*
714 * Check separator comma + space and skip extra spaces before the next component.
715 */
716 if (cchString)
717 {
718 if (pszString[0] != ',')
719 return false;
720 if (pszString[1] != ' ' && pszString[1] != '\t')
721 return false;
722 pszString += 2;
723 cchString -= 2;
724
725 while (*pszString == ' ' || *pszString == '\t')
726 {
727 pszString++;
728 cchString--;
729 }
730 }
731 }
732 }
733
734 /*
735 * If we got thru the whole name and the whole string, we're good.
736 */
737 return *pszString == '\0';
738}
739
740
741RTDECL(int) RTCrX509Name_FormatAsString(PCRTCRX509NAME pThis, char *pszBuf, size_t cbBuf, size_t *pcbActual)
742{
743 /*
744 * The usual double loop for walking the components.
745 */
746 size_t off = 0;
747 int rc = VINF_SUCCESS;
748 for (uint32_t i = 0; i < pThis->cItems; i++)
749 {
750 PCRTCRX509RELATIVEDISTINGUISHEDNAME pRdn = &pThis->paItems[i];
751 for (uint32_t j = 0; j < pRdn->cItems; j++)
752 {
753 PCRTCRX509ATTRIBUTETYPEANDVALUE pComponent = &pRdn->paItems[j];
754
755 /*
756 * Must be a string.
757 */
758 if (pComponent->Value.enmType != RTASN1TYPE_STRING)
759 return VERR_CR_X509_NAME_NOT_STRING;
760
761 /*
762 * Look up the component name prefix.
763 */
764 uint32_t iName = RT_ELEMENTS(g_aRdnMap);
765 while (iName-- > 0)
766 if (RTAsn1ObjId_CompareWithString(&pComponent->Type, g_aRdnMap[iName].pszOid) == 0)
767 break;
768 AssertMsgReturn(iName != UINT32_MAX, ("Please extend g_aRdnMap with '%s'.\n", pComponent->Type.szObjId),
769 VERR_CR_X509_NAME_MISSING_RDN_MAP_ENTRY);
770
771 /*
772 * Append the prefix.
773 */
774 if (off)
775 {
776 if (off + 2 < cbBuf)
777 {
778 pszBuf[off] = ',';
779 pszBuf[off + 1] = ' ';
780 }
781 else
782 rc = VERR_BUFFER_OVERFLOW;
783 off += 2;
784 }
785
786 if (off + g_aRdnMap[iName].cchShortNm + 1 < cbBuf)
787 {
788 memcpy(&pszBuf[off], g_aRdnMap[iName].pszShortNm, g_aRdnMap[iName].cchShortNm);
789 pszBuf[off + g_aRdnMap[iName].cchShortNm] = '=';
790 }
791 else
792 rc = VERR_BUFFER_OVERFLOW;
793 off += g_aRdnMap[iName].cchShortNm + 1;
794
795 /*
796 * Add the component string.
797 */
798 const char *pszUtf8;
799 size_t cchUtf8;
800 int rc2 = RTAsn1String_QueryUtf8(&pComponent->Value.u.String, &pszUtf8, &cchUtf8);
801 AssertRCReturn(rc2, rc2);
802 if (off + cchUtf8 < cbBuf)
803 memcpy(&pszBuf[off], pszUtf8, cchUtf8);
804 else
805 rc = VERR_BUFFER_OVERFLOW;
806 off += cchUtf8;
807 }
808 }
809
810 if (pcbActual)
811 *pcbActual = off + 1;
812 if (off < cbBuf)
813 pszBuf[off] = '\0';
814 return rc;
815}
816
817
818
819/*
820 * One X.509 GeneralName.
821 */
822
823/**
824 * Name constraint matching (RFC-5280): DNS Name.
825 *
826 * @returns true on match, false on mismatch.
827 * @param pConstraint The constraint name.
828 * @param pName The name to match against the constraint.
829 */
830static bool rtCrX509GeneralName_ConstraintMatchDnsName(PCRTCRX509GENERALNAME pConstraint, PCRTCRX509GENERALNAME pName)
831{
832 /*
833 * Empty constraint string is taken to match everything.
834 */
835 if (pConstraint->u.pT2_DnsName->Asn1Core.cb == 0)
836 return true;
837
838 /*
839 * Get the UTF-8 strings for the two.
840 */
841 size_t cchConstraint;
842 char const *pszConstraint;
843 int rc = RTAsn1String_QueryUtf8(pConstraint->u.pT2_DnsName, &pszConstraint, &cchConstraint);
844 if (RT_SUCCESS(rc))
845 {
846 size_t cchFull;
847 char const *pszFull;
848 rc = RTAsn1String_QueryUtf8(pName->u.pT2_DnsName, &pszFull, &cchFull);
849 if (RT_SUCCESS(rc))
850 {
851 /*
852 * No match if the constraint is longer.
853 */
854 if (cchConstraint > cchFull)
855 return false;
856
857 /*
858 * No match if the constraint and name tail doesn't match
859 * in a case-insensitive compare.
860 */
861 size_t offFull = cchFull - cchConstraint;
862 if (RTStrICmp(&pszFull[offFull], pszConstraint) != 0)
863 return false;
864 if (!offFull)
865 return true;
866
867 /*
868 * The matching constraint must be delimited by a dot in the full
869 * name. There seems to be some discussion whether ".oracle.com"
870 * should match "www..oracle.com". This implementation does choose
871 * to not succeed in that case.
872 */
873 if ((pszFull[offFull - 1] == '.') ^ (pszFull[offFull] == '.'))
874 return true;
875
876 return false;
877 }
878 }
879
880 /* fall back. */
881 return RTCrX509GeneralName_Compare(pConstraint, pName) == 0;
882}
883
884
885/**
886 * Name constraint matching (RFC-5280): RFC-822 (email).
887 *
888 * @returns true on match, false on mismatch.
889 * @param pConstraint The constraint name.
890 * @param pName The name to match against the constraint.
891 */
892static bool rtCrX509GeneralName_ConstraintMatchRfc822Name(PCRTCRX509GENERALNAME pConstraint, PCRTCRX509GENERALNAME pName)
893{
894 /*
895 * Empty constraint string is taken to match everything.
896 */
897 if (pConstraint->u.pT1_Rfc822->Asn1Core.cb == 0)
898 return true;
899
900 /*
901 * Get the UTF-8 strings for the two.
902 */
903 size_t cchConstraint;
904 char const *pszConstraint;
905 int rc = RTAsn1String_QueryUtf8(pConstraint->u.pT1_Rfc822, &pszConstraint, &cchConstraint);
906 if (RT_SUCCESS(rc))
907 {
908 size_t cchFull;
909 char const *pszFull;
910 rc = RTAsn1String_QueryUtf8(pName->u.pT1_Rfc822, &pszFull, &cchFull);
911 if (RT_SUCCESS(rc))
912 {
913 /*
914 * No match if the constraint is longer.
915 */
916 if (cchConstraint > cchFull)
917 return false;
918
919 /*
920 * A lone dot matches everything.
921 */
922 if (cchConstraint == 1 && *pszConstraint == '.')
923 return true;
924
925 /*
926 * If there is a '@' in the constraint, the entire address must match.
927 */
928 const char *pszConstraintAt = (const char *)memchr(pszConstraint, '@', cchConstraint);
929 if (pszConstraintAt)
930 return cchConstraint == cchFull && RTStrICmp(pszConstraint, pszFull) == 0;
931
932 /*
933 * No match if the constraint and name tail doesn't match
934 * in a case-insensitive compare.
935 */
936 size_t offFull = cchFull - cchConstraint;
937 if (RTStrICmp(&pszFull[offFull], pszConstraint) != 0)
938 return false;
939
940 /*
941 * If the constraint starts with a dot, we're supposed to be
942 * satisfied with a tail match.
943 */
944 /** @todo Check if this should match even if offFull == 0. */
945 if (*pszConstraint == '.')
946 return true;
947
948 /*
949 * Otherwise, we require a hostname match and thus expect an '@'
950 * immediatly preceding the constraint match.
951 */
952 if (pszFull[offFull - 1] == '@')
953 return true;
954
955 return false;
956 }
957 }
958
959 /* fall back. */
960 return RTCrX509GeneralName_Compare(pConstraint, pName) == 0;
961}
962
963
964/**
965 * Extracts the hostname from an URI.
966 *
967 * @returns true if successfully extract, false if no hostname present.
968 * @param pszUri The URI.
969 * @param pchHostName .
970 * @param pcchHostName .
971 */
972static bool rtCrX509GeneralName_ExtractHostName(const char *pszUri, const char **pchHostName, size_t *pcchHostName)
973{
974 /*
975 * Skip the schema name.
976 */
977 const char *pszStart = strchr(pszUri, ':');
978 while (pszStart && (pszStart[1] != '/' || pszStart[2] != '/'))
979 pszStart = strchr(pszStart + 1, ':');
980 if (pszStart)
981 {
982 pszStart += 3;
983
984 /*
985 * The name ends with the first slash or ":port".
986 */
987 const char *pszEnd = strchr(pszStart, '/');
988 if (!pszEnd)
989 pszEnd = strchr(pszStart, '\0');
990 if (memchr(pszStart, ':', pszEnd - pszStart))
991 do
992 pszEnd--;
993 while (*pszEnd != ':');
994 if (pszEnd != pszStart)
995 {
996 /*
997 * Drop access credentials at the front of the string if present.
998 */
999 const char *pszAt = (const char *)memchr(pszStart, '@', pszEnd - pszStart);
1000 if (pszAt)
1001 pszStart = pszAt + 1;
1002
1003 /*
1004 * If there is still some string left, that's the host name.
1005 */
1006 if (pszEnd != pszStart)
1007 {
1008 *pcchHostName = pszEnd - pszStart;
1009 *pchHostName = pszStart;
1010 return true;
1011 }
1012 }
1013 }
1014
1015 *pcchHostName = 0;
1016 *pchHostName = NULL;
1017 return false;
1018}
1019
1020
1021/**
1022 * Name constraint matching (RFC-5280): URI.
1023 *
1024 * @returns true on match, false on mismatch.
1025 * @param pConstraint The constraint name.
1026 * @param pName The name to match against the constraint.
1027 */
1028static bool rtCrX509GeneralName_ConstraintMatchUri(PCRTCRX509GENERALNAME pConstraint, PCRTCRX509GENERALNAME pName)
1029{
1030 /*
1031 * Empty constraint string is taken to match everything.
1032 */
1033 if (pConstraint->u.pT6_Uri->Asn1Core.cb == 0)
1034 return true;
1035
1036 /*
1037 * Get the UTF-8 strings for the two.
1038 */
1039 size_t cchConstraint;
1040 char const *pszConstraint;
1041 int rc = RTAsn1String_QueryUtf8(pConstraint->u.pT6_Uri, &pszConstraint, &cchConstraint);
1042 if (RT_SUCCESS(rc))
1043 {
1044 size_t cchFull;
1045 char const *pszFull;
1046 rc = RTAsn1String_QueryUtf8(pName->u.pT6_Uri, &pszFull, &cchFull);
1047 if (RT_SUCCESS(rc))
1048 {
1049 /*
1050 * Isolate the hostname in the name.
1051 */
1052 size_t cchHostName;
1053 const char *pchHostName;
1054 if (rtCrX509GeneralName_ExtractHostName(pszFull, &pchHostName, &cchHostName))
1055 {
1056 /*
1057 * Domain constraint.
1058 */
1059 if (*pszConstraint == '.')
1060 {
1061 if (cchHostName >= cchConstraint)
1062 {
1063 size_t offHostName = cchHostName - cchConstraint;
1064 if (RTStrICmp(&pchHostName[offHostName], pszConstraint) == 0)
1065 {
1066 /* "http://www..oracle.com" does not match ".oracle.com".
1067 It's debatable whether "http://.oracle.com/" should match. */
1068 if ( !offHostName
1069 || pchHostName[offHostName - 1] != '.')
1070 return true;
1071 }
1072 }
1073 }
1074 /*
1075 * Host name constraint. Full match required.
1076 */
1077 else if ( cchHostName == cchConstraint
1078 && RTStrNICmp(pchHostName, pszConstraint, cchHostName) == 0)
1079 return true;
1080 }
1081 return false;
1082 }
1083 }
1084
1085 /* fall back. */
1086 return RTCrX509GeneralName_Compare(pConstraint, pName) == 0;
1087}
1088
1089
1090/**
1091 * Name constraint matching (RFC-5280): IP address.
1092 *
1093 * @returns true on match, false on mismatch.
1094 * @param pConstraint The constraint name.
1095 * @param pName The name to match against the constraint.
1096 */
1097static bool rtCrX509GeneralName_ConstraintMatchIpAddress(PCRTCRX509GENERALNAME pConstraint, PCRTCRX509GENERALNAME pName)
1098{
1099 uint8_t const *pbConstraint = pConstraint->u.pT7_IpAddress->Asn1Core.uData.pu8;
1100 uint8_t const *pbFull = pName->u.pT7_IpAddress->Asn1Core.uData.pu8;
1101
1102 /*
1103 * IPv4.
1104 */
1105 if ( pConstraint->u.pT7_IpAddress->Asn1Core.cb == 8 /* ip+netmask*/
1106 && pName->u.pT7_IpAddress->Asn1Core.cb == 4) /* ip */
1107 return ((pbFull[0] ^ pbConstraint[0]) & pbConstraint[4]) == 0
1108 && ((pbFull[1] ^ pbConstraint[1]) & pbConstraint[5]) == 0
1109 && ((pbFull[2] ^ pbConstraint[2]) & pbConstraint[6]) == 0
1110 && ((pbFull[3] ^ pbConstraint[3]) & pbConstraint[7]) == 0;
1111
1112 /*
1113 * IPv6.
1114 */
1115 if ( pConstraint->u.pT7_IpAddress->Asn1Core.cb == 32 /* ip+netmask*/
1116 && pName->u.pT7_IpAddress->Asn1Core.cb == 16) /* ip */
1117 {
1118 for (uint32_t i = 0; i < 16; i++)
1119 if (((pbFull[i] ^ pbConstraint[i]) & pbConstraint[i + 16]) != 0)
1120 return false;
1121 return true;
1122 }
1123
1124 return RTCrX509GeneralName_Compare(pConstraint, pName) == 0;
1125}
1126
1127
1128RTDECL(bool) RTCrX509GeneralName_ConstraintMatch(PCRTCRX509GENERALNAME pConstraint, PCRTCRX509GENERALNAME pName)
1129{
1130 if (pConstraint->enmChoice == pName->enmChoice)
1131 {
1132 if (RTCRX509GENERALNAME_IS_DIRECTORY_NAME(pConstraint))
1133 return RTCrX509Name_ConstraintMatch(&pConstraint->u.pT4->DirectoryName, &pName->u.pT4->DirectoryName);
1134
1135 if (RTCRX509GENERALNAME_IS_DNS_NAME(pConstraint))
1136 return rtCrX509GeneralName_ConstraintMatchDnsName(pConstraint, pName);
1137
1138 if (RTCRX509GENERALNAME_IS_RFC822_NAME(pConstraint))
1139 return rtCrX509GeneralName_ConstraintMatchRfc822Name(pConstraint, pName);
1140
1141 if (RTCRX509GENERALNAME_IS_URI(pConstraint))
1142 return rtCrX509GeneralName_ConstraintMatchUri(pConstraint, pName);
1143
1144 if (RTCRX509GENERALNAME_IS_IP_ADDRESS(pConstraint))
1145 return rtCrX509GeneralName_ConstraintMatchIpAddress(pConstraint, pName);
1146
1147 AssertFailed();
1148 return RTCrX509GeneralName_Compare(pConstraint, pName) == 0;
1149 }
1150 return false;
1151}
1152
1153
1154/*
1155 * Sequence of X.509 GeneralNames.
1156 */
1157
1158
1159/*
1160 * X.509 UniqueIdentifier.
1161 */
1162
1163
1164/*
1165 * X.509 SubjectPublicKeyInfo.
1166 */
1167
1168
1169/*
1170 * X.509 AuthorityKeyIdentifier (IPRT representation).
1171 */
1172
1173
1174/*
1175 * One X.509 PolicyQualifierInfo.
1176 */
1177
1178
1179/*
1180 * Sequence of X.509 PolicyQualifierInfo.
1181 */
1182
1183
1184/*
1185 * One X.509 PolicyInformation.
1186 */
1187
1188
1189/*
1190 * Sequence of X.509 CertificatePolicies.
1191 */
1192
1193
1194/*
1195 * One X.509 PolicyMapping (IPRT representation).
1196 */
1197
1198
1199/*
1200 * Sequence of X.509 PolicyMappings (IPRT representation).
1201 */
1202
1203
1204/*
1205 * X.509 BasicConstraints (IPRT representation).
1206 */
1207
1208
1209/*
1210 * X.509 GeneralSubtree (IPRT representation).
1211 */
1212
1213
1214RTDECL(bool) RTCrX509GeneralSubtree_ConstraintMatch(PCRTCRX509GENERALSUBTREE pConstraint, PCRTCRX509GENERALSUBTREE pName)
1215{
1216 return RTCrX509GeneralName_ConstraintMatch(&pConstraint->Base, &pName->Base);
1217}
1218
1219
1220/*
1221 * Sequence of X.509 GeneralSubtrees (IPRT representation).
1222 */
1223
1224
1225/*
1226 * X.509 NameConstraints (IPRT representation).
1227 */
1228
1229
1230/*
1231 * X.509 PolicyConstraints (IPRT representation).
1232 */
1233
1234
1235/*
1236 * One X.509 Extension.
1237 */
1238
1239
1240/*
1241 * Sequence of X.509 Extensions.
1242 */
1243
1244
1245/*
1246 * X.509 TbsCertificate.
1247 */
1248
1249static void rtCrx509TbsCertificate_AddKeyUsageFlags(PRTCRX509TBSCERTIFICATE pThis, PCRTCRX509EXTENSION pExtension)
1250{
1251 AssertReturnVoid(pExtension->enmValue == RTCRX509EXTENSIONVALUE_BIT_STRING);
1252 AssertReturnVoid(pExtension->ExtnValue.pEncapsulated->cb <= 2);
1253 pThis->T3.fKeyUsage |= (uint32_t)RTAsn1BitString_GetAsUInt64((PCRTASN1BITSTRING)pExtension->ExtnValue.pEncapsulated);
1254}
1255
1256
1257static void rtCrx509TbsCertificate_AddExtKeyUsageFlags(PRTCRX509TBSCERTIFICATE pThis, PCRTCRX509EXTENSION pExtension)
1258{
1259 AssertReturnVoid(pExtension->enmValue == RTCRX509EXTENSIONVALUE_SEQ_OF_OBJ_IDS);
1260 PCRTASN1SEQOFOBJIDS pObjIds = (PCRTASN1SEQOFOBJIDS)pExtension->ExtnValue.pEncapsulated;
1261 uint32_t i = pObjIds->cItems;
1262 while (i-- > 0)
1263 {
1264
1265 if (RTAsn1ObjId_CompareWithString(&pObjIds->paItems[i], RTCRX509_ANY_EXTENDED_KEY_USAGE_OID) == 0)
1266 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_ANY;
1267 else if (RTAsn1ObjId_StartsWith(&pObjIds->paItems[i], RTCRX509_ID_KP_OID))
1268 {
1269 if (RTAsn1ObjIdCountComponents(&pObjIds->paItems[i]) == 9)
1270 switch (RTAsn1ObjIdGetLastComponentsAsUInt32(&pObjIds->paItems[i]))
1271 {
1272 case 1: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_SERVER_AUTH; break;
1273 case 2: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_CLIENT_AUTH; break;
1274 case 3: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_CODE_SIGNING; break;
1275 case 4: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_EMAIL_PROTECTION; break;
1276 case 5: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_IPSEC_END_SYSTEM; break;
1277 case 6: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_IPSEC_TUNNEL; break;
1278 case 7: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_IPSEC_USER; break;
1279 case 8: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_TIME_STAMPING; break;
1280 case 9: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OCSP_SIGNING; break;
1281 case 10: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_DVCS; break;
1282 case 11: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_SBGP_CERT_AA_SERVICE_AUTH; break;
1283 case 13: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_EAP_OVER_PPP; break;
1284 case 14: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_EAP_OVER_LAN; break;
1285 default: pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OTHER; break;
1286 }
1287 else
1288 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OTHER;
1289 }
1290 else if (RTAsn1ObjId_StartsWith(&pObjIds->paItems[i], RTCRX509_APPLE_EKU_APPLE_EXTENDED_KEY_USAGE_OID))
1291 {
1292 if (RTAsn1ObjId_CompareWithString(&pObjIds->paItems[i], RTCRX509_APPLE_EKU_CODE_SIGNING_OID) == 0)
1293 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_CODE_SIGNING;
1294 else if (RTAsn1ObjId_CompareWithString(&pObjIds->paItems[i], RTCRX509_APPLE_EKU_CODE_SIGNING_DEVELOPMENT_OID) == 0)
1295 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_CODE_SIGNING_DEVELOPMENT;
1296 else if (RTAsn1ObjId_CompareWithString(&pObjIds->paItems[i], RTCRX509_APPLE_EKU_SOFTWARE_UPDATE_SIGNING_OID) == 0)
1297 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_SOFTWARE_UPDATE_SIGNING;
1298 else if (RTAsn1ObjId_CompareWithString(&pObjIds->paItems[i], RTCRX509_APPLE_EKU_CODE_SIGNING_THRID_PARTY_OID) == 0)
1299 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_CODE_SIGNING_THIRD_PARTY;
1300 else if (RTAsn1ObjId_CompareWithString(&pObjIds->paItems[i], RTCRX509_APPLE_EKU_RESOURCE_SIGNING_OID) == 0)
1301 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_RESOURCE_SIGNING;
1302 else if (RTAsn1ObjId_CompareWithString(&pObjIds->paItems[i], RTCRX509_APPLE_EKU_SYSTEM_IDENTITY_OID) == 0)
1303 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_APPLE_SYSTEM_IDENTITY;
1304 else
1305 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OTHER;
1306 }
1307 else if (RTAsn1ObjId_StartsWith(&pObjIds->paItems[i], "1.3.6.1.4.1.311"))
1308 {
1309 if (RTAsn1ObjId_CompareWithString(&pObjIds->paItems[i], RTCRX509_MS_EKU_TIMESTAMP_SIGNING_OID) == 0)
1310 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_TIMESTAMP_SIGNING;
1311 else if (RTAsn1ObjId_CompareWithString(&pObjIds->paItems[i], RTCRX509_MS_EKU_NT5_CRYPTO_OID) == 0)
1312 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_NT5_CRYPTO;
1313 else if (RTAsn1ObjId_CompareWithString(&pObjIds->paItems[i], RTCRX509_MS_EKU_OEM_WHQL_CRYPTO_OID) == 0)
1314 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_OEM_WHQL_CRYPTO;
1315 else if (RTAsn1ObjId_CompareWithString(&pObjIds->paItems[i], RTCRX509_MS_EKU_EMBEDDED_NT_CRYPTO_OID) == 0)
1316 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_EMBEDDED_NT_CRYPTO;
1317 else if (RTAsn1ObjId_CompareWithString(&pObjIds->paItems[i], RTCRX509_MS_EKU_KERNEL_MODE_CODE_SIGNING_OID) == 0)
1318 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_KERNEL_MODE_CODE_SIGNING;
1319 else if (RTAsn1ObjId_CompareWithString(&pObjIds->paItems[i], RTCRX509_MS_EKU_LIFETIME_SIGNING_OID) == 0)
1320 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_LIFETIME_SIGNING;
1321 else if (RTAsn1ObjId_CompareWithString(&pObjIds->paItems[i], RTCRX509_MS_EKU_DRM_OID) == 0)
1322 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_DRM;
1323 else if (RTAsn1ObjId_CompareWithString(&pObjIds->paItems[i], RTCRX509_MS_EKU_DRM_INDIVIDUALIZATION_OID) == 0)
1324 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_MS_DRM_INDIVIDUALIZATION;
1325 else
1326 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OTHER;
1327 }
1328 else
1329 pThis->T3.fExtKeyUsage |= RTCRX509CERT_EKU_F_OTHER;
1330 }
1331}
1332
1333
1334/**
1335 * (Re-)Process the certificate extensions.
1336 *
1337 * Will fail if duplicate extensions are encountered.
1338 *
1339 * @returns IPRT status code.
1340 * @param pThis The to-be-signed certificate part.
1341 * @param pErrInfo Where to return extended error details,
1342 * optional.
1343 */
1344RTDECL(int) RTCrX509TbsCertificate_ReprocessExtensions(PRTCRX509TBSCERTIFICATE pThis, PRTERRINFO pErrInfo)
1345{
1346 /*
1347 * Clear all variables we will set.
1348 */
1349 pThis->T3.fFlags = 0;
1350 pThis->T3.fKeyUsage = 0;
1351 pThis->T3.fExtKeyUsage = 0;
1352 pThis->T3.pAuthorityKeyIdentifier = NULL;
1353 pThis->T3.pSubjectKeyIdentifier = NULL;
1354 pThis->T3.pAltSubjectName = NULL;
1355 pThis->T3.pAltIssuerName = NULL;
1356 pThis->T3.pCertificatePolicies = NULL;
1357 pThis->T3.pPolicyMappings = NULL;
1358 pThis->T3.pBasicConstraints = NULL;
1359 pThis->T3.pNameConstraints = NULL;
1360 pThis->T3.pPolicyConstraints = NULL;
1361 pThis->T3.pInhibitAnyPolicy = NULL;
1362
1363#define CHECK_SET_PRESENT_RET_ON_DUP(a_pThis, a_pErrInfo, a_fPresentFlag) \
1364 do { \
1365 if ((a_pThis)->T3.fFlags & (a_fPresentFlag)) \
1366 return RTErrInfoSet(a_pErrInfo, VERR_CR_X509_TBSCERT_DUPLICATE_EXTENSION, \
1367 "Duplicate extension " #a_fPresentFlag); \
1368 (a_pThis)->T3.fFlags |= (a_fPresentFlag); \
1369 } while (0)
1370
1371 /*
1372 * Process all the extensions.
1373 */
1374 for (uint32_t i = 0; i < pThis->T3.Extensions.cItems; i++)
1375 {
1376 PCRTASN1OBJID pExtnId = &pThis->T3.Extensions.paItems[i].ExtnId;
1377 PCRTASN1OCTETSTRING pExtValue = &pThis->T3.Extensions.paItems[i].ExtnValue;
1378 if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_KEY_USAGE_OID) == 0)
1379 {
1380 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_KEY_USAGE);
1381 rtCrx509TbsCertificate_AddKeyUsageFlags(pThis, &pThis->T3.Extensions.paItems[i]);
1382 Assert(pThis->T3.Extensions.paItems[i].enmValue == RTCRX509EXTENSIONVALUE_BIT_STRING);
1383 }
1384 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_EXT_KEY_USAGE_OID) == 0)
1385 {
1386 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_EXT_KEY_USAGE);
1387 rtCrx509TbsCertificate_AddExtKeyUsageFlags(pThis, &pThis->T3.Extensions.paItems[i]);
1388 Assert(pThis->T3.Extensions.paItems[i].enmValue == RTCRX509EXTENSIONVALUE_SEQ_OF_OBJ_IDS);
1389 }
1390 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_AUTHORITY_KEY_IDENTIFIER_OID) == 0)
1391 {
1392 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_AUTHORITY_KEY_IDENTIFIER);
1393 pThis->T3.pAuthorityKeyIdentifier = (PCRTCRX509AUTHORITYKEYIDENTIFIER)pExtValue->pEncapsulated;
1394 Assert(pThis->T3.Extensions.paItems[i].enmValue == RTCRX509EXTENSIONVALUE_AUTHORITY_KEY_IDENTIFIER);
1395 }
1396 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_OLD_AUTHORITY_KEY_IDENTIFIER_OID) == 0)
1397 {
1398 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_OLD_AUTHORITY_KEY_IDENTIFIER);
1399 pThis->T3.pOldAuthorityKeyIdentifier = (PCRTCRX509OLDAUTHORITYKEYIDENTIFIER)pExtValue->pEncapsulated;
1400 Assert(pThis->T3.Extensions.paItems[i].enmValue == RTCRX509EXTENSIONVALUE_OLD_AUTHORITY_KEY_IDENTIFIER);
1401 }
1402 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_SUBJECT_KEY_IDENTIFIER_OID) == 0)
1403 {
1404 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_SUBJECT_KEY_IDENTIFIER);
1405 pThis->T3.pSubjectKeyIdentifier = (PCRTASN1OCTETSTRING)pExtValue->pEncapsulated;
1406 Assert(pThis->T3.Extensions.paItems[i].enmValue == RTCRX509EXTENSIONVALUE_OCTET_STRING);
1407 }
1408 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_SUBJECT_ALT_NAME_OID) == 0)
1409 {
1410 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_SUBJECT_ALT_NAME);
1411 pThis->T3.pAltSubjectName = (PCRTCRX509GENERALNAMES)pExtValue->pEncapsulated;
1412 Assert(pThis->T3.Extensions.paItems[i].enmValue == RTCRX509EXTENSIONVALUE_GENERAL_NAMES);
1413 }
1414 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_ISSUER_ALT_NAME_OID) == 0)
1415 {
1416 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_ISSUER_ALT_NAME);
1417 pThis->T3.pAltIssuerName = (PCRTCRX509GENERALNAMES)pExtValue->pEncapsulated;
1418 Assert(pThis->T3.Extensions.paItems[i].enmValue == RTCRX509EXTENSIONVALUE_GENERAL_NAMES);
1419 }
1420 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_CERTIFICATE_POLICIES_OID) == 0)
1421 {
1422 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_CERTIFICATE_POLICIES);
1423 pThis->T3.pCertificatePolicies = (PCRTCRX509CERTIFICATEPOLICIES)pExtValue->pEncapsulated;
1424 Assert(pThis->T3.Extensions.paItems[i].enmValue == RTCRX509EXTENSIONVALUE_CERTIFICATE_POLICIES);
1425 }
1426 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_POLICY_MAPPINGS_OID) == 0)
1427 {
1428 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_POLICY_MAPPINGS);
1429 pThis->T3.pPolicyMappings = (PCRTCRX509POLICYMAPPINGS)pExtValue->pEncapsulated;
1430 Assert(pThis->T3.Extensions.paItems[i].enmValue == RTCRX509EXTENSIONVALUE_POLICY_MAPPINGS);
1431 }
1432 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_BASIC_CONSTRAINTS_OID) == 0)
1433 {
1434 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_BASIC_CONSTRAINTS);
1435 pThis->T3.pBasicConstraints = (PCRTCRX509BASICCONSTRAINTS)pExtValue->pEncapsulated;
1436 Assert(pThis->T3.Extensions.paItems[i].enmValue == RTCRX509EXTENSIONVALUE_BASIC_CONSTRAINTS);
1437 }
1438 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_NAME_CONSTRAINTS_OID) == 0)
1439 {
1440 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_NAME_CONSTRAINTS);
1441 pThis->T3.pNameConstraints = (PCRTCRX509NAMECONSTRAINTS)pExtValue->pEncapsulated;
1442 Assert(pThis->T3.Extensions.paItems[i].enmValue == RTCRX509EXTENSIONVALUE_NAME_CONSTRAINTS);
1443 }
1444 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_POLICY_CONSTRAINTS_OID) == 0)
1445 {
1446 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_POLICY_CONSTRAINTS);
1447 pThis->T3.pPolicyConstraints = (PCRTCRX509POLICYCONSTRAINTS)pExtValue->pEncapsulated;
1448 Assert(pThis->T3.Extensions.paItems[i].enmValue == RTCRX509EXTENSIONVALUE_POLICY_CONSTRAINTS);
1449 }
1450 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_INHIBIT_ANY_POLICY_OID) == 0)
1451 {
1452 CHECK_SET_PRESENT_RET_ON_DUP(pThis, pErrInfo, RTCRX509TBSCERTIFICATE_F_PRESENT_INHIBIT_ANY_POLICY);
1453 pThis->T3.pInhibitAnyPolicy = (PCRTASN1INTEGER)pExtValue->pEncapsulated;
1454 Assert(pThis->T3.Extensions.paItems[i].enmValue == RTCRX509EXTENSIONVALUE_INTEGER);
1455 }
1456 else if (RTAsn1ObjId_CompareWithString(pExtnId, RTCRX509_ID_CE_ACCEPTABLE_CERT_POLICIES_OID) == 0)
1457 pThis->T3.fFlags |= RTCRX509TBSCERTIFICATE_F_PRESENT_ACCEPTABLE_CERT_POLICIES;
1458 else
1459 pThis->T3.fFlags |= RTCRX509TBSCERTIFICATE_F_PRESENT_OTHER;
1460 }
1461
1462 if (!pThis->T3.fFlags)
1463 pThis->T3.fFlags |= RTCRX509TBSCERTIFICATE_F_PRESENT_NONE;
1464
1465#undef CHECK_SET_PRESENT_RET_ON_DUP
1466 return VINF_SUCCESS;
1467}
1468
1469
1470
1471/*
1472 * One X.509 Certificate.
1473 */
1474
1475RTDECL(bool) RTCrX509Certificate_MatchIssuerAndSerialNumber(PCRTCRX509CERTIFICATE pCertificate,
1476 PCRTCRX509NAME pIssuer, PCRTASN1INTEGER pSerialNumber)
1477{
1478 if ( RTAsn1Integer_UnsignedCompare(&pCertificate->TbsCertificate.SerialNumber, pSerialNumber) == 0
1479 && RTCrX509Name_Compare(&pCertificate->TbsCertificate.Issuer, pIssuer) == 0)
1480 return true;
1481 return false;
1482}
1483
1484
1485RTDECL(bool) RTCrX509Certificate_MatchSubjectOrAltSubjectByRfc5280(PCRTCRX509CERTIFICATE pThis, PCRTCRX509NAME pName)
1486{
1487 if (RTCrX509Name_MatchByRfc5280(&pThis->TbsCertificate.Subject, pName))
1488 return true;
1489
1490 if (RTCrX509Extensions_IsPresent(&pThis->TbsCertificate.T3.Extensions))
1491 for (uint32_t i = 0; i < pThis->TbsCertificate.T3.Extensions.cItems; i++)
1492 {
1493 PCRTCRX509EXTENSION pExt = &pThis->TbsCertificate.T3.Extensions.paItems[i];
1494 if ( pExt->enmValue == RTCRX509EXTENSIONVALUE_GENERAL_NAMES
1495 && RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCRX509_ID_CE_SUBJECT_ALT_NAME_OID))
1496 {
1497 PCRTCRX509GENERALNAMES pGeneralNames = (PCRTCRX509GENERALNAMES)pExt->ExtnValue.pEncapsulated;
1498 for (uint32_t j = 0; j < pGeneralNames->cItems; j++)
1499 if ( RTCRX509GENERALNAME_IS_DIRECTORY_NAME(&pGeneralNames->paItems[j])
1500 && RTCrX509Name_MatchByRfc5280(&pGeneralNames->paItems[j].u.pT4->DirectoryName, pName))
1501 return true;
1502 }
1503 }
1504 return false;
1505}
1506
1507
1508RTDECL(bool) RTCrX509Certificate_IsSelfSigned(PCRTCRX509CERTIFICATE pCertificate)
1509{
1510 if (RTCrX509Certificate_IsPresent(pCertificate))
1511 {
1512 return RTCrX509Name_MatchByRfc5280(&pCertificate->TbsCertificate.Subject,
1513 &pCertificate->TbsCertificate.Issuer);
1514 }
1515 return false;
1516}
1517
1518
1519/*
1520 * Set of X.509 Certificates.
1521 */
1522
1523RTDECL(PCRTCRX509CERTIFICATE)
1524RTCrX509Certificates_FindByIssuerAndSerialNumber(PCRTCRX509CERTIFICATES pCertificates,
1525 PCRTCRX509NAME pIssuer, PCRTASN1INTEGER pSerialNumber)
1526{
1527 for (uint32_t i = 0; i < pCertificates->cItems; i++)
1528 if (RTCrX509Certificate_MatchIssuerAndSerialNumber(&pCertificates->paItems[i], pIssuer, pSerialNumber))
1529 return &pCertificates->paItems[i];
1530 return NULL;
1531}
1532
1533
Note: See TracBrowser for help on using the repository browser.

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