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 /* Given the contents of an X.501 Name return the contents of a normalized
1373 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1374 const DERItem
*x501name
) {
1375 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1376 CFIndex length
= x501name
->length
;
1377 CFDataSetLength(result
, length
);
1378 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1381 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1383 require_noerr_quiet(drtn
, badDER
);
1386 /* Always points to last rdn tag. */
1387 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1388 /* Offset relative to base of current rdn set tag. */
1389 CFIndex rdnTagLocation
= 0;
1390 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1391 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1392 /* We don't allow empty RDNs. */
1393 require_quiet(rdn
.content
.length
!= 0, badDER
);
1394 /* Length of the tag and length of the current rdn. */
1395 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1396 CFIndex rdnContentLength
= rdn
.content
.length
;
1397 /* Copy the tag and length of the RDN. */
1398 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1401 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1402 require_quiet(drtn
== DR_Success
, badDER
);
1405 /* Always points to tag of current atv sequence. */
1406 const DERByte
*atvTag
= atvSeq
.nextItem
;
1407 /* Offset relative to base of current atv sequence tag. */
1408 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1409 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1410 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1411 /* Length of the tag and length of the current atv. */
1412 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1413 CFIndex atvContentLength
= atv
.content
.length
;
1414 /* Copy the tag and length of the atv and the atv itself. */
1415 memcpy(base
+ atvTagLocation
, atvTag
,
1416 atvTLLength
+ atv
.content
.length
);
1418 /* Now decode the atv sequence. */
1419 DERAttributeTypeAndValue atvPair
;
1420 drtn
= DERParseSequenceContent(&atv
.content
,
1421 DERNumAttributeTypeAndValueItemSpecs
,
1422 DERAttributeTypeAndValueItemSpecs
,
1423 &atvPair
, sizeof(atvPair
));
1424 require_noerr_quiet(drtn
, badDER
);
1425 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1426 DERDecodedInfo value
;
1427 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1428 require_noerr_quiet(drtn
, badDER
);
1430 /* (c) attribute values in PrintableString are not case sensitive
1431 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1433 (d) attribute values in PrintableString are compared after
1434 removing leading and trailing white space and converting internal
1435 substrings of one or more consecutive white space characters to a
1437 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1438 /* Offset relative to base of current value tag. */
1439 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1440 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1441 CFIndex valueContentLength
= value
.content
.length
;
1443 /* Now copy all the bytes, but convert to upper case while
1444 doing so and convert multiple whitespace chars into a
1446 bool lastWasBlank
= false;
1447 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1448 CFIndex valueCurrentLocation
= valueLocation
;
1450 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1451 UInt8 ch
= value
.content
.data
[ix
];
1456 /* Don't insert a space for first character
1458 if (valueCurrentLocation
> valueLocation
) {
1459 base
[valueCurrentLocation
++] = ' ';
1461 lastWasBlank
= true;
1464 lastWasBlank
= false;
1465 if ('a' <= ch
&& ch
<= 'z') {
1466 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1468 base
[valueCurrentLocation
++] = ch
;
1472 /* Finally if lastWasBlank remove the trailing space. */
1473 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1474 valueCurrentLocation
--;
1476 /* Adjust content length to normalized length. */
1477 valueContentLength
= valueCurrentLocation
- valueLocation
;
1479 /* Number of bytes by which the length should be shorted. */
1480 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1481 if (lengthDiff
== 0) {
1482 /* Easy case no need to adjust lengths. */
1484 /* Hard work we need to go back and fix up length fields
1486 1) The value itself.
1487 2) The ATV Sequence containing type/value
1488 3) The RDN Set containing one or more atv pairs.
1492 /* Step 1 fix up length of value. */
1493 /* Length of value tag and length minus the tag. */
1494 DERSize newValueTLLength
= valueTLLength
- 1;
1495 drtn
= DEREncodeLength(valueContentLength
,
1496 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1497 require(drtn
== DR_Success
, badDER
);
1498 /* Add the length of the tag back in. */
1500 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1502 /* The size of the length field changed, let's slide
1503 the value back by valueLLDiff bytes. */
1504 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1505 base
+ valueTagLocation
+ valueTLLength
,
1506 valueContentLength
);
1507 /* The length diff for the enclosing object. */
1508 lengthDiff
+= valueLLDiff
;
1511 /* Step 2 fix up length of the enclosing ATV Sequence. */
1512 atvContentLength
-= lengthDiff
;
1513 DERSize newATVTLLength
= atvTLLength
- 1;
1514 drtn
= DEREncodeLength(atvContentLength
,
1515 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1516 require(drtn
== DR_Success
, badDER
);
1517 /* Add the length of the tag back in. */
1519 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1521 /* The size of the length field changed, let's slide
1522 the value back by valueLLDiff bytes. */
1523 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1524 base
+ atvTagLocation
+ atvTLLength
,
1526 /* The length diff for the enclosing object. */
1527 lengthDiff
+= atvLLDiff
;
1528 atvTLLength
= newATVTLLength
;
1531 /* Step 3 fix up length of enclosing RDN Set. */
1532 rdnContentLength
-= lengthDiff
;
1533 DERSize newRDNTLLength
= rdnTLLength
- 1;
1534 drtn
= DEREncodeLength(rdnContentLength
,
1535 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1536 require_quiet(drtn
== DR_Success
, badDER
);
1537 /* Add the length of the tag back in. */
1539 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1541 /* The size of the length field changed, let's slide
1542 the value back by valueLLDiff bytes. */
1543 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1544 base
+ rdnTagLocation
+ rdnTLLength
,
1546 /* The length diff for the enclosing object. */
1547 lengthDiff
+= rdnLLDiff
;
1548 rdnTLLength
= newRDNTLLength
;
1550 /* Adjust the locations that might have changed due to
1552 atvTagLocation
-= rdnLLDiff
;
1554 (void) lengthDiff
; // No next object, silence analyzer
1557 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1558 atvTag
= atvSeq
.nextItem
;
1560 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1561 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1562 rdnTag
= rdnSeq
.nextItem
;
1564 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1565 /* Truncate the result to the proper length. */
1566 CFDataSetLength(result
, rdnTagLocation
);
1575 static CFDataRef
SecDERItemCopySequence(DERItem
*content
) {
1576 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
1577 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
1578 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
1580 CFDataSetLength(sequence
, sequence_length
);
1581 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
1582 *sequence_ptr
++ = ONE_BYTE_ASN1_CONSTR_SEQUENCE
;
1583 require_noerr_quiet(DEREncodeLength(content
->length
,
1584 sequence_ptr
, &seq_len_length
), out
);
1585 sequence_ptr
+= seq_len_length
;
1586 memcpy(sequence_ptr
, content
->data
, content
->length
);
1589 CFReleaseSafe(sequence
);
1593 static CFDataRef
SecCopySequenceFromContent(CFDataRef content
) {
1595 tmpItem
.data
= (void *)CFDataGetBytePtr(content
);
1596 tmpItem
.length
= CFDataGetLength(content
);
1598 return SecDERItemCopySequence(&tmpItem
);
1601 CFDataRef
SecDistinguishedNameCopyNormalizedContent(CFDataRef distinguished_name
)
1603 const DERItem name
= { (unsigned char *)CFDataGetBytePtr(distinguished_name
), CFDataGetLength(distinguished_name
) };
1604 DERDecodedInfo content
;
1605 /* Decode top level sequence into DERItem */
1606 if (!DERDecodeItem(&name
, &content
) && (content
.tag
== ASN1_CONSTR_SEQUENCE
))
1607 return createNormalizedX501Name(kCFAllocatorDefault
, &content
.content
);
1611 CFDataRef
SecDistinguishedNameCopyNormalizedSequence(CFDataRef distinguished_name
)
1613 if (!distinguished_name
) { return NULL
; }
1614 CFDataRef normalizedContent
= SecDistinguishedNameCopyNormalizedContent(distinguished_name
);
1615 if (!normalizedContent
) { return NULL
; }
1616 CFDataRef result
= SecCopySequenceFromContent(normalizedContent
);
1617 CFReleaseNull(normalizedContent
);
1621 /* AUDIT[securityd]:
1622 certificate->_der is a caller provided data of any length (might be 0).
1624 Top level certificate decode.
1626 static bool SecCertificateParse(SecCertificateRef certificate
)
1631 require_quiet(certificate
, badCert
);
1632 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1634 /* top level decode */
1635 DERSignedCertCrl signedCert
;
1636 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1637 DERSignedCertCrlItemSpecs
, &signedCert
,
1638 sizeof(signedCert
));
1639 require_noerr_quiet(drtn
, badCert
);
1640 /* Store tbs since we need to digest it for verification later on. */
1641 certificate
->_tbs
= signedCert
.tbs
;
1643 /* decode the TBSCert - it was saved in full DER form */
1645 drtn
= DERParseSequence(&signedCert
.tbs
,
1646 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1647 &tbsCert
, sizeof(tbsCert
));
1648 require_noerr_quiet(drtn
, badCert
);
1650 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1651 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1652 of the params field. */
1653 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1654 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1655 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1656 require_noerr_quiet(drtn
, badCert
);
1658 /* The contents of signedCert.sig is a bit string whose contents
1659 are the signature itself. */
1660 DERByte numUnusedBits
;
1661 drtn
= DERParseBitString(&signedCert
.sig
,
1662 &certificate
->_signature
, &numUnusedBits
);
1663 require_noerr_quiet(drtn
, badCert
);
1665 /* Now decode the tbsCert. */
1667 /* First we turn the optional version into an int. */
1668 if (tbsCert
.version
.length
) {
1669 DERDecodedInfo decoded
;
1670 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1671 require_noerr_quiet(drtn
, badCert
);
1672 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1673 require_quiet(decoded
.content
.length
== 1, badCert
);
1674 certificate
->_version
= decoded
.content
.data
[0];
1675 if (certificate
->_version
> 2) {
1676 secwarning("Invalid certificate version (%d), must be 0..2",
1677 certificate
->_version
);
1679 require_quiet(certificate
->_version
> 0, badCert
);
1680 require_quiet(certificate
->_version
< 3, badCert
);
1682 certificate
->_version
= 0;
1685 /* The serial number is in the tbsCert.serialNum - it was saved in
1686 INTEGER form without the tag and length. */
1687 certificate
->_serialNum
= tbsCert
.serialNum
;
1689 /* Note: RFC5280 4.1.2.2 limits serial number values to 20 octets.
1690 For now, we warn about larger values, but will still create the
1691 certificate with values up to 36 octets to avoid breaking some
1692 nonconforming certs with slightly longer serial numbers.
1693 We also explicitly allow serial numbers of 21 octets where the
1694 leading byte is 0x00 which is used to make a negative 20 octet
1697 if (tbsCert
.serialNum
.length
< 1 || tbsCert
.serialNum
.length
> 21 ||
1698 (tbsCert
.serialNum
.length
== 21 && tbsCert
.serialNum
.data
[0] != 0x00)) {
1699 secwarning("Invalid serial number length (%ld), must be 1..20",
1700 tbsCert
.serialNum
.length
);
1702 require_quiet(tbsCert
.serialNum
.data
!= NULL
&&
1703 tbsCert
.serialNum
.length
>= 1 &&
1704 tbsCert
.serialNum
.length
<= 37, badCert
);
1705 certificate
->_serialNumber
= CFDataCreate(allocator
,
1706 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1708 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1709 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1710 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1711 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1712 require_noerr_quiet(drtn
, badCert
);
1714 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1715 and length fields. */
1716 certificate
->_issuer
= tbsCert
.issuer
;
1717 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1720 /* sequence we're given: decode the tbsCerts Validity sequence. */
1721 DERValidity validity
;
1722 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1723 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1724 &validity
, sizeof(validity
));
1725 require_noerr_quiet(drtn
, badCert
);
1726 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1727 &certificate
->_notBefore
), badCert
);
1728 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1729 &certificate
->_notAfter
), badCert
);
1731 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1732 and length fields. */
1733 certificate
->_subject
= tbsCert
.subject
;
1734 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1737 /* Keep the SPKI around for CT */
1738 certificate
->_subjectPublicKeyInfo
= tbsCert
.subjectPubKey
;
1740 /* sequence we're given: encoded DERSubjPubKeyInfo */
1741 DERSubjPubKeyInfo pubKeyInfo
;
1742 drtn
= DERParseSequenceContent(&tbsCert
.subjectPubKey
,
1743 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1744 &pubKeyInfo
, sizeof(pubKeyInfo
));
1745 require_noerr_quiet(drtn
, badCert
);
1747 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1748 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1749 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1750 &certificate
->_algId
, sizeof(certificate
->_algId
));
1751 require_noerr_quiet(drtn
, badCert
);
1753 /* Now we can figure out the key's algorithm id and params based on
1754 certificate->_algId.oid. */
1756 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1757 are a PKCS1 format RSA key. */
1758 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1759 &certificate
->_pubKeyDER
, &numUnusedBits
);
1760 require_noerr_quiet(drtn
, badCert
);
1762 /* The contents of tbsCert.issuerID is a bit string. */
1763 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1765 /* The contents of tbsCert.subjectID is a bit string. */
1766 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1769 certificate
->_unparseableKnownExtensionIndex
= kCFNotFound
;
1770 if (tbsCert
.extensions
.length
) {
1771 CFIndex extensionCount
= 0;
1774 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1775 require_noerr_quiet(drtn
, badCert
);
1776 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1777 DERDecodedInfo currDecoded
;
1778 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1780 /* ! = MUST recognize ? = SHOULD recognize
1783 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1784 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1785 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1786 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1787 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1788 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1789 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1790 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1792 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1793 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1794 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1795 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1796 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1797 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1798 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1799 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1801 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1802 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1807 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1809 /* Put some upper limit on the number of extensions allowed. */
1810 require_quiet(extensionCount
< 10000, badCert
);
1811 certificate
->_extensionCount
= extensionCount
;
1812 certificate
->_extensions
= malloc(sizeof(SecCertificateExtension
) * (extensionCount
> 0 ? extensionCount
: 1));
1813 require_quiet(certificate
->_extensions
, badCert
);
1816 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1817 require_noerr_quiet(drtn
, badCert
);
1818 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1819 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1820 require_quiet(drtn
== DR_Success
|| (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1821 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1823 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1824 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1825 &extn
, sizeof(extn
));
1826 require_noerr_quiet(drtn
, badCert
);
1827 /* Copy stuff into certificate->extensions[ix]. */
1828 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1829 require_noerr_quiet(drtn
= DERParseBooleanWithDefault(&extn
.critical
, false,
1830 &certificate
->_extensions
[ix
].critical
), badCert
);
1831 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1833 SecCertificateExtensionParser parser
=
1834 (SecCertificateExtensionParser
)CFDictionaryGetValue(sExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1836 /* Invoke the parser. If the extension is critical and the
1837 * parser fails, fail the cert. */
1838 bool parseResult
= parser(certificate
, &certificate
->_extensions
[ix
]);
1840 certificate
->_unparseableKnownExtensionIndex
= ix
;
1842 require_quiet(parseResult
|| !certificate
->_extensions
[ix
].critical
, badCert
);
1843 } else if (certificate
->_extensions
[ix
].critical
) {
1844 if (isAppleExtensionOID(&extn
.extnID
)) {
1847 secdebug("cert", "Found unknown critical extension");
1848 certificate
->_foundUnknownCriticalExtension
= true;
1850 secdebug("cert", "Found unknown non critical extension");
1854 checkForMissingRevocationInfo(certificate
);
1863 /* Public API functions. */
1864 SecCertificateRef
SecCertificateCreateWithBytes(CFAllocatorRef allocator
,
1865 const UInt8
*der_bytes
, CFIndex der_length
) {
1866 if (der_bytes
== NULL
) return NULL
;
1867 if (der_length
== 0) return NULL
;
1869 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1870 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1871 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1873 memset((char*)result
+ sizeof(result
->_base
), 0,
1874 sizeof(*result
) - sizeof(result
->_base
));
1875 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1876 result
->_der
.length
= der_length
;
1877 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1878 if (!SecCertificateParse(result
)) {
1886 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1887 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1888 const UInt8
*der_bytes
, CFIndex der_length
);
1890 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1891 const UInt8
*der_bytes
, CFIndex der_length
) {
1892 return SecCertificateCreateWithBytes(allocator
, der_bytes
, der_length
);
1894 /* @@@ End of placeholder. */
1896 /* AUDIT[securityd](done):
1897 der_certificate is a caller provided data of any length (might be 0), only
1898 its cf type has been checked.
1900 SecCertificateRef
SecCertificateCreateWithData(CFAllocatorRef allocator
,
1901 CFDataRef der_certificate
) {
1902 if (!der_certificate
) {
1905 CFIndex size
= sizeof(struct __SecCertificate
);
1906 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1907 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1909 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1910 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1911 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1912 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1913 if (!SecCertificateParse(result
)) {
1921 SecCertificateRef
SecCertificateCreateWithKeychainItem(CFAllocatorRef allocator
,
1922 CFDataRef der_certificate
,
1923 CFTypeRef keychain_item
)
1925 SecCertificateRef result
= SecCertificateCreateWithData(allocator
, der_certificate
);
1927 CFRetainSafe(keychain_item
);
1928 result
->_keychain_item
= keychain_item
;
1933 OSStatus
SecCertificateSetKeychainItem(SecCertificateRef certificate
,
1934 CFTypeRef keychain_item
)
1939 CFRetainSafe(keychain_item
);
1940 CFReleaseSafe(certificate
->_keychain_item
);
1941 certificate
->_keychain_item
= keychain_item
;
1942 return errSecSuccess
;
1945 CFDataRef
SecCertificateCopyData(SecCertificateRef certificate
) {
1947 CFDataRef result
= NULL
;
1951 if (certificate
->_der_data
) {
1952 CFRetain(certificate
->_der_data
);
1953 result
= certificate
->_der_data
;
1955 result
= CFDataCreate(CFGetAllocator(certificate
),
1956 certificate
->_der
.data
, certificate
->_der
.length
);
1958 /* FIXME: If we wish to cache result we need to lock the certificate.
1959 Also this create 2 copies of the certificate data which is somewhat
1962 certificate
->_der_data
= result
;
1969 CFIndex
SecCertificateGetLength(SecCertificateRef certificate
) {
1970 return certificate
->_der
.length
;
1973 const UInt8
*SecCertificateGetBytePtr(SecCertificateRef certificate
) {
1974 return certificate
->_der
.data
;
1977 static bool SecCertificateIsCertificate(SecCertificateRef certificate
) {
1981 #ifndef IS_TRUSTTESTS
1982 /* TrustTests registers two SecCertificate TypeIDs, so we'll skip this check
1983 * in the tests and just let the tests crash if they pass the wrong object type. */
1984 if (CFGetTypeID(certificate
) != SecCertificateGetTypeID()) {
1991 /* Used to recreate preCert from cert for Certificate Transparency */
1992 CFDataRef
SecCertificateCopyPrecertTBS(SecCertificateRef certificate
)
1994 CFDataRef outData
= NULL
;
1995 DERItem tbsIn
= certificate
->_tbs
;
1996 DERItem tbsOut
= {0,};
1997 DERItem extensionsOut
= {0,};
1998 DERItem
*extensionsList
= malloc(sizeof(DERItem
)*certificate
->_extensionCount
); /* This maybe one too many */
1999 DERItemSpec
*extensionsListSpecs
= malloc(sizeof(DERItemSpec
)*certificate
->_extensionCount
);
2003 require_quiet(extensionsList
&& extensionsListSpecs
, out
);
2005 /* decode the TBSCert - it was saved in full DER form */
2006 drtn
= DERParseSequence(&tbsIn
,
2007 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
2008 &tbsCert
, sizeof(tbsCert
));
2009 require_noerr_quiet(drtn
, out
);
2011 /* Go over extensions and filter any SCT extension */
2012 CFIndex extensionsCount
= 0;
2014 if (tbsCert
.extensions
.length
) {
2017 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
2019 require_noerr_quiet(drtn
, out
);
2020 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
2021 DERDecodedInfo currDecoded
;
2022 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
2024 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, out
);
2026 drtn
= DERParseSequenceContent(&currDecoded
.content
,
2027 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
2028 &extn
, sizeof(extn
));
2029 require_noerr_quiet(drtn
, out
);
2031 if (extn
.extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
2032 !memcmp(extn
.extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
.extnID
.length
))
2035 extensionsList
[extensionsCount
] = currDecoded
.content
;
2036 extensionsListSpecs
[extensionsCount
].offset
= sizeof(DERItem
)*extensionsCount
;
2037 extensionsListSpecs
[extensionsCount
].options
= 0;
2038 extensionsListSpecs
[extensionsCount
].tag
= ASN1_CONSTR_SEQUENCE
;
2043 require_quiet(drtn
== DR_EndOfSequence
, out
);
2047 /* Encode extensions */
2048 extensionsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
);
2049 extensionsOut
.data
= malloc(extensionsOut
.length
);
2050 require_quiet(extensionsOut
.data
, out
);
2051 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
, extensionsOut
.data
, &extensionsOut
.length
);
2052 require_noerr_quiet(drtn
, out
);
2054 tbsCert
.extensions
= extensionsOut
;
2056 tbsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
);
2057 tbsOut
.data
= malloc(tbsOut
.length
);
2058 require_quiet(tbsOut
.data
, out
);
2059 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
, tbsOut
.data
, &tbsOut
.length
);
2060 require_noerr_quiet(drtn
, out
);
2062 outData
= CFDataCreate(kCFAllocatorDefault
, tbsOut
.data
, tbsOut
.length
);
2065 if (extensionsOut
.data
) free(extensionsOut
.data
);
2066 if (tbsOut
.data
) free(tbsOut
.data
);
2067 if (extensionsList
) free(extensionsList
);
2068 if (extensionsListSpecs
) free(extensionsListSpecs
);
2073 /* From rfc3280 - Appendix B. ASN.1 Notes
2075 Object Identifiers (OIDs) are used throughout this specification to
2076 identify certificate policies, public key and signature algorithms,
2077 certificate extensions, etc. There is no maximum size for OIDs.
2078 This specification mandates support for OIDs which have arc elements
2079 with values that are less than 2^28, that is, they MUST be between 0
2080 and 268,435,455, inclusive. This allows each arc element to be
2081 represented within a single 32 bit word. Implementations MUST also
2082 support OIDs where the length of the dotted decimal (see [RFC 2252],
2083 section 4.1) string representation can be up to 100 bytes
2084 (inclusive). Implementations MUST be able to handle OIDs with up to
2085 20 elements (inclusive). CAs SHOULD NOT issue certificates which
2086 contain OIDs that exceed these requirements. Likewise, CRL issuers
2087 SHOULD NOT issue CRLs which contain OIDs that exceed these
2091 /* Oids longer than this are considered invalid. */
2092 #define MAX_OID_SIZE 32
2094 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
2095 const DERItem
*oid
) {
2097 if (oid
->length
== 0) {
2098 return SecCopyCertString(SEC_NULL_KEY
);
2100 if (oid
->length
> MAX_OID_SIZE
) {
2101 return SecCopyCertString(SEC_OID_TOO_LONG_KEY
);
2104 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
2106 // The first two levels are encoded into one byte, since the root level
2107 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
2108 // y may be > 39, so we have to add special-case handling for this.
2109 uint32_t x
= oid
->data
[0] / 40;
2110 uint32_t y
= oid
->data
[0] % 40;
2113 // Handle special case for large y if x = 2
2117 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
2120 for (x
= 1; x
< oid
->length
; ++x
)
2122 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
2123 /* @@@ value may not span more than 4 bytes. */
2124 /* A max number of 20 values is allowed. */
2125 if (!(oid
->data
[x
] & 0x80))
2127 CFStringAppendFormat(result
, NULL
, CFSTR(".%" PRIu32
), value
);
2134 static CFStringRef
copyOidDescription(CFAllocatorRef allocator
,
2135 const DERItem
*oid
, bool localized
) {
2136 if (!oid
|| oid
->length
== 0) {
2137 return (localized
) ? SecCopyCertString(SEC_NULL_KEY
) : SEC_NULL_KEY
;
2140 CFStringRef name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
2145 /* Build the key we use to lookup the localized OID description. */
2146 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
2147 oid
->length
* 3 + 5);
2148 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), oid
->length
);
2149 for (DERSize ix
= 0; ix
< oid
->length
; ++ix
) {
2150 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
2152 CFStringRef locname
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
2153 if (locname
&& !CFEqual(oidKey
, locname
)) {
2154 /* Found localized description string, so use it instead of OID. */
2155 CFReleaseSafe(name
);
2158 CFReleaseSafe(locname
);
2165 /* Return the ipAddress as a dotted quad for ipv4, or as 8 colon separated
2166 4 digit hex strings for ipv6. Return NULL if the provided IP doesn't
2167 have a length of exactly 4 or 16 octets.
2168 Note: hex values are normalized to uppercase.
2170 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
2171 const DERItem
*ip
) {
2172 /* This is the IP Address as an OCTET STRING.
2173 For IPv4 it's 4 octets addr, or 8 octets, addr/mask.
2174 For IPv6 it's 16 octets addr, or 32 octets addr/mask.
2176 CFStringRef value
= NULL
;
2177 if (ip
->length
== IPv4ADDRLEN
) {
2178 value
= CFStringCreateWithFormat(allocator
, NULL
,
2179 CFSTR("%u.%u.%u.%u"),
2180 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
2181 } else if (ip
->length
== IPv6ADDRLEN
) {
2182 value
= CFStringCreateWithFormat(allocator
, NULL
,
2183 CFSTR("%02X%02X:%02X%02X:%02X%02X:%02X%02X:"
2184 "%02X%02X:%02X%02X:%02X%02X:%02X%02X"),
2185 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
2186 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
2187 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
2188 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
2194 void appendProperty(CFMutableArrayRef properties
, CFStringRef propertyType
,
2195 CFStringRef label
, CFStringRef localizedLabel
, CFTypeRef value
,
2197 CFDictionaryRef property
;
2199 CFStringRef ll
= NULL
;
2201 /* use unlocalized label, overriding localizedLabel */
2202 ll
= localizedLabel
= (CFStringRef
) CFRetainSafe(label
);
2203 } else if (!localizedLabel
) {
2204 /* copy localized label for unlocalized label */
2205 ll
= localizedLabel
= SecCopyCertString(label
);
2207 const void *all_keys
[4];
2208 all_keys
[0] = kSecPropertyKeyType
;
2209 all_keys
[1] = kSecPropertyKeyLabel
;
2210 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
2211 all_keys
[3] = kSecPropertyKeyValue
;
2212 const void *property_values
[] = {
2218 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2219 all_keys
, property_values
, value
? 4 : 3,
2220 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2223 const void *nolabel_keys
[2];
2224 nolabel_keys
[0] = kSecPropertyKeyType
;
2225 nolabel_keys
[1] = kSecPropertyKeyValue
;
2226 const void *property_values
[] = {
2230 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2231 nolabel_keys
, property_values
, 2,
2232 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2235 CFArrayAppendValue(properties
, property
);
2236 CFRelease(property
);
2240 #define UTC_TIME_NOSEC_ZULU_LEN 11
2242 #define UTC_TIME_ZULU_LEN 13
2243 /* YYMMDDhhmmssThhmm */
2244 #define UTC_TIME_LOCALIZED_LEN 17
2245 /* YYYYMMDDhhmmssZ */
2246 #define GENERALIZED_TIME_ZULU_LEN 15
2247 /* YYYYMMDDhhmmssThhmm */
2248 #define GENERALIZED_TIME_LOCALIZED_LEN 19
2250 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
2252 static inline int parseDecimalPair(const DERByte
**p
) {
2253 const DERByte
*cp
= *p
;
2255 return 10 * (cp
[0] - '0') + cp
[1] - '0';
2258 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime.
2259 Return a CFErrorRef in the error parameter if decoding fails.
2260 Note that this is needed to distinguish an error condition from a
2261 valid time which specifies 2001-01-01 00:00:00 (i.e. a value of 0).
2263 CFAbsoluteTime
SecAbsoluteTimeFromDateContentWithError(DERTag tag
,
2264 const uint8_t *bytes
,
2266 CFErrorRef
*error
) {
2270 if (NULL
== bytes
|| 0 == length
) {
2274 bool isUtcLength
= false;
2275 bool isLocalized
= false;
2276 bool noSeconds
= false;
2278 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
2282 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
2285 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
2287 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
2290 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
2293 default: /* unknown format */
2297 /* Make sure the der tag fits the thing inside it. */
2298 if (tag
== ASN1_UTC_TIME
) {
2302 } else if (tag
== ASN1_GENERALIZED_TIME
) {
2310 const DERByte
*cp
= bytes
;
2311 /* Check that all characters are digits, except if localized the timezone
2312 indicator or if not localized the 'Z' at the end. */
2314 for (ix
= 0; ix
< length
; ++ix
) {
2315 if (!(isdigit(cp
[ix
]))) {
2316 if ((isLocalized
&& ix
== length
- 5 &&
2317 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
2318 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
2325 /* Parse the date and time fields. */
2326 int year
, month
, day
, hour
, minute
, second
;
2328 year
= parseDecimalPair(&cp
);
2330 /* 0 <= year < 50 : assume century 21 */
2332 } else if (year
< 70) {
2333 /* 50 <= year < 70 : illegal per PKIX */
2336 /* 70 < year <= 99 : assume century 20 */
2340 year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
2342 month
= parseDecimalPair(&cp
);
2343 day
= parseDecimalPair(&cp
);
2344 hour
= parseDecimalPair(&cp
);
2345 minute
= parseDecimalPair(&cp
);
2349 second
= parseDecimalPair(&cp
);
2352 CFTimeInterval timeZoneOffset
;
2354 /* ZONE INDICATOR */
2355 int multiplier
= *cp
++ == '+' ? 60 : -60;
2356 timeZoneOffset
= multiplier
*
2357 (parseDecimalPair(&cp
) * 60 + parseDecimalPair(&cp
));
2362 secdebug("dateparse",
2363 "date %.*s year: %04d%02d%02d%02d%02d%02d%+05g",
2364 (int) length
, bytes
, year
, month
,
2365 day
, hour
, minute
, second
,
2366 timeZoneOffset
/ 60);
2368 static int mdays
[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
2369 int is_leap_year
= year
% 4 == 0 && (year
% 100 != 0 || year
% 400 == 0) ? 1 : 0;
2370 /* Some basic checks on the date, allowing leap seconds */
2371 if (month
< 1 || month
> 12 || day
< 1 || day
> 31 || hour
> 23 || minute
> 59 || second
> 60
2372 || (month
== 2 && day
> mdays
[month
] - mdays
[month
- 1] + is_leap_year
)
2373 || (month
!= 2 && day
> mdays
[month
] - mdays
[month
- 1])) {
2378 int dy
= year
- 2001;
2383 int leap_days
= dy
/ 4 - dy
/ 100 + dy
/ 400;
2384 day
+= ((year
- 2001) * 365 + leap_days
) + mdays
[month
- 1] - 1;
2386 day
+= is_leap_year
;
2388 CFAbsoluteTime absTime
= (CFAbsoluteTime
)((day
* 24.0 + hour
) * 60.0 + minute
) * 60.0 + second
;
2389 return absTime
- timeZoneOffset
;
2393 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
2398 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
2400 return SecAbsoluteTimeFromDateContentWithError(tag
, bytes
, length
, NULL
);
2403 __attribute__((__nonnull__
)) static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
2404 CFAbsoluteTime
*pabsTime
) {
2405 CFErrorRef error
= NULL
;
2406 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContentWithError(tag
, date
->data
,
2407 date
->length
, &error
);
2409 secwarning("Invalid date specification in certificate (see RFC5280 4.1.2.5)");
2414 *pabsTime
= absTime
;
2418 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
2419 true if the date was valid and properly decoded, also return the result in
2420 absTime. Return false otherwise. */
2421 __attribute__((__nonnull__
)) static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
2422 CFAbsoluteTime
*absTime
) {
2423 if (dateChoice
->length
== 0) return false;
2425 DERDecodedInfo decoded
;
2426 if (DERDecodeItem(dateChoice
, &decoded
))
2429 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
2433 static void appendDataProperty(CFMutableArrayRef properties
,
2434 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
,
2436 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
2437 der_data
->data
, der_data
->length
);
2438 appendProperty(properties
, kSecPropertyTypeData
, label
, localizedLabel
,
2443 static void appendRelabeledProperty(CFMutableArrayRef properties
,
2445 CFStringRef localizedLabel
,
2446 const DERItem
*der_data
,
2447 CFStringRef labelFormat
,
2449 CFStringRef newLabel
=
2450 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2451 labelFormat
, label
);
2452 CFStringRef ll
= NULL
;
2453 CFStringRef localizedLabelFormat
= NULL
;
2455 /* use provided label and format strings; do not localize */
2456 ll
= localizedLabel
= (CFStringRef
) CFRetainSafe(label
);
2457 localizedLabelFormat
= (CFStringRef
) CFRetainSafe(labelFormat
);
2459 if (!localizedLabel
) {
2460 /* copy localized label for provided label */
2461 ll
= localizedLabel
= SecCopyCertString(label
);
2463 /* copy localized format for provided format */
2464 localizedLabelFormat
= SecCopyCertString(labelFormat
);
2467 CFStringRef newLocalizedLabel
=
2468 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2469 localizedLabelFormat
, localizedLabel
);
2471 CFReleaseSafe(localizedLabelFormat
);
2472 appendDataProperty(properties
, newLabel
, newLocalizedLabel
, der_data
, localized
);
2473 CFReleaseSafe(newLabel
);
2474 CFReleaseSafe(newLocalizedLabel
);
2478 static void appendUnparsedProperty(CFMutableArrayRef properties
,
2479 CFStringRef label
, CFStringRef localizedLabel
,
2480 const DERItem
*der_data
, bool localized
) {
2481 appendRelabeledProperty(properties
, label
, localizedLabel
, der_data
,
2482 SEC_UNPARSED_KEY
, localized
);
2485 static void appendInvalidProperty(CFMutableArrayRef properties
,
2486 CFStringRef label
, const DERItem
*der_data
, bool localized
) {
2487 appendRelabeledProperty(properties
, label
, NULL
, der_data
,
2488 SEC_INVALID_KEY
, localized
);
2491 static void appendDateContentProperty(CFMutableArrayRef properties
,
2492 CFStringRef label
, DERTag tag
,
2493 const DERItem
*dateContent
, bool localized
) {
2494 CFAbsoluteTime absTime
;
2495 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
2496 /* Date decode failure; insert hex bytes instead. */
2497 return appendInvalidProperty(properties
, label
, dateContent
, localized
);
2499 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2500 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
, localized
);
2504 static void appendDateProperty(CFMutableArrayRef properties
,
2505 CFStringRef label
, CFAbsoluteTime absTime
, bool localized
) {
2506 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2507 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
, localized
);
2511 static void appendValidityPeriodProperty(CFMutableArrayRef parent
, CFStringRef label
,
2512 SecCertificateRef certificate
, bool localized
) {
2513 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2514 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2516 appendDateProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2517 certificate
->_notBefore
, localized
);
2518 appendDateProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2519 certificate
->_notAfter
, localized
);
2521 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
, localized
);
2522 CFReleaseNull(properties
);
2525 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
2526 CFStringRef label
, const DERItem
*ip
, bool localized
) {
2528 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
2530 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
, localized
);
2533 appendUnparsedProperty(properties
, label
, NULL
, ip
, localized
);
2537 static void appendURLContentProperty(CFMutableArrayRef properties
,
2538 CFStringRef label
, const DERItem
*urlContent
, bool localized
) {
2539 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
2540 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2542 appendProperty(properties
, kSecPropertyTypeURL
, label
, NULL
, url
, localized
);
2545 appendInvalidProperty(properties
, label
, urlContent
, localized
);
2549 static void appendURLProperty(CFMutableArrayRef properties
,
2550 CFStringRef label
, const DERItem
*url
, bool localized
) {
2551 DERDecodedInfo decoded
;
2554 drtn
= DERDecodeItem(url
, &decoded
);
2555 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2556 appendInvalidProperty(properties
, label
, url
, localized
);
2558 appendURLContentProperty(properties
, label
, &decoded
.content
, localized
);
2562 static void appendOIDProperty(CFMutableArrayRef properties
,
2563 CFStringRef label
, CFStringRef llabel
, const DERItem
*oid
, bool localized
) {
2564 CFStringRef oid_string
=
2565 copyOidDescription(CFGetAllocator(properties
), oid
, localized
);
2566 appendProperty(properties
, kSecPropertyTypeString
, label
, llabel
,
2567 oid_string
, localized
);
2568 CFRelease(oid_string
);
2571 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2572 CFStringRef label
, const DERAlgorithmId
*algorithm
, bool localized
) {
2573 CFMutableArrayRef alg_props
=
2574 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2575 &kCFTypeArrayCallBacks
);
2576 appendOIDProperty(alg_props
, SEC_ALGORITHM_KEY
, NULL
,
2577 &algorithm
->oid
, localized
);
2578 if (algorithm
->params
.length
) {
2579 if (algorithm
->params
.length
== 2 &&
2580 algorithm
->params
.data
[0] == ASN1_NULL
&&
2581 algorithm
->params
.data
[1] == 0) {
2582 CFStringRef value
= SecCopyCertString(SEC_NONE_KEY
);
2583 appendProperty(alg_props
, kSecPropertyTypeString
,
2584 SEC_PARAMETERS_KEY
, NULL
, value
, localized
);
2587 appendUnparsedProperty(alg_props
, SEC_PARAMETERS_KEY
, NULL
,
2588 &algorithm
->params
, localized
);
2591 appendProperty(properties
, kSecPropertyTypeSection
, label
, NULL
,
2592 alg_props
, localized
);
2593 CFRelease(alg_props
);
2596 static void appendPublicKeyProperty(CFMutableArrayRef parent
, CFStringRef label
,
2597 SecCertificateRef certificate
, bool localized
) {
2598 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2599 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2601 /* Public key algorithm. */
2602 appendAlgorithmProperty(properties
, SEC_PUBLIC_KEY_ALG_KEY
,
2603 &certificate
->_algId
, localized
);
2605 /* Public Key Size */
2606 SecKeyRef publicKey
= SecCertificateCopyKey(certificate
);
2608 size_t sizeInBytes
= SecKeyGetBlockSize(publicKey
);
2609 CFStringRef sizeInBitsString
= CFStringCreateWithFormat(allocator
, NULL
,
2610 CFSTR("%ld"), (sizeInBytes
*8));
2611 if (sizeInBitsString
) {
2612 appendProperty(properties
, kSecPropertyTypeString
, SEC_PUBLIC_KEY_SIZE_KEY
,
2613 NULL
, sizeInBitsString
, localized
);
2615 CFReleaseNull(sizeInBitsString
);
2617 CFReleaseNull(publicKey
);
2619 /* Consider breaking down an RSA public key into modulus and
2621 appendDataProperty(properties
, SEC_PUBLIC_KEY_DATA_KEY
, NULL
,
2622 &certificate
->_pubKeyDER
, localized
);
2624 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2625 properties
, localized
);
2626 CFReleaseNull(properties
);
2629 static void appendSignatureProperty(CFMutableArrayRef parent
, CFStringRef label
,
2630 SecCertificateRef certificate
, bool localized
) {
2631 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2632 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2634 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
2635 &certificate
->_tbsSigAlg
, localized
);
2637 appendDataProperty(properties
, SEC_SIGNATURE_DATA_KEY
, NULL
,
2638 &certificate
->_signature
, localized
);
2640 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2641 properties
, localized
);
2642 CFReleaseNull(properties
);
2645 static void appendFingerprintsProperty(CFMutableArrayRef parent
, CFStringRef label
,
2646 SecCertificateRef certificate
, bool localized
) {
2647 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2648 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2650 CFDataRef sha256Fingerprint
= SecCertificateCopySHA256Digest(certificate
);
2651 if (sha256Fingerprint
) {
2652 appendProperty(properties
, kSecPropertyTypeData
, SEC_SHA2_FINGERPRINT_KEY
,
2653 NULL
, sha256Fingerprint
, localized
);
2655 CFReleaseNull(sha256Fingerprint
);
2657 appendProperty(properties
, kSecPropertyTypeData
, SEC_SHA1_FINGERPRINT_KEY
,
2658 NULL
, SecCertificateGetSHA1Digest(certificate
), localized
);
2660 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2661 properties
, localized
);
2662 CFReleaseNull(properties
);
2665 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2666 const DERItem
*blob
) {
2667 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2668 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2669 blob
->length
* 3 - 1);
2670 for (ix
= 0; ix
< length
; ++ix
)
2672 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2674 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2679 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2680 CFStringRef blobType
, CFStringRef quanta
,
2681 const DERItem
*blob
, bool localized
) {
2682 CFStringRef localizedBlobType
= (localized
) ?
2683 SecCopyCertString(blobType
) : (CFStringRef
) CFRetainSafe(blobType
);
2684 CFStringRef localizedQuanta
= (localized
) ?
2685 SecCopyCertString(quanta
) : (CFStringRef
) CFRetainSafe(quanta
);
2686 /* "format string for encoded field data (e.g. Sequence; 128 bytes; "
2687 "data = 00 00 ...)" */
2688 CFStringRef blobFormat
= (localized
) ?
2689 SecCopyCertString(SEC_BLOB_KEY
) : SEC_BLOB_KEY
;
2690 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2691 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2692 blobFormat
, localizedBlobType
, blob
->length
, localizedQuanta
, hex
);
2694 CFRelease(blobFormat
);
2695 CFReleaseSafe(localizedQuanta
);
2696 CFReleaseSafe(localizedBlobType
);
2701 /* Return a string verbatim (unlocalized) from a DER field. */
2702 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2703 const DERItem
*string
, CFStringEncoding encoding
,
2704 bool printableOnly
) {
2705 /* Strip potential bogus trailing zero from printable strings. */
2706 DERSize length
= string
->length
;
2707 if (length
&& string
->data
[length
- 1] == 0) {
2708 /* Don't mess with the length of UTF16 strings though. */
2709 if (encoding
!= kCFStringEncodingUTF16
)
2712 /* A zero length string isn't considered printable. */
2713 if (!length
&& printableOnly
)
2716 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2717 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2718 passing false makes it treat it as native endian by default. */
2719 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2720 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2724 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2727 /* From rfc3280 - Appendix B. ASN.1 Notes
2729 CAs MUST force the serialNumber to be a non-negative integer, that
2730 is, the sign bit in the DER encoding of the INTEGER value MUST be
2731 zero - this can be done by adding a leading (leftmost) `00'H octet if
2732 necessary. This removes a potential ambiguity in mapping between a
2733 string of octets and an integer value.
2735 As noted in section 4.1.2.2, serial numbers can be expected to
2736 contain long integers. Certificate users MUST be able to handle
2737 serialNumber values up to 20 octets in length. Conformant CAs MUST
2738 NOT use serialNumber values longer than 20 octets.
2741 /* Return the given numeric data as a string: decimal up to 64 bits,
2744 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2745 const DERItem
*integer
) {
2747 CFIndex ix
, length
= integer
->length
;
2749 if (length
== 0 || length
> 8)
2750 return copyHexDescription(allocator
, integer
);
2752 for(ix
= 0; ix
< length
; ++ix
) {
2754 value
+= integer
->data
[ix
];
2757 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2760 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2761 DERTag tag
, const DERItem
*derThing
, bool printableOnly
, bool localized
) {
2762 if (!derThing
) { return NULL
; }
2766 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2767 case ASN1_PRINTABLE_STRING
:
2768 case ASN1_IA5_STRING
:
2769 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2770 case ASN1_UTF8_STRING
:
2771 case ASN1_GENERAL_STRING
:
2772 case ASN1_UNIVERSAL_STRING
:
2773 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2774 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2775 case ASN1_VIDEOTEX_STRING
: // 21
2776 case ASN1_VISIBLE_STRING
: // 26
2777 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2778 case ASN1_BMP_STRING
: // 30
2779 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2780 case ASN1_OCTET_STRING
:
2781 return printableOnly
? NULL
:
2782 copyBlobString(allocator
, SEC_BYTE_STRING_KEY
, SEC_BYTES_KEY
,
2783 derThing
, localized
);
2784 case ASN1_BIT_STRING
:
2785 return printableOnly
? NULL
:
2786 copyBlobString(allocator
, SEC_BIT_STRING_KEY
, SEC_BITS_KEY
,
2787 derThing
, localized
);
2788 case ASN1_CONSTR_SEQUENCE
:
2789 return printableOnly
? NULL
:
2790 copyBlobString(allocator
, SEC_SEQUENCE_KEY
, SEC_BYTES_KEY
,
2791 derThing
, localized
);
2792 case ASN1_CONSTR_SET
:
2793 return printableOnly
? NULL
:
2794 copyBlobString(allocator
, SEC_SET_KEY
, SEC_BYTES_KEY
,
2795 derThing
, localized
);
2796 case ASN1_OBJECT_ID
:
2797 return printableOnly
? NULL
: copyOidDescription(allocator
, derThing
, localized
);
2799 if (printableOnly
) {
2802 CFStringRef fmt
= (localized
) ?
2803 SecCopyCertString(SEC_NOT_DISPLAYED_KEY
) : SEC_NOT_DISPLAYED_KEY
;
2804 if (!fmt
) { return NULL
; }
2805 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
2806 (unsigned long)tag
, (unsigned long)derThing
->length
);
2813 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2814 const DERItem
*derThing
, bool printableOnly
, bool localized
) {
2815 DERDecodedInfo decoded
;
2818 drtn
= DERDecodeItem(derThing
, &decoded
);
2820 /* TODO: Perhaps put something in the label saying we couldn't parse
2822 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2824 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2825 &decoded
.content
, false, localized
);
2829 static void appendDERThingProperty(CFMutableArrayRef properties
,
2830 CFStringRef label
, CFStringRef localizedLabel
,
2831 const DERItem
*derThing
, bool localized
) {
2832 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2833 derThing
, false, localized
);
2835 appendProperty(properties
, kSecPropertyTypeString
, label
, localizedLabel
,
2838 CFReleaseSafe(value
);
2841 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2842 const DERItem
*rdnValue
, CFIndex rdnIX
,
2844 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2846 /* If there is more than one value pair we create a subsection for the
2847 second pair, and append things to the subsection for subsequent
2849 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2850 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2852 /* Since this is the second rdn pair for a given rdn, we setup a
2853 new subsection for this rdn. We remove the first property
2854 from the properties array and make it the first element in the
2855 subsection instead. */
2856 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2857 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2858 CFArrayAppendValue(rdn_props
, lastValue
);
2859 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2860 appendProperty(properties
, kSecPropertyTypeSection
, NULL
, NULL
,
2861 rdn_props
, localized
);
2862 properties
= rdn_props
;
2863 // rdn_props is now retained by the original properties array
2864 CFReleaseSafe(rdn_props
);
2866 /* Since this is the third or later rdn pair we have already
2867 created a subsection in the top level properties array. Instead
2868 of appending to that directly we append to the array inside the
2870 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2871 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2875 /* Finally we append the new rdn value to the property array. */
2877 SecDERItemCopyOIDDecimalRepresentation(CFGetAllocator(properties
),
2879 CFStringRef localizedLabel
= copyOidDescription(CFGetAllocator(properties
),
2880 rdnType
, localized
);
2881 appendDERThingProperty(properties
, label
, localizedLabel
,
2882 rdnValue
, localized
);
2883 CFReleaseSafe(label
);
2884 CFReleaseSafe(localizedLabel
);
2885 return errSecSuccess
;
2888 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2889 const DERItem
*rdnSetContent
, bool localized
) {
2890 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2891 &kCFTypeArrayCallBacks
);
2892 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2893 appendRDNProperty
, localized
);
2895 CFArrayRemoveAllValues(properties
);
2896 appendInvalidProperty(properties
, SEC_RDN_KEY
, rdnSetContent
,
2904 From rfc3739 - 3.1.2. Subject
2906 When parsing the subject here are some tips for a short name of the cert.
2907 Choice I: commonName
2908 Choice II: givenName
2909 Choice III: pseudonym
2911 The commonName attribute value SHALL, when present, contain a name
2912 of the subject. This MAY be in the subject's preferred
2913 presentation format, or a format preferred by the CA, or some
2914 other format. Pseudonyms, nicknames, and names with spelling
2915 other than defined by the registered name MAY be used. To
2916 understand the nature of the name presented in commonName,
2917 complying applications MAY have to examine present values of the
2918 givenName and surname attributes, or the pseudonym attribute.
2921 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2922 const DERItem
*x501NameContent
, bool localized
) {
2923 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2924 &kCFTypeArrayCallBacks
);
2925 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2926 appendRDNProperty
, localized
);
2928 CFArrayRemoveAllValues(properties
);
2929 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
,
2930 x501NameContent
, localized
);
2936 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2937 const DERItem
*x501Name
, bool localized
) {
2938 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2939 &kCFTypeArrayCallBacks
);
2940 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
, localized
);
2942 CFArrayRemoveAllValues(properties
);
2943 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
,
2944 x501Name
, localized
);
2950 static void appendIntegerProperty(CFMutableArrayRef properties
,
2951 CFStringRef label
, const DERItem
*integer
, bool localized
) {
2952 CFStringRef string
= copyIntegerContentDescription(
2953 CFGetAllocator(properties
), integer
);
2954 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2959 static void appendBoolProperty(CFMutableArrayRef properties
,
2960 CFStringRef label
, bool boolean
, bool localized
) {
2961 CFStringRef key
= (boolean
) ? SEC_YES_KEY
: SEC_NO_KEY
;
2962 CFStringRef value
= (localized
) ? SecCopyCertString(key
) : key
;
2963 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2968 static void appendBooleanProperty(CFMutableArrayRef properties
,
2969 CFStringRef label
, const DERItem
*boolean
,
2970 bool defaultValue
, bool localized
) {
2972 DERReturn drtn
= DERParseBooleanWithDefault(boolean
, defaultValue
, &result
);
2974 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2975 appendInvalidProperty(properties
, label
, boolean
, localized
);
2977 appendBoolProperty(properties
, label
, result
, localized
);
2981 static void appendSerialNumberProperty(CFMutableArrayRef parent
, CFStringRef label
,
2982 DERItem
*serialNum
, bool localized
) {
2983 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2984 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2986 if (serialNum
->length
) {
2987 appendIntegerProperty(properties
, SEC_SERIAL_NUMBER_KEY
,
2988 serialNum
, localized
);
2989 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2990 properties
, localized
);
2993 CFReleaseNull(properties
);
2996 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2997 CFStringRef label
, const DERItem
*bitStringContent
,
2998 const CFStringRef
*names
, CFIndex namesCount
,
3000 DERSize len
= bitStringContent
->length
- 1;
3001 require_quiet(len
== 1 || len
== 2, badDER
);
3002 DERByte numUnusedBits
= bitStringContent
->data
[0];
3003 require_quiet(numUnusedBits
< 8, badDER
);
3004 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
3005 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
3006 uint_fast16_t value
= bitStringContent
->data
[1];
3009 value
= (value
<< 8) + bitStringContent
->data
[2];
3015 CFStringRef fmt
= (localized
) ?
3016 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
3017 CFStringRef string
= NULL
;
3018 for (ix
= 0; ix
< bits
; ++ix
) {
3019 CFStringRef localizedName
= (localized
) ? SecCopyCertString(names
[ix
]) : CFRetainSafe(names
[ix
]);
3023 CFStringCreateWithFormat(CFGetAllocator(properties
),
3024 NULL
, fmt
, string
, localizedName
);
3028 string
= localizedName
;
3033 CFReleaseNull(localizedName
);
3036 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3037 string
? string
: CFSTR(""), localized
);
3038 CFReleaseSafe(string
);
3041 appendInvalidProperty(properties
, label
, bitStringContent
, localized
);
3044 static void appendBitStringNames(CFMutableArrayRef properties
,
3045 CFStringRef label
, const DERItem
*bitString
,
3046 const CFStringRef
*names
, CFIndex namesCount
,
3048 DERDecodedInfo bitStringContent
;
3049 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
3050 require_noerr_quiet(drtn
, badDER
);
3051 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
3052 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
3053 names
, namesCount
, localized
);
3056 appendInvalidProperty(properties
, label
, bitString
, localized
);
3059 static void appendKeyUsage(CFMutableArrayRef properties
,
3060 const DERItem
*extnValue
, bool localized
) {
3061 static const CFStringRef usageNames
[] = {
3062 SEC_DIGITAL_SIGNATURE_KEY
,
3063 SEC_NON_REPUDIATION_KEY
,
3064 SEC_KEY_ENCIPHERMENT_KEY
,
3065 SEC_DATA_ENCIPHERMENT_KEY
,
3066 SEC_KEY_AGREEMENT_KEY
,
3069 SEC_ENCIPHER_ONLY_KEY
,
3070 SEC_DECIPHER_ONLY_KEY
3072 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3073 usageNames
, array_size(usageNames
), localized
);
3076 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
3077 const DERItem
*extnValue
, bool localized
) {
3078 DERPrivateKeyUsagePeriod pkup
;
3079 DERReturn drtn
= DERParseSequence(extnValue
,
3080 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
3081 &pkup
, sizeof(pkup
));
3082 require_noerr_quiet(drtn
, badDER
);
3083 if (pkup
.notBefore
.length
) {
3084 appendDateContentProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
3085 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
, localized
);
3087 if (pkup
.notAfter
.length
) {
3088 appendDateContentProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
3089 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
, localized
);
3093 appendInvalidProperty(properties
, SEC_PRIVATE_KU_PERIOD_KEY
,
3094 extnValue
, localized
);
3097 static void appendStringContentProperty(CFMutableArrayRef properties
,
3098 CFStringRef label
, const DERItem
*stringContent
,
3099 CFStringEncoding encoding
, bool localized
) {
3100 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
3101 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
3103 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3107 appendInvalidProperty(properties
, label
, stringContent
, localized
);
3112 OtherName ::= SEQUENCE {
3113 type-id OBJECT IDENTIFIER,
3114 value [0] EXPLICIT ANY DEFINED BY type-id }
3116 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
3117 const DERItem
*otherNameContent
, bool localized
) {
3119 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
3120 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
3122 require_noerr_quiet(drtn
, badDER
);
3123 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3125 SecDERItemCopyOIDDecimalRepresentation(allocator
, &on
.typeIdentifier
);
3126 CFStringRef localizedLabel
=
3127 copyOidDescription(allocator
, &on
.typeIdentifier
, localized
);
3128 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
,
3131 appendProperty(properties
, kSecPropertyTypeString
, label
,
3132 localizedLabel
, value_string
, localized
);
3134 appendUnparsedProperty(properties
, label
, localizedLabel
,
3135 &on
.value
, localized
);
3137 CFReleaseSafe(value_string
);
3138 CFReleaseSafe(label
);
3139 CFReleaseSafe(localizedLabel
);
3142 appendInvalidProperty(properties
, SEC_OTHER_NAME_KEY
,
3143 otherNameContent
, localized
);
3147 GeneralName ::= CHOICE {
3148 otherName [0] OtherName,
3149 rfc822Name [1] IA5String,
3150 dNSName [2] IA5String,
3151 x400Address [3] ORAddress,
3152 directoryName [4] Name,
3153 ediPartyName [5] EDIPartyName,
3154 uniformResourceIdentifier [6] IA5String,
3155 iPAddress [7] OCTET STRING,
3156 registeredID [8] OBJECT IDENTIFIER}
3158 EDIPartyName ::= SEQUENCE {
3159 nameAssigner [0] DirectoryString OPTIONAL,
3160 partyName [1] DirectoryString }
3162 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
3163 DERTag tag
, const DERItem
*generalName
, bool localized
) {
3165 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
3166 appendOtherNameContentProperty(properties
, generalName
, localized
);
3168 case ASN1_CONTEXT_SPECIFIC
| 1:
3170 appendStringContentProperty(properties
, SEC_EMAIL_ADDRESS_KEY
,
3171 generalName
, kCFStringEncodingASCII
, localized
);
3173 case ASN1_CONTEXT_SPECIFIC
| 2:
3175 appendStringContentProperty(properties
, SEC_DNS_NAME_KEY
, generalName
,
3176 kCFStringEncodingASCII
, localized
);
3178 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
3179 appendUnparsedProperty(properties
, SEC_X400_ADDRESS_KEY
, NULL
,
3180 generalName
, localized
);
3182 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
3184 CFArrayRef directory_plist
=
3185 createPropertiesForX501Name(CFGetAllocator(properties
),
3186 generalName
, localized
);
3187 appendProperty(properties
, kSecPropertyTypeSection
,
3188 SEC_DIRECTORY_NAME_KEY
, NULL
, directory_plist
, localized
);
3189 CFRelease(directory_plist
);
3192 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
3193 appendUnparsedProperty(properties
, SEC_EDI_PARTY_NAME_KEY
, NULL
,
3194 generalName
, localized
);
3196 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
3197 /* Technically I don't think this is valid, but there are certs out
3198 in the wild that use a constructed IA5String. In particular the
3199 VeriSign Time Stamping Authority CA.cer does this. */
3200 appendURLProperty(properties
, SEC_URI_KEY
, generalName
, localized
);
3202 case ASN1_CONTEXT_SPECIFIC
| 6:
3203 appendURLContentProperty(properties
, SEC_URI_KEY
, generalName
, localized
);
3205 case ASN1_CONTEXT_SPECIFIC
| 7:
3206 appendIPAddressContentProperty(properties
, SEC_IP_ADDRESS_KEY
,
3207 generalName
, localized
);
3209 case ASN1_CONTEXT_SPECIFIC
| 8:
3210 appendOIDProperty(properties
, SEC_REGISTERED_ID_KEY
, NULL
,
3211 generalName
, localized
);
3222 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
3223 const DERItem
*generalName
, bool localized
) {
3224 DERDecodedInfo generalNameContent
;
3225 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
3226 require_noerr_quiet(drtn
, badDER
);
3227 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
3228 &generalNameContent
.content
, localized
))
3231 appendInvalidProperty(properties
, SEC_GENERAL_NAME_KEY
,
3232 generalName
, localized
);
3237 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
3239 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
3240 const DERItem
*generalNamesContent
, bool localized
) {
3242 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
3243 require_noerr_quiet(drtn
, badDER
);
3244 DERDecodedInfo generalNameContent
;
3245 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
3247 if (!appendGeneralNameContentProperty(properties
,
3248 generalNameContent
.tag
, &generalNameContent
.content
, localized
)) {
3252 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3255 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
3256 generalNamesContent
, localized
);
3259 static void appendGeneralNames(CFMutableArrayRef properties
,
3260 const DERItem
*generalNames
, bool localized
) {
3261 DERDecodedInfo generalNamesContent
;
3262 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
3263 require_noerr_quiet(drtn
, badDER
);
3264 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
3266 appendGeneralNamesContent(properties
, &generalNamesContent
.content
,
3270 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
3271 generalNames
, localized
);
3275 BasicConstraints ::= SEQUENCE {
3276 cA BOOLEAN DEFAULT FALSE,
3277 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
3279 static void appendBasicConstraints(CFMutableArrayRef properties
,
3280 const DERItem
*extnValue
, bool localized
) {
3281 DERBasicConstraints basicConstraints
;
3282 DERReturn drtn
= DERParseSequence(extnValue
,
3283 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
3284 &basicConstraints
, sizeof(basicConstraints
));
3285 require_noerr_quiet(drtn
, badDER
);
3287 appendBooleanProperty(properties
, SEC_CERT_AUTHORITY_KEY
,
3288 &basicConstraints
.cA
, false, localized
);
3290 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
3291 appendIntegerProperty(properties
, SEC_PATH_LEN_CONSTRAINT_KEY
,
3292 &basicConstraints
.pathLenConstraint
, localized
);
3296 appendInvalidProperty(properties
, SEC_BASIC_CONSTRAINTS_KEY
,
3297 extnValue
, localized
);
3301 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
3303 * NameConstraints ::= SEQUENCE {
3304 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
3305 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
3307 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
3309 * GeneralSubtree ::= SEQUENCE {
3311 * minimum [0] BaseDistance DEFAULT 0,
3312 * maximum [1] BaseDistance OPTIONAL }
3314 * BaseDistance ::= INTEGER (0..MAX)
3316 static void appendNameConstraints(CFMutableArrayRef properties
,
3317 const DERItem
*extnValue
, bool localized
) {
3318 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3319 DERNameConstraints nc
;
3321 drtn
= DERParseSequence(extnValue
,
3322 DERNumNameConstraintsItemSpecs
,
3323 DERNameConstraintsItemSpecs
,
3325 require_noerr_quiet(drtn
, badDER
);
3326 if (nc
.permittedSubtrees
.length
) {
3328 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.permittedSubtrees
, &gsSeq
), badDER
);
3329 DERDecodedInfo gsContent
;
3330 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3331 DERGeneralSubtree derGS
;
3332 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3333 drtn
= DERParseSequenceContent(&gsContent
.content
,
3334 DERNumGeneralSubtreeItemSpecs
,
3335 DERGeneralSubtreeItemSpecs
,
3336 &derGS
, sizeof(derGS
));
3337 require_noerr_quiet(drtn
, badDER
);
3338 if (derGS
.minimum
.length
) {
3339 appendIntegerProperty(properties
, SEC_PERMITTED_MINIMUM_KEY
,
3340 &derGS
.minimum
, localized
);
3342 if (derGS
.maximum
.length
) {
3343 appendIntegerProperty(properties
, SEC_PERMITTED_MAXIMUM_KEY
,
3344 &derGS
.maximum
, localized
);
3346 if (derGS
.generalName
.length
) {
3347 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3348 &kCFTypeArrayCallBacks
);
3349 appendProperty(properties
, kSecPropertyTypeSection
,
3350 SEC_PERMITTED_NAME_KEY
, NULL
, base
, localized
);
3351 appendGeneralNameProperty(base
, &derGS
.generalName
, localized
);
3355 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3357 if (nc
.excludedSubtrees
.length
) {
3359 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.excludedSubtrees
, &gsSeq
), badDER
);
3360 DERDecodedInfo gsContent
;
3361 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3362 DERGeneralSubtree derGS
;
3363 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3364 drtn
= DERParseSequenceContent(&gsContent
.content
,
3365 DERNumGeneralSubtreeItemSpecs
,
3366 DERGeneralSubtreeItemSpecs
,
3367 &derGS
, sizeof(derGS
));
3368 require_noerr_quiet(drtn
, badDER
);
3369 if (derGS
.minimum
.length
) {
3370 appendIntegerProperty(properties
, SEC_EXCLUDED_MINIMUM_KEY
,
3371 &derGS
.minimum
, localized
);
3373 if (derGS
.maximum
.length
) {
3374 appendIntegerProperty(properties
, SEC_EXCLUDED_MAXIMUM_KEY
,
3375 &derGS
.maximum
, localized
);
3377 if (derGS
.generalName
.length
) {
3378 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3379 &kCFTypeArrayCallBacks
);
3380 appendProperty(properties
, kSecPropertyTypeSection
,
3381 SEC_EXCLUDED_NAME_KEY
, NULL
, base
, localized
);
3382 appendGeneralNameProperty(base
, &derGS
.generalName
, localized
);
3386 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3391 appendInvalidProperty(properties
, SEC_NAME_CONSTRAINTS_KEY
,
3392 extnValue
, localized
);
3396 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
3398 DistributionPoint ::= SEQUENCE {
3399 distributionPoint [0] DistributionPointName OPTIONAL,
3400 reasons [1] ReasonFlags OPTIONAL,
3401 cRLIssuer [2] GeneralNames OPTIONAL }
3403 DistributionPointName ::= CHOICE {
3404 fullName [0] GeneralNames,
3405 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
3407 ReasonFlags ::= BIT STRING {
3411 affiliationChanged (3),
3413 cessationOfOperation (5),
3414 certificateHold (6),
3415 privilegeWithdrawn (7),
3418 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
3419 const DERItem
*extnValue
, bool localized
) {
3420 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3423 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
3424 require_noerr_quiet(drtn
, badDER
);
3425 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3426 DERDecodedInfo dpSeqContent
;
3427 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
3428 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3429 DERDistributionPoint dp
;
3430 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
3431 DERNumDistributionPointItemSpecs
,
3432 DERDistributionPointItemSpecs
,
3434 require_noerr_quiet(drtn
, badDER
);
3435 if (dp
.distributionPoint
.length
) {
3436 DERDecodedInfo distributionPointName
;
3437 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
3438 require_noerr_quiet(drtn
, badDER
);
3439 if (distributionPointName
.tag
==
3440 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
3442 appendGeneralNamesContent(properties
,
3443 &distributionPointName
.content
, localized
);
3444 } else if (distributionPointName
.tag
==
3445 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
3446 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
3447 &dp
.reasons
, localized
);
3448 appendProperty(properties
, kSecPropertyTypeSection
,
3449 SEC_NAME_REL_CRL_ISSUER_KEY
, NULL
, rdn_props
, localized
);
3450 CFRelease(rdn_props
);
3455 if (dp
.reasons
.length
) {
3456 static const CFStringRef reasonNames
[] = {
3458 SEC_KEY_COMPROMISE_KEY
,
3459 SEC_CA_COMPROMISE_KEY
,
3460 SEC_AFFILIATION_CHANGED_KEY
,
3462 SEC_CESSATION_OF_OPER_KEY
,
3463 SEC_CERTIFICATE_HOLD_KEY
,
3464 SEC_PRIV_WITHDRAWN_KEY
,
3465 SEC_AA_COMPROMISE_KEY
3467 appendBitStringContentNames(properties
, SEC_REASONS_KEY
,
3469 reasonNames
, array_size(reasonNames
), localized
);
3471 if (dp
.cRLIssuer
.length
) {
3472 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
3473 &kCFTypeArrayCallBacks
);
3474 appendProperty(properties
, kSecPropertyTypeSection
,
3475 SEC_CRL_ISSUER_KEY
, NULL
, crlIssuer
, localized
);
3476 CFRelease(crlIssuer
);
3477 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
, localized
);
3480 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3483 appendInvalidProperty(properties
, SEC_CRL_DISTR_POINTS_KEY
,
3484 extnValue
, localized
);
3488 Decode a sequence of integers into a comma separated list of ints.
3490 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
3491 CFStringRef label
, const DERItem
*intSequenceContent
,
3493 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3495 CFStringRef fmt
= NULL
, value
= NULL
, intDesc
= NULL
, v
= NULL
;
3496 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
3497 require_noerr_quiet(drtn
, badDER
);
3498 DERDecodedInfo intContent
;
3500 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
3501 require_quiet(fmt
, badDER
);
3502 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
)) == DR_Success
) {
3503 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
3504 intDesc
= copyIntegerContentDescription(
3505 allocator
, &intContent
.content
);
3506 require_quiet(intDesc
, badDER
);
3508 v
= CFStringCreateWithFormat(allocator
, NULL
, fmt
, value
, intDesc
);
3509 CFReleaseNull(value
);
3510 require_quiet(v
, badDER
);
3513 value
= CFStringCreateMutableCopy(allocator
, 0, intDesc
);
3514 require_quiet(value
, badDER
);
3516 CFReleaseNull(intDesc
);
3519 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3521 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3526 /* DROPTHOUGH if !value. */
3529 CFReleaseNull(intDesc
);
3530 CFReleaseNull(value
);
3531 appendInvalidProperty(properties
, label
, intSequenceContent
, localized
);
3534 static void appendCertificatePolicies(CFMutableArrayRef properties
,
3535 const DERItem
*extnValue
, bool localized
) {
3536 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3537 CFStringRef piLabel
= NULL
, piFmt
= NULL
, lpiLabel
= NULL
;
3538 CFStringRef pqLabel
= NULL
, pqFmt
= NULL
, lpqLabel
= NULL
;
3541 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
3542 require_noerr_quiet(drtn
, badDER
);
3543 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3544 DERDecodedInfo piContent
;
3546 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
3547 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3548 DERPolicyInformation pi
;
3549 drtn
= DERParseSequenceContent(&piContent
.content
,
3550 DERNumPolicyInformationItemSpecs
,
3551 DERPolicyInformationItemSpecs
,
3553 require_noerr_quiet(drtn
, badDER
);
3554 piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3555 SEC_POLICY_IDENTIFIER_KEY
, pin
);
3556 require_quiet(piLabel
, badDER
);
3557 piFmt
= (localized
) ?
3558 SecCopyCertString(SEC_POLICY_IDENTIFIER_KEY
) : SEC_POLICY_IDENTIFIER_KEY
;
3559 require_quiet(piFmt
, badDER
);
3560 lpiLabel
= CFStringCreateWithFormat(allocator
, NULL
, piFmt
, pin
++);
3561 require_quiet(lpiLabel
, badDER
);
3562 CFReleaseNull(piFmt
);
3563 appendOIDProperty(properties
, piLabel
, lpiLabel
,
3564 &pi
.policyIdentifier
, localized
);
3565 CFReleaseNull(piLabel
);
3566 CFReleaseNull(lpiLabel
);
3567 if (pi
.policyQualifiers
.length
== 0)
3571 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
3572 require_noerr_quiet(drtn
, badDER
);
3573 DERDecodedInfo pqContent
;
3575 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
3576 DERPolicyQualifierInfo pqi
;
3577 drtn
= DERParseSequenceContent(&pqContent
.content
,
3578 DERNumPolicyQualifierInfoItemSpecs
,
3579 DERPolicyQualifierInfoItemSpecs
,
3581 require_noerr_quiet(drtn
, badDER
);
3582 DERDecodedInfo qualifierContent
;
3583 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
3584 require_noerr_quiet(drtn
, badDER
);
3585 pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3586 SEC_POLICY_QUALIFIER_KEY
, pqn
);
3587 require_quiet(pqLabel
, badDER
);
3588 pqFmt
= (localized
) ?
3589 SecCopyCertString(SEC_POLICY_QUALIFIER_KEY
) : SEC_POLICY_QUALIFIER_KEY
;
3590 require_quiet(pqFmt
, badDER
);
3591 lpqLabel
= CFStringCreateWithFormat(allocator
, NULL
, pqFmt
, pqn
++);
3592 require_quiet(lpqLabel
, badDER
);
3593 CFReleaseNull(pqFmt
);
3594 appendOIDProperty(properties
, pqLabel
, lpqLabel
,
3595 &pqi
.policyQualifierID
, localized
);
3596 CFReleaseNull(pqLabel
);
3597 CFReleaseNull(lpqLabel
);
3598 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
3599 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
3600 appendURLContentProperty(properties
, SEC_CPS_URI_KEY
,
3601 &qualifierContent
.content
, localized
);
3602 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
3603 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3605 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
3606 DERNumUserNoticeItemSpecs
,
3607 DERUserNoticeItemSpecs
,
3609 require_noerr_quiet(drtn
, badDER
);
3610 if (un
.noticeRef
.length
) {
3611 DERNoticeReference nr
;
3612 drtn
= DERParseSequenceContent(&un
.noticeRef
,
3613 DERNumNoticeReferenceItemSpecs
,
3614 DERNoticeReferenceItemSpecs
,
3616 require_noerr_quiet(drtn
, badDER
);
3617 appendDERThingProperty(properties
,
3618 SEC_ORGANIZATION_KEY
, NULL
,
3619 &nr
.organization
, localized
);
3620 appendIntegerSequenceContent(properties
,
3621 SEC_NOTICE_NUMBERS_KEY
, &nr
.noticeNumbers
, localized
);
3623 if (un
.explicitText
.length
) {
3624 appendDERThingProperty(properties
, SEC_EXPLICIT_TEXT_KEY
,
3625 NULL
, &un
.explicitText
, localized
);
3628 appendUnparsedProperty(properties
, SEC_QUALIFIER_KEY
, NULL
,
3629 &pqi
.qualifier
, localized
);
3632 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3634 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3637 CFReleaseNull(piFmt
);
3638 CFReleaseNull(piLabel
);
3639 CFReleaseNull(lpiLabel
);
3640 CFReleaseNull(pqFmt
);
3641 CFReleaseNull(pqLabel
);
3642 CFReleaseNull(lpqLabel
);
3643 appendInvalidProperty(properties
, SEC_CERT_POLICIES_KEY
,
3644 extnValue
, localized
);
3647 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
3648 const DERItem
*extnValue
, bool localized
) {
3650 DERDecodedInfo keyIdentifier
;
3651 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
3652 require_noerr_quiet(drtn
, badDER
);
3653 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
3654 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3655 &keyIdentifier
.content
, localized
);
3659 appendInvalidProperty(properties
, SEC_SUBJ_KEY_ID_KEY
,
3660 extnValue
, localized
);
3664 AuthorityKeyIdentifier ::= SEQUENCE {
3665 keyIdentifier [0] KeyIdentifier OPTIONAL,
3666 authorityCertIssuer [1] GeneralNames OPTIONAL,
3667 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
3668 -- authorityCertIssuer and authorityCertSerialNumber MUST both
3669 -- be present or both be absent
3671 KeyIdentifier ::= OCTET STRING
3673 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
3674 const DERItem
*extnValue
, bool localized
) {
3675 DERAuthorityKeyIdentifier akid
;
3677 drtn
= DERParseSequence(extnValue
,
3678 DERNumAuthorityKeyIdentifierItemSpecs
,
3679 DERAuthorityKeyIdentifierItemSpecs
,
3680 &akid
, sizeof(akid
));
3681 require_noerr_quiet(drtn
, badDER
);
3682 if (akid
.keyIdentifier
.length
) {
3683 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3684 &akid
.keyIdentifier
, localized
);
3686 if (akid
.authorityCertIssuer
.length
||
3687 akid
.authorityCertSerialNumber
.length
) {
3688 require_quiet(akid
.authorityCertIssuer
.length
&&
3689 akid
.authorityCertSerialNumber
.length
, badDER
);
3690 /* Perhaps put in a subsection called Authority Certificate Issuer. */
3691 appendGeneralNamesContent(properties
,
3692 &akid
.authorityCertIssuer
, localized
);
3693 appendIntegerProperty(properties
, SEC_AUTH_CERT_SERIAL_KEY
,
3694 &akid
.authorityCertSerialNumber
, localized
);
3699 appendInvalidProperty(properties
, SEC_AUTHORITY_KEY_ID_KEY
,
3700 extnValue
, localized
);
3704 PolicyConstraints ::= SEQUENCE {
3705 requireExplicitPolicy [0] SkipCerts OPTIONAL,
3706 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
3708 SkipCerts ::= INTEGER (0..MAX)
3710 static void appendPolicyConstraints(CFMutableArrayRef properties
,
3711 const DERItem
*extnValue
, bool localized
) {
3712 DERPolicyConstraints pc
;
3714 drtn
= DERParseSequence(extnValue
,
3715 DERNumPolicyConstraintsItemSpecs
,
3716 DERPolicyConstraintsItemSpecs
,
3718 require_noerr_quiet(drtn
, badDER
);
3719 if (pc
.requireExplicitPolicy
.length
) {
3720 appendIntegerProperty(properties
, SEC_REQUIRE_EXPL_POLICY_KEY
,
3721 &pc
.requireExplicitPolicy
, localized
);
3723 if (pc
.inhibitPolicyMapping
.length
) {
3724 appendIntegerProperty(properties
, SEC_INHIBIT_POLICY_MAP_KEY
,
3725 &pc
.inhibitPolicyMapping
, localized
);
3731 appendInvalidProperty(properties
, SEC_POLICY_CONSTRAINTS_KEY
,
3732 extnValue
, localized
);
3736 extendedKeyUsage EXTENSION ::= {
3737 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
3738 IDENTIFIED BY id-ce-extKeyUsage }
3740 KeyPurposeId ::= OBJECT IDENTIFIER
3742 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
3743 const DERItem
*extnValue
, bool localized
) {
3746 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
3747 require_noerr_quiet(drtn
, badDER
);
3748 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3749 DERDecodedInfo currDecoded
;
3750 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3751 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
3752 appendOIDProperty(properties
, SEC_PURPOSE_KEY
, NULL
,
3753 &currDecoded
.content
, localized
);
3755 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3758 appendInvalidProperty(properties
, SEC_EXTENDED_KEY_USAGE_KEY
,
3759 extnValue
, localized
);
3763 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3765 AuthorityInfoAccessSyntax ::=
3766 SEQUENCE SIZE (1..MAX) OF AccessDescription
3768 AccessDescription ::= SEQUENCE {
3769 accessMethod OBJECT IDENTIFIER,
3770 accessLocation GeneralName }
3772 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3774 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3776 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3778 static void appendInfoAccess(CFMutableArrayRef properties
,
3779 const DERItem
*extnValue
, bool localized
) {
3782 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3783 require_noerr_quiet(drtn
, badDER
);
3784 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3785 DERDecodedInfo adContent
;
3786 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3787 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3788 DERAccessDescription ad
;
3789 drtn
= DERParseSequenceContent(&adContent
.content
,
3790 DERNumAccessDescriptionItemSpecs
,
3791 DERAccessDescriptionItemSpecs
,
3793 require_noerr_quiet(drtn
, badDER
);
3794 appendOIDProperty(properties
, SEC_ACCESS_METHOD_KEY
, NULL
,
3795 &ad
.accessMethod
, localized
);
3796 //TODO: Do something with SEC_ACCESS_LOCATION_KEY
3797 appendGeneralNameProperty(properties
, &ad
.accessLocation
, localized
);
3799 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3802 appendInvalidProperty(properties
, SEC_AUTH_INFO_ACCESS_KEY
,
3803 extnValue
, localized
);
3806 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3807 const DERItem
*extnValue
, bool localized
) {
3808 static const CFStringRef certTypes
[] = {
3812 SEC_OBJECT_SIGNING_KEY
,
3816 SEC_OBJECT_SIGNING_CA_KEY
3818 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3819 certTypes
, array_size(certTypes
), localized
);
3822 static bool appendPrintableDERSequence(CFMutableArrayRef properties
,
3823 CFStringRef label
, const DERItem
*sequence
, bool localized
) {
3826 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3827 require_noerr_quiet(drtn
, badSequence
);
3828 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3829 DERDecodedInfo currDecoded
;
3830 bool appendedSomething
= false;
3831 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3832 switch (currDecoded
.tag
)
3835 case ASN1_SEQUENCE
: // 16
3836 case ASN1_SET
: // 17
3837 // skip constructed object lengths
3840 case ASN1_UTF8_STRING
: // 12
3841 case ASN1_NUMERIC_STRING
: // 18
3842 case ASN1_PRINTABLE_STRING
: // 19
3843 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3844 case ASN1_VIDEOTEX_STRING
: // 21
3845 case ASN1_IA5_STRING
: // 22
3846 case ASN1_GRAPHIC_STRING
: // 25
3847 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3848 case ASN1_GENERAL_STRING
: // 27
3849 case ASN1_UNIVERSAL_STRING
: // 28
3851 CFStringRef string
=
3852 copyDERThingContentDescription(CFGetAllocator(properties
),
3853 currDecoded
.tag
, &currDecoded
.content
, false, localized
);
3854 require_quiet(string
, badSequence
);
3856 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3858 CFReleaseNull(string
);
3859 appendedSomething
= true;
3866 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3867 return appendedSomething
;
3872 static void appendExtension(CFMutableArrayRef parent
,
3873 const SecCertificateExtension
*extn
,
3875 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3876 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3877 &kCFTypeArrayCallBacks
);
3879 *extnID
= &extn
->extnID
,
3880 *extnValue
= &extn
->extnValue
;
3881 CFStringRef label
= NULL
;
3882 CFStringRef localizedLabel
= NULL
;
3884 appendBoolProperty(properties
, SEC_CRITICAL_KEY
, extn
->critical
, localized
);
3885 require_quiet(extnID
, xit
);
3887 bool handled
= true;
3888 /* Extensions that we know how to handle ourselves... */
3889 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3890 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3892 switch (extnID
->data
[extnID
->length
- 1]) {
3893 case 14: /* SubjectKeyIdentifier id-ce 14 */
3894 appendSubjectKeyIdentifier(properties
, extnValue
, localized
);
3896 case 15: /* KeyUsage id-ce 15 */
3897 appendKeyUsage(properties
, extnValue
, localized
);
3899 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3900 appendPrivateKeyUsagePeriod(properties
, extnValue
, localized
);
3902 case 17: /* SubjectAltName id-ce 17 */
3903 case 18: /* IssuerAltName id-ce 18 */
3904 appendGeneralNames(properties
, extnValue
, localized
);
3906 case 19: /* BasicConstraints id-ce 19 */
3907 appendBasicConstraints(properties
, extnValue
, localized
);
3909 case 30: /* NameConstraints id-ce 30 */
3910 appendNameConstraints(properties
, extnValue
, localized
);
3912 case 31: /* CRLDistributionPoints id-ce 31 */
3913 appendCrlDistributionPoints(properties
, extnValue
, localized
);
3915 case 32: /* CertificatePolicies id-ce 32 */
3916 appendCertificatePolicies(properties
, extnValue
, localized
);
3918 case 33: /* PolicyMappings id-ce 33 */
3921 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3922 appendAuthorityKeyIdentifier(properties
, extnValue
, localized
);
3924 case 36: /* PolicyConstraints id-ce 36 */
3925 appendPolicyConstraints(properties
, extnValue
, localized
);
3927 case 37: /* ExtKeyUsage id-ce 37 */
3928 appendExtendedKeyUsage(properties
, extnValue
, localized
);
3930 case 46: /* FreshestCRL id-ce 46 */
3933 case 54: /* InhibitAnyPolicy id-ce 54 */
3940 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3941 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3943 switch (extnID
->data
[extnID
->length
- 1]) {
3944 case 1: /* AuthorityInfoAccess id-pe 1 */
3945 appendInfoAccess(properties
, extnValue
, localized
);
3947 case 3: /* QCStatements id-pe 3 */
3950 case 11: /* SubjectInfoAccess id-pe 11 */
3951 appendInfoAccess(properties
, extnValue
, localized
);
3957 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3958 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3959 appendNetscapeCertType(properties
, extnValue
, localized
);
3965 /* Try to parse and display printable string(s). */
3966 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
, localized
)) {
3967 /* Nothing to do here appendPrintableDERSequence did the work. */
3969 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3970 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
, localized
);
3973 label
= SecDERItemCopyOIDDecimalRepresentation(allocator
, extnID
);
3974 localizedLabel
= copyOidDescription(allocator
, extnID
, localized
);
3975 appendProperty(parent
, kSecPropertyTypeSection
, label
, localizedLabel
,
3976 properties
, localized
);
3978 CFReleaseSafe(localizedLabel
);
3979 CFReleaseSafe(label
);
3980 CFReleaseSafe(properties
);
3983 /* Different types of summary types from least desired to most desired. */
3986 kSummaryTypePrintable
,
3987 kSummaryTypeOrganizationName
,
3988 kSummaryTypeOrganizationalUnitName
,
3989 kSummaryTypeCommonName
,
3993 enum SummaryType type
;
3994 CFStringRef summary
;
3995 CFStringRef description
;
3998 static OSStatus
obtainSummaryFromX501Name(void *context
,
3999 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
,
4001 struct Summary
*summary
= (struct Summary
*)context
;
4002 enum SummaryType stype
= kSummaryTypeNone
;
4003 CFStringRef string
= NULL
;
4004 if (DEROidCompare(type
, &oidCommonName
)) {
4005 stype
= kSummaryTypeCommonName
;
4006 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4007 stype
= kSummaryTypeOrganizationalUnitName
;
4008 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4009 stype
= kSummaryTypeOrganizationName
;
4010 } else if (DEROidCompare(type
, &oidDescription
)) {
4011 string
= copyDERThingDescription(kCFAllocatorDefault
, value
,
4014 if (summary
->description
) {
4015 CFStringRef fmt
= (localized
) ?
4016 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
4017 CFStringRef newDescription
= CFStringCreateWithFormat(kCFAllocatorDefault
,
4018 NULL
, fmt
, string
, summary
->description
);
4020 CFRelease(summary
->description
);
4021 summary
->description
= newDescription
;
4023 summary
->description
= string
;
4026 stype
= kSummaryTypePrintable
;
4029 stype
= kSummaryTypePrintable
;
4032 /* Build a string with all instances of the most desired
4033 component type in reverse order encountered comma separated list,
4034 The order of desirability is defined by enum SummaryType. */
4035 if (summary
->type
<= stype
) {
4037 string
= copyDERThingDescription(kCFAllocatorDefault
, value
,
4041 if (summary
->type
== stype
) {
4042 CFStringRef fmt
= (localized
) ?
4043 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
4044 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
,
4045 NULL
, fmt
, string
, summary
->summary
);
4048 string
= newSummary
;
4050 summary
->type
= stype
;
4052 CFReleaseSafe(summary
->summary
);
4053 summary
->summary
= string
;
4056 CFReleaseSafe(string
);
4059 return errSecSuccess
;
4062 CFStringRef
SecCertificateCopySubjectSummary(SecCertificateRef certificate
) {
4063 struct Summary summary
= {};
4064 OSStatus status
= parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
, true);
4065 if (status
!= errSecSuccess
) {
4068 /* If we found a description and a common name we change the summary to
4069 CommonName (Description). */
4070 if (summary
.description
) {
4071 if (summary
.type
== kSummaryTypeCommonName
) {
4072 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
4073 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
4075 CFRelease(summary
.summary
);
4076 summary
.summary
= newSummary
;
4078 CFRelease(summary
.description
);
4081 if (!summary
.summary
) {
4082 /* If we didn't find a suitable printable string in the subject at all, we try
4083 the first email address in the certificate instead. */
4084 CFArrayRef names
= SecCertificateCopyRFC822Names(certificate
);
4086 /* If we didn't find any email addresses in the certificate, we try finding
4087 a DNS name instead. */
4088 names
= SecCertificateCopyDNSNames(certificate
);
4091 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
4092 CFRetain(summary
.summary
);
4097 return summary
.summary
;
4100 CFStringRef
SecCertificateCopyIssuerSummary(SecCertificateRef certificate
) {
4101 struct Summary summary
= {};
4102 OSStatus status
= parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
, true);
4103 if (status
!= errSecSuccess
) {
4106 /* If we found a description and a common name we change the summary to
4107 CommonName (Description). */
4108 if (summary
.description
) {
4109 if (summary
.type
== kSummaryTypeCommonName
) {
4110 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
4111 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
4113 CFRelease(summary
.summary
);
4114 summary
.summary
= newSummary
;
4116 CFRelease(summary
.description
);
4119 return summary
.summary
;
4122 /* Return the earliest date on which all certificates in this chain are still
4124 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
4125 SecCertificateRef certificate
) {
4126 CFAbsoluteTime earliest
= certificate
->_notAfter
;
4128 while (certificate
->_parent
) {
4129 certificate
= certificate
->_parent
;
4130 if (earliest
> certificate
->_notAfter
)
4131 earliest
= certificate
->_notAfter
;
4138 /* Return the latest date on which all certificates in this chain will be
4140 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
4141 SecCertificateRef certificate
) {
4142 CFAbsoluteTime latest
= certificate
->_notBefore
;
4144 while (certificate
->_parent
) {
4145 certificate
= certificate
->_parent
;
4146 if (latest
< certificate
->_notBefore
)
4147 latest
= certificate
->_notBefore
;
4154 bool SecCertificateIsValid(SecCertificateRef certificate
,
4155 CFAbsoluteTime verifyTime
) {
4156 return certificate
&& certificate
->_notBefore
<= verifyTime
&&
4157 verifyTime
<= certificate
->_notAfter
;
4160 CFIndex
SecCertificateVersion(SecCertificateRef certificate
) {
4161 return certificate
->_version
+ 1;
4164 CFAbsoluteTime
SecCertificateNotValidBefore(SecCertificateRef certificate
) {
4165 return certificate
->_notBefore
;
4168 CFAbsoluteTime
SecCertificateNotValidAfter(SecCertificateRef certificate
) {
4169 return certificate
->_notAfter
;
4172 CFMutableArrayRef
SecCertificateCopySummaryProperties(
4173 SecCertificateRef certificate
, CFAbsoluteTime verifyTime
) {
4174 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4175 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
4176 &kCFTypeArrayCallBacks
);
4177 bool localized
= true;
4179 /* First we put the subject summary name. */
4180 CFStringRef ssummary
= SecCertificateCopySubjectSummary(certificate
);
4182 appendProperty(summary
, kSecPropertyTypeTitle
,
4183 NULL
, NULL
, ssummary
, localized
);
4184 CFRelease(ssummary
);
4187 /* Let see if this certificate is currently valid. */
4189 CFAbsoluteTime when
;
4190 CFStringRef message
;
4192 if (verifyTime
> certificate
->_notAfter
) {
4193 label
= SEC_EXPIRED_KEY
;
4194 when
= certificate
->_notAfter
;
4195 ptype
= kSecPropertyTypeError
;
4196 message
= SEC_CERT_EXPIRED_KEY
;
4197 } else if (certificate
->_notBefore
> verifyTime
) {
4198 label
= SEC_VALID_FROM_KEY
;
4199 when
= certificate
->_notBefore
;
4200 ptype
= kSecPropertyTypeError
;
4201 message
= SEC_CERT_NOT_YET_VALID_KEY
;
4203 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
4204 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
4205 if (verifyTime
> last
) {
4206 label
= SEC_EXPIRED_KEY
;
4208 ptype
= kSecPropertyTypeError
;
4209 message
= SEC_ISSUER_EXPIRED_KEY
;
4210 } else if (verifyTime
< first
) {
4211 label
= SEC_VALID_FROM_KEY
;
4213 ptype
= kSecPropertyTypeError
;
4214 message
= SEC_ISSR_NOT_YET_VALID_KEY
;
4216 label
= SEC_EXPIRES_KEY
;
4217 when
= certificate
->_notAfter
;
4218 ptype
= kSecPropertyTypeSuccess
;
4219 message
= SEC_CERT_VALID_KEY
;
4223 appendDateProperty(summary
, label
, when
, localized
);
4224 CFStringRef lmessage
= SecCopyCertString(message
);
4225 appendProperty(summary
, ptype
, NULL
, NULL
, lmessage
, localized
);
4226 CFRelease(lmessage
);
4231 CFArrayRef
SecCertificateCopyLegacyProperties(SecCertificateRef certificate
) {
4233 This function replicates the content returned by SecCertificateCopyProperties
4234 prior to 10.12.4, providing stable return values for SecCertificateCopyValues.
4235 Unlike SecCertificateCopyProperties, it does not cache the result and
4236 assumes the caller will do so.
4238 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4239 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
,
4240 0, &kCFTypeArrayCallBacks
);
4243 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
4244 &certificate
->_subject
, false);
4245 appendProperty(properties
, kSecPropertyTypeSection
, CFSTR("Subject Name"),
4246 NULL
, subject_plist
, false);
4247 CFRelease(subject_plist
);
4250 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
4251 &certificate
->_issuer
, false);
4252 appendProperty(properties
, kSecPropertyTypeSection
, CFSTR("Issuer Name"),
4253 NULL
, issuer_plist
, false);
4254 CFRelease(issuer_plist
);
4257 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
4258 NULL
, CFSTR("%d"), certificate
->_version
+ 1);
4259 appendProperty(properties
, kSecPropertyTypeString
, CFSTR("Version"),
4260 NULL
, versionString
, false);
4261 CFRelease(versionString
);
4264 if (certificate
->_serialNum
.length
) {
4265 appendIntegerProperty(properties
, CFSTR("Serial Number"),
4266 &certificate
->_serialNum
, false);
4269 /* Signature Algorithm */
4270 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
4271 &certificate
->_tbsSigAlg
, false);
4273 /* Validity dates */
4274 appendDateProperty(properties
, CFSTR("Not Valid Before"), certificate
->_notBefore
, false);
4275 appendDateProperty(properties
, CFSTR("Not Valid After"), certificate
->_notAfter
, false);
4277 if (certificate
->_subjectUniqueID
.length
) {
4278 appendDataProperty(properties
, CFSTR("Subject Unique ID"),
4279 NULL
, &certificate
->_subjectUniqueID
, false);
4281 if (certificate
->_issuerUniqueID
.length
) {
4282 appendDataProperty(properties
, CFSTR("Issuer Unique ID"),
4283 NULL
, &certificate
->_issuerUniqueID
, false);
4286 /* Public Key Algorithm */
4287 appendAlgorithmProperty(properties
, CFSTR("Public Key Algorithm"),
4288 &certificate
->_algId
, false);
4290 /* Public Key Data */
4291 appendDataProperty(properties
, CFSTR("Public Key Data"),
4292 NULL
, &certificate
->_pubKeyDER
, false);
4295 appendDataProperty(properties
, CFSTR("Signature"),
4296 NULL
, &certificate
->_signature
, false);
4300 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4301 appendExtension(properties
, &certificate
->_extensions
[ix
], false);
4305 appendFingerprintsProperty(properties
, CFSTR("Fingerprints"), certificate
, false);
4310 static CFArrayRef
CopyProperties(SecCertificateRef certificate
, Boolean localized
) {
4311 if (!certificate
->_properties
) {
4312 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4313 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
4314 &kCFTypeArrayCallBacks
);
4315 require_quiet(properties
, out
);
4317 /* First we put the Subject Name in the property list. */
4318 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
4319 &certificate
->_subject
,
4321 if (subject_plist
) {
4322 appendProperty(properties
, kSecPropertyTypeSection
,
4323 SEC_SUBJECT_NAME_KEY
, NULL
, subject_plist
, localized
);
4325 CFReleaseNull(subject_plist
);
4327 /* Next we put the Issuer Name in the property list. */
4328 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
4329 &certificate
->_issuer
,
4332 appendProperty(properties
, kSecPropertyTypeSection
,
4333 SEC_ISSUER_NAME_KEY
, NULL
, issuer_plist
, localized
);
4335 CFReleaseNull(issuer_plist
);
4338 CFStringRef fmt
= SecCopyCertString(SEC_CERT_VERSION_VALUE_KEY
);
4339 CFStringRef versionString
= NULL
;
4341 versionString
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
4342 certificate
->_version
+ 1);
4345 if (versionString
) {
4346 appendProperty(properties
, kSecPropertyTypeString
,
4347 SEC_VERSION_KEY
, NULL
, versionString
, localized
);
4349 CFReleaseNull(versionString
);
4352 appendSerialNumberProperty(properties
, SEC_SERIAL_NUMBER_KEY
, &certificate
->_serialNum
, localized
);
4354 /* Validity dates. */
4355 appendValidityPeriodProperty(properties
, SEC_VALIDITY_PERIOD_KEY
, certificate
, localized
);
4357 if (certificate
->_subjectUniqueID
.length
) {
4358 appendDataProperty(properties
, SEC_SUBJECT_UNIQUE_ID_KEY
, NULL
,
4359 &certificate
->_subjectUniqueID
, localized
);
4361 if (certificate
->_issuerUniqueID
.length
) {
4362 appendDataProperty(properties
, SEC_ISSUER_UNIQUE_ID_KEY
, NULL
,
4363 &certificate
->_issuerUniqueID
, localized
);
4366 appendPublicKeyProperty(properties
, SEC_PUBLIC_KEY_KEY
, certificate
, localized
);
4369 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4370 appendExtension(properties
, &certificate
->_extensions
[ix
], localized
);
4374 appendSignatureProperty(properties
, SEC_SIGNATURE_KEY
, certificate
, localized
);
4376 appendFingerprintsProperty(properties
, SEC_FINGERPRINTS_KEY
, certificate
, localized
);
4378 certificate
->_properties
= properties
;
4382 CFRetainSafe(certificate
->_properties
);
4383 return certificate
->_properties
;
4386 CFArrayRef
SecCertificateCopyProperties(SecCertificateRef certificate
) {
4388 Wrapper function which defaults to localized string properties
4389 for compatibility with prior releases.
4391 return CopyProperties(certificate
, true);
4394 CFArrayRef
SecCertificateCopyLocalizedProperties(SecCertificateRef certificate
, Boolean localized
) {
4396 Wrapper function which permits caller to specify whether
4397 localized string properties are used.
4399 return CopyProperties(certificate
, localized
);
4402 /* Unified serial number API */
4403 CFDataRef
SecCertificateCopySerialNumberData(
4404 SecCertificateRef certificate
,
4409 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
4413 if (certificate
->_serialNumber
) {
4414 CFRetain(certificate
->_serialNumber
);
4416 return certificate
->_serialNumber
;
4420 /* On iOS, the SecCertificateCopySerialNumber API takes one argument. */
4421 CFDataRef
SecCertificateCopySerialNumber(
4422 SecCertificateRef certificate
) {
4423 return SecCertificateCopySerialNumberData(certificate
, NULL
);
4427 CFDataRef
SecCertificateGetNormalizedIssuerContent(
4428 SecCertificateRef certificate
) {
4429 return certificate
->_normalizedIssuer
;
4432 CFDataRef
SecCertificateGetNormalizedSubjectContent(
4433 SecCertificateRef certificate
) {
4434 return certificate
->_normalizedSubject
;
4437 /* Verify that certificate was signed by issuerKey. */
4438 OSStatus
SecCertificateIsSignedBy(SecCertificateRef certificate
,
4439 SecKeyRef issuerKey
) {
4440 #pragma clang diagnostic push
4441 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
4442 /* Setup algId in SecAsn1AlgId format. */
4444 #pragma clang diagnostic pop
4445 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
4446 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
4447 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
4448 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
4450 /* RFC5280 4.1.1.2, 4.1.2.3 requires the actual signature algorithm
4451 must match the specified algorithm in the TBSCertificate. */
4452 bool sigAlgMatch
= DEROidCompare(&certificate
->_sigAlg
.oid
,
4453 &certificate
->_tbsSigAlg
.oid
);
4455 secwarning("Signature algorithm mismatch in certificate (see RFC5280 4.1.1.2)");
4458 CFErrorRef error
= NULL
;
4460 !SecVerifySignatureWithPublicKey(issuerKey
, &algId
,
4461 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
4462 certificate
->_signature
.data
, certificate
->_signature
.length
, &error
))
4464 #if !defined(NDEBUG)
4465 secdebug("verify", "signature verify failed: %" PRIdOSStatus
, (error
) ? (OSStatus
)CFErrorGetCode(error
) : errSecNotSigner
);
4467 CFReleaseSafe(error
);
4468 return errSecNotSigner
;
4471 return errSecSuccess
;
4474 const DERItem
* SecCertificateGetSubjectAltName(SecCertificateRef certificate
) {
4475 if (!certificate
->_subjectAltName
) {
4478 return &certificate
->_subjectAltName
->extnValue
;
4481 /* Convert IPv4 address string to canonical data format (4 bytes) */
4482 static bool convertIPv4Address(CFStringRef name
, CFDataRef
*dataIP
) {
4483 /* IPv4: 4 octets in decimal separated by dots. */
4484 bool result
= false;
4486 if (CFStringGetLength(name
) < 7 || /* min size is #.#.#.# */
4487 CFStringGetLength(name
) > 15) { /* max size is ###.###.###.### */
4491 CFCharacterSetRef allowed
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789."));
4492 CFCharacterSetRef disallowed
= CFCharacterSetCreateInvertedSet(NULL
, allowed
);
4493 CFMutableDataRef data
= CFDataCreateMutable(NULL
, 0);
4494 CFArrayRef parts
= CFStringCreateArrayBySeparatingStrings(NULL
, name
, CFSTR("."));
4495 CFIndex i
, count
= (parts
) ? CFArrayGetCount(parts
) : 0;
4497 /* Check character set */
4498 if (CFStringFindCharacterFromSet(name
, disallowed
,
4499 CFRangeMake(0, CFStringGetLength(name
)),
4500 kCFCompareForcedOrdering
, NULL
)) {
4504 /* Check number of labels */
4505 if (CFArrayGetCount(parts
) != 4) {
4509 /* Check each label and convert */
4510 for (i
= 0; i
< count
; i
++) {
4511 CFStringRef octet
= (CFStringRef
) CFArrayGetValueAtIndex(parts
, i
);
4512 char *cString
= CFStringToCString(octet
);
4513 uint32_t value
= atoi(cString
);
4518 uint8_t byte
= value
;
4519 CFDataAppendBytes(data
, &byte
, 1);
4524 *dataIP
= (CFDataRef
) CFRetain(data
);
4528 CFReleaseNull(data
);
4529 CFReleaseNull(parts
);
4530 CFReleaseNull(allowed
);
4531 CFReleaseNull(disallowed
);
4535 /* Convert IPv6 address string to canonical data format (16 bytes) */
4536 static bool convertIPv6Address(CFStringRef name
, CFDataRef
*dataIP
) {
4537 /* IPv6: 8 16-bit fields with colon delimiters. */
4538 /* Note: we don't support conversion of hybrid IPv4-mapped addresses here. */
4539 bool result
= false;
4540 CFMutableStringRef addr
= NULL
;
4541 CFIndex length
= (name
) ? CFStringGetLength(name
) : 0;
4542 /* Sanity check size */
4543 if (length
< 2 || /* min size is '::' */
4544 length
> 41) { /* max size is '[####:####:####:####:####:####:####:####]' */
4547 /* Remove literal brackets, if present */
4548 if (CFStringHasPrefix(name
, CFSTR("[")) && CFStringHasSuffix(name
, CFSTR("]"))) {
4549 CFStringRef tmpName
= CFStringCreateWithSubstring(NULL
, name
, CFRangeMake(1, length
-2));
4551 addr
= CFStringCreateMutableCopy(NULL
, 0, tmpName
);
4556 addr
= CFStringCreateMutableCopy(NULL
, 0, name
);
4558 CFStringUppercase(addr
, CFLocaleGetSystem());
4560 CFCharacterSetRef allowed
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789ABCDEF:"));
4561 CFCharacterSetRef disallowed
= CFCharacterSetCreateInvertedSet(NULL
, allowed
);
4562 CFMutableDataRef data
= CFDataCreateMutable(NULL
, 0);
4563 CFArrayRef parts
= CFStringCreateArrayBySeparatingStrings(NULL
, addr
, CFSTR(":"));
4564 CFIndex i
, count
= (parts
) ? CFArrayGetCount(parts
) : 0;
4566 /* Check character set */
4567 if (CFStringFindCharacterFromSet(addr
, disallowed
,
4568 CFRangeMake(0, CFStringGetLength(addr
)),
4569 kCFCompareForcedOrdering
, NULL
)) {
4573 /* Check number of fields (no fewer than 3, no more than 8) */
4574 if (CFArrayGetCount(parts
) < 3 || CFArrayGetCount(parts
) > 8) {
4578 /* Check each field and convert to network-byte-order value */
4579 for (i
= 0; i
< count
; i
++) {
4580 uint16_t svalue
= 0;
4581 CFStringRef fieldValue
= (CFStringRef
) CFArrayGetValueAtIndex(parts
, i
);
4582 char *cString
= CFStringToCString(fieldValue
);
4583 length
= (cString
) ? strlen(cString
) : 0;
4585 /* empty value indicates one or more zeros in the address */
4586 if (i
== 0 || i
== count
-1) { /* leading or trailing part of '::' */
4587 CFDataAppendBytes(data
, (const UInt8
*)&svalue
, 2);
4588 } else { /* determine how many fields are missing, then zero-fill */
4589 CFIndex z
, missing
= (8 - count
) + 1;
4590 for (z
= 0; z
< missing
; z
++) {
4591 CFDataAppendBytes(data
, (const UInt8
*)&svalue
, 2);
4594 } else if (length
<= 4) {
4595 /* valid field value is 4 characters or less */
4596 unsigned long value
= strtoul(cString
, NULL
, 16);
4597 svalue
= htons(value
& 0xFFFF);
4598 CFDataAppendBytes(data
, (const UInt8
*)&svalue
, 2);
4602 if (CFDataGetLength(data
) != IPv6ADDRLEN
) {
4603 goto out
; /* after expansion, data must be exactly 16 bytes */
4608 *dataIP
= (CFDataRef
) CFRetain(data
);
4612 CFReleaseNull(data
);
4613 CFReleaseNull(parts
);
4614 CFReleaseNull(allowed
);
4615 CFReleaseNull(disallowed
);
4616 CFReleaseNull(addr
);
4620 static bool convertIPAddress(CFStringRef string
, CFDataRef
*dataIP
) {
4621 if (NULL
== string
) {
4624 if (convertIPv4Address(string
, dataIP
) ||
4625 convertIPv6Address(string
, dataIP
)) {
4631 bool SecFrameworkIsIPAddress(CFStringRef string
) {
4632 return convertIPAddress(string
, NULL
);
4635 CFDataRef
SecFrameworkCopyIPAddressData(CFStringRef string
) {
4636 CFDataRef data
= NULL
;
4637 if (!convertIPAddress(string
, &data
)) {
4643 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
4644 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4645 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
4646 if (gnType
== GNT_IPAddress
) {
4647 CFStringRef string
= copyIPAddressContentDescription(
4648 kCFAllocatorDefault
, generalName
);
4650 CFArrayAppendValue(ipAddresses
, string
);
4653 return errSecInvalidCertificate
;
4656 return errSecSuccess
;
4659 CFArrayRef
SecCertificateCopyIPAddresses(SecCertificateRef certificate
) {
4660 /* These can only exist in the subject alt name. */
4661 if (!certificate
->_subjectAltName
)
4664 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
4665 0, &kCFTypeArrayCallBacks
);
4666 OSStatus status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4667 ipAddresses
, appendIPAddressesFromGeneralNames
);
4668 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
4669 CFRelease(ipAddresses
);
4675 static OSStatus
appendIPAddressesFromX501Name(void *context
, const DERItem
*type
,
4676 const DERItem
*value
, CFIndex rdnIX
,
4678 CFMutableArrayRef addrs
= (CFMutableArrayRef
)context
;
4679 if (DEROidCompare(type
, &oidCommonName
)) {
4680 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4681 value
, true, localized
);
4683 CFDataRef data
= NULL
;
4684 if (convertIPAddress(string
, &data
)) {
4685 CFArrayAppendValue(addrs
, data
);
4686 CFReleaseNull(data
);
4690 return errSecInvalidCertificate
;
4693 return errSecSuccess
;
4696 CFArrayRef
SecCertificateCopyIPAddressesFromSubject(SecCertificateRef certificate
) {
4697 CFMutableArrayRef addrs
= CFArrayCreateMutable(kCFAllocatorDefault
,
4698 0, &kCFTypeArrayCallBacks
);
4699 OSStatus status
= parseX501NameContent(&certificate
->_subject
, addrs
,
4700 appendIPAddressesFromX501Name
, true);
4701 if (status
|| CFArrayGetCount(addrs
) == 0) {
4702 CFReleaseNull(addrs
);
4708 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
4709 const DERItem
*generalName
) {
4710 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4711 if (gnType
== GNT_DNSName
) {
4712 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4713 generalName
->data
, generalName
->length
,
4714 kCFStringEncodingUTF8
, FALSE
);
4716 CFArrayAppendValue(dnsNames
, string
);
4719 return errSecInvalidCertificate
;
4722 return errSecSuccess
;
4725 /* Return true if the passed in string matches the
4726 Preferred name syntax from sections 2.3.1. in RFC 1035.
4727 With the added check that we disallow empty dns names.
4728 Also in order to support wildcard DNSNames we allow for the '*'
4729 character anywhere in a dns component where we currently allow
4732 <domain> ::= <subdomain> | " "
4734 <subdomain> ::= <label> | <subdomain> "." <label>
4736 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
4738 RFC 3696 redefined labels as:
4739 <label> ::= <let-dig> [ [ <ldh-str> ] <let-dig> ]
4740 with the caveat that the highest-level labels is never all-numeric.
4742 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
4744 <let-dig-hyp> ::= <let-dig> | "-"
4746 <let-dig> ::= <letter> | <digit>
4748 <letter> ::= any one of the 52 alphabetic characters A through Z in
4749 upper case and a through z in lower case
4751 <digit> ::= any one of the ten digits 0 through 9
4753 bool SecFrameworkIsDNSName(CFStringRef string
) {
4754 CFStringInlineBuffer buf
= {};
4755 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
4756 /* From RFC 1035 2.3.4. Size limits:
4757 labels 63 octets or less
4758 names 255 octets or less */
4759 require_quiet(length
<= 255, notDNS
);
4760 CFRange range
= { 0, length
};
4761 CFStringInitInlineBuffer(string
, &buf
, range
);
4765 kDNSStateAfterAlpha
,
4766 kDNSStateAfterDigit
,
4768 } state
= kDNSStateInital
;
4769 Boolean labelHasAlpha
= false;
4771 for (ix
= 0; ix
< length
; ++ix
) {
4772 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
4775 require_quiet(labelLength
<= 64 &&
4776 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4778 state
= kDNSStateAfterDot
;
4779 labelHasAlpha
= false;
4781 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
4783 state
= kDNSStateAfterAlpha
;
4784 labelHasAlpha
= true;
4785 } else if ('0' <= ch
&& ch
<= '9') {
4786 state
= kDNSStateAfterDigit
;
4787 } else if (ch
== '-') {
4788 require_quiet(state
== kDNSStateAfterAlpha
||
4789 state
== kDNSStateAfterDigit
||
4790 state
== kDNSStateAfterDash
, notDNS
);
4791 state
= kDNSStateAfterDash
;
4797 /* We don't allow a dns name to end in a dot or dash. */
4798 require_quiet(labelLength
<= 63 &&
4799 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4802 /* Additionally, the rightmost label must have letters in it. */
4803 require_quiet(labelHasAlpha
== true, notDNS
);
4810 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4811 const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4812 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4813 if (DEROidCompare(type
, &oidCommonName
)) {
4814 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4815 value
, true, localized
);
4817 if (SecFrameworkIsDNSName(string
)) {
4818 /* We found a common name that is formatted like a valid
4820 CFArrayAppendValue(dnsNames
, string
);
4824 return errSecInvalidCertificate
;
4827 return errSecSuccess
;
4830 CFArrayRef
SecCertificateCopyDNSNamesFromSubject(SecCertificateRef certificate
) {
4831 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4832 0, &kCFTypeArrayCallBacks
);
4833 OSStatus status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4834 appendDNSNamesFromX501Name
, true);
4835 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4836 CFReleaseNull(dnsNames
);
4840 /* appendDNSNamesFromX501Name allows IP addresses, we don't want those for this function */
4841 __block CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4842 CFArrayForEach(dnsNames
, ^(const void *value
) {
4843 CFStringRef name
= (CFStringRef
)value
;
4844 if (!convertIPAddress(name
, NULL
)) {
4845 CFArrayAppendValue(result
, name
);
4848 CFReleaseNull(dnsNames
);
4849 if (CFArrayGetCount(result
) == 0) {
4850 CFReleaseNull(result
);
4856 CFArrayRef
SecCertificateCopyDNSNamesFromSAN(SecCertificateRef certificate
) {
4857 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4858 0, &kCFTypeArrayCallBacks
);
4859 OSStatus status
= errSecSuccess
;
4860 if (certificate
->_subjectAltName
) {
4861 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4862 dnsNames
, appendDNSNamesFromGeneralNames
);
4865 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4866 CFReleaseNull(dnsNames
);
4871 /* Not everything returned by this function is going to be a proper DNS name,
4872 we also return the certificates common name entries from the subject,
4873 assuming they look like dns names as specified in RFC 1035. */
4874 CFArrayRef
SecCertificateCopyDNSNames(SecCertificateRef certificate
) {
4875 /* These can exist in the subject alt name or in the subject. */
4876 CFArrayRef sanNames
= SecCertificateCopyDNSNamesFromSAN(certificate
);
4877 if (sanNames
&& CFArrayGetCount(sanNames
) > 0) {
4880 CFReleaseNull(sanNames
);
4882 /* RFC 2818 section 3.1. Server Identity
4884 If a subjectAltName extension of type dNSName is present, that MUST
4885 be used as the identity. Otherwise, the (most specific) Common Name
4886 field in the Subject field of the certificate MUST be used. Although
4887 the use of the Common Name is existing practice, it is deprecated and
4888 Certification Authorities are encouraged to use the dNSName instead.
4891 This implies that if we found 1 or more DNSNames in the
4892 subjectAltName, we should not use the Common Name of the subject as
4896 /* To preserve bug for bug compatibility, we can't use SecCertificateCopyDNSNamesFromSubject
4897 * because that function filters out IP Addresses. This function is Private, but
4898 * SecCertificateCopyValues uses it and that's Public. */
4899 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4900 0, &kCFTypeArrayCallBacks
);
4901 OSStatus status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4902 appendDNSNamesFromX501Name
, true);
4903 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4904 CFReleaseNull(dnsNames
);
4909 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4910 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4911 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4912 if (gnType
== GNT_RFC822Name
) {
4913 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4914 generalName
->data
, generalName
->length
,
4915 kCFStringEncodingASCII
, FALSE
);
4917 CFArrayAppendValue(dnsNames
, string
);
4920 return errSecInvalidCertificate
;
4923 return errSecSuccess
;
4926 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4927 const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4928 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4929 if (DEROidCompare(type
, &oidEmailAddress
)) {
4930 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4931 value
, true, localized
);
4933 CFArrayAppendValue(dnsNames
, string
);
4936 return errSecInvalidCertificate
;
4939 return errSecSuccess
;
4942 CFArrayRef
SecCertificateCopyRFC822Names(SecCertificateRef certificate
) {
4943 /* These can exist in the subject alt name or in the subject. */
4944 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4945 0, &kCFTypeArrayCallBacks
);
4946 OSStatus status
= errSecSuccess
;
4947 if (certificate
->_subjectAltName
) {
4948 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4949 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4952 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4953 appendRFC822NamesFromX501Name
, true);
4955 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4956 CFRelease(rfc822Names
);
4962 OSStatus
SecCertificateCopyEmailAddresses(SecCertificateRef certificate
, CFArrayRef
* __nonnull CF_RETURNS_RETAINED emailAddresses
) {
4963 if (!certificate
|| !emailAddresses
) {
4966 *emailAddresses
= SecCertificateCopyRFC822Names(certificate
);
4967 if (*emailAddresses
== NULL
) {
4968 *emailAddresses
= CFArrayCreate(NULL
, NULL
, 0, &kCFTypeArrayCallBacks
);
4970 return errSecSuccess
;
4973 CFArrayRef
SecCertificateCopyRFC822NamesFromSubject(SecCertificateRef certificate
) {
4974 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4975 0, &kCFTypeArrayCallBacks
);
4976 OSStatus status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4977 appendRFC822NamesFromX501Name
, true);
4978 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4979 CFRelease(rfc822Names
);
4985 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4986 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4987 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4988 if (DEROidCompare(type
, &oidCommonName
)) {
4989 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4990 value
, true, localized
);
4992 CFArrayAppendValue(commonNames
, string
);
4995 return errSecInvalidCertificate
;
4998 return errSecSuccess
;
5001 CFArrayRef
SecCertificateCopyCommonNames(SecCertificateRef certificate
) {
5002 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
5003 0, &kCFTypeArrayCallBacks
);
5005 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
5006 appendCommonNamesFromX501Name
, true);
5007 if (status
|| CFArrayGetCount(commonNames
) == 0) {
5008 CFRelease(commonNames
);
5014 OSStatus
SecCertificateCopyCommonName(SecCertificateRef certificate
, CFStringRef
*commonName
)
5019 CFArrayRef commonNames
= SecCertificateCopyCommonNames(certificate
);
5021 return errSecInternal
;
5025 CFIndex count
= CFArrayGetCount(commonNames
);
5026 *commonName
= CFRetainSafe(CFArrayGetValueAtIndex(commonNames
, count
-1));
5028 CFReleaseSafe(commonNames
);
5029 return errSecSuccess
;
5032 static OSStatus
appendOrganizationFromX501Name(void *context
,
5033 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5034 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
5035 if (DEROidCompare(type
, &oidOrganizationName
)) {
5036 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
5037 value
, true, localized
);
5039 CFArrayAppendValue(organization
, string
);
5042 return errSecInvalidCertificate
;
5045 return errSecSuccess
;
5048 CFArrayRef
SecCertificateCopyOrganizationFromX501NameContent(const DERItem
*nameContent
) {
5049 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
5050 0, &kCFTypeArrayCallBacks
);
5052 status
= parseX501NameContent(nameContent
, organization
,
5053 appendOrganizationFromX501Name
, true);
5054 if (status
|| CFArrayGetCount(organization
) == 0) {
5055 CFRelease(organization
);
5056 organization
= NULL
;
5058 return organization
;
5061 CFArrayRef
SecCertificateCopyOrganization(SecCertificateRef certificate
) {
5062 return SecCertificateCopyOrganizationFromX501NameContent(&certificate
->_subject
);
5065 static OSStatus
appendOrganizationalUnitFromX501Name(void *context
,
5066 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5067 CFMutableArrayRef organizationalUnit
= (CFMutableArrayRef
)context
;
5068 if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
5069 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
5070 value
, true, localized
);
5072 CFArrayAppendValue(organizationalUnit
, string
);
5075 return errSecInvalidCertificate
;
5078 return errSecSuccess
;
5081 CFArrayRef
SecCertificateCopyOrganizationalUnit(SecCertificateRef certificate
) {
5082 CFMutableArrayRef organizationalUnit
= CFArrayCreateMutable(kCFAllocatorDefault
,
5083 0, &kCFTypeArrayCallBacks
);
5085 status
= parseX501NameContent(&certificate
->_subject
, organizationalUnit
,
5086 appendOrganizationalUnitFromX501Name
, true);
5087 if (status
|| CFArrayGetCount(organizationalUnit
) == 0) {
5088 CFRelease(organizationalUnit
);
5089 organizationalUnit
= NULL
;
5091 return organizationalUnit
;
5094 static OSStatus
appendCountryFromX501Name(void *context
,
5095 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5096 CFMutableArrayRef countries
= (CFMutableArrayRef
)context
;
5097 if (DEROidCompare(type
, &oidCountryName
)) {
5098 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
5099 value
, true, localized
);
5101 CFArrayAppendValue(countries
, string
);
5104 return errSecInvalidCertificate
;
5107 return errSecSuccess
;
5110 CFArrayRef
SecCertificateCopyCountry(SecCertificateRef certificate
) {
5111 CFMutableArrayRef countries
= CFArrayCreateMutable(kCFAllocatorDefault
,
5112 0, &kCFTypeArrayCallBacks
);
5114 status
= parseX501NameContent(&certificate
->_subject
, countries
,
5115 appendCountryFromX501Name
, true);
5116 if (status
|| CFArrayGetCount(countries
) == 0) {
5117 CFRelease(countries
);
5123 const SecCEBasicConstraints
*
5124 SecCertificateGetBasicConstraints(SecCertificateRef certificate
) {
5125 if (certificate
->_basicConstraints
.present
)
5126 return &certificate
->_basicConstraints
;
5131 CFArrayRef
SecCertificateGetPermittedSubtrees(SecCertificateRef certificate
) {
5132 return (certificate
->_permittedSubtrees
);
5135 CFArrayRef
SecCertificateGetExcludedSubtrees(SecCertificateRef certificate
) {
5136 return (certificate
->_excludedSubtrees
);
5139 const SecCEPolicyConstraints
*
5140 SecCertificateGetPolicyConstraints(SecCertificateRef certificate
) {
5141 if (certificate
->_policyConstraints
.present
)
5142 return &certificate
->_policyConstraints
;
5147 const SecCEPolicyMappings
*
5148 SecCertificateGetPolicyMappings(SecCertificateRef certificate
) {
5149 if (certificate
->_policyMappings
.present
) {
5150 return &certificate
->_policyMappings
;
5156 const SecCECertificatePolicies
*
5157 SecCertificateGetCertificatePolicies(SecCertificateRef certificate
) {
5158 if (certificate
->_certificatePolicies
.present
)
5159 return &certificate
->_certificatePolicies
;
5164 const SecCEInhibitAnyPolicy
*
5165 SecCertificateGetInhibitAnyPolicySkipCerts(SecCertificateRef certificate
) {
5166 if (certificate
->_inhibitAnyPolicySkipCerts
.present
) {
5167 return &certificate
->_inhibitAnyPolicySkipCerts
;
5173 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
5174 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
5175 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
5176 if (gnType
== GNT_OtherName
) {
5178 DERReturn drtn
= DERParseSequenceContent(generalName
,
5179 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
5181 require_noerr_quiet(drtn
, badDER
);
5182 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
5184 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
5185 &on
.value
, true, true), badDER
);
5186 CFArrayAppendValue(ntPrincipalNames
, string
);
5190 return errSecSuccess
;
5193 return errSecInvalidCertificate
;
5197 CFArrayRef
SecCertificateCopyNTPrincipalNames(SecCertificateRef certificate
) {
5198 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
5199 0, &kCFTypeArrayCallBacks
);
5200 OSStatus status
= errSecSuccess
;
5201 if (certificate
->_subjectAltName
) {
5202 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
5203 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
5205 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
5206 CFRelease(ntPrincipalNames
);
5207 ntPrincipalNames
= NULL
;
5209 return ntPrincipalNames
;
5212 static OSStatus
appendToRFC2253String(void *context
,
5213 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5214 CFMutableStringRef string
= (CFMutableStringRef
)context
;
5218 ST stateOrProvinceName
5220 OU organizationalUnitName
5222 STREET streetAddress
5226 /* Prepend a + if this is not the first RDN in an RDN set.
5227 Otherwise prepend a , if this is not the first RDN. */
5229 CFStringAppend(string
, CFSTR("+"));
5230 else if (CFStringGetLength(string
)) {
5231 CFStringAppend(string
, CFSTR(","));
5234 CFStringRef label
, oid
= NULL
;
5235 /* @@@ Consider changing this to a dictionary lookup keyed by the
5236 decimal representation. */
5237 if (DEROidCompare(type
, &oidCommonName
)) {
5238 label
= CFSTR("CN");
5239 } else if (DEROidCompare(type
, &oidLocalityName
)) {
5241 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
5242 label
= CFSTR("ST");
5243 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
5245 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
5246 label
= CFSTR("OU");
5247 } else if (DEROidCompare(type
, &oidCountryName
)) {
5250 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
5251 label
= CFSTR("STREET");
5252 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
5253 label
= CFSTR("DC");
5254 } else if (DEROidCompare(type
, &oidUserID
)) {
5255 label
= CFSTR("UID");
5258 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
5261 CFStringAppend(string
, label
);
5262 CFStringAppend(string
, CFSTR("="));
5263 CFStringRef raw
= NULL
;
5265 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true, localized
);
5268 /* Append raw to string while escaping:
5269 a space or "#" character occurring at the beginning of the string
5270 a space character occurring at the end of the string
5271 one of the characters ",", "+", """, "\", "<", ">" or ";"
5273 CFStringInlineBuffer buffer
= {};
5274 CFIndex ix
, length
= CFStringGetLength(raw
);
5275 CFRange range
= { 0, length
};
5276 CFStringInitInlineBuffer(raw
, &buffer
, range
);
5277 for (ix
= 0; ix
< length
; ++ix
) {
5278 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
5280 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
5281 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
5282 ch
== '<' || ch
== '>' || ch
== ';' ||
5283 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
5284 (ch
== '#' && ix
== 0)) {
5285 UniChar chars
[] = { '\\', ch
};
5286 CFStringAppendCharacters(string
, chars
, 2);
5288 CFStringAppendCharacters(string
, &ch
, 1);
5293 /* Append the value in hex. */
5294 CFStringAppend(string
, CFSTR("#"));
5296 for (ix
= 0; ix
< value
->length
; ++ix
)
5297 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
5302 return errSecSuccess
;
5305 CFStringRef
SecCertificateCopySubjectString(SecCertificateRef certificate
) {
5306 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
5307 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
, true);
5308 if (status
|| CFStringGetLength(string
) == 0) {
5315 static OSStatus
appendToCompanyNameString(void *context
,
5316 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5317 CFMutableStringRef string
= (CFMutableStringRef
)context
;
5318 if (CFStringGetLength(string
) != 0)
5319 return errSecSuccess
;
5321 if (!DEROidCompare(type
, &oidOrganizationName
))
5322 return errSecSuccess
;
5325 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true, localized
);
5327 return errSecSuccess
;
5328 CFStringAppend(string
, raw
);
5331 return errSecSuccess
;
5334 CFStringRef
SecCertificateCopyCompanyName(SecCertificateRef certificate
) {
5335 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
5336 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
5337 appendToCompanyNameString
, true);
5338 if (status
|| CFStringGetLength(string
) == 0) {
5345 CFDataRef
SecCertificateCopyIssuerSequence(
5346 SecCertificateRef certificate
) {
5347 return SecDERItemCopySequence(&certificate
->_issuer
);
5350 CFDataRef
SecCertificateCopySubjectSequence(
5351 SecCertificateRef certificate
) {
5352 return SecDERItemCopySequence(&certificate
->_subject
);
5355 CFDataRef
SecCertificateCopyNormalizedIssuerSequence(SecCertificateRef certificate
) {
5356 if (!certificate
|| !certificate
->_normalizedIssuer
) {
5359 return SecCopySequenceFromContent(certificate
->_normalizedIssuer
);
5362 CFDataRef
SecCertificateCopyNormalizedSubjectSequence(SecCertificateRef certificate
) {
5363 if (!certificate
|| !certificate
->_normalizedSubject
) {
5366 return SecCopySequenceFromContent(certificate
->_normalizedSubject
);
5369 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithm(
5370 SecCertificateRef certificate
) {
5371 return &certificate
->_algId
;
5374 const DERItem
*SecCertificateGetPublicKeyData(SecCertificateRef certificate
) {
5375 return &certificate
->_pubKeyDER
;
5379 /* There is already a SecCertificateCopyPublicKey with different args on OS X,
5380 so we will refer to this one internally as SecCertificateCopyPublicKey_ios.
5382 __nullable SecKeyRef
SecCertificateCopyPublicKey_ios(SecCertificateRef certificate
)
5384 __nullable SecKeyRef
SecCertificateCopyPublicKey(SecCertificateRef certificate
)
5387 return SecCertificateCopyKey(certificate
);
5390 SecKeyRef
SecCertificateCopyKey(SecCertificateRef certificate
) {
5391 if (certificate
->_pubKey
== NULL
) {
5392 const DERAlgorithmId
*algId
=
5393 SecCertificateGetPublicKeyAlgorithm(certificate
);
5394 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
5395 const DERItem
*params
= NULL
;
5396 if (algId
->params
.length
!= 0) {
5397 params
= &algId
->params
;
5399 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
5400 SecAsn1Item params1
= {
5401 .Data
= params
? params
->data
: NULL
,
5402 .Length
= params
? params
->length
: 0
5404 SecAsn1Item keyData1
= {
5405 .Data
= keyData
? keyData
->data
: NULL
,
5406 .Length
= keyData
? keyData
->length
: 0
5408 certificate
->_pubKey
= SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
5412 return CFRetainSafe(certificate
->_pubKey
);
5415 static CFIndex
SecCertificateGetPublicKeyAlgorithmIdAndSize(SecCertificateRef certificate
, size_t *keySizeInBytes
) {
5416 CFIndex keyAlgID
= kSecNullAlgorithmID
;
5419 SecKeyRef pubKey
= NULL
;
5420 require_quiet(certificate
, out
);
5421 require_quiet(pubKey
= SecCertificateCopyKey(certificate
) ,out
);
5422 size
= SecKeyGetBlockSize(pubKey
);
5423 keyAlgID
= SecKeyGetAlgorithmId(pubKey
);
5426 CFReleaseNull(pubKey
);
5427 if (keySizeInBytes
) { *keySizeInBytes
= size
; }
5432 * Public keys in certificates may be considered "weak" or "strong" or neither
5433 * (that is, in between). Certificates using weak keys are not trusted at all.
5434 * Certificates using neither strong nor weak keys are only trusted in certain
5435 * contexts. SecPolicy and SecPolicyServer define the contexts by which we enforce
5436 * these (or stronger) key size trust policies.
5438 bool SecCertificateIsWeakKey(SecCertificateRef certificate
) {
5439 if (!certificate
) { return true; }
5443 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5444 case kSecRSAAlgorithmID
:
5445 if (MIN_RSA_KEY_SIZE
<= size
) weak
= false;
5447 case kSecECDSAAlgorithmID
:
5448 if (MIN_EC_KEY_SIZE
<= size
) weak
= false;
5456 bool SecCertificateIsStrongKey(SecCertificateRef certificate
) {
5457 if (!certificate
) { return false; }
5459 bool strong
= false;
5461 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5462 case kSecRSAAlgorithmID
:
5463 if (MIN_STRONG_RSA_KEY_SIZE
<= size
) strong
= true;
5465 case kSecECDSAAlgorithmID
:
5466 if (MIN_STRONG_EC_KEY_SIZE
<= size
) strong
= true;
5474 bool SecCertificateIsWeakHash(SecCertificateRef certificate
) {
5475 if (!certificate
) { return true; }
5476 SecSignatureHashAlgorithm certAlg
= 0;
5477 certAlg
= SecCertificateGetSignatureHashAlgorithm(certificate
);
5478 if (certAlg
== kSecSignatureHashAlgorithmUnknown
||
5479 certAlg
== kSecSignatureHashAlgorithmMD2
||
5480 certAlg
== kSecSignatureHashAlgorithmMD4
||
5481 certAlg
== kSecSignatureHashAlgorithmMD5
||
5482 certAlg
== kSecSignatureHashAlgorithmSHA1
) {
5488 bool SecCertificateIsAtLeastMinKeySize(SecCertificateRef certificate
,
5489 CFDictionaryRef keySizes
) {
5490 if (!certificate
) { return false; }
5492 bool goodSize
= false;
5494 CFNumberRef minSize
;
5495 size_t minSizeInBits
;
5496 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5497 case kSecRSAAlgorithmID
:
5498 if(CFDictionaryGetValueIfPresent(keySizes
, kSecAttrKeyTypeRSA
, (const void**)&minSize
)
5499 && minSize
&& CFNumberGetValue(minSize
, kCFNumberLongType
, &minSizeInBits
)) {
5500 if (size
>= (size_t)(minSizeInBits
+7)/8) goodSize
= true;
5503 case kSecECDSAAlgorithmID
:
5504 if(CFDictionaryGetValueIfPresent(keySizes
, kSecAttrKeyTypeEC
, (const void**)&minSize
)
5505 && minSize
&& CFNumberGetValue(minSize
, kCFNumberLongType
, &minSizeInBits
)) {
5506 if (size
>= (size_t)(minSizeInBits
+7)/8) goodSize
= true;
5515 CFDataRef
SecCertificateGetSHA1Digest(SecCertificateRef certificate
) {
5516 if (!certificate
|| !certificate
->_der
.data
) {
5519 if (!certificate
->_sha1Digest
) {
5520 certificate
->_sha1Digest
=
5521 SecSHA1DigestCreate(CFGetAllocator(certificate
),
5522 certificate
->_der
.data
, certificate
->_der
.length
);
5524 return certificate
->_sha1Digest
;
5527 CFDataRef
SecCertificateCopySHA256Digest(SecCertificateRef certificate
) {
5528 if (!certificate
|| !certificate
->_der
.data
) {
5531 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
5532 certificate
->_der
.data
, certificate
->_der
.length
);
5535 CFDataRef
SecCertificateCopyIssuerSHA1Digest(SecCertificateRef certificate
) {
5536 CFDataRef digest
= NULL
;
5537 CFDataRef issuer
= SecCertificateCopyIssuerSequence(certificate
);
5539 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
5540 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
5546 CFDataRef
SecCertificateCopyPublicKeySHA1Digest(SecCertificateRef certificate
) {
5547 if (!certificate
|| !certificate
->_pubKeyDER
.data
) {
5550 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
5551 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
5554 static CFDataRef
SecCertificateCopySPKIEncoded(SecCertificateRef certificate
) {
5555 /* SPKI is saved without the tag/length by libDER, so we need to re-encode */
5556 if (!certificate
|| !certificate
->_subjectPublicKeyInfo
.data
) {
5559 DERSize size
= DERLengthOfItem(ASN1_CONSTR_SEQUENCE
, certificate
->_subjectPublicKeyInfo
.length
);
5560 if (size
< certificate
->_subjectPublicKeyInfo
.length
) {
5563 uint8_t *temp
= malloc(size
);
5567 DERReturn drtn
= DEREncodeItem(ASN1_CONSTR_SEQUENCE
,
5568 certificate
->_subjectPublicKeyInfo
.length
,
5569 certificate
->_subjectPublicKeyInfo
.data
,
5571 CFDataRef encodedSPKI
= NULL
;
5572 if (drtn
== DR_Success
) {
5573 encodedSPKI
= CFDataCreate(NULL
, temp
, size
);
5579 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA1Digest(SecCertificateRef certificate
) {
5580 CFDataRef encodedSPKI
= SecCertificateCopySPKIEncoded(certificate
);
5581 if (!encodedSPKI
) { return NULL
; }
5582 CFDataRef hash
= SecSHA1DigestCreate(CFGetAllocator(certificate
),
5583 CFDataGetBytePtr(encodedSPKI
),
5584 CFDataGetLength(encodedSPKI
));
5585 CFReleaseNull(encodedSPKI
);
5589 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA256Digest(SecCertificateRef certificate
) {
5590 CFDataRef encodedSPKI
= SecCertificateCopySPKIEncoded(certificate
);
5591 if (!encodedSPKI
) { return NULL
; }
5592 CFDataRef hash
= SecSHA256DigestCreate(CFGetAllocator(certificate
),
5593 CFDataGetBytePtr(encodedSPKI
),
5594 CFDataGetLength(encodedSPKI
));
5595 CFReleaseNull(encodedSPKI
);
5599 CFTypeRef
SecCertificateCopyKeychainItem(SecCertificateRef certificate
)
5604 CFRetainSafe(certificate
->_keychain_item
);
5605 return certificate
->_keychain_item
;
5608 CFDataRef
SecCertificateGetAuthorityKeyID(SecCertificateRef certificate
) {
5612 if (!certificate
->_authorityKeyID
&&
5613 certificate
->_authorityKeyIdentifier
.length
) {
5614 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
5615 certificate
->_authorityKeyIdentifier
.data
,
5616 certificate
->_authorityKeyIdentifier
.length
);
5619 return certificate
->_authorityKeyID
;
5622 CFDataRef
SecCertificateGetSubjectKeyID(SecCertificateRef certificate
) {
5626 if (!certificate
->_subjectKeyID
&&
5627 certificate
->_subjectKeyIdentifier
.length
) {
5628 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
5629 certificate
->_subjectKeyIdentifier
.data
,
5630 certificate
->_subjectKeyIdentifier
.length
);
5633 return certificate
->_subjectKeyID
;
5636 CFArrayRef
SecCertificateGetCRLDistributionPoints(SecCertificateRef certificate
) {
5640 return certificate
->_crlDistributionPoints
;
5643 CFArrayRef
SecCertificateGetOCSPResponders(SecCertificateRef certificate
) {
5647 return certificate
->_ocspResponders
;
5650 CFArrayRef
SecCertificateGetCAIssuers(SecCertificateRef certificate
) {
5654 return certificate
->_caIssuers
;
5657 bool SecCertificateHasCriticalSubjectAltName(SecCertificateRef certificate
) {
5661 return certificate
->_subjectAltName
&&
5662 certificate
->_subjectAltName
->critical
;
5665 bool SecCertificateHasSubject(SecCertificateRef certificate
) {
5669 /* Since the _subject field is the content of the subject and not the
5670 whole thing, we can simply check for a 0 length subject here. */
5671 return certificate
->_subject
.length
!= 0;
5674 bool SecCertificateHasUnknownCriticalExtension(SecCertificateRef certificate
) {
5678 return certificate
->_foundUnknownCriticalExtension
;
5681 /* Private API functions. */
5682 void SecCertificateShow(SecCertificateRef certificate
) {
5684 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
5685 fprintf(stderr
, "\n");
5689 CFDictionaryRef
SecCertificateCopyAttributeDictionary(
5690 SecCertificateRef certificate
) {
5691 if (!SecCertificateIsCertificate(certificate
)) {
5694 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
5695 CFNumberRef certificateType
= NULL
;
5696 CFNumberRef certificateEncoding
= NULL
;
5697 CFStringRef label
= NULL
;
5698 CFStringRef alias
= NULL
;
5699 CFDataRef skid
= NULL
;
5700 CFDataRef pubKeyDigest
= NULL
;
5701 CFDataRef certData
= NULL
;
5702 CFDictionaryRef dict
= NULL
;
5706 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
5707 SInt32 ctv
= certificate
->_version
+ 1;
5708 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
5709 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
5710 require_quiet(certificateType
!= NULL
, out
);
5711 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
5712 require_quiet(certificateEncoding
!= NULL
, out
);
5713 certData
= SecCertificateCopyData(certificate
);
5714 require_quiet(certData
!= NULL
, out
);
5715 skid
= SecCertificateGetSubjectKeyID(certificate
);
5716 require_quiet(certificate
->_pubKeyDER
.data
!= NULL
&& certificate
->_pubKeyDER
.length
> 0, out
);
5717 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
5718 certificate
->_pubKeyDER
.length
);
5719 require_quiet(pubKeyDigest
!= NULL
, out
);
5721 /* We still need to figure out how to deal with multi valued attributes. */
5722 alias
= SecCertificateCopyRFC822Names(certificate
);
5723 label
= SecCertificateCopySubjectSummary(certificate
);
5729 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
5730 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
5731 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
5733 DICT_ADDPAIR(kSecAttrLabel
, label
);
5736 DICT_ADDPAIR(kSecAttrAlias
, alias
);
5738 if (isData(certificate
->_normalizedSubject
)) {
5739 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
5741 require_quiet(isData(certificate
->_normalizedIssuer
), out
);
5742 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
5743 require_quiet(isData(certificate
->_serialNumber
), out
);
5744 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
5746 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
5748 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
5749 DICT_ADDPAIR(kSecValueData
, certData
);
5750 dict
= DICT_CREATE(allocator
);
5753 CFReleaseSafe(label
);
5754 CFReleaseSafe(alias
);
5755 CFReleaseSafe(pubKeyDigest
);
5756 CFReleaseSafe(certData
);
5757 CFReleaseSafe(certificateEncoding
);
5758 CFReleaseSafe(certificateType
);
5763 SecCertificateRef
SecCertificateCreateFromAttributeDictionary(
5764 CFDictionaryRef refAttributes
) {
5765 /* @@@ Support having an allocator in refAttributes. */
5766 CFAllocatorRef allocator
= NULL
;
5767 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
5768 return data
? SecCertificateCreateWithData(allocator
, data
) : NULL
;
5772 static bool _SecCertificateIsSelfSigned(SecCertificateRef certificate
) {
5773 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
5774 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
5775 SecKeyRef publicKey
= NULL
;
5776 require(SecCertificateIsCertificate(certificate
), out
);
5777 require(publicKey
= SecCertificateCopyKey(certificate
), out
);
5778 CFDataRef normalizedIssuer
=
5779 SecCertificateGetNormalizedIssuerContent(certificate
);
5780 CFDataRef normalizedSubject
=
5781 SecCertificateGetNormalizedSubjectContent(certificate
);
5782 require_quiet(normalizedIssuer
&& normalizedSubject
&&
5783 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
5785 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(certificate
);
5786 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyID(certificate
);
5787 if (authorityKeyID
) {
5788 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
5791 require_noerr_quiet(SecCertificateIsSignedBy(certificate
, publicKey
), out
);
5793 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
5795 CFReleaseSafe(publicKey
);
5798 return (certificate
->_isSelfSigned
== kSecSelfSignedTrue
);
5801 bool SecCertificateIsCA(SecCertificateRef certificate
) {
5802 bool result
= false;
5803 require(SecCertificateIsCertificate(certificate
), out
);
5804 if (SecCertificateVersion(certificate
) >= 3) {
5805 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraints(certificate
);
5806 result
= (basicConstraints
&& basicConstraints
->isCA
);
5809 result
= _SecCertificateIsSelfSigned(certificate
);
5815 bool SecCertificateIsSelfSignedCA(SecCertificateRef certificate
) {
5816 return (_SecCertificateIsSelfSigned(certificate
) && SecCertificateIsCA(certificate
));
5819 OSStatus
SecCertificateIsSelfSigned(SecCertificateRef certificate
, Boolean
*isSelfSigned
) {
5820 if (!SecCertificateIsCertificate(certificate
)) {
5821 return errSecInvalidCertificate
;
5823 if (!isSelfSigned
) {
5826 *isSelfSigned
= _SecCertificateIsSelfSigned(certificate
);
5827 return errSecSuccess
;
5830 SecKeyUsage
SecCertificateGetKeyUsage(SecCertificateRef certificate
) {
5832 return kSecKeyUsageUnspecified
;
5834 return certificate
->_keyUsage
;
5837 CFArrayRef
SecCertificateCopyExtendedKeyUsage(SecCertificateRef certificate
)
5839 CFMutableArrayRef extended_key_usage_oids
=
5840 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
5841 require_quiet(certificate
&& extended_key_usage_oids
, out
);
5843 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5844 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5845 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
5846 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
5849 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
5850 require_noerr_quiet(drtn
, out
);
5851 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
5852 DERDecodedInfo currDecoded
;
5854 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
5855 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
5856 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
5857 currDecoded
.content
.data
, currDecoded
.content
.length
);
5858 require_quiet(oid
, out
);
5859 CFArrayAppendValue(extended_key_usage_oids
, oid
);
5862 require_quiet(drtn
== DR_EndOfSequence
, out
);
5863 return extended_key_usage_oids
;
5867 CFReleaseSafe(extended_key_usage_oids
);
5871 CFArrayRef
SecCertificateCopySignedCertificateTimestamps(SecCertificateRef certificate
)
5873 require_quiet(certificate
, out
);
5876 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5877 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5878 if (extn
->extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
5879 !memcmp(extn
->extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
->extnID
.length
)) {
5880 /* Got the SCT oid */
5881 DERDecodedInfo sctList
;
5882 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &sctList
);
5883 require_noerr_quiet(drtn
, out
);
5884 require_quiet(sctList
.tag
== ASN1_OCTET_STRING
, out
);
5885 return SecCreateSignedCertificateTimestampsArrayFromSerializedSCTList(sctList
.content
.data
, sctList
.content
.length
);
5893 static bool matches_expected(DERItem der
, CFTypeRef expected
) {
5894 if (der
.length
> 1) {
5895 DERDecodedInfo decoded
;
5896 DERDecodeItem(&der
, &decoded
);
5897 switch (decoded
.tag
) {
5900 return decoded
.content
.length
== 0 && expected
== NULL
;
5904 case ASN1_IA5_STRING
:
5905 case ASN1_UTF8_STRING
: {
5906 if (isString(expected
)) {
5907 CFStringRef expectedString
= (CFStringRef
) expected
;
5908 CFStringRef itemString
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFStringEncodingUTF8
, false, kCFAllocatorNull
);
5910 bool result
= (kCFCompareEqualTo
== CFStringCompare(expectedString
, itemString
, 0));
5911 CFReleaseNull(itemString
);
5917 case ASN1_OCTET_STRING
: {
5918 if (isData(expected
)) {
5919 CFDataRef expectedData
= (CFDataRef
) expected
;
5920 CFDataRef itemData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFAllocatorNull
);
5922 bool result
= CFEqual(expectedData
, itemData
);
5923 CFReleaseNull(itemData
);
5929 case ASN1_INTEGER
: {
5930 SInt32 expected_value
= 0;
5931 if (isString(expected
))
5933 CFStringRef aStr
= (CFStringRef
)expected
;
5934 expected_value
= CFStringGetIntValue(aStr
);
5936 else if (isNumber(expected
))
5938 CFNumberGetValue(expected
, kCFNumberSInt32Type
, &expected_value
);
5941 uint32_t num_value
= 0;
5942 if (!DERParseInteger(&decoded
.content
, &num_value
))
5944 return ((uint32_t)expected_value
== num_value
);
5957 static bool cert_contains_marker_extension_value(SecCertificateRef certificate
, CFDataRef oid
, CFTypeRef expectedValue
)
5960 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
5961 size_t oid_len
= CFDataGetLength(oid
);
5963 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5964 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5965 if (extn
->extnID
.length
== oid_len
5966 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
5968 return matches_expected(extn
->extnValue
, expectedValue
);
5974 static bool cert_contains_marker_extension(SecCertificateRef certificate
, CFTypeRef oid
)
5976 return cert_contains_marker_extension_value(certificate
, oid
, NULL
);
5979 struct search_context
{
5981 SecCertificateRef certificate
;
5984 static bool GetDecimalValueOfString(CFStringRef string
, uint32_t* value
)
5986 CFCharacterSetRef nonDecimalDigit
= CFCharacterSetCreateInvertedSet(NULL
, CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
));
5987 bool result
= false;
5989 if ( CFStringGetLength(string
) > 0
5990 && !CFStringFindCharacterFromSet(string
, nonDecimalDigit
, CFRangeMake(0, CFStringGetLength(string
)), kCFCompareForcedOrdering
, NULL
))
5993 *value
= CFStringGetIntValue(string
);
5997 CFReleaseNull(nonDecimalDigit
);
6002 bool SecCertificateIsOidString(CFStringRef oid
)
6004 if (!oid
) return false;
6005 if (2 >= CFStringGetLength(oid
)) return false;
6008 /* oid string only has the allowed characters */
6009 CFCharacterSetRef decimalOid
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789."));
6010 CFCharacterSetRef nonDecimalOid
= CFCharacterSetCreateInvertedSet(NULL
, decimalOid
);
6011 if (CFStringFindCharacterFromSet(oid
, nonDecimalOid
, CFRangeMake(0, CFStringGetLength(oid
)), kCFCompareForcedOrdering
, NULL
)) {
6015 /* first arc is allowed */
6016 UniChar firstArc
[2];
6017 CFRange firstTwo
= {0, 2};
6018 CFStringGetCharacters(oid
, firstTwo
, firstArc
);
6019 if (firstArc
[1] != '.' ||
6020 (firstArc
[0] != '0' && firstArc
[0] != '1' && firstArc
[0] != '2')) {
6024 CFReleaseNull(decimalOid
);
6025 CFReleaseNull(nonDecimalOid
);
6030 CFDataRef
SecCertificateCreateOidDataFromString(CFAllocatorRef allocator
, CFStringRef string
)
6032 CFMutableDataRef currentResult
= NULL
;
6033 CFDataRef encodedResult
= NULL
;
6035 CFArrayRef parts
= NULL
;
6038 if (!string
|| !SecCertificateIsOidString(string
))
6041 parts
= CFStringCreateArrayBySeparatingStrings(allocator
, string
, CFSTR("."));
6046 count
= CFArrayGetCount(parts
);
6050 // assume no more than 5 bytes needed to represent any part of the oid,
6051 // since we limit parts to 32-bit values,
6052 // but the first two parts only need 1 byte
6053 currentResult
= CFDataCreateMutable(allocator
, 1+(count
-2)*5);
6059 part
= CFArrayGetValueAtIndex(parts
, 0);
6061 if (!GetDecimalValueOfString(part
, &x
) || x
> 6)
6068 part
= CFArrayGetValueAtIndex(parts
, 1);
6070 if (!GetDecimalValueOfString(part
, &x
) || x
> 39)
6076 CFDataAppendBytes(currentResult
, &firstByte
, 1);
6078 for (CFIndex i
= 2; i
< count
&& GetDecimalValueOfString(CFArrayGetValueAtIndex(parts
, i
), &x
); ++i
) {
6079 uint8_t b
[5] = {0, 0, 0, 0, 0};
6081 b
[3] = 0x80 | ((x
>> 7) & 0x7F);
6082 b
[2] = 0x80 | ((x
>> 14) & 0x7F);
6083 b
[1] = 0x80 | ((x
>> 21) & 0x7F);
6084 b
[0] = 0x80 | ((x
>> 28) & 0x7F);
6086 // Skip the unused extension bytes.
6087 size_t skipBytes
= 0;
6088 while (b
[skipBytes
] == 0x80)
6091 CFDataAppendBytes(currentResult
, b
+ skipBytes
, sizeof(b
) - skipBytes
);
6094 encodedResult
= currentResult
;
6095 currentResult
= NULL
;
6098 CFReleaseNull(parts
);
6099 CFReleaseNull(currentResult
);
6101 return encodedResult
;
6104 static void check_for_marker(const void *key
, const void *value
, void *context
)
6106 struct search_context
* search_ctx
= (struct search_context
*) context
;
6107 CFStringRef key_string
= (CFStringRef
) key
;
6108 CFTypeRef value_ref
= (CFTypeRef
) value
;
6110 // If we could have short-circuited the iteration
6111 // we would have, but the best we can do
6112 // is not waste time comparing once a match
6114 if (search_ctx
->found
)
6117 if (!isString(key_string
))
6120 CFDataRef key_data
= SecCertificateCreateOidDataFromString(NULL
, key_string
);
6122 if (!isData(key_data
))
6125 if (cert_contains_marker_extension_value(search_ctx
->certificate
, key_data
, value_ref
))
6126 search_ctx
->found
= true;
6128 CFReleaseNull(key_data
);
6132 // CFType Ref is either:
6134 // CFData - OID to match with no data permitted
6135 // CFString - decimal OID to match
6136 // CFDictionary - OID -> Value table for expected values Single Object or Array
6137 // CFArray - Array of the above.
6139 // This returns true if any of the requirements are met.
6140 bool SecCertificateHasMarkerExtension(SecCertificateRef certificate
, CFTypeRef oids
)
6142 if (NULL
== certificate
|| NULL
== oids
) {
6144 } else if (CFGetTypeID(oids
) == CFArrayGetTypeID()) {
6145 CFIndex ix
, length
= CFArrayGetCount(oids
);
6146 for (ix
= 0; ix
< length
; ix
++)
6147 if (SecCertificateHasMarkerExtension(certificate
, CFArrayGetValueAtIndex((CFArrayRef
)oids
, ix
)))
6149 } else if (CFGetTypeID(oids
) == CFDictionaryGetTypeID()) {
6150 struct search_context context
= { .found
= false, .certificate
= certificate
};
6151 CFDictionaryApplyFunction((CFDictionaryRef
) oids
, &check_for_marker
, &context
);
6152 return context
.found
;
6153 } else if (CFGetTypeID(oids
) == CFDataGetTypeID()) {
6154 return cert_contains_marker_extension(certificate
, oids
);
6155 } else if (CFGetTypeID(oids
) == CFStringGetTypeID()) {
6156 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, oids
);
6157 if (dataOid
== NULL
) return false;
6158 bool result
= cert_contains_marker_extension(certificate
, dataOid
);
6159 CFReleaseNull(dataOid
);
6165 // Since trust evaluation checks for the id-pkix-ocsp-nocheck OID marker
6166 // in every certificate, this function caches the OID data once instead of
6167 // parsing the same OID string each time.
6169 bool SecCertificateHasOCSPNoCheckMarkerExtension(SecCertificateRef certificate
)
6171 static CFDataRef sOCSPNoCheckOIDData
= NULL
;
6172 static dispatch_once_t onceToken
;
6174 dispatch_once(&onceToken
, ^{
6175 sOCSPNoCheckOIDData
= SecCertificateCreateOidDataFromString(NULL
, CFSTR("1.3.6.1.5.5.7.48.1.5"));
6177 return SecCertificateHasMarkerExtension(certificate
, sOCSPNoCheckOIDData
);
6180 static DERItem
*cert_extension_value_for_marker(SecCertificateRef certificate
, CFDataRef oid
) {
6182 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
6183 size_t oid_len
= CFDataGetLength(oid
);
6185 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
6186 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
6187 if (extn
->extnID
.length
== oid_len
6188 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
6190 return (DERItem
*)&extn
->extnValue
;
6197 // CFType Ref is either:
6199 // CFData - OID to match with no data permitted
6200 // CFString - decimal OID to match
6202 DERItem
*SecCertificateGetExtensionValue(SecCertificateRef certificate
, CFTypeRef oid
) {
6203 if (!certificate
|| !oid
) {
6207 if(CFGetTypeID(oid
) == CFDataGetTypeID()) {
6208 return cert_extension_value_for_marker(certificate
, oid
);
6209 } else if (CFGetTypeID(oid
) == CFStringGetTypeID()) {
6210 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, oid
);
6211 if (dataOid
== NULL
) return NULL
;
6212 DERItem
*result
= cert_extension_value_for_marker(certificate
, dataOid
);
6213 CFReleaseNull(dataOid
);
6220 CFDataRef
SecCertificateCopyExtensionValue(SecCertificateRef certificate
, CFTypeRef extensionOID
, bool *isCritical
) {
6221 if (!certificate
|| !extensionOID
) {
6225 CFDataRef oid
= NULL
, extensionValue
= NULL
;
6226 if (CFGetTypeID(extensionOID
) == CFDataGetTypeID()) {
6227 oid
= CFRetainSafe(extensionOID
);
6228 } else if (CFGetTypeID(extensionOID
) == CFStringGetTypeID()) {
6229 oid
= SecCertificateCreateOidDataFromString(NULL
, extensionOID
);
6236 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
6237 size_t oid_len
= CFDataGetLength(oid
);
6239 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
6240 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
6241 if (extn
->extnID
.length
== oid_len
6242 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
6245 *isCritical
= extn
->critical
;
6247 extensionValue
= CFDataCreate(NULL
, extn
->extnValue
.data
, extn
->extnValue
.length
);
6253 return extensionValue
;
6256 CFDataRef
SecCertificateCopyiAPAuthCapabilities(SecCertificateRef certificate
) {
6260 CFDataRef extensionData
= NULL
;
6261 DERItem
*extensionValue
= NULL
;
6262 extensionValue
= SecCertificateGetExtensionValue(certificate
,
6263 CFSTR("1.2.840.113635.100.6.36"));
6264 require_quiet(extensionValue
, out
);
6265 /* The extension is a octet string containing the DER-encoded 32-byte octet string */
6266 require_quiet(extensionValue
->length
== 34, out
);
6267 DERDecodedInfo decodedValue
;
6268 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
6269 if (decodedValue
.tag
== ASN1_OCTET_STRING
) {
6270 require_quiet(decodedValue
.content
.length
== 32, out
);
6271 extensionData
= CFDataCreate(NULL
, decodedValue
.content
.data
,
6272 decodedValue
.content
.length
);
6274 require_quiet(extensionValue
->data
[33] == 0x00 &&
6275 extensionValue
->data
[32] == 0x00, out
);
6276 extensionData
= CFDataCreate(NULL
, extensionValue
->data
, 32);
6279 return extensionData
;
6283 /* From iapd IAPAuthenticationTypes.h */
6284 typedef struct IapCertSerialNumber
6286 uint8_t xservID
; // Xserver ID
6287 uint8_t hsmID
; // Hardware security module ID (generated cert)
6288 uint8_t delimiter01
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6289 uint8_t dateYear
; // Date year cert was issued
6290 uint8_t dateMonth
; // Date month cert was issued
6291 uint8_t dateDay
; // Date day cert was issued
6292 uint8_t delimiter02
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6293 uint8_t devClass
; // iAP device class (maps to lingo permissions)
6294 uint8_t delimiter03
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6295 uint8_t batchNumHi
; // Batch number high byte (15:08)
6296 uint8_t batchNumLo
; // Batch number low byte (07:00)
6297 uint8_t delimiter04
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6298 uint8_t serialNumHi
; // Serial number high byte (23:16)
6299 uint8_t serialNumMid
; // Serial number middle byte (15:08)
6300 uint8_t serialNumLo
; // Serial number low byte (07:00)
6302 } IapCertSerialNumber_t
, *pIapCertSerialNumber_t
;
6305 #define IAP_CERT_FIELD_DELIMITER 0xAA // "Apple_Accessory" delimiter
6306 SeciAuthVersion
SecCertificateGetiAuthVersion(SecCertificateRef certificate
) {
6308 return kSeciAuthInvalid
;
6310 if (NULL
!= SecCertificateGetExtensionValue(certificate
,
6311 CFSTR("1.2.840.113635.100.6.36"))) {
6312 /* v3 Capabilities Extension */
6313 return kSeciAuthVersion3
;
6315 if (NULL
!= SecCertificateGetExtensionValue(certificate
,
6316 CFSTR("1.2.840.113635.100.6.59.1"))) {
6317 /* SW Auth General Capabilities Extension */
6318 return kSeciAuthVersionSW
;
6320 DERItem serialNumber
= certificate
->_serialNum
;
6321 require_quiet(serialNumber
.data
, out
);
6322 require_quiet(serialNumber
.length
== 15, out
);
6323 require_quiet(serialNumber
.data
[2] == IAP_CERT_FIELD_DELIMITER
&&
6324 serialNumber
.data
[6] == IAP_CERT_FIELD_DELIMITER
&&
6325 serialNumber
.data
[8] == IAP_CERT_FIELD_DELIMITER
&&
6326 serialNumber
.data
[11] == IAP_CERT_FIELD_DELIMITER
, out
);
6327 return kSeciAuthVersion2
;
6329 return kSeciAuthInvalid
;
6332 static CFStringRef
SecCertificateiAPSWAuthCapabilitiesTypeToOID(SeciAPSWAuthCapabilitiesType type
) {
6333 CFStringRef extensionOID
= NULL
;
6334 /* Get the oid for the type */
6335 if (type
== kSeciAPSWAuthGeneralCapabilities
) {
6336 extensionOID
= CFSTR("1.2.840.113635.100.6.59.1");
6337 } else if (type
== kSeciAPSWAuthAirPlayCapabilities
) {
6338 extensionOID
= CFSTR("1.2.840.113635.100.6.59.2");
6339 } else if (type
== kSeciAPSWAuthHomeKitCapabilities
) {
6340 extensionOID
= CFSTR("1.2.840.113635.100.6.59.3");
6342 return extensionOID
;
6345 CFDataRef
SecCertificateCopyiAPSWAuthCapabilities(SecCertificateRef certificate
, SeciAPSWAuthCapabilitiesType type
) {
6349 CFDataRef extensionData
= NULL
;
6350 DERItem
*extensionValue
= NULL
;
6351 CFStringRef extensionOID
= SecCertificateiAPSWAuthCapabilitiesTypeToOID(type
);
6352 require_quiet(extensionOID
, out
);
6353 extensionValue
= SecCertificateGetExtensionValue(certificate
, extensionOID
);
6354 require_quiet(extensionValue
, out
);
6355 /* The extension is a octet string containing the DER-encoded variable-length octet string */
6356 DERDecodedInfo decodedValue
;
6357 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
6358 if (decodedValue
.tag
== ASN1_OCTET_STRING
) {
6359 extensionData
= CFDataCreate(NULL
, decodedValue
.content
.data
,
6360 decodedValue
.content
.length
);
6363 return extensionData
;
6366 CFStringRef
SecCertificateCopyComponentType(SecCertificateRef certificate
) {
6370 CFStringRef componentType
= NULL
;
6371 DERItem
*extensionValue
= SecCertificateGetExtensionValue(certificate
, CFSTR("1.2.840.113635.100.11.1"));
6372 require_quiet(extensionValue
, out
);
6373 /* The componentType is an IA5String */
6374 DERDecodedInfo decodedValue
;
6375 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
6376 if (decodedValue
.tag
== ASN1_IA5_STRING
) {
6377 componentType
= CFStringCreateWithBytes(NULL
, decodedValue
.content
.data
, decodedValue
.content
.length
, kCFStringEncodingASCII
, false);
6380 return componentType
;
6383 SecCertificateRef
SecCertificateCreateWithPEM(CFAllocatorRef allocator
,
6384 CFDataRef pem_certificate
)
6386 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
6387 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
6388 uint8_t *base64_data
= NULL
;
6389 SecCertificateRef cert
= NULL
;
6390 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
6391 const size_t length
= CFDataGetLength(pem_certificate
);
6392 char *begin
= strnstr((const char *)data
, begin_cert
, length
);
6393 char *end
= strnstr((const char *)data
, end_cert
, length
);
6396 begin
+= sizeof(begin_cert
) - 1;
6397 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
6398 if (base64_length
&& (base64_length
< (size_t)CFDataGetLength(pem_certificate
))) {
6399 require_quiet(base64_data
= calloc(1, base64_length
), out
);
6400 require_action_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
, free(base64_data
));
6401 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, base64_data
, base64_length
);
6410 // -- MARK -- XPC encoding/decoding
6413 bool SecCertificateAppendToXPCArray(SecCertificateRef certificate
, xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
6415 return true; // NOOP
6417 size_t length
= SecCertificateGetLength(certificate
);
6418 const uint8_t *bytes
= SecCertificateGetBytePtr(certificate
);
6419 #if SECTRUST_VERBOSE_DEBUG
6420 secerror("cert=0x%lX length=%d bytes=0x%lX", (uintptr_t)certificate
, (int)length
, (uintptr_t)bytes
);
6422 if (!length
|| !bytes
) {
6423 return SecError(errSecParam
, error
, CFSTR("failed to der encode certificate"));
6425 xpc_array_set_data(xpc_certificates
, XPC_ARRAY_APPEND
, bytes
, length
);
6429 SecCertificateRef
SecCertificateCreateWithXPCArrayAtIndex(xpc_object_t xpc_certificates
, size_t index
, CFErrorRef
*error
) {
6430 SecCertificateRef certificate
= NULL
;
6432 const uint8_t *bytes
= xpc_array_get_data(xpc_certificates
, index
, &length
);
6434 certificate
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, bytes
, length
);
6437 SecError(errSecParam
, error
, CFSTR("certificates[%zu] failed to decode"), index
);
6442 xpc_object_t
SecCertificateArrayCopyXPCArray(CFArrayRef certificates
, CFErrorRef
*error
) {
6443 xpc_object_t xpc_certificates
;
6444 require_action_quiet(xpc_certificates
= xpc_array_create(NULL
, 0), exit
,
6445 SecError(errSecAllocate
, error
, CFSTR("failed to create xpc_array")));
6446 CFIndex ix
, count
= CFArrayGetCount(certificates
);
6447 for (ix
= 0; ix
< count
; ++ix
) {
6448 SecCertificateRef certificate
= (SecCertificateRef
) CFArrayGetValueAtIndex(certificates
, ix
);
6449 #if SECTRUST_VERBOSE_DEBUG
6450 CFIndex length
= SecCertificateGetLength(certificate
);
6451 const UInt8
*bytes
= SecCertificateGetBytePtr(certificate
);
6452 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
);
6454 if (!SecCertificateAppendToXPCArray(certificate
, xpc_certificates
, error
)) {
6455 xpc_release(xpc_certificates
);
6456 xpc_certificates
= NULL
;
6462 return xpc_certificates
;
6465 CFArrayRef
SecCertificateXPCArrayCopyArray(xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
6466 CFMutableArrayRef certificates
= NULL
;
6467 require_action_quiet(xpc_get_type(xpc_certificates
) == XPC_TYPE_ARRAY
, exit
,
6468 SecError(errSecParam
, error
, CFSTR("certificates xpc value is not an array")));
6469 size_t count
= xpc_array_get_count(xpc_certificates
);
6470 require_action_quiet(certificates
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
), exit
,
6471 SecError(errSecAllocate
, error
, CFSTR("failed to create CFArray of capacity %zu"), count
));
6474 for (ix
= 0; ix
< count
; ++ix
) {
6475 SecCertificateRef cert
= SecCertificateCreateWithXPCArrayAtIndex(xpc_certificates
, ix
, error
);
6477 CFRelease(certificates
);
6480 CFArraySetValueAtIndex(certificates
, ix
, cert
);
6485 return certificates
;
6488 #define do_if_registered(sdp, ...) if (gTrustd && gTrustd->sdp) { return gTrustd->sdp(__VA_ARGS__); }
6491 static CFArrayRef
CopyEscrowCertificates(SecCertificateEscrowRootType escrowRootType
, CFErrorRef
* error
)
6493 __block CFArrayRef result
= NULL
;
6495 do_if_registered(ota_CopyEscrowCertificates
, escrowRootType
, error
);
6497 securityd_send_sync_and_do(kSecXPCOpOTAGetEscrowCertificates
, error
,
6498 ^bool(xpc_object_t message
, CFErrorRef
*blockError
)
6500 xpc_dictionary_set_uint64(message
, "escrowType", (uint64_t)escrowRootType
);
6503 ^bool(xpc_object_t response
, CFErrorRef
*blockError
)
6505 xpc_object_t xpc_array
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
6507 if (response
&& (NULL
!= xpc_array
)) {
6508 result
= (CFArrayRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_array
);
6511 return SecError(errSecInternal
, blockError
, CFSTR("Did not get the Escrow certificates"));
6513 return result
!= NULL
;
6518 CFArrayRef
SecCertificateCopyEscrowRoots(SecCertificateEscrowRootType escrowRootType
)
6520 CFArrayRef result
= NULL
;
6522 CFDataRef certData
= NULL
;
6525 if (kSecCertificateBaselineEscrowRoot
== escrowRootType
||
6526 kSecCertificateBaselinePCSEscrowRoot
== escrowRootType
||
6527 kSecCertificateBaselineEscrowBackupRoot
== escrowRootType
||
6528 kSecCertificateBaselineEscrowEnrollmentRoot
== escrowRootType
)
6530 // The request is for the base line certificates.
6531 // Use the hard coded data to generate the return array.
6532 struct RootRecord
** pEscrowRoots
;
6533 switch (escrowRootType
) {
6534 case kSecCertificateBaselineEscrowRoot
:
6535 numRoots
= kNumberOfBaseLineEscrowRoots
;
6536 pEscrowRoots
= kBaseLineEscrowRoots
;
6538 case kSecCertificateBaselinePCSEscrowRoot
:
6539 numRoots
= kNumberOfBaseLinePCSEscrowRoots
;
6540 pEscrowRoots
= kBaseLinePCSEscrowRoots
;
6542 case kSecCertificateBaselineEscrowBackupRoot
:
6543 numRoots
= kNumberOfBaseLineEscrowBackupRoots
;
6544 pEscrowRoots
= kBaseLineEscrowBackupRoots
;
6546 case kSecCertificateBaselineEscrowEnrollmentRoot
:
6548 numRoots
= kNumberOfBaseLineEscrowEnrollmentRoots
;
6549 pEscrowRoots
= kBaseLineEscrowEnrollmentRoots
;
6553 // Get the hard coded set of roots
6554 SecCertificateRef baseLineCerts
[numRoots
];
6555 struct RootRecord
* pRootRecord
= NULL
;
6557 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6558 pRootRecord
= pEscrowRoots
[iCnt
];
6559 if (NULL
!= pRootRecord
&& pRootRecord
->_length
> 0 && NULL
!= pRootRecord
->_bytes
) {
6560 certData
= CFDataCreate(kCFAllocatorDefault
, pRootRecord
->_bytes
, pRootRecord
->_length
);
6561 if (NULL
!= certData
) {
6562 baseLineCerts
[iCnt
] = SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
6563 CFRelease(certData
);
6567 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)baseLineCerts
, numRoots
, &kCFTypeArrayCallBacks
);
6568 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6569 if (NULL
!= baseLineCerts
[iCnt
]) {
6570 CFRelease(baseLineCerts
[iCnt
]);
6574 // The request is for the current certificates.
6575 CFErrorRef error
= NULL
;
6576 CFArrayRef cert_datas
= CopyEscrowCertificates(escrowRootType
, &error
);
6577 if (NULL
!= error
|| NULL
== cert_datas
) {
6578 if (NULL
!= error
) {
6581 if (NULL
!= cert_datas
) {
6582 CFRelease(cert_datas
);
6587 numRoots
= (int)(CFArrayGetCount(cert_datas
));
6589 SecCertificateRef assetCerts
[numRoots
];
6590 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6591 certData
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, iCnt
);
6592 if (NULL
!= certData
) {
6593 SecCertificateRef aCertRef
= SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
6594 assetCerts
[iCnt
] = aCertRef
;
6597 assetCerts
[iCnt
] = NULL
;
6602 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)assetCerts
, numRoots
, &kCFTypeArrayCallBacks
);
6603 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6604 if (NULL
!= assetCerts
[iCnt
]) {
6605 CFRelease(assetCerts
[iCnt
]);
6609 CFReleaseSafe(cert_datas
);
6614 static CFDictionaryRef
CopyTrustedCTLogs(CFErrorRef
* error
)
6616 __block CFDictionaryRef result
= NULL
;
6618 // call function directly and return if we are built in server mode
6619 do_if_registered(sec_ota_pki_copy_trusted_ct_logs
, error
);
6621 securityd_send_sync_and_do(kSecXPCOpOTAPKICopyTrustedCTLogs
, error
,
6622 ^bool(xpc_object_t message
, CFErrorRef
*blockError
) {
6623 // input: set message parameters here
6625 }, ^bool(xpc_object_t response
, CFErrorRef
*blockError
) {
6626 // output: get dictionary from response object
6627 xpc_object_t xpc_dictionary
= NULL
;
6629 xpc_dictionary
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
6631 if (xpc_dictionary
&& (xpc_get_type(xpc_dictionary
) == XPC_TYPE_DICTIONARY
)) {
6632 result
= (CFDictionaryRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_dictionary
);
6634 return SecError(errSecInternal
, blockError
, CFSTR("Unable to get CT logs"));
6636 return result
!= NULL
;
6641 #define CTLOG_KEYID_LENGTH 32 /* key id data length */
6643 static CFDictionaryRef
CopyCTLogForKeyID(CFDataRef keyID
, CFErrorRef
* error
)
6645 __block CFDictionaryRef result
= NULL
;
6646 if (!isData(keyID
)) {
6647 (void) SecError(errSecParam
, error
, CFSTR("keyID was not a valid CFDataRef"));
6650 const void *p
= CFDataGetBytePtr(keyID
);
6651 if (!p
|| CFDataGetLength(keyID
) != CTLOG_KEYID_LENGTH
) {
6652 (void) SecError(errSecParam
, error
, CFSTR("keyID data was not the expected length"));
6655 // call function directly and return if we are built in server mode
6656 do_if_registered(sec_ota_pki_copy_ct_log_for_keyid
, keyID
, error
);
6658 securityd_send_sync_and_do(kSecXPCOpOTAPKICopyCTLogForKeyID
, error
,
6659 ^bool(xpc_object_t message
, CFErrorRef
*blockError
) {
6660 // input: set message parameters here
6661 xpc_dictionary_set_data(message
, kSecXPCData
, p
, CTLOG_KEYID_LENGTH
);
6663 }, ^bool(xpc_object_t response
, CFErrorRef
*blockError
) {
6664 // output: get dictionary from response object
6665 xpc_object_t xpc_dictionary
= NULL
;
6667 xpc_dictionary
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
6669 if (xpc_dictionary
&& (xpc_get_type(xpc_dictionary
) == XPC_TYPE_DICTIONARY
)) {
6670 result
= (CFDictionaryRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_dictionary
);
6672 return SecError(errSecInternal
, blockError
, CFSTR("Unable to match CT log"));
6674 return result
!= NULL
;
6679 CFDictionaryRef
SecCertificateCopyTrustedCTLogs(void)
6681 CFErrorRef localError
= NULL
;
6682 CFDictionaryRef result
= CopyTrustedCTLogs(&localError
);
6683 CFReleaseSafe(localError
);
6687 CFDictionaryRef
SecCertificateCopyCTLogForKeyID(CFDataRef keyID
)
6689 CFErrorRef localError
= NULL
;
6690 CFDictionaryRef result
= CopyCTLogForKeyID(keyID
, &localError
);
6691 CFReleaseSafe(localError
);
6695 SEC_CONST_DECL (kSecSignatureDigestAlgorithmUnknown
, "SignatureDigestUnknown");
6696 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD2
, "SignatureDigestMD2");
6697 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD4
, "SignatureDigestMD4");
6698 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD5
, "SignatureDigestMD5");
6699 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA1
, "SignatureDigestSHA1");
6700 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA224
, "SignatureDigestSHA224");
6701 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA256
, "SignatureDigestSHA256");
6702 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA384
, "SignatureDigestSHA284");
6703 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA512
, "SignatureDigestSHA512");
6705 SecSignatureHashAlgorithm
SecCertificateGetSignatureHashAlgorithm(SecCertificateRef certificate
)
6707 SecSignatureHashAlgorithm result
= kSecSignatureHashAlgorithmUnknown
;
6708 DERAlgorithmId
*algId
= (certificate
) ? &certificate
->_tbsSigAlg
: NULL
;
6709 const DERItem
*algOid
= (algId
) ? &algId
->oid
: NULL
;
6711 if (!algOid
->data
|| !algOid
->length
) {
6714 /* classify the signature algorithm OID into one of our known types */
6715 if (DEROidCompare(algOid
, &oidSha512Ecdsa
) ||
6716 DEROidCompare(algOid
, &oidSha512Rsa
) ||
6717 DEROidCompare(algOid
, &oidSha512
)) {
6718 result
= kSecSignatureHashAlgorithmSHA512
;
6721 if (DEROidCompare(algOid
, &oidSha384Ecdsa
) ||
6722 DEROidCompare(algOid
, &oidSha384Rsa
) ||
6723 DEROidCompare(algOid
, &oidSha384
)) {
6724 result
= kSecSignatureHashAlgorithmSHA384
;
6727 if (DEROidCompare(algOid
, &oidSha256Ecdsa
) ||
6728 DEROidCompare(algOid
, &oidSha256Rsa
) ||
6729 DEROidCompare(algOid
, &oidSha256
)) {
6730 result
= kSecSignatureHashAlgorithmSHA256
;
6733 if (DEROidCompare(algOid
, &oidSha224Ecdsa
) ||
6734 DEROidCompare(algOid
, &oidSha224Rsa
) ||
6735 DEROidCompare(algOid
, &oidSha224
)) {
6736 result
= kSecSignatureHashAlgorithmSHA224
;
6739 if (DEROidCompare(algOid
, &oidSha1Ecdsa
) ||
6740 DEROidCompare(algOid
, &oidSha1Rsa
) ||
6741 DEROidCompare(algOid
, &oidSha1Dsa
) ||
6742 DEROidCompare(algOid
, &oidSha1DsaOIW
) ||
6743 DEROidCompare(algOid
, &oidSha1DsaCommonOIW
) ||
6744 DEROidCompare(algOid
, &oidSha1RsaOIW
) ||
6745 DEROidCompare(algOid
, &oidSha1Fee
) ||
6746 DEROidCompare(algOid
, &oidSha1
)) {
6747 result
= kSecSignatureHashAlgorithmSHA1
;
6750 if (DEROidCompare(algOid
, &oidMd5Rsa
) ||
6751 DEROidCompare(algOid
, &oidMd5Fee
) ||
6752 DEROidCompare(algOid
, &oidMd5
)) {
6753 result
= kSecSignatureHashAlgorithmMD5
;
6756 if (DEROidCompare(algOid
, &oidMd4Rsa
) ||
6757 DEROidCompare(algOid
, &oidMd4
)) {
6758 result
= kSecSignatureHashAlgorithmMD4
;
6761 if (DEROidCompare(algOid
, &oidMd2Rsa
) ||
6762 DEROidCompare(algOid
, &oidMd2
)) {
6763 result
= kSecSignatureHashAlgorithmMD2
;
6772 CFArrayRef
SecCertificateCopyiPhoneDeviceCAChain(void) {
6773 CFMutableArrayRef result
= NULL
;
6774 SecCertificateRef iPhoneDeviceCA
= NULL
, iPhoneCA
= NULL
, appleRoot
= NULL
;
6776 require_quiet(iPhoneDeviceCA
= SecCertificateCreateWithBytes(NULL
, _AppleiPhoneDeviceCA
, sizeof(_AppleiPhoneDeviceCA
)),
6778 require_quiet(iPhoneCA
= SecCertificateCreateWithBytes(NULL
, _AppleiPhoneCA
, sizeof(_AppleiPhoneCA
)),
6780 require_quiet(appleRoot
= SecCertificateCreateWithBytes(NULL
, _AppleRootCA
, sizeof(_AppleRootCA
)),
6783 require_quiet(result
= CFArrayCreateMutable(NULL
, 3, &kCFTypeArrayCallBacks
), errOut
);
6784 CFArrayAppendValue(result
, iPhoneDeviceCA
);
6785 CFArrayAppendValue(result
, iPhoneCA
);
6786 CFArrayAppendValue(result
, appleRoot
);
6789 CFReleaseNull(iPhoneDeviceCA
);
6790 CFReleaseNull(iPhoneCA
);
6791 CFReleaseNull(appleRoot
);
6795 bool SecCertificateGetDeveloperIDDate(SecCertificateRef certificate
, CFAbsoluteTime
*time
, CFErrorRef
*error
) {
6796 if (!certificate
|| !time
) {
6797 return SecError(errSecParam
, error
, CFSTR("DeveloperID Date parsing: missing required input"));
6799 DERItem
*extensionValue
= SecCertificateGetExtensionValue(certificate
, CFSTR("1.2.840.113635.100.6.1.33"));
6800 if (!extensionValue
) {
6801 return SecError(errSecMissingRequiredExtension
, error
, CFSTR("DeveloperID Date parsing: extension not found"));
6803 DERDecodedInfo decodedValue
;
6804 if (DERDecodeItem(extensionValue
, &decodedValue
) != DR_Success
) {
6805 return SecError(errSecDecode
, error
, CFSTR("DeveloperID Date parsing: extension value failed to decode"));
6807 /* The extension value is a DERGeneralizedTime encoded in a UTF8String */
6808 CFErrorRef localError
= NULL
;
6809 if (decodedValue
.tag
== ASN1_UTF8_STRING
) {
6810 *time
= SecAbsoluteTimeFromDateContentWithError(ASN1_GENERALIZED_TIME
, decodedValue
.content
.data
, decodedValue
.content
.length
, &localError
);
6812 return SecError(errSecDecode
, error
, CFSTR("DeveloperID Date parsing: extension value wrong tag"));
6814 return CFErrorPropagate(localError
, error
);
6817 CFIndex
SecCertificateGetUnparseableKnownExtension(SecCertificateRef certificate
) {
6821 return certificate
->_unparseableKnownExtensionIndex
;