2 * Copyright (c) 2006-2019 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/oids.h>
52 #include <Security/SecBasePriv.h>
53 #include "SecRSAKey.h"
54 #include "SecFramework.h"
55 #include <Security/SecItem.h>
56 #include <Security/SecItemPriv.h>
57 #include "SecSignatureVerificationSupport.h"
59 #include <utilities/debugging.h>
60 #include <utilities/SecCFWrappers.h>
61 #include <utilities/SecCFError.h>
62 #include <utilities/SecSCTUtils.h>
63 #include <utilities/array_size.h>
65 #include <libkern/OSByteOrder.h>
67 #include <Security/SecInternal.h>
68 #include <Security/SecFrameworkStrings.h>
69 #include "SecBase64.h"
70 #include "AppleBaselineEscrowCertificates.h"
71 #include "AppleiPhoneDeviceCACertificates.h"
72 #include <ipc/securityd_client.h>
73 #include <Security/SecKeyInternal.h>
75 #pragma clang diagnostic ignored "-Wformat=2"
77 /* The minimum key sizes necessary to not be considered "weak" */
78 #define MIN_RSA_KEY_SIZE 128 // 1024-bit
79 #define MIN_EC_KEY_SIZE 20 // 160-bit
81 /* The minimum key sizes necessary to be considered "strong" */
82 #define MIN_STRONG_RSA_KEY_SIZE 256 // 2048-bit
83 #define MIN_STRONG_EC_KEY_SIZE 28 // 224-bit
85 #define IPv4ADDRLEN 4 // 4 octets
86 #define IPv6ADDRLEN 16 // 16 octets
88 typedef struct SecCertificateExtension
{
92 } SecCertificateExtension
;
95 kSecSelfSignedUnknown
= 0,
100 struct __SecCertificate
{
103 DERItem _der
; /* Entire certificate in DER form. */
104 DERItem _tbs
; /* To Be Signed cert DER bytes. */
105 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
106 DERItem _signature
; /* The content of the sig bit string. */
109 DERItem _serialNum
; /* Integer. */
110 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
111 DERItem _issuer
; /* Sequence of RDN. */
112 CFAbsoluteTime _notBefore
;
113 CFAbsoluteTime _notAfter
;
114 DERItem _subject
; /* Sequence of RDN. */
115 DERItem _subjectPublicKeyInfo
; /* SPKI (without tag/length) */
116 DERAlgorithmId _algId
; /* oid and params of _pubKeyDER. */
117 DERItem _pubKeyDER
; /* contents of bit string */
118 DERItem _issuerUniqueID
; /* bit string, optional */
119 DERItem _subjectUniqueID
; /* bit string, optional */
121 bool _foundUnknownCriticalExtension
;
123 /* Well known certificate extensions. */
124 SecCEBasicConstraints _basicConstraints
;
125 SecCEPolicyConstraints _policyConstraints
;
126 SecCEPolicyMappings _policyMappings
;
127 SecCECertificatePolicies _certificatePolicies
;
128 SecCEInhibitAnyPolicy _inhibitAnyPolicySkipCerts
;
130 /* If KeyUsage extension is not present this is 0, otherwise it's
131 the value of the extension. */
132 SecKeyUsage _keyUsage
;
134 /* OCTETS of SubjectKeyIdentifier extensions KeyIdentifier.
135 Length = 0 if not present. */
136 DERItem _subjectKeyIdentifier
;
138 /* OCTETS of AuthorityKeyIdentifier extensions KeyIdentifier.
139 Length = 0 if not present. */
140 DERItem _authorityKeyIdentifier
;
141 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
142 _authorityKeyIdentifierSerialNumber have non zero length if present.
143 Both are either present or absent together. */
144 DERItem _authorityKeyIdentifierIssuer
;
145 DERItem _authorityKeyIdentifierSerialNumber
;
147 /* Subject alt name extension, if present. Not malloced, it's just a
148 pointer to an element in the _extensions array. */
149 const SecCertificateExtension
*_subjectAltName
;
151 /* Parsed extension values. */
153 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
154 CFMutableArrayRef _crlDistributionPoints
;
156 /* Array of CFURLRefs containing the URI values of accessLocations of each
157 id-ad-ocsp AccessDescription in the Authority Information Access
159 CFMutableArrayRef _ocspResponders
;
161 /* Array of CFURLRefs containing the URI values of accessLocations of each
162 id-ad-caIssuers AccessDescription in the Authority Information Access
164 CFMutableArrayRef _caIssuers
;
166 /* Array of CFDataRefs containing the generalNames for permittedSubtrees
168 CFArrayRef _permittedSubtrees
;
170 /* Array of CFDataRefs containing the generalNames for excludedSubtrees
172 CFArrayRef _excludedSubtrees
;
174 CFMutableArrayRef _embeddedSCTs
;
176 /* All other (non known) extensions. The _extensions array is malloced. */
177 CFIndex _extensionCount
;
178 SecCertificateExtension
*_extensions
;
179 CFIndex _unparseableKnownExtensionIndex
;
181 /* Optional cached fields. */
184 CFArrayRef _properties
;
185 CFDataRef _serialNumber
;
186 CFDataRef _normalizedIssuer
;
187 CFDataRef _normalizedSubject
;
188 CFDataRef _authorityKeyID
;
189 CFDataRef _subjectKeyID
;
191 CFDataRef _sha1Digest
;
192 CFTypeRef _keychain_item
;
193 uint8_t _isSelfSigned
;
197 #define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v);
199 SEC_CONST_DECL (kSecCertificateProductionEscrowKey
, "ProductionEscrowKey");
200 SEC_CONST_DECL (kSecCertificateProductionPCSEscrowKey
, "ProductionPCSEscrowKey");
201 SEC_CONST_DECL (kSecCertificateEscrowFileName
, "AppleESCertificates");
203 /* Public Constants for property list keys. */
204 SEC_CONST_DECL (kSecPropertyKeyType
, "type");
205 SEC_CONST_DECL (kSecPropertyKeyLabel
, "label");
206 SEC_CONST_DECL (kSecPropertyKeyLocalizedLabel
, "localized label");
207 SEC_CONST_DECL (kSecPropertyKeyValue
, "value");
209 /* Public Constants for property list values. */
210 SEC_CONST_DECL (kSecPropertyTypeWarning
, "warning");
211 SEC_CONST_DECL (kSecPropertyTypeError
, "error");
212 SEC_CONST_DECL (kSecPropertyTypeSuccess
, "success");
213 SEC_CONST_DECL (kSecPropertyTypeTitle
, "title");
214 SEC_CONST_DECL (kSecPropertyTypeSection
, "section");
215 SEC_CONST_DECL (kSecPropertyTypeData
, "data");
216 SEC_CONST_DECL (kSecPropertyTypeString
, "string");
217 SEC_CONST_DECL (kSecPropertyTypeURL
, "url");
218 SEC_CONST_DECL (kSecPropertyTypeDate
, "date");
219 SEC_CONST_DECL (kSecPropertyTypeArray
, "array");
220 SEC_CONST_DECL (kSecPropertyTypeNumber
, "number");
222 /* Extension parsing routine. */
223 typedef bool (*SecCertificateExtensionParser
)(SecCertificateRef certificate
,
224 const SecCertificateExtension
*extn
);
226 /* Mapping from extension OIDs (as a DERItem *) to
227 SecCertificateExtensionParser extension parsing routines. */
228 static CFDictionaryRef sExtensionParsers
;
230 /* Forward declarations of static functions. */
231 static CFStringRef
SecCertificateCopyDescription(CFTypeRef cf
);
232 static void SecCertificateDestroy(CFTypeRef cf
);
233 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
234 CFAbsoluteTime
*absTime
) __attribute__((__nonnull__
));
236 /* Static functions. */
237 static CFStringRef
SecCertificateCopyDescription(CFTypeRef cf
) {
238 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
239 CFStringRef subject
= SecCertificateCopySubjectSummary(certificate
);
240 CFStringRef issuer
= SecCertificateCopyIssuerSummary(certificate
);
241 CFStringRef desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
242 CFSTR("<cert(%p) s: %@ i: %@>"), certificate
, subject
, issuer
);
243 CFReleaseSafe(issuer
);
244 CFReleaseSafe(subject
);
248 static void SecCertificateDestroy(CFTypeRef cf
) {
249 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
250 if (certificate
->_certificatePolicies
.policies
) {
251 free(certificate
->_certificatePolicies
.policies
);
252 certificate
->_certificatePolicies
.policies
= NULL
;
254 if (certificate
->_policyMappings
.mappings
) {
255 free(certificate
->_policyMappings
.mappings
);
256 certificate
->_policyMappings
.mappings
= NULL
;
258 CFReleaseNull(certificate
->_crlDistributionPoints
);
259 CFReleaseNull(certificate
->_ocspResponders
);
260 CFReleaseNull(certificate
->_caIssuers
);
261 if (certificate
->_extensions
) {
262 free(certificate
->_extensions
);
263 certificate
->_extensions
= NULL
;
265 CFReleaseNull(certificate
->_pubKey
);
266 CFReleaseNull(certificate
->_der_data
);
267 CFReleaseNull(certificate
->_properties
);
268 CFReleaseNull(certificate
->_serialNumber
);
269 CFReleaseNull(certificate
->_normalizedIssuer
);
270 CFReleaseNull(certificate
->_normalizedSubject
);
271 CFReleaseNull(certificate
->_authorityKeyID
);
272 CFReleaseNull(certificate
->_subjectKeyID
);
273 CFReleaseNull(certificate
->_sha1Digest
);
274 CFReleaseNull(certificate
->_keychain_item
);
275 CFReleaseNull(certificate
->_permittedSubtrees
);
276 CFReleaseNull(certificate
->_excludedSubtrees
);
279 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
280 SecCertificateRef cert1
= (SecCertificateRef
)cf1
;
281 SecCertificateRef cert2
= (SecCertificateRef
)cf2
;
284 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
286 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
289 /* Hash of the certificate is der length + signature length + last 4 bytes
291 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
292 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
293 size_t der_length
= certificate
->_der
.length
;
294 size_t sig_length
= certificate
->_signature
.length
;
295 size_t ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
296 CFHashCode hashCode
= 0;
297 for (; ix
< sig_length
; ++ix
)
298 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
300 return (hashCode
+ der_length
+ sig_length
);
305 /************************************************************************/
306 /************************* General Name Parsing *************************/
307 /************************************************************************/
309 GeneralName ::= CHOICE {
310 otherName [0] OtherName,
311 rfc822Name [1] IA5String,
312 dNSName [2] IA5String,
313 x400Address [3] ORAddress,
314 directoryName [4] Name,
315 ediPartyName [5] EDIPartyName,
316 uniformResourceIdentifier [6] IA5String,
317 iPAddress [7] OCTET STRING,
318 registeredID [8] OBJECT IDENTIFIER}
320 OtherName ::= SEQUENCE {
321 type-id OBJECT IDENTIFIER,
322 value [0] EXPLICIT ANY DEFINED BY type-id }
324 EDIPartyName ::= SEQUENCE {
325 nameAssigner [0] DirectoryString OPTIONAL,
326 partyName [1] DirectoryString }
328 OSStatus
SecCertificateParseGeneralNameContentProperty(DERTag tag
,
329 const DERItem
*generalNameContent
,
330 void *context
, parseGeneralNameCallback callback
) {
332 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
333 return callback(context
, GNT_OtherName
, generalNameContent
);
334 case ASN1_CONTEXT_SPECIFIC
| 1:
335 return callback(context
, GNT_RFC822Name
, generalNameContent
);
336 case ASN1_CONTEXT_SPECIFIC
| 2:
337 return callback(context
, GNT_DNSName
, generalNameContent
);
338 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
339 return callback(context
, GNT_X400Address
, generalNameContent
);
340 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
341 return callback(context
, GNT_DirectoryName
, generalNameContent
);
342 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
343 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
344 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
346 /* Technically I don't think this is valid, but there are certs out
347 in the wild that use a constructed IA5String. In particular the
348 VeriSign Time Stamping Authority CA.cer does this. */
349 DERDecodedInfo uriContent
;
350 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
351 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
352 return callback(context
, GNT_URI
, &uriContent
.content
);
354 case ASN1_CONTEXT_SPECIFIC
| 6:
355 return callback(context
, GNT_URI
, generalNameContent
);
356 case ASN1_CONTEXT_SPECIFIC
| 7:
357 return callback(context
, GNT_IPAddress
, generalNameContent
);
358 case ASN1_CONTEXT_SPECIFIC
| 8:
359 return callback(context
, GNT_RegisteredID
, generalNameContent
);
364 return errSecInvalidCertificate
;
367 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
368 void *context
, parseGeneralNameCallback callback
) {
370 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
371 require_noerr_quiet(drtn
, badDER
);
372 DERDecodedInfo generalNameContent
;
373 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
375 OSStatus status
= SecCertificateParseGeneralNameContentProperty(
376 generalNameContent
.tag
, &generalNameContent
.content
, context
,
381 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
382 return errSecSuccess
;
385 return errSecInvalidCertificate
;
388 OSStatus
SecCertificateParseGeneralNames(const DERItem
*generalNames
, void *context
,
389 parseGeneralNameCallback callback
) {
390 DERDecodedInfo generalNamesContent
;
391 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
392 require_noerr_quiet(drtn
, badDER
);
393 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
394 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
397 return errSecInvalidCertificate
;
403 GeneralName ::= CHOICE {
404 otherName [0] OtherName,
405 rfc822Name [1] IA5String,
406 dNSName [2] IA5String,
407 x400Address [3] ORAddress,
408 directoryName [4] Name,
409 ediPartyName [5] EDIPartyName,
410 uniformResourceIdentifier [6] IA5String,
411 iPAddress [7] OCTET STRING,
412 registeredID [8] OBJECT IDENTIFIER}
414 EDIPartyName ::= SEQUENCE {
415 nameAssigner [0] DirectoryString OPTIONAL,
416 partyName [1] DirectoryString }
418 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
419 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
421 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
422 generalName
->nameType
= GNT_OtherName
;
423 generalName
->berEncoded
= true;
424 generalName
->name
= *generalNameContent
;
426 case ASN1_CONTEXT_SPECIFIC
| 1:
428 generalName
->nameType
= GNT_RFC822Name
;
429 generalName
->berEncoded
= false;
430 generalName
->name
= *generalNameContent
;
432 case ASN1_CONTEXT_SPECIFIC
| 2:
434 generalName
->nameType
= GNT_DNSName
;
435 generalName
->berEncoded
= false;
436 generalName
->name
= *generalNameContent
;
438 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
439 generalName
->nameType
= GNT_X400Address
;
440 generalName
->berEncoded
= true;
441 generalName
->name
= *generalNameContent
;
443 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
444 generalName
->nameType
= GNT_DirectoryName
;
445 generalName
->berEncoded
= true;
446 generalName
->name
= *generalNameContent
;
448 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
449 generalName
->nameType
= GNT_EdiPartyName
;
450 generalName
->berEncoded
= true;
451 generalName
->name
= *generalNameContent
;
453 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
455 /* Technically I don't think this is valid, but there are certs out
456 in the wild that use a constructed IA5String. In particular the
457 VeriSign Time Stamping Authority CA.cer does this. */
458 DERDecodedInfo decoded
;
459 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
460 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
461 generalName
->nameType
= GNT_URI
;
462 generalName
->berEncoded
= false;
463 generalName
->name
= decoded
.content
;
466 case ASN1_CONTEXT_SPECIFIC
| 6:
467 generalName
->nameType
= GNT_URI
;
468 generalName
->berEncoded
= false;
469 generalName
->name
= *generalNameContent
;
471 case ASN1_CONTEXT_SPECIFIC
| 7:
472 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
473 8 octects, addr/mask for ipv6 it's 32. */
474 generalName
->nameType
= GNT_IPAddress
;
475 generalName
->berEncoded
= false;
476 generalName
->name
= *generalNameContent
;
478 case ASN1_CONTEXT_SPECIFIC
| 8:
479 /* name is the content of an OID. */
480 generalName
->nameType
= GNT_RegisteredID
;
481 generalName
->berEncoded
= false;
482 generalName
->name
= *generalNameContent
;
488 return errSecSuccess
;
490 return errSecInvalidCertificate
;
494 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
496 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
497 CFIndex
*count
, SecCEGeneralName
**name
) {
498 SecCEGeneralName
*generalNames
= NULL
;
500 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
501 require_noerr_quiet(drtn
, badDER
);
502 DERDecodedInfo generalNameContent
;
503 CFIndex generalNamesCount
= 0;
504 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
508 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
510 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
512 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
514 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
516 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
517 &generalNameContent
.content
, &generalNames
[ix
])) {
522 *count
= generalNamesCount
;
523 *name
= generalNames
;
524 return errSecSuccess
;
529 return errSecInvalidCertificate
;
532 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
533 CFIndex
*count
, SecCEGeneralName
**name
) {
534 DERDecodedInfo generalNamesContent
;
535 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
536 require_noerr_quiet(drtn
, badDER
);
537 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
539 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
540 return errSecSuccess
;
542 return errSecInvalidCertificate
;
546 /************************************************************************/
547 /************************** X.509 Name Parsing **************************/
548 /************************************************************************/
550 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
551 const DERItem
*value
, CFIndex rdnIX
, bool localized
);
553 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
554 parseX501NameCallback callback
, bool localized
) {
556 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
557 require_noerr_quiet(drtn
, badDER
);
558 DERDecodedInfo atvContent
;
560 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
561 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
562 DERAttributeTypeAndValue atv
;
563 drtn
= DERParseSequenceContent(&atvContent
.content
,
564 DERNumAttributeTypeAndValueItemSpecs
,
565 DERAttributeTypeAndValueItemSpecs
,
567 require_noerr_quiet(drtn
, badDER
);
568 require_quiet(atv
.type
.length
!= 0, badDER
);
569 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++, localized
);
574 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
576 return errSecSuccess
;
578 return errSecInvalidCertificate
;
581 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
582 parseX501NameCallback callback
, bool localized
) {
584 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
585 require_noerr_quiet(drtn
, badDER
);
586 DERDecodedInfo currDecoded
;
587 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
588 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
589 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
590 callback
, localized
);
595 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
597 return errSecSuccess
;
600 return errSecInvalidCertificate
;
603 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
604 parseX501NameCallback callback
, bool localized
) {
605 DERDecodedInfo x501NameContent
;
606 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
607 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
608 return errSecInvalidCertificate
;
610 return parseX501NameContent(&x501NameContent
.content
, context
,
611 callback
, localized
);
615 /************************************************************************/
616 /********************** Extension Parsing Routines **********************/
617 /************************************************************************/
619 static bool SecCEPSubjectKeyIdentifier(SecCertificateRef certificate
,
620 const SecCertificateExtension
*extn
) {
621 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
622 DERDecodedInfo keyIdentifier
;
623 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
624 require_noerr_quiet(drtn
, badDER
);
625 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
626 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
630 secwarning("Invalid SubjectKeyIdentifier Extension");
634 static bool SecCEPKeyUsage(SecCertificateRef certificate
,
635 const SecCertificateExtension
*extn
) {
636 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
637 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
638 DERDecodedInfo bitStringContent
;
639 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
640 require_noerr_quiet(drtn
, badDER
);
641 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
642 /* check that there's no extra bytes at the end */
643 require_quiet(bitStringContent
.content
.data
+ bitStringContent
.content
.length
== extn
->extnValue
.data
+ extn
->extnValue
.length
, 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 is the lsb. */
649 uint16_t bits
= 8 * len
- numUnusedBits
;
650 uint16_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
;
669 secwarning("Invalid KeyUsage Extension");
673 static bool SecCEPPrivateKeyUsagePeriod(SecCertificateRef certificate
,
674 const SecCertificateExtension
*extn
) {
675 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
679 static bool SecCEPSubjectAltName(SecCertificateRef certificate
,
680 const SecCertificateExtension
*extn
) {
681 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
682 certificate
->_subjectAltName
= extn
;
686 static bool SecCEPIssuerAltName(SecCertificateRef certificate
,
687 const SecCertificateExtension
*extn
) {
688 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
692 static bool SecCEPBasicConstraints(SecCertificateRef certificate
,
693 const SecCertificateExtension
*extn
) {
694 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
695 DERBasicConstraints basicConstraints
;
696 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
697 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
698 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
699 require_noerr_quiet(DERParseBooleanWithDefault(&basicConstraints
.cA
, false,
700 &certificate
->_basicConstraints
.isCA
), badDER
);
701 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
702 require_noerr_quiet(DERParseInteger(
703 &basicConstraints
.pathLenConstraint
,
704 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
705 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
707 certificate
->_basicConstraints
.present
= true;
708 certificate
->_basicConstraints
.critical
= extn
->critical
;
711 certificate
->_basicConstraints
.present
= false;
712 secwarning("Invalid BasicConstraints Extension");
718 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
720 * NameConstraints ::= SEQUENCE {
721 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
722 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
724 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
726 * GeneralSubtree ::= SEQUENCE {
728 * minimum [0] BaseDistance DEFAULT 0,
729 * maximum [1] BaseDistance OPTIONAL }
731 * BaseDistance ::= INTEGER (0..MAX)
733 static DERReturn
parseGeneralSubtrees(DERItem
*derSubtrees
, CFArrayRef
*generalSubtrees
) {
734 CFMutableArrayRef gs
= NULL
;
736 DERReturn drtn
= DERDecodeSeqContentInit(derSubtrees
, &gsSeq
);
737 require_noerr_quiet(drtn
, badDER
);
738 DERDecodedInfo gsContent
;
739 require_quiet(gs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
740 &kCFTypeArrayCallBacks
),
742 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
743 DERGeneralSubtree derGS
;
744 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
745 drtn
= DERParseSequenceContent(&gsContent
.content
,
746 DERNumGeneralSubtreeItemSpecs
,
747 DERGeneralSubtreeItemSpecs
,
748 &derGS
, sizeof(derGS
));
749 require_noerr_quiet(drtn
, badDER
);
752 * Within this profile, the minimum and maximum fields are not used with
753 * any name forms, thus, the minimum MUST be zero, and maximum MUST be
756 * Because minimum DEFAULT 0, absence equivalent to present and 0.
758 if (derGS
.minimum
.length
) {
760 require_noerr_quiet(DERParseInteger(&derGS
.minimum
, &minimum
),
762 require_quiet(minimum
== 0, badDER
);
764 require_quiet(derGS
.maximum
.length
== 0, badDER
);
765 require_quiet(derGS
.generalName
.length
!= 0, badDER
);
767 CFDataRef generalName
= NULL
;
768 require_quiet(generalName
= CFDataCreate(kCFAllocatorDefault
,
769 derGS
.generalName
.data
,
770 derGS
.generalName
.length
),
772 CFArrayAppendValue(gs
, generalName
);
773 CFReleaseNull(generalName
);
775 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
777 // since generalSubtrees is a pointer to an instance variable,
778 // make sure we release the existing array before assignment.
779 CFReleaseSafe(*generalSubtrees
);
780 *generalSubtrees
= gs
;
786 secdebug("cert","failed to parse GeneralSubtrees");
790 static bool SecCEPNameConstraints(SecCertificateRef certificate
,
791 const SecCertificateExtension
*extn
) {
792 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
793 DERNameConstraints nc
;
795 drtn
= DERParseSequence(&extn
->extnValue
,
796 DERNumNameConstraintsItemSpecs
,
797 DERNameConstraintsItemSpecs
,
799 require_noerr_quiet(drtn
, badDER
);
800 if (nc
.permittedSubtrees
.length
) {
801 require_noerr_quiet(parseGeneralSubtrees(&nc
.permittedSubtrees
, &certificate
->_permittedSubtrees
), badDER
);
803 if (nc
.excludedSubtrees
.length
) {
804 require_noerr_quiet(parseGeneralSubtrees(&nc
.excludedSubtrees
, &certificate
->_excludedSubtrees
), badDER
);
809 secwarning("Invalid Name Constraints extension");
813 static OSStatus
appendCRLDPFromGeneralNames(void *context
, SecCEGeneralNameType type
,
814 const DERItem
*value
) {
815 CFMutableArrayRef
*crlDPs
= (CFMutableArrayRef
*)context
;
816 if (type
== GNT_URI
) {
818 url
= CFURLCreateWithBytes(NULL
, value
->data
, value
->length
, kCFStringEncodingASCII
, NULL
);
821 *crlDPs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
823 CFArrayAppendValue(*crlDPs
, url
);
827 return errSecSuccess
;
831 id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= { id-ce 31 }
833 CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
835 DistributionPoint ::= SEQUENCE {
836 distributionPoint [0] DistributionPointName OPTIONAL,
837 reasons [1] ReasonFlags OPTIONAL,
838 cRLIssuer [2] GeneralNames OPTIONAL }
840 DistributionPointName ::= CHOICE {
841 fullName [0] GeneralNames,
842 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
844 static bool SecCEPCrlDistributionPoints(SecCertificateRef certificate
,
845 const SecCertificateExtension
*extn
) {
846 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
847 DERSequence crlDPSeq
;
849 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &crlDPSeq
);
850 require_noerr_quiet(drtn
, badDER
);
851 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
852 DERDecodedInfo dpContent
;
853 while ((drtn
= DERDecodeSeqNext(&crlDPSeq
, &dpContent
)) == DR_Success
) {
854 require_quiet(dpContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
855 DERDistributionPoint dp
;
856 drtn
= DERParseSequenceContent(&dpContent
.content
, DERNumDistributionPointItemSpecs
,
857 DERDistributionPointItemSpecs
, &dp
, sizeof(dp
));
858 require_noerr_quiet(drtn
, badDER
);
859 require_quiet(dp
.distributionPoint
.data
|| dp
.cRLIssuer
.data
, badDER
);
860 if (dp
.distributionPoint
.data
) {
861 DERDecodedInfo dpName
;
862 drtn
= DERDecodeItem(&dp
.distributionPoint
, &dpName
);
863 require_noerr_quiet(drtn
, badDER
);
864 switch (dpName
.tag
) {
865 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
866 drtn
= parseGeneralNamesContent(&dpName
.content
, &certificate
->_crlDistributionPoints
,
867 appendCRLDPFromGeneralNames
);
868 require_noerr_quiet(drtn
, badDER
);
870 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1:
871 /* RelativeDistinguishName. Nothing we can do with that. */
877 if (dp
.cRLIssuer
.data
) {
878 drtn
= parseGeneralNamesContent(&dp
.cRLIssuer
, &certificate
->_crlDistributionPoints
,
879 appendCRLDPFromGeneralNames
);
880 require_noerr_quiet(drtn
, badDER
);
883 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
886 secwarning("Invalid CRL Distribution Points extension");
891 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
893 PolicyInformation ::= SEQUENCE {
894 policyIdentifier CertPolicyId,
895 policyQualifiers SEQUENCE SIZE (1..MAX) OF
896 PolicyQualifierInfo OPTIONAL }
898 CertPolicyId ::= OBJECT IDENTIFIER
900 PolicyQualifierInfo ::= SEQUENCE {
901 policyQualifierId PolicyQualifierId,
902 qualifier ANY DEFINED BY policyQualifierId }
904 /* maximum number of policies of 8192 seems more than adequate */
905 #define MAX_CERTIFICATE_POLICIES 8192
906 static bool SecCEPCertificatePolicies(SecCertificateRef certificate
,
907 const SecCertificateExtension
*extn
) {
908 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
911 SecCEPolicyInformation
*policies
= NULL
;
912 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
913 require_noerr_quiet(drtn
, badDER
);
914 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
915 DERDecodedInfo piContent
;
916 DERSize policy_count
= 0;
917 while ((policy_count
< MAX_CERTIFICATE_POLICIES
) &&
918 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
919 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
922 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
923 require_quiet(policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
)
924 * (policy_count
> 0 ? policy_count
: 1)),
926 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
927 require_noerr_quiet(drtn
, badDER
);
928 DERSize policy_ix
= 0;
929 while ((policy_ix
< (policy_count
> 0 ? policy_count
: 1)) &&
930 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
931 DERPolicyInformation pi
;
932 drtn
= DERParseSequenceContent(&piContent
.content
,
933 DERNumPolicyInformationItemSpecs
,
934 DERPolicyInformationItemSpecs
,
936 require_noerr_quiet(drtn
, badDER
);
937 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
938 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
940 certificate
->_certificatePolicies
.present
= true;
941 certificate
->_certificatePolicies
.critical
= extn
->critical
;
942 certificate
->_certificatePolicies
.numPolicies
= policy_count
;
943 certificate
->_certificatePolicies
.policies
= policies
;
948 certificate
->_certificatePolicies
.present
= false;
949 secwarning("Invalid CertificatePolicies Extension");
954 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
956 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
957 issuerDomainPolicy CertPolicyId,
958 subjectDomainPolicy CertPolicyId }
960 #define MAX_POLICY_MAPPINGS 8192
961 static bool SecCEPPolicyMappings(SecCertificateRef certificate
,
962 const SecCertificateExtension
*extn
) {
963 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
966 SecCEPolicyMapping
*mappings
= NULL
;
967 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
968 require_noerr_quiet(drtn
, badDER
);
969 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
970 DERDecodedInfo pmContent
;
971 DERSize mapping_count
= 0;
972 while ((mapping_count
< MAX_POLICY_MAPPINGS
) &&
973 (drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
974 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
977 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
978 require_quiet(mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
979 * (mapping_count
> 0 ? mapping_count
: 1)),
981 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
982 require_noerr_quiet(drtn
, badDER
);
983 DERSize mapping_ix
= 0;
984 while ((mapping_ix
< (mapping_count
> 0 ? mapping_count
: 1)) &&
985 (drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
986 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
988 drtn
= DERParseSequenceContent(&pmContent
.content
,
989 DERNumPolicyMappingItemSpecs
,
990 DERPolicyMappingItemSpecs
,
992 require_noerr_quiet(drtn
, badDER
);
993 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
994 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
996 certificate
->_policyMappings
.present
= true;
997 certificate
->_policyMappings
.critical
= extn
->critical
;
998 certificate
->_policyMappings
.numMappings
= mapping_count
;
999 certificate
->_policyMappings
.mappings
= mappings
;
1005 certificate
->_policyMappings
.present
= false;
1006 secwarning("Invalid CertificatePolicies Extension");
1011 AuthorityKeyIdentifier ::= SEQUENCE {
1012 keyIdentifier [0] KeyIdentifier OPTIONAL,
1013 authorityCertIssuer [1] GeneralNames OPTIONAL,
1014 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
1015 -- authorityCertIssuer and authorityCertSerialNumber MUST both
1016 -- be present or both be absent
1018 KeyIdentifier ::= OCTET STRING
1020 static bool SecCEPAuthorityKeyIdentifier(SecCertificateRef certificate
,
1021 const SecCertificateExtension
*extn
) {
1022 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1023 DERAuthorityKeyIdentifier akid
;
1025 drtn
= DERParseSequence(&extn
->extnValue
,
1026 DERNumAuthorityKeyIdentifierItemSpecs
,
1027 DERAuthorityKeyIdentifierItemSpecs
,
1028 &akid
, sizeof(akid
));
1029 require_noerr_quiet(drtn
, badDER
);
1030 if (akid
.keyIdentifier
.length
) {
1031 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
1033 if (akid
.authorityCertIssuer
.length
||
1034 akid
.authorityCertSerialNumber
.length
) {
1035 require_quiet(akid
.authorityCertIssuer
.length
&&
1036 akid
.authorityCertSerialNumber
.length
, badDER
);
1037 /* Perhaps put in a subsection called Authority Certificate Issuer. */
1038 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
1039 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
1044 secwarning("Invalid AuthorityKeyIdentifier Extension");
1048 static bool SecCEPPolicyConstraints(SecCertificateRef certificate
,
1049 const SecCertificateExtension
*extn
) {
1050 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1051 DERPolicyConstraints pc
;
1053 drtn
= DERParseSequence(&extn
->extnValue
,
1054 DERNumPolicyConstraintsItemSpecs
,
1055 DERPolicyConstraintsItemSpecs
,
1057 require_noerr_quiet(drtn
, badDER
);
1058 if (pc
.requireExplicitPolicy
.length
) {
1059 require_noerr_quiet(DERParseInteger(
1060 &pc
.requireExplicitPolicy
,
1061 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
1062 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
1064 if (pc
.inhibitPolicyMapping
.length
) {
1065 require_noerr_quiet(DERParseInteger(
1066 &pc
.inhibitPolicyMapping
,
1067 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
1068 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
1071 certificate
->_policyConstraints
.present
= true;
1072 certificate
->_policyConstraints
.critical
= extn
->critical
;
1076 certificate
->_policyConstraints
.present
= false;
1077 secwarning("Invalid PolicyConstraints Extension");
1081 static bool SecCEPExtendedKeyUsage(SecCertificateRef certificate
,
1082 const SecCertificateExtension
*extn
) {
1083 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1086 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &ekuTag
, &ekuSeq
);
1087 require_quiet((drtn
== DR_Success
) && (ekuTag
== ASN1_CONSTR_SEQUENCE
), badDER
);
1088 DERDecodedInfo ekuContent
;
1089 while ((drtn
= DERDecodeSeqNext(&ekuSeq
, &ekuContent
)) == DR_Success
) {
1090 require_quiet(ekuContent
.tag
== ASN1_OBJECT_ID
, badDER
);
1092 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1095 secwarning("Invalidate EKU Extension");
1100 InhibitAnyPolicy ::= SkipCerts
1102 SkipCerts ::= INTEGER (0..MAX)
1104 static bool SecCEPInhibitAnyPolicy(SecCertificateRef certificate
,
1105 const SecCertificateExtension
*extn
) {
1106 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1107 DERDecodedInfo iapContent
;
1108 require_noerr_quiet(DERDecodeItem(&extn
->extnValue
, &iapContent
), badDER
);
1109 require_quiet(iapContent
.tag
== ASN1_INTEGER
, badDER
);
1110 require_noerr_quiet(DERParseInteger(
1111 &iapContent
.content
,
1112 &certificate
->_inhibitAnyPolicySkipCerts
.skipCerts
), badDER
);
1114 certificate
->_inhibitAnyPolicySkipCerts
.present
= true;
1115 certificate
->_inhibitAnyPolicySkipCerts
.critical
= extn
->critical
;
1118 certificate
->_inhibitAnyPolicySkipCerts
.present
= false;
1119 secwarning("Invalid InhibitAnyPolicy Extension");
1124 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
1126 AuthorityInfoAccessSyntax ::=
1127 SEQUENCE SIZE (1..MAX) OF AccessDescription
1129 AccessDescription ::= SEQUENCE {
1130 accessMethod OBJECT IDENTIFIER,
1131 accessLocation GeneralName }
1133 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
1135 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
1137 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
1139 static bool SecCEPAuthorityInfoAccess(SecCertificateRef certificate
,
1140 const SecCertificateExtension
*extn
) {
1141 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1144 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
1145 require_noerr_quiet(drtn
, badDER
);
1146 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1147 DERDecodedInfo adContent
;
1148 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
1149 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1150 DERAccessDescription ad
;
1151 drtn
= DERParseSequenceContent(&adContent
.content
,
1152 DERNumAccessDescriptionItemSpecs
,
1153 DERAccessDescriptionItemSpecs
,
1155 require_noerr_quiet(drtn
, badDER
);
1156 CFMutableArrayRef
*urls
;
1157 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
1158 urls
= &certificate
->_ocspResponders
;
1159 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
1160 urls
= &certificate
->_caIssuers
;
1164 DERDecodedInfo generalNameContent
;
1165 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
1166 require_noerr_quiet(drtn
, badDER
);
1167 switch (generalNameContent
.tag
) {
1169 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
1170 /* Technically I don't think this is valid, but there are certs out
1171 in the wild that use a constructed IA5String. In particular the
1172 VeriSign Time Stamping Authority CA.cer does this. */
1174 case ASN1_CONTEXT_SPECIFIC
| 6:
1176 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1177 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
1178 kCFStringEncodingASCII
, NULL
);
1181 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1182 CFArrayAppendValue(*urls
, url
);
1188 secdebug("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02llx v: %.*s",
1189 generalNameContent
.tag
, (int) generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1194 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1197 secwarning("Invalid Authority Information Access extension");
1201 /* Apple Worldwide Developer Relations Certificate Authority subject name.
1202 * This is a DER sequence with the leading tag and length bytes removed,
1203 * to match what tbsCert.issuer contains.
1205 static const unsigned char Apple_WWDR_CA_Subject_Name
[]={
1206 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,
1207 0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0A,0x41,0x70,0x70,0x6C,0x65,
1208 0x20,0x49,0x6E,0x63,0x2E,0x31,0x2C,0x30,0x2A,0x06,0x03,0x55,0x04,0x0B,0x0C,0x23,
1209 0x41,0x70,0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,
1210 0x44,0x65,0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,
1211 0x6F,0x6E,0x73,0x31,0x44,0x30,0x42,0x06,0x03,0x55,0x04,0x03,0x0C,0x3B,0x41,0x70,
1212 0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,0x44,0x65,
1213 0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,0x6F,0x6E,
1214 0x73,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,
1215 0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79
1218 static void checkForMissingRevocationInfo(SecCertificateRef certificate
) {
1220 certificate
->_crlDistributionPoints
||
1221 certificate
->_ocspResponders
) {
1222 /* We already have an OCSP or CRL URI (or no cert) */
1225 /* Specify an appropriate OCSP responder if we recognize the issuer. */
1226 CFURLRef url
= NULL
;
1227 if (sizeof(Apple_WWDR_CA_Subject_Name
) == certificate
->_issuer
.length
&&
1228 !memcmp(certificate
->_issuer
.data
, Apple_WWDR_CA_Subject_Name
,
1229 sizeof(Apple_WWDR_CA_Subject_Name
))) {
1230 const char *WWDR_OCSP_URI
= "http://ocsp.apple.com/ocsp-wwdr01";
1231 url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1232 (const UInt8
*)WWDR_OCSP_URI
, strlen(WWDR_OCSP_URI
),
1233 kCFStringEncodingASCII
, NULL
);
1236 CFMutableArrayRef
*urls
= &certificate
->_ocspResponders
;
1237 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1238 CFArrayAppendValue(*urls
, url
);
1243 static bool SecCEPSubjectInfoAccess(SecCertificateRef certificate
,
1244 const SecCertificateExtension
*extn
) {
1245 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1249 static bool SecCEPNetscapeCertType(SecCertificateRef certificate
,
1250 const SecCertificateExtension
*extn
) {
1251 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1255 static bool SecCEPEntrustVersInfo(SecCertificateRef certificate
,
1256 const SecCertificateExtension
*extn
) {
1257 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1261 static bool SecCEPEscrowMarker(SecCertificateRef certificate
,
1262 const SecCertificateExtension
*extn
) {
1263 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1267 static bool SecCEPOCSPNoCheck(SecCertificateRef certificate
,
1268 const SecCertificateExtension
*extn
) {
1269 secdebug("cert", "ocsp-nocheck critical: %s", extn
->critical
? "yes" : "no");
1273 /* Dictionary key callback for comparing to DERItems. */
1274 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1275 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1278 /* Dictionary key callback calculating the hash of a DERItem. */
1279 static CFHashCode
SecDERItemHash(const void *value
) {
1280 const DERItem
*derItem
= (const DERItem
*)value
;
1281 CFHashCode hash
= derItem
->length
;
1282 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1283 for (; ix
< derItem
->length
; ++ix
) {
1284 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1290 /* Dictionary key callbacks using the above 2 functions. */
1291 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1295 NULL
, /* copyDescription */
1296 SecDERItemEqual
, /* equal */
1297 SecDERItemHash
/* hash */
1300 static void SecCertificateInitializeExtensionParsers(void) {
1301 /* Build a dictionary that maps from extension OIDs to callback functions
1302 which can parse the extension of the type given. */
1303 static const void *extnOIDs
[] = {
1304 &oidSubjectKeyIdentifier
,
1306 &oidPrivateKeyUsagePeriod
,
1309 &oidBasicConstraints
,
1310 &oidNameConstraints
,
1311 &oidCrlDistributionPoints
,
1312 &oidCertificatePolicies
,
1314 &oidAuthorityKeyIdentifier
,
1315 &oidPolicyConstraints
,
1316 &oidExtendedKeyUsage
,
1317 &oidInhibitAnyPolicy
,
1318 &oidAuthorityInfoAccess
,
1319 &oidSubjectInfoAccess
,
1320 &oidNetscapeCertType
,
1321 &oidEntrustVersInfo
,
1322 &oidApplePolicyEscrowService
,
1325 static const void *extnParsers
[] = {
1326 SecCEPSubjectKeyIdentifier
,
1328 SecCEPPrivateKeyUsagePeriod
,
1329 SecCEPSubjectAltName
,
1330 SecCEPIssuerAltName
,
1331 SecCEPBasicConstraints
,
1332 SecCEPNameConstraints
,
1333 SecCEPCrlDistributionPoints
,
1334 SecCEPCertificatePolicies
,
1335 SecCEPPolicyMappings
,
1336 SecCEPAuthorityKeyIdentifier
,
1337 SecCEPPolicyConstraints
,
1338 SecCEPExtendedKeyUsage
,
1339 SecCEPInhibitAnyPolicy
,
1340 SecCEPAuthorityInfoAccess
,
1341 SecCEPSubjectInfoAccess
,
1342 SecCEPNetscapeCertType
,
1343 SecCEPEntrustVersInfo
,
1347 sExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1348 extnParsers
, array_size(extnOIDs
),
1349 &SecDERItemKeyCallBacks
, NULL
);
1352 CFGiblisWithFunctions(SecCertificate
, NULL
, NULL
, SecCertificateDestroy
, SecCertificateEqual
, SecCertificateHash
, NULL
, SecCertificateCopyDescription
, NULL
, NULL
, ^{
1353 SecCertificateInitializeExtensionParsers();
1356 static bool isAppleExtensionOID(const DERItem
*extnID
)
1358 static const uint8_t appleExtensionArc
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x06 };
1359 static const uint8_t appleComponentExtensionArc
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0b };
1360 static const uint8_t appleSigningExtensionArc
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0c };
1361 static const uint8_t appleEncryptionExtensionArc
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0d };
1362 if (!extnID
|| !extnID
->data
|| (extnID
->length
<= sizeof(appleExtensionArc
))) {
1365 return (!memcmp(extnID
->data
, appleExtensionArc
, sizeof(appleExtensionArc
)) ||
1366 !memcmp(extnID
->data
, appleComponentExtensionArc
, sizeof(appleComponentExtensionArc
)) ||
1367 !memcmp(extnID
->data
, appleSigningExtensionArc
, sizeof(appleSigningExtensionArc
)) ||
1368 !memcmp(extnID
->data
, appleEncryptionExtensionArc
, sizeof(appleEncryptionExtensionArc
)));
1371 static bool isCCCExtensionOID(const DERItem
*extnID
)
1373 static const uint8_t cccVehicleCA
[] = { 0x2B,0x06,0x01,0x04,0x01,0x82,0xC4,0x69,0x05,0x09 };
1374 static const uint8_t cccIntermediateCA
[] = { 0x2B,0x06,0x01,0x04,0x01,0x82,0xC4,0x69,0x05,0x08 };
1375 static const uint8_t cccVehicle
[] = { 0x2B,0x06,0x01,0x04,0x01,0x82,0xC4,0x69,0x05,0x01 };
1376 if (!extnID
|| !extnID
->data
|| (extnID
->length
!= sizeof(cccVehicleCA
))) {
1379 return (!memcmp(extnID
->data
, cccVehicleCA
, sizeof(cccVehicleCA
)) ||
1380 !memcmp(extnID
->data
, cccIntermediateCA
, sizeof(cccIntermediateCA
)) ||
1381 !memcmp(extnID
->data
, cccVehicle
, sizeof(cccVehicle
)));
1384 /* Given the contents of an X.501 Name return the contents of a normalized
1386 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1387 const DERItem
*x501name
) {
1388 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1389 CFIndex length
= x501name
->length
;
1390 CFDataSetLength(result
, length
);
1391 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1394 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1396 require_noerr_quiet(drtn
, badDER
);
1399 /* Always points to last rdn tag. */
1400 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1401 /* Offset relative to base of current rdn set tag. */
1402 CFIndex rdnTagLocation
= 0;
1403 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1404 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1405 /* We don't allow empty RDNs. */
1406 require_quiet(rdn
.content
.length
!= 0, badDER
);
1407 /* Length of the tag and length of the current rdn. */
1408 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1409 CFIndex rdnContentLength
= rdn
.content
.length
;
1410 /* Copy the tag and length of the RDN. */
1411 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1414 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1415 require_quiet(drtn
== DR_Success
, badDER
);
1418 /* Always points to tag of current atv sequence. */
1419 const DERByte
*atvTag
= atvSeq
.nextItem
;
1420 /* Offset relative to base of current atv sequence tag. */
1421 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1422 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1423 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1424 /* Length of the tag and length of the current atv. */
1425 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1426 CFIndex atvContentLength
= atv
.content
.length
;
1427 /* Copy the tag and length of the atv and the atv itself. */
1428 memcpy(base
+ atvTagLocation
, atvTag
,
1429 atvTLLength
+ atv
.content
.length
);
1431 /* Now decode the atv sequence. */
1432 DERAttributeTypeAndValue atvPair
;
1433 drtn
= DERParseSequenceContent(&atv
.content
,
1434 DERNumAttributeTypeAndValueItemSpecs
,
1435 DERAttributeTypeAndValueItemSpecs
,
1436 &atvPair
, sizeof(atvPair
));
1437 require_noerr_quiet(drtn
, badDER
);
1438 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1439 DERDecodedInfo value
;
1440 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1441 require_noerr_quiet(drtn
, badDER
);
1443 /* (c) attribute values in PrintableString are not case sensitive
1444 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1446 (d) attribute values in PrintableString are compared after
1447 removing leading and trailing white space and converting internal
1448 substrings of one or more consecutive white space characters to a
1450 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1451 /* Offset relative to base of current value tag. */
1452 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1453 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1454 CFIndex valueContentLength
= value
.content
.length
;
1456 /* Now copy all the bytes, but convert to upper case while
1457 doing so and convert multiple whitespace chars into a
1459 bool lastWasBlank
= false;
1460 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1461 CFIndex valueCurrentLocation
= valueLocation
;
1463 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1464 UInt8 ch
= value
.content
.data
[ix
];
1469 /* Don't insert a space for first character
1471 if (valueCurrentLocation
> valueLocation
) {
1472 base
[valueCurrentLocation
++] = ' ';
1474 lastWasBlank
= true;
1477 lastWasBlank
= false;
1478 if ('a' <= ch
&& ch
<= 'z') {
1479 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1481 base
[valueCurrentLocation
++] = ch
;
1485 /* Finally if lastWasBlank remove the trailing space. */
1486 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1487 valueCurrentLocation
--;
1489 /* Adjust content length to normalized length. */
1490 valueContentLength
= valueCurrentLocation
- valueLocation
;
1492 /* Number of bytes by which the length should be shorted. */
1493 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1494 if (lengthDiff
== 0) {
1495 /* Easy case no need to adjust lengths. */
1497 /* Hard work we need to go back and fix up length fields
1499 1) The value itself.
1500 2) The ATV Sequence containing type/value
1501 3) The RDN Set containing one or more atv pairs.
1505 /* Step 1 fix up length of value. */
1506 /* Length of value tag and length minus the tag. */
1507 DERSize newValueTLLength
= valueTLLength
- 1;
1508 drtn
= DEREncodeLength(valueContentLength
,
1509 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1510 require(drtn
== DR_Success
, badDER
);
1511 /* Add the length of the tag back in. */
1513 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1515 /* The size of the length field changed, let's slide
1516 the value back by valueLLDiff bytes. */
1517 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1518 base
+ valueTagLocation
+ valueTLLength
,
1519 valueContentLength
);
1520 /* The length diff for the enclosing object. */
1521 lengthDiff
+= valueLLDiff
;
1524 /* Step 2 fix up length of the enclosing ATV Sequence. */
1525 atvContentLength
-= lengthDiff
;
1526 DERSize newATVTLLength
= atvTLLength
- 1;
1527 drtn
= DEREncodeLength(atvContentLength
,
1528 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1529 require(drtn
== DR_Success
, badDER
);
1530 /* Add the length of the tag back in. */
1532 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1534 /* The size of the length field changed, let's slide
1535 the value back by valueLLDiff bytes. */
1536 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1537 base
+ atvTagLocation
+ atvTLLength
,
1539 /* The length diff for the enclosing object. */
1540 lengthDiff
+= atvLLDiff
;
1541 atvTLLength
= newATVTLLength
;
1544 /* Step 3 fix up length of enclosing RDN Set. */
1545 rdnContentLength
-= lengthDiff
;
1546 DERSize newRDNTLLength
= rdnTLLength
- 1;
1547 drtn
= DEREncodeLength(rdnContentLength
,
1548 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1549 require_quiet(drtn
== DR_Success
, badDER
);
1550 /* Add the length of the tag back in. */
1552 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1554 /* The size of the length field changed, let's slide
1555 the value back by valueLLDiff bytes. */
1556 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1557 base
+ rdnTagLocation
+ rdnTLLength
,
1559 /* The length diff for the enclosing object. */
1560 lengthDiff
+= rdnLLDiff
;
1561 rdnTLLength
= newRDNTLLength
;
1563 /* Adjust the locations that might have changed due to
1565 atvTagLocation
-= rdnLLDiff
;
1567 (void) lengthDiff
; // No next object, silence analyzer
1570 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1571 atvTag
= atvSeq
.nextItem
;
1573 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1574 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1575 rdnTag
= rdnSeq
.nextItem
;
1577 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1578 /* Truncate the result to the proper length. */
1579 CFDataSetLength(result
, rdnTagLocation
);
1588 static CFDataRef
SecDERItemCopySequence(DERItem
*content
) {
1589 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
1590 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
1591 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
1593 CFDataSetLength(sequence
, sequence_length
);
1594 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
1595 *sequence_ptr
++ = ONE_BYTE_ASN1_CONSTR_SEQUENCE
;
1596 require_noerr_quiet(DEREncodeLength(content
->length
,
1597 sequence_ptr
, &seq_len_length
), out
);
1598 sequence_ptr
+= seq_len_length
;
1599 memcpy(sequence_ptr
, content
->data
, content
->length
);
1602 CFReleaseSafe(sequence
);
1606 static CFDataRef
SecCopySequenceFromContent(CFDataRef content
) {
1608 tmpItem
.data
= (void *)CFDataGetBytePtr(content
);
1609 tmpItem
.length
= CFDataGetLength(content
);
1611 return SecDERItemCopySequence(&tmpItem
);
1614 CFDataRef
SecDistinguishedNameCopyNormalizedContent(CFDataRef distinguished_name
)
1616 const DERItem name
= { (unsigned char *)CFDataGetBytePtr(distinguished_name
), CFDataGetLength(distinguished_name
) };
1617 DERDecodedInfo content
;
1618 /* Decode top level sequence into DERItem */
1619 if (!DERDecodeItem(&name
, &content
) && (content
.tag
== ASN1_CONSTR_SEQUENCE
))
1620 return createNormalizedX501Name(kCFAllocatorDefault
, &content
.content
);
1624 CFDataRef
SecDistinguishedNameCopyNormalizedSequence(CFDataRef distinguished_name
)
1626 if (!distinguished_name
) { return NULL
; }
1627 CFDataRef normalizedContent
= SecDistinguishedNameCopyNormalizedContent(distinguished_name
);
1628 if (!normalizedContent
) { return NULL
; }
1629 CFDataRef result
= SecCopySequenceFromContent(normalizedContent
);
1630 CFReleaseNull(normalizedContent
);
1634 /* AUDIT[securityd]:
1635 certificate->_der is a caller provided data of any length (might be 0).
1637 Top level certificate decode.
1639 static bool SecCertificateParse(SecCertificateRef certificate
)
1644 require_quiet(certificate
, badCert
);
1645 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1647 /* top level decode */
1648 DERSignedCertCrl signedCert
;
1649 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1650 DERSignedCertCrlItemSpecs
, &signedCert
,
1651 sizeof(signedCert
));
1652 require_noerr_quiet(drtn
, badCert
);
1653 /* Store tbs since we need to digest it for verification later on. */
1654 certificate
->_tbs
= signedCert
.tbs
;
1656 /* decode the TBSCert - it was saved in full DER form */
1658 drtn
= DERParseSequence(&signedCert
.tbs
,
1659 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1660 &tbsCert
, sizeof(tbsCert
));
1661 require_noerr_quiet(drtn
, badCert
);
1663 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1664 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1665 of the params field. */
1666 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1667 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1668 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1669 require_noerr_quiet(drtn
, badCert
);
1671 /* The contents of signedCert.sig is a bit string whose contents
1672 are the signature itself. */
1673 DERByte numUnusedBits
;
1674 drtn
= DERParseBitString(&signedCert
.sig
,
1675 &certificate
->_signature
, &numUnusedBits
);
1676 require_noerr_quiet(drtn
, badCert
);
1678 /* Now decode the tbsCert. */
1680 /* First we turn the optional version into an int. */
1681 if (tbsCert
.version
.length
) {
1682 DERDecodedInfo decoded
;
1683 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1684 require_noerr_quiet(drtn
, badCert
);
1685 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1686 require_quiet(decoded
.content
.length
== 1, badCert
);
1687 certificate
->_version
= decoded
.content
.data
[0];
1688 if (certificate
->_version
> 2) {
1689 secwarning("Invalid certificate version (%d), must be 0..2",
1690 certificate
->_version
);
1692 require_quiet(certificate
->_version
> 0, badCert
);
1693 require_quiet(certificate
->_version
< 3, badCert
);
1695 certificate
->_version
= 0;
1698 /* The serial number is in the tbsCert.serialNum - it was saved in
1699 INTEGER form without the tag and length. */
1700 certificate
->_serialNum
= tbsCert
.serialNum
;
1702 /* Note: RFC5280 4.1.2.2 limits serial number values to 20 octets.
1703 For now, we warn about larger values, but will still create the
1704 certificate with values up to 36 octets to avoid breaking some
1705 nonconforming certs with slightly longer serial numbers.
1706 We also explicitly allow serial numbers of 21 octets where the
1707 leading byte is 0x00 which is used to make a negative 20 octet
1710 if (tbsCert
.serialNum
.length
< 1 || tbsCert
.serialNum
.length
> 21 ||
1711 (tbsCert
.serialNum
.length
== 21 && tbsCert
.serialNum
.data
[0] != 0x00)) {
1712 secwarning("Invalid serial number length (%ld), must be 1..20",
1713 tbsCert
.serialNum
.length
);
1715 require_quiet(tbsCert
.serialNum
.data
!= NULL
&&
1716 tbsCert
.serialNum
.length
>= 1 &&
1717 tbsCert
.serialNum
.length
<= 37, badCert
);
1718 certificate
->_serialNumber
= CFDataCreate(allocator
,
1719 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1721 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1722 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1723 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1724 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1725 require_noerr_quiet(drtn
, badCert
);
1727 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1728 and length fields. */
1729 certificate
->_issuer
= tbsCert
.issuer
;
1730 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1733 /* sequence we're given: decode the tbsCerts Validity sequence. */
1734 DERValidity validity
;
1735 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1736 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1737 &validity
, sizeof(validity
));
1738 require_noerr_quiet(drtn
, badCert
);
1739 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1740 &certificate
->_notBefore
), badCert
);
1741 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1742 &certificate
->_notAfter
), badCert
);
1744 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1745 and length fields. */
1746 certificate
->_subject
= tbsCert
.subject
;
1747 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1750 /* Keep the SPKI around for CT */
1751 certificate
->_subjectPublicKeyInfo
= tbsCert
.subjectPubKey
;
1753 /* sequence we're given: encoded DERSubjPubKeyInfo */
1754 DERSubjPubKeyInfo pubKeyInfo
;
1755 drtn
= DERParseSequenceContent(&tbsCert
.subjectPubKey
,
1756 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1757 &pubKeyInfo
, sizeof(pubKeyInfo
));
1758 require_noerr_quiet(drtn
, badCert
);
1760 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1761 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1762 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1763 &certificate
->_algId
, sizeof(certificate
->_algId
));
1764 require_noerr_quiet(drtn
, badCert
);
1766 /* Now we can figure out the key's algorithm id and params based on
1767 certificate->_algId.oid. */
1769 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1770 are a PKCS1 format RSA key. */
1771 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1772 &certificate
->_pubKeyDER
, &numUnusedBits
);
1773 require_noerr_quiet(drtn
, badCert
);
1775 /* The contents of tbsCert.issuerID is a bit string. */
1776 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1778 /* The contents of tbsCert.subjectID is a bit string. */
1779 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1782 certificate
->_unparseableKnownExtensionIndex
= kCFNotFound
;
1783 if (tbsCert
.extensions
.length
) {
1784 CFIndex extensionCount
= 0;
1787 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1788 require_noerr_quiet(drtn
, badCert
);
1789 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1790 DERDecodedInfo currDecoded
;
1791 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1793 /* ! = MUST recognize ? = SHOULD recognize
1796 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1797 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1798 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1799 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1800 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1801 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1802 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1803 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1805 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1806 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1807 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1808 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1809 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1810 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1811 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1812 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1814 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1815 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1820 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1822 /* Put some upper limit on the number of extensions allowed. */
1823 require_quiet(extensionCount
< 10000, badCert
);
1824 certificate
->_extensionCount
= extensionCount
;
1825 certificate
->_extensions
= malloc(sizeof(SecCertificateExtension
) * (extensionCount
> 0 ? extensionCount
: 1));
1826 require_quiet(certificate
->_extensions
, badCert
);
1829 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1830 require_noerr_quiet(drtn
, badCert
);
1831 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1832 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1833 require_quiet(drtn
== DR_Success
|| (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1834 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1836 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1837 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1838 &extn
, sizeof(extn
));
1839 require_noerr_quiet(drtn
, badCert
);
1840 /* Copy stuff into certificate->extensions[ix]. */
1841 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1842 require_noerr_quiet(drtn
= DERParseBooleanWithDefault(&extn
.critical
, false,
1843 &certificate
->_extensions
[ix
].critical
), badCert
);
1844 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1846 SecCertificateExtensionParser parser
=
1847 (SecCertificateExtensionParser
)CFDictionaryGetValue(sExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1849 /* Invoke the parser. If the extension is critical and the
1850 * parser fails, fail the cert. */
1851 bool parseResult
= parser(certificate
, &certificate
->_extensions
[ix
]);
1853 certificate
->_unparseableKnownExtensionIndex
= ix
;
1855 require_quiet(parseResult
|| !certificate
->_extensions
[ix
].critical
, badCert
);
1856 } else if (certificate
->_extensions
[ix
].critical
) {
1857 if (isAppleExtensionOID(&extn
.extnID
) || isCCCExtensionOID(&extn
.extnID
)) {
1860 secdebug("cert", "Found unknown critical extension");
1861 certificate
->_foundUnknownCriticalExtension
= true;
1863 secdebug("cert", "Found unknown non critical extension");
1867 checkForMissingRevocationInfo(certificate
);
1876 /* Public API functions. */
1877 SecCertificateRef
SecCertificateCreateWithBytes(CFAllocatorRef allocator
,
1878 const UInt8
*der_bytes
, CFIndex der_length
) {
1879 if (der_bytes
== NULL
) return NULL
;
1880 if (der_length
== 0) return NULL
;
1882 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1883 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1884 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1886 memset((char*)result
+ sizeof(result
->_base
), 0,
1887 sizeof(*result
) - sizeof(result
->_base
));
1888 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1889 result
->_der
.length
= der_length
;
1890 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1891 if (!SecCertificateParse(result
)) {
1899 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1900 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1901 const UInt8
*der_bytes
, CFIndex der_length
);
1903 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1904 const UInt8
*der_bytes
, CFIndex der_length
) {
1905 return SecCertificateCreateWithBytes(allocator
, der_bytes
, der_length
);
1907 /* @@@ End of placeholder. */
1909 /* AUDIT[securityd](done):
1910 der_certificate is a caller provided data of any length (might be 0), only
1911 its cf type has been checked.
1913 SecCertificateRef
SecCertificateCreateWithData(CFAllocatorRef allocator
,
1914 CFDataRef der_certificate
) {
1915 if (!der_certificate
) {
1918 CFIndex size
= sizeof(struct __SecCertificate
);
1919 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1920 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1922 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1923 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1924 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1925 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1926 if (!SecCertificateParse(result
)) {
1934 SecCertificateRef
SecCertificateCreateWithKeychainItem(CFAllocatorRef allocator
,
1935 CFDataRef der_certificate
,
1936 CFTypeRef keychain_item
)
1938 SecCertificateRef result
= SecCertificateCreateWithData(allocator
, der_certificate
);
1940 CFRetainSafe(keychain_item
);
1941 result
->_keychain_item
= keychain_item
;
1946 OSStatus
SecCertificateSetKeychainItem(SecCertificateRef certificate
,
1947 CFTypeRef keychain_item
)
1952 CFRetainSafe(keychain_item
);
1953 CFReleaseSafe(certificate
->_keychain_item
);
1954 certificate
->_keychain_item
= keychain_item
;
1955 return errSecSuccess
;
1958 CFDataRef
SecCertificateCopyData(SecCertificateRef certificate
) {
1960 CFDataRef result
= NULL
;
1964 if (certificate
->_der_data
) {
1965 CFRetain(certificate
->_der_data
);
1966 result
= certificate
->_der_data
;
1968 result
= CFDataCreate(CFGetAllocator(certificate
),
1969 certificate
->_der
.data
, certificate
->_der
.length
);
1971 /* FIXME: If we wish to cache result we need to lock the certificate.
1972 Also this create 2 copies of the certificate data which is somewhat
1975 certificate
->_der_data
= result
;
1982 CFIndex
SecCertificateGetLength(SecCertificateRef certificate
) {
1983 return certificate
->_der
.length
;
1986 const UInt8
*SecCertificateGetBytePtr(SecCertificateRef certificate
) {
1987 return certificate
->_der
.data
;
1990 static bool SecCertificateIsCertificate(SecCertificateRef certificate
) {
1994 #ifndef IS_TRUSTTESTS
1995 /* TrustTests registers two SecCertificate TypeIDs, so we'll skip this check
1996 * in the tests and just let the tests crash if they pass the wrong object type. */
1997 if (CFGetTypeID(certificate
) != SecCertificateGetTypeID()) {
2004 /* Used to recreate preCert from cert for Certificate Transparency */
2005 CFDataRef
SecCertificateCopyPrecertTBS(SecCertificateRef certificate
)
2007 CFDataRef outData
= NULL
;
2008 DERItem tbsIn
= certificate
->_tbs
;
2009 DERItem tbsOut
= {0,};
2010 DERItem extensionsOut
= {0,};
2011 DERItem
*extensionsList
= malloc(sizeof(DERItem
)*certificate
->_extensionCount
); /* This maybe one too many */
2012 DERItemSpec
*extensionsListSpecs
= malloc(sizeof(DERItemSpec
)*certificate
->_extensionCount
);
2016 require_quiet(extensionsList
&& extensionsListSpecs
, out
);
2018 /* decode the TBSCert - it was saved in full DER form */
2019 drtn
= DERParseSequence(&tbsIn
,
2020 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
2021 &tbsCert
, sizeof(tbsCert
));
2022 require_noerr_quiet(drtn
, out
);
2024 /* Go over extensions and filter any SCT extension */
2025 CFIndex extensionsCount
= 0;
2027 if (tbsCert
.extensions
.length
) {
2030 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
2032 require_noerr_quiet(drtn
, out
);
2033 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
2034 DERDecodedInfo currDecoded
;
2035 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
2037 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, out
);
2039 drtn
= DERParseSequenceContent(&currDecoded
.content
,
2040 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
2041 &extn
, sizeof(extn
));
2042 require_noerr_quiet(drtn
, out
);
2044 if (extn
.extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
2045 !memcmp(extn
.extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
.extnID
.length
))
2048 extensionsList
[extensionsCount
] = currDecoded
.content
;
2049 extensionsListSpecs
[extensionsCount
].offset
= sizeof(DERItem
)*extensionsCount
;
2050 extensionsListSpecs
[extensionsCount
].options
= 0;
2051 extensionsListSpecs
[extensionsCount
].tag
= ASN1_CONSTR_SEQUENCE
;
2056 require_quiet(drtn
== DR_EndOfSequence
, out
);
2060 /* Encode extensions */
2061 extensionsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
);
2062 extensionsOut
.data
= malloc(extensionsOut
.length
);
2063 require_quiet(extensionsOut
.data
, out
);
2064 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
, extensionsOut
.data
, &extensionsOut
.length
);
2065 require_noerr_quiet(drtn
, out
);
2067 tbsCert
.extensions
= extensionsOut
;
2069 tbsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
);
2070 tbsOut
.data
= malloc(tbsOut
.length
);
2071 require_quiet(tbsOut
.data
, out
);
2072 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
, tbsOut
.data
, &tbsOut
.length
);
2073 require_noerr_quiet(drtn
, out
);
2075 outData
= CFDataCreate(kCFAllocatorDefault
, tbsOut
.data
, tbsOut
.length
);
2078 if (extensionsOut
.data
) free(extensionsOut
.data
);
2079 if (tbsOut
.data
) free(tbsOut
.data
);
2080 if (extensionsList
) free(extensionsList
);
2081 if (extensionsListSpecs
) free(extensionsListSpecs
);
2086 /* From rfc3280 - Appendix B. ASN.1 Notes
2088 Object Identifiers (OIDs) are used throughout this specification to
2089 identify certificate policies, public key and signature algorithms,
2090 certificate extensions, etc. There is no maximum size for OIDs.
2091 This specification mandates support for OIDs which have arc elements
2092 with values that are less than 2^28, that is, they MUST be between 0
2093 and 268,435,455, inclusive. This allows each arc element to be
2094 represented within a single 32 bit word. Implementations MUST also
2095 support OIDs where the length of the dotted decimal (see [RFC 2252],
2096 section 4.1) string representation can be up to 100 bytes
2097 (inclusive). Implementations MUST be able to handle OIDs with up to
2098 20 elements (inclusive). CAs SHOULD NOT issue certificates which
2099 contain OIDs that exceed these requirements. Likewise, CRL issuers
2100 SHOULD NOT issue CRLs which contain OIDs that exceed these
2104 /* Oids longer than this are considered invalid. */
2105 #define MAX_OID_SIZE 32
2107 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
2108 const DERItem
*oid
) {
2110 if (oid
->length
== 0) {
2111 return SecCopyCertString(SEC_NULL_KEY
);
2113 if (oid
->length
> MAX_OID_SIZE
) {
2114 return SecCopyCertString(SEC_OID_TOO_LONG_KEY
);
2117 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
2119 // The first two levels are encoded into one byte, since the root level
2120 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
2121 // y may be > 39, so we have to add special-case handling for this.
2122 uint32_t x
= oid
->data
[0] / 40;
2123 uint32_t y
= oid
->data
[0] % 40;
2126 // Handle special case for large y if x = 2
2130 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
2133 for (x
= 1; x
< oid
->length
; ++x
)
2135 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
2136 /* @@@ value may not span more than 4 bytes. */
2137 /* A max number of 20 values is allowed. */
2138 if (!(oid
->data
[x
] & 0x80))
2140 CFStringAppendFormat(result
, NULL
, CFSTR(".%" PRIu32
), value
);
2147 static CFStringRef
copyOidDescription(CFAllocatorRef allocator
,
2148 const DERItem
*oid
, bool localized
) {
2149 if (!oid
|| oid
->length
== 0) {
2150 return (localized
) ? SecCopyCertString(SEC_NULL_KEY
) : SEC_NULL_KEY
;
2153 CFStringRef name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
2158 /* Build the key we use to lookup the localized OID description. */
2159 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
2160 oid
->length
* 3 + 5);
2161 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), oid
->length
);
2162 for (DERSize ix
= 0; ix
< oid
->length
; ++ix
) {
2163 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
2165 CFStringRef locname
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
2166 if (locname
&& !CFEqual(oidKey
, locname
)) {
2167 /* Found localized description string, so use it instead of OID. */
2168 CFReleaseSafe(name
);
2171 CFReleaseSafe(locname
);
2178 /* Return the ipAddress as a dotted quad for ipv4, or as 8 colon separated
2179 4 digit hex strings for ipv6. Return NULL if the provided IP doesn't
2180 have a length of exactly 4 or 16 octets.
2181 Note: hex values are normalized to uppercase.
2183 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
2184 const DERItem
*ip
) {
2185 /* This is the IP Address as an OCTET STRING.
2186 For IPv4 it's 4 octets addr, or 8 octets, addr/mask.
2187 For IPv6 it's 16 octets addr, or 32 octets addr/mask.
2189 CFStringRef value
= NULL
;
2190 if (ip
->length
== IPv4ADDRLEN
) {
2191 value
= CFStringCreateWithFormat(allocator
, NULL
,
2192 CFSTR("%u.%u.%u.%u"),
2193 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
2194 } else if (ip
->length
== IPv6ADDRLEN
) {
2195 value
= CFStringCreateWithFormat(allocator
, NULL
,
2196 CFSTR("%02X%02X:%02X%02X:%02X%02X:%02X%02X:"
2197 "%02X%02X:%02X%02X:%02X%02X:%02X%02X"),
2198 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
2199 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
2200 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
2201 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
2207 void appendProperty(CFMutableArrayRef properties
, CFStringRef propertyType
,
2208 CFStringRef label
, CFStringRef localizedLabel
, CFTypeRef value
,
2210 CFDictionaryRef property
;
2212 CFStringRef ll
= NULL
;
2214 /* use unlocalized label, overriding localizedLabel */
2215 ll
= localizedLabel
= (CFStringRef
) CFRetainSafe(label
);
2216 } else if (!localizedLabel
) {
2217 /* copy localized label for unlocalized label */
2218 ll
= localizedLabel
= SecCopyCertString(label
);
2220 const void *all_keys
[4];
2221 all_keys
[0] = kSecPropertyKeyType
;
2222 all_keys
[1] = kSecPropertyKeyLabel
;
2223 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
2224 all_keys
[3] = kSecPropertyKeyValue
;
2225 const void *property_values
[] = {
2231 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2232 all_keys
, property_values
, value
? 4 : 3,
2233 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2236 const void *nolabel_keys
[2];
2237 nolabel_keys
[0] = kSecPropertyKeyType
;
2238 nolabel_keys
[1] = kSecPropertyKeyValue
;
2239 const void *property_values
[] = {
2243 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2244 nolabel_keys
, property_values
, 2,
2245 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2248 CFArrayAppendValue(properties
, property
);
2249 CFRelease(property
);
2253 #define UTC_TIME_NOSEC_ZULU_LEN 11
2255 #define UTC_TIME_ZULU_LEN 13
2256 /* YYMMDDhhmmssThhmm */
2257 #define UTC_TIME_LOCALIZED_LEN 17
2258 /* YYYYMMDDhhmmssZ */
2259 #define GENERALIZED_TIME_ZULU_LEN 15
2260 /* YYYYMMDDhhmmssThhmm */
2261 #define GENERALIZED_TIME_LOCALIZED_LEN 19
2263 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
2265 static inline int parseDecimalPair(const DERByte
**p
) {
2266 const DERByte
*cp
= *p
;
2268 return 10 * (cp
[0] - '0') + cp
[1] - '0';
2271 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime.
2272 Return a CFErrorRef in the error parameter if decoding fails.
2273 Note that this is needed to distinguish an error condition from a
2274 valid time which specifies 2001-01-01 00:00:00 (i.e. a value of 0).
2276 CFAbsoluteTime
SecAbsoluteTimeFromDateContentWithError(DERTag tag
,
2277 const uint8_t *bytes
,
2279 CFErrorRef
*error
) {
2283 if (NULL
== bytes
|| 0 == length
) {
2287 bool isUtcLength
= false;
2288 bool isLocalized
= false;
2289 bool noSeconds
= false;
2291 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
2295 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
2298 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
2300 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
2303 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
2306 default: /* unknown format */
2310 /* Make sure the der tag fits the thing inside it. */
2311 if (tag
== ASN1_UTC_TIME
) {
2315 } else if (tag
== ASN1_GENERALIZED_TIME
) {
2323 const DERByte
*cp
= bytes
;
2324 /* Check that all characters are digits, except if localized the timezone
2325 indicator or if not localized the 'Z' at the end. */
2327 for (ix
= 0; ix
< length
; ++ix
) {
2328 if (!(isdigit(cp
[ix
]))) {
2329 if ((isLocalized
&& ix
== length
- 5 &&
2330 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
2331 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
2338 /* Parse the date and time fields. */
2339 int year
, month
, day
, hour
, minute
, second
;
2341 year
= parseDecimalPair(&cp
);
2343 /* 0 <= year < 50 : assume century 21 */
2345 } else if (year
< 70) {
2346 /* 50 <= year < 70 : illegal per PKIX */
2349 /* 70 < year <= 99 : assume century 20 */
2353 year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
2355 month
= parseDecimalPair(&cp
);
2356 day
= parseDecimalPair(&cp
);
2357 hour
= parseDecimalPair(&cp
);
2358 minute
= parseDecimalPair(&cp
);
2362 second
= parseDecimalPair(&cp
);
2365 CFTimeInterval timeZoneOffset
;
2367 /* ZONE INDICATOR */
2368 int multiplier
= *cp
++ == '+' ? 60 : -60;
2369 timeZoneOffset
= multiplier
*
2370 (parseDecimalPair(&cp
) * 60 + parseDecimalPair(&cp
));
2375 secdebug("dateparse",
2376 "date %.*s year: %04d%02d%02d%02d%02d%02d%+05g",
2377 (int) length
, bytes
, year
, month
,
2378 day
, hour
, minute
, second
,
2379 timeZoneOffset
/ 60);
2381 static int mdays
[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
2382 int is_leap_year
= year
% 4 == 0 && (year
% 100 != 0 || year
% 400 == 0) ? 1 : 0;
2383 /* Some basic checks on the date, allowing leap seconds */
2384 if (month
< 1 || month
> 12 || day
< 1 || day
> 31 || hour
> 23 || minute
> 59 || second
> 60
2385 || (month
== 2 && day
> mdays
[month
] - mdays
[month
- 1] + is_leap_year
)
2386 || (month
!= 2 && day
> mdays
[month
] - mdays
[month
- 1])) {
2391 int dy
= year
- 2001;
2396 int leap_days
= dy
/ 4 - dy
/ 100 + dy
/ 400;
2397 day
+= ((year
- 2001) * 365 + leap_days
) + mdays
[month
- 1] - 1;
2399 day
+= is_leap_year
;
2401 CFAbsoluteTime absTime
= (CFAbsoluteTime
)((day
* 24.0 + hour
) * 60.0 + minute
) * 60.0 + second
;
2402 return absTime
- timeZoneOffset
;
2406 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
2411 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
2413 return SecAbsoluteTimeFromDateContentWithError(tag
, bytes
, length
, NULL
);
2416 __attribute__((__nonnull__
)) static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
2417 CFAbsoluteTime
*pabsTime
) {
2418 CFErrorRef error
= NULL
;
2419 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContentWithError(tag
, date
->data
,
2420 date
->length
, &error
);
2422 secwarning("Invalid date specification in certificate (see RFC5280 4.1.2.5)");
2427 *pabsTime
= absTime
;
2431 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
2432 true if the date was valid and properly decoded, also return the result in
2433 absTime. Return false otherwise. */
2434 __attribute__((__nonnull__
)) static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
2435 CFAbsoluteTime
*absTime
) {
2436 if (dateChoice
->length
== 0) return false;
2438 DERDecodedInfo decoded
;
2439 if (DERDecodeItem(dateChoice
, &decoded
))
2442 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
2446 static void appendDataProperty(CFMutableArrayRef properties
,
2447 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
,
2449 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
2450 der_data
->data
, der_data
->length
);
2451 appendProperty(properties
, kSecPropertyTypeData
, label
, localizedLabel
,
2456 static void appendRelabeledProperty(CFMutableArrayRef properties
,
2458 CFStringRef localizedLabel
,
2459 const DERItem
*der_data
,
2460 CFStringRef labelFormat
,
2462 CFStringRef newLabel
=
2463 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2464 labelFormat
, label
);
2465 CFStringRef ll
= NULL
;
2466 CFStringRef localizedLabelFormat
= NULL
;
2468 /* use provided label and format strings; do not localize */
2469 ll
= localizedLabel
= (CFStringRef
) CFRetainSafe(label
);
2470 localizedLabelFormat
= (CFStringRef
) CFRetainSafe(labelFormat
);
2472 if (!localizedLabel
) {
2473 /* copy localized label for provided label */
2474 ll
= localizedLabel
= SecCopyCertString(label
);
2476 /* copy localized format for provided format */
2477 localizedLabelFormat
= SecCopyCertString(labelFormat
);
2480 CFStringRef newLocalizedLabel
=
2481 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2482 localizedLabelFormat
, localizedLabel
);
2484 CFReleaseSafe(localizedLabelFormat
);
2485 appendDataProperty(properties
, newLabel
, newLocalizedLabel
, der_data
, localized
);
2486 CFReleaseSafe(newLabel
);
2487 CFReleaseSafe(newLocalizedLabel
);
2491 static void appendUnparsedProperty(CFMutableArrayRef properties
,
2492 CFStringRef label
, CFStringRef localizedLabel
,
2493 const DERItem
*der_data
, bool localized
) {
2494 appendRelabeledProperty(properties
, label
, localizedLabel
, der_data
,
2495 SEC_UNPARSED_KEY
, localized
);
2498 static void appendInvalidProperty(CFMutableArrayRef properties
,
2499 CFStringRef label
, const DERItem
*der_data
, bool localized
) {
2500 appendRelabeledProperty(properties
, label
, NULL
, der_data
,
2501 SEC_INVALID_KEY
, localized
);
2504 static void appendDateContentProperty(CFMutableArrayRef properties
,
2505 CFStringRef label
, DERTag tag
,
2506 const DERItem
*dateContent
, bool localized
) {
2507 CFAbsoluteTime absTime
;
2508 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
2509 /* Date decode failure; insert hex bytes instead. */
2510 return appendInvalidProperty(properties
, label
, dateContent
, localized
);
2512 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2513 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
, localized
);
2517 static void appendDateProperty(CFMutableArrayRef properties
,
2518 CFStringRef label
, CFAbsoluteTime absTime
, bool localized
) {
2519 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2520 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
, localized
);
2524 static void appendValidityPeriodProperty(CFMutableArrayRef parent
, CFStringRef label
,
2525 SecCertificateRef certificate
, bool localized
) {
2526 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2527 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2529 appendDateProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2530 certificate
->_notBefore
, localized
);
2531 appendDateProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2532 certificate
->_notAfter
, localized
);
2534 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
, localized
);
2535 CFReleaseNull(properties
);
2538 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
2539 CFStringRef label
, const DERItem
*ip
, bool localized
) {
2541 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
2543 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
, localized
);
2546 appendUnparsedProperty(properties
, label
, NULL
, ip
, localized
);
2550 static void appendURLContentProperty(CFMutableArrayRef properties
,
2551 CFStringRef label
, const DERItem
*urlContent
, bool localized
) {
2552 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
2553 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2555 appendProperty(properties
, kSecPropertyTypeURL
, label
, NULL
, url
, localized
);
2558 appendInvalidProperty(properties
, label
, urlContent
, localized
);
2562 static void appendURLProperty(CFMutableArrayRef properties
,
2563 CFStringRef label
, const DERItem
*url
, bool localized
) {
2564 DERDecodedInfo decoded
;
2567 drtn
= DERDecodeItem(url
, &decoded
);
2568 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2569 appendInvalidProperty(properties
, label
, url
, localized
);
2571 appendURLContentProperty(properties
, label
, &decoded
.content
, localized
);
2575 static void appendOIDProperty(CFMutableArrayRef properties
,
2576 CFStringRef label
, CFStringRef llabel
, const DERItem
*oid
, bool localized
) {
2577 CFStringRef oid_string
=
2578 copyOidDescription(CFGetAllocator(properties
), oid
, localized
);
2579 appendProperty(properties
, kSecPropertyTypeString
, label
, llabel
,
2580 oid_string
, localized
);
2581 CFRelease(oid_string
);
2584 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2585 CFStringRef label
, const DERAlgorithmId
*algorithm
, bool localized
) {
2586 CFMutableArrayRef alg_props
=
2587 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2588 &kCFTypeArrayCallBacks
);
2589 appendOIDProperty(alg_props
, SEC_ALGORITHM_KEY
, NULL
,
2590 &algorithm
->oid
, localized
);
2591 if (algorithm
->params
.length
) {
2592 if (algorithm
->params
.length
== 2 &&
2593 algorithm
->params
.data
[0] == ASN1_NULL
&&
2594 algorithm
->params
.data
[1] == 0) {
2595 CFStringRef value
= SecCopyCertString(SEC_NONE_KEY
);
2596 appendProperty(alg_props
, kSecPropertyTypeString
,
2597 SEC_PARAMETERS_KEY
, NULL
, value
, localized
);
2600 appendUnparsedProperty(alg_props
, SEC_PARAMETERS_KEY
, NULL
,
2601 &algorithm
->params
, localized
);
2604 appendProperty(properties
, kSecPropertyTypeSection
, label
, NULL
,
2605 alg_props
, localized
);
2606 CFRelease(alg_props
);
2609 static void appendPublicKeyProperty(CFMutableArrayRef parent
, CFStringRef label
,
2610 SecCertificateRef certificate
, bool localized
) {
2611 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2612 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2614 /* Public key algorithm. */
2615 appendAlgorithmProperty(properties
, SEC_PUBLIC_KEY_ALG_KEY
,
2616 &certificate
->_algId
, localized
);
2618 /* Public Key Size */
2619 SecKeyRef publicKey
= SecCertificateCopyKey(certificate
);
2621 size_t sizeInBytes
= SecKeyGetBlockSize(publicKey
);
2622 CFStringRef sizeInBitsString
= CFStringCreateWithFormat(allocator
, NULL
,
2623 CFSTR("%ld"), (sizeInBytes
*8));
2624 if (sizeInBitsString
) {
2625 appendProperty(properties
, kSecPropertyTypeString
, SEC_PUBLIC_KEY_SIZE_KEY
,
2626 NULL
, sizeInBitsString
, localized
);
2628 CFReleaseNull(sizeInBitsString
);
2630 CFReleaseNull(publicKey
);
2632 /* Consider breaking down an RSA public key into modulus and
2634 appendDataProperty(properties
, SEC_PUBLIC_KEY_DATA_KEY
, NULL
,
2635 &certificate
->_pubKeyDER
, localized
);
2637 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2638 properties
, localized
);
2639 CFReleaseNull(properties
);
2642 static void appendSignatureProperty(CFMutableArrayRef parent
, CFStringRef label
,
2643 SecCertificateRef certificate
, bool localized
) {
2644 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2645 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2647 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
2648 &certificate
->_tbsSigAlg
, localized
);
2650 appendDataProperty(properties
, SEC_SIGNATURE_DATA_KEY
, NULL
,
2651 &certificate
->_signature
, localized
);
2653 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2654 properties
, localized
);
2655 CFReleaseNull(properties
);
2658 static void appendFingerprintsProperty(CFMutableArrayRef parent
, CFStringRef label
,
2659 SecCertificateRef certificate
, bool localized
) {
2660 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2661 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2663 CFDataRef sha256Fingerprint
= SecCertificateCopySHA256Digest(certificate
);
2664 if (sha256Fingerprint
) {
2665 appendProperty(properties
, kSecPropertyTypeData
, SEC_SHA2_FINGERPRINT_KEY
,
2666 NULL
, sha256Fingerprint
, localized
);
2668 CFReleaseNull(sha256Fingerprint
);
2670 appendProperty(properties
, kSecPropertyTypeData
, SEC_SHA1_FINGERPRINT_KEY
,
2671 NULL
, SecCertificateGetSHA1Digest(certificate
), localized
);
2673 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2674 properties
, localized
);
2675 CFReleaseNull(properties
);
2678 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2679 const DERItem
*blob
) {
2680 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2681 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2682 blob
->length
* 3 - 1);
2683 for (ix
= 0; ix
< length
; ++ix
)
2685 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2687 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2692 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2693 CFStringRef blobType
, CFStringRef quanta
,
2694 const DERItem
*blob
, bool localized
) {
2695 CFStringRef localizedBlobType
= (localized
) ?
2696 SecCopyCertString(blobType
) : (CFStringRef
) CFRetainSafe(blobType
);
2697 CFStringRef localizedQuanta
= (localized
) ?
2698 SecCopyCertString(quanta
) : (CFStringRef
) CFRetainSafe(quanta
);
2699 /* "format string for encoded field data (e.g. Sequence; 128 bytes; "
2700 "data = 00 00 ...)" */
2701 CFStringRef blobFormat
= (localized
) ?
2702 SecCopyCertString(SEC_BLOB_KEY
) : SEC_BLOB_KEY
;
2703 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2704 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2705 blobFormat
, localizedBlobType
, blob
->length
, localizedQuanta
, hex
);
2707 CFRelease(blobFormat
);
2708 CFReleaseSafe(localizedQuanta
);
2709 CFReleaseSafe(localizedBlobType
);
2714 /* Return a string verbatim (unlocalized) from a DER field. */
2715 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2716 const DERItem
*string
, CFStringEncoding encoding
,
2717 bool printableOnly
) {
2718 /* Strip potential bogus trailing zero from printable strings. */
2719 DERSize length
= string
->length
;
2720 if (length
&& string
->data
[length
- 1] == 0) {
2721 /* Don't mess with the length of UTF16 strings though. */
2722 if (encoding
!= kCFStringEncodingUTF16
)
2725 /* A zero length string isn't considered printable. */
2726 if (!length
&& printableOnly
)
2729 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2730 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2731 passing false makes it treat it as native endian by default. */
2732 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2733 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2737 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2740 /* From rfc3280 - Appendix B. ASN.1 Notes
2742 CAs MUST force the serialNumber to be a non-negative integer, that
2743 is, the sign bit in the DER encoding of the INTEGER value MUST be
2744 zero - this can be done by adding a leading (leftmost) `00'H octet if
2745 necessary. This removes a potential ambiguity in mapping between a
2746 string of octets and an integer value.
2748 As noted in section 4.1.2.2, serial numbers can be expected to
2749 contain long integers. Certificate users MUST be able to handle
2750 serialNumber values up to 20 octets in length. Conformant CAs MUST
2751 NOT use serialNumber values longer than 20 octets.
2754 /* Return the given numeric data as a string: decimal up to 64 bits,
2757 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2758 const DERItem
*integer
) {
2760 CFIndex ix
, length
= integer
->length
;
2762 if (length
== 0 || length
> 8)
2763 return copyHexDescription(allocator
, integer
);
2765 for(ix
= 0; ix
< length
; ++ix
) {
2767 value
+= integer
->data
[ix
];
2770 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2773 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2774 DERTag tag
, const DERItem
*derThing
, bool printableOnly
, bool localized
) {
2775 if (!derThing
) { return NULL
; }
2779 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2780 case ASN1_PRINTABLE_STRING
:
2781 case ASN1_IA5_STRING
:
2782 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2783 case ASN1_UTF8_STRING
:
2784 case ASN1_GENERAL_STRING
:
2785 case ASN1_UNIVERSAL_STRING
:
2786 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2787 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2788 case ASN1_VIDEOTEX_STRING
: // 21
2789 case ASN1_VISIBLE_STRING
: // 26
2790 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2791 case ASN1_BMP_STRING
: // 30
2792 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2793 case ASN1_OCTET_STRING
:
2794 return printableOnly
? NULL
:
2795 copyBlobString(allocator
, SEC_BYTE_STRING_KEY
, SEC_BYTES_KEY
,
2796 derThing
, localized
);
2797 case ASN1_BIT_STRING
:
2798 return printableOnly
? NULL
:
2799 copyBlobString(allocator
, SEC_BIT_STRING_KEY
, SEC_BITS_KEY
,
2800 derThing
, localized
);
2801 case ASN1_CONSTR_SEQUENCE
:
2802 return printableOnly
? NULL
:
2803 copyBlobString(allocator
, SEC_SEQUENCE_KEY
, SEC_BYTES_KEY
,
2804 derThing
, localized
);
2805 case ASN1_CONSTR_SET
:
2806 return printableOnly
? NULL
:
2807 copyBlobString(allocator
, SEC_SET_KEY
, SEC_BYTES_KEY
,
2808 derThing
, localized
);
2809 case ASN1_OBJECT_ID
:
2810 return printableOnly
? NULL
: copyOidDescription(allocator
, derThing
, localized
);
2812 if (printableOnly
) {
2815 CFStringRef fmt
= (localized
) ?
2816 SecCopyCertString(SEC_NOT_DISPLAYED_KEY
) : SEC_NOT_DISPLAYED_KEY
;
2817 if (!fmt
) { return NULL
; }
2818 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
2819 (unsigned long)tag
, (unsigned long)derThing
->length
);
2826 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2827 const DERItem
*derThing
, bool printableOnly
, bool localized
) {
2828 DERDecodedInfo decoded
;
2831 drtn
= DERDecodeItem(derThing
, &decoded
);
2833 /* TODO: Perhaps put something in the label saying we couldn't parse
2835 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2837 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2838 &decoded
.content
, false, localized
);
2842 static void appendDERThingProperty(CFMutableArrayRef properties
,
2843 CFStringRef label
, CFStringRef localizedLabel
,
2844 const DERItem
*derThing
, bool localized
) {
2845 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2846 derThing
, false, localized
);
2848 appendProperty(properties
, kSecPropertyTypeString
, label
, localizedLabel
,
2851 CFReleaseSafe(value
);
2854 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2855 const DERItem
*rdnValue
, CFIndex rdnIX
,
2857 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2859 /* If there is more than one value pair we create a subsection for the
2860 second pair, and append things to the subsection for subsequent
2862 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2863 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2865 /* Since this is the second rdn pair for a given rdn, we setup a
2866 new subsection for this rdn. We remove the first property
2867 from the properties array and make it the first element in the
2868 subsection instead. */
2869 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2870 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2871 CFArrayAppendValue(rdn_props
, lastValue
);
2872 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2873 appendProperty(properties
, kSecPropertyTypeSection
, NULL
, NULL
,
2874 rdn_props
, localized
);
2875 properties
= rdn_props
;
2876 // rdn_props is now retained by the original properties array
2877 CFReleaseSafe(rdn_props
);
2879 /* Since this is the third or later rdn pair we have already
2880 created a subsection in the top level properties array. Instead
2881 of appending to that directly we append to the array inside the
2883 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2884 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2888 /* Finally we append the new rdn value to the property array. */
2890 SecDERItemCopyOIDDecimalRepresentation(CFGetAllocator(properties
),
2892 CFStringRef localizedLabel
= copyOidDescription(CFGetAllocator(properties
),
2893 rdnType
, localized
);
2894 appendDERThingProperty(properties
, label
, localizedLabel
,
2895 rdnValue
, localized
);
2896 CFReleaseSafe(label
);
2897 CFReleaseSafe(localizedLabel
);
2898 return errSecSuccess
;
2901 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2902 const DERItem
*rdnSetContent
, bool localized
) {
2903 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2904 &kCFTypeArrayCallBacks
);
2905 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2906 appendRDNProperty
, localized
);
2908 CFArrayRemoveAllValues(properties
);
2909 appendInvalidProperty(properties
, SEC_RDN_KEY
, rdnSetContent
,
2917 From rfc3739 - 3.1.2. Subject
2919 When parsing the subject here are some tips for a short name of the cert.
2920 Choice I: commonName
2921 Choice II: givenName
2922 Choice III: pseudonym
2924 The commonName attribute value SHALL, when present, contain a name
2925 of the subject. This MAY be in the subject's preferred
2926 presentation format, or a format preferred by the CA, or some
2927 other format. Pseudonyms, nicknames, and names with spelling
2928 other than defined by the registered name MAY be used. To
2929 understand the nature of the name presented in commonName,
2930 complying applications MAY have to examine present values of the
2931 givenName and surname attributes, or the pseudonym attribute.
2934 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2935 const DERItem
*x501NameContent
, bool localized
) {
2936 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2937 &kCFTypeArrayCallBacks
);
2938 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2939 appendRDNProperty
, localized
);
2941 CFArrayRemoveAllValues(properties
);
2942 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
,
2943 x501NameContent
, localized
);
2949 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2950 const DERItem
*x501Name
, bool localized
) {
2951 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2952 &kCFTypeArrayCallBacks
);
2953 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
, localized
);
2955 CFArrayRemoveAllValues(properties
);
2956 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
,
2957 x501Name
, localized
);
2963 static void appendIntegerProperty(CFMutableArrayRef properties
,
2964 CFStringRef label
, const DERItem
*integer
, bool localized
) {
2965 CFStringRef string
= copyIntegerContentDescription(
2966 CFGetAllocator(properties
), integer
);
2967 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2972 static void appendBoolProperty(CFMutableArrayRef properties
,
2973 CFStringRef label
, bool boolean
, bool localized
) {
2974 CFStringRef key
= (boolean
) ? SEC_YES_KEY
: SEC_NO_KEY
;
2975 CFStringRef value
= (localized
) ? SecCopyCertString(key
) : key
;
2976 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2981 static void appendBooleanProperty(CFMutableArrayRef properties
,
2982 CFStringRef label
, const DERItem
*boolean
,
2983 bool defaultValue
, bool localized
) {
2985 DERReturn drtn
= DERParseBooleanWithDefault(boolean
, defaultValue
, &result
);
2987 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2988 appendInvalidProperty(properties
, label
, boolean
, localized
);
2990 appendBoolProperty(properties
, label
, result
, localized
);
2994 static void appendSerialNumberProperty(CFMutableArrayRef parent
, CFStringRef label
,
2995 DERItem
*serialNum
, bool localized
) {
2996 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2997 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2999 if (serialNum
->length
) {
3000 appendIntegerProperty(properties
, SEC_SERIAL_NUMBER_KEY
,
3001 serialNum
, localized
);
3002 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
3003 properties
, localized
);
3006 CFReleaseNull(properties
);
3009 static void appendBitStringContentNames(CFMutableArrayRef properties
,
3010 CFStringRef label
, const DERItem
*bitStringContent
,
3011 const CFStringRef
*names
, CFIndex namesCount
,
3013 DERSize len
= bitStringContent
->length
- 1;
3014 require_quiet(len
== 1 || len
== 2, badDER
);
3015 DERByte numUnusedBits
= bitStringContent
->data
[0];
3016 require_quiet(numUnusedBits
< 8, badDER
);
3017 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
3018 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
3019 uint_fast16_t value
= bitStringContent
->data
[1];
3022 value
= (value
<< 8) + bitStringContent
->data
[2];
3028 CFStringRef fmt
= (localized
) ?
3029 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
3030 CFStringRef string
= NULL
;
3031 for (ix
= 0; ix
< bits
; ++ix
) {
3032 CFStringRef localizedName
= (localized
) ? SecCopyCertString(names
[ix
]) : CFRetainSafe(names
[ix
]);
3036 CFStringCreateWithFormat(CFGetAllocator(properties
),
3037 NULL
, fmt
, string
, localizedName
);
3041 string
= localizedName
;
3046 CFReleaseNull(localizedName
);
3049 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3050 string
? string
: CFSTR(""), localized
);
3051 CFReleaseSafe(string
);
3054 appendInvalidProperty(properties
, label
, bitStringContent
, localized
);
3057 static void appendBitStringNames(CFMutableArrayRef properties
,
3058 CFStringRef label
, const DERItem
*bitString
,
3059 const CFStringRef
*names
, CFIndex namesCount
,
3061 DERDecodedInfo bitStringContent
;
3062 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
3063 require_noerr_quiet(drtn
, badDER
);
3064 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
3065 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
3066 names
, namesCount
, localized
);
3069 appendInvalidProperty(properties
, label
, bitString
, localized
);
3072 static void appendKeyUsage(CFMutableArrayRef properties
,
3073 const DERItem
*extnValue
, bool localized
) {
3074 static const CFStringRef usageNames
[] = {
3075 SEC_DIGITAL_SIGNATURE_KEY
,
3076 SEC_NON_REPUDIATION_KEY
,
3077 SEC_KEY_ENCIPHERMENT_KEY
,
3078 SEC_DATA_ENCIPHERMENT_KEY
,
3079 SEC_KEY_AGREEMENT_KEY
,
3082 SEC_ENCIPHER_ONLY_KEY
,
3083 SEC_DECIPHER_ONLY_KEY
3085 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3086 usageNames
, array_size(usageNames
), localized
);
3089 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
3090 const DERItem
*extnValue
, bool localized
) {
3091 DERPrivateKeyUsagePeriod pkup
;
3092 DERReturn drtn
= DERParseSequence(extnValue
,
3093 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
3094 &pkup
, sizeof(pkup
));
3095 require_noerr_quiet(drtn
, badDER
);
3096 if (pkup
.notBefore
.length
) {
3097 appendDateContentProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
3098 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
, localized
);
3100 if (pkup
.notAfter
.length
) {
3101 appendDateContentProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
3102 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
, localized
);
3106 appendInvalidProperty(properties
, SEC_PRIVATE_KU_PERIOD_KEY
,
3107 extnValue
, localized
);
3110 static void appendStringContentProperty(CFMutableArrayRef properties
,
3111 CFStringRef label
, const DERItem
*stringContent
,
3112 CFStringEncoding encoding
, bool localized
) {
3113 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
3114 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
3116 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3120 appendInvalidProperty(properties
, label
, stringContent
, localized
);
3125 OtherName ::= SEQUENCE {
3126 type-id OBJECT IDENTIFIER,
3127 value [0] EXPLICIT ANY DEFINED BY type-id }
3129 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
3130 const DERItem
*otherNameContent
, bool localized
) {
3132 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
3133 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
3135 require_noerr_quiet(drtn
, badDER
);
3136 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3138 SecDERItemCopyOIDDecimalRepresentation(allocator
, &on
.typeIdentifier
);
3139 CFStringRef localizedLabel
=
3140 copyOidDescription(allocator
, &on
.typeIdentifier
, localized
);
3141 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
,
3144 appendProperty(properties
, kSecPropertyTypeString
, label
,
3145 localizedLabel
, value_string
, localized
);
3147 appendUnparsedProperty(properties
, label
, localizedLabel
,
3148 &on
.value
, localized
);
3150 CFReleaseSafe(value_string
);
3151 CFReleaseSafe(label
);
3152 CFReleaseSafe(localizedLabel
);
3155 appendInvalidProperty(properties
, SEC_OTHER_NAME_KEY
,
3156 otherNameContent
, localized
);
3160 GeneralName ::= CHOICE {
3161 otherName [0] OtherName,
3162 rfc822Name [1] IA5String,
3163 dNSName [2] IA5String,
3164 x400Address [3] ORAddress,
3165 directoryName [4] Name,
3166 ediPartyName [5] EDIPartyName,
3167 uniformResourceIdentifier [6] IA5String,
3168 iPAddress [7] OCTET STRING,
3169 registeredID [8] OBJECT IDENTIFIER}
3171 EDIPartyName ::= SEQUENCE {
3172 nameAssigner [0] DirectoryString OPTIONAL,
3173 partyName [1] DirectoryString }
3175 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
3176 DERTag tag
, const DERItem
*generalName
, bool localized
) {
3178 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
3179 appendOtherNameContentProperty(properties
, generalName
, localized
);
3181 case ASN1_CONTEXT_SPECIFIC
| 1:
3183 appendStringContentProperty(properties
, SEC_EMAIL_ADDRESS_KEY
,
3184 generalName
, kCFStringEncodingASCII
, localized
);
3186 case ASN1_CONTEXT_SPECIFIC
| 2:
3188 appendStringContentProperty(properties
, SEC_DNS_NAME_KEY
, generalName
,
3189 kCFStringEncodingASCII
, localized
);
3191 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
3192 appendUnparsedProperty(properties
, SEC_X400_ADDRESS_KEY
, NULL
,
3193 generalName
, localized
);
3195 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
3197 CFArrayRef directory_plist
=
3198 createPropertiesForX501Name(CFGetAllocator(properties
),
3199 generalName
, localized
);
3200 appendProperty(properties
, kSecPropertyTypeSection
,
3201 SEC_DIRECTORY_NAME_KEY
, NULL
, directory_plist
, localized
);
3202 CFRelease(directory_plist
);
3205 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
3206 appendUnparsedProperty(properties
, SEC_EDI_PARTY_NAME_KEY
, NULL
,
3207 generalName
, localized
);
3209 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
3210 /* Technically I don't think this is valid, but there are certs out
3211 in the wild that use a constructed IA5String. In particular the
3212 VeriSign Time Stamping Authority CA.cer does this. */
3213 appendURLProperty(properties
, SEC_URI_KEY
, generalName
, localized
);
3215 case ASN1_CONTEXT_SPECIFIC
| 6:
3216 appendURLContentProperty(properties
, SEC_URI_KEY
, generalName
, localized
);
3218 case ASN1_CONTEXT_SPECIFIC
| 7:
3219 appendIPAddressContentProperty(properties
, SEC_IP_ADDRESS_KEY
,
3220 generalName
, localized
);
3222 case ASN1_CONTEXT_SPECIFIC
| 8:
3223 appendOIDProperty(properties
, SEC_REGISTERED_ID_KEY
, NULL
,
3224 generalName
, localized
);
3235 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
3236 const DERItem
*generalName
, bool localized
) {
3237 DERDecodedInfo generalNameContent
;
3238 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
3239 require_noerr_quiet(drtn
, badDER
);
3240 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
3241 &generalNameContent
.content
, localized
))
3244 appendInvalidProperty(properties
, SEC_GENERAL_NAME_KEY
,
3245 generalName
, localized
);
3250 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
3252 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
3253 const DERItem
*generalNamesContent
, bool localized
) {
3255 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
3256 require_noerr_quiet(drtn
, badDER
);
3257 DERDecodedInfo generalNameContent
;
3258 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
3260 if (!appendGeneralNameContentProperty(properties
,
3261 generalNameContent
.tag
, &generalNameContent
.content
, localized
)) {
3265 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3268 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
3269 generalNamesContent
, localized
);
3272 static void appendGeneralNames(CFMutableArrayRef properties
,
3273 const DERItem
*generalNames
, bool localized
) {
3274 DERDecodedInfo generalNamesContent
;
3275 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
3276 require_noerr_quiet(drtn
, badDER
);
3277 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
3279 appendGeneralNamesContent(properties
, &generalNamesContent
.content
,
3283 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
3284 generalNames
, localized
);
3288 BasicConstraints ::= SEQUENCE {
3289 cA BOOLEAN DEFAULT FALSE,
3290 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
3292 static void appendBasicConstraints(CFMutableArrayRef properties
,
3293 const DERItem
*extnValue
, bool localized
) {
3294 DERBasicConstraints basicConstraints
;
3295 DERReturn drtn
= DERParseSequence(extnValue
,
3296 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
3297 &basicConstraints
, sizeof(basicConstraints
));
3298 require_noerr_quiet(drtn
, badDER
);
3300 appendBooleanProperty(properties
, SEC_CERT_AUTHORITY_KEY
,
3301 &basicConstraints
.cA
, false, localized
);
3303 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
3304 appendIntegerProperty(properties
, SEC_PATH_LEN_CONSTRAINT_KEY
,
3305 &basicConstraints
.pathLenConstraint
, localized
);
3309 appendInvalidProperty(properties
, SEC_BASIC_CONSTRAINTS_KEY
,
3310 extnValue
, localized
);
3314 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
3316 * NameConstraints ::= SEQUENCE {
3317 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
3318 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
3320 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
3322 * GeneralSubtree ::= SEQUENCE {
3324 * minimum [0] BaseDistance DEFAULT 0,
3325 * maximum [1] BaseDistance OPTIONAL }
3327 * BaseDistance ::= INTEGER (0..MAX)
3329 static void appendNameConstraints(CFMutableArrayRef properties
,
3330 const DERItem
*extnValue
, bool localized
) {
3331 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3332 DERNameConstraints nc
;
3334 drtn
= DERParseSequence(extnValue
,
3335 DERNumNameConstraintsItemSpecs
,
3336 DERNameConstraintsItemSpecs
,
3338 require_noerr_quiet(drtn
, badDER
);
3339 if (nc
.permittedSubtrees
.length
) {
3341 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.permittedSubtrees
, &gsSeq
), badDER
);
3342 DERDecodedInfo gsContent
;
3343 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3344 DERGeneralSubtree derGS
;
3345 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3346 drtn
= DERParseSequenceContent(&gsContent
.content
,
3347 DERNumGeneralSubtreeItemSpecs
,
3348 DERGeneralSubtreeItemSpecs
,
3349 &derGS
, sizeof(derGS
));
3350 require_noerr_quiet(drtn
, badDER
);
3351 if (derGS
.minimum
.length
) {
3352 appendIntegerProperty(properties
, SEC_PERMITTED_MINIMUM_KEY
,
3353 &derGS
.minimum
, localized
);
3355 if (derGS
.maximum
.length
) {
3356 appendIntegerProperty(properties
, SEC_PERMITTED_MAXIMUM_KEY
,
3357 &derGS
.maximum
, localized
);
3359 if (derGS
.generalName
.length
) {
3360 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3361 &kCFTypeArrayCallBacks
);
3362 appendProperty(properties
, kSecPropertyTypeSection
,
3363 SEC_PERMITTED_NAME_KEY
, NULL
, base
, localized
);
3364 appendGeneralNameProperty(base
, &derGS
.generalName
, localized
);
3368 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3370 if (nc
.excludedSubtrees
.length
) {
3372 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.excludedSubtrees
, &gsSeq
), badDER
);
3373 DERDecodedInfo gsContent
;
3374 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3375 DERGeneralSubtree derGS
;
3376 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3377 drtn
= DERParseSequenceContent(&gsContent
.content
,
3378 DERNumGeneralSubtreeItemSpecs
,
3379 DERGeneralSubtreeItemSpecs
,
3380 &derGS
, sizeof(derGS
));
3381 require_noerr_quiet(drtn
, badDER
);
3382 if (derGS
.minimum
.length
) {
3383 appendIntegerProperty(properties
, SEC_EXCLUDED_MINIMUM_KEY
,
3384 &derGS
.minimum
, localized
);
3386 if (derGS
.maximum
.length
) {
3387 appendIntegerProperty(properties
, SEC_EXCLUDED_MAXIMUM_KEY
,
3388 &derGS
.maximum
, localized
);
3390 if (derGS
.generalName
.length
) {
3391 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3392 &kCFTypeArrayCallBacks
);
3393 appendProperty(properties
, kSecPropertyTypeSection
,
3394 SEC_EXCLUDED_NAME_KEY
, NULL
, base
, localized
);
3395 appendGeneralNameProperty(base
, &derGS
.generalName
, localized
);
3399 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3404 appendInvalidProperty(properties
, SEC_NAME_CONSTRAINTS_KEY
,
3405 extnValue
, localized
);
3409 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
3411 DistributionPoint ::= SEQUENCE {
3412 distributionPoint [0] DistributionPointName OPTIONAL,
3413 reasons [1] ReasonFlags OPTIONAL,
3414 cRLIssuer [2] GeneralNames OPTIONAL }
3416 DistributionPointName ::= CHOICE {
3417 fullName [0] GeneralNames,
3418 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
3420 ReasonFlags ::= BIT STRING {
3424 affiliationChanged (3),
3426 cessationOfOperation (5),
3427 certificateHold (6),
3428 privilegeWithdrawn (7),
3431 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
3432 const DERItem
*extnValue
, bool localized
) {
3433 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3436 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
3437 require_noerr_quiet(drtn
, badDER
);
3438 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3439 DERDecodedInfo dpSeqContent
;
3440 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
3441 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3442 DERDistributionPoint dp
;
3443 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
3444 DERNumDistributionPointItemSpecs
,
3445 DERDistributionPointItemSpecs
,
3447 require_noerr_quiet(drtn
, badDER
);
3448 if (dp
.distributionPoint
.length
) {
3449 DERDecodedInfo distributionPointName
;
3450 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
3451 require_noerr_quiet(drtn
, badDER
);
3452 if (distributionPointName
.tag
==
3453 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
3455 appendGeneralNamesContent(properties
,
3456 &distributionPointName
.content
, localized
);
3457 } else if (distributionPointName
.tag
==
3458 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
3459 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
3460 &dp
.reasons
, localized
);
3461 appendProperty(properties
, kSecPropertyTypeSection
,
3462 SEC_NAME_REL_CRL_ISSUER_KEY
, NULL
, rdn_props
, localized
);
3463 CFRelease(rdn_props
);
3468 if (dp
.reasons
.length
) {
3469 static const CFStringRef reasonNames
[] = {
3471 SEC_KEY_COMPROMISE_KEY
,
3472 SEC_CA_COMPROMISE_KEY
,
3473 SEC_AFFILIATION_CHANGED_KEY
,
3475 SEC_CESSATION_OF_OPER_KEY
,
3476 SEC_CERTIFICATE_HOLD_KEY
,
3477 SEC_PRIV_WITHDRAWN_KEY
,
3478 SEC_AA_COMPROMISE_KEY
3480 appendBitStringContentNames(properties
, SEC_REASONS_KEY
,
3482 reasonNames
, array_size(reasonNames
), localized
);
3484 if (dp
.cRLIssuer
.length
) {
3485 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
3486 &kCFTypeArrayCallBacks
);
3487 appendProperty(properties
, kSecPropertyTypeSection
,
3488 SEC_CRL_ISSUER_KEY
, NULL
, crlIssuer
, localized
);
3489 CFRelease(crlIssuer
);
3490 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
, localized
);
3493 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3496 appendInvalidProperty(properties
, SEC_CRL_DISTR_POINTS_KEY
,
3497 extnValue
, localized
);
3501 Decode a sequence of integers into a comma separated list of ints.
3503 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
3504 CFStringRef label
, const DERItem
*intSequenceContent
,
3506 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3508 CFStringRef fmt
= NULL
, value
= NULL
, intDesc
= NULL
, v
= NULL
;
3509 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
3510 require_noerr_quiet(drtn
, badDER
);
3511 DERDecodedInfo intContent
;
3513 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
3514 require_quiet(fmt
, badDER
);
3515 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
)) == DR_Success
) {
3516 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
3517 intDesc
= copyIntegerContentDescription(
3518 allocator
, &intContent
.content
);
3519 require_quiet(intDesc
, badDER
);
3521 v
= CFStringCreateWithFormat(allocator
, NULL
, fmt
, value
, intDesc
);
3522 CFReleaseNull(value
);
3523 require_quiet(v
, badDER
);
3526 value
= CFStringCreateMutableCopy(allocator
, 0, intDesc
);
3527 require_quiet(value
, badDER
);
3529 CFReleaseNull(intDesc
);
3532 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3534 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3539 /* DROPTHOUGH if !value. */
3542 CFReleaseNull(intDesc
);
3543 CFReleaseNull(value
);
3544 appendInvalidProperty(properties
, label
, intSequenceContent
, localized
);
3547 static void appendCertificatePolicies(CFMutableArrayRef properties
,
3548 const DERItem
*extnValue
, bool localized
) {
3549 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3550 CFStringRef piLabel
= NULL
, piFmt
= NULL
, lpiLabel
= NULL
;
3551 CFStringRef pqLabel
= NULL
, pqFmt
= NULL
, lpqLabel
= NULL
;
3554 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
3555 require_noerr_quiet(drtn
, badDER
);
3556 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3557 DERDecodedInfo piContent
;
3559 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
3560 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3561 DERPolicyInformation pi
;
3562 drtn
= DERParseSequenceContent(&piContent
.content
,
3563 DERNumPolicyInformationItemSpecs
,
3564 DERPolicyInformationItemSpecs
,
3566 require_noerr_quiet(drtn
, badDER
);
3567 piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3568 SEC_POLICY_IDENTIFIER_KEY
, pin
);
3569 require_quiet(piLabel
, badDER
);
3570 piFmt
= (localized
) ?
3571 SecCopyCertString(SEC_POLICY_IDENTIFIER_KEY
) : SEC_POLICY_IDENTIFIER_KEY
;
3572 require_quiet(piFmt
, badDER
);
3573 lpiLabel
= CFStringCreateWithFormat(allocator
, NULL
, piFmt
, pin
++);
3574 require_quiet(lpiLabel
, badDER
);
3575 CFReleaseNull(piFmt
);
3576 appendOIDProperty(properties
, piLabel
, lpiLabel
,
3577 &pi
.policyIdentifier
, localized
);
3578 CFReleaseNull(piLabel
);
3579 CFReleaseNull(lpiLabel
);
3580 if (pi
.policyQualifiers
.length
== 0)
3584 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
3585 require_noerr_quiet(drtn
, badDER
);
3586 DERDecodedInfo pqContent
;
3588 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
3589 DERPolicyQualifierInfo pqi
;
3590 drtn
= DERParseSequenceContent(&pqContent
.content
,
3591 DERNumPolicyQualifierInfoItemSpecs
,
3592 DERPolicyQualifierInfoItemSpecs
,
3594 require_noerr_quiet(drtn
, badDER
);
3595 DERDecodedInfo qualifierContent
;
3596 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
3597 require_noerr_quiet(drtn
, badDER
);
3598 pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3599 SEC_POLICY_QUALIFIER_KEY
, pqn
);
3600 require_quiet(pqLabel
, badDER
);
3601 pqFmt
= (localized
) ?
3602 SecCopyCertString(SEC_POLICY_QUALIFIER_KEY
) : SEC_POLICY_QUALIFIER_KEY
;
3603 require_quiet(pqFmt
, badDER
);
3604 lpqLabel
= CFStringCreateWithFormat(allocator
, NULL
, pqFmt
, pqn
++);
3605 require_quiet(lpqLabel
, badDER
);
3606 CFReleaseNull(pqFmt
);
3607 appendOIDProperty(properties
, pqLabel
, lpqLabel
,
3608 &pqi
.policyQualifierID
, localized
);
3609 CFReleaseNull(pqLabel
);
3610 CFReleaseNull(lpqLabel
);
3611 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
3612 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
3613 appendURLContentProperty(properties
, SEC_CPS_URI_KEY
,
3614 &qualifierContent
.content
, localized
);
3615 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
3616 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3618 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
3619 DERNumUserNoticeItemSpecs
,
3620 DERUserNoticeItemSpecs
,
3622 require_noerr_quiet(drtn
, badDER
);
3623 if (un
.noticeRef
.length
) {
3624 DERNoticeReference nr
;
3625 drtn
= DERParseSequenceContent(&un
.noticeRef
,
3626 DERNumNoticeReferenceItemSpecs
,
3627 DERNoticeReferenceItemSpecs
,
3629 require_noerr_quiet(drtn
, badDER
);
3630 appendDERThingProperty(properties
,
3631 SEC_ORGANIZATION_KEY
, NULL
,
3632 &nr
.organization
, localized
);
3633 appendIntegerSequenceContent(properties
,
3634 SEC_NOTICE_NUMBERS_KEY
, &nr
.noticeNumbers
, localized
);
3636 if (un
.explicitText
.length
) {
3637 appendDERThingProperty(properties
, SEC_EXPLICIT_TEXT_KEY
,
3638 NULL
, &un
.explicitText
, localized
);
3641 appendUnparsedProperty(properties
, SEC_QUALIFIER_KEY
, NULL
,
3642 &pqi
.qualifier
, localized
);
3645 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3647 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3650 CFReleaseNull(piFmt
);
3651 CFReleaseNull(piLabel
);
3652 CFReleaseNull(lpiLabel
);
3653 CFReleaseNull(pqFmt
);
3654 CFReleaseNull(pqLabel
);
3655 CFReleaseNull(lpqLabel
);
3656 appendInvalidProperty(properties
, SEC_CERT_POLICIES_KEY
,
3657 extnValue
, localized
);
3660 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
3661 const DERItem
*extnValue
, bool localized
) {
3663 DERDecodedInfo keyIdentifier
;
3664 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
3665 require_noerr_quiet(drtn
, badDER
);
3666 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
3667 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3668 &keyIdentifier
.content
, localized
);
3672 appendInvalidProperty(properties
, SEC_SUBJ_KEY_ID_KEY
,
3673 extnValue
, localized
);
3677 AuthorityKeyIdentifier ::= SEQUENCE {
3678 keyIdentifier [0] KeyIdentifier OPTIONAL,
3679 authorityCertIssuer [1] GeneralNames OPTIONAL,
3680 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
3681 -- authorityCertIssuer and authorityCertSerialNumber MUST both
3682 -- be present or both be absent
3684 KeyIdentifier ::= OCTET STRING
3686 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
3687 const DERItem
*extnValue
, bool localized
) {
3688 DERAuthorityKeyIdentifier akid
;
3690 drtn
= DERParseSequence(extnValue
,
3691 DERNumAuthorityKeyIdentifierItemSpecs
,
3692 DERAuthorityKeyIdentifierItemSpecs
,
3693 &akid
, sizeof(akid
));
3694 require_noerr_quiet(drtn
, badDER
);
3695 if (akid
.keyIdentifier
.length
) {
3696 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3697 &akid
.keyIdentifier
, localized
);
3699 if (akid
.authorityCertIssuer
.length
||
3700 akid
.authorityCertSerialNumber
.length
) {
3701 require_quiet(akid
.authorityCertIssuer
.length
&&
3702 akid
.authorityCertSerialNumber
.length
, badDER
);
3703 /* Perhaps put in a subsection called Authority Certificate Issuer. */
3704 appendGeneralNamesContent(properties
,
3705 &akid
.authorityCertIssuer
, localized
);
3706 appendIntegerProperty(properties
, SEC_AUTH_CERT_SERIAL_KEY
,
3707 &akid
.authorityCertSerialNumber
, localized
);
3712 appendInvalidProperty(properties
, SEC_AUTHORITY_KEY_ID_KEY
,
3713 extnValue
, localized
);
3717 PolicyConstraints ::= SEQUENCE {
3718 requireExplicitPolicy [0] SkipCerts OPTIONAL,
3719 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
3721 SkipCerts ::= INTEGER (0..MAX)
3723 static void appendPolicyConstraints(CFMutableArrayRef properties
,
3724 const DERItem
*extnValue
, bool localized
) {
3725 DERPolicyConstraints pc
;
3727 drtn
= DERParseSequence(extnValue
,
3728 DERNumPolicyConstraintsItemSpecs
,
3729 DERPolicyConstraintsItemSpecs
,
3731 require_noerr_quiet(drtn
, badDER
);
3732 if (pc
.requireExplicitPolicy
.length
) {
3733 appendIntegerProperty(properties
, SEC_REQUIRE_EXPL_POLICY_KEY
,
3734 &pc
.requireExplicitPolicy
, localized
);
3736 if (pc
.inhibitPolicyMapping
.length
) {
3737 appendIntegerProperty(properties
, SEC_INHIBIT_POLICY_MAP_KEY
,
3738 &pc
.inhibitPolicyMapping
, localized
);
3744 appendInvalidProperty(properties
, SEC_POLICY_CONSTRAINTS_KEY
,
3745 extnValue
, localized
);
3749 extendedKeyUsage EXTENSION ::= {
3750 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
3751 IDENTIFIED BY id-ce-extKeyUsage }
3753 KeyPurposeId ::= OBJECT IDENTIFIER
3755 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
3756 const DERItem
*extnValue
, bool localized
) {
3759 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
3760 require_noerr_quiet(drtn
, badDER
);
3761 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3762 DERDecodedInfo currDecoded
;
3763 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3764 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
3765 appendOIDProperty(properties
, SEC_PURPOSE_KEY
, NULL
,
3766 &currDecoded
.content
, localized
);
3768 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3771 appendInvalidProperty(properties
, SEC_EXTENDED_KEY_USAGE_KEY
,
3772 extnValue
, localized
);
3776 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3778 AuthorityInfoAccessSyntax ::=
3779 SEQUENCE SIZE (1..MAX) OF AccessDescription
3781 AccessDescription ::= SEQUENCE {
3782 accessMethod OBJECT IDENTIFIER,
3783 accessLocation GeneralName }
3785 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3787 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3789 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3791 static void appendInfoAccess(CFMutableArrayRef properties
,
3792 const DERItem
*extnValue
, bool localized
) {
3795 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3796 require_noerr_quiet(drtn
, badDER
);
3797 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3798 DERDecodedInfo adContent
;
3799 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3800 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3801 DERAccessDescription ad
;
3802 drtn
= DERParseSequenceContent(&adContent
.content
,
3803 DERNumAccessDescriptionItemSpecs
,
3804 DERAccessDescriptionItemSpecs
,
3806 require_noerr_quiet(drtn
, badDER
);
3807 appendOIDProperty(properties
, SEC_ACCESS_METHOD_KEY
, NULL
,
3808 &ad
.accessMethod
, localized
);
3809 //TODO: Do something with SEC_ACCESS_LOCATION_KEY
3810 appendGeneralNameProperty(properties
, &ad
.accessLocation
, localized
);
3812 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3815 appendInvalidProperty(properties
, SEC_AUTH_INFO_ACCESS_KEY
,
3816 extnValue
, localized
);
3819 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3820 const DERItem
*extnValue
, bool localized
) {
3821 static const CFStringRef certTypes
[] = {
3825 SEC_OBJECT_SIGNING_KEY
,
3829 SEC_OBJECT_SIGNING_CA_KEY
3831 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3832 certTypes
, array_size(certTypes
), localized
);
3835 static bool appendPrintableDERSequence(CFMutableArrayRef properties
,
3836 CFStringRef label
, const DERItem
*sequence
, bool localized
) {
3839 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3840 require_noerr_quiet(drtn
, badSequence
);
3841 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3842 DERDecodedInfo currDecoded
;
3843 bool appendedSomething
= false;
3844 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3845 switch (currDecoded
.tag
)
3848 case ASN1_SEQUENCE
: // 16
3849 case ASN1_SET
: // 17
3850 // skip constructed object lengths
3853 case ASN1_UTF8_STRING
: // 12
3854 case ASN1_NUMERIC_STRING
: // 18
3855 case ASN1_PRINTABLE_STRING
: // 19
3856 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3857 case ASN1_VIDEOTEX_STRING
: // 21
3858 case ASN1_IA5_STRING
: // 22
3859 case ASN1_GRAPHIC_STRING
: // 25
3860 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3861 case ASN1_GENERAL_STRING
: // 27
3862 case ASN1_UNIVERSAL_STRING
: // 28
3864 CFStringRef string
=
3865 copyDERThingContentDescription(CFGetAllocator(properties
),
3866 currDecoded
.tag
, &currDecoded
.content
, false, localized
);
3867 require_quiet(string
, badSequence
);
3869 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3871 CFReleaseNull(string
);
3872 appendedSomething
= true;
3879 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3880 return appendedSomething
;
3885 static void appendExtension(CFMutableArrayRef parent
,
3886 const SecCertificateExtension
*extn
,
3888 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3889 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3890 &kCFTypeArrayCallBacks
);
3892 *extnID
= &extn
->extnID
,
3893 *extnValue
= &extn
->extnValue
;
3894 CFStringRef label
= NULL
;
3895 CFStringRef localizedLabel
= NULL
;
3897 appendBoolProperty(properties
, SEC_CRITICAL_KEY
, extn
->critical
, localized
);
3898 require_quiet(extnID
, xit
);
3900 bool handled
= true;
3901 /* Extensions that we know how to handle ourselves... */
3902 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3903 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3905 switch (extnID
->data
[extnID
->length
- 1]) {
3906 case 14: /* SubjectKeyIdentifier id-ce 14 */
3907 appendSubjectKeyIdentifier(properties
, extnValue
, localized
);
3909 case 15: /* KeyUsage id-ce 15 */
3910 appendKeyUsage(properties
, extnValue
, localized
);
3912 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3913 appendPrivateKeyUsagePeriod(properties
, extnValue
, localized
);
3915 case 17: /* SubjectAltName id-ce 17 */
3916 case 18: /* IssuerAltName id-ce 18 */
3917 appendGeneralNames(properties
, extnValue
, localized
);
3919 case 19: /* BasicConstraints id-ce 19 */
3920 appendBasicConstraints(properties
, extnValue
, localized
);
3922 case 30: /* NameConstraints id-ce 30 */
3923 appendNameConstraints(properties
, extnValue
, localized
);
3925 case 31: /* CRLDistributionPoints id-ce 31 */
3926 appendCrlDistributionPoints(properties
, extnValue
, localized
);
3928 case 32: /* CertificatePolicies id-ce 32 */
3929 appendCertificatePolicies(properties
, extnValue
, localized
);
3931 case 33: /* PolicyMappings id-ce 33 */
3934 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3935 appendAuthorityKeyIdentifier(properties
, extnValue
, localized
);
3937 case 36: /* PolicyConstraints id-ce 36 */
3938 appendPolicyConstraints(properties
, extnValue
, localized
);
3940 case 37: /* ExtKeyUsage id-ce 37 */
3941 appendExtendedKeyUsage(properties
, extnValue
, localized
);
3943 case 46: /* FreshestCRL id-ce 46 */
3946 case 54: /* InhibitAnyPolicy id-ce 54 */
3953 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3954 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3956 switch (extnID
->data
[extnID
->length
- 1]) {
3957 case 1: /* AuthorityInfoAccess id-pe 1 */
3958 appendInfoAccess(properties
, extnValue
, localized
);
3960 case 3: /* QCStatements id-pe 3 */
3963 case 11: /* SubjectInfoAccess id-pe 11 */
3964 appendInfoAccess(properties
, extnValue
, localized
);
3970 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3971 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3972 appendNetscapeCertType(properties
, extnValue
, localized
);
3978 /* Try to parse and display printable string(s). */
3979 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
, localized
)) {
3980 /* Nothing to do here appendPrintableDERSequence did the work. */
3982 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3983 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
, localized
);
3986 label
= SecDERItemCopyOIDDecimalRepresentation(allocator
, extnID
);
3987 localizedLabel
= copyOidDescription(allocator
, extnID
, localized
);
3988 appendProperty(parent
, kSecPropertyTypeSection
, label
, localizedLabel
,
3989 properties
, localized
);
3991 CFReleaseSafe(localizedLabel
);
3992 CFReleaseSafe(label
);
3993 CFReleaseSafe(properties
);
3996 /* Different types of summary types from least desired to most desired. */
3999 kSummaryTypePrintable
,
4000 kSummaryTypeOrganizationName
,
4001 kSummaryTypeOrganizationalUnitName
,
4002 kSummaryTypeCommonName
,
4006 enum SummaryType type
;
4007 CFStringRef summary
;
4008 CFStringRef description
;
4011 static OSStatus
obtainSummaryFromX501Name(void *context
,
4012 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
,
4014 struct Summary
*summary
= (struct Summary
*)context
;
4015 enum SummaryType stype
= kSummaryTypeNone
;
4016 CFStringRef string
= NULL
;
4017 if (DEROidCompare(type
, &oidCommonName
)) {
4018 stype
= kSummaryTypeCommonName
;
4019 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4020 stype
= kSummaryTypeOrganizationalUnitName
;
4021 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4022 stype
= kSummaryTypeOrganizationName
;
4023 } else if (DEROidCompare(type
, &oidDescription
)) {
4024 string
= copyDERThingDescription(kCFAllocatorDefault
, value
,
4027 if (summary
->description
) {
4028 CFStringRef fmt
= (localized
) ?
4029 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
4030 CFStringRef newDescription
= CFStringCreateWithFormat(kCFAllocatorDefault
,
4031 NULL
, fmt
, string
, summary
->description
);
4033 CFRelease(summary
->description
);
4034 summary
->description
= newDescription
;
4036 summary
->description
= string
;
4039 stype
= kSummaryTypePrintable
;
4042 stype
= kSummaryTypePrintable
;
4045 /* Build a string with all instances of the most desired
4046 component type in reverse order encountered comma separated list,
4047 The order of desirability is defined by enum SummaryType. */
4048 if (summary
->type
<= stype
) {
4050 string
= copyDERThingDescription(kCFAllocatorDefault
, value
,
4054 if (summary
->type
== stype
) {
4055 CFStringRef fmt
= (localized
) ?
4056 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
4057 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
,
4058 NULL
, fmt
, string
, summary
->summary
);
4061 string
= newSummary
;
4063 summary
->type
= stype
;
4065 CFReleaseSafe(summary
->summary
);
4066 summary
->summary
= string
;
4069 CFReleaseSafe(string
);
4072 return errSecSuccess
;
4075 CFStringRef
SecCertificateCopySubjectSummary(SecCertificateRef certificate
) {
4076 struct Summary summary
= {};
4077 OSStatus status
= parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
, true);
4078 if (status
!= errSecSuccess
) {
4081 /* If we found a description and a common name we change the summary to
4082 CommonName (Description). */
4083 if (summary
.description
) {
4084 if (summary
.type
== kSummaryTypeCommonName
) {
4085 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
4086 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
4088 CFRelease(summary
.summary
);
4089 summary
.summary
= newSummary
;
4091 CFRelease(summary
.description
);
4094 if (!summary
.summary
) {
4095 /* If we didn't find a suitable printable string in the subject at all, we try
4096 the first email address in the certificate instead. */
4097 CFArrayRef names
= SecCertificateCopyRFC822Names(certificate
);
4099 /* If we didn't find any email addresses in the certificate, we try finding
4100 a DNS name instead. */
4101 names
= SecCertificateCopyDNSNames(certificate
);
4104 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
4105 CFRetain(summary
.summary
);
4110 return summary
.summary
;
4113 CFStringRef
SecCertificateCopyIssuerSummary(SecCertificateRef certificate
) {
4114 struct Summary summary
= {};
4115 OSStatus status
= parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
, true);
4116 if (status
!= errSecSuccess
) {
4119 /* If we found a description and a common name we change the summary to
4120 CommonName (Description). */
4121 if (summary
.description
) {
4122 if (summary
.type
== kSummaryTypeCommonName
) {
4123 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
4124 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
4126 CFRelease(summary
.summary
);
4127 summary
.summary
= newSummary
;
4129 CFRelease(summary
.description
);
4132 return summary
.summary
;
4135 /* Return the earliest date on which all certificates in this chain are still
4137 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
4138 SecCertificateRef certificate
) {
4139 CFAbsoluteTime earliest
= certificate
->_notAfter
;
4141 while (certificate
->_parent
) {
4142 certificate
= certificate
->_parent
;
4143 if (earliest
> certificate
->_notAfter
)
4144 earliest
= certificate
->_notAfter
;
4151 /* Return the latest date on which all certificates in this chain will be
4153 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
4154 SecCertificateRef certificate
) {
4155 CFAbsoluteTime latest
= certificate
->_notBefore
;
4157 while (certificate
->_parent
) {
4158 certificate
= certificate
->_parent
;
4159 if (latest
< certificate
->_notBefore
)
4160 latest
= certificate
->_notBefore
;
4167 bool SecCertificateIsValid(SecCertificateRef certificate
,
4168 CFAbsoluteTime verifyTime
) {
4169 return certificate
&& certificate
->_notBefore
<= verifyTime
&&
4170 verifyTime
<= certificate
->_notAfter
;
4173 CFIndex
SecCertificateVersion(SecCertificateRef certificate
) {
4174 return certificate
->_version
+ 1;
4177 CFAbsoluteTime
SecCertificateNotValidBefore(SecCertificateRef certificate
) {
4178 return certificate
->_notBefore
;
4181 CFAbsoluteTime
SecCertificateNotValidAfter(SecCertificateRef certificate
) {
4182 return certificate
->_notAfter
;
4185 CFMutableArrayRef
SecCertificateCopySummaryProperties(
4186 SecCertificateRef certificate
, CFAbsoluteTime verifyTime
) {
4187 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4188 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
4189 &kCFTypeArrayCallBacks
);
4190 bool localized
= true;
4192 /* First we put the subject summary name. */
4193 CFStringRef ssummary
= SecCertificateCopySubjectSummary(certificate
);
4195 appendProperty(summary
, kSecPropertyTypeTitle
,
4196 NULL
, NULL
, ssummary
, localized
);
4197 CFRelease(ssummary
);
4200 /* Let see if this certificate is currently valid. */
4202 CFAbsoluteTime when
;
4203 CFStringRef message
;
4205 if (verifyTime
> certificate
->_notAfter
) {
4206 label
= SEC_EXPIRED_KEY
;
4207 when
= certificate
->_notAfter
;
4208 ptype
= kSecPropertyTypeError
;
4209 message
= SEC_CERT_EXPIRED_KEY
;
4210 } else if (certificate
->_notBefore
> verifyTime
) {
4211 label
= SEC_VALID_FROM_KEY
;
4212 when
= certificate
->_notBefore
;
4213 ptype
= kSecPropertyTypeError
;
4214 message
= SEC_CERT_NOT_YET_VALID_KEY
;
4216 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
4217 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
4218 if (verifyTime
> last
) {
4219 label
= SEC_EXPIRED_KEY
;
4221 ptype
= kSecPropertyTypeError
;
4222 message
= SEC_ISSUER_EXPIRED_KEY
;
4223 } else if (verifyTime
< first
) {
4224 label
= SEC_VALID_FROM_KEY
;
4226 ptype
= kSecPropertyTypeError
;
4227 message
= SEC_ISSR_NOT_YET_VALID_KEY
;
4229 label
= SEC_EXPIRES_KEY
;
4230 when
= certificate
->_notAfter
;
4231 ptype
= kSecPropertyTypeSuccess
;
4232 message
= SEC_CERT_VALID_KEY
;
4236 appendDateProperty(summary
, label
, when
, localized
);
4237 CFStringRef lmessage
= SecCopyCertString(message
);
4238 appendProperty(summary
, ptype
, NULL
, NULL
, lmessage
, localized
);
4239 CFRelease(lmessage
);
4244 CFArrayRef
SecCertificateCopyLegacyProperties(SecCertificateRef certificate
) {
4246 This function replicates the content returned by SecCertificateCopyProperties
4247 prior to 10.12.4, providing stable return values for SecCertificateCopyValues.
4248 Unlike SecCertificateCopyProperties, it does not cache the result and
4249 assumes the caller will do so.
4251 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4252 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
,
4253 0, &kCFTypeArrayCallBacks
);
4256 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
4257 &certificate
->_subject
, false);
4258 appendProperty(properties
, kSecPropertyTypeSection
, CFSTR("Subject Name"),
4259 NULL
, subject_plist
, false);
4260 CFRelease(subject_plist
);
4263 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
4264 &certificate
->_issuer
, false);
4265 appendProperty(properties
, kSecPropertyTypeSection
, CFSTR("Issuer Name"),
4266 NULL
, issuer_plist
, false);
4267 CFRelease(issuer_plist
);
4270 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
4271 NULL
, CFSTR("%d"), certificate
->_version
+ 1);
4272 appendProperty(properties
, kSecPropertyTypeString
, CFSTR("Version"),
4273 NULL
, versionString
, false);
4274 CFRelease(versionString
);
4277 if (certificate
->_serialNum
.length
) {
4278 appendIntegerProperty(properties
, CFSTR("Serial Number"),
4279 &certificate
->_serialNum
, false);
4282 /* Signature Algorithm */
4283 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
4284 &certificate
->_tbsSigAlg
, false);
4286 /* Validity dates */
4287 appendDateProperty(properties
, CFSTR("Not Valid Before"), certificate
->_notBefore
, false);
4288 appendDateProperty(properties
, CFSTR("Not Valid After"), certificate
->_notAfter
, false);
4290 if (certificate
->_subjectUniqueID
.length
) {
4291 appendDataProperty(properties
, CFSTR("Subject Unique ID"),
4292 NULL
, &certificate
->_subjectUniqueID
, false);
4294 if (certificate
->_issuerUniqueID
.length
) {
4295 appendDataProperty(properties
, CFSTR("Issuer Unique ID"),
4296 NULL
, &certificate
->_issuerUniqueID
, false);
4299 /* Public Key Algorithm */
4300 appendAlgorithmProperty(properties
, CFSTR("Public Key Algorithm"),
4301 &certificate
->_algId
, false);
4303 /* Public Key Data */
4304 appendDataProperty(properties
, CFSTR("Public Key Data"),
4305 NULL
, &certificate
->_pubKeyDER
, false);
4308 appendDataProperty(properties
, CFSTR("Signature"),
4309 NULL
, &certificate
->_signature
, false);
4313 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4314 appendExtension(properties
, &certificate
->_extensions
[ix
], false);
4318 appendFingerprintsProperty(properties
, CFSTR("Fingerprints"), certificate
, false);
4323 static CFArrayRef
CopyProperties(SecCertificateRef certificate
, Boolean localized
) {
4324 if (!certificate
->_properties
) {
4325 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4326 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
4327 &kCFTypeArrayCallBacks
);
4328 require_quiet(properties
, out
);
4330 /* First we put the Subject Name in the property list. */
4331 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
4332 &certificate
->_subject
,
4334 if (subject_plist
) {
4335 appendProperty(properties
, kSecPropertyTypeSection
,
4336 SEC_SUBJECT_NAME_KEY
, NULL
, subject_plist
, localized
);
4338 CFReleaseNull(subject_plist
);
4340 /* Next we put the Issuer Name in the property list. */
4341 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
4342 &certificate
->_issuer
,
4345 appendProperty(properties
, kSecPropertyTypeSection
,
4346 SEC_ISSUER_NAME_KEY
, NULL
, issuer_plist
, localized
);
4348 CFReleaseNull(issuer_plist
);
4351 CFStringRef fmt
= SecCopyCertString(SEC_CERT_VERSION_VALUE_KEY
);
4352 CFStringRef versionString
= NULL
;
4354 versionString
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
4355 certificate
->_version
+ 1);
4358 if (versionString
) {
4359 appendProperty(properties
, kSecPropertyTypeString
,
4360 SEC_VERSION_KEY
, NULL
, versionString
, localized
);
4362 CFReleaseNull(versionString
);
4365 appendSerialNumberProperty(properties
, SEC_SERIAL_NUMBER_KEY
, &certificate
->_serialNum
, localized
);
4367 /* Validity dates. */
4368 appendValidityPeriodProperty(properties
, SEC_VALIDITY_PERIOD_KEY
, certificate
, localized
);
4370 if (certificate
->_subjectUniqueID
.length
) {
4371 appendDataProperty(properties
, SEC_SUBJECT_UNIQUE_ID_KEY
, NULL
,
4372 &certificate
->_subjectUniqueID
, localized
);
4374 if (certificate
->_issuerUniqueID
.length
) {
4375 appendDataProperty(properties
, SEC_ISSUER_UNIQUE_ID_KEY
, NULL
,
4376 &certificate
->_issuerUniqueID
, localized
);
4379 appendPublicKeyProperty(properties
, SEC_PUBLIC_KEY_KEY
, certificate
, localized
);
4382 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4383 appendExtension(properties
, &certificate
->_extensions
[ix
], localized
);
4387 appendSignatureProperty(properties
, SEC_SIGNATURE_KEY
, certificate
, localized
);
4389 appendFingerprintsProperty(properties
, SEC_FINGERPRINTS_KEY
, certificate
, localized
);
4391 certificate
->_properties
= properties
;
4395 CFRetainSafe(certificate
->_properties
);
4396 return certificate
->_properties
;
4399 CFArrayRef
SecCertificateCopyProperties(SecCertificateRef certificate
) {
4401 Wrapper function which defaults to localized string properties
4402 for compatibility with prior releases.
4404 return CopyProperties(certificate
, true);
4407 CFArrayRef
SecCertificateCopyLocalizedProperties(SecCertificateRef certificate
, Boolean localized
) {
4409 Wrapper function which permits caller to specify whether
4410 localized string properties are used.
4412 return CopyProperties(certificate
, localized
);
4415 /* Unified serial number API */
4416 CFDataRef
SecCertificateCopySerialNumberData(
4417 SecCertificateRef certificate
,
4422 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
4426 if (certificate
->_serialNumber
) {
4427 CFRetain(certificate
->_serialNumber
);
4429 return certificate
->_serialNumber
;
4433 /* On iOS, the SecCertificateCopySerialNumber API takes one argument. */
4434 CFDataRef
SecCertificateCopySerialNumber(
4435 SecCertificateRef certificate
) {
4436 return SecCertificateCopySerialNumberData(certificate
, NULL
);
4440 CFDataRef
SecCertificateGetNormalizedIssuerContent(
4441 SecCertificateRef certificate
) {
4442 return certificate
->_normalizedIssuer
;
4445 CFDataRef
SecCertificateGetNormalizedSubjectContent(
4446 SecCertificateRef certificate
) {
4447 return certificate
->_normalizedSubject
;
4450 /* Verify that certificate was signed by issuerKey. */
4451 OSStatus
SecCertificateIsSignedBy(SecCertificateRef certificate
,
4452 SecKeyRef issuerKey
) {
4453 #pragma clang diagnostic push
4454 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
4455 /* Setup algId in SecAsn1AlgId format. */
4457 #pragma clang diagnostic pop
4458 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
4459 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
4460 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
4461 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
4463 /* RFC5280 4.1.1.2, 4.1.2.3 requires the actual signature algorithm
4464 must match the specified algorithm in the TBSCertificate. */
4465 bool sigAlgMatch
= DEROidCompare(&certificate
->_sigAlg
.oid
,
4466 &certificate
->_tbsSigAlg
.oid
);
4468 secwarning("Signature algorithm mismatch in certificate (see RFC5280 4.1.1.2)");
4471 CFErrorRef error
= NULL
;
4473 !SecVerifySignatureWithPublicKey(issuerKey
, &algId
,
4474 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
4475 certificate
->_signature
.data
, certificate
->_signature
.length
, &error
))
4477 #if !defined(NDEBUG)
4478 secdebug("verify", "signature verify failed: %" PRIdOSStatus
, (error
) ? (OSStatus
)CFErrorGetCode(error
) : errSecNotSigner
);
4480 CFReleaseSafe(error
);
4481 return errSecNotSigner
;
4484 return errSecSuccess
;
4487 const DERItem
* SecCertificateGetSubjectAltName(SecCertificateRef certificate
) {
4488 if (!certificate
->_subjectAltName
) {
4491 return &certificate
->_subjectAltName
->extnValue
;
4494 /* Convert IPv4 address string to canonical data format (4 bytes) */
4495 static bool convertIPv4Address(CFStringRef name
, CFDataRef
*dataIP
) {
4496 /* IPv4: 4 octets in decimal separated by dots. */
4497 bool result
= false;
4499 if (CFStringGetLength(name
) < 7 || /* min size is #.#.#.# */
4500 CFStringGetLength(name
) > 15) { /* max size is ###.###.###.### */
4504 CFCharacterSetRef allowed
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789."));
4505 CFCharacterSetRef disallowed
= CFCharacterSetCreateInvertedSet(NULL
, allowed
);
4506 CFMutableDataRef data
= CFDataCreateMutable(NULL
, 0);
4507 CFArrayRef parts
= CFStringCreateArrayBySeparatingStrings(NULL
, name
, CFSTR("."));
4508 CFIndex i
, count
= (parts
) ? CFArrayGetCount(parts
) : 0;
4510 /* Check character set */
4511 if (CFStringFindCharacterFromSet(name
, disallowed
,
4512 CFRangeMake(0, CFStringGetLength(name
)),
4513 kCFCompareForcedOrdering
, NULL
)) {
4517 /* Check number of labels */
4518 if (CFArrayGetCount(parts
) != 4) {
4522 /* Check each label and convert */
4523 for (i
= 0; i
< count
; i
++) {
4524 CFStringRef octet
= (CFStringRef
) CFArrayGetValueAtIndex(parts
, i
);
4525 char *cString
= CFStringToCString(octet
);
4526 uint32_t value
= atoi(cString
);
4531 uint8_t byte
= value
;
4532 CFDataAppendBytes(data
, &byte
, 1);
4537 *dataIP
= (CFDataRef
) CFRetain(data
);
4541 CFReleaseNull(data
);
4542 CFReleaseNull(parts
);
4543 CFReleaseNull(allowed
);
4544 CFReleaseNull(disallowed
);
4548 /* Convert IPv6 address string to canonical data format (16 bytes) */
4549 static bool convertIPv6Address(CFStringRef name
, CFDataRef
*dataIP
) {
4550 /* IPv6: 8 16-bit fields with colon delimiters. */
4551 /* Note: we don't support conversion of hybrid IPv4-mapped addresses here. */
4552 bool result
= false;
4553 CFMutableStringRef addr
= NULL
;
4554 CFIndex length
= (name
) ? CFStringGetLength(name
) : 0;
4555 /* Sanity check size */
4556 if (length
< 2 || /* min size is '::' */
4557 length
> 41) { /* max size is '[####:####:####:####:####:####:####:####]' */
4560 /* Remove literal brackets, if present */
4561 if (CFStringHasPrefix(name
, CFSTR("[")) && CFStringHasSuffix(name
, CFSTR("]"))) {
4562 CFStringRef tmpName
= CFStringCreateWithSubstring(NULL
, name
, CFRangeMake(1, length
-2));
4564 addr
= CFStringCreateMutableCopy(NULL
, 0, tmpName
);
4569 addr
= CFStringCreateMutableCopy(NULL
, 0, name
);
4571 CFStringUppercase(addr
, CFLocaleGetSystem());
4573 CFCharacterSetRef allowed
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789ABCDEF:"));
4574 CFCharacterSetRef disallowed
= CFCharacterSetCreateInvertedSet(NULL
, allowed
);
4575 CFMutableDataRef data
= CFDataCreateMutable(NULL
, 0);
4576 CFArrayRef parts
= CFStringCreateArrayBySeparatingStrings(NULL
, addr
, CFSTR(":"));
4577 CFIndex i
, count
= (parts
) ? CFArrayGetCount(parts
) : 0;
4579 /* Check character set */
4580 if (CFStringFindCharacterFromSet(addr
, disallowed
,
4581 CFRangeMake(0, CFStringGetLength(addr
)),
4582 kCFCompareForcedOrdering
, NULL
)) {
4586 /* Check number of fields (no fewer than 3, no more than 8) */
4587 if (CFArrayGetCount(parts
) < 3 || CFArrayGetCount(parts
) > 8) {
4591 /* Check each field and convert to network-byte-order value */
4592 for (i
= 0; i
< count
; i
++) {
4593 uint16_t svalue
= 0;
4594 CFStringRef fieldValue
= (CFStringRef
) CFArrayGetValueAtIndex(parts
, i
);
4595 char *cString
= CFStringToCString(fieldValue
);
4596 length
= (cString
) ? strlen(cString
) : 0;
4598 /* empty value indicates one or more zeros in the address */
4599 if (i
== 0 || i
== count
-1) { /* leading or trailing part of '::' */
4600 CFDataAppendBytes(data
, (const UInt8
*)&svalue
, 2);
4601 } else { /* determine how many fields are missing, then zero-fill */
4602 CFIndex z
, missing
= (8 - count
) + 1;
4603 for (z
= 0; z
< missing
; z
++) {
4604 CFDataAppendBytes(data
, (const UInt8
*)&svalue
, 2);
4607 } else if (length
<= 4) {
4608 /* valid field value is 4 characters or less */
4609 unsigned long value
= strtoul(cString
, NULL
, 16);
4610 svalue
= htons(value
& 0xFFFF);
4611 CFDataAppendBytes(data
, (const UInt8
*)&svalue
, 2);
4615 if (CFDataGetLength(data
) != IPv6ADDRLEN
) {
4616 goto out
; /* after expansion, data must be exactly 16 bytes */
4621 *dataIP
= (CFDataRef
) CFRetain(data
);
4625 CFReleaseNull(data
);
4626 CFReleaseNull(parts
);
4627 CFReleaseNull(allowed
);
4628 CFReleaseNull(disallowed
);
4629 CFReleaseNull(addr
);
4633 static bool convertIPAddress(CFStringRef string
, CFDataRef
*dataIP
) {
4634 if (NULL
== string
) {
4637 if (convertIPv4Address(string
, dataIP
) ||
4638 convertIPv6Address(string
, dataIP
)) {
4644 bool SecFrameworkIsIPAddress(CFStringRef string
) {
4645 return convertIPAddress(string
, NULL
);
4648 CFDataRef
SecFrameworkCopyIPAddressData(CFStringRef string
) {
4649 CFDataRef data
= NULL
;
4650 if (!convertIPAddress(string
, &data
)) {
4656 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
4657 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4658 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
4659 if (gnType
== GNT_IPAddress
) {
4660 CFStringRef string
= copyIPAddressContentDescription(
4661 kCFAllocatorDefault
, generalName
);
4663 CFArrayAppendValue(ipAddresses
, string
);
4666 return errSecInvalidCertificate
;
4669 return errSecSuccess
;
4672 CFArrayRef
SecCertificateCopyIPAddresses(SecCertificateRef certificate
) {
4673 /* These can only exist in the subject alt name. */
4674 if (!certificate
->_subjectAltName
)
4677 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
4678 0, &kCFTypeArrayCallBacks
);
4679 OSStatus status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4680 ipAddresses
, appendIPAddressesFromGeneralNames
);
4681 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
4682 CFRelease(ipAddresses
);
4688 static OSStatus
appendIPAddressesFromX501Name(void *context
, const DERItem
*type
,
4689 const DERItem
*value
, CFIndex rdnIX
,
4691 CFMutableArrayRef addrs
= (CFMutableArrayRef
)context
;
4692 if (DEROidCompare(type
, &oidCommonName
)) {
4693 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4694 value
, true, localized
);
4696 CFDataRef data
= NULL
;
4697 if (convertIPAddress(string
, &data
)) {
4698 CFArrayAppendValue(addrs
, data
);
4699 CFReleaseNull(data
);
4703 return errSecInvalidCertificate
;
4706 return errSecSuccess
;
4709 CFArrayRef
SecCertificateCopyIPAddressesFromSubject(SecCertificateRef certificate
) {
4710 CFMutableArrayRef addrs
= CFArrayCreateMutable(kCFAllocatorDefault
,
4711 0, &kCFTypeArrayCallBacks
);
4712 OSStatus status
= parseX501NameContent(&certificate
->_subject
, addrs
,
4713 appendIPAddressesFromX501Name
, true);
4714 if (status
|| CFArrayGetCount(addrs
) == 0) {
4715 CFReleaseNull(addrs
);
4721 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
4722 const DERItem
*generalName
) {
4723 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4724 if (gnType
== GNT_DNSName
) {
4725 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4726 generalName
->data
, generalName
->length
,
4727 kCFStringEncodingUTF8
, FALSE
);
4729 CFArrayAppendValue(dnsNames
, string
);
4732 return errSecInvalidCertificate
;
4735 return errSecSuccess
;
4738 /* Return true if the passed in string matches the
4739 Preferred name syntax from sections 2.3.1. in RFC 1035.
4740 With the added check that we disallow empty dns names.
4741 Also in order to support wildcard DNSNames we allow for the '*'
4742 character anywhere in a dns component where we currently allow
4745 <domain> ::= <subdomain> | " "
4747 <subdomain> ::= <label> | <subdomain> "." <label>
4749 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
4751 RFC 3696 redefined labels as:
4752 <label> ::= <let-dig> [ [ <ldh-str> ] <let-dig> ]
4753 with the caveat that the highest-level labels is never all-numeric.
4755 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
4757 <let-dig-hyp> ::= <let-dig> | "-"
4759 <let-dig> ::= <letter> | <digit>
4761 <letter> ::= any one of the 52 alphabetic characters A through Z in
4762 upper case and a through z in lower case
4764 <digit> ::= any one of the ten digits 0 through 9
4766 bool SecFrameworkIsDNSName(CFStringRef string
) {
4767 CFStringInlineBuffer buf
= {};
4768 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
4769 /* From RFC 1035 2.3.4. Size limits:
4770 labels 63 octets or less
4771 names 255 octets or less */
4772 require_quiet(length
<= 255, notDNS
);
4773 CFRange range
= { 0, length
};
4774 CFStringInitInlineBuffer(string
, &buf
, range
);
4778 kDNSStateAfterAlpha
,
4779 kDNSStateAfterDigit
,
4781 } state
= kDNSStateInital
;
4782 Boolean labelHasAlpha
= false;
4784 for (ix
= 0; ix
< length
; ++ix
) {
4785 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
4788 require_quiet(labelLength
<= 64 &&
4789 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4791 state
= kDNSStateAfterDot
;
4792 labelHasAlpha
= false;
4794 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
4796 state
= kDNSStateAfterAlpha
;
4797 labelHasAlpha
= true;
4798 } else if ('0' <= ch
&& ch
<= '9') {
4799 state
= kDNSStateAfterDigit
;
4800 } else if (ch
== '-') {
4801 require_quiet(state
== kDNSStateAfterAlpha
||
4802 state
== kDNSStateAfterDigit
||
4803 state
== kDNSStateAfterDash
, notDNS
);
4804 state
= kDNSStateAfterDash
;
4810 /* We don't allow a dns name to end in a dot or dash. */
4811 require_quiet(labelLength
<= 63 &&
4812 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4815 /* Additionally, the rightmost label must have letters in it. */
4816 require_quiet(labelHasAlpha
== true, notDNS
);
4823 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4824 const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4825 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4826 if (DEROidCompare(type
, &oidCommonName
)) {
4827 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4828 value
, true, localized
);
4830 if (SecFrameworkIsDNSName(string
)) {
4831 /* We found a common name that is formatted like a valid
4833 CFArrayAppendValue(dnsNames
, string
);
4837 return errSecInvalidCertificate
;
4840 return errSecSuccess
;
4843 CFArrayRef
SecCertificateCopyDNSNamesFromSubject(SecCertificateRef certificate
) {
4844 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4845 0, &kCFTypeArrayCallBacks
);
4846 OSStatus status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4847 appendDNSNamesFromX501Name
, true);
4848 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4849 CFReleaseNull(dnsNames
);
4853 /* appendDNSNamesFromX501Name allows IP addresses, we don't want those for this function */
4854 __block CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4855 CFArrayForEach(dnsNames
, ^(const void *value
) {
4856 CFStringRef name
= (CFStringRef
)value
;
4857 if (!convertIPAddress(name
, NULL
)) {
4858 CFArrayAppendValue(result
, name
);
4861 CFReleaseNull(dnsNames
);
4862 if (CFArrayGetCount(result
) == 0) {
4863 CFReleaseNull(result
);
4869 CFArrayRef
SecCertificateCopyDNSNamesFromSAN(SecCertificateRef certificate
) {
4870 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4871 0, &kCFTypeArrayCallBacks
);
4872 OSStatus status
= errSecSuccess
;
4873 if (certificate
->_subjectAltName
) {
4874 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4875 dnsNames
, appendDNSNamesFromGeneralNames
);
4878 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4879 CFReleaseNull(dnsNames
);
4884 /* Not everything returned by this function is going to be a proper DNS name,
4885 we also return the certificates common name entries from the subject,
4886 assuming they look like dns names as specified in RFC 1035. */
4887 CFArrayRef
SecCertificateCopyDNSNames(SecCertificateRef certificate
) {
4888 /* These can exist in the subject alt name or in the subject. */
4889 CFArrayRef sanNames
= SecCertificateCopyDNSNamesFromSAN(certificate
);
4890 if (sanNames
&& CFArrayGetCount(sanNames
) > 0) {
4893 CFReleaseNull(sanNames
);
4895 /* RFC 2818 section 3.1. Server Identity
4897 If a subjectAltName extension of type dNSName is present, that MUST
4898 be used as the identity. Otherwise, the (most specific) Common Name
4899 field in the Subject field of the certificate MUST be used. Although
4900 the use of the Common Name is existing practice, it is deprecated and
4901 Certification Authorities are encouraged to use the dNSName instead.
4904 This implies that if we found 1 or more DNSNames in the
4905 subjectAltName, we should not use the Common Name of the subject as
4909 /* To preserve bug for bug compatibility, we can't use SecCertificateCopyDNSNamesFromSubject
4910 * because that function filters out IP Addresses. This function is Private, but
4911 * SecCertificateCopyValues uses it and that's Public. */
4912 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4913 0, &kCFTypeArrayCallBacks
);
4914 OSStatus status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4915 appendDNSNamesFromX501Name
, true);
4916 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4917 CFReleaseNull(dnsNames
);
4922 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4923 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4924 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4925 if (gnType
== GNT_RFC822Name
) {
4926 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4927 generalName
->data
, generalName
->length
,
4928 kCFStringEncodingASCII
, FALSE
);
4930 CFArrayAppendValue(dnsNames
, string
);
4933 return errSecInvalidCertificate
;
4936 return errSecSuccess
;
4939 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4940 const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4941 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4942 if (DEROidCompare(type
, &oidEmailAddress
)) {
4943 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4944 value
, true, localized
);
4946 CFArrayAppendValue(dnsNames
, string
);
4949 return errSecInvalidCertificate
;
4952 return errSecSuccess
;
4955 CFArrayRef
SecCertificateCopyRFC822Names(SecCertificateRef certificate
) {
4956 /* These can exist in the subject alt name or in the subject. */
4957 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4958 0, &kCFTypeArrayCallBacks
);
4959 OSStatus status
= errSecSuccess
;
4960 if (certificate
->_subjectAltName
) {
4961 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4962 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4965 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4966 appendRFC822NamesFromX501Name
, true);
4968 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4969 CFRelease(rfc822Names
);
4975 OSStatus
SecCertificateCopyEmailAddresses(SecCertificateRef certificate
, CFArrayRef
* __nonnull CF_RETURNS_RETAINED emailAddresses
) {
4976 if (!certificate
|| !emailAddresses
) {
4979 *emailAddresses
= SecCertificateCopyRFC822Names(certificate
);
4980 if (*emailAddresses
== NULL
) {
4981 *emailAddresses
= CFArrayCreate(NULL
, NULL
, 0, &kCFTypeArrayCallBacks
);
4983 return errSecSuccess
;
4986 CFArrayRef
SecCertificateCopyRFC822NamesFromSubject(SecCertificateRef certificate
) {
4987 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4988 0, &kCFTypeArrayCallBacks
);
4989 OSStatus status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4990 appendRFC822NamesFromX501Name
, true);
4991 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4992 CFRelease(rfc822Names
);
4998 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4999 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5000 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
5001 if (DEROidCompare(type
, &oidCommonName
)) {
5002 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
5003 value
, true, localized
);
5005 CFArrayAppendValue(commonNames
, string
);
5008 return errSecInvalidCertificate
;
5011 return errSecSuccess
;
5014 CFArrayRef
SecCertificateCopyCommonNames(SecCertificateRef certificate
) {
5015 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
5016 0, &kCFTypeArrayCallBacks
);
5018 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
5019 appendCommonNamesFromX501Name
, true);
5020 if (status
|| CFArrayGetCount(commonNames
) == 0) {
5021 CFRelease(commonNames
);
5027 OSStatus
SecCertificateCopyCommonName(SecCertificateRef certificate
, CFStringRef
*commonName
)
5032 CFArrayRef commonNames
= SecCertificateCopyCommonNames(certificate
);
5034 return errSecInternal
;
5038 CFIndex count
= CFArrayGetCount(commonNames
);
5039 *commonName
= CFRetainSafe(CFArrayGetValueAtIndex(commonNames
, count
-1));
5041 CFReleaseSafe(commonNames
);
5042 return errSecSuccess
;
5045 static OSStatus
appendOrganizationFromX501Name(void *context
,
5046 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5047 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
5048 if (DEROidCompare(type
, &oidOrganizationName
)) {
5049 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
5050 value
, true, localized
);
5052 CFArrayAppendValue(organization
, string
);
5055 return errSecInvalidCertificate
;
5058 return errSecSuccess
;
5061 CFArrayRef
SecCertificateCopyOrganizationFromX501NameContent(const DERItem
*nameContent
) {
5062 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
5063 0, &kCFTypeArrayCallBacks
);
5065 status
= parseX501NameContent(nameContent
, organization
,
5066 appendOrganizationFromX501Name
, true);
5067 if (status
|| CFArrayGetCount(organization
) == 0) {
5068 CFRelease(organization
);
5069 organization
= NULL
;
5071 return organization
;
5074 CFArrayRef
SecCertificateCopyOrganization(SecCertificateRef certificate
) {
5075 return SecCertificateCopyOrganizationFromX501NameContent(&certificate
->_subject
);
5078 static OSStatus
appendOrganizationalUnitFromX501Name(void *context
,
5079 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5080 CFMutableArrayRef organizationalUnit
= (CFMutableArrayRef
)context
;
5081 if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
5082 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
5083 value
, true, localized
);
5085 CFArrayAppendValue(organizationalUnit
, string
);
5088 return errSecInvalidCertificate
;
5091 return errSecSuccess
;
5094 CFArrayRef
SecCertificateCopyOrganizationalUnit(SecCertificateRef certificate
) {
5095 CFMutableArrayRef organizationalUnit
= CFArrayCreateMutable(kCFAllocatorDefault
,
5096 0, &kCFTypeArrayCallBacks
);
5098 status
= parseX501NameContent(&certificate
->_subject
, organizationalUnit
,
5099 appendOrganizationalUnitFromX501Name
, true);
5100 if (status
|| CFArrayGetCount(organizationalUnit
) == 0) {
5101 CFRelease(organizationalUnit
);
5102 organizationalUnit
= NULL
;
5104 return organizationalUnit
;
5107 static OSStatus
appendCountryFromX501Name(void *context
,
5108 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5109 CFMutableArrayRef countries
= (CFMutableArrayRef
)context
;
5110 if (DEROidCompare(type
, &oidCountryName
)) {
5111 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
5112 value
, true, localized
);
5114 CFArrayAppendValue(countries
, string
);
5117 return errSecInvalidCertificate
;
5120 return errSecSuccess
;
5123 CFArrayRef
SecCertificateCopyCountry(SecCertificateRef certificate
) {
5124 CFMutableArrayRef countries
= CFArrayCreateMutable(kCFAllocatorDefault
,
5125 0, &kCFTypeArrayCallBacks
);
5127 status
= parseX501NameContent(&certificate
->_subject
, countries
,
5128 appendCountryFromX501Name
, true);
5129 if (status
|| CFArrayGetCount(countries
) == 0) {
5130 CFRelease(countries
);
5136 const SecCEBasicConstraints
*
5137 SecCertificateGetBasicConstraints(SecCertificateRef certificate
) {
5138 if (certificate
->_basicConstraints
.present
)
5139 return &certificate
->_basicConstraints
;
5144 CFArrayRef
SecCertificateGetPermittedSubtrees(SecCertificateRef certificate
) {
5145 return (certificate
->_permittedSubtrees
);
5148 CFArrayRef
SecCertificateGetExcludedSubtrees(SecCertificateRef certificate
) {
5149 return (certificate
->_excludedSubtrees
);
5152 const SecCEPolicyConstraints
*
5153 SecCertificateGetPolicyConstraints(SecCertificateRef certificate
) {
5154 if (certificate
->_policyConstraints
.present
)
5155 return &certificate
->_policyConstraints
;
5160 const SecCEPolicyMappings
*
5161 SecCertificateGetPolicyMappings(SecCertificateRef certificate
) {
5162 if (certificate
->_policyMappings
.present
) {
5163 return &certificate
->_policyMappings
;
5169 const SecCECertificatePolicies
*
5170 SecCertificateGetCertificatePolicies(SecCertificateRef certificate
) {
5171 if (certificate
->_certificatePolicies
.present
)
5172 return &certificate
->_certificatePolicies
;
5177 const SecCEInhibitAnyPolicy
*
5178 SecCertificateGetInhibitAnyPolicySkipCerts(SecCertificateRef certificate
) {
5179 if (certificate
->_inhibitAnyPolicySkipCerts
.present
) {
5180 return &certificate
->_inhibitAnyPolicySkipCerts
;
5186 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
5187 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
5188 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
5189 if (gnType
== GNT_OtherName
) {
5191 DERReturn drtn
= DERParseSequenceContent(generalName
,
5192 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
5194 require_noerr_quiet(drtn
, badDER
);
5195 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
5197 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
5198 &on
.value
, true, true), badDER
);
5199 CFArrayAppendValue(ntPrincipalNames
, string
);
5203 return errSecSuccess
;
5206 return errSecInvalidCertificate
;
5210 CFArrayRef
SecCertificateCopyNTPrincipalNames(SecCertificateRef certificate
) {
5211 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
5212 0, &kCFTypeArrayCallBacks
);
5213 OSStatus status
= errSecSuccess
;
5214 if (certificate
->_subjectAltName
) {
5215 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
5216 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
5218 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
5219 CFRelease(ntPrincipalNames
);
5220 ntPrincipalNames
= NULL
;
5222 return ntPrincipalNames
;
5225 static OSStatus
appendToRFC2253String(void *context
,
5226 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5227 CFMutableStringRef string
= (CFMutableStringRef
)context
;
5231 ST stateOrProvinceName
5233 OU organizationalUnitName
5235 STREET streetAddress
5239 /* Prepend a + if this is not the first RDN in an RDN set.
5240 Otherwise prepend a , if this is not the first RDN. */
5242 CFStringAppend(string
, CFSTR("+"));
5243 else if (CFStringGetLength(string
)) {
5244 CFStringAppend(string
, CFSTR(","));
5247 CFStringRef label
, oid
= NULL
;
5248 /* @@@ Consider changing this to a dictionary lookup keyed by the
5249 decimal representation. */
5250 if (DEROidCompare(type
, &oidCommonName
)) {
5251 label
= CFSTR("CN");
5252 } else if (DEROidCompare(type
, &oidLocalityName
)) {
5254 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
5255 label
= CFSTR("ST");
5256 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
5258 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
5259 label
= CFSTR("OU");
5260 } else if (DEROidCompare(type
, &oidCountryName
)) {
5263 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
5264 label
= CFSTR("STREET");
5265 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
5266 label
= CFSTR("DC");
5267 } else if (DEROidCompare(type
, &oidUserID
)) {
5268 label
= CFSTR("UID");
5271 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
5274 CFStringAppend(string
, label
);
5275 CFStringAppend(string
, CFSTR("="));
5276 CFStringRef raw
= NULL
;
5278 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true, localized
);
5281 /* Append raw to string while escaping:
5282 a space or "#" character occurring at the beginning of the string
5283 a space character occurring at the end of the string
5284 one of the characters ",", "+", """, "\", "<", ">" or ";"
5286 CFStringInlineBuffer buffer
= {};
5287 CFIndex ix
, length
= CFStringGetLength(raw
);
5288 CFRange range
= { 0, length
};
5289 CFStringInitInlineBuffer(raw
, &buffer
, range
);
5290 for (ix
= 0; ix
< length
; ++ix
) {
5291 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
5293 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
5294 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
5295 ch
== '<' || ch
== '>' || ch
== ';' ||
5296 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
5297 (ch
== '#' && ix
== 0)) {
5298 UniChar chars
[] = { '\\', ch
};
5299 CFStringAppendCharacters(string
, chars
, 2);
5301 CFStringAppendCharacters(string
, &ch
, 1);
5306 /* Append the value in hex. */
5307 CFStringAppend(string
, CFSTR("#"));
5309 for (ix
= 0; ix
< value
->length
; ++ix
)
5310 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
5315 return errSecSuccess
;
5318 CFStringRef
SecCertificateCopySubjectString(SecCertificateRef certificate
) {
5319 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
5320 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
, true);
5321 if (status
|| CFStringGetLength(string
) == 0) {
5328 static OSStatus
appendToCompanyNameString(void *context
,
5329 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5330 CFMutableStringRef string
= (CFMutableStringRef
)context
;
5331 if (CFStringGetLength(string
) != 0)
5332 return errSecSuccess
;
5334 if (!DEROidCompare(type
, &oidOrganizationName
))
5335 return errSecSuccess
;
5338 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true, localized
);
5340 return errSecSuccess
;
5341 CFStringAppend(string
, raw
);
5344 return errSecSuccess
;
5347 CFStringRef
SecCertificateCopyCompanyName(SecCertificateRef certificate
) {
5348 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
5349 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
5350 appendToCompanyNameString
, true);
5351 if (status
|| CFStringGetLength(string
) == 0) {
5358 CFDataRef
SecCertificateCopyIssuerSequence(
5359 SecCertificateRef certificate
) {
5360 return SecDERItemCopySequence(&certificate
->_issuer
);
5363 CFDataRef
SecCertificateCopySubjectSequence(
5364 SecCertificateRef certificate
) {
5365 return SecDERItemCopySequence(&certificate
->_subject
);
5368 CFDataRef
SecCertificateCopyNormalizedIssuerSequence(SecCertificateRef certificate
) {
5369 if (!certificate
|| !certificate
->_normalizedIssuer
) {
5372 return SecCopySequenceFromContent(certificate
->_normalizedIssuer
);
5375 CFDataRef
SecCertificateCopyNormalizedSubjectSequence(SecCertificateRef certificate
) {
5376 if (!certificate
|| !certificate
->_normalizedSubject
) {
5379 return SecCopySequenceFromContent(certificate
->_normalizedSubject
);
5382 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithm(
5383 SecCertificateRef certificate
) {
5384 return &certificate
->_algId
;
5387 const DERItem
*SecCertificateGetPublicKeyData(SecCertificateRef certificate
) {
5388 return &certificate
->_pubKeyDER
;
5392 /* There is already a SecCertificateCopyPublicKey with different args on OS X,
5393 so we will refer to this one internally as SecCertificateCopyPublicKey_ios.
5395 __nullable SecKeyRef
SecCertificateCopyPublicKey_ios(SecCertificateRef certificate
)
5397 __nullable SecKeyRef
SecCertificateCopyPublicKey(SecCertificateRef certificate
)
5400 return SecCertificateCopyKey(certificate
);
5403 SecKeyRef
SecCertificateCopyKey(SecCertificateRef certificate
) {
5404 if (certificate
->_pubKey
== NULL
) {
5405 const DERAlgorithmId
*algId
=
5406 SecCertificateGetPublicKeyAlgorithm(certificate
);
5407 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
5408 const DERItem
*params
= NULL
;
5409 if (algId
->params
.length
!= 0) {
5410 params
= &algId
->params
;
5412 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
5413 SecAsn1Item params1
= {
5414 .Data
= params
? params
->data
: NULL
,
5415 .Length
= params
? params
->length
: 0
5417 SecAsn1Item keyData1
= {
5418 .Data
= keyData
? keyData
->data
: NULL
,
5419 .Length
= keyData
? keyData
->length
: 0
5421 certificate
->_pubKey
= SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
5425 return CFRetainSafe(certificate
->_pubKey
);
5428 static CFIndex
SecCertificateGetPublicKeyAlgorithmIdAndSize(SecCertificateRef certificate
, size_t *keySizeInBytes
) {
5429 CFIndex keyAlgID
= kSecNullAlgorithmID
;
5432 SecKeyRef pubKey
= NULL
;
5433 require_quiet(certificate
, out
);
5434 require_quiet(pubKey
= SecCertificateCopyKey(certificate
) ,out
);
5435 size
= SecKeyGetBlockSize(pubKey
);
5436 keyAlgID
= SecKeyGetAlgorithmId(pubKey
);
5439 CFReleaseNull(pubKey
);
5440 if (keySizeInBytes
) { *keySizeInBytes
= size
; }
5445 * Public keys in certificates may be considered "weak" or "strong" or neither
5446 * (that is, in between). Certificates using weak keys are not trusted at all.
5447 * Certificates using neither strong nor weak keys are only trusted in certain
5448 * contexts. SecPolicy and SecPolicyServer define the contexts by which we enforce
5449 * these (or stronger) key size trust policies.
5451 bool SecCertificateIsWeakKey(SecCertificateRef certificate
) {
5452 if (!certificate
) { return true; }
5456 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5457 case kSecRSAAlgorithmID
:
5458 if (MIN_RSA_KEY_SIZE
<= size
) weak
= false;
5460 case kSecECDSAAlgorithmID
:
5461 if (MIN_EC_KEY_SIZE
<= size
) weak
= false;
5469 bool SecCertificateIsStrongKey(SecCertificateRef certificate
) {
5470 if (!certificate
) { return false; }
5472 bool strong
= false;
5474 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5475 case kSecRSAAlgorithmID
:
5476 if (MIN_STRONG_RSA_KEY_SIZE
<= size
) strong
= true;
5478 case kSecECDSAAlgorithmID
:
5479 if (MIN_STRONG_EC_KEY_SIZE
<= size
) strong
= true;
5487 bool SecCertificateIsWeakHash(SecCertificateRef certificate
) {
5488 if (!certificate
) { return true; }
5489 SecSignatureHashAlgorithm certAlg
= 0;
5490 certAlg
= SecCertificateGetSignatureHashAlgorithm(certificate
);
5491 if (certAlg
== kSecSignatureHashAlgorithmUnknown
||
5492 certAlg
== kSecSignatureHashAlgorithmMD2
||
5493 certAlg
== kSecSignatureHashAlgorithmMD4
||
5494 certAlg
== kSecSignatureHashAlgorithmMD5
||
5495 certAlg
== kSecSignatureHashAlgorithmSHA1
) {
5501 bool SecCertificateIsAtLeastMinKeySize(SecCertificateRef certificate
,
5502 CFDictionaryRef keySizes
) {
5503 if (!certificate
) { return false; }
5505 bool goodSize
= false;
5507 CFNumberRef minSize
;
5508 size_t minSizeInBits
;
5509 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5510 case kSecRSAAlgorithmID
:
5511 if(CFDictionaryGetValueIfPresent(keySizes
, kSecAttrKeyTypeRSA
, (const void**)&minSize
)
5512 && minSize
&& CFNumberGetValue(minSize
, kCFNumberLongType
, &minSizeInBits
)) {
5513 if (size
>= (size_t)(minSizeInBits
+7)/8) goodSize
= true;
5516 case kSecECDSAAlgorithmID
:
5517 if(CFDictionaryGetValueIfPresent(keySizes
, kSecAttrKeyTypeEC
, (const void**)&minSize
)
5518 && minSize
&& CFNumberGetValue(minSize
, kCFNumberLongType
, &minSizeInBits
)) {
5519 if (size
>= (size_t)(minSizeInBits
+7)/8) goodSize
= true;
5528 CFDataRef
SecCertificateGetSHA1Digest(SecCertificateRef certificate
) {
5529 if (!certificate
|| !certificate
->_der
.data
) {
5532 if (!certificate
->_sha1Digest
) {
5533 certificate
->_sha1Digest
=
5534 SecSHA1DigestCreate(CFGetAllocator(certificate
),
5535 certificate
->_der
.data
, certificate
->_der
.length
);
5537 return certificate
->_sha1Digest
;
5540 CFDataRef
SecCertificateCopySHA256Digest(SecCertificateRef certificate
) {
5541 if (!certificate
|| !certificate
->_der
.data
) {
5544 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
5545 certificate
->_der
.data
, certificate
->_der
.length
);
5548 CFDataRef
SecCertificateCopyIssuerSHA1Digest(SecCertificateRef certificate
) {
5549 CFDataRef digest
= NULL
;
5550 CFDataRef issuer
= SecCertificateCopyIssuerSequence(certificate
);
5552 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
5553 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
5559 CFDataRef
SecCertificateCopyPublicKeySHA1Digest(SecCertificateRef certificate
) {
5560 if (!certificate
|| !certificate
->_pubKeyDER
.data
) {
5563 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
5564 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
5567 static CFDataRef
SecCertificateCopySPKIEncoded(SecCertificateRef certificate
) {
5568 /* SPKI is saved without the tag/length by libDER, so we need to re-encode */
5569 if (!certificate
|| !certificate
->_subjectPublicKeyInfo
.data
) {
5572 DERSize size
= DERLengthOfItem(ASN1_CONSTR_SEQUENCE
, certificate
->_subjectPublicKeyInfo
.length
);
5573 if (size
< certificate
->_subjectPublicKeyInfo
.length
) {
5576 uint8_t *temp
= malloc(size
);
5580 DERReturn drtn
= DEREncodeItem(ASN1_CONSTR_SEQUENCE
,
5581 certificate
->_subjectPublicKeyInfo
.length
,
5582 certificate
->_subjectPublicKeyInfo
.data
,
5584 CFDataRef encodedSPKI
= NULL
;
5585 if (drtn
== DR_Success
) {
5586 encodedSPKI
= CFDataCreate(NULL
, temp
, size
);
5592 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA1Digest(SecCertificateRef certificate
) {
5593 CFDataRef encodedSPKI
= SecCertificateCopySPKIEncoded(certificate
);
5594 if (!encodedSPKI
) { return NULL
; }
5595 CFDataRef hash
= SecSHA1DigestCreate(CFGetAllocator(certificate
),
5596 CFDataGetBytePtr(encodedSPKI
),
5597 CFDataGetLength(encodedSPKI
));
5598 CFReleaseNull(encodedSPKI
);
5602 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA256Digest(SecCertificateRef certificate
) {
5603 CFDataRef encodedSPKI
= SecCertificateCopySPKIEncoded(certificate
);
5604 if (!encodedSPKI
) { return NULL
; }
5605 CFDataRef hash
= SecSHA256DigestCreate(CFGetAllocator(certificate
),
5606 CFDataGetBytePtr(encodedSPKI
),
5607 CFDataGetLength(encodedSPKI
));
5608 CFReleaseNull(encodedSPKI
);
5612 CFTypeRef
SecCertificateCopyKeychainItem(SecCertificateRef certificate
)
5617 CFRetainSafe(certificate
->_keychain_item
);
5618 return certificate
->_keychain_item
;
5621 CFDataRef
SecCertificateGetAuthorityKeyID(SecCertificateRef certificate
) {
5625 if (!certificate
->_authorityKeyID
&&
5626 certificate
->_authorityKeyIdentifier
.length
) {
5627 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
5628 certificate
->_authorityKeyIdentifier
.data
,
5629 certificate
->_authorityKeyIdentifier
.length
);
5632 return certificate
->_authorityKeyID
;
5635 CFDataRef
SecCertificateGetSubjectKeyID(SecCertificateRef certificate
) {
5639 if (!certificate
->_subjectKeyID
&&
5640 certificate
->_subjectKeyIdentifier
.length
) {
5641 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
5642 certificate
->_subjectKeyIdentifier
.data
,
5643 certificate
->_subjectKeyIdentifier
.length
);
5646 return certificate
->_subjectKeyID
;
5649 CFArrayRef
SecCertificateGetCRLDistributionPoints(SecCertificateRef certificate
) {
5653 return certificate
->_crlDistributionPoints
;
5656 CFArrayRef
SecCertificateGetOCSPResponders(SecCertificateRef certificate
) {
5660 return certificate
->_ocspResponders
;
5663 CFArrayRef
SecCertificateGetCAIssuers(SecCertificateRef certificate
) {
5667 return certificate
->_caIssuers
;
5670 bool SecCertificateHasCriticalSubjectAltName(SecCertificateRef certificate
) {
5674 return certificate
->_subjectAltName
&&
5675 certificate
->_subjectAltName
->critical
;
5678 bool SecCertificateHasSubject(SecCertificateRef certificate
) {
5682 /* Since the _subject field is the content of the subject and not the
5683 whole thing, we can simply check for a 0 length subject here. */
5684 return certificate
->_subject
.length
!= 0;
5687 bool SecCertificateHasUnknownCriticalExtension(SecCertificateRef certificate
) {
5691 return certificate
->_foundUnknownCriticalExtension
;
5694 /* Private API functions. */
5695 void SecCertificateShow(SecCertificateRef certificate
) {
5697 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
5698 fprintf(stderr
, "\n");
5702 CFDictionaryRef
SecCertificateCopyAttributeDictionary(
5703 SecCertificateRef certificate
) {
5704 if (!SecCertificateIsCertificate(certificate
)) {
5707 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
5708 CFNumberRef certificateType
= NULL
;
5709 CFNumberRef certificateEncoding
= NULL
;
5710 CFStringRef label
= NULL
;
5711 CFStringRef alias
= NULL
;
5712 CFDataRef skid
= NULL
;
5713 CFDataRef pubKeyDigest
= NULL
;
5714 CFDataRef certData
= NULL
;
5715 CFDictionaryRef dict
= NULL
;
5719 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
5720 SInt32 ctv
= certificate
->_version
+ 1;
5721 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
5722 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
5723 require_quiet(certificateType
!= NULL
, out
);
5724 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
5725 require_quiet(certificateEncoding
!= NULL
, out
);
5726 certData
= SecCertificateCopyData(certificate
);
5727 require_quiet(certData
!= NULL
, out
);
5728 skid
= SecCertificateGetSubjectKeyID(certificate
);
5729 require_quiet(certificate
->_pubKeyDER
.data
!= NULL
&& certificate
->_pubKeyDER
.length
> 0, out
);
5730 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
5731 certificate
->_pubKeyDER
.length
);
5732 require_quiet(pubKeyDigest
!= NULL
, out
);
5734 /* We still need to figure out how to deal with multi valued attributes. */
5735 alias
= SecCertificateCopyRFC822Names(certificate
);
5736 label
= SecCertificateCopySubjectSummary(certificate
);
5742 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
5743 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
5744 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
5746 DICT_ADDPAIR(kSecAttrLabel
, label
);
5749 DICT_ADDPAIR(kSecAttrAlias
, alias
);
5751 if (isData(certificate
->_normalizedSubject
)) {
5752 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
5754 require_quiet(isData(certificate
->_normalizedIssuer
), out
);
5755 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
5756 require_quiet(isData(certificate
->_serialNumber
), out
);
5757 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
5759 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
5761 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
5762 DICT_ADDPAIR(kSecValueData
, certData
);
5763 dict
= DICT_CREATE(allocator
);
5766 CFReleaseSafe(label
);
5767 CFReleaseSafe(alias
);
5768 CFReleaseSafe(pubKeyDigest
);
5769 CFReleaseSafe(certData
);
5770 CFReleaseSafe(certificateEncoding
);
5771 CFReleaseSafe(certificateType
);
5776 SecCertificateRef
SecCertificateCreateFromAttributeDictionary(
5777 CFDictionaryRef refAttributes
) {
5778 /* @@@ Support having an allocator in refAttributes. */
5779 CFAllocatorRef allocator
= NULL
;
5780 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
5781 return data
? SecCertificateCreateWithData(allocator
, data
) : NULL
;
5785 static bool _SecCertificateIsSelfSigned(SecCertificateRef certificate
) {
5786 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
5787 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
5788 SecKeyRef publicKey
= NULL
;
5789 require(SecCertificateIsCertificate(certificate
), out
);
5790 require(publicKey
= SecCertificateCopyKey(certificate
), out
);
5791 CFDataRef normalizedIssuer
=
5792 SecCertificateGetNormalizedIssuerContent(certificate
);
5793 CFDataRef normalizedSubject
=
5794 SecCertificateGetNormalizedSubjectContent(certificate
);
5795 require_quiet(normalizedIssuer
&& normalizedSubject
&&
5796 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
5798 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(certificate
);
5799 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyID(certificate
);
5800 if (authorityKeyID
) {
5801 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
5804 require_noerr_quiet(SecCertificateIsSignedBy(certificate
, publicKey
), out
);
5806 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
5808 CFReleaseSafe(publicKey
);
5811 return (certificate
->_isSelfSigned
== kSecSelfSignedTrue
);
5814 bool SecCertificateIsCA(SecCertificateRef certificate
) {
5815 bool result
= false;
5816 require(SecCertificateIsCertificate(certificate
), out
);
5817 if (SecCertificateVersion(certificate
) >= 3) {
5818 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraints(certificate
);
5819 result
= (basicConstraints
&& basicConstraints
->isCA
);
5822 result
= _SecCertificateIsSelfSigned(certificate
);
5828 bool SecCertificateIsSelfSignedCA(SecCertificateRef certificate
) {
5829 return (_SecCertificateIsSelfSigned(certificate
) && SecCertificateIsCA(certificate
));
5832 OSStatus
SecCertificateIsSelfSigned(SecCertificateRef certificate
, Boolean
*isSelfSigned
) {
5833 if (!SecCertificateIsCertificate(certificate
)) {
5834 return errSecInvalidCertificate
;
5836 if (!isSelfSigned
) {
5839 *isSelfSigned
= _SecCertificateIsSelfSigned(certificate
);
5840 return errSecSuccess
;
5843 SecKeyUsage
SecCertificateGetKeyUsage(SecCertificateRef certificate
) {
5845 return kSecKeyUsageUnspecified
;
5847 return certificate
->_keyUsage
;
5850 CFArrayRef
SecCertificateCopyExtendedKeyUsage(SecCertificateRef certificate
)
5852 CFMutableArrayRef extended_key_usage_oids
=
5853 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
5854 require_quiet(certificate
&& extended_key_usage_oids
, out
);
5856 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5857 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5858 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
5859 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
5862 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
5863 require_noerr_quiet(drtn
, out
);
5864 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
5865 DERDecodedInfo currDecoded
;
5867 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
5868 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
5869 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
5870 currDecoded
.content
.data
, currDecoded
.content
.length
);
5871 require_quiet(oid
, out
);
5872 CFArrayAppendValue(extended_key_usage_oids
, oid
);
5875 require_quiet(drtn
== DR_EndOfSequence
, out
);
5876 return extended_key_usage_oids
;
5880 CFReleaseSafe(extended_key_usage_oids
);
5884 CFArrayRef
SecCertificateCopySignedCertificateTimestamps(SecCertificateRef certificate
)
5886 require_quiet(certificate
, out
);
5889 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5890 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5891 if (extn
->extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
5892 !memcmp(extn
->extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
->extnID
.length
)) {
5893 /* Got the SCT oid */
5894 DERDecodedInfo sctList
;
5895 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &sctList
);
5896 require_noerr_quiet(drtn
, out
);
5897 require_quiet(sctList
.tag
== ASN1_OCTET_STRING
, out
);
5898 return SecCreateSignedCertificateTimestampsArrayFromSerializedSCTList(sctList
.content
.data
, sctList
.content
.length
);
5906 static bool matches_expected(DERItem der
, CFTypeRef expected
) {
5907 if (der
.length
> 1) {
5908 DERDecodedInfo decoded
;
5909 DERDecodeItem(&der
, &decoded
);
5910 switch (decoded
.tag
) {
5913 return decoded
.content
.length
== 0 && expected
== NULL
;
5917 case ASN1_IA5_STRING
:
5918 case ASN1_UTF8_STRING
: {
5919 if (isString(expected
)) {
5920 CFStringRef expectedString
= (CFStringRef
) expected
;
5921 CFStringRef itemString
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFStringEncodingUTF8
, false, kCFAllocatorNull
);
5923 bool result
= (kCFCompareEqualTo
== CFStringCompare(expectedString
, itemString
, 0));
5924 CFReleaseNull(itemString
);
5930 case ASN1_OCTET_STRING
: {
5931 if (isData(expected
)) {
5932 CFDataRef expectedData
= (CFDataRef
) expected
;
5933 CFDataRef itemData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFAllocatorNull
);
5935 bool result
= CFEqual(expectedData
, itemData
);
5936 CFReleaseNull(itemData
);
5942 case ASN1_INTEGER
: {
5943 SInt32 expected_value
= 0;
5944 if (isString(expected
))
5946 CFStringRef aStr
= (CFStringRef
)expected
;
5947 expected_value
= CFStringGetIntValue(aStr
);
5949 else if (isNumber(expected
))
5951 CFNumberGetValue(expected
, kCFNumberSInt32Type
, &expected_value
);
5954 uint32_t num_value
= 0;
5955 if (!DERParseInteger(&decoded
.content
, &num_value
))
5957 return ((uint32_t)expected_value
== num_value
);
5970 static bool cert_contains_marker_extension_value(SecCertificateRef certificate
, CFDataRef oid
, CFTypeRef expectedValue
)
5973 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
5974 size_t oid_len
= CFDataGetLength(oid
);
5976 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5977 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5978 if (extn
->extnID
.length
== oid_len
5979 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
5981 return matches_expected(extn
->extnValue
, expectedValue
);
5987 static bool cert_contains_marker_extension(SecCertificateRef certificate
, CFTypeRef oid
)
5989 return cert_contains_marker_extension_value(certificate
, oid
, NULL
);
5992 struct search_context
{
5994 SecCertificateRef certificate
;
5997 static bool GetDecimalValueOfString(CFStringRef string
, uint32_t* value
)
5999 CFCharacterSetRef nonDecimalDigit
= CFCharacterSetCreateInvertedSet(NULL
, CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
));
6000 bool result
= false;
6002 if ( CFStringGetLength(string
) > 0
6003 && !CFStringFindCharacterFromSet(string
, nonDecimalDigit
, CFRangeMake(0, CFStringGetLength(string
)), kCFCompareForcedOrdering
, NULL
))
6006 *value
= CFStringGetIntValue(string
);
6010 CFReleaseNull(nonDecimalDigit
);
6015 bool SecCertificateIsOidString(CFStringRef oid
)
6017 if (!oid
) return false;
6018 if (2 >= CFStringGetLength(oid
)) return false;
6021 /* oid string only has the allowed characters */
6022 CFCharacterSetRef decimalOid
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789."));
6023 CFCharacterSetRef nonDecimalOid
= CFCharacterSetCreateInvertedSet(NULL
, decimalOid
);
6024 if (CFStringFindCharacterFromSet(oid
, nonDecimalOid
, CFRangeMake(0, CFStringGetLength(oid
)), kCFCompareForcedOrdering
, NULL
)) {
6028 /* first arc is allowed */
6029 UniChar firstArc
[2];
6030 CFRange firstTwo
= {0, 2};
6031 CFStringGetCharacters(oid
, firstTwo
, firstArc
);
6032 if (firstArc
[1] != '.' ||
6033 (firstArc
[0] != '0' && firstArc
[0] != '1' && firstArc
[0] != '2')) {
6037 CFReleaseNull(decimalOid
);
6038 CFReleaseNull(nonDecimalOid
);
6043 CFDataRef
SecCertificateCreateOidDataFromString(CFAllocatorRef allocator
, CFStringRef string
)
6045 CFMutableDataRef currentResult
= NULL
;
6046 CFDataRef encodedResult
= NULL
;
6048 CFArrayRef parts
= NULL
;
6051 if (!string
|| !SecCertificateIsOidString(string
))
6054 parts
= CFStringCreateArrayBySeparatingStrings(allocator
, string
, CFSTR("."));
6059 count
= CFArrayGetCount(parts
);
6063 // assume no more than 5 bytes needed to represent any part of the oid,
6064 // since we limit parts to 32-bit values,
6065 // but the first two parts only need 1 byte
6066 currentResult
= CFDataCreateMutable(allocator
, 1+(count
-2)*5);
6072 part
= CFArrayGetValueAtIndex(parts
, 0);
6074 if (!GetDecimalValueOfString(part
, &x
) || x
> 6)
6081 part
= CFArrayGetValueAtIndex(parts
, 1);
6083 if (!GetDecimalValueOfString(part
, &x
) || x
> 39)
6089 CFDataAppendBytes(currentResult
, &firstByte
, 1);
6091 for (CFIndex i
= 2; i
< count
&& GetDecimalValueOfString(CFArrayGetValueAtIndex(parts
, i
), &x
); ++i
) {
6092 uint8_t b
[5] = {0, 0, 0, 0, 0};
6094 b
[3] = 0x80 | ((x
>> 7) & 0x7F);
6095 b
[2] = 0x80 | ((x
>> 14) & 0x7F);
6096 b
[1] = 0x80 | ((x
>> 21) & 0x7F);
6097 b
[0] = 0x80 | ((x
>> 28) & 0x7F);
6099 // Skip the unused extension bytes.
6100 size_t skipBytes
= 0;
6101 while (b
[skipBytes
] == 0x80)
6104 CFDataAppendBytes(currentResult
, b
+ skipBytes
, sizeof(b
) - skipBytes
);
6107 encodedResult
= currentResult
;
6108 currentResult
= NULL
;
6111 CFReleaseNull(parts
);
6112 CFReleaseNull(currentResult
);
6114 return encodedResult
;
6117 static void check_for_marker(const void *key
, const void *value
, void *context
)
6119 struct search_context
* search_ctx
= (struct search_context
*) context
;
6120 CFStringRef key_string
= (CFStringRef
) key
;
6121 CFTypeRef value_ref
= (CFTypeRef
) value
;
6123 // If we could have short-circuited the iteration
6124 // we would have, but the best we can do
6125 // is not waste time comparing once a match
6127 if (search_ctx
->found
)
6130 if (!isString(key_string
))
6133 CFDataRef key_data
= SecCertificateCreateOidDataFromString(NULL
, key_string
);
6135 if (!isData(key_data
))
6138 if (cert_contains_marker_extension_value(search_ctx
->certificate
, key_data
, value_ref
))
6139 search_ctx
->found
= true;
6141 CFReleaseNull(key_data
);
6145 // CFType Ref is either:
6147 // CFData - OID to match with no data permitted
6148 // CFString - decimal OID to match
6149 // CFDictionary - OID -> Value table for expected values Single Object or Array
6150 // CFArray - Array of the above.
6152 // This returns true if any of the requirements are met.
6153 bool SecCertificateHasMarkerExtension(SecCertificateRef certificate
, CFTypeRef oids
)
6155 if (NULL
== certificate
|| NULL
== oids
) {
6157 } else if (CFGetTypeID(oids
) == CFArrayGetTypeID()) {
6158 CFIndex ix
, length
= CFArrayGetCount(oids
);
6159 for (ix
= 0; ix
< length
; ix
++)
6160 if (SecCertificateHasMarkerExtension(certificate
, CFArrayGetValueAtIndex((CFArrayRef
)oids
, ix
)))
6162 } else if (CFGetTypeID(oids
) == CFDictionaryGetTypeID()) {
6163 struct search_context context
= { .found
= false, .certificate
= certificate
};
6164 CFDictionaryApplyFunction((CFDictionaryRef
) oids
, &check_for_marker
, &context
);
6165 return context
.found
;
6166 } else if (CFGetTypeID(oids
) == CFDataGetTypeID()) {
6167 return cert_contains_marker_extension(certificate
, oids
);
6168 } else if (CFGetTypeID(oids
) == CFStringGetTypeID()) {
6169 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, oids
);
6170 if (dataOid
== NULL
) return false;
6171 bool result
= cert_contains_marker_extension(certificate
, dataOid
);
6172 CFReleaseNull(dataOid
);
6178 // Since trust evaluation checks for the id-pkix-ocsp-nocheck OID marker
6179 // in every certificate, this function caches the OID data once instead of
6180 // parsing the same OID string each time.
6182 bool SecCertificateHasOCSPNoCheckMarkerExtension(SecCertificateRef certificate
)
6184 static CFDataRef sOCSPNoCheckOIDData
= NULL
;
6185 static dispatch_once_t onceToken
;
6187 dispatch_once(&onceToken
, ^{
6188 sOCSPNoCheckOIDData
= SecCertificateCreateOidDataFromString(NULL
, CFSTR("1.3.6.1.5.5.7.48.1.5"));
6190 return SecCertificateHasMarkerExtension(certificate
, sOCSPNoCheckOIDData
);
6193 static DERItem
*cert_extension_value_for_marker(SecCertificateRef certificate
, CFDataRef oid
) {
6195 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
6196 size_t oid_len
= CFDataGetLength(oid
);
6198 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
6199 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
6200 if (extn
->extnID
.length
== oid_len
6201 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
6203 return (DERItem
*)&extn
->extnValue
;
6210 // CFType Ref is either:
6212 // CFData - OID to match with no data permitted
6213 // CFString - decimal OID to match
6215 DERItem
*SecCertificateGetExtensionValue(SecCertificateRef certificate
, CFTypeRef oid
) {
6216 if (!certificate
|| !oid
) {
6220 if(CFGetTypeID(oid
) == CFDataGetTypeID()) {
6221 return cert_extension_value_for_marker(certificate
, oid
);
6222 } else if (CFGetTypeID(oid
) == CFStringGetTypeID()) {
6223 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, oid
);
6224 if (dataOid
== NULL
) return NULL
;
6225 DERItem
*result
= cert_extension_value_for_marker(certificate
, dataOid
);
6226 CFReleaseNull(dataOid
);
6233 CFDataRef
SecCertificateCopyExtensionValue(SecCertificateRef certificate
, CFTypeRef extensionOID
, bool *isCritical
) {
6234 if (!certificate
|| !extensionOID
) {
6238 CFDataRef oid
= NULL
, extensionValue
= NULL
;
6239 if (CFGetTypeID(extensionOID
) == CFDataGetTypeID()) {
6240 oid
= CFRetainSafe(extensionOID
);
6241 } else if (CFGetTypeID(extensionOID
) == CFStringGetTypeID()) {
6242 oid
= SecCertificateCreateOidDataFromString(NULL
, extensionOID
);
6249 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
6250 size_t oid_len
= CFDataGetLength(oid
);
6252 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
6253 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
6254 if (extn
->extnID
.length
== oid_len
6255 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
6258 *isCritical
= extn
->critical
;
6260 extensionValue
= CFDataCreate(NULL
, extn
->extnValue
.data
, extn
->extnValue
.length
);
6266 return extensionValue
;
6269 CFDataRef
SecCertificateCopyiAPAuthCapabilities(SecCertificateRef certificate
) {
6273 CFDataRef extensionData
= NULL
;
6274 DERItem
*extensionValue
= NULL
;
6275 extensionValue
= SecCertificateGetExtensionValue(certificate
,
6276 CFSTR("1.2.840.113635.100.6.36"));
6277 require_quiet(extensionValue
, out
);
6278 /* The extension is a octet string containing the DER-encoded 32-byte octet string */
6279 require_quiet(extensionValue
->length
== 34, out
);
6280 DERDecodedInfo decodedValue
;
6281 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
6282 if (decodedValue
.tag
== ASN1_OCTET_STRING
) {
6283 require_quiet(decodedValue
.content
.length
== 32, out
);
6284 extensionData
= CFDataCreate(NULL
, decodedValue
.content
.data
,
6285 decodedValue
.content
.length
);
6287 require_quiet(extensionValue
->data
[33] == 0x00 &&
6288 extensionValue
->data
[32] == 0x00, out
);
6289 extensionData
= CFDataCreate(NULL
, extensionValue
->data
, 32);
6292 return extensionData
;
6296 /* From iapd IAPAuthenticationTypes.h */
6297 typedef struct IapCertSerialNumber
6299 uint8_t xservID
; // Xserver ID
6300 uint8_t hsmID
; // Hardware security module ID (generated cert)
6301 uint8_t delimiter01
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6302 uint8_t dateYear
; // Date year cert was issued
6303 uint8_t dateMonth
; // Date month cert was issued
6304 uint8_t dateDay
; // Date day cert was issued
6305 uint8_t delimiter02
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6306 uint8_t devClass
; // iAP device class (maps to lingo permissions)
6307 uint8_t delimiter03
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6308 uint8_t batchNumHi
; // Batch number high byte (15:08)
6309 uint8_t batchNumLo
; // Batch number low byte (07:00)
6310 uint8_t delimiter04
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6311 uint8_t serialNumHi
; // Serial number high byte (23:16)
6312 uint8_t serialNumMid
; // Serial number middle byte (15:08)
6313 uint8_t serialNumLo
; // Serial number low byte (07:00)
6315 } IapCertSerialNumber_t
, *pIapCertSerialNumber_t
;
6318 #define IAP_CERT_FIELD_DELIMITER 0xAA // "Apple_Accessory" delimiter
6319 SeciAuthVersion
SecCertificateGetiAuthVersion(SecCertificateRef certificate
) {
6321 return kSeciAuthInvalid
;
6323 if (NULL
!= SecCertificateGetExtensionValue(certificate
,
6324 CFSTR("1.2.840.113635.100.6.36"))) {
6325 /* v3 Capabilities Extension */
6326 return kSeciAuthVersion3
;
6328 if (NULL
!= SecCertificateGetExtensionValue(certificate
,
6329 CFSTR("1.2.840.113635.100.6.59.1"))) {
6330 /* SW Auth General Capabilities Extension */
6331 return kSeciAuthVersionSW
;
6333 DERItem serialNumber
= certificate
->_serialNum
;
6334 require_quiet(serialNumber
.data
, out
);
6335 require_quiet(serialNumber
.length
== 15, out
);
6336 require_quiet(serialNumber
.data
[2] == IAP_CERT_FIELD_DELIMITER
&&
6337 serialNumber
.data
[6] == IAP_CERT_FIELD_DELIMITER
&&
6338 serialNumber
.data
[8] == IAP_CERT_FIELD_DELIMITER
&&
6339 serialNumber
.data
[11] == IAP_CERT_FIELD_DELIMITER
, out
);
6340 return kSeciAuthVersion2
;
6342 return kSeciAuthInvalid
;
6345 static CFStringRef
SecCertificateiAPSWAuthCapabilitiesTypeToOID(SeciAPSWAuthCapabilitiesType type
) {
6346 CFStringRef extensionOID
= NULL
;
6347 /* Get the oid for the type */
6348 if (type
== kSeciAPSWAuthGeneralCapabilities
) {
6349 extensionOID
= CFSTR("1.2.840.113635.100.6.59.1");
6350 } else if (type
== kSeciAPSWAuthAirPlayCapabilities
) {
6351 extensionOID
= CFSTR("1.2.840.113635.100.6.59.2");
6352 } else if (type
== kSeciAPSWAuthHomeKitCapabilities
) {
6353 extensionOID
= CFSTR("1.2.840.113635.100.6.59.3");
6355 return extensionOID
;
6358 CFDataRef
SecCertificateCopyiAPSWAuthCapabilities(SecCertificateRef certificate
, SeciAPSWAuthCapabilitiesType type
) {
6362 CFDataRef extensionData
= NULL
;
6363 DERItem
*extensionValue
= NULL
;
6364 CFStringRef extensionOID
= SecCertificateiAPSWAuthCapabilitiesTypeToOID(type
);
6365 require_quiet(extensionOID
, out
);
6366 extensionValue
= SecCertificateGetExtensionValue(certificate
, extensionOID
);
6367 require_quiet(extensionValue
, out
);
6368 /* The extension is a octet string containing the DER-encoded variable-length octet string */
6369 DERDecodedInfo decodedValue
;
6370 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
6371 if (decodedValue
.tag
== ASN1_OCTET_STRING
) {
6372 extensionData
= CFDataCreate(NULL
, decodedValue
.content
.data
,
6373 decodedValue
.content
.length
);
6376 return extensionData
;
6379 CFStringRef
SecCertificateCopyComponentType(SecCertificateRef certificate
) {
6383 CFStringRef componentType
= NULL
;
6384 DERItem
*extensionValue
= SecCertificateGetExtensionValue(certificate
, CFSTR("1.2.840.113635.100.11.1"));
6385 require_quiet(extensionValue
, out
);
6386 /* The componentType is an IA5String */
6387 DERDecodedInfo decodedValue
;
6388 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
6389 if (decodedValue
.tag
== ASN1_IA5_STRING
) {
6390 componentType
= CFStringCreateWithBytes(NULL
, decodedValue
.content
.data
, decodedValue
.content
.length
, kCFStringEncodingASCII
, false);
6393 return componentType
;
6396 SecCertificateRef
SecCertificateCreateWithPEM(CFAllocatorRef allocator
,
6397 CFDataRef pem_certificate
)
6399 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
6400 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
6401 uint8_t *base64_data
= NULL
;
6402 SecCertificateRef cert
= NULL
;
6403 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
6404 const size_t length
= CFDataGetLength(pem_certificate
);
6405 char *begin
= strnstr((const char *)data
, begin_cert
, length
);
6406 char *end
= strnstr((const char *)data
, end_cert
, length
);
6409 begin
+= sizeof(begin_cert
) - 1;
6410 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
6411 if (base64_length
&& (base64_length
< (size_t)CFDataGetLength(pem_certificate
))) {
6412 require_quiet(base64_data
= calloc(1, base64_length
), out
);
6413 require_action_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
, free(base64_data
));
6414 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, base64_data
, base64_length
);
6423 // -- MARK -- XPC encoding/decoding
6426 bool SecCertificateAppendToXPCArray(SecCertificateRef certificate
, xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
6428 return true; // NOOP
6430 size_t length
= SecCertificateGetLength(certificate
);
6431 const uint8_t *bytes
= SecCertificateGetBytePtr(certificate
);
6432 #if SECTRUST_VERBOSE_DEBUG
6433 secerror("cert=0x%lX length=%d bytes=0x%lX", (uintptr_t)certificate
, (int)length
, (uintptr_t)bytes
);
6435 if (!length
|| !bytes
) {
6436 return SecError(errSecParam
, error
, CFSTR("failed to der encode certificate"));
6438 xpc_array_set_data(xpc_certificates
, XPC_ARRAY_APPEND
, bytes
, length
);
6442 SecCertificateRef
SecCertificateCreateWithXPCArrayAtIndex(xpc_object_t xpc_certificates
, size_t index
, CFErrorRef
*error
) {
6443 SecCertificateRef certificate
= NULL
;
6445 const uint8_t *bytes
= xpc_array_get_data(xpc_certificates
, index
, &length
);
6447 certificate
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, bytes
, length
);
6450 SecError(errSecParam
, error
, CFSTR("certificates[%zu] failed to decode"), index
);
6455 xpc_object_t
SecCertificateArrayCopyXPCArray(CFArrayRef certificates
, CFErrorRef
*error
) {
6456 xpc_object_t xpc_certificates
;
6457 require_action_quiet(xpc_certificates
= xpc_array_create(NULL
, 0), exit
,
6458 SecError(errSecAllocate
, error
, CFSTR("failed to create xpc_array")));
6459 CFIndex ix
, count
= CFArrayGetCount(certificates
);
6460 for (ix
= 0; ix
< count
; ++ix
) {
6461 SecCertificateRef certificate
= (SecCertificateRef
) CFArrayGetValueAtIndex(certificates
, ix
);
6462 #if SECTRUST_VERBOSE_DEBUG
6463 CFIndex length
= SecCertificateGetLength(certificate
);
6464 const UInt8
*bytes
= SecCertificateGetBytePtr(certificate
);
6465 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
);
6467 if (!SecCertificateAppendToXPCArray(certificate
, xpc_certificates
, error
)) {
6468 xpc_release(xpc_certificates
);
6469 xpc_certificates
= NULL
;
6475 return xpc_certificates
;
6478 CFArrayRef
SecCertificateXPCArrayCopyArray(xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
6479 CFMutableArrayRef certificates
= NULL
;
6480 require_action_quiet(xpc_get_type(xpc_certificates
) == XPC_TYPE_ARRAY
, exit
,
6481 SecError(errSecParam
, error
, CFSTR("certificates xpc value is not an array")));
6482 size_t count
= xpc_array_get_count(xpc_certificates
);
6483 require_action_quiet(certificates
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
), exit
,
6484 SecError(errSecAllocate
, error
, CFSTR("failed to create CFArray of capacity %zu"), count
));
6487 for (ix
= 0; ix
< count
; ++ix
) {
6488 SecCertificateRef cert
= SecCertificateCreateWithXPCArrayAtIndex(xpc_certificates
, ix
, error
);
6490 CFRelease(certificates
);
6493 CFArraySetValueAtIndex(certificates
, ix
, cert
);
6498 return certificates
;
6501 #define do_if_registered(sdp, ...) if (gTrustd && gTrustd->sdp) { return gTrustd->sdp(__VA_ARGS__); }
6504 static CFArrayRef
CopyEscrowCertificates(SecCertificateEscrowRootType escrowRootType
, CFErrorRef
* error
)
6506 __block CFArrayRef result
= NULL
;
6508 do_if_registered(ota_CopyEscrowCertificates
, escrowRootType
, error
);
6510 securityd_send_sync_and_do(kSecXPCOpOTAGetEscrowCertificates
, error
,
6511 ^bool(xpc_object_t message
, CFErrorRef
*blockError
)
6513 xpc_dictionary_set_uint64(message
, "escrowType", (uint64_t)escrowRootType
);
6516 ^bool(xpc_object_t response
, CFErrorRef
*blockError
)
6518 xpc_object_t xpc_array
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
6520 if (response
&& (NULL
!= xpc_array
)) {
6521 result
= (CFArrayRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_array
);
6524 return SecError(errSecInternal
, blockError
, CFSTR("Did not get the Escrow certificates"));
6526 return result
!= NULL
;
6531 CFArrayRef
SecCertificateCopyEscrowRoots(SecCertificateEscrowRootType escrowRootType
)
6533 CFArrayRef result
= NULL
;
6535 CFDataRef certData
= NULL
;
6538 if (kSecCertificateBaselineEscrowRoot
== escrowRootType
||
6539 kSecCertificateBaselinePCSEscrowRoot
== escrowRootType
||
6540 kSecCertificateBaselineEscrowBackupRoot
== escrowRootType
||
6541 kSecCertificateBaselineEscrowEnrollmentRoot
== escrowRootType
)
6543 // The request is for the base line certificates.
6544 // Use the hard coded data to generate the return array.
6545 struct RootRecord
** pEscrowRoots
;
6546 switch (escrowRootType
) {
6547 case kSecCertificateBaselineEscrowRoot
:
6548 numRoots
= kNumberOfBaseLineEscrowRoots
;
6549 pEscrowRoots
= kBaseLineEscrowRoots
;
6551 case kSecCertificateBaselinePCSEscrowRoot
:
6552 numRoots
= kNumberOfBaseLinePCSEscrowRoots
;
6553 pEscrowRoots
= kBaseLinePCSEscrowRoots
;
6555 case kSecCertificateBaselineEscrowBackupRoot
:
6556 numRoots
= kNumberOfBaseLineEscrowBackupRoots
;
6557 pEscrowRoots
= kBaseLineEscrowBackupRoots
;
6559 case kSecCertificateBaselineEscrowEnrollmentRoot
:
6561 numRoots
= kNumberOfBaseLineEscrowEnrollmentRoots
;
6562 pEscrowRoots
= kBaseLineEscrowEnrollmentRoots
;
6566 // Get the hard coded set of roots
6567 SecCertificateRef baseLineCerts
[numRoots
];
6568 struct RootRecord
* pRootRecord
= NULL
;
6570 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6571 pRootRecord
= pEscrowRoots
[iCnt
];
6572 if (NULL
!= pRootRecord
&& pRootRecord
->_length
> 0 && NULL
!= pRootRecord
->_bytes
) {
6573 certData
= CFDataCreate(kCFAllocatorDefault
, pRootRecord
->_bytes
, pRootRecord
->_length
);
6574 if (NULL
!= certData
) {
6575 baseLineCerts
[iCnt
] = SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
6576 CFRelease(certData
);
6580 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)baseLineCerts
, numRoots
, &kCFTypeArrayCallBacks
);
6581 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6582 if (NULL
!= baseLineCerts
[iCnt
]) {
6583 CFRelease(baseLineCerts
[iCnt
]);
6587 // The request is for the current certificates.
6588 CFErrorRef error
= NULL
;
6589 CFArrayRef cert_datas
= CopyEscrowCertificates(escrowRootType
, &error
);
6590 if (NULL
!= error
|| NULL
== cert_datas
) {
6591 if (NULL
!= error
) {
6594 if (NULL
!= cert_datas
) {
6595 CFRelease(cert_datas
);
6600 numRoots
= (int)(CFArrayGetCount(cert_datas
));
6602 SecCertificateRef assetCerts
[numRoots
];
6603 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6604 certData
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, iCnt
);
6605 if (NULL
!= certData
) {
6606 SecCertificateRef aCertRef
= SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
6607 assetCerts
[iCnt
] = aCertRef
;
6610 assetCerts
[iCnt
] = NULL
;
6615 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)assetCerts
, numRoots
, &kCFTypeArrayCallBacks
);
6616 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6617 if (NULL
!= assetCerts
[iCnt
]) {
6618 CFRelease(assetCerts
[iCnt
]);
6622 CFReleaseSafe(cert_datas
);
6627 static CFDictionaryRef
CopyTrustedCTLogs(CFErrorRef
* error
)
6629 __block CFDictionaryRef result
= NULL
;
6631 // call function directly and return if we are built in server mode
6632 do_if_registered(sec_ota_pki_copy_trusted_ct_logs
, error
);
6634 securityd_send_sync_and_do(kSecXPCOpOTAPKICopyTrustedCTLogs
, error
,
6635 ^bool(xpc_object_t message
, CFErrorRef
*blockError
) {
6636 // input: set message parameters here
6638 }, ^bool(xpc_object_t response
, CFErrorRef
*blockError
) {
6639 // output: get dictionary from response object
6640 xpc_object_t xpc_dictionary
= NULL
;
6642 xpc_dictionary
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
6644 if (xpc_dictionary
&& (xpc_get_type(xpc_dictionary
) == XPC_TYPE_DICTIONARY
)) {
6645 result
= (CFDictionaryRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_dictionary
);
6647 return SecError(errSecInternal
, blockError
, CFSTR("Unable to get CT logs"));
6649 return result
!= NULL
;
6654 #define CTLOG_KEYID_LENGTH 32 /* key id data length */
6656 static CFDictionaryRef
CopyCTLogForKeyID(CFDataRef keyID
, CFErrorRef
* error
)
6658 __block CFDictionaryRef result
= NULL
;
6659 if (!isData(keyID
)) {
6660 (void) SecError(errSecParam
, error
, CFSTR("keyID was not a valid CFDataRef"));
6663 const void *p
= CFDataGetBytePtr(keyID
);
6664 if (!p
|| CFDataGetLength(keyID
) != CTLOG_KEYID_LENGTH
) {
6665 (void) SecError(errSecParam
, error
, CFSTR("keyID data was not the expected length"));
6668 // call function directly and return if we are built in server mode
6669 do_if_registered(sec_ota_pki_copy_ct_log_for_keyid
, keyID
, error
);
6671 securityd_send_sync_and_do(kSecXPCOpOTAPKICopyCTLogForKeyID
, error
,
6672 ^bool(xpc_object_t message
, CFErrorRef
*blockError
) {
6673 // input: set message parameters here
6674 xpc_dictionary_set_data(message
, kSecXPCData
, p
, CTLOG_KEYID_LENGTH
);
6676 }, ^bool(xpc_object_t response
, CFErrorRef
*blockError
) {
6677 // output: get dictionary from response object
6678 xpc_object_t xpc_dictionary
= NULL
;
6680 xpc_dictionary
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
6682 if (xpc_dictionary
&& (xpc_get_type(xpc_dictionary
) == XPC_TYPE_DICTIONARY
)) {
6683 result
= (CFDictionaryRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_dictionary
);
6685 return SecError(errSecInternal
, blockError
, CFSTR("Unable to match CT log"));
6687 return result
!= NULL
;
6692 CFDictionaryRef
SecCertificateCopyTrustedCTLogs(void)
6694 CFErrorRef localError
= NULL
;
6695 CFDictionaryRef result
= CopyTrustedCTLogs(&localError
);
6696 CFReleaseSafe(localError
);
6700 CFDictionaryRef
SecCertificateCopyCTLogForKeyID(CFDataRef keyID
)
6702 CFErrorRef localError
= NULL
;
6703 CFDictionaryRef result
= CopyCTLogForKeyID(keyID
, &localError
);
6704 CFReleaseSafe(localError
);
6708 SEC_CONST_DECL (kSecSignatureDigestAlgorithmUnknown
, "SignatureDigestUnknown");
6709 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD2
, "SignatureDigestMD2");
6710 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD4
, "SignatureDigestMD4");
6711 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD5
, "SignatureDigestMD5");
6712 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA1
, "SignatureDigestSHA1");
6713 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA224
, "SignatureDigestSHA224");
6714 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA256
, "SignatureDigestSHA256");
6715 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA384
, "SignatureDigestSHA284");
6716 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA512
, "SignatureDigestSHA512");
6718 SecSignatureHashAlgorithm
SecCertificateGetSignatureHashAlgorithm(SecCertificateRef certificate
)
6720 SecSignatureHashAlgorithm result
= kSecSignatureHashAlgorithmUnknown
;
6721 DERAlgorithmId
*algId
= (certificate
) ? &certificate
->_tbsSigAlg
: NULL
;
6722 const DERItem
*algOid
= (algId
) ? &algId
->oid
: NULL
;
6724 if (!algOid
->data
|| !algOid
->length
) {
6727 /* classify the signature algorithm OID into one of our known types */
6728 if (DEROidCompare(algOid
, &oidSha512Ecdsa
) ||
6729 DEROidCompare(algOid
, &oidSha512Rsa
) ||
6730 DEROidCompare(algOid
, &oidSha512
)) {
6731 result
= kSecSignatureHashAlgorithmSHA512
;
6734 if (DEROidCompare(algOid
, &oidSha384Ecdsa
) ||
6735 DEROidCompare(algOid
, &oidSha384Rsa
) ||
6736 DEROidCompare(algOid
, &oidSha384
)) {
6737 result
= kSecSignatureHashAlgorithmSHA384
;
6740 if (DEROidCompare(algOid
, &oidSha256Ecdsa
) ||
6741 DEROidCompare(algOid
, &oidSha256Rsa
) ||
6742 DEROidCompare(algOid
, &oidSha256
)) {
6743 result
= kSecSignatureHashAlgorithmSHA256
;
6746 if (DEROidCompare(algOid
, &oidSha224Ecdsa
) ||
6747 DEROidCompare(algOid
, &oidSha224Rsa
) ||
6748 DEROidCompare(algOid
, &oidSha224
)) {
6749 result
= kSecSignatureHashAlgorithmSHA224
;
6752 if (DEROidCompare(algOid
, &oidSha1Ecdsa
) ||
6753 DEROidCompare(algOid
, &oidSha1Rsa
) ||
6754 DEROidCompare(algOid
, &oidSha1Dsa
) ||
6755 DEROidCompare(algOid
, &oidSha1DsaOIW
) ||
6756 DEROidCompare(algOid
, &oidSha1DsaCommonOIW
) ||
6757 DEROidCompare(algOid
, &oidSha1RsaOIW
) ||
6758 DEROidCompare(algOid
, &oidSha1Fee
) ||
6759 DEROidCompare(algOid
, &oidSha1
)) {
6760 result
= kSecSignatureHashAlgorithmSHA1
;
6763 if (DEROidCompare(algOid
, &oidMd5Rsa
) ||
6764 DEROidCompare(algOid
, &oidMd5Fee
) ||
6765 DEROidCompare(algOid
, &oidMd5
)) {
6766 result
= kSecSignatureHashAlgorithmMD5
;
6769 if (DEROidCompare(algOid
, &oidMd4Rsa
) ||
6770 DEROidCompare(algOid
, &oidMd4
)) {
6771 result
= kSecSignatureHashAlgorithmMD4
;
6774 if (DEROidCompare(algOid
, &oidMd2Rsa
) ||
6775 DEROidCompare(algOid
, &oidMd2
)) {
6776 result
= kSecSignatureHashAlgorithmMD2
;
6785 CFArrayRef
SecCertificateCopyiPhoneDeviceCAChain(void) {
6786 CFMutableArrayRef result
= NULL
;
6787 SecCertificateRef iPhoneDeviceCA
= NULL
, iPhoneCA
= NULL
, appleRoot
= NULL
;
6789 require_quiet(iPhoneDeviceCA
= SecCertificateCreateWithBytes(NULL
, _AppleiPhoneDeviceCA
, sizeof(_AppleiPhoneDeviceCA
)),
6791 require_quiet(iPhoneCA
= SecCertificateCreateWithBytes(NULL
, _AppleiPhoneCA
, sizeof(_AppleiPhoneCA
)),
6793 require_quiet(appleRoot
= SecCertificateCreateWithBytes(NULL
, _AppleRootCA
, sizeof(_AppleRootCA
)),
6796 require_quiet(result
= CFArrayCreateMutable(NULL
, 3, &kCFTypeArrayCallBacks
), errOut
);
6797 CFArrayAppendValue(result
, iPhoneDeviceCA
);
6798 CFArrayAppendValue(result
, iPhoneCA
);
6799 CFArrayAppendValue(result
, appleRoot
);
6802 CFReleaseNull(iPhoneDeviceCA
);
6803 CFReleaseNull(iPhoneCA
);
6804 CFReleaseNull(appleRoot
);
6808 bool SecCertificateGetDeveloperIDDate(SecCertificateRef certificate
, CFAbsoluteTime
*time
, CFErrorRef
*error
) {
6809 if (!certificate
|| !time
) {
6810 return SecError(errSecParam
, error
, CFSTR("DeveloperID Date parsing: missing required input"));
6812 DERItem
*extensionValue
= SecCertificateGetExtensionValue(certificate
, CFSTR("1.2.840.113635.100.6.1.33"));
6813 if (!extensionValue
) {
6814 return SecError(errSecMissingRequiredExtension
, error
, CFSTR("DeveloperID Date parsing: extension not found"));
6816 DERDecodedInfo decodedValue
;
6817 if (DERDecodeItem(extensionValue
, &decodedValue
) != DR_Success
) {
6818 return SecError(errSecDecode
, error
, CFSTR("DeveloperID Date parsing: extension value failed to decode"));
6820 /* The extension value is a DERGeneralizedTime encoded in a UTF8String */
6821 CFErrorRef localError
= NULL
;
6822 if (decodedValue
.tag
== ASN1_UTF8_STRING
) {
6823 *time
= SecAbsoluteTimeFromDateContentWithError(ASN1_GENERALIZED_TIME
, decodedValue
.content
.data
, decodedValue
.content
.length
, &localError
);
6825 return SecError(errSecDecode
, error
, CFSTR("DeveloperID Date parsing: extension value wrong tag"));
6827 return CFErrorPropagate(localError
, error
);
6830 CFIndex
SecCertificateGetUnparseableKnownExtension(SecCertificateRef certificate
) {
6834 return certificate
->_unparseableKnownExtensionIndex
;