2 * Copyright (c) 2006-2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecCertificate.c - CoreFoundation based certificate object
29 /* Allows us to build genanchors against the BaseSDK. */
30 #undef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
31 #undef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
34 #include <Security/SecCertificateInternal.h>
35 #include <utilities/SecIOFormat.h>
36 #include <CommonCrypto/CommonDigest.h>
37 #include <CoreFoundation/CFRuntime.h>
38 #include <CoreFoundation/CFString.h>
39 #include <CoreFoundation/CFBundle.h>
40 #include <CoreFoundation/CFDictionary.h>
41 #include <CoreFoundation/CFNumber.h>
42 #include <CoreFoundation/CFCalendar.h>
43 #include <CoreFoundation/CFTimeZone.h>
44 #include <CoreFoundation/CFXPCBridge.h>
46 #include <AssertMacros.h>
47 #include <libDER/DER_CertCrl.h>
48 #include <libDER/DER_Encode.h>
49 #include <libDER/DER_Keys.h>
50 #include <libDER/asn1Types.h>
51 #include <libDER/oidsPriv.h>
52 #include "SecBasePriv.h"
53 #include "SecRSAKey.h"
54 #include "SecFramework.h"
56 #include "SecItemPriv.h"
58 #include <utilities/debugging.h>
59 #include <utilities/SecCFWrappers.h>
60 #include <utilities/SecCFError.h>
61 #include <utilities/SecSCTUtils.h>
62 #include <utilities/array_size.h>
64 #include <libkern/OSByteOrder.h>
66 #include <Security/SecInternal.h>
67 #include <Security/SecFrameworkStrings.h>
68 #include "SecBase64.h"
69 #include "AppleBaselineEscrowCertificates.h"
70 #include <ipc/securityd_client.h>
72 typedef struct SecCertificateExtension
{
76 } SecCertificateExtension
;
79 typedef struct KnownExtension
{
85 kSecSelfSignedUnknown
= 0,
91 struct __SecCertificate
{
94 DERItem _der
; /* Entire certificate in DER form. */
95 DERItem _tbs
; /* To Be Signed cert DER bytes. */
96 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
97 DERItem _signature
; /* The content of the sig bit string. */
100 DERItem _serialNum
; /* Integer. */
101 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
102 DERItem _issuer
; /* Sequence of RDN. */
103 CFAbsoluteTime _notBefore
;
104 CFAbsoluteTime _notAfter
;
105 DERItem _subject
; /* Sequence of RDN. */
106 DERItem _subjectPublicKeyInfo
; /* SPKI */
107 DERAlgorithmId _algId
; /* oid and params of _pubKeyDER. */
108 DERItem _pubKeyDER
; /* contents of bit string */
109 DERItem _issuerUniqueID
; /* bit string, optional */
110 DERItem _subjectUniqueID
; /* bit string, optional */
113 /* Known extensions if the certificate contains them,
114 extnValue.length will be > 0. */
115 KnownExtension _authorityKeyID
;
117 /* This extension is used to uniquely identify a certificate from among
118 several that have the same subject name. If the extension is not
119 present, its value is calculated by performing a SHA-1 hash of the
120 certificate's DER encoded subjectPublicKeyInfo, as recommended by
122 KnownExtension _subjectKeyID
;
123 KnownExtension _keyUsage
;
124 KnownExtension _extendedKeyUsage
;
125 KnownExtension _basicConstraints
;
126 KnownExtension _netscapeCertType
;
127 KnownExtension _subjectAltName
;
128 KnownExtension _qualCertStatements
;
131 bool _foundUnknownCriticalExtension
;
133 /* Well known certificate extensions. */
134 SecCEBasicConstraints _basicConstraints
;
135 SecCEPolicyConstraints _policyConstraints
;
136 CFDictionaryRef _policyMappings
;
137 SecCECertificatePolicies _certificatePolicies
;
139 /* If InhibitAnyPolicy extension is not present or invalid UINT32_MAX,
140 value of the SkipCerts field of the InhibitAnyPolicy extension
142 uint32_t _inhibitAnyPolicySkipCerts
;
144 /* If KeyUsage extension is not present this is 0, otherwise it's
145 the value of the extension. */
146 SecKeyUsage _keyUsage
;
148 /* OCTECTS of SubjectKeyIdentifier extensions KeyIdentifier.
149 Length = 0 if not present. */
150 DERItem _subjectKeyIdentifier
;
152 /* OCTECTS of AuthorityKeyIdentifier extensions KeyIdentifier.
153 Length = 0 if not present. */
154 DERItem _authorityKeyIdentifier
;
155 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
156 _authorityKeyIdentifierSerialNumber have non zero length if present.
157 Both are either present or absent together. */
158 DERItem _authorityKeyIdentifierIssuer
;
159 DERItem _authorityKeyIdentifierSerialNumber
;
161 /* Subject alt name extension, if present. Not malloced, it's just a
162 pointer to an element in the _extensions array. */
163 const SecCertificateExtension
*_subjectAltName
;
165 /* Parsed extension values. */
167 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
168 CFMutableArrayRef _crlDistributionPoints
;
170 /* Array of CFURLRefs containing the URI values of accessLocations of each
171 id-ad-ocsp AccessDescription in the Authority Information Access
173 CFMutableArrayRef _ocspResponders
;
175 /* Array of CFURLRefs containing the URI values of accessLocations of each
176 id-ad-caIssuers AccessDescription in the Authority Information Access
178 CFMutableArrayRef _caIssuers
;
180 /* Array of CFDataRefs containing the generalNames for permittedSubtrees
182 CFArrayRef _permittedSubtrees
;
184 /* Array of CFDataRefs containing the generalNames for excludedSubtrees
186 CFArrayRef _excludedSubtrees
;
188 CFMutableArrayRef _embeddedSCTs
;
190 /* All other (non known) extensions. The _extensions array is malloced. */
191 CFIndex _extensionCount
;
192 SecCertificateExtension
*_extensions
;
194 /* Optional cached fields. */
197 CFArrayRef _properties
;
198 CFDataRef _serialNumber
;
199 CFDataRef _normalizedIssuer
;
200 CFDataRef _normalizedSubject
;
201 CFDataRef _authorityKeyID
;
202 CFDataRef _subjectKeyID
;
204 CFDataRef _sha1Digest
;
205 CFTypeRef _keychain_item
;
206 uint8_t _isSelfSigned
;
210 #define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v);
212 SEC_CONST_DECL (kSecCertificateProductionEscrowKey
, "ProductionEscrowKey");
213 SEC_CONST_DECL (kSecCertificateProductionPCSEscrowKey
, "ProductionPCSEscrowKey");
214 SEC_CONST_DECL (kSecCertificateEscrowFileName
, "AppleESCertificates");
216 /* Public Constants for property list keys. */
217 SEC_CONST_DECL (kSecPropertyKeyType
, "type");
218 SEC_CONST_DECL (kSecPropertyKeyLabel
, "label");
219 SEC_CONST_DECL (kSecPropertyKeyLocalizedLabel
, "localized label");
220 SEC_CONST_DECL (kSecPropertyKeyValue
, "value");
222 /* Public Constants for property list values. */
223 SEC_CONST_DECL (kSecPropertyTypeWarning
, "warning");
224 SEC_CONST_DECL (kSecPropertyTypeError
, "error");
225 SEC_CONST_DECL (kSecPropertyTypeSuccess
, "success");
226 SEC_CONST_DECL (kSecPropertyTypeTitle
, "title");
227 SEC_CONST_DECL (kSecPropertyTypeSection
, "section");
228 SEC_CONST_DECL (kSecPropertyTypeData
, "data");
229 SEC_CONST_DECL (kSecPropertyTypeString
, "string");
230 SEC_CONST_DECL (kSecPropertyTypeURL
, "url");
231 SEC_CONST_DECL (kSecPropertyTypeDate
, "date");
233 /* Extension parsing routine. */
234 typedef void (*SecCertificateExtensionParser
)(SecCertificateRef certificate
,
235 const SecCertificateExtension
*extn
);
237 /* Mapping from extension OIDs (as a DERItem *) to
238 SecCertificateExtensionParser extension parsing routines. */
239 static CFDictionaryRef sExtensionParsers
;
241 /* Forward declarations of static functions. */
242 static CFStringRef
SecCertificateDescribe(CFTypeRef cf
);
243 static void SecCertificateDestroy(CFTypeRef cf
);
244 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
245 CFAbsoluteTime
*absTime
) __attribute__((__nonnull__
));
247 /* Static functions. */
248 static CF_RETURNS_RETAINED CFStringRef
SecCertificateDescribe(CFTypeRef cf
) {
249 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
250 CFStringRef subject
= SecCertificateCopySubjectSummary(certificate
);
251 CFStringRef issuer
= SecCertificateCopyIssuerSummary(certificate
);
252 CFStringRef desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
253 CFSTR("<cert(%p) s: %@ i: %@>"), certificate
, subject
, issuer
);
254 CFReleaseSafe(issuer
);
255 CFReleaseSafe(subject
);
259 static void SecCertificateDestroy(CFTypeRef cf
) {
260 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
261 if (certificate
->_certificatePolicies
.policies
)
262 free(certificate
->_certificatePolicies
.policies
);
263 CFReleaseSafe(certificate
->_policyMappings
);
264 CFReleaseSafe(certificate
->_crlDistributionPoints
);
265 CFReleaseSafe(certificate
->_ocspResponders
);
266 CFReleaseSafe(certificate
->_caIssuers
);
267 if (certificate
->_extensions
) {
268 free(certificate
->_extensions
);
270 CFReleaseSafe(certificate
->_pubKey
);
271 CFReleaseSafe(certificate
->_der_data
);
272 CFReleaseSafe(certificate
->_properties
);
273 CFReleaseSafe(certificate
->_serialNumber
);
274 CFReleaseSafe(certificate
->_normalizedIssuer
);
275 CFReleaseSafe(certificate
->_normalizedSubject
);
276 CFReleaseSafe(certificate
->_authorityKeyID
);
277 CFReleaseSafe(certificate
->_subjectKeyID
);
278 CFReleaseSafe(certificate
->_sha1Digest
);
279 CFReleaseSafe(certificate
->_keychain_item
);
280 CFReleaseSafe(certificate
->_permittedSubtrees
);
281 CFReleaseSafe(certificate
->_excludedSubtrees
);
284 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
285 SecCertificateRef cert1
= (SecCertificateRef
)cf1
;
286 SecCertificateRef cert2
= (SecCertificateRef
)cf2
;
289 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
291 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
294 /* Hash of the certificate is der length + signature length + last 4 bytes
296 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
297 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
298 size_t der_length
= certificate
->_der
.length
;
299 size_t sig_length
= certificate
->_signature
.length
;
300 size_t ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
301 CFHashCode hashCode
= 0;
302 for (; ix
< sig_length
; ++ix
)
303 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
305 return (hashCode
+ der_length
+ sig_length
);
310 /************************************************************************/
311 /************************* General Name Parsing *************************/
312 /************************************************************************/
314 GeneralName ::= CHOICE {
315 otherName [0] OtherName,
316 rfc822Name [1] IA5String,
317 dNSName [2] IA5String,
318 x400Address [3] ORAddress,
319 directoryName [4] Name,
320 ediPartyName [5] EDIPartyName,
321 uniformResourceIdentifier [6] IA5String,
322 iPAddress [7] OCTET STRING,
323 registeredID [8] OBJECT IDENTIFIER}
325 OtherName ::= SEQUENCE {
326 type-id OBJECT IDENTIFIER,
327 value [0] EXPLICIT ANY DEFINED BY type-id }
329 EDIPartyName ::= SEQUENCE {
330 nameAssigner [0] DirectoryString OPTIONAL,
331 partyName [1] DirectoryString }
333 OSStatus
SecCertificateParseGeneralNameContentProperty(DERTag tag
,
334 const DERItem
*generalNameContent
,
335 void *context
, parseGeneralNameCallback callback
) {
337 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
338 return callback(context
, GNT_OtherName
, generalNameContent
);
339 case ASN1_CONTEXT_SPECIFIC
| 1:
340 return callback(context
, GNT_RFC822Name
, generalNameContent
);
341 case ASN1_CONTEXT_SPECIFIC
| 2:
342 return callback(context
, GNT_DNSName
, generalNameContent
);
343 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
344 return callback(context
, GNT_X400Address
, generalNameContent
);
345 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
346 return callback(context
, GNT_DirectoryName
, generalNameContent
);
347 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
348 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
349 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
351 /* Technically I don't think this is valid, but there are certs out
352 in the wild that use a constructed IA5String. In particular the
353 VeriSign Time Stamping Authority CA.cer does this. */
354 DERDecodedInfo uriContent
;
355 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
356 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
357 return callback(context
, GNT_URI
, &uriContent
.content
);
359 case ASN1_CONTEXT_SPECIFIC
| 6:
360 return callback(context
, GNT_URI
, generalNameContent
);
361 case ASN1_CONTEXT_SPECIFIC
| 7:
362 return callback(context
, GNT_IPAddress
, generalNameContent
);
363 case ASN1_CONTEXT_SPECIFIC
| 8:
364 return callback(context
, GNT_RegisteredID
, generalNameContent
);
369 return errSecInvalidCertificate
;
372 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
373 void *context
, parseGeneralNameCallback callback
) {
375 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
376 require_noerr_quiet(drtn
, badDER
);
377 DERDecodedInfo generalNameContent
;
378 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
380 OSStatus status
= SecCertificateParseGeneralNameContentProperty(
381 generalNameContent
.tag
, &generalNameContent
.content
, context
,
386 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
387 return errSecSuccess
;
390 return errSecInvalidCertificate
;
393 OSStatus
SecCertificateParseGeneralNames(const DERItem
*generalNames
, void *context
,
394 parseGeneralNameCallback callback
) {
395 DERDecodedInfo generalNamesContent
;
396 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
397 require_noerr_quiet(drtn
, badDER
);
398 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
399 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
402 return errSecInvalidCertificate
;
408 GeneralName ::= CHOICE {
409 otherName [0] OtherName,
410 rfc822Name [1] IA5String,
411 dNSName [2] IA5String,
412 x400Address [3] ORAddress,
413 directoryName [4] Name,
414 ediPartyName [5] EDIPartyName,
415 uniformResourceIdentifier [6] IA5String,
416 iPAddress [7] OCTET STRING,
417 registeredID [8] OBJECT IDENTIFIER}
419 EDIPartyName ::= SEQUENCE {
420 nameAssigner [0] DirectoryString OPTIONAL,
421 partyName [1] DirectoryString }
423 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
424 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
426 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
427 generalName
->nameType
= GNT_OtherName
;
428 generalName
->berEncoded
= true;
429 generalName
->name
= *generalNameContent
;
431 case ASN1_CONTEXT_SPECIFIC
| 1:
433 generalName
->nameType
= GNT_RFC822Name
;
434 generalName
->berEncoded
= false;
435 generalName
->name
= *generalNameContent
;
437 case ASN1_CONTEXT_SPECIFIC
| 2:
439 generalName
->nameType
= GNT_DNSName
;
440 generalName
->berEncoded
= false;
441 generalName
->name
= *generalNameContent
;
443 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
444 generalName
->nameType
= GNT_X400Address
;
445 generalName
->berEncoded
= true;
446 generalName
->name
= *generalNameContent
;
448 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
449 generalName
->nameType
= GNT_DirectoryName
;
450 generalName
->berEncoded
= true;
451 generalName
->name
= *generalNameContent
;
453 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
454 generalName
->nameType
= GNT_EdiPartyName
;
455 generalName
->berEncoded
= true;
456 generalName
->name
= *generalNameContent
;
458 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
460 /* Technically I don't think this is valid, but there are certs out
461 in the wild that use a constructed IA5String. In particular the
462 VeriSign Time Stamping Authority CA.cer does this. */
463 DERDecodedInfo decoded
;
464 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
465 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
466 generalName
->nameType
= GNT_URI
;
467 generalName
->berEncoded
= false;
468 generalName
->name
= decoded
.content
;
471 case ASN1_CONTEXT_SPECIFIC
| 6:
472 generalName
->nameType
= GNT_URI
;
473 generalName
->berEncoded
= false;
474 generalName
->name
= *generalNameContent
;
476 case ASN1_CONTEXT_SPECIFIC
| 7:
477 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
478 8 octects, addr/mask for ipv6 it's 32. */
479 generalName
->nameType
= GNT_IPAddress
;
480 generalName
->berEncoded
= false;
481 generalName
->name
= *generalNameContent
;
483 case ASN1_CONTEXT_SPECIFIC
| 8:
484 /* name is the content of an OID. */
485 generalName
->nameType
= GNT_RegisteredID
;
486 generalName
->berEncoded
= false;
487 generalName
->name
= *generalNameContent
;
493 return errSecSuccess
;
495 return errSecInvalidCertificate
;
499 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
501 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
502 CFIndex
*count
, SecCEGeneralName
**name
) {
503 SecCEGeneralName
*generalNames
= NULL
;
505 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
506 require_noerr_quiet(drtn
, badDER
);
507 DERDecodedInfo generalNameContent
;
508 CFIndex generalNamesCount
= 0;
509 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
513 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
515 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
517 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
519 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
521 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
522 &generalNameContent
.content
, &generalNames
[ix
])) {
527 *count
= generalNamesCount
;
528 *name
= generalNames
;
529 return errSecSuccess
;
534 return errSecInvalidCertificate
;
537 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
538 CFIndex
*count
, SecCEGeneralName
**name
) {
539 DERDecodedInfo generalNamesContent
;
540 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
541 require_noerr_quiet(drtn
, badDER
);
542 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
544 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
545 return errSecSuccess
;
547 return errSecInvalidCertificate
;
551 /************************************************************************/
552 /************************** X.509 Name Parsing **************************/
553 /************************************************************************/
555 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
556 const DERItem
*value
, CFIndex rdnIX
);
558 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
559 parseX501NameCallback callback
) {
561 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
562 require_noerr_quiet(drtn
, badDER
);
563 DERDecodedInfo atvContent
;
565 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
566 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
567 DERAttributeTypeAndValue atv
;
568 drtn
= DERParseSequenceContent(&atvContent
.content
,
569 DERNumAttributeTypeAndValueItemSpecs
,
570 DERAttributeTypeAndValueItemSpecs
,
572 require_noerr_quiet(drtn
, badDER
);
573 require_quiet(atv
.type
.length
!= 0, badDER
);
574 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++);
578 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
580 return errSecSuccess
;
582 return errSecInvalidCertificate
;
585 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
586 parseX501NameCallback callback
) {
588 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
589 require_noerr_quiet(drtn
, badDER
);
590 DERDecodedInfo currDecoded
;
591 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
592 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
593 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
598 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
600 return errSecSuccess
;
603 return errSecInvalidCertificate
;
606 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
607 parseX501NameCallback callback
) {
608 DERDecodedInfo x501NameContent
;
609 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
610 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
611 return errSecInvalidCertificate
;
613 return parseX501NameContent(&x501NameContent
.content
, context
,
618 /************************************************************************/
619 /********************** Extension Parsing Routines **********************/
620 /************************************************************************/
622 static void SecCEPSubjectKeyIdentifier(SecCertificateRef certificate
,
623 const SecCertificateExtension
*extn
) {
624 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
625 DERDecodedInfo keyIdentifier
;
626 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
627 require_noerr_quiet(drtn
, badDER
);
628 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
629 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
633 secwarning("Invalid SubjectKeyIdentifier Extension");
636 static void SecCEPKeyUsage(SecCertificateRef certificate
,
637 const SecCertificateExtension
*extn
) {
638 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
639 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
640 DERDecodedInfo bitStringContent
;
641 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
642 require_noerr_quiet(drtn
, badDER
);
643 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
644 DERSize len
= bitStringContent
.content
.length
- 1;
645 require_quiet(len
== 1 || len
== 2, badDER
);
646 DERByte numUnusedBits
= bitStringContent
.content
.data
[0];
647 require_quiet(numUnusedBits
< 8, badDER
);
648 /* Flip the bits in the bit string so the first bit in the lsb. */
649 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
650 uint_fast16_t value
= bitStringContent
.content
.data
[1];
653 value
= (value
<< 8) + bitStringContent
.content
.data
[2];
659 for (ix
= 0; ix
< bits
; ++ix
) {
665 certificate
->_keyUsage
= keyUsage
;
668 certificate
->_keyUsage
= kSecKeyUsageUnspecified
;
671 static void SecCEPPrivateKeyUsagePeriod(SecCertificateRef certificate
,
672 const SecCertificateExtension
*extn
) {
673 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
676 static void SecCEPSubjectAltName(SecCertificateRef certificate
,
677 const SecCertificateExtension
*extn
) {
678 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
679 certificate
->_subjectAltName
= extn
;
682 static void SecCEPIssuerAltName(SecCertificateRef certificate
,
683 const SecCertificateExtension
*extn
) {
684 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
687 static void SecCEPBasicConstraints(SecCertificateRef certificate
,
688 const SecCertificateExtension
*extn
) {
689 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
690 DERBasicConstraints basicConstraints
;
691 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
692 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
693 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
694 require_noerr_quiet(DERParseBoolean(&basicConstraints
.cA
, false,
695 &certificate
->_basicConstraints
.isCA
), badDER
);
696 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
697 require_noerr_quiet(DERParseInteger(
698 &basicConstraints
.pathLenConstraint
,
699 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
700 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
702 certificate
->_basicConstraints
.present
= true;
703 certificate
->_basicConstraints
.critical
= extn
->critical
;
706 certificate
->_basicConstraints
.present
= false;
707 secwarning("Invalid BasicConstraints Extension");
712 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
714 * NameConstraints ::= SEQUENCE {
715 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
716 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
718 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
720 * GeneralSubtree ::= SEQUENCE {
722 * minimum [0] BaseDistance DEFAULT 0,
723 * maximum [1] BaseDistance OPTIONAL }
725 * BaseDistance ::= INTEGER (0..MAX)
727 static DERReturn
parseGeneralSubtrees(DERItem
*derSubtrees
, CFArrayRef
*generalSubtrees
) {
728 CFMutableArrayRef gs
= NULL
;
730 DERReturn drtn
= DERDecodeSeqContentInit(derSubtrees
, &gsSeq
);
731 require_noerr_quiet(drtn
, badDER
);
732 DERDecodedInfo gsContent
;
733 require_quiet(gs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
734 &kCFTypeArrayCallBacks
),
736 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
737 DERGeneralSubtree derGS
;
738 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
739 drtn
= DERParseSequenceContent(&gsContent
.content
,
740 DERNumGeneralSubtreeItemSpecs
,
741 DERGeneralSubtreeItemSpecs
,
742 &derGS
, sizeof(derGS
));
743 require_noerr_quiet(drtn
, badDER
);
746 * Within this profile, the minimum and maximum fields are not used with
747 * any name forms, thus, the minimum MUST be zero, and maximum MUST be
750 * Because minimum DEFAULT 0, absence equivalent to present and 0.
752 if (derGS
.minimum
.length
) {
754 require_noerr_quiet(DERParseInteger(&derGS
.minimum
, &minimum
),
756 require_quiet(minimum
== 0, badDER
);
758 require_quiet(derGS
.maximum
.length
== 0, badDER
);
759 require_quiet(derGS
.generalName
.length
!= 0, badDER
);
761 CFDataRef generalName
= NULL
;
762 require_quiet(generalName
= CFDataCreate(kCFAllocatorDefault
,
763 derGS
.generalName
.data
,
764 derGS
.generalName
.length
),
766 CFArrayAppendValue(gs
, generalName
);
767 CFReleaseNull(generalName
);
770 *generalSubtrees
= gs
;
772 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
777 secdebug("cert","failed to parse GeneralSubtrees");
781 static void SecCEPNameConstraints(SecCertificateRef certificate
,
782 const SecCertificateExtension
*extn
) {
783 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
784 DERNameConstraints nc
;
786 drtn
= DERParseSequence(&extn
->extnValue
,
787 DERNumNameConstraintsItemSpecs
,
788 DERNameConstraintsItemSpecs
,
790 require_noerr_quiet(drtn
, badDER
);
791 if (nc
.permittedSubtrees
.length
) {
792 require_noerr_quiet(parseGeneralSubtrees(&nc
.permittedSubtrees
, &certificate
->_permittedSubtrees
), badDER
);
794 if (nc
.excludedSubtrees
.length
) {
795 require_noerr_quiet(parseGeneralSubtrees(&nc
.excludedSubtrees
, &certificate
->_excludedSubtrees
), badDER
);
800 secdebug("cert", "failed to parse Name Constraints extension");
803 static void SecCEPCrlDistributionPoints(SecCertificateRef certificate
,
804 const SecCertificateExtension
*extn
) {
805 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
809 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
811 PolicyInformation ::= SEQUENCE {
812 policyIdentifier CertPolicyId,
813 policyQualifiers SEQUENCE SIZE (1..MAX) OF
814 PolicyQualifierInfo OPTIONAL }
816 CertPolicyId ::= OBJECT IDENTIFIER
818 PolicyQualifierInfo ::= SEQUENCE {
819 policyQualifierId PolicyQualifierId,
820 qualifier ANY DEFINED BY policyQualifierId }
822 static void SecCEPCertificatePolicies(SecCertificateRef certificate
,
823 const SecCertificateExtension
*extn
) {
824 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
827 SecCEPolicyInformation
*policies
= NULL
;
828 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
829 require_noerr_quiet(drtn
, badDER
);
830 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
831 DERDecodedInfo piContent
;
832 DERSize policy_count
= 0;
833 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
834 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
837 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
838 policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
)
839 * (policy_count
> 0 ? policy_count
: 1));
840 DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
841 DERSize policy_ix
= 0;
842 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
843 DERPolicyInformation pi
;
844 drtn
= DERParseSequenceContent(&piContent
.content
,
845 DERNumPolicyInformationItemSpecs
,
846 DERPolicyInformationItemSpecs
,
848 require_noerr_quiet(drtn
, badDER
);
849 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
850 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
852 certificate
->_certificatePolicies
.present
= true;
853 certificate
->_certificatePolicies
.critical
= extn
->critical
;
854 certificate
->_certificatePolicies
.numPolicies
= policy_count
;
855 certificate
->_certificatePolicies
.policies
= policies
;
860 certificate
->_certificatePolicies
.present
= false;
861 secwarning("Invalid CertificatePolicies Extension");
865 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
867 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
868 issuerDomainPolicy CertPolicyId,
869 subjectDomainPolicy CertPolicyId }
872 static void SecCEPPolicyMappings(SecCertificateRef certificate
,
873 const SecCertificateExtension
*extn
) {
874 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
877 SecCEPolicyMapping
*mappings
= NULL
;
878 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
879 require_noerr_quiet(drtn
, badDER
);
880 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
881 DERDecodedInfo pmContent
;
882 DERSize mapping_count
= 0;
883 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
884 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
887 mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
889 DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
890 DERSize mapping_ix
= 0;
891 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
893 drtn
= DERParseSequenceContent(&pmContent
.content
,
894 DERNumPolicyMappingItemSpecs
,
895 DERPolicyMappingItemSpecs
,
897 require_noerr_quiet(drtn
, badDER
);
898 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
899 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
901 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
902 certificate
->_policyMappings
.present
= true;
903 certificate
->_policyMappings
.critical
= extn
->critical
;
904 certificate
->_policyMappings
.numMappings
= mapping_count
;
905 certificate
->_policyMappings
.mappings
= mappings
;
910 CFReleaseSafe(mappings
);
911 certificate
->_policyMappings
.present
= false;
912 secwarning("Invalid CertificatePolicies Extension");
915 static void SecCEPPolicyMappings(SecCertificateRef certificate
,
916 const SecCertificateExtension
*extn
) {
917 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
920 CFMutableDictionaryRef mappings
= NULL
;
921 CFDataRef idp
= NULL
, sdp
= NULL
;
922 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
923 require_noerr_quiet(drtn
, badDER
);
924 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
925 DERDecodedInfo pmContent
;
926 require_quiet(mappings
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
927 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
),
929 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
930 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
932 drtn
= DERParseSequenceContent(&pmContent
.content
,
933 DERNumPolicyMappingItemSpecs
,
934 DERPolicyMappingItemSpecs
,
936 require_noerr_quiet(drtn
, badDER
);
937 require_quiet(idp
= CFDataCreate(kCFAllocatorDefault
,
938 pm
.issuerDomainPolicy
.data
, pm
.issuerDomainPolicy
.length
), badDER
);
939 require_quiet(sdp
= CFDataCreate(kCFAllocatorDefault
,
940 pm
.subjectDomainPolicy
.data
, pm
.subjectDomainPolicy
.length
), badDER
);
941 CFMutableArrayRef sdps
=
942 (CFMutableArrayRef
)CFDictionaryGetValue(mappings
, idp
);
944 CFArrayAppendValue(sdps
, sdp
);
946 require_quiet(sdps
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
947 &kCFTypeArrayCallBacks
), badDER
);
948 CFDictionarySetValue(mappings
, idp
, sdps
);
954 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
955 certificate
->_policyMappings
= mappings
;
960 CFReleaseSafe(mappings
);
961 certificate
->_policyMappings
= NULL
;
962 secwarning("Invalid CertificatePolicies Extension");
967 AuthorityKeyIdentifier ::= SEQUENCE {
968 keyIdentifier [0] KeyIdentifier OPTIONAL,
969 authorityCertIssuer [1] GeneralNames OPTIONAL,
970 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
971 -- authorityCertIssuer and authorityCertSerialNumber MUST both
972 -- be present or both be absent
974 KeyIdentifier ::= OCTET STRING
976 static void SecCEPAuthorityKeyIdentifier(SecCertificateRef certificate
,
977 const SecCertificateExtension
*extn
) {
978 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
979 DERAuthorityKeyIdentifier akid
;
981 drtn
= DERParseSequence(&extn
->extnValue
,
982 DERNumAuthorityKeyIdentifierItemSpecs
,
983 DERAuthorityKeyIdentifierItemSpecs
,
984 &akid
, sizeof(akid
));
985 require_noerr_quiet(drtn
, badDER
);
986 if (akid
.keyIdentifier
.length
) {
987 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
989 if (akid
.authorityCertIssuer
.length
||
990 akid
.authorityCertSerialNumber
.length
) {
991 require_quiet(akid
.authorityCertIssuer
.length
&&
992 akid
.authorityCertSerialNumber
.length
, badDER
);
993 /* Perhaps put in a subsection called Authority Certificate Issuer. */
994 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
995 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
1000 secwarning("Invalid AuthorityKeyIdentifier Extension");
1003 static void SecCEPPolicyConstraints(SecCertificateRef certificate
,
1004 const SecCertificateExtension
*extn
) {
1005 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1006 DERPolicyConstraints pc
;
1008 drtn
= DERParseSequence(&extn
->extnValue
,
1009 DERNumPolicyConstraintsItemSpecs
,
1010 DERPolicyConstraintsItemSpecs
,
1012 require_noerr_quiet(drtn
, badDER
);
1013 if (pc
.requireExplicitPolicy
.length
) {
1014 require_noerr_quiet(DERParseInteger(
1015 &pc
.requireExplicitPolicy
,
1016 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
1017 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
1019 if (pc
.inhibitPolicyMapping
.length
) {
1020 require_noerr_quiet(DERParseInteger(
1021 &pc
.inhibitPolicyMapping
,
1022 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
1023 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
1026 certificate
->_policyConstraints
.present
= true;
1027 certificate
->_policyConstraints
.critical
= extn
->critical
;
1031 certificate
->_policyConstraints
.present
= false;
1032 secwarning("Invalid PolicyConstraints Extension");
1035 static void SecCEPExtendedKeyUsage(SecCertificateRef certificate
,
1036 const SecCertificateExtension
*extn
) {
1037 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1041 InhibitAnyPolicy ::= SkipCerts
1043 SkipCerts ::= INTEGER (0..MAX)
1045 static void SecCEPInhibitAnyPolicy(SecCertificateRef certificate
,
1046 const SecCertificateExtension
*extn
) {
1047 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1048 require_noerr_quiet(DERParseInteger(
1050 &certificate
->_inhibitAnyPolicySkipCerts
), badDER
);
1053 certificate
->_inhibitAnyPolicySkipCerts
= UINT32_MAX
;
1054 secwarning("Invalid InhibitAnyPolicy Extension");
1058 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
1060 AuthorityInfoAccessSyntax ::=
1061 SEQUENCE SIZE (1..MAX) OF AccessDescription
1063 AccessDescription ::= SEQUENCE {
1064 accessMethod OBJECT IDENTIFIER,
1065 accessLocation GeneralName }
1067 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
1069 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
1071 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
1073 static void SecCEPAuthorityInfoAccess(SecCertificateRef certificate
,
1074 const SecCertificateExtension
*extn
) {
1075 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1078 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
1079 require_noerr_quiet(drtn
, badDER
);
1080 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1081 DERDecodedInfo adContent
;
1082 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
1083 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1084 DERAccessDescription ad
;
1085 drtn
= DERParseSequenceContent(&adContent
.content
,
1086 DERNumAccessDescriptionItemSpecs
,
1087 DERAccessDescriptionItemSpecs
,
1089 require_noerr_quiet(drtn
, badDER
);
1090 CFMutableArrayRef
*urls
;
1091 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
1092 urls
= &certificate
->_ocspResponders
;
1093 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
1094 urls
= &certificate
->_caIssuers
;
1098 DERDecodedInfo generalNameContent
;
1099 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
1100 require_noerr_quiet(drtn
, badDER
);
1101 switch (generalNameContent
.tag
) {
1103 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
1104 /* Technically I don't think this is valid, but there are certs out
1105 in the wild that use a constructed IA5String. In particular the
1106 VeriSign Time Stamping Authority CA.cer does this. */
1108 case ASN1_CONTEXT_SPECIFIC
| 6:
1110 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1111 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
1112 kCFStringEncodingASCII
, NULL
);
1115 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1116 CFArrayAppendValue(*urls
, url
);
1122 secdebug("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02x v: %.*s",
1123 generalNameContent
.tag
, (int) generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1128 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1131 secdebug("cert", "failed to parse Authority Information Access extension");
1134 /* Apple Worldwide Developer Relations Certificate Authority subject name.
1135 * This is a DER sequence with the leading tag and length bytes removed,
1136 * to match what tbsCert.issuer contains.
1138 static const unsigned char Apple_WWDR_CA_Subject_Name
[]={
1139 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,
1140 0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0A,0x41,0x70,0x70,0x6C,0x65,
1141 0x20,0x49,0x6E,0x63,0x2E,0x31,0x2C,0x30,0x2A,0x06,0x03,0x55,0x04,0x0B,0x0C,0x23,
1142 0x41,0x70,0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,
1143 0x44,0x65,0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,
1144 0x6F,0x6E,0x73,0x31,0x44,0x30,0x42,0x06,0x03,0x55,0x04,0x03,0x0C,0x3B,0x41,0x70,
1145 0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,0x44,0x65,
1146 0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,0x6F,0x6E,
1147 0x73,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,
1148 0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79
1151 static void checkForMissingRevocationInfo(SecCertificateRef certificate
) {
1153 certificate
->_crlDistributionPoints
||
1154 certificate
->_ocspResponders
) {
1155 /* We already have an OCSP or CRL URI (or no cert) */
1158 /* Specify an appropriate OCSP responder if we recognize the issuer. */
1159 CFURLRef url
= NULL
;
1160 if (sizeof(Apple_WWDR_CA_Subject_Name
) == certificate
->_issuer
.length
&&
1161 !memcmp(certificate
->_issuer
.data
, Apple_WWDR_CA_Subject_Name
,
1162 sizeof(Apple_WWDR_CA_Subject_Name
))) {
1163 const char *WWDR_OCSP_URI
= "http://ocsp.apple.com/ocsp-wwdr01";
1164 url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1165 (const UInt8
*)WWDR_OCSP_URI
, strlen(WWDR_OCSP_URI
),
1166 kCFStringEncodingASCII
, NULL
);
1169 CFMutableArrayRef
*urls
= &certificate
->_ocspResponders
;
1170 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1171 CFArrayAppendValue(*urls
, url
);
1176 static void SecCEPSubjectInfoAccess(SecCertificateRef certificate
,
1177 const SecCertificateExtension
*extn
) {
1178 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1181 static void SecCEPNetscapeCertType(SecCertificateRef certificate
,
1182 const SecCertificateExtension
*extn
) {
1183 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1186 static void SecCEPEntrustVersInfo(SecCertificateRef certificate
,
1187 const SecCertificateExtension
*extn
) {
1188 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1191 static void SecCEPEscrowMarker(SecCertificateRef certificate
,
1192 const SecCertificateExtension
*extn
) {
1193 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1197 /* Dictionary key callback for comparing to DERItems. */
1198 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1199 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1202 /* Dictionary key callback calculating the hash of a DERItem. */
1203 static CFHashCode
SecDERItemHash(const void *value
) {
1204 const DERItem
*derItem
= (const DERItem
*)value
;
1205 CFHashCode hash
= derItem
->length
;
1206 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1207 for (; ix
< derItem
->length
; ++ix
) {
1208 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1214 /* Dictionary key callbacks using the above 2 functions. */
1215 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1219 NULL
, /* copyDescription */
1220 SecDERItemEqual
, /* equal */
1221 SecDERItemHash
/* hash */
1224 static void SecCertificateInitializeExtensionParsers(void) {
1225 /* Build a dictionary that maps from extension OIDs to callback functions
1226 which can parse the extension of the type given. */
1227 static const void *extnOIDs
[] = {
1228 &oidSubjectKeyIdentifier
,
1230 &oidPrivateKeyUsagePeriod
,
1233 &oidBasicConstraints
,
1234 &oidNameConstraints
,
1235 &oidCrlDistributionPoints
,
1236 &oidCertificatePolicies
,
1238 &oidAuthorityKeyIdentifier
,
1239 &oidPolicyConstraints
,
1240 &oidExtendedKeyUsage
,
1241 &oidInhibitAnyPolicy
,
1242 &oidAuthorityInfoAccess
,
1243 &oidSubjectInfoAccess
,
1244 &oidNetscapeCertType
,
1245 &oidEntrustVersInfo
,
1246 &oidApplePolicyEscrowService
1248 static const void *extnParsers
[] = {
1249 SecCEPSubjectKeyIdentifier
,
1251 SecCEPPrivateKeyUsagePeriod
,
1252 SecCEPSubjectAltName
,
1253 SecCEPIssuerAltName
,
1254 SecCEPBasicConstraints
,
1255 SecCEPNameConstraints
,
1256 SecCEPCrlDistributionPoints
,
1257 SecCEPCertificatePolicies
,
1258 SecCEPPolicyMappings
,
1259 SecCEPAuthorityKeyIdentifier
,
1260 SecCEPPolicyConstraints
,
1261 SecCEPExtendedKeyUsage
,
1262 SecCEPInhibitAnyPolicy
,
1263 SecCEPAuthorityInfoAccess
,
1264 SecCEPSubjectInfoAccess
,
1265 SecCEPNetscapeCertType
,
1266 SecCEPEntrustVersInfo
,
1269 sExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1270 extnParsers
, array_size(extnOIDs
),
1271 &SecDERItemKeyCallBacks
, NULL
);
1274 CFGiblisWithFunctions(SecCertificate
, NULL
, NULL
, SecCertificateDestroy
, SecCertificateEqual
, SecCertificateHash
, NULL
, SecCertificateDescribe
, NULL
, NULL
, ^{
1275 SecCertificateInitializeExtensionParsers();
1278 static bool isAppleExtensionOID(const DERItem
*extnID
)
1280 static const uint8_t appleExtension
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x06 };
1281 return (extnID
&& extnID
->data
&&
1282 extnID
->length
> sizeof(appleExtension
) &&
1283 !memcmp(extnID
->data
, appleExtension
, sizeof(appleExtension
)));
1286 /* Given the contents of an X.501 Name return the contents of a normalized
1288 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1289 const DERItem
*x501name
) {
1290 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1291 CFIndex length
= x501name
->length
;
1292 CFDataSetLength(result
, length
);
1293 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1296 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1298 require_noerr_quiet(drtn
, badDER
);
1301 /* Always points to last rdn tag. */
1302 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1303 /* Offset relative to base of current rdn set tag. */
1304 CFIndex rdnTagLocation
= 0;
1305 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1306 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1307 /* We don't allow empty RDNs. */
1308 require_quiet(rdn
.content
.length
!= 0, badDER
);
1309 /* Length of the tag and length of the current rdn. */
1310 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1311 CFIndex rdnContentLength
= rdn
.content
.length
;
1312 /* Copy the tag and length of the RDN. */
1313 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1316 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1317 require_quiet(drtn
== DR_Success
, badDER
);
1320 /* Always points to tag of current atv sequence. */
1321 const DERByte
*atvTag
= atvSeq
.nextItem
;
1322 /* Offset relative to base of current atv sequence tag. */
1323 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1324 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1325 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1326 /* Length of the tag and length of the current atv. */
1327 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1328 CFIndex atvContentLength
= atv
.content
.length
;
1329 /* Copy the tag and length of the atv and the atv itself. */
1330 memcpy(base
+ atvTagLocation
, atvTag
,
1331 atvTLLength
+ atv
.content
.length
);
1333 /* Now decode the atv sequence. */
1334 DERAttributeTypeAndValue atvPair
;
1335 drtn
= DERParseSequenceContent(&atv
.content
,
1336 DERNumAttributeTypeAndValueItemSpecs
,
1337 DERAttributeTypeAndValueItemSpecs
,
1338 &atvPair
, sizeof(atvPair
));
1339 require_noerr_quiet(drtn
, badDER
);
1340 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1341 DERDecodedInfo value
;
1342 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1343 require_noerr_quiet(drtn
, badDER
);
1345 /* (c) attribute values in PrintableString are not case sensitive
1346 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1348 (d) attribute values in PrintableString are compared after
1349 removing leading and trailing white space and converting internal
1350 substrings of one or more consecutive white space characters to a
1352 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1353 /* Offset relative to base of current value tag. */
1354 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1355 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1356 CFIndex valueContentLength
= value
.content
.length
;
1358 /* Now copy all the bytes, but convert to upper case while
1359 doing so and convert multiple whitespace chars into a
1361 bool lastWasBlank
= false;
1362 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1363 CFIndex valueCurrentLocation
= valueLocation
;
1365 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1366 UInt8 ch
= value
.content
.data
[ix
];
1371 /* Don't insert a space for first character
1373 if (valueCurrentLocation
> valueLocation
) {
1374 base
[valueCurrentLocation
++] = ' ';
1376 lastWasBlank
= true;
1379 lastWasBlank
= false;
1380 if ('a' <= ch
&& ch
<= 'z') {
1381 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1383 base
[valueCurrentLocation
++] = ch
;
1387 /* Finally if lastWasBlank remove the trailing space. */
1388 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1389 valueCurrentLocation
--;
1391 /* Adjust content length to normalized length. */
1392 valueContentLength
= valueCurrentLocation
- valueLocation
;
1394 /* Number of bytes by which the length should be shorted. */
1395 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1396 if (lengthDiff
== 0) {
1397 /* Easy case no need to adjust lengths. */
1399 /* Hard work we need to go back and fix up length fields
1401 1) The value itself.
1402 2) The ATV Sequence containing type/value
1403 3) The RDN Set containing one or more atv pairs.
1407 /* Step 1 fix up length of value. */
1408 /* Length of value tag and length minus the tag. */
1409 DERSize newValueTLLength
= valueTLLength
- 1;
1410 drtn
= DEREncodeLength(valueContentLength
,
1411 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1412 require(drtn
== DR_Success
, badDER
);
1413 /* Add the length of the tag back in. */
1415 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1417 /* The size of the length field changed, let's slide
1418 the value back by valueLLDiff bytes. */
1419 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1420 base
+ valueTagLocation
+ valueTLLength
,
1421 valueContentLength
);
1422 /* The length diff for the enclosing object. */
1423 lengthDiff
+= valueLLDiff
;
1426 /* Step 2 fix up length of the enclosing ATV Sequence. */
1427 atvContentLength
-= lengthDiff
;
1428 DERSize newATVTLLength
= atvTLLength
- 1;
1429 drtn
= DEREncodeLength(atvContentLength
,
1430 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1431 require(drtn
== DR_Success
, badDER
);
1432 /* Add the length of the tag back in. */
1434 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1436 /* The size of the length field changed, let's slide
1437 the value back by valueLLDiff bytes. */
1438 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1439 base
+ atvTagLocation
+ atvTLLength
,
1441 /* The length diff for the enclosing object. */
1442 lengthDiff
+= atvLLDiff
;
1443 atvTLLength
= newATVTLLength
;
1446 /* Step 3 fix up length of enclosing RDN Set. */
1447 rdnContentLength
-= lengthDiff
;
1448 DERSize newRDNTLLength
= rdnTLLength
- 1;
1449 drtn
= DEREncodeLength(rdnContentLength
,
1450 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1451 require_quiet(drtn
== DR_Success
, badDER
);
1452 /* Add the length of the tag back in. */
1454 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1456 /* The size of the length field changed, let's slide
1457 the value back by valueLLDiff bytes. */
1458 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1459 base
+ rdnTagLocation
+ rdnTLLength
,
1461 /* The length diff for the enclosing object. */
1462 lengthDiff
+= rdnLLDiff
;
1463 rdnTLLength
= newRDNTLLength
;
1465 /* Adjust the locations that might have changed due to
1467 atvTagLocation
-= rdnLLDiff
;
1469 (void) lengthDiff
; // No next object, silence analyzer
1472 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1473 atvTag
= atvSeq
.nextItem
;
1475 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1476 rdnTag
= rdnSeq
.nextItem
;
1478 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1479 /* Truncate the result to the proper length. */
1480 CFDataSetLength(result
, rdnTagLocation
);
1489 CFDataRef
SecDistinguishedNameCopyNormalizedContent(CFDataRef distinguished_name
)
1491 const DERItem name
= { (unsigned char *)CFDataGetBytePtr(distinguished_name
), CFDataGetLength(distinguished_name
) };
1492 DERDecodedInfo content
;
1493 /* Decode top level sequence into DERItem */
1494 if (!DERDecodeItem(&name
, &content
) && (content
.tag
== ASN1_CONSTR_SEQUENCE
))
1495 return createNormalizedX501Name(kCFAllocatorDefault
, &content
.content
);
1499 /* AUDIT[securityd]:
1500 certificate->_der is a caller provided data of any length (might be 0).
1502 Top level certificate decode.
1504 static bool SecCertificateParse(SecCertificateRef certificate
)
1509 require_quiet(certificate
, badCert
);
1510 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1512 /* top level decode */
1513 DERSignedCertCrl signedCert
;
1514 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1515 DERSignedCertCrlItemSpecs
, &signedCert
,
1516 sizeof(signedCert
));
1517 require_noerr_quiet(drtn
, badCert
);
1518 /* Store tbs since we need to digest it for verification later on. */
1519 certificate
->_tbs
= signedCert
.tbs
;
1521 /* decode the TBSCert - it was saved in full DER form */
1523 drtn
= DERParseSequence(&signedCert
.tbs
,
1524 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1525 &tbsCert
, sizeof(tbsCert
));
1526 require_noerr_quiet(drtn
, badCert
);
1528 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1529 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1530 of the params field. */
1531 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1532 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1533 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1534 require_noerr_quiet(drtn
, badCert
);
1536 /* The contents of signedCert.sig is a bit string whose contents
1537 are the signature itself. */
1538 DERByte numUnusedBits
;
1539 drtn
= DERParseBitString(&signedCert
.sig
,
1540 &certificate
->_signature
, &numUnusedBits
);
1541 require_noerr_quiet(drtn
, badCert
);
1543 /* Now decode the tbsCert. */
1545 /* First we turn the optional version into an int. */
1546 if (tbsCert
.version
.length
) {
1547 DERDecodedInfo decoded
;
1548 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1549 require_noerr_quiet(drtn
, badCert
);
1550 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1551 require_quiet(decoded
.content
.length
== 1, badCert
);
1552 certificate
->_version
= decoded
.content
.data
[0];
1553 require_quiet(certificate
->_version
> 0, badCert
);
1554 require_quiet(certificate
->_version
< 3, badCert
);
1556 certificate
->_version
= 0;
1559 /* The serial number is in the tbsCert.serialNum - it was saved in
1560 INTEGER form without the tag and length. */
1561 certificate
->_serialNum
= tbsCert
.serialNum
;
1562 certificate
->_serialNumber
= CFDataCreate(allocator
,
1563 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1565 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1566 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1567 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1568 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1569 require_noerr_quiet(drtn
, badCert
);
1571 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1572 and length fields. */
1573 certificate
->_issuer
= tbsCert
.issuer
;
1574 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1577 /* sequence we're given: decode the tbsCerts Validity sequence. */
1578 DERValidity validity
;
1579 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1580 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1581 &validity
, sizeof(validity
));
1582 require_noerr_quiet(drtn
, badCert
);
1583 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1584 &certificate
->_notBefore
), badCert
);
1585 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1586 &certificate
->_notAfter
), badCert
);
1588 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1589 and length fields. */
1590 certificate
->_subject
= tbsCert
.subject
;
1591 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1594 /* Keep the SPKI around for CT */
1595 certificate
->_subjectPublicKeyInfo
= tbsCert
.subjectPubKey
;
1597 /* sequence we're given: encoded DERSubjPubKeyInfo - it was saved in full DER form */
1598 DERSubjPubKeyInfo pubKeyInfo
;
1599 drtn
= DERParseSequence(&tbsCert
.subjectPubKey
,
1600 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1601 &pubKeyInfo
, sizeof(pubKeyInfo
));
1602 require_noerr_quiet(drtn
, badCert
);
1604 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1605 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1606 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1607 &certificate
->_algId
, sizeof(certificate
->_algId
));
1608 require_noerr_quiet(drtn
, badCert
);
1610 /* Now we can figure out the key's algorithm id and params based on
1611 certificate->_algId.oid. */
1613 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1614 are a PKCS1 format RSA key. */
1615 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1616 &certificate
->_pubKeyDER
, &numUnusedBits
);
1617 require_noerr_quiet(drtn
, badCert
);
1619 /* The contents of tbsCert.issuerID is a bit string. */
1620 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1622 /* The contents of tbsCert.subjectID is a bit string. */
1623 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1626 if (tbsCert
.extensions
.length
) {
1627 CFIndex extensionCount
= 0;
1630 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1632 require_noerr_quiet(drtn
, badCert
);
1633 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1634 DERDecodedInfo currDecoded
;
1635 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1637 /* ! = MUST recognize ? = SHOULD recognize
1640 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1641 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1642 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1643 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1644 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1645 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1646 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1647 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1649 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1650 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1651 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1652 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1653 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1654 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1655 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1656 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1658 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1659 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1664 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1666 /* Put some upper limit on the number of extensions allowed. */
1667 require_quiet(extensionCount
< 10000, badCert
);
1668 certificate
->_extensionCount
= extensionCount
;
1669 certificate
->_extensions
=
1670 malloc(sizeof(SecCertificateExtension
) * (extensionCount
> 0 ? extensionCount
: 1));
1673 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1674 require_noerr_quiet(drtn
, badCert
);
1675 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1676 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1677 require_quiet(drtn
== DR_Success
||
1678 (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1679 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1681 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1682 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1683 &extn
, sizeof(extn
));
1684 require_noerr_quiet(drtn
, badCert
);
1685 /* Copy stuff into certificate->extensions[ix]. */
1686 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1687 require_noerr_quiet(drtn
= DERParseBoolean(&extn
.critical
, false,
1688 &certificate
->_extensions
[ix
].critical
), badCert
);
1689 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1691 SecCertificateExtensionParser parser
=
1692 (SecCertificateExtensionParser
)CFDictionaryGetValue(
1693 sExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1695 /* Invoke the parser. */
1696 parser(certificate
, &certificate
->_extensions
[ix
]);
1697 } else if (certificate
->_extensions
[ix
].critical
) {
1698 if (isAppleExtensionOID(&extn
.extnID
)) {
1701 secdebug("cert", "Found unknown critical extension");
1702 certificate
->_foundUnknownCriticalExtension
= true;
1704 secdebug("cert", "Found unknown non critical extension");
1708 checkForMissingRevocationInfo(certificate
);
1717 /* Public API functions. */
1718 SecCertificateRef
SecCertificateCreateWithBytes(CFAllocatorRef allocator
,
1719 const UInt8
*der_bytes
, CFIndex der_length
) {
1720 if (der_bytes
== NULL
) return NULL
;
1721 if (der_length
== 0) return NULL
;
1723 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1724 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1725 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1727 memset((char*)result
+ sizeof(result
->_base
), 0,
1728 sizeof(*result
) - sizeof(result
->_base
));
1729 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1730 result
->_der
.length
= der_length
;
1731 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1732 if (!SecCertificateParse(result
)) {
1740 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1741 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1742 const UInt8
*der_bytes
, CFIndex der_length
);
1744 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1745 const UInt8
*der_bytes
, CFIndex der_length
) {
1746 return SecCertificateCreateWithBytes(allocator
, der_bytes
, der_length
);
1748 /* @@@ End of placeholder. */
1750 /* AUDIT[securityd](done):
1751 der_certificate is a caller provided data of any length (might be 0), only
1752 its cf type has been checked.
1754 SecCertificateRef
SecCertificateCreateWithData(CFAllocatorRef allocator
,
1755 CFDataRef der_certificate
) {
1756 check(der_certificate
);
1757 CFIndex size
= sizeof(struct __SecCertificate
);
1758 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1759 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1761 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1762 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1763 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1764 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1765 if (!SecCertificateParse(result
)) {
1773 SecCertificateRef
SecCertificateCreateWithKeychainItem(CFAllocatorRef allocator
,
1774 CFDataRef der_certificate
,
1775 CFTypeRef keychain_item
)
1777 SecCertificateRef result
= SecCertificateCreateWithData(allocator
, der_certificate
);
1779 CFRetainSafe(keychain_item
);
1780 result
->_keychain_item
= keychain_item
;
1785 CFDataRef
SecCertificateCopyData(SecCertificateRef certificate
) {
1787 CFDataRef result
= NULL
;
1791 if (certificate
->_der_data
) {
1792 CFRetain(certificate
->_der_data
);
1793 result
= certificate
->_der_data
;
1795 result
= CFDataCreate(CFGetAllocator(certificate
),
1796 certificate
->_der
.data
, certificate
->_der
.length
);
1798 /* FIXME: If we wish to cache result we need to lock the certificate.
1799 Also this create 2 copies of the certificate data which is somewhat
1802 certificate
->_der_data
= result
;
1809 CFIndex
SecCertificateGetLength(SecCertificateRef certificate
) {
1810 return certificate
->_der
.length
;
1813 const UInt8
*SecCertificateGetBytePtr(SecCertificateRef certificate
) {
1814 return certificate
->_der
.data
;
1817 /* Used to recreate preCert from cert for Certificate Transparency */
1818 CFDataRef
SecCertificateCopyPrecertTBS(SecCertificateRef certificate
)
1820 CFDataRef outData
= NULL
;
1821 DERItem tbsIn
= certificate
->_tbs
;
1822 DERItem tbsOut
= {0,};
1823 DERItem extensionsOut
= {0,};
1824 DERItem
*extensionsList
= malloc(sizeof(DERItem
)*certificate
->_extensionCount
); /* This maybe one too many */
1825 DERItemSpec
*extensionsListSpecs
= malloc(sizeof(DERItemSpec
)*certificate
->_extensionCount
);
1829 /* decode the TBSCert - it was saved in full DER form */
1830 drtn
= DERParseSequence(&tbsIn
,
1831 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1832 &tbsCert
, sizeof(tbsCert
));
1833 require_noerr_quiet(drtn
, out
);
1835 /* Go over extensions and filter any SCT extension */
1836 CFIndex extensionsCount
= 0;
1838 if (tbsCert
.extensions
.length
) {
1841 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1843 require_noerr_quiet(drtn
, out
);
1844 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
1845 DERDecodedInfo currDecoded
;
1846 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1848 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, out
);
1850 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1851 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1852 &extn
, sizeof(extn
));
1853 require_noerr_quiet(drtn
, out
);
1855 if (extn
.extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
1856 !memcmp(extn
.extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
.extnID
.length
))
1859 extensionsList
[extensionsCount
] = currDecoded
.content
;
1860 extensionsListSpecs
[extensionsCount
].offset
= sizeof(DERItem
)*extensionsCount
;
1861 extensionsListSpecs
[extensionsCount
].options
= 0;
1862 extensionsListSpecs
[extensionsCount
].tag
= ASN1_CONSTR_SEQUENCE
;
1867 require_quiet(drtn
== DR_EndOfSequence
, out
);
1871 /* Encode extensions */
1872 extensionsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
);
1873 extensionsOut
.data
= malloc(extensionsOut
.length
);
1874 require_quiet(extensionsOut
.data
, out
);
1875 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
, extensionsOut
.data
, &extensionsOut
.length
);
1876 require_noerr_quiet(drtn
, out
);
1878 tbsCert
.extensions
= extensionsOut
;
1880 tbsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
);
1881 tbsOut
.data
= malloc(tbsOut
.length
);
1882 require_quiet(tbsOut
.data
, out
);
1883 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
, tbsOut
.data
, &tbsOut
.length
);
1884 require_noerr_quiet(drtn
, out
);
1886 outData
= CFDataCreate(kCFAllocatorDefault
, tbsOut
.data
, tbsOut
.length
);
1889 free(extensionsOut
.data
);
1891 free(extensionsList
);
1892 free(extensionsListSpecs
);
1897 /* From rfc3280 - Appendix B. ASN.1 Notes
1899 Object Identifiers (OIDs) are used throughout this specification to
1900 identify certificate policies, public key and signature algorithms,
1901 certificate extensions, etc. There is no maximum size for OIDs.
1902 This specification mandates support for OIDs which have arc elements
1903 with values that are less than 2^28, that is, they MUST be between 0
1904 and 268,435,455, inclusive. This allows each arc element to be
1905 represented within a single 32 bit word. Implementations MUST also
1906 support OIDs where the length of the dotted decimal (see [RFC 2252],
1907 section 4.1) string representation can be up to 100 bytes
1908 (inclusive). Implementations MUST be able to handle OIDs with up to
1909 20 elements (inclusive). CAs SHOULD NOT issue certificates which
1910 contain OIDs that exceed these requirements. Likewise, CRL issuers
1911 SHOULD NOT issue CRLs which contain OIDs that exceed these
1915 /* Oids longer than this are considered invalid. */
1916 #define MAX_OID_SIZE 32
1918 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
1919 const DERItem
*oid
) {
1921 if (oid
->length
== 0) {
1922 return SecCopyCertString(SEC_NULL_KEY
);
1924 if (oid
->length
> MAX_OID_SIZE
) {
1925 return SecCopyCertString(SEC_OID_TOO_LONG_KEY
);
1928 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
1930 // The first two levels are encoded into one byte, since the root level
1931 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
1932 // y may be > 39, so we have to add special-case handling for this.
1933 uint32_t x
= oid
->data
[0] / 40;
1934 uint32_t y
= oid
->data
[0] % 40;
1937 // Handle special case for large y if x = 2
1941 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
1944 for (x
= 1; x
< oid
->length
; ++x
)
1946 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
1947 /* @@@ value may not span more than 4 bytes. */
1948 /* A max number of 20 values is allowed. */
1949 if (!(oid
->data
[x
] & 0x80))
1951 CFStringAppendFormat(result
, NULL
, CFSTR(".%" PRIu32
), value
);
1958 static CFStringRef
copyLocalizedOidDescription(CFAllocatorRef allocator
,
1959 const DERItem
*oid
) {
1960 if (oid
->length
== 0) {
1961 return SecCopyCertString(SEC_NULL_KEY
);
1964 /* Build the key we use to lookup the localized OID description. */
1965 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
1966 oid
->length
* 3 + 5);
1967 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), oid
->length
);
1969 for (ix
= 0; ix
< oid
->length
; ++ix
)
1970 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
1972 CFStringRef name
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
1973 if (CFEqual(oidKey
, name
)) {
1975 name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1982 /* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated
1983 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't
1984 have a length of exactly 4 or 16 octects. */
1985 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
1986 const DERItem
*ip
) {
1987 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
1988 4 octects addr, or 8 octects, addr/mask for ipv6 it's
1989 16 octects addr, or 32 octects addr/mask. */
1990 CFStringRef value
= NULL
;
1991 if (ip
->length
== 4) {
1992 value
= CFStringCreateWithFormat(allocator
, NULL
,
1993 CFSTR("%u.%u.%u.%u"),
1994 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
1995 } else if (ip
->length
== 16) {
1996 value
= CFStringCreateWithFormat(allocator
, NULL
,
1997 CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
1998 "%02x%02x:%02x%02x:%02x%02x:%02x%02x"),
1999 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
2000 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
2001 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
2002 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
2009 static CFStringRef
copyFullOidDescription(CFAllocatorRef allocator
,
2010 const DERItem
*oid
) {
2011 CFStringRef decimal
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
2012 CFStringRef name
= copyLocalizedOidDescription(allocator
, oid
);
2013 CFStringRef oid_string
= CFStringCreateWithFormat(allocator
, NULL
,
2014 CFSTR("%@ (%@)"), name
, decimal
);
2021 void appendProperty(CFMutableArrayRef properties
, CFStringRef propertyType
,
2022 CFStringRef label
, CFStringRef localizedLabel
, CFTypeRef value
) {
2023 CFDictionaryRef property
;
2026 if (localizedLabel
) {
2029 ll
= localizedLabel
= SecCopyCertString(label
);
2031 const void *all_keys
[4];
2032 all_keys
[0] = kSecPropertyKeyType
;
2033 all_keys
[1] = kSecPropertyKeyLabel
;
2034 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
2035 all_keys
[3] = kSecPropertyKeyValue
;
2036 const void *property_values
[] = {
2042 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2043 all_keys
, property_values
, value
? 4 : 3,
2044 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2047 const void *nolabel_keys
[2];
2048 nolabel_keys
[0] = kSecPropertyKeyType
;
2049 nolabel_keys
[1] = kSecPropertyKeyValue
;
2050 const void *property_values
[] = {
2054 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2055 nolabel_keys
, property_values
, 2,
2056 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2059 CFArrayAppendValue(properties
, property
);
2060 CFRelease(property
);
2064 #define UTC_TIME_NOSEC_ZULU_LEN 11
2066 #define UTC_TIME_ZULU_LEN 13
2067 /* YYMMDDhhmmssThhmm */
2068 #define UTC_TIME_LOCALIZED_LEN 17
2069 /* YYYYMMDDhhmmssZ */
2070 #define GENERALIZED_TIME_ZULU_LEN 15
2071 /* YYYYMMDDhhmmssThhmm */
2072 #define GENERALIZED_TIME_LOCALIZED_LEN 19
2074 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
2076 static inline int parseDecimalPair(const DERByte
**p
) {
2077 const DERByte
*cp
= *p
;
2079 return 10 * (cp
[0] - '0') + cp
[1] - '0';
2082 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
2083 true if the date was valid and properly decoded, also return the result in
2084 absTime. Return false otherwise. */
2085 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
2092 bool isUtcLength
= false;
2093 bool isLocalized
= false;
2094 bool noSeconds
= false;
2096 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
2100 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
2103 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
2105 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
2108 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
2111 default: /* unknown format */
2115 /* Make sure the der tag fits the thing inside it. */
2116 if (tag
== ASN1_UTC_TIME
) {
2119 } else if (tag
== ASN1_GENERALIZED_TIME
) {
2126 const DERByte
*cp
= bytes
;
2127 /* Check that all characters are digits, except if localized the timezone
2128 indicator or if not localized the 'Z' at the end. */
2130 for (ix
= 0; ix
< length
; ++ix
) {
2131 if (!(isdigit(cp
[ix
]))) {
2132 if ((isLocalized
&& ix
== length
- 5 &&
2133 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
2134 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
2141 /* Parse the date and time fields. */
2142 int year
, month
, day
, hour
, minute
, second
;
2144 year
= parseDecimalPair(&cp
);
2146 /* 0 <= year < 50 : assume century 21 */
2148 } else if (year
< 70) {
2149 /* 50 <= year < 70 : illegal per PKIX */
2152 /* 70 < year <= 99 : assume century 20 */
2156 year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
2158 month
= parseDecimalPair(&cp
);
2159 day
= parseDecimalPair(&cp
);
2160 hour
= parseDecimalPair(&cp
);
2161 minute
= parseDecimalPair(&cp
);
2165 second
= parseDecimalPair(&cp
);
2168 CFTimeInterval timeZoneOffset
;
2170 /* ZONE INDICATOR */
2171 int multiplier
= *cp
++ == '+' ? 60 : -60;
2172 timeZoneOffset
= multiplier
*
2173 (parseDecimalPair(&cp
) * 60 + parseDecimalPair(&cp
));
2178 secdebug("dateparse",
2179 "date %.*s year: %04d%02d%02d%02d%02d%02d%+05g",
2180 (int) length
, bytes
, year
, month
,
2181 day
, hour
, minute
, second
,
2182 timeZoneOffset
/ 60);
2184 static int mdays
[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
2185 int is_leap_year
= year
% 4 == 0 && (year
% 100 != 0 || year
% 400 == 0) ? 1 : 0;
2186 if (month
< 1 || month
> 12 || day
< 1 || day
> 31 || hour
> 23 || minute
> 59 || second
> 59
2187 || (month
== 2 && day
> mdays
[month
] - mdays
[month
- 1] + is_leap_year
)
2188 || (month
!= 2 && day
> mdays
[month
] - mdays
[month
- 1])) {
2193 int dy
= year
- 2001;
2198 int leap_days
= dy
/ 4 - dy
/ 100 + dy
/ 400;
2199 day
+= ((year
- 2001) * 365 + leap_days
) + mdays
[month
- 1] - 1;
2201 day
+= is_leap_year
;
2203 CFAbsoluteTime absTime
= (CFAbsoluteTime
)((day
* 24 + hour
) * 60 + minute
) * 60 + second
;
2204 return absTime
- timeZoneOffset
;
2207 __attribute__((__nonnull__
)) static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
2208 CFAbsoluteTime
*pabsTime
) {
2209 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContent(tag
, date
->data
,
2211 if (absTime
== NULL_TIME
)
2214 *pabsTime
= absTime
;
2218 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
2219 true if the date was valid and properly decoded, also return the result in
2220 absTime. Return false otherwise. */
2221 __attribute__((__nonnull__
)) static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
2222 CFAbsoluteTime
*absTime
) {
2223 if (dateChoice
->length
== 0) return false;
2225 DERDecodedInfo decoded
;
2226 if (DERDecodeItem(dateChoice
, &decoded
))
2229 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
2233 static void appendDataProperty(CFMutableArrayRef properties
,
2234 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
) {
2235 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
2236 der_data
->data
, der_data
->length
);
2237 appendProperty(properties
, kSecPropertyTypeData
, label
, localizedLabel
,
2242 static void appendRelabeledProperty(CFMutableArrayRef properties
,
2244 CFStringRef localizedLabel
,
2245 const DERItem
*der_data
,
2246 CFStringRef labelFormat
) {
2247 CFStringRef newLabel
=
2248 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2249 labelFormat
, label
);
2251 if (localizedLabel
) {
2254 ll
= localizedLabel
= SecCopyCertString(label
);
2256 CFStringRef localizedLabelFormat
= SecCopyCertString(labelFormat
);
2257 CFStringRef newLocalizedLabel
=
2258 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2259 localizedLabelFormat
, localizedLabel
);
2261 CFReleaseSafe(localizedLabelFormat
);
2262 appendDataProperty(properties
, newLabel
, newLocalizedLabel
, der_data
);
2263 CFReleaseSafe(newLabel
);
2264 CFReleaseSafe(newLocalizedLabel
);
2268 static void appendUnparsedProperty(CFMutableArrayRef properties
,
2269 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
) {
2270 appendRelabeledProperty(properties
, label
, localizedLabel
, der_data
,
2274 static void appendInvalidProperty(CFMutableArrayRef properties
,
2275 CFStringRef label
, const DERItem
*der_data
) {
2276 appendRelabeledProperty(properties
, label
, NULL
, der_data
, SEC_INVALID_KEY
);
2279 static void appendDateContentProperty(CFMutableArrayRef properties
,
2280 CFStringRef label
, DERTag tag
,
2281 const DERItem
*dateContent
) {
2282 CFAbsoluteTime absTime
;
2283 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
2284 /* Date decode failure insert hex bytes instead. */
2285 return appendInvalidProperty(properties
, label
, dateContent
);
2287 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2288 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
);
2292 static void appendDateProperty(CFMutableArrayRef properties
,
2293 CFStringRef label
, CFAbsoluteTime absTime
) {
2294 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2295 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
);
2299 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
2300 CFStringRef label
, const DERItem
*ip
) {
2302 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
2304 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2307 appendUnparsedProperty(properties
, label
, NULL
, ip
);
2311 static void appendURLContentProperty(CFMutableArrayRef properties
,
2312 CFStringRef label
, const DERItem
*urlContent
) {
2313 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
2314 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2316 appendProperty(properties
, kSecPropertyTypeURL
, label
, NULL
, url
);
2319 appendInvalidProperty(properties
, label
, urlContent
);
2323 static void appendURLProperty(CFMutableArrayRef properties
,
2324 CFStringRef label
, const DERItem
*url
) {
2325 DERDecodedInfo decoded
;
2328 drtn
= DERDecodeItem(url
, &decoded
);
2329 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2330 appendInvalidProperty(properties
, label
, url
);
2332 appendURLContentProperty(properties
, label
, &decoded
.content
);
2336 static void appendOIDProperty(CFMutableArrayRef properties
,
2337 CFStringRef label
, CFStringRef llabel
, const DERItem
*oid
) {
2338 CFStringRef oid_string
=
2339 copyLocalizedOidDescription(CFGetAllocator(properties
), oid
);
2340 appendProperty(properties
, kSecPropertyTypeString
, label
, llabel
,
2342 CFRelease(oid_string
);
2345 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2346 CFStringRef label
, const DERAlgorithmId
*algorithm
) {
2347 CFMutableArrayRef alg_props
=
2348 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2349 &kCFTypeArrayCallBacks
);
2350 appendOIDProperty(alg_props
, SEC_ALGORITHM_KEY
, NULL
, &algorithm
->oid
);
2351 if (algorithm
->params
.length
) {
2352 if (algorithm
->params
.length
== 2 &&
2353 algorithm
->params
.data
[0] == ASN1_NULL
&&
2354 algorithm
->params
.data
[1] == 0) {
2355 CFStringRef value
= SecCopyCertString(SEC_NONE_KEY
);
2356 appendProperty(alg_props
, kSecPropertyTypeString
,
2357 SEC_PARAMETERS_KEY
, NULL
, value
);
2360 appendUnparsedProperty(alg_props
, SEC_PARAMETERS_KEY
, NULL
,
2361 &algorithm
->params
);
2364 appendProperty(properties
, kSecPropertyTypeSection
, label
, NULL
, alg_props
);
2365 CFRelease(alg_props
);
2368 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2369 const DERItem
*blob
) {
2370 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2371 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2372 blob
->length
* 3 - 1);
2373 for (ix
= 0; ix
< length
; ++ix
)
2375 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2377 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2382 /* Returns a (localized) blob string. */
2383 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2384 CFStringRef blobType
, CFStringRef quanta
, const DERItem
*blob
) {
2385 CFStringRef localizedBlobType
= SecCopyCertString(blobType
);
2386 CFStringRef localizedQuanta
= SecCopyCertString(quanta
);
2387 /* "format string for encoded field data (e.g. Sequence; 128 bytes; "
2388 "data = 00 00 ...)" */
2389 CFStringRef blobFormat
= SecCopyCertString(SEC_BLOB_KEY
);
2390 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2391 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2392 blobFormat
, localizedBlobType
, blob
->length
, localizedQuanta
, hex
);
2394 CFRelease(blobFormat
);
2395 CFReleaseSafe(localizedQuanta
);
2396 CFReleaseSafe(localizedBlobType
);
2401 /* Return a string verbatim (unlocalized) from a DER field. */
2402 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2403 const DERItem
*string
, CFStringEncoding encoding
,
2404 bool printableOnly
) {
2405 /* Strip potential bogus trailing zero from printable strings. */
2406 DERSize length
= string
->length
;
2407 if (length
&& string
->data
[length
- 1] == 0) {
2408 /* Don't mess with the length of UTF16 strings though. */
2409 if (encoding
!= kCFStringEncodingUTF16
)
2412 /* A zero length string isn't considered printable. */
2413 if (!length
&& printableOnly
)
2416 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2417 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2418 passing false makes it treat it as native endian by default. */
2419 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2420 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2424 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2427 /* From rfc3280 - Appendix B. ASN.1 Notes
2429 CAs MUST force the serialNumber to be a non-negative integer, that
2430 is, the sign bit in the DER encoding of the INTEGER value MUST be
2431 zero - this can be done by adding a leading (leftmost) `00'H octet if
2432 necessary. This removes a potential ambiguity in mapping between a
2433 string of octets and an integer value.
2435 As noted in section 4.1.2.2, serial numbers can be expected to
2436 contain long integers. Certificate users MUST be able to handle
2437 serialNumber values up to 20 octets in length. Conformant CAs MUST
2438 NOT use serialNumber values longer than 20 octets.
2441 /* Return the given numeric data as a string: decimal up to 64 bits,
2443 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2444 const DERItem
*integer
) {
2446 CFIndex ix
, length
= integer
->length
;
2448 if (length
== 0 || length
> 8)
2449 return copyHexDescription(allocator
, integer
);
2451 for(ix
= 0; ix
< length
; ++ix
) {
2453 value
+= integer
->data
[ix
];
2456 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2459 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2460 DERTag tag
, const DERItem
*derThing
, bool printableOnly
) {
2464 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2465 case ASN1_PRINTABLE_STRING
:
2466 case ASN1_IA5_STRING
:
2467 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2468 case ASN1_UTF8_STRING
:
2469 case ASN1_GENERAL_STRING
:
2470 case ASN1_UNIVERSAL_STRING
:
2471 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2472 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2473 case ASN1_VIDEOTEX_STRING
: // 21
2474 case ASN1_VISIBLE_STRING
: // 26
2475 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2476 case ASN1_BMP_STRING
: // 30
2477 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2478 case ASN1_OCTET_STRING
:
2479 return printableOnly
? NULL
:
2480 copyBlobString(allocator
, SEC_BYTE_STRING_KEY
, SEC_BYTES_KEY
,
2482 //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing);
2483 case ASN1_BIT_STRING
:
2484 return printableOnly
? NULL
:
2485 copyBlobString(allocator
, SEC_BIT_STRING_KEY
, SEC_BITS_KEY
,
2487 case ASN1_CONSTR_SEQUENCE
:
2488 return printableOnly
? NULL
:
2489 copyBlobString(allocator
, SEC_SEQUENCE_KEY
, SEC_BYTES_KEY
,
2491 case ASN1_CONSTR_SET
:
2492 return printableOnly
? NULL
:
2493 copyBlobString(allocator
, SEC_SET_KEY
, SEC_BYTES_KEY
, derThing
);
2494 case ASN1_OBJECT_ID
:
2495 return printableOnly
? NULL
: copyLocalizedOidDescription(allocator
, derThing
);
2497 if (printableOnly
) {
2500 CFStringRef fmt
= SecCopyCertString(SEC_NOT_DISPLAYED_KEY
);
2501 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
2502 tag
, derThing
->length
);
2509 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2510 const DERItem
*derThing
, bool printableOnly
) {
2511 DERDecodedInfo decoded
;
2514 drtn
= DERDecodeItem(derThing
, &decoded
);
2516 /* TODO: Perhaps put something in the label saying we couldn't parse
2518 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2520 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2521 &decoded
.content
, false);
2525 static void appendDERThingProperty(CFMutableArrayRef properties
,
2526 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*derThing
) {
2527 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2529 appendProperty(properties
, kSecPropertyTypeString
, label
, localizedLabel
,
2531 CFReleaseSafe(value
);
2534 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2535 const DERItem
*rdnValue
, CFIndex rdnIX
) {
2536 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2538 /* If there is more than one value pair we create a subsection for the
2539 second pair, and append things to the subsection for subsequent
2541 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2542 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2544 /* Since this is the second rdn pair for a given rdn, we setup a
2545 new subsection for this rdn. We remove the first property
2546 from the properties array and make it the first element in the
2547 subsection instead. */
2548 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2549 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2550 CFArrayAppendValue(rdn_props
, lastValue
);
2551 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2552 appendProperty(properties
, kSecPropertyTypeSection
, NULL
, NULL
,
2554 properties
= rdn_props
;
2556 /* Since this is the third or later rdn pair we have already
2557 created a subsection in the top level properties array. Instead
2558 of appending to that directly we append to the array inside the
2560 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2561 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2565 /* Finally we append the new rdn value to the property array. */
2566 CFStringRef label
= SecDERItemCopyOIDDecimalRepresentation(
2567 CFGetAllocator(properties
), rdnType
);
2568 CFStringRef localizedLabel
=
2569 copyLocalizedOidDescription(CFGetAllocator(properties
), rdnType
);
2570 appendDERThingProperty(properties
, label
, localizedLabel
, rdnValue
);
2571 CFReleaseSafe(label
);
2572 CFReleaseSafe(localizedLabel
);
2573 return errSecSuccess
;
2576 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2577 const DERItem
*rdnSetContent
) {
2578 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2579 &kCFTypeArrayCallBacks
);
2580 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2583 CFArrayRemoveAllValues(properties
);
2584 appendInvalidProperty(properties
, SEC_RDN_KEY
, rdnSetContent
);
2591 From rfc3739 - 3.1.2. Subject
2593 When parsing the subject here are some tips for a short name of the cert.
2594 Choice I: commonName
2595 Choice II: givenName
2596 Choice III: pseudonym
2598 The commonName attribute value SHALL, when present, contain a name
2599 of the subject. This MAY be in the subject's preferred
2600 presentation format, or a format preferred by the CA, or some
2601 other format. Pseudonyms, nicknames, and names with spelling
2602 other than defined by the registered name MAY be used. To
2603 understand the nature of the name presented in commonName,
2604 complying applications MAY have to examine present values of the
2605 givenName and surname attributes, or the pseudonym attribute.
2608 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2609 const DERItem
*x501NameContent
) {
2610 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2611 &kCFTypeArrayCallBacks
);
2612 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2615 CFArrayRemoveAllValues(properties
);
2616 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
, x501NameContent
);
2622 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2623 const DERItem
*x501Name
) {
2624 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2625 &kCFTypeArrayCallBacks
);
2626 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
);
2628 CFArrayRemoveAllValues(properties
);
2629 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
, x501Name
);
2635 static void appendIntegerProperty(CFMutableArrayRef properties
,
2636 CFStringRef label
, const DERItem
*integer
) {
2637 CFStringRef string
= copyIntegerContentDescription(
2638 CFGetAllocator(properties
), integer
);
2639 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, string
);
2643 static void appendBoolProperty(CFMutableArrayRef properties
,
2644 CFStringRef label
, bool boolean
) {
2645 CFStringRef value
= SecCopyCertString(boolean
? SEC_YES_KEY
: SEC_NO_KEY
);
2646 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2650 static void appendBooleanProperty(CFMutableArrayRef properties
,
2651 CFStringRef label
, const DERItem
*boolean
, bool defaultValue
) {
2653 DERReturn drtn
= DERParseBoolean(boolean
, defaultValue
, &result
);
2655 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2656 appendInvalidProperty(properties
, label
, boolean
);
2658 appendBoolProperty(properties
, label
, result
);
2662 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2663 CFStringRef label
, const DERItem
*bitStringContent
,
2664 const CFStringRef
*names
, CFIndex namesCount
) {
2665 DERSize len
= bitStringContent
->length
- 1;
2666 require_quiet(len
== 1 || len
== 2, badDER
);
2667 DERByte numUnusedBits
= bitStringContent
->data
[0];
2668 require_quiet(numUnusedBits
< 8, badDER
);
2669 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
2670 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
2671 uint_fast16_t value
= bitStringContent
->data
[1];
2674 value
= (value
<< 8) + bitStringContent
->data
[2];
2680 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
2681 CFStringRef string
= NULL
;
2682 for (ix
= 0; ix
< bits
; ++ix
) {
2686 CFStringCreateWithFormat(CFGetAllocator(properties
),
2687 NULL
, fmt
, string
, names
[ix
]);
2698 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2699 string
? string
: CFSTR(""));
2700 CFReleaseSafe(string
);
2703 appendInvalidProperty(properties
, label
, bitStringContent
);
2706 static void appendBitStringNames(CFMutableArrayRef properties
,
2707 CFStringRef label
, const DERItem
*bitString
,
2708 const CFStringRef
*names
, CFIndex namesCount
) {
2709 DERDecodedInfo bitStringContent
;
2710 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
2711 require_noerr_quiet(drtn
, badDER
);
2712 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
2713 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
2717 appendInvalidProperty(properties
, label
, bitString
);
2721 typedef uint16_t SecKeyUsage
;
2723 #define kSecKeyUsageDigitalSignature 0x8000
2724 #define kSecKeyUsageNonRepudiation 0x4000
2725 #define kSecKeyUsageKeyEncipherment 0x2000
2726 #define kSecKeyUsageDataEncipherment 0x1000
2727 #define kSecKeyUsageKeyAgreement 0x0800
2728 #define kSecKeyUsageKeyCertSign 0x0400
2729 #define kSecKeyUsageCRLSign 0x0200
2730 #define kSecKeyUsageEncipherOnly 0x0100
2731 #define kSecKeyUsageDecipherOnly 0x0080
2734 KeyUsage ::= BIT STRING {
2735 digitalSignature (0),
2737 keyEncipherment (2),
2738 dataEncipherment (3),
2745 static void appendKeyUsage(CFMutableArrayRef properties
,
2746 const DERItem
*extnValue
) {
2747 if ((extnValue
->length
!= 4 && extnValue
->length
!= 5) ||
2748 extnValue
->data
[0] != ASN1_BIT_STRING
||
2749 extnValue
->data
[1] < 2 || extnValue
->data
[1] > 3 ||
2750 extnValue
->data
[2] > 7) {
2751 appendInvalidProperty(properties
, CFSTR("KeyUsage Extension"),
2754 CFMutableStringRef string
=
2755 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2756 SecKeyUsage usage
= (extnValue
->data
[3] << 8);
2757 if (extnValue
->length
== 5)
2758 usage
+= extnValue
->data
[4];
2759 secdebug("keyusage", "keyusage: %04X", usage
);
2760 static const CFStringRef usageNames
[] = {
2761 CFSTR("Digital Signature"),
2762 CFSTR("Non-Repudiation"),
2763 CFSTR("Key Encipherment"),
2764 CFSTR("Data Encipherment"),
2765 CFSTR("Key Agreement"),
2771 bool didOne
= false;
2772 SecKeyUsage mask
= kSecKeyUsageDigitalSignature
;
2773 CFIndex ix
, bits
= (extnValue
->data
[1] - 1) * 8 - extnValue
->data
[2];
2774 for (ix
= 0; ix
< bits
; ++ix
) {
2777 CFStringAppend(string
, CFSTR(", "));
2781 /* @@@ Localize usageNames[ix]. */
2782 CFStringAppend(string
, usageNames
[ix
]);
2786 appendProperty(properties
, kSecPropertyTypeString
, CFSTR("Usage"),
2792 static void appendKeyUsage(CFMutableArrayRef properties
,
2793 const DERItem
*extnValue
) {
2794 static const CFStringRef usageNames
[] = {
2795 SEC_DIGITAL_SIGNATURE_KEY
,
2796 SEC_NON_REPUDIATION_KEY
,
2797 SEC_KEY_ENCIPHERMENT_KEY
,
2798 SEC_DATA_ENCIPHERMENT_KEY
,
2799 SEC_KEY_AGREEMENT_KEY
,
2802 SEC_ENCIPHER_ONLY_KEY
,
2803 SEC_DECIPHER_ONLY_KEY
2805 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
2806 usageNames
, array_size(usageNames
));
2810 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
2811 const DERItem
*extnValue
) {
2812 DERPrivateKeyUsagePeriod pkup
;
2813 DERReturn drtn
= DERParseSequence(extnValue
,
2814 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
2815 &pkup
, sizeof(pkup
));
2816 require_noerr_quiet(drtn
, badDER
);
2817 if (pkup
.notBefore
.length
) {
2818 appendDateContentProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2819 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
);
2821 if (pkup
.notAfter
.length
) {
2822 appendDateContentProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2823 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
);
2827 appendInvalidProperty(properties
, SEC_PRIVATE_KU_PERIOD_KEY
, extnValue
);
2830 static void appendStringContentProperty(CFMutableArrayRef properties
,
2831 CFStringRef label
, const DERItem
*stringContent
,
2832 CFStringEncoding encoding
) {
2833 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
2834 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
2836 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, string
);
2839 appendInvalidProperty(properties
, label
, stringContent
);
2844 OtherName ::= SEQUENCE {
2845 type-id OBJECT IDENTIFIER,
2846 value [0] EXPLICIT ANY DEFINED BY type-id }
2848 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
2849 const DERItem
*otherNameContent
) {
2851 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
2852 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
2854 require_noerr_quiet(drtn
, badDER
);
2855 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2857 SecDERItemCopyOIDDecimalRepresentation(allocator
, &on
.typeIdentifier
);
2858 CFStringRef localizedLabel
=
2859 copyLocalizedOidDescription(allocator
, &on
.typeIdentifier
);
2860 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
, false);
2862 appendProperty(properties
, kSecPropertyTypeString
, label
,
2863 localizedLabel
, value_string
);
2865 appendUnparsedProperty(properties
, label
, localizedLabel
, &on
.value
);
2867 CFReleaseSafe(value_string
);
2868 CFReleaseSafe(label
);
2869 CFReleaseSafe(localizedLabel
);
2872 appendInvalidProperty(properties
, SEC_OTHER_NAME_KEY
, otherNameContent
);
2876 GeneralName ::= CHOICE {
2877 otherName [0] OtherName,
2878 rfc822Name [1] IA5String,
2879 dNSName [2] IA5String,
2880 x400Address [3] ORAddress,
2881 directoryName [4] Name,
2882 ediPartyName [5] EDIPartyName,
2883 uniformResourceIdentifier [6] IA5String,
2884 iPAddress [7] OCTET STRING,
2885 registeredID [8] OBJECT IDENTIFIER}
2887 EDIPartyName ::= SEQUENCE {
2888 nameAssigner [0] DirectoryString OPTIONAL,
2889 partyName [1] DirectoryString }
2891 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
2892 DERTag tag
, const DERItem
*generalName
) {
2894 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
2895 appendOtherNameContentProperty(properties
, generalName
);
2897 case ASN1_CONTEXT_SPECIFIC
| 1:
2899 appendStringContentProperty(properties
, SEC_EMAIL_ADDRESS_KEY
,
2900 generalName
, kCFStringEncodingASCII
);
2902 case ASN1_CONTEXT_SPECIFIC
| 2:
2904 appendStringContentProperty(properties
, SEC_DNS_NAME_KEY
, generalName
,
2905 kCFStringEncodingASCII
);
2907 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
2908 appendUnparsedProperty(properties
, SEC_X400_ADDRESS_KEY
, NULL
,
2911 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
2913 CFArrayRef directory_plist
=
2914 createPropertiesForX501Name(CFGetAllocator(properties
),
2916 appendProperty(properties
, kSecPropertyTypeSection
,
2917 SEC_DIRECTORY_NAME_KEY
, NULL
, directory_plist
);
2918 CFRelease(directory_plist
);
2921 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
2922 appendUnparsedProperty(properties
, SEC_EDI_PARTY_NAME_KEY
, NULL
,
2925 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
2926 /* Technically I don't think this is valid, but there are certs out
2927 in the wild that use a constructed IA5String. In particular the
2928 VeriSign Time Stamping Authority CA.cer does this. */
2929 appendURLProperty(properties
, SEC_URI_KEY
, generalName
);
2931 case ASN1_CONTEXT_SPECIFIC
| 6:
2932 appendURLContentProperty(properties
, SEC_URI_KEY
, generalName
);
2934 case ASN1_CONTEXT_SPECIFIC
| 7:
2935 appendIPAddressContentProperty(properties
, SEC_IP_ADDRESS_KEY
,
2938 case ASN1_CONTEXT_SPECIFIC
| 8:
2939 appendOIDProperty(properties
, SEC_REGISTERED_ID_KEY
, NULL
, generalName
);
2950 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
2951 const DERItem
*generalName
) {
2952 DERDecodedInfo generalNameContent
;
2953 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
2954 require_noerr_quiet(drtn
, badDER
);
2955 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
2956 &generalNameContent
.content
))
2959 appendInvalidProperty(properties
, SEC_GENERAL_NAME_KEY
, generalName
);
2964 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
2966 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
2967 const DERItem
*generalNamesContent
) {
2969 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
2970 require_noerr_quiet(drtn
, badDER
);
2971 DERDecodedInfo generalNameContent
;
2972 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
2974 if (!appendGeneralNameContentProperty(properties
,
2975 generalNameContent
.tag
, &generalNameContent
.content
)) {
2979 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2982 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
2983 generalNamesContent
);
2986 static void appendGeneralNames(CFMutableArrayRef properties
,
2987 const DERItem
*generalNames
) {
2988 DERDecodedInfo generalNamesContent
;
2989 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
2990 require_noerr_quiet(drtn
, badDER
);
2991 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
2993 appendGeneralNamesContent(properties
, &generalNamesContent
.content
);
2996 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
, generalNames
);
3000 BasicConstraints ::= SEQUENCE {
3001 cA BOOLEAN DEFAULT FALSE,
3002 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
3004 static void appendBasicConstraints(CFMutableArrayRef properties
,
3005 const DERItem
*extnValue
) {
3006 DERBasicConstraints basicConstraints
;
3007 DERReturn drtn
= DERParseSequence(extnValue
,
3008 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
3009 &basicConstraints
, sizeof(basicConstraints
));
3010 require_noerr_quiet(drtn
, badDER
);
3012 appendBooleanProperty(properties
, SEC_CERT_AUTHORITY_KEY
,
3013 &basicConstraints
.cA
, false);
3015 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
3016 appendIntegerProperty(properties
, SEC_PATH_LEN_CONSTRAINT_KEY
,
3017 &basicConstraints
.pathLenConstraint
);
3021 appendInvalidProperty(properties
, SEC_BASIC_CONSTRAINTS_KEY
, extnValue
);
3025 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
3027 * NameConstraints ::= SEQUENCE {
3028 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
3029 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
3031 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
3033 * GeneralSubtree ::= SEQUENCE {
3035 * minimum [0] BaseDistance DEFAULT 0,
3036 * maximum [1] BaseDistance OPTIONAL }
3038 * BaseDistance ::= INTEGER (0..MAX)
3040 static void appendNameConstraints(CFMutableArrayRef properties
,
3041 const DERItem
*extnValue
) {
3042 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3043 DERNameConstraints nc
;
3045 drtn
= DERParseSequence(extnValue
,
3046 DERNumNameConstraintsItemSpecs
,
3047 DERNameConstraintsItemSpecs
,
3049 require_noerr_quiet(drtn
, badDER
);
3050 if (nc
.permittedSubtrees
.length
) {
3052 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.permittedSubtrees
, &gsSeq
), badDER
);
3053 DERDecodedInfo gsContent
;
3054 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3055 DERGeneralSubtree derGS
;
3056 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3057 drtn
= DERParseSequenceContent(&gsContent
.content
,
3058 DERNumGeneralSubtreeItemSpecs
,
3059 DERGeneralSubtreeItemSpecs
,
3060 &derGS
, sizeof(derGS
));
3061 require_noerr_quiet(drtn
, badDER
);
3062 if (derGS
.minimum
.length
) {
3063 appendIntegerProperty(properties
, SEC_PERMITTED_MINIMUM_KEY
, &derGS
.minimum
);
3065 if (derGS
.maximum
.length
) {
3066 appendIntegerProperty(properties
, SEC_PERMITTED_MAXIMUM_KEY
, &derGS
.maximum
);
3068 if (derGS
.generalName
.length
) {
3069 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3070 &kCFTypeArrayCallBacks
);
3071 appendProperty(properties
, kSecPropertyTypeSection
,
3072 SEC_PERMITTED_NAME_KEY
, NULL
, base
);
3073 appendGeneralNameProperty(base
, &derGS
.generalName
);
3078 if (nc
.excludedSubtrees
.length
) {
3080 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.excludedSubtrees
, &gsSeq
), badDER
);
3081 DERDecodedInfo gsContent
;
3082 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3083 DERGeneralSubtree derGS
;
3084 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3085 drtn
= DERParseSequenceContent(&gsContent
.content
,
3086 DERNumGeneralSubtreeItemSpecs
,
3087 DERGeneralSubtreeItemSpecs
,
3088 &derGS
, sizeof(derGS
));
3089 require_noerr_quiet(drtn
, badDER
);
3090 if (derGS
.minimum
.length
) {
3091 appendIntegerProperty(properties
, SEC_EXCLUDED_MINIMUM_KEY
, &derGS
.minimum
);
3093 if (derGS
.maximum
.length
) {
3094 appendIntegerProperty(properties
, SEC_EXCLUDED_MAXIMUM_KEY
, &derGS
.maximum
);
3096 if (derGS
.generalName
.length
) {
3097 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3098 &kCFTypeArrayCallBacks
);
3099 appendProperty(properties
, kSecPropertyTypeSection
,
3100 SEC_EXCLUDED_NAME_KEY
, NULL
, base
);
3101 appendGeneralNameProperty(base
, &derGS
.generalName
);
3109 appendInvalidProperty(properties
, SEC_NAME_CONSTRAINTS_KEY
, extnValue
);
3113 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
3115 DistributionPoint ::= SEQUENCE {
3116 distributionPoint [0] DistributionPointName OPTIONAL,
3117 reasons [1] ReasonFlags OPTIONAL,
3118 cRLIssuer [2] GeneralNames OPTIONAL }
3120 DistributionPointName ::= CHOICE {
3121 fullName [0] GeneralNames,
3122 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
3124 ReasonFlags ::= BIT STRING {
3128 affiliationChanged (3),
3130 cessationOfOperation (5),
3131 certificateHold (6),
3132 privilegeWithdrawn (7),
3135 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
3136 const DERItem
*extnValue
) {
3137 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3140 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
3141 require_noerr_quiet(drtn
, badDER
);
3142 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3143 DERDecodedInfo dpSeqContent
;
3144 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
3145 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3146 DERDistributionPoint dp
;
3147 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
3148 DERNumDistributionPointItemSpecs
,
3149 DERDistributionPointItemSpecs
,
3151 require_noerr_quiet(drtn
, badDER
);
3152 if (dp
.distributionPoint
.length
) {
3153 DERDecodedInfo distributionPointName
;
3154 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
3155 require_noerr_quiet(drtn
, badDER
);
3156 if (distributionPointName
.tag
==
3157 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
3159 appendGeneralNamesContent(properties
,
3160 &distributionPointName
.content
);
3161 } else if (distributionPointName
.tag
==
3162 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
3163 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
3165 appendProperty(properties
, kSecPropertyTypeSection
,
3166 SEC_NAME_REL_CRL_ISSUER_KEY
, NULL
, rdn_props
);
3167 CFRelease(rdn_props
);
3172 if (dp
.reasons
.length
) {
3173 static const CFStringRef reasonNames
[] = {
3175 SEC_KEY_COMPROMISE_KEY
,
3176 SEC_CA_COMPROMISE_KEY
,
3177 SEC_AFFILIATION_CHANGED_KEY
,
3179 SEC_CESSATION_OF_OPER_KEY
,
3180 SEC_CERTIFICATE_HOLD_KEY
,
3181 SEC_PRIV_WITHDRAWN_KEY
,
3182 SEC_AA_COMPROMISE_KEY
3184 appendBitStringContentNames(properties
, SEC_REASONS_KEY
,
3186 reasonNames
, array_size(reasonNames
));
3188 if (dp
.cRLIssuer
.length
) {
3189 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
3190 &kCFTypeArrayCallBacks
);
3191 appendProperty(properties
, kSecPropertyTypeSection
,
3192 SEC_CRL_ISSUER_KEY
, NULL
, crlIssuer
);
3193 CFRelease(crlIssuer
);
3194 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
);
3197 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3200 appendInvalidProperty(properties
, SEC_CRL_DISTR_POINTS_KEY
, extnValue
);
3203 /* Decode a sequence of integers into a comma separated list of ints. */
3204 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
3205 CFStringRef label
, const DERItem
*intSequenceContent
) {
3206 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3208 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
3209 require_noerr_quiet(drtn
, badDER
);
3210 DERDecodedInfo intContent
;
3211 CFStringRef value
= NULL
;
3212 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3213 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
)) == DR_Success
) {
3214 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
3215 CFStringRef intDesc
= copyIntegerContentDescription(
3216 allocator
, &intContent
.content
);
3219 v
= CFStringCreateWithFormat(allocator
, NULL
, fmt
, value
, intDesc
);
3228 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3230 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
3234 /* DROPTHOUGH if !value. */
3236 appendInvalidProperty(properties
, label
, intSequenceContent
);
3239 static void appendCertificatePolicies(CFMutableArrayRef properties
,
3240 const DERItem
*extnValue
) {
3241 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3244 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
3245 require_noerr_quiet(drtn
, badDER
);
3246 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3247 DERDecodedInfo piContent
;
3249 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
3250 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3251 DERPolicyInformation pi
;
3252 drtn
= DERParseSequenceContent(&piContent
.content
,
3253 DERNumPolicyInformationItemSpecs
,
3254 DERPolicyInformationItemSpecs
,
3256 require_noerr_quiet(drtn
, badDER
);
3257 CFStringRef piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3258 SEC_POLICY_IDENTIFIER_KEY
, pin
);
3259 CFStringRef piFmt
= SecCopyCertString(SEC_POLICY_IDENTIFIER_KEY
);
3260 CFStringRef lpiLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3263 appendOIDProperty(properties
, piLabel
, lpiLabel
, &pi
.policyIdentifier
);
3265 CFRelease(lpiLabel
);
3266 if (pi
.policyQualifiers
.length
== 0)
3270 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
3271 require_noerr_quiet(drtn
, badDER
);
3272 DERDecodedInfo pqContent
;
3274 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
3275 DERPolicyQualifierInfo pqi
;
3276 drtn
= DERParseSequenceContent(&pqContent
.content
,
3277 DERNumPolicyQualifierInfoItemSpecs
,
3278 DERPolicyQualifierInfoItemSpecs
,
3280 require_noerr_quiet(drtn
, badDER
);
3281 DERDecodedInfo qualifierContent
;
3282 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
3283 require_noerr_quiet(drtn
, badDER
);
3284 CFStringRef pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3285 SEC_POLICY_QUALIFIER_KEY
, pqn
);
3286 CFStringRef pqFmt
= SecCopyCertString(SEC_POLICY_QUALIFIER_KEY
);
3287 CFStringRef lpqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3290 appendOIDProperty(properties
, pqLabel
, lpqLabel
,
3291 &pqi
.policyQualifierID
);
3293 CFRelease(lpqLabel
);
3294 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
3295 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
3296 appendURLContentProperty(properties
, SEC_CPS_URI_KEY
,
3297 &qualifierContent
.content
);
3298 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
3299 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3301 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
3302 DERNumUserNoticeItemSpecs
,
3303 DERUserNoticeItemSpecs
,
3305 require_noerr_quiet(drtn
, badDER
);
3306 if (un
.noticeRef
.length
) {
3307 DERNoticeReference nr
;
3308 drtn
= DERParseSequenceContent(&un
.noticeRef
,
3309 DERNumNoticeReferenceItemSpecs
,
3310 DERNoticeReferenceItemSpecs
,
3312 require_noerr_quiet(drtn
, badDER
);
3313 appendDERThingProperty(properties
,
3314 SEC_ORGANIZATION_KEY
, NULL
,
3316 appendIntegerSequenceContent(properties
,
3317 SEC_NOTICE_NUMBERS_KEY
, &nr
.noticeNumbers
);
3319 if (un
.explicitText
.length
) {
3320 appendDERThingProperty(properties
, SEC_EXPLICIT_TEXT_KEY
,
3321 NULL
, &un
.explicitText
);
3324 appendUnparsedProperty(properties
, SEC_QUALIFIER_KEY
, NULL
,
3329 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3332 appendInvalidProperty(properties
, SEC_CERT_POLICIES_KEY
, extnValue
);
3335 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
3336 const DERItem
*extnValue
) {
3338 DERDecodedInfo keyIdentifier
;
3339 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
3340 require_noerr_quiet(drtn
, badDER
);
3341 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
3342 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3343 &keyIdentifier
.content
);
3347 appendInvalidProperty(properties
, SEC_SUBJ_KEY_ID_KEY
,
3352 AuthorityKeyIdentifier ::= SEQUENCE {
3353 keyIdentifier [0] KeyIdentifier OPTIONAL,
3354 authorityCertIssuer [1] GeneralNames OPTIONAL,
3355 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
3356 -- authorityCertIssuer and authorityCertSerialNumber MUST both
3357 -- be present or both be absent
3359 KeyIdentifier ::= OCTET STRING
3361 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
3362 const DERItem
*extnValue
) {
3363 DERAuthorityKeyIdentifier akid
;
3365 drtn
= DERParseSequence(extnValue
,
3366 DERNumAuthorityKeyIdentifierItemSpecs
,
3367 DERAuthorityKeyIdentifierItemSpecs
,
3368 &akid
, sizeof(akid
));
3369 require_noerr_quiet(drtn
, badDER
);
3370 if (akid
.keyIdentifier
.length
) {
3371 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3372 &akid
.keyIdentifier
);
3374 if (akid
.authorityCertIssuer
.length
||
3375 akid
.authorityCertSerialNumber
.length
) {
3376 require_quiet(akid
.authorityCertIssuer
.length
&&
3377 akid
.authorityCertSerialNumber
.length
, badDER
);
3378 /* Perhaps put in a subsection called Authority Certificate Issuer. */
3379 appendGeneralNamesContent(properties
,
3380 &akid
.authorityCertIssuer
);
3381 appendIntegerProperty(properties
, SEC_AUTH_CERT_SERIAL_KEY
,
3382 &akid
.authorityCertSerialNumber
);
3387 appendInvalidProperty(properties
, SEC_AUTHORITY_KEY_ID_KEY
, extnValue
);
3391 PolicyConstraints ::= SEQUENCE {
3392 requireExplicitPolicy [0] SkipCerts OPTIONAL,
3393 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
3395 SkipCerts ::= INTEGER (0..MAX)
3397 static void appendPolicyConstraints(CFMutableArrayRef properties
,
3398 const DERItem
*extnValue
) {
3399 DERPolicyConstraints pc
;
3401 drtn
= DERParseSequence(extnValue
,
3402 DERNumPolicyConstraintsItemSpecs
,
3403 DERPolicyConstraintsItemSpecs
,
3405 require_noerr_quiet(drtn
, badDER
);
3406 if (pc
.requireExplicitPolicy
.length
) {
3407 appendIntegerProperty(properties
, SEC_REQUIRE_EXPL_POLICY_KEY
,
3408 &pc
.requireExplicitPolicy
);
3410 if (pc
.inhibitPolicyMapping
.length
) {
3411 appendIntegerProperty(properties
, SEC_INHIBIT_POLICY_MAP_KEY
,
3412 &pc
.inhibitPolicyMapping
);
3418 appendInvalidProperty(properties
, SEC_POLICY_CONSTRAINTS_KEY
, extnValue
);
3422 extendedKeyUsage EXTENSION ::= {
3423 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
3424 IDENTIFIED BY id-ce-extKeyUsage }
3426 KeyPurposeId ::= OBJECT IDENTIFIER
3428 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
3429 const DERItem
*extnValue
) {
3432 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
3433 require_noerr_quiet(drtn
, badDER
);
3434 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3435 DERDecodedInfo currDecoded
;
3436 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3437 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
3438 appendOIDProperty(properties
, SEC_PURPOSE_KEY
, NULL
,
3439 &currDecoded
.content
);
3441 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3444 appendInvalidProperty(properties
, SEC_EXTENDED_KEY_USAGE_KEY
, extnValue
);
3448 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3450 AuthorityInfoAccessSyntax ::=
3451 SEQUENCE SIZE (1..MAX) OF AccessDescription
3453 AccessDescription ::= SEQUENCE {
3454 accessMethod OBJECT IDENTIFIER,
3455 accessLocation GeneralName }
3457 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3459 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3461 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3463 static void appendInfoAccess(CFMutableArrayRef properties
,
3464 const DERItem
*extnValue
) {
3467 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3468 require_noerr_quiet(drtn
, badDER
);
3469 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3470 DERDecodedInfo adContent
;
3471 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3472 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3473 DERAccessDescription ad
;
3474 drtn
= DERParseSequenceContent(&adContent
.content
,
3475 DERNumAccessDescriptionItemSpecs
,
3476 DERAccessDescriptionItemSpecs
,
3478 require_noerr_quiet(drtn
, badDER
);
3479 appendOIDProperty(properties
, SEC_ACCESS_METHOD_KEY
, NULL
,
3481 //TODO: Do something with SEC_ACCESS_LOCATION_KEY
3482 appendGeneralNameProperty(properties
, &ad
.accessLocation
);
3484 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3487 appendInvalidProperty(properties
, SEC_AUTH_INFO_ACCESS_KEY
, extnValue
);
3490 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3491 const DERItem
*extnValue
) {
3492 static const CFStringRef certTypes
[] = {
3496 SEC_OBJECT_SIGNING_KEY
,
3500 SEC_OBJECT_SIGNING_CA_KEY
3502 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3503 certTypes
, array_size(certTypes
));
3507 static void appendEntrustVersInfo(CFMutableArrayRef properties
,
3508 const DERItem
*extnValue
) {
3512 * The list of Qualified Cert Statement statementIds we understand, even though
3513 * we don't actually do anything with them; if these are found in a Qualified
3514 * Cert Statement that's critical, we can truthfully say "yes we understand this".
3516 static const CSSM_OID_PTR knownQualifiedCertStatements
[] =
3518 /* id-qcs := { id-pkix 11 } */
3519 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V1
, /* id-qcs 1 */
3520 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V2
, /* id-qcs 2 */
3521 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_COMPLIANCE
,
3522 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE
,
3523 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_RETENTION
,
3524 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_SSCD
3526 #define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
3528 static void appendQCCertStatements(CFMutableArrayRef properties
,
3529 const DERItem
*extnValue
) {
3534 static bool appendPrintableDERSequence(CFMutableArrayRef properties
,
3535 CFStringRef label
, const DERItem
*sequence
) {
3538 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3539 require_noerr_quiet(drtn
, badSequence
);
3540 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3541 DERDecodedInfo currDecoded
;
3542 bool appendedSomething
= false;
3543 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3544 switch (currDecoded
.tag
)
3547 case ASN1_SEQUENCE
: // 16
3548 case ASN1_SET
: // 17
3549 // skip constructed object lengths
3552 case ASN1_UTF8_STRING
: // 12
3553 case ASN1_NUMERIC_STRING
: // 18
3554 case ASN1_PRINTABLE_STRING
: // 19
3555 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3556 case ASN1_VIDEOTEX_STRING
: // 21
3557 case ASN1_IA5_STRING
: // 22
3558 case ASN1_GRAPHIC_STRING
: // 25
3559 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3560 case ASN1_GENERAL_STRING
: // 27
3561 case ASN1_UNIVERSAL_STRING
: // 28
3563 CFStringRef string
=
3564 copyDERThingContentDescription(CFGetAllocator(properties
),
3565 currDecoded
.tag
, &currDecoded
.content
, false);
3566 //CFStringRef cleanString = copyStringRemovingPercentEscapes(string);
3568 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3571 appendedSomething
= true;
3578 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3579 return appendedSomething
;
3584 static void appendExtension(CFMutableArrayRef parent
,
3585 const SecCertificateExtension
*extn
) {
3586 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3587 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3588 &kCFTypeArrayCallBacks
);
3590 *extnID
= &extn
->extnID
,
3591 *extnValue
= &extn
->extnValue
;
3592 CFStringRef label
= NULL
;
3593 CFStringRef localizedLabel
= NULL
;
3595 appendBoolProperty(properties
, SEC_CRITICAL_KEY
, extn
->critical
);
3596 require_quiet(extnID
, xit
);
3599 bool handled
= true;
3600 /* Extensions that we know how to handle ourselves... */
3601 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3602 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3604 switch (extnID
->data
[extnID
->length
- 1]) {
3605 case 14: /* SubjectKeyIdentifier id-ce 14 */
3606 appendSubjectKeyIdentifier(properties
, extnValue
);
3608 case 15: /* KeyUsage id-ce 15 */
3609 appendKeyUsage(properties
, extnValue
);
3611 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3612 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3614 case 17: /* SubjectAltName id-ce 17 */
3615 case 18: /* IssuerAltName id-ce 18 */
3616 appendGeneralNames(properties
, extnValue
);
3618 case 19: /* BasicConstraints id-ce 19 */
3619 appendBasicConstraints(properties
, extnValue
);
3621 case 30: /* NameConstraints id-ce 30 */
3622 appendNameConstraints(properties
, extnValue
);
3624 case 31: /* CRLDistributionPoints id-ce 31 */
3625 appendCrlDistributionPoints(properties
, extnValue
);
3627 case 32: /* CertificatePolicies id-ce 32 */
3628 appendCertificatePolicies(properties
, extnValue
);
3630 case 33: /* PolicyMappings id-ce 33 */
3633 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3634 appendAuthorityKeyIdentifier(properties
, extnValue
);
3636 case 36: /* PolicyConstraints id-ce 36 */
3637 appendPolicyConstraints(properties
, extnValue
);
3639 case 37: /* ExtKeyUsage id-ce 37 */
3640 appendExtendedKeyUsage(properties
, extnValue
);
3642 case 46: /* FreshestCRL id-ce 46 */
3645 case 54: /* InhibitAnyPolicy id-ce 54 */
3652 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3653 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3655 switch (extnID
->data
[extnID
->length
- 1]) {
3656 case 1: /* AuthorityInfoAccess id-pe 1 */
3657 appendInfoAccess(properties
, extnValue
);
3659 case 3: /* QCStatements id-pe 3 */
3662 case 11: /* SubjectInfoAccess id-pe 11 */
3663 appendInfoAccess(properties
, extnValue
);
3669 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3670 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3671 appendNetscapeCertType(properties
, extnValue
);
3677 /* Try to parse and display printable string(s). */
3678 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
)) {
3679 /* Nothing to do here appendPrintableDERSequence did the work. */
3681 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3682 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
);
3686 /* Extensions that we know how to handle ourselves... */
3687 if (DEROidCompare(extnID
, &oidSubjectKeyIdentifier
)) {
3688 appendSubjectKeyIdentifier(properties
, extnValue
);
3689 } else if (DEROidCompare(extnID
, &oidKeyUsage
)) {
3690 appendKeyUsage(properties
, extnValue
);
3691 } else if (DEROidCompare(extnID
, &oidPrivateKeyUsagePeriod
)) {
3692 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3693 } else if (DEROidCompare(extnID
, &oidSubjectAltName
)) {
3694 appendGeneralNames(properties
, extnValue
);
3695 } else if (DEROidCompare(extnID
, &oidIssuerAltName
)) {
3696 appendGeneralNames(properties
, extnValue
);
3697 } else if (DEROidCompare(extnID
, &oidBasicConstraints
)) {
3698 appendBasicConstraints(properties
, extnValue
);
3699 } else if (DEROidCompare(extnID
, &oidNameConstraints
)) {
3700 appendNameConstraints(properties
, extnValue
);
3701 } else if (DEROidCompare(extnID
, &oidCrlDistributionPoints
)) {
3702 appendCrlDistributionPoints(properties
, extnValue
);
3703 } else if (DEROidCompare(extnID
, &oidCertificatePolicies
)) {
3704 appendCertificatePolicies(properties
, extnValue
);
3705 } else if (DEROidCompare(extnID
, &oidAuthorityKeyIdentifier
)) {
3706 appendAuthorityKeyIdentifier(properties
, extnValue
);
3707 } else if (DEROidCompare(extnID
, &oidPolicyConstraints
)) {
3708 appendPolicyConstraints(properties
, extnValue
);
3709 } else if (DEROidCompare(extnID
, &oidExtendedKeyUsage
)) {
3710 appendExtendedKeyUsage(properties
, extnValue
);
3711 } else if (DEROidCompare(extnID
, &oidAuthorityInfoAccess
)) {
3712 appendInfoAccess(properties
, extnValue
);
3713 } else if (DEROidCompare(extnID
, &oidSubjectInfoAccess
)) {
3714 appendInfoAccess(properties
, extnValue
);
3715 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3716 appendNetscapeCertType(properties
, extnValue
);
3718 } else if (DEROidCompare(extnID
, &oidEntrustVersInfo
)) {
3719 appendEntrustVersInfo(properties
, extnValue
);
3722 /* Try to parse and display printable string(s). */
3723 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
)) {
3724 /* Nothing to do here appendPrintableDERSequence did the work. */
3726 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3727 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
);
3730 label
= SecDERItemCopyOIDDecimalRepresentation(allocator
, extnID
);
3731 localizedLabel
= copyLocalizedOidDescription(allocator
, extnID
);
3732 appendProperty(parent
, kSecPropertyTypeSection
, label
, localizedLabel
, properties
);
3735 CFReleaseSafe(localizedLabel
);
3736 CFReleaseSafe(label
);
3737 CFReleaseSafe(properties
);
3740 /* Different types of summary types from least desired to most desired. */
3743 kSummaryTypePrintable
,
3744 kSummaryTypeOrganizationName
,
3745 kSummaryTypeOrganizationalUnitName
,
3746 kSummaryTypeCommonName
,
3750 enum SummaryType type
;
3751 CFStringRef summary
;
3752 CFStringRef description
;
3755 static OSStatus
obtainSummaryFromX501Name(void *context
,
3756 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
3757 struct Summary
*summary
= (struct Summary
*)context
;
3758 enum SummaryType stype
= kSummaryTypeNone
;
3759 CFStringRef string
= NULL
;
3760 if (DEROidCompare(type
, &oidCommonName
)) {
3761 stype
= kSummaryTypeCommonName
;
3762 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
3763 stype
= kSummaryTypeOrganizationalUnitName
;
3764 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
3765 stype
= kSummaryTypeOrganizationName
;
3766 } else if (DEROidCompare(type
, &oidDescription
)) {
3767 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3769 if (summary
->description
) {
3770 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3771 CFStringRef newDescription
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, string
, summary
->description
);
3773 CFRelease(summary
->description
);
3774 summary
->description
= newDescription
;
3776 summary
->description
= string
;
3779 stype
= kSummaryTypePrintable
;
3782 stype
= kSummaryTypePrintable
;
3785 /* Build a string with all instances of the most desired
3786 component type in reverse order encountered comma separated list,
3787 The order of desirability is defined by enum SummaryType. */
3788 if (summary
->type
<= stype
) {
3790 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3793 if (summary
->type
== stype
) {
3794 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3795 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, string
, summary
->summary
);
3798 string
= newSummary
;
3800 summary
->type
= stype
;
3802 CFReleaseSafe(summary
->summary
);
3803 summary
->summary
= string
;
3806 CFReleaseSafe(string
);
3809 return errSecSuccess
;
3812 CFStringRef
SecCertificateCopySubjectSummary(SecCertificateRef certificate
) {
3813 struct Summary summary
= {};
3814 parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
);
3815 /* If we found a description and a common name we change the summary to
3816 CommonName (Description). */
3817 if (summary
.description
) {
3818 if (summary
.type
== kSummaryTypeCommonName
) {
3819 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3820 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3822 CFRelease(summary
.summary
);
3823 summary
.summary
= newSummary
;
3825 CFRelease(summary
.description
);
3828 if (!summary
.summary
) {
3829 /* If we didn't find a suitable printable string in the subject at all, we try
3830 the first email address in the certificate instead. */
3831 CFArrayRef names
= SecCertificateCopyRFC822Names(certificate
);
3833 /* If we didn't find any email addresses in the certificate, we try finding
3834 a DNS name instead. */
3835 names
= SecCertificateCopyDNSNames(certificate
);
3838 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
3839 CFRetain(summary
.summary
);
3844 return summary
.summary
;
3847 CFStringRef
SecCertificateCopyIssuerSummary(SecCertificateRef certificate
) {
3848 struct Summary summary
= {};
3849 parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
);
3850 /* If we found a description and a common name we change the summary to
3851 CommonName (Description). */
3852 if (summary
.description
) {
3853 if (summary
.type
== kSummaryTypeCommonName
) {
3854 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3855 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3857 CFRelease(summary
.summary
);
3858 summary
.summary
= newSummary
;
3860 CFRelease(summary
.description
);
3863 return summary
.summary
;
3866 /* Return the earliest date on which all certificates in this chain are still
3868 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
3869 SecCertificateRef certificate
) {
3870 CFAbsoluteTime earliest
= certificate
->_notAfter
;
3872 while (certificate
->_parent
) {
3873 certificate
= certificate
->_parent
;
3874 if (earliest
> certificate
->_notAfter
)
3875 earliest
= certificate
->_notAfter
;
3882 /* Return the latest date on which all certificates in this chain will be
3884 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
3885 SecCertificateRef certificate
) {
3886 CFAbsoluteTime latest
= certificate
->_notBefore
;
3888 while (certificate
->_parent
) {
3889 certificate
= certificate
->_parent
;
3890 if (latest
< certificate
->_notBefore
)
3891 latest
= certificate
->_notBefore
;
3898 bool SecCertificateIsValid(SecCertificateRef certificate
,
3899 CFAbsoluteTime verifyTime
) {
3900 return certificate
&& certificate
->_notBefore
<= verifyTime
&&
3901 verifyTime
<= certificate
->_notAfter
;
3904 CFIndex
SecCertificateVersion(SecCertificateRef certificate
) {
3905 return certificate
->_version
+ 1;
3908 CFAbsoluteTime
SecCertificateNotValidBefore(SecCertificateRef certificate
) {
3909 return certificate
->_notBefore
;
3912 CFAbsoluteTime
SecCertificateNotValidAfter(SecCertificateRef certificate
) {
3913 return certificate
->_notAfter
;
3916 CFMutableArrayRef
SecCertificateCopySummaryProperties(
3917 SecCertificateRef certificate
, CFAbsoluteTime verifyTime
) {
3918 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3919 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
3920 &kCFTypeArrayCallBacks
);
3922 /* First we put the subject summary name. */
3923 CFStringRef ssummary
= SecCertificateCopySubjectSummary(certificate
);
3925 appendProperty(summary
, kSecPropertyTypeTitle
,
3926 NULL
, NULL
, ssummary
);
3927 CFRelease(ssummary
);
3930 CFStringRef isummary
= SEC_ISSUER_SUMMARY_KEY
;
3931 appendProperty(summary
, kSecPropertyTypeString
,
3932 SEC_ISSUED_BY_KEY
, isummary
);
3933 CFRelease(isummary
);
3936 /* Let see if this certificate is currently valid. */
3938 CFAbsoluteTime when
;
3939 CFStringRef message
;
3941 if (verifyTime
> certificate
->_notAfter
) {
3942 label
= SEC_EXPIRED_KEY
;
3943 when
= certificate
->_notAfter
;
3944 ptype
= kSecPropertyTypeError
;
3945 message
= SEC_CERT_EXPIRED_KEY
;
3946 } else if (certificate
->_notBefore
> verifyTime
) {
3947 label
= SEC_VALID_FROM_KEY
;
3948 when
= certificate
->_notBefore
;
3949 ptype
= kSecPropertyTypeError
;
3950 message
= SEC_CERT_NOT_YET_VALID_KEY
;
3952 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
3953 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
3954 if (verifyTime
> last
) {
3955 label
= SEC_EXPIRED_KEY
;
3957 ptype
= kSecPropertyTypeError
;
3958 message
= SEC_ISSUER_EXPIRED_KEY
;
3959 } else if (verifyTime
< first
) {
3960 label
= SEC_VALID_FROM_KEY
;
3962 ptype
= kSecPropertyTypeError
;
3963 message
= SEC_ISSR_NOT_YET_VALID_KEY
;
3965 label
= SEC_EXPIRES_KEY
;
3966 when
= certificate
->_notAfter
;
3967 ptype
= kSecPropertyTypeSuccess
;
3968 message
= SEC_CERT_VALID_KEY
;
3972 appendDateProperty(summary
, label
, when
);
3973 CFStringRef lmessage
= SecCopyCertString(message
);
3974 appendProperty(summary
, ptype
, NULL
, NULL
, lmessage
);
3975 CFRelease(lmessage
);
3980 CFArrayRef
SecCertificateCopyProperties(SecCertificateRef certificate
) {
3981 if (!certificate
->_properties
) {
3982 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3983 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3984 &kCFTypeArrayCallBacks
);
3986 /* First we put the Subject Name in the property list. */
3987 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
3988 &certificate
->_subject
);
3989 appendProperty(properties
, kSecPropertyTypeSection
,
3990 SEC_SUBJECT_NAME_KEY
, NULL
, subject_plist
);
3991 CFRelease(subject_plist
);
3994 /* Put Normalized subject in for testing. */
3995 if (certificate
->_normalizedSubject
) {
3996 DERItem nsubject
= {
3997 (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
),
3998 CFDataGetLength(certificate
->_normalizedSubject
)
4000 CFArrayRef nsubject_plist
= createPropertiesForX501NameContent(allocator
,
4002 appendProperty(properties
, kSecPropertyTypeSection
,
4003 CFSTR("Normalized Subject Name"), nsubject_plist
);
4004 CFRelease(nsubject_plist
);
4008 /* Next we put the Issuer Name in the property list. */
4009 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
4010 &certificate
->_issuer
);
4011 appendProperty(properties
, kSecPropertyTypeSection
,
4012 SEC_ISSUER_NAME_KEY
, NULL
, issuer_plist
);
4013 CFRelease(issuer_plist
);
4016 /* Certificate version/type. */
4017 bool isRoot
= false;
4018 CFStringRef fmt
= SecCopyCertString(SEC_X509_VERSION_KEY
);
4019 CFStringRef typeString
= CFStringCreateWithFormat(allocator
, NULL
,
4020 fmt
, certificate
->_version
+ 1, isRoot
? "root " : "");
4022 appendProperty(properties
, kSecPropertyTypeString
,
4023 SEC_CERTIFICATE_TYPE_KEY
, typeString
);
4024 CFRelease(typeString
);
4028 CFStringRef fmt
= SecCopyCertString(SEC_CERT_VERSION_VALUE_KEY
);
4029 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
4030 NULL
, fmt
, certificate
->_version
+ 1);
4032 appendProperty(properties
, kSecPropertyTypeString
,
4033 SEC_VERSION_KEY
, NULL
, versionString
);
4034 CFRelease(versionString
);
4037 if (certificate
->_serialNum
.length
) {
4038 appendIntegerProperty(properties
, SEC_SERIAL_NUMBER_KEY
,
4039 &certificate
->_serialNum
);
4042 /* Signature algorithm. */
4044 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
4045 &certificate
->_sigAlg
);
4047 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
4048 &certificate
->_tbsSigAlg
);
4051 /* Validity dates. */
4052 appendDateProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
4053 certificate
->_notBefore
);
4054 appendDateProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
4055 certificate
->_notAfter
);
4057 if (certificate
->_subjectUniqueID
.length
) {
4058 appendDataProperty(properties
, SEC_SUBJECT_UNIQUE_ID_KEY
, NULL
,
4059 &certificate
->_subjectUniqueID
);
4061 if (certificate
->_issuerUniqueID
.length
) {
4062 appendDataProperty(properties
, SEC_ISSUER_UNIQUE_ID_KEY
, NULL
,
4063 &certificate
->_issuerUniqueID
);
4066 /* Public key algorithm. */
4067 appendAlgorithmProperty(properties
, SEC_PUBLIC_KEY_ALG_KEY
,
4068 &certificate
->_algId
);
4070 /* Consider breaking down an RSA public key into modulus and
4072 appendDataProperty(properties
, SEC_PULIC_KEY_DATA_KEY
, NULL
,
4073 &certificate
->_pubKeyDER
);
4074 /* TODO: Add Key Size. */
4075 /* TODO: Add Key Usage. */
4077 appendDataProperty(properties
, SEC_SIGNATURE_KEY
, NULL
,
4078 &certificate
->_signature
);
4081 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4082 appendExtension(properties
, &certificate
->_extensions
[ix
]);
4085 /* TODO: Certificate/Key Fingerprints. */
4087 certificate
->_properties
= properties
;
4090 CFRetain(certificate
->_properties
);
4091 return certificate
->_properties
;
4094 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
4095 /* On OS X, the SecCertificateCopySerialNumber API takes two arguments. */
4096 CFDataRef
SecCertificateCopySerialNumber(
4097 SecCertificateRef certificate
,
4098 CFErrorRef
*error
) {
4101 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
4105 if (certificate
->_serialNumber
) {
4106 CFRetain(certificate
->_serialNumber
);
4108 return certificate
->_serialNumber
;
4111 /* On iOS, the SecCertificateCopySerialNumber API takes one argument. */
4112 CFDataRef
SecCertificateCopySerialNumber(
4113 SecCertificateRef certificate
) {
4114 if (certificate
->_serialNumber
) {
4115 CFRetain(certificate
->_serialNumber
);
4117 return certificate
->_serialNumber
;
4121 CFDataRef
SecCertificateGetNormalizedIssuerContent(
4122 SecCertificateRef certificate
) {
4123 return certificate
->_normalizedIssuer
;
4126 CFDataRef
SecCertificateGetNormalizedSubjectContent(
4127 SecCertificateRef certificate
) {
4128 return certificate
->_normalizedSubject
;
4131 /* Verify that certificate was signed by issuerKey. */
4132 OSStatus
SecCertificateIsSignedBy(SecCertificateRef certificate
,
4133 SecKeyRef issuerKey
) {
4134 /* Setup algId in SecAsn1AlgId format. */
4136 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
4137 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
4138 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
4139 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
4141 OSStatus status
= SecKeyDigestAndVerify(issuerKey
, &algId
,
4142 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
4143 certificate
->_signature
.data
, certificate
->_signature
.length
);
4145 secdebug("verify", "signature verify failed: %" PRIdOSStatus
, status
);
4146 return errSecNotSigner
;
4149 return errSecSuccess
;
4153 static OSStatus
SecCertificateIsIssuedBy(SecCertificateRef certificate
,
4154 SecCertificateRef issuer
, bool signatureCheckOnly
) {
4155 if (!signatureCheckOnly
) {
4156 /* It turns out we don't actually need to use normalized subject and
4157 issuer according to rfc2459. */
4159 /* If present we should check issuerID against the issuer subjectID. */
4161 /* If we have an AuthorityKeyIdentifier extension that has a keyIdentifier
4162 then we should look for a SubjectKeyIdentifier in the issuer
4164 If we have a authorityCertSerialNumber we can use that for chaining.
4165 If we have a authorityCertIssuer we can use that? (or not) */
4167 /* Verify that this cert was issued by issuer. Do so by chaining
4168 either issuerID to subjectID or normalized issuer to normalized
4170 CFDataRef normalizedIssuer
=
4171 SecCertificateGetNormalizedIssuerContent(certificate
);
4172 CFDataRef normalizedIssuerSubject
=
4173 SecCertificateGetNormalizedSubjectContent(issuer
);
4174 if (normalizedIssuer
&& normalizedIssuerSubject
&&
4175 !CFEqual(normalizedIssuer
, normalizedIssuerSubject
))
4176 return errSecIssuerMismatch
;
4179 /* Next verify that this cert was signed by issuer. */
4180 SecKeyRef issuerKey
= SecCertificateGetPublicKey(issuer
);
4182 /* Get the encodedDigestInfo from the digest of the subject's TBSCert */
4183 /* FIXME: We sould cache this (or at least the digest) until we find
4184 a suitable issuer. */
4185 uint8_t signedData
[DER_SHA1_DIGEST_INFO_LEN
];
4186 CFIndex signedDataLength
;
4187 CertVerifyReturn crtn
;
4188 if (DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidSha1Rsa
)) {
4189 signedDataLength
= DER_SHA1_DIGEST_INFO_LEN
;
4190 crtn
= sha1DigestInfo(&certificate
->_tbs
, signedData
);
4191 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd5Rsa
)) {
4192 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
4193 crtn
= mdDigestInfo(WD_MD5
, &certificate
->_tbs
, signedData
);
4194 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd2Rsa
)) {
4195 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
4196 crtn
= mdDigestInfo(WD_MD2
, &certificate
->_tbs
, signedData
);
4198 secdebug("verify", "unsupported algorithm");
4199 return errSecUnsupportedAlgorithm
;
4202 secdebug("verify", "*DigestInfo returned: %d", crtn
);
4203 /* FIXME: Do proper error code translation. */
4204 return errSecUnsupportedAlgorithm
;
4207 OSStatus status
= SecKeyRawVerify(issuerKey
, kSecPaddingPKCS1
,
4208 signedData
, signedDataLength
,
4209 certificate
->_signature
.data
, certificate
->_signature
.length
);
4211 secdebug("verify", "signature verify failed: %d", status
);
4212 return errSecNotSigner
;
4215 return errSecSuccess
;
4218 static OSStatus
_SecCertificateSetParent(SecCertificateRef certificate
,
4219 SecCertificateRef issuer
, bool signatureCheckOnly
) {
4221 if (certificate
->_parent
) {
4222 /* Setting a certificates issuer twice is only allowed if the new
4223 issuer is equal to the current one. */
4224 return issuer
&& CFEqual(certificate
->_parent
, issuer
);
4228 OSStatus status
= SecCertificateIsIssuedBy(certificate
, issuer
,
4229 signatureCheckOnly
);
4231 OSStatus status
= errSecSuccess
;
4234 if (CFEqual(certificate
, issuer
)) {
4235 /* We don't retain ourselves cause that would be bad mojo,
4236 however we do record that we are properly self signed. */
4237 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
4238 secdebug("cert", "set self as parent");
4239 return errSecSuccess
;
4243 certificate
->_parent
= issuer
;
4244 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
4250 /* Return true iff we were able to set our own parent from one of the
4251 certificates in other_certificates, return false otherwise. If
4252 signatureCheckOnly is true, we can skip the subject == issuer or
4253 authorityKeyIdentifier tests. */
4254 static bool SecCertificateSetParentFrom(SecCertificateRef certificate
,
4255 CFArrayRef other_certificates
, bool signatureCheckOnly
) {
4256 CFIndex count
= CFArrayGetCount(other_certificates
);
4258 for (ix
= 0; ix
< count
; ++ix
) {
4259 SecCertificateRef candidate
= (SecCertificateRef
)
4260 CFArrayGetValueAtIndex(other_certificates
, ix
);
4261 if (_SecCertificateSetParent(certificate
, candidate
,
4262 signatureCheckOnly
))
4268 /* Lookup the parent of certificate in the keychain and set it. */
4269 static bool SecCertificateFindParent(SecCertificateRef certificate
) {
4270 /* FIXME: Search for things other than just subject of our issuer if we
4271 have a subjectID or authorityKeyIdentifier. */
4272 CFDataRef normalizedIssuer
=
4273 SecCertificateGetNormalizedIssuerContent(certificate
);
4274 const void *keys
[] = {
4281 kSecClassCertificate
,
4286 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4,
4287 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4289 OSStatus status
= SecItemCopyMatching(query
, &results
);
4292 secdebug("cert", "SecCertificateFindParent: SecItemCopyMatching: %d",
4296 CFArrayRef certs
= (CFArrayRef
)results
;
4297 /* Since we already know the certificates we are providing as candidates
4298 have been checked for subject matching, we can ask
4299 SecCertificateSetParentFrom to skip everything except the signature
4301 bool result
= SecCertificateSetParentFrom(certificate
, certs
, true);
4306 OSStatus
SecCertificateCompleteChain(SecCertificateRef certificate
,
4307 CFArrayRef other_certificates
) {
4309 if (certificate
->_parent
== NULL
) {
4310 Boolean isSelfSigned
= false;
4311 OSStatus status
= SecCertificateIsSelfSigned(certificate
, &isSelfSigned
);
4312 if (!status
&& isSelfSigned
)
4313 return errSecSuccess
;
4314 if (!other_certificates
||
4315 !SecCertificateSetParentFrom(certificate
, other_certificates
,
4317 if (!SecCertificateFindParent(certificate
))
4318 return errSecIssuerNotFound
;
4321 certificate
= certificate
->_parent
;
4326 const DERItem
* SecCertificateGetSubjectAltName(SecCertificateRef certificate
) {
4327 if (!certificate
->_subjectAltName
) {
4330 return &certificate
->_subjectAltName
->extnValue
;
4333 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
4334 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4335 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
4336 if (gnType
== GNT_IPAddress
) {
4337 CFStringRef string
= copyIPAddressContentDescription(
4338 kCFAllocatorDefault
, generalName
);
4340 CFArrayAppendValue(ipAddresses
, string
);
4343 return errSecInvalidCertificate
;
4346 return errSecSuccess
;
4349 CFArrayRef
SecCertificateCopyIPAddresses(SecCertificateRef certificate
) {
4350 /* These can only exist in the subject alt name. */
4351 if (!certificate
->_subjectAltName
)
4354 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
4355 0, &kCFTypeArrayCallBacks
);
4356 OSStatus status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4357 ipAddresses
, appendIPAddressesFromGeneralNames
);
4358 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
4359 CFRelease(ipAddresses
);
4365 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
4366 const DERItem
*generalName
) {
4367 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4368 if (gnType
== GNT_DNSName
) {
4369 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4370 generalName
->data
, generalName
->length
,
4371 kCFStringEncodingUTF8
, FALSE
);
4373 CFArrayAppendValue(dnsNames
, string
);
4376 return errSecInvalidCertificate
;
4379 return errSecSuccess
;
4382 /* Return true if the passed in string matches the
4383 Preferred name syntax from sections 2.3.1. in RFC 1035.
4384 With the added check that we disallow empty dns names.
4385 Also in order to support wildcard DNSNames we allow for the '*'
4386 character anywhere in a dns component where we currently allow
4389 <domain> ::= <subdomain> | " "
4391 <subdomain> ::= <label> | <subdomain> "." <label>
4393 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
4395 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
4397 <let-dig-hyp> ::= <let-dig> | "-"
4399 <let-dig> ::= <letter> | <digit>
4401 <letter> ::= any one of the 52 alphabetic characters A through Z in
4402 upper case and a through z in lower case
4404 <digit> ::= any one of the ten digits 0 through 9
4406 static bool isDNSName(CFStringRef string
) {
4407 CFStringInlineBuffer buf
;
4408 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
4409 /* From RFC 1035 2.3.4. Size limits:
4410 labels 63 octets or less
4411 names 255 octets or less */
4412 require_quiet(length
<= 255, notDNS
);
4413 CFRange range
= { 0, length
};
4414 CFStringInitInlineBuffer(string
, &buf
, range
);
4418 kDNSStateAfterAlpha
,
4419 kDNSStateAfterDigit
,
4421 } state
= kDNSStateInital
;
4423 for (ix
= 0; ix
< length
; ++ix
) {
4424 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
4427 require_quiet(labelLength
<= 64 &&
4428 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4430 state
= kDNSStateAfterDot
;
4432 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
4434 state
= kDNSStateAfterAlpha
;
4435 } else if ('0' <= ch
&& ch
<= '9') {
4437 /* The requirement for labels to start with a letter was
4438 dropped so we don't check this anymore. */
4439 require_quiet(state
== kDNSStateAfterAlpha
||
4440 state
== kDNSStateAfterDigit
||
4441 state
== kDNSStateAfterDash
, notDNS
);
4443 state
= kDNSStateAfterDigit
;
4444 } else if (ch
== '-') {
4445 require_quiet(state
== kDNSStateAfterAlpha
||
4446 state
== kDNSStateAfterDigit
||
4447 state
== kDNSStateAfterDash
, notDNS
);
4448 state
= kDNSStateAfterDash
;
4454 /* We don't allow a dns name to end in a dot or dash. */
4455 require_quiet(labelLength
<= 63 &&
4456 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4464 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4465 const DERItem
*value
, CFIndex rdnIX
) {
4466 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4467 if (DEROidCompare(type
, &oidCommonName
)) {
4468 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4471 if (isDNSName(string
)) {
4472 /* We found a common name that is formatted like a valid
4474 CFArrayAppendValue(dnsNames
, string
);
4478 return errSecInvalidCertificate
;
4481 return errSecSuccess
;
4484 /* Not everything returned by this function is going to be a proper DNS name,
4485 we also return the certificates common name entries from the subject,
4486 assuming they look like dns names as specified in RFC 1035. */
4487 CFArrayRef
SecCertificateCopyDNSNames(SecCertificateRef certificate
) {
4488 /* These can exist in the subject alt name or in the subject. */
4489 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4490 0, &kCFTypeArrayCallBacks
);
4491 OSStatus status
= errSecSuccess
;
4492 if (certificate
->_subjectAltName
) {
4493 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4494 dnsNames
, appendDNSNamesFromGeneralNames
);
4496 /* RFC 2818 section 3.1. Server Identity
4498 If a subjectAltName extension of type dNSName is present, that MUST
4499 be used as the identity. Otherwise, the (most specific) Common Name
4500 field in the Subject field of the certificate MUST be used. Although
4501 the use of the Common Name is existing practice, it is deprecated and
4502 Certification Authorities are encouraged to use the dNSName instead.
4505 This implies that if we found 1 or more DNSNames in the
4506 subjectAltName, we should not use the Common Name of the subject as
4509 if (!status
&& CFArrayGetCount(dnsNames
) == 0) {
4510 status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4511 appendDNSNamesFromX501Name
);
4513 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4514 CFRelease(dnsNames
);
4520 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4521 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4522 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4523 if (gnType
== GNT_RFC822Name
) {
4524 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4525 generalName
->data
, generalName
->length
,
4526 kCFStringEncodingASCII
, FALSE
);
4528 CFArrayAppendValue(dnsNames
, string
);
4531 return errSecInvalidCertificate
;
4534 return errSecSuccess
;
4537 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4538 const DERItem
*value
, CFIndex rdnIX
) {
4539 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4540 if (DEROidCompare(type
, &oidEmailAddress
)) {
4541 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4544 CFArrayAppendValue(dnsNames
, string
);
4547 return errSecInvalidCertificate
;
4550 return errSecSuccess
;
4553 CFArrayRef
SecCertificateCopyRFC822Names(SecCertificateRef certificate
) {
4554 /* These can exist in the subject alt name or in the subject. */
4555 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4556 0, &kCFTypeArrayCallBacks
);
4557 OSStatus status
= errSecSuccess
;
4558 if (certificate
->_subjectAltName
) {
4559 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4560 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4563 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4564 appendRFC822NamesFromX501Name
);
4566 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4567 CFRelease(rfc822Names
);
4573 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4574 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4575 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4576 if (DEROidCompare(type
, &oidCommonName
)) {
4577 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4580 CFArrayAppendValue(commonNames
, string
);
4583 return errSecInvalidCertificate
;
4586 return errSecSuccess
;
4589 CFArrayRef
SecCertificateCopyCommonNames(SecCertificateRef certificate
) {
4590 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4591 0, &kCFTypeArrayCallBacks
);
4593 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4594 appendCommonNamesFromX501Name
);
4595 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4596 CFRelease(commonNames
);
4602 static OSStatus
appendOrganizationFromX501Name(void *context
,
4603 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4604 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4605 if (DEROidCompare(type
, &oidOrganizationName
)) {
4606 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4609 CFArrayAppendValue(organization
, string
);
4612 return errSecInvalidCertificate
;
4615 return errSecSuccess
;
4618 CFArrayRef
SecCertificateCopyOrganization(SecCertificateRef certificate
) {
4619 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4620 0, &kCFTypeArrayCallBacks
);
4622 status
= parseX501NameContent(&certificate
->_subject
, organization
,
4623 appendOrganizationFromX501Name
);
4624 if (status
|| CFArrayGetCount(organization
) == 0) {
4625 CFRelease(organization
);
4626 organization
= NULL
;
4628 return organization
;
4631 static OSStatus
appendOrganizationalUnitFromX501Name(void *context
,
4632 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4633 CFMutableArrayRef organizationalUnit
= (CFMutableArrayRef
)context
;
4634 if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4635 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4638 CFArrayAppendValue(organizationalUnit
, string
);
4641 return errSecInvalidCertificate
;
4644 return errSecSuccess
;
4647 CFArrayRef
SecCertificateCopyOrganizationalUnit(SecCertificateRef certificate
) {
4648 CFMutableArrayRef organizationalUnit
= CFArrayCreateMutable(kCFAllocatorDefault
,
4649 0, &kCFTypeArrayCallBacks
);
4651 status
= parseX501NameContent(&certificate
->_subject
, organizationalUnit
,
4652 appendOrganizationalUnitFromX501Name
);
4653 if (status
|| CFArrayGetCount(organizationalUnit
) == 0) {
4654 CFRelease(organizationalUnit
);
4655 organizationalUnit
= NULL
;
4657 return organizationalUnit
;
4660 const SecCEBasicConstraints
*
4661 SecCertificateGetBasicConstraints(SecCertificateRef certificate
) {
4662 if (certificate
->_basicConstraints
.present
)
4663 return &certificate
->_basicConstraints
;
4668 CFArrayRef
SecCertificateGetPermittedSubtrees(SecCertificateRef certificate
) {
4669 return (certificate
->_permittedSubtrees
);
4672 CFArrayRef
SecCertificateGetExcludedSubtrees(SecCertificateRef certificate
) {
4673 return (certificate
->_excludedSubtrees
);
4676 const SecCEPolicyConstraints
*
4677 SecCertificateGetPolicyConstraints(SecCertificateRef certificate
) {
4678 if (certificate
->_policyConstraints
.present
)
4679 return &certificate
->_policyConstraints
;
4685 SecCertificateGetPolicyMappings(SecCertificateRef certificate
) {
4686 return certificate
->_policyMappings
;
4689 const SecCECertificatePolicies
*
4690 SecCertificateGetCertificatePolicies(SecCertificateRef certificate
) {
4691 if (certificate
->_certificatePolicies
.present
)
4692 return &certificate
->_certificatePolicies
;
4698 SecCertificateGetInhibitAnyPolicySkipCerts(SecCertificateRef certificate
) {
4699 return certificate
->_inhibitAnyPolicySkipCerts
;
4702 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
4703 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4704 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
4705 if (gnType
== GNT_OtherName
) {
4707 DERReturn drtn
= DERParseSequenceContent(generalName
,
4708 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
4710 require_noerr_quiet(drtn
, badDER
);
4711 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
4713 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
4714 &on
.value
, true), badDER
);
4715 CFArrayAppendValue(ntPrincipalNames
, string
);
4719 return errSecSuccess
;
4722 return errSecInvalidCertificate
;
4726 CFArrayRef
SecCertificateCopyNTPrincipalNames(SecCertificateRef certificate
) {
4727 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4728 0, &kCFTypeArrayCallBacks
);
4729 OSStatus status
= errSecSuccess
;
4730 if (certificate
->_subjectAltName
) {
4731 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4732 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
4734 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
4735 CFRelease(ntPrincipalNames
);
4736 ntPrincipalNames
= NULL
;
4738 return ntPrincipalNames
;
4741 static OSStatus
appendToRFC2253String(void *context
,
4742 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4743 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4747 ST stateOrProvinceName
4749 OU organizationalUnitName
4751 STREET streetAddress
4755 /* Prepend a + if this is not the first RDN in an RDN set.
4756 Otherwise prepend a , if this is not the first RDN. */
4758 CFStringAppend(string
, CFSTR("+"));
4759 else if (CFStringGetLength(string
)) {
4760 CFStringAppend(string
, CFSTR(","));
4763 CFStringRef label
, oid
= NULL
;
4764 /* @@@ Consider changing this to a dictionary lookup keyed by the
4765 decimal representation. */
4766 if (DEROidCompare(type
, &oidCommonName
)) {
4767 label
= CFSTR("CN");
4768 } else if (DEROidCompare(type
, &oidLocalityName
)) {
4770 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
4771 label
= CFSTR("ST");
4772 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4774 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4775 label
= CFSTR("OU");
4776 } else if (DEROidCompare(type
, &oidCountryName
)) {
4779 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
4780 label
= CFSTR("STREET");
4781 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
4782 label
= CFSTR("DC");
4783 } else if (DEROidCompare(type
, &oidUserID
)) {
4784 label
= CFSTR("UID");
4787 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
4790 CFStringAppend(string
, label
);
4791 CFStringAppend(string
, CFSTR("="));
4792 CFStringRef raw
= NULL
;
4794 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4797 /* Append raw to string while escaping:
4798 a space or "#" character occurring at the beginning of the string
4799 a space character occurring at the end of the string
4800 one of the characters ",", "+", """, "\", "<", ">" or ";"
4802 CFStringInlineBuffer buffer
;
4803 CFIndex ix
, length
= CFStringGetLength(raw
);
4804 CFRange range
= { 0, length
};
4805 CFStringInitInlineBuffer(raw
, &buffer
, range
);
4806 for (ix
= 0; ix
< length
; ++ix
) {
4807 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
4809 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
4810 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
4811 ch
== '<' || ch
== '>' || ch
== ';' ||
4812 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
4813 (ch
== '#' && ix
== 0)) {
4814 UniChar chars
[] = { '\\', ch
};
4815 CFStringAppendCharacters(string
, chars
, 2);
4817 CFStringAppendCharacters(string
, &ch
, 1);
4822 /* Append the value in hex. */
4823 CFStringAppend(string
, CFSTR("#"));
4825 for (ix
= 0; ix
< value
->length
; ++ix
)
4826 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
4831 return errSecSuccess
;
4834 CFStringRef
SecCertificateCopySubjectString(SecCertificateRef certificate
) {
4835 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4836 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
);
4837 if (status
|| CFStringGetLength(string
) == 0) {
4844 static OSStatus
appendToCompanyNameString(void *context
,
4845 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4846 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4847 if (CFStringGetLength(string
) != 0)
4848 return errSecSuccess
;
4850 if (!DEROidCompare(type
, &oidOrganizationName
))
4851 return errSecSuccess
;
4854 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4856 return errSecSuccess
;
4857 CFStringAppend(string
, raw
);
4860 return errSecSuccess
;
4863 CFStringRef
SecCertificateCopyCompanyName(SecCertificateRef certificate
) {
4864 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4865 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
4866 appendToCompanyNameString
);
4867 if (status
|| CFStringGetLength(string
) == 0) {
4874 static CFDataRef
SecDERItemCopySequence(DERItem
*content
) {
4875 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
4876 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
4877 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
4879 CFDataSetLength(sequence
, sequence_length
);
4880 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
4881 *sequence_ptr
++ = 0x30; /* ASN1_CONSTR_SEQUENCE */
4882 require_noerr_quiet(DEREncodeLength(content
->length
,
4883 sequence_ptr
, &seq_len_length
), out
);
4884 sequence_ptr
+= seq_len_length
;
4885 memcpy(sequence_ptr
, content
->data
, content
->length
);
4888 CFReleaseSafe(sequence
);
4892 CFDataRef
SecCertificateCopyIssuerSequence(
4893 SecCertificateRef certificate
) {
4894 return SecDERItemCopySequence(&certificate
->_issuer
);
4897 CFDataRef
SecCertificateCopySubjectSequence(
4898 SecCertificateRef certificate
) {
4899 return SecDERItemCopySequence(&certificate
->_subject
);
4902 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithm(
4903 SecCertificateRef certificate
) {
4904 return &certificate
->_algId
;
4907 const DERItem
*SecCertificateGetPublicKeyData(SecCertificateRef certificate
) {
4908 return &certificate
->_pubKeyDER
;
4912 /* There is already a SecCertificateCopyPublicKey with different args on OS X,
4913 so we will refer to this one internally as SecCertificateCopyPublicKey_ios.
4915 SecKeyRef
SecCertificateCopyPublicKey_ios(SecCertificateRef certificate
)
4917 SecKeyRef
SecCertificateCopyPublicKey(SecCertificateRef certificate
)
4920 const DERAlgorithmId
*algId
=
4921 SecCertificateGetPublicKeyAlgorithm(certificate
);
4922 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
4923 const DERItem
*params
= NULL
;
4924 if (algId
->params
.length
!= 0) {
4925 params
= &algId
->params
;
4927 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
4928 SecAsn1Item params1
= {
4929 .Data
= params
? params
->data
: NULL
,
4930 .Length
= params
? params
->length
: 0
4932 SecAsn1Item keyData1
= {
4933 .Data
= keyData
? keyData
->data
: NULL
,
4934 .Length
= keyData
? keyData
->length
: 0
4936 return SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
4940 CFDataRef
SecCertificateGetSHA1Digest(SecCertificateRef certificate
) {
4941 if (!certificate
->_sha1Digest
) {
4942 certificate
->_sha1Digest
=
4943 SecSHA1DigestCreate(CFGetAllocator(certificate
),
4944 certificate
->_der
.data
, certificate
->_der
.length
);
4947 return certificate
->_sha1Digest
;
4950 CFDataRef
SecCertificateCopySHA256Digest(SecCertificateRef certificate
) {
4951 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
4952 certificate
->_der
.data
, certificate
->_der
.length
);
4955 CFDataRef
SecCertificateCopyIssuerSHA1Digest(SecCertificateRef certificate
) {
4956 CFDataRef digest
= NULL
;
4957 CFDataRef issuer
= SecCertificateCopyIssuerSequence(certificate
);
4959 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
4960 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
4966 CFDataRef
SecCertificateCopyPublicKeySHA1Digest(SecCertificateRef certificate
) {
4967 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
4968 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
4971 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA256Digest(SecCertificateRef certificate
) {
4972 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
4973 certificate
->_subjectPublicKeyInfo
.data
, certificate
->_subjectPublicKeyInfo
.length
);
4976 CFTypeRef
SecCertificateCopyKeychainItem(SecCertificateRef certificate
)
4981 CFRetainSafe(certificate
->_keychain_item
);
4982 return certificate
->_keychain_item
;
4985 CFDataRef
SecCertificateGetAuthorityKeyID(SecCertificateRef certificate
) {
4986 if (!certificate
->_authorityKeyID
&&
4987 certificate
->_authorityKeyIdentifier
.length
) {
4988 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
4989 certificate
->_authorityKeyIdentifier
.data
,
4990 certificate
->_authorityKeyIdentifier
.length
);
4993 return certificate
->_authorityKeyID
;
4996 CFDataRef
SecCertificateGetSubjectKeyID(SecCertificateRef certificate
) {
4997 if (!certificate
->_subjectKeyID
&&
4998 certificate
->_subjectKeyIdentifier
.length
) {
4999 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
5000 certificate
->_subjectKeyIdentifier
.data
,
5001 certificate
->_subjectKeyIdentifier
.length
);
5004 return certificate
->_subjectKeyID
;
5007 CFArrayRef
SecCertificateGetCRLDistributionPoints(SecCertificateRef certificate
) {
5008 return certificate
->_crlDistributionPoints
;
5011 CFArrayRef
SecCertificateGetOCSPResponders(SecCertificateRef certificate
) {
5012 return certificate
->_ocspResponders
;
5015 CFArrayRef
SecCertificateGetCAIssuers(SecCertificateRef certificate
) {
5016 return certificate
->_caIssuers
;
5019 bool SecCertificateHasCriticalSubjectAltName(SecCertificateRef certificate
) {
5020 return certificate
->_subjectAltName
&&
5021 certificate
->_subjectAltName
->critical
;
5024 bool SecCertificateHasSubject(SecCertificateRef certificate
) {
5025 /* Since the _subject field is the content of the subject and not the
5026 whole thing, we can simply check for a 0 length subject here. */
5027 return certificate
->_subject
.length
!= 0;
5030 bool SecCertificateHasUnknownCriticalExtension(SecCertificateRef certificate
) {
5031 return certificate
->_foundUnknownCriticalExtension
;
5034 /* Private API functions. */
5035 void SecCertificateShow(SecCertificateRef certificate
) {
5037 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
5038 fprintf(stderr
, "\n");
5042 CFDictionaryRef
SecCertificateCopyAttributeDictionary(
5043 SecCertificateRef certificate
) {
5044 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
5045 CFNumberRef certificateType
, certificateEncoding
;
5046 CFStringRef label
, alias
;
5047 CFDataRef skid
, pubKeyDigest
, certData
;
5048 CFDictionaryRef dict
= NULL
;
5052 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
5053 SInt32 ctv
= certificate
->_version
+ 1;
5054 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
5055 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
5056 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
5057 certData
= SecCertificateCopyData(certificate
);
5058 skid
= SecCertificateGetSubjectKeyID(certificate
);
5059 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
5060 certificate
->_pubKeyDER
.length
);
5062 /* We still need to figure out how to deal with multi valued attributes. */
5063 alias
= SecCertificateCopyRFC822Names(certificate
);
5064 label
= SecCertificateCopySubjectSummary(certificate
);
5070 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
5071 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
5072 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
5074 DICT_ADDPAIR(kSecAttrLabel
, label
);
5076 DICT_ADDPAIR(kSecAttrAlias
, alias
);
5077 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
5078 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
5079 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
5081 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
5082 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
5083 DICT_ADDPAIR(kSecValueData
, certData
);
5084 dict
= DICT_CREATE(allocator
);
5086 CFReleaseSafe(label
);
5087 CFReleaseSafe(pubKeyDigest
);
5088 CFReleaseSafe(certData
);
5089 CFReleaseSafe(certificateEncoding
);
5090 CFReleaseSafe(certificateType
);
5095 SecCertificateRef
SecCertificateCreateFromAttributeDictionary(
5096 CFDictionaryRef refAttributes
) {
5097 /* @@@ Support having an allocator in refAttributes. */
5098 CFAllocatorRef allocator
= NULL
;
5099 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
5100 return data
? SecCertificateCreateWithData(allocator
, data
) : NULL
;
5104 OSStatus
SecCertificateIsSelfSigned(SecCertificateRef certificate
, Boolean
*isSelfSigned
) {
5105 if (!certificate
|| (CFGetTypeID(certificate
) != SecCertificateGetTypeID())) {
5106 return errSecInvalidCertificate
;
5108 if (!isSelfSigned
) {
5112 // %%% TBD: IsSelfSigned doesn't require basicConstraints like IsSelfSignedCA,
5113 // which is actually what we want here. Probably need a separate version
5114 // of this function to do the signature comparison, and have the basicConstraints
5115 // check be implemented only in IsSelfSignedCA.
5117 if (certificate
->_isSelfSigned
== 0) {
5118 certificate
->_isSelfSigned
=
5119 (SecCertificateIsIssuedBy(certificate
, certificate
, 0) ?
5122 *isSelfSigned
= (certificate
->_isSelfSigned
== 1);
5124 *isSelfSigned
= SecCertificateIsSelfSignedCA(certificate
);
5126 return errSecSuccess
;
5129 bool SecCertificateIsSelfSignedCA(SecCertificateRef certificate
) {
5130 bool result
= false;
5131 SecKeyRef publicKey
= NULL
;
5133 require(publicKey
= SecCertificateCopyPublicKey_ios(certificate
), out
);
5135 require(publicKey
= SecCertificateCopyPublicKey(certificate
), out
);
5137 CFDataRef normalizedIssuer
=
5138 SecCertificateGetNormalizedIssuerContent(certificate
);
5139 CFDataRef normalizedSubject
=
5140 SecCertificateGetNormalizedSubjectContent(certificate
);
5141 require_quiet(normalizedIssuer
&& normalizedSubject
&&
5142 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
5144 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(certificate
);
5145 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyID(certificate
);
5146 if (authorityKeyID
) {
5147 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
5150 if (SecCertificateVersion(certificate
) >= 3) {
5151 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraints(certificate
);
5152 require_quiet(basicConstraints
&& basicConstraints
->isCA
, out
);
5153 require_noerr_quiet(SecCertificateIsSignedBy(certificate
, publicKey
), out
);
5158 CFReleaseSafe(publicKey
);
5162 SecKeyUsage
SecCertificateGetKeyUsage(SecCertificateRef certificate
) {
5163 return certificate
->_keyUsage
;
5166 CFArrayRef
SecCertificateCopyExtendedKeyUsage(SecCertificateRef certificate
)
5168 CFMutableArrayRef extended_key_usage_oids
=
5169 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
5170 require_quiet(extended_key_usage_oids
, out
);
5172 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5173 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5174 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
5175 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
5178 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
5179 require_noerr_quiet(drtn
, out
);
5180 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
5181 DERDecodedInfo currDecoded
;
5183 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
5184 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
5185 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
5186 currDecoded
.content
.data
, currDecoded
.content
.length
);
5188 CFArrayAppendValue(extended_key_usage_oids
, oid
);
5192 require_quiet(drtn
== DR_EndOfSequence
, out
);
5193 return extended_key_usage_oids
;
5197 CFReleaseSafe(extended_key_usage_oids
);
5201 CFArrayRef
SecCertificateCopySignedCertificateTimestamps(SecCertificateRef certificate
)
5205 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5206 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5207 if (extn
->extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
5208 !memcmp(extn
->extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
->extnID
.length
)) {
5209 /* Got the SCT oid */
5210 DERDecodedInfo sctList
;
5211 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &sctList
);
5212 require_noerr_quiet(drtn
, out
);
5213 require_quiet(sctList
.tag
== ASN1_OCTET_STRING
, out
);
5214 return SecCreateSignedCertificateTimestampsArrayFromSerializedSCTList(sctList
.content
.data
, sctList
.content
.length
);
5222 static bool matches_expected(DERItem der
, CFTypeRef expected
) {
5223 if (der
.length
> 1) {
5224 DERDecodedInfo decoded
;
5225 DERDecodeItem(&der
, &decoded
);
5226 switch (decoded
.tag
) {
5229 return decoded
.content
.length
== 0 && expected
== NULL
;
5233 case ASN1_UTF8_STRING
: {
5234 if (isString(expected
)) {
5235 CFStringRef expectedString
= (CFStringRef
) expected
;
5236 CFStringRef itemString
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFStringEncodingUTF8
, false, kCFAllocatorNull
);
5238 bool result
= (kCFCompareEqualTo
== CFStringCompare(expectedString
, itemString
, 0));
5239 CFReleaseNull(itemString
);
5245 case ASN1_OCTET_STRING
: {
5246 if (isData(expected
)) {
5247 CFDataRef expectedData
= (CFDataRef
) expected
;
5248 CFDataRef itemData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFAllocatorNull
);
5250 bool result
= CFEqual(expectedData
, itemData
);
5251 CFReleaseNull(itemData
);
5257 case ASN1_INTEGER
: {
5258 SInt32 expected_value
= 0;
5259 if (isString(expected
))
5261 CFStringRef aStr
= (CFStringRef
)expected
;
5262 expected_value
= CFStringGetIntValue(aStr
);
5264 else if (isNumber(expected
))
5266 CFNumberGetValue(expected
, kCFNumberSInt32Type
, &expected_value
);
5269 uint32_t num_value
= 0;
5270 if (!DERParseInteger(&decoded
.content
, &num_value
))
5272 return ((uint32_t)expected_value
== num_value
);
5285 static bool cert_contains_marker_extension_value(SecCertificateRef certificate
, CFDataRef oid
, CFTypeRef expectedValue
)
5288 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
5289 size_t oid_len
= CFDataGetLength(oid
);
5291 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5292 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5293 if (extn
->extnID
.length
== oid_len
5294 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
5296 return matches_expected(extn
->extnValue
, expectedValue
);
5302 static bool cert_contains_marker_extension(SecCertificateRef certificate
, CFTypeRef oid
)
5304 return cert_contains_marker_extension_value(certificate
, oid
, NULL
);
5307 struct search_context
{
5309 SecCertificateRef certificate
;
5312 static bool GetDecimalValueOfString(CFStringRef string
, uint32_t* value
)
5314 CFCharacterSetRef nonDecimalDigit
= CFCharacterSetCreateInvertedSet(NULL
, CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
));
5315 bool result
= false;
5317 if ( CFStringGetLength(string
) > 0
5318 && !CFStringFindCharacterFromSet(string
, nonDecimalDigit
, CFRangeMake(0, CFStringGetLength(string
)), kCFCompareForcedOrdering
, NULL
))
5321 *value
= CFStringGetIntValue(string
);
5325 CFReleaseNull(nonDecimalDigit
);
5330 static CFDataRef
CreateOidDataFromString(CFAllocatorRef allocator
, CFStringRef string
)
5332 CFMutableDataRef currentResult
= NULL
;
5333 CFDataRef encodedResult
= NULL
;
5335 CFArrayRef parts
= NULL
;
5341 parts
= CFStringCreateArrayBySeparatingStrings(NULL
, string
, CFSTR("."));
5346 count
= CFArrayGetCount(parts
);
5350 // assume no more than 5 bytes needed to represent any part of the oid,
5351 // since we limit parts to 32-bit values,
5352 // but the first two parts only need 1 byte
5353 currentResult
= CFDataCreateMutable(allocator
, 1+(count
-2)*5);
5359 part
= CFArrayGetValueAtIndex(parts
, 0);
5361 if (!GetDecimalValueOfString(part
, &x
) || x
> 6)
5368 part
= CFArrayGetValueAtIndex(parts
, 1);
5370 if (!GetDecimalValueOfString(part
, &x
) || x
> 39)
5376 CFDataAppendBytes(currentResult
, &firstByte
, 1);
5378 for (CFIndex i
= 2; i
< count
&& GetDecimalValueOfString(CFArrayGetValueAtIndex(parts
, i
), &x
); ++i
) {
5379 uint8_t b
[5] = {0, 0, 0, 0, 0};
5381 b
[3] = 0x80 | ((x
>> 7) & 0x7F);
5382 b
[2] = 0x80 | ((x
>> 14) & 0x7F);
5383 b
[1] = 0x80 | ((x
>> 21) & 0x7F);
5384 b
[0] = 0x80 | ((x
>> 28) & 0x7F);
5386 // Skip the unused extension bytes.
5387 size_t skipBytes
= 0;
5388 while (b
[skipBytes
] == 0x80)
5391 CFDataAppendBytes(currentResult
, b
+ skipBytes
, sizeof(b
) - skipBytes
);
5394 encodedResult
= currentResult
;
5395 currentResult
= NULL
;
5398 CFReleaseNull(parts
);
5399 CFReleaseNull(currentResult
);
5401 return encodedResult
;
5404 static void check_for_marker(const void *key
, const void *value
, void *context
)
5406 struct search_context
* search_ctx
= (struct search_context
*) context
;
5407 CFStringRef key_string
= (CFStringRef
) key
;
5408 CFTypeRef value_ref
= (CFTypeRef
) value
;
5410 // If we could have short circuted the iteration
5411 // we would have, but the best we can do
5412 // is not waste time comparing once a match
5414 if (search_ctx
->found
)
5417 if (CFGetTypeID(key_string
) != CFStringGetTypeID())
5420 CFDataRef key_data
= CreateOidDataFromString(NULL
, key_string
);
5422 if (NULL
== key_data
)
5425 if (cert_contains_marker_extension_value(search_ctx
->certificate
, key_data
, value_ref
))
5426 search_ctx
->found
= true;
5428 CFReleaseNull(key_data
);
5432 // CFType Ref is either:
5434 // CFData - OID to match with no data permitted
5435 // CFDictionary - OID -> Value table for expected values Single Object or Array
5436 // CFArray - Array of the above.
5438 // This returns true if any of the requirements are met.
5439 bool SecCertificateHasMarkerExtension(SecCertificateRef certificate
, CFTypeRef oids
)
5441 if (CFGetTypeID(oids
) == CFArrayGetTypeID()) {
5442 CFIndex ix
, length
= CFArrayGetCount(oids
);
5443 for (ix
= 0; ix
< length
; ix
++)
5444 if (SecCertificateHasMarkerExtension(certificate
, CFArrayGetValueAtIndex((CFArrayRef
)oids
, ix
)))
5446 } else if (CFGetTypeID(oids
) == CFDictionaryGetTypeID()) {
5447 struct search_context context
= { .found
= false, .certificate
= certificate
};
5448 CFDictionaryApplyFunction((CFDictionaryRef
) oids
, &check_for_marker
, &context
);
5449 return context
.found
;
5450 } else if (CFGetTypeID(oids
) == CFDataGetTypeID()) {
5451 return cert_contains_marker_extension(certificate
, oids
);
5456 SecCertificateRef
SecCertificateCreateWithPEM(CFAllocatorRef allocator
,
5457 CFDataRef pem_certificate
)
5459 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
5460 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
5461 uint8_t *base64_data
= NULL
;
5462 SecCertificateRef cert
= NULL
;
5463 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
5464 //const size_t length = CFDataGetLength(pem_certificate);
5465 char *begin
= strstr((const char *)data
, begin_cert
);
5466 char *end
= strstr((const char *)data
, end_cert
);
5469 begin
+= sizeof(begin_cert
) - 1;
5470 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
5471 if (base64_length
) {
5472 require_quiet(base64_data
= calloc(1, base64_length
), out
);
5473 require_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
);
5474 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, base64_data
, base64_length
);
5483 // -- MARK -- XPC encoding/decoding
5486 bool SecCertificateAppendToXPCArray(SecCertificateRef certificate
, xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
5488 return true; // NOOP
5490 size_t length
= SecCertificateGetLength(certificate
);
5491 const uint8_t *bytes
= SecCertificateGetBytePtr(certificate
);
5492 #if SECTRUST_VERBOSE_DEBUG
5493 secerror("cert=0x%lX length=%d bytes=0x%lX", (uintptr_t)certificate
, (int)length
, (uintptr_t)bytes
);
5495 if (!length
|| !bytes
) {
5496 return SecError(errSecParam
, error
, CFSTR("failed to der encode certificate"));
5498 xpc_array_set_data(xpc_certificates
, XPC_ARRAY_APPEND
, bytes
, length
);
5502 SecCertificateRef
SecCertificateCreateWithXPCArrayAtIndex(xpc_object_t xpc_certificates
, size_t index
, CFErrorRef
*error
) {
5503 SecCertificateRef certificate
= NULL
;
5505 const uint8_t *bytes
= xpc_array_get_data(xpc_certificates
, index
, &length
);
5507 certificate
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, bytes
, length
);
5510 SecError(errSecParam
, error
, CFSTR("certificates[%zu] failed to decode"), index
);
5515 xpc_object_t
SecCertificateArrayCopyXPCArray(CFArrayRef certificates
, CFErrorRef
*error
) {
5516 xpc_object_t xpc_certificates
;
5517 require_action_quiet(xpc_certificates
= xpc_array_create(NULL
, 0), exit
,
5518 SecError(errSecAllocate
, error
, CFSTR("failed to create xpc_array")));
5519 CFIndex ix
, count
= CFArrayGetCount(certificates
);
5520 for (ix
= 0; ix
< count
; ++ix
) {
5521 SecCertificateRef certificate
= (SecCertificateRef
) CFArrayGetValueAtIndex(certificates
, ix
);
5522 #if SECTRUST_VERBOSE_DEBUG
5523 CFIndex length
= SecCertificateGetLength(certificate
);
5524 const UInt8
*bytes
= SecCertificateGetBytePtr(certificate
);
5525 secerror("idx=%d of %d; cert=0x%lX length=%ld bytes=0x%lX", (int)ix
, (int)count
, (uintptr_t)certificate
, (size_t)length
, (uintptr_t)bytes
);
5527 if (!SecCertificateAppendToXPCArray(certificate
, xpc_certificates
, error
)) {
5528 xpc_release(xpc_certificates
);
5529 xpc_certificates
= NULL
;
5535 return xpc_certificates
;
5538 CFArrayRef
SecCertificateXPCArrayCopyArray(xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
5539 CFMutableArrayRef certificates
= NULL
;
5540 require_action_quiet(xpc_get_type(xpc_certificates
) == XPC_TYPE_ARRAY
, exit
,
5541 SecError(errSecParam
, error
, CFSTR("certificates xpc value is not an array")));
5542 size_t count
= xpc_array_get_count(xpc_certificates
);
5543 require_action_quiet(certificates
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
), exit
,
5544 SecError(errSecAllocate
, error
, CFSTR("failed to create CFArray of capacity %zu"), count
));
5547 for (ix
= 0; ix
< count
; ++ix
) {
5548 SecCertificateRef cert
= SecCertificateCreateWithXPCArrayAtIndex(xpc_certificates
, ix
, error
);
5550 CFRelease(certificates
);
5553 CFArraySetValueAtIndex(certificates
, ix
, cert
);
5558 return certificates
;
5561 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
5564 static CFArrayRef
CopyEscrowCertificates(SecCertificateEscrowRootType escrowRootType
, CFErrorRef
* error
)
5566 __block CFArrayRef result
= NULL
;
5568 do_if_registered(ota_CopyEscrowCertificates
, escrowRootType
, error
);
5570 securityd_send_sync_and_do(kSecXPCOpOTAGetEscrowCertificates
, error
,
5571 ^bool(xpc_object_t message
, CFErrorRef
*error
)
5573 xpc_dictionary_set_uint64(message
, "escrowType", (uint64_t)escrowRootType
);
5576 ^bool(xpc_object_t response
, CFErrorRef
*error
)
5578 xpc_object_t xpc_array
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
5580 if (response
&& (NULL
!= xpc_array
))
5582 result
= (CFArrayRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_array
);
5586 return SecError(errSecInternal
, error
, CFSTR("Did not get the Escrow certificates"));
5588 return result
!= NULL
;
5593 CFArrayRef
SecCertificateCopyEscrowRoots(SecCertificateEscrowRootType escrowRootType
)
5595 CFArrayRef result
= NULL
;
5597 CFDataRef certData
= NULL
;
5600 // The request is for the base line certificates.
5601 // Use the hard coded data to generate the return array
5602 if (kSecCertificateBaselineEscrowRoot
== escrowRootType
||
5603 kSecCertificateBaselinePCSEscrowRoot
== escrowRootType
)
5605 // Get the hard coded set of roots
5606 numRoots
= (kSecCertificateBaselineEscrowRoot
== escrowRootType
) ?
5607 kNumberOfBaseLineEscrowRoots
:
5608 kNumberOfBaseLinePCSEscrowRoots
;
5609 SecCertificateRef baseLineCerts
[numRoots
];
5610 struct RootRecord
** pEscrowRoots
= kBaseLineEscrowRoots
;
5611 struct RootRecord
* pRootRecord
= NULL
;
5613 if (kSecCertificateBaselinePCSEscrowRoot
== escrowRootType
) {
5614 pEscrowRoots
= kBaseLinePCSEscrowRoots
;
5617 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++)
5619 pRootRecord
= pEscrowRoots
[iCnt
];
5620 if (NULL
!= pRootRecord
&& pRootRecord
->_length
> 0 && NULL
!= pRootRecord
->_bytes
)
5622 certData
= CFDataCreate(kCFAllocatorDefault
, pRootRecord
->_bytes
, pRootRecord
->_length
);
5623 if (NULL
!= certData
)
5625 baseLineCerts
[iCnt
] = SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
5626 CFRelease(certData
);
5630 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)baseLineCerts
, numRoots
, &kCFTypeArrayCallBacks
);
5631 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++)
5633 if (NULL
!= baseLineCerts
[iCnt
])
5635 CFRelease(baseLineCerts
[iCnt
]);
5639 // The request is for the current certificates.
5642 CFErrorRef error
= NULL
;
5643 CFArrayRef cert_datas
= CopyEscrowCertificates(escrowRootType
, &error
);
5644 if (NULL
!= error
|| NULL
== cert_datas
)
5651 if (NULL
!= cert_datas
)
5653 CFRelease(cert_datas
);
5658 numRoots
= (int)(CFArrayGetCount(cert_datas
));
5660 SecCertificateRef assetCerts
[numRoots
];
5661 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++)
5663 certData
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, iCnt
);
5664 if (NULL
!= certData
)
5666 SecCertificateRef aCertRef
= SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
5667 assetCerts
[iCnt
] = aCertRef
;
5671 assetCerts
[iCnt
] = NULL
;
5677 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)assetCerts
, numRoots
, &kCFTypeArrayCallBacks
);
5678 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++)
5680 if (NULL
!= assetCerts
[iCnt
])
5682 CFRelease(assetCerts
[iCnt
]);
5686 CFReleaseSafe(cert_datas
);
5691 SecSignatureHashAlgorithm
SecCertificateGetSignatureHashAlgorithm(SecCertificateRef certificate
)
5693 SecSignatureHashAlgorithm result
= kSecSignatureHashAlgorithmUnknown
;
5694 DERAlgorithmId
*algId
= (certificate
) ? &certificate
->_tbsSigAlg
: NULL
;
5695 const DERItem
*algOid
= (algId
) ? &algId
->oid
: NULL
;
5697 if (!algOid
->data
|| !algOid
->length
) {
5700 /* classify the signature algorithm OID into one of our known types */
5701 if (DEROidCompare(algOid
, &oidSha512Ecdsa
) ||
5702 DEROidCompare(algOid
, &oidSha512Rsa
) ||
5703 DEROidCompare(algOid
, &oidSha512
)) {
5704 result
= kSecSignatureHashAlgorithmSHA512
;
5707 if (DEROidCompare(algOid
, &oidSha384Ecdsa
) ||
5708 DEROidCompare(algOid
, &oidSha384Rsa
) ||
5709 DEROidCompare(algOid
, &oidSha384
)) {
5710 result
= kSecSignatureHashAlgorithmSHA384
;
5713 if (DEROidCompare(algOid
, &oidSha256Ecdsa
) ||
5714 DEROidCompare(algOid
, &oidSha256Rsa
) ||
5715 DEROidCompare(algOid
, &oidSha256
)) {
5716 result
= kSecSignatureHashAlgorithmSHA256
;
5719 if (DEROidCompare(algOid
, &oidSha224Ecdsa
) ||
5720 DEROidCompare(algOid
, &oidSha224Rsa
) ||
5721 DEROidCompare(algOid
, &oidSha224
)) {
5722 result
= kSecSignatureHashAlgorithmSHA224
;
5725 if (DEROidCompare(algOid
, &oidSha1Ecdsa
) ||
5726 DEROidCompare(algOid
, &oidSha1Rsa
) ||
5727 DEROidCompare(algOid
, &oidSha1Dsa
) ||
5728 DEROidCompare(algOid
, &oidSha1DsaOIW
) ||
5729 DEROidCompare(algOid
, &oidSha1DsaCommonOIW
) ||
5730 DEROidCompare(algOid
, &oidSha1RsaOIW
) ||
5731 DEROidCompare(algOid
, &oidSha1Fee
) ||
5732 DEROidCompare(algOid
, &oidSha1
)) {
5733 result
= kSecSignatureHashAlgorithmSHA1
;
5736 if (DEROidCompare(algOid
, &oidMd5Rsa
) ||
5737 DEROidCompare(algOid
, &oidMd5Fee
) ||
5738 DEROidCompare(algOid
, &oidMd5
)) {
5739 result
= kSecSignatureHashAlgorithmMD5
;
5742 if (DEROidCompare(algOid
, &oidMd4Rsa
) ||
5743 DEROidCompare(algOid
, &oidMd4
)) {
5744 result
= kSecSignatureHashAlgorithmMD4
;
5747 if (DEROidCompare(algOid
, &oidMd2Rsa
) ||
5748 DEROidCompare(algOid
, &oidMd2
)) {
5749 result
= kSecSignatureHashAlgorithmMD2
;