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>
74 #include "AppleExternalRootCertificates.h"
75 #include <Security/SecInternalReleasePriv.h>
77 #pragma clang diagnostic ignored "-Wformat=2"
79 /* The minimum key sizes necessary to not be considered "weak" */
80 #define MIN_RSA_KEY_SIZE 128 // 1024-bit
81 #define MIN_EC_KEY_SIZE 20 // 160-bit
83 /* The minimum key sizes necessary to be considered "strong" */
84 #define MIN_STRONG_RSA_KEY_SIZE 256 // 2048-bit
85 #define MIN_STRONG_EC_KEY_SIZE 28 // 224-bit
87 #define IPv4ADDRLEN 4 // 4 octets
88 #define IPv6ADDRLEN 16 // 16 octets
90 #define MAX_EXTENSIONS 10000
91 #define MAX_ATTRIBUTE_TYPE_AND_VALUES 1024
92 #define MAX_CRL_DPS 1024
93 #define MAX_CERTIFICATE_POLICIES 8192
94 #define MAX_POLICY_MAPPINGS 8192
98 typedef struct SecCertificateExtension
{
102 } SecCertificateExtension
;
105 kSecSelfSignedUnknown
= 0,
110 struct __SecCertificate
{
113 DERItem _der
; /* Entire certificate in DER form. */
114 DERItem _tbs
; /* To Be Signed cert DER bytes. */
115 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
116 DERItem _signature
; /* The content of the sig bit string. */
119 DERItem _serialNum
; /* Integer. */
120 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
121 DERItem _issuer
; /* Sequence of RDN. */
122 CFAbsoluteTime _notBefore
;
123 CFAbsoluteTime _notAfter
;
124 DERItem _subject
; /* Sequence of RDN. */
125 DERItem _subjectPublicKeyInfo
; /* SPKI (without tag/length) */
126 DERAlgorithmId _algId
; /* oid and params of _pubKeyDER. */
127 DERItem _pubKeyDER
; /* contents of bit string */
128 DERItem _issuerUniqueID
; /* bit string, optional */
129 DERItem _subjectUniqueID
; /* bit string, optional */
131 bool _foundUnknownCriticalExtension
;
133 /* Well known certificate extensions. */
134 SecCEBasicConstraints _basicConstraints
;
135 SecCEPolicyConstraints _policyConstraints
;
136 SecCEPolicyMappings _policyMappings
;
137 SecCECertificatePolicies _certificatePolicies
;
138 SecCEInhibitAnyPolicy _inhibitAnyPolicySkipCerts
;
140 /* If KeyUsage extension is not present this is 0, otherwise it's
141 the value of the extension. */
142 SecKeyUsage _keyUsage
;
144 /* OCTETS of SubjectKeyIdentifier extensions KeyIdentifier.
145 Length = 0 if not present. */
146 DERItem _subjectKeyIdentifier
;
148 /* OCTETS of AuthorityKeyIdentifier extensions KeyIdentifier.
149 Length = 0 if not present. */
150 DERItem _authorityKeyIdentifier
;
151 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
152 _authorityKeyIdentifierSerialNumber have non zero length if present.
153 Both are either present or absent together. */
154 DERItem _authorityKeyIdentifierIssuer
;
155 DERItem _authorityKeyIdentifierSerialNumber
;
157 /* Subject alt name extension, if present. Not malloced, it's just a
158 pointer to an element in the _extensions array. */
159 const SecCertificateExtension
*_subjectAltName
;
161 /* Parsed extension values. */
163 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
164 CFMutableArrayRef _crlDistributionPoints
;
166 /* Array of CFURLRefs containing the URI values of accessLocations of each
167 id-ad-ocsp AccessDescription in the Authority Information Access
169 CFMutableArrayRef _ocspResponders
;
171 /* Array of CFURLRefs containing the URI values of accessLocations of each
172 id-ad-caIssuers AccessDescription in the Authority Information Access
174 CFMutableArrayRef _caIssuers
;
176 /* Array of CFDataRefs containing the generalNames for permittedSubtrees
178 CFArrayRef _permittedSubtrees
;
180 /* Array of CFDataRefs containing the generalNames for excludedSubtrees
182 CFArrayRef _excludedSubtrees
;
184 CFMutableArrayRef _embeddedSCTs
;
186 /* All other (non known) extensions. The _extensions array is malloced. */
187 CFIndex _extensionCount
;
188 SecCertificateExtension
*_extensions
;
189 CFIndex _unparseableKnownExtensionIndex
;
191 /* Optional cached fields. */
194 CFArrayRef _properties
;
195 CFDataRef _serialNumber
;
196 CFDataRef _normalizedIssuer
;
197 CFDataRef _normalizedSubject
;
198 CFDataRef _authorityKeyID
;
199 CFDataRef _subjectKeyID
;
201 CFDataRef _sha1Digest
;
202 CFTypeRef _keychain_item
;
203 uint8_t _isSelfSigned
;
207 #define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v);
209 SEC_CONST_DECL (kSecCertificateProductionEscrowKey
, "ProductionEscrowKey");
210 SEC_CONST_DECL (kSecCertificateProductionPCSEscrowKey
, "ProductionPCSEscrowKey");
211 SEC_CONST_DECL (kSecCertificateEscrowFileName
, "AppleESCertificates");
213 /* Public Constants for property list keys. */
214 SEC_CONST_DECL (kSecPropertyKeyType
, "type");
215 SEC_CONST_DECL (kSecPropertyKeyLabel
, "label");
216 SEC_CONST_DECL (kSecPropertyKeyLocalizedLabel
, "localized label");
217 SEC_CONST_DECL (kSecPropertyKeyValue
, "value");
219 /* Public Constants for property list values. */
220 SEC_CONST_DECL (kSecPropertyTypeWarning
, "warning");
221 SEC_CONST_DECL (kSecPropertyTypeError
, "error");
222 SEC_CONST_DECL (kSecPropertyTypeSuccess
, "success");
223 SEC_CONST_DECL (kSecPropertyTypeTitle
, "title");
224 SEC_CONST_DECL (kSecPropertyTypeSection
, "section");
225 SEC_CONST_DECL (kSecPropertyTypeData
, "data");
226 SEC_CONST_DECL (kSecPropertyTypeString
, "string");
227 SEC_CONST_DECL (kSecPropertyTypeURL
, "url");
228 SEC_CONST_DECL (kSecPropertyTypeDate
, "date");
229 SEC_CONST_DECL (kSecPropertyTypeArray
, "array");
230 SEC_CONST_DECL (kSecPropertyTypeNumber
, "number");
232 /* Extension parsing routine. */
233 typedef bool (*SecCertificateExtensionParser
)(SecCertificateRef certificate
,
234 const SecCertificateExtension
*extn
);
236 /* Mapping from extension OIDs (as a DERItem *) to
237 SecCertificateExtensionParser extension parsing routines. */
238 static CFDictionaryRef sExtensionParsers
;
240 /* Forward declarations of static functions. */
241 static CFStringRef
SecCertificateCopyDescription(CFTypeRef cf
);
242 static void SecCertificateDestroy(CFTypeRef cf
);
243 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
244 CFAbsoluteTime
*absTime
) __attribute__((__nonnull__
));
246 /* Static functions. */
247 static CFStringRef
SecCertificateCopyDescription(CFTypeRef cf
) {
248 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
249 CFStringRef subject
= SecCertificateCopySubjectSummary(certificate
);
250 CFStringRef issuer
= SecCertificateCopyIssuerSummary(certificate
);
251 CFStringRef desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
252 CFSTR("<cert(%p) s: %@ i: %@>"), certificate
, subject
, issuer
);
253 CFReleaseSafe(issuer
);
254 CFReleaseSafe(subject
);
258 static void SecCertificateDestroy(CFTypeRef cf
) {
259 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
260 if (certificate
->_certificatePolicies
.policies
) {
261 free(certificate
->_certificatePolicies
.policies
);
262 certificate
->_certificatePolicies
.policies
= NULL
;
264 if (certificate
->_policyMappings
.mappings
) {
265 free(certificate
->_policyMappings
.mappings
);
266 certificate
->_policyMappings
.mappings
= NULL
;
268 CFReleaseNull(certificate
->_crlDistributionPoints
);
269 CFReleaseNull(certificate
->_ocspResponders
);
270 CFReleaseNull(certificate
->_caIssuers
);
271 if (certificate
->_extensions
) {
272 free(certificate
->_extensions
);
273 certificate
->_extensions
= NULL
;
275 CFReleaseNull(certificate
->_pubKey
);
276 CFReleaseNull(certificate
->_der_data
);
277 CFReleaseNull(certificate
->_properties
);
278 CFReleaseNull(certificate
->_serialNumber
);
279 CFReleaseNull(certificate
->_normalizedIssuer
);
280 CFReleaseNull(certificate
->_normalizedSubject
);
281 CFReleaseNull(certificate
->_authorityKeyID
);
282 CFReleaseNull(certificate
->_subjectKeyID
);
283 CFReleaseNull(certificate
->_sha1Digest
);
284 CFReleaseNull(certificate
->_keychain_item
);
285 CFReleaseNull(certificate
->_permittedSubtrees
);
286 CFReleaseNull(certificate
->_excludedSubtrees
);
289 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
290 SecCertificateRef cert1
= (SecCertificateRef
)cf1
;
291 SecCertificateRef cert2
= (SecCertificateRef
)cf2
;
294 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
296 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
299 /* Hash of the certificate is der length + signature length + last 4 bytes
301 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
302 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
303 size_t der_length
= certificate
->_der
.length
;
304 size_t sig_length
= certificate
->_signature
.length
;
305 size_t ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
306 CFHashCode hashCode
= 0;
307 for (; ix
< sig_length
; ++ix
)
308 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
310 return (hashCode
+ der_length
+ sig_length
);
313 /************************************************************************/
314 /************************* General Name Parsing *************************/
315 /************************************************************************/
317 GeneralName ::= CHOICE {
318 otherName [0] OtherName,
319 rfc822Name [1] IA5String,
320 dNSName [2] IA5String,
321 x400Address [3] ORAddress,
322 directoryName [4] Name,
323 ediPartyName [5] EDIPartyName,
324 uniformResourceIdentifier [6] IA5String,
325 iPAddress [7] OCTET STRING,
326 registeredID [8] OBJECT IDENTIFIER}
328 OtherName ::= SEQUENCE {
329 type-id OBJECT IDENTIFIER,
330 value [0] EXPLICIT ANY DEFINED BY type-id }
332 EDIPartyName ::= SEQUENCE {
333 nameAssigner [0] DirectoryString OPTIONAL,
334 partyName [1] DirectoryString }
336 OSStatus
SecCertificateParseGeneralNameContentProperty(DERTag tag
,
337 const DERItem
*generalNameContent
,
338 void *context
, parseGeneralNameCallback callback
) {
340 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
341 return callback(context
, GNT_OtherName
, generalNameContent
);
342 case ASN1_CONTEXT_SPECIFIC
| 1:
343 return callback(context
, GNT_RFC822Name
, generalNameContent
);
344 case ASN1_CONTEXT_SPECIFIC
| 2:
345 return callback(context
, GNT_DNSName
, generalNameContent
);
346 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
347 return callback(context
, GNT_X400Address
, generalNameContent
);
348 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
349 return callback(context
, GNT_DirectoryName
, generalNameContent
);
350 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
351 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
352 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
354 /* Technically I don't think this is valid, but there are certs out
355 in the wild that use a constructed IA5String. In particular the
356 VeriSign Time Stamping Authority CA.cer does this. */
357 DERDecodedInfo uriContent
;
358 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
359 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
360 return callback(context
, GNT_URI
, &uriContent
.content
);
362 case ASN1_CONTEXT_SPECIFIC
| 6:
363 return callback(context
, GNT_URI
, generalNameContent
);
364 case ASN1_CONTEXT_SPECIFIC
| 7:
365 return callback(context
, GNT_IPAddress
, generalNameContent
);
366 case ASN1_CONTEXT_SPECIFIC
| 8:
367 return callback(context
, GNT_RegisteredID
, generalNameContent
);
372 return errSecInvalidCertificate
;
375 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
376 void *context
, parseGeneralNameCallback callback
) {
378 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
379 require_noerr_quiet(drtn
, badDER
);
380 DERDecodedInfo generalNameContent
;
381 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
383 OSStatus status
= SecCertificateParseGeneralNameContentProperty(
384 generalNameContent
.tag
, &generalNameContent
.content
, context
,
389 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
390 return errSecSuccess
;
393 return errSecInvalidCertificate
;
396 OSStatus
SecCertificateParseGeneralNames(const DERItem
*generalNames
, void *context
,
397 parseGeneralNameCallback callback
) {
398 DERDecodedInfo generalNamesContent
;
399 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
400 require_noerr_quiet(drtn
, badDER
);
401 // GeneralNames ::= SEQUENCE SIZE (1..MAX)
402 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
403 require_quiet(generalNamesContent
.content
.length
> 0, badDER
); // not defining a max due to use of large number of SANs
404 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
407 return errSecInvalidCertificate
;
410 /************************************************************************/
411 /************************** X.509 Name Parsing **************************/
412 /************************************************************************/
414 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
415 const DERItem
*value
, CFIndex rdnIX
, bool localized
);
417 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
418 parseX501NameCallback callback
, bool localized
) {
420 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
421 require_noerr_quiet(drtn
, badDER
);
422 DERDecodedInfo atvContent
;
424 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
425 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
426 DERAttributeTypeAndValue atv
;
427 drtn
= DERParseSequenceContent(&atvContent
.content
,
428 DERNumAttributeTypeAndValueItemSpecs
,
429 DERAttributeTypeAndValueItemSpecs
,
431 require_noerr_quiet(drtn
, badDER
);
432 require_quiet(atv
.type
.length
!= 0, badDER
);
433 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++, localized
);
438 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
440 return errSecSuccess
;
442 return errSecInvalidCertificate
;
445 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
446 parseX501NameCallback callback
, bool localized
) {
448 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
449 require_noerr_quiet(drtn
, badDER
);
450 DERDecodedInfo currDecoded
;
452 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
453 /* RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue */
454 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
455 require_quiet(currDecoded
.content
.length
> 0, badDER
);
456 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
457 callback
, localized
);
462 require_quiet(atv_count
< MAX_ATTRIBUTE_TYPE_AND_VALUES
, badDER
);
464 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
466 return errSecSuccess
;
469 return errSecInvalidCertificate
;
472 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
473 parseX501NameCallback callback
, bool localized
) {
474 DERDecodedInfo x501NameContent
;
475 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
476 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
477 return errSecInvalidCertificate
;
479 return parseX501NameContent(&x501NameContent
.content
, context
,
480 callback
, localized
);
484 /************************************************************************/
485 /********************** Extension Parsing Routines **********************/
486 /************************************************************************/
488 static bool SecCEPSubjectKeyIdentifier(SecCertificateRef certificate
,
489 const SecCertificateExtension
*extn
) {
490 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
491 DERDecodedInfo keyIdentifier
;
492 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
493 require_noerr_quiet(drtn
, badDER
);
494 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
495 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
499 secwarning("Invalid SubjectKeyIdentifier Extension");
503 static bool SecCEPKeyUsage(SecCertificateRef certificate
,
504 const SecCertificateExtension
*extn
) {
505 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
506 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
507 DERDecodedInfo bitStringContent
;
508 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
509 require_noerr_quiet(drtn
, badDER
);
510 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
511 /* check that there's no extra bytes at the end */
512 require_quiet(bitStringContent
.content
.data
+ bitStringContent
.content
.length
== extn
->extnValue
.data
+ extn
->extnValue
.length
, badDER
);
513 DERSize len
= bitStringContent
.content
.length
- 1;
514 require_quiet(len
== 1 || len
== 2, badDER
);
515 DERByte numUnusedBits
= bitStringContent
.content
.data
[0];
516 require_quiet(numUnusedBits
< 8, badDER
);
517 /* Flip the bits in the bit string so the first bit is the lsb. */
518 uint16_t bits
= 8 * len
- numUnusedBits
;
519 uint16_t value
= bitStringContent
.content
.data
[1];
522 value
= (value
<< 8) + bitStringContent
.content
.data
[2];
528 for (ix
= 0; ix
< bits
; ++ix
) {
534 certificate
->_keyUsage
= keyUsage
;
537 certificate
->_keyUsage
= kSecKeyUsageUnspecified
;
538 secwarning("Invalid KeyUsage Extension");
542 static bool SecCEPPrivateKeyUsagePeriod(SecCertificateRef certificate
,
543 const SecCertificateExtension
*extn
) {
544 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
548 static OSStatus
verifySubjectAltGeneralName(void *context
, SecCEGeneralNameType type
,
549 const DERItem
*value
) {
550 // Nothing to do for now
551 return errSecSuccess
;
554 static bool SecCEPSubjectAltName(SecCertificateRef certificate
,
555 const SecCertificateExtension
*extn
) {
556 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
557 // Make sure that the SAN is parse-able
558 require_noerr_quiet(SecCertificateParseGeneralNames(&extn
->extnValue
, NULL
, verifySubjectAltGeneralName
), badDER
);
559 certificate
->_subjectAltName
= extn
;
562 certificate
->_subjectAltName
= NULL
;
563 secwarning("Invalid SubjectAltName Extension");
567 static bool SecCEPIssuerAltName(SecCertificateRef certificate
,
568 const SecCertificateExtension
*extn
) {
569 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
573 static bool SecCEPBasicConstraints(SecCertificateRef certificate
,
574 const SecCertificateExtension
*extn
) {
575 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
576 DERBasicConstraints basicConstraints
;
577 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
578 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
579 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
580 require_noerr_quiet(DERParseBooleanWithDefault(&basicConstraints
.cA
, false,
581 &certificate
->_basicConstraints
.isCA
), badDER
);
582 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
583 require_noerr_quiet(DERParseInteger(
584 &basicConstraints
.pathLenConstraint
,
585 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
586 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
588 certificate
->_basicConstraints
.present
= true;
589 certificate
->_basicConstraints
.critical
= extn
->critical
;
592 certificate
->_basicConstraints
.present
= false;
593 secwarning("Invalid BasicConstraints Extension");
599 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
601 * NameConstraints ::= SEQUENCE {
602 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
603 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
605 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
607 * GeneralSubtree ::= SEQUENCE {
609 * minimum [0] BaseDistance DEFAULT 0,
610 * maximum [1] BaseDistance OPTIONAL }
612 * BaseDistance ::= INTEGER (0..MAX)
614 static DERReturn
parseGeneralSubtrees(DERItem
*derSubtrees
, CFArrayRef
*generalSubtrees
) {
615 CFMutableArrayRef gs
= NULL
;
617 DERReturn drtn
= DERDecodeSeqContentInit(derSubtrees
, &gsSeq
);
618 require_noerr_quiet(drtn
, badDER
);
619 DERDecodedInfo gsContent
;
620 require_quiet(gs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
621 &kCFTypeArrayCallBacks
),
623 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
624 DERGeneralSubtree derGS
;
625 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
626 drtn
= DERParseSequenceContent(&gsContent
.content
,
627 DERNumGeneralSubtreeItemSpecs
,
628 DERGeneralSubtreeItemSpecs
,
629 &derGS
, sizeof(derGS
));
630 require_noerr_quiet(drtn
, badDER
);
633 * Within this profile, the minimum and maximum fields are not used with
634 * any name forms, thus, the minimum MUST be zero, and maximum MUST be
637 * Because minimum DEFAULT 0, absence equivalent to present and 0.
639 if (derGS
.minimum
.length
) {
641 require_noerr_quiet(DERParseInteger(&derGS
.minimum
, &minimum
),
643 require_quiet(minimum
== 0, badDER
);
645 require_quiet(derGS
.maximum
.length
== 0, badDER
);
646 require_quiet(derGS
.generalName
.length
!= 0, badDER
);
648 CFDataRef generalName
= NULL
;
649 require_quiet(generalName
= CFDataCreate(kCFAllocatorDefault
,
650 derGS
.generalName
.data
,
651 derGS
.generalName
.length
),
653 CFArrayAppendValue(gs
, generalName
);
654 CFReleaseNull(generalName
);
656 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
658 // since generalSubtrees is a pointer to an instance variable,
659 // make sure we release the existing array before assignment.
660 CFReleaseSafe(*generalSubtrees
);
661 *generalSubtrees
= gs
;
667 secdebug("cert","failed to parse GeneralSubtrees");
671 static bool SecCEPNameConstraints(SecCertificateRef certificate
,
672 const SecCertificateExtension
*extn
) {
673 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
674 DERNameConstraints nc
;
676 drtn
= DERParseSequence(&extn
->extnValue
,
677 DERNumNameConstraintsItemSpecs
,
678 DERNameConstraintsItemSpecs
,
680 require_noerr_quiet(drtn
, badDER
);
681 if (nc
.permittedSubtrees
.length
) {
682 require_noerr_quiet(parseGeneralSubtrees(&nc
.permittedSubtrees
, &certificate
->_permittedSubtrees
), badDER
);
684 if (nc
.excludedSubtrees
.length
) {
685 require_noerr_quiet(parseGeneralSubtrees(&nc
.excludedSubtrees
, &certificate
->_excludedSubtrees
), badDER
);
690 secwarning("Invalid Name Constraints extension");
694 static OSStatus
appendCRLDPFromGeneralNames(void *context
, SecCEGeneralNameType type
,
695 const DERItem
*value
) {
696 CFMutableArrayRef
*crlDPs
= (CFMutableArrayRef
*)context
;
697 if (type
== GNT_URI
) {
699 url
= CFURLCreateWithBytes(NULL
, value
->data
, value
->length
, kCFStringEncodingASCII
, NULL
);
702 *crlDPs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
704 CFArrayAppendValue(*crlDPs
, url
);
708 return errSecSuccess
;
712 id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= { id-ce 31 }
714 CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
716 DistributionPoint ::= SEQUENCE {
717 distributionPoint [0] DistributionPointName OPTIONAL,
718 reasons [1] ReasonFlags OPTIONAL,
719 cRLIssuer [2] GeneralNames OPTIONAL }
721 DistributionPointName ::= CHOICE {
722 fullName [0] GeneralNames,
723 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
725 static bool SecCEPCrlDistributionPoints(SecCertificateRef certificate
,
726 const SecCertificateExtension
*extn
) {
727 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
728 DERSequence crlDPSeq
;
730 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &crlDPSeq
);
731 require_noerr_quiet(drtn
, badDER
);
732 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
733 require_quiet(crlDPSeq
.nextItem
!= crlDPSeq
.end
, badDER
);
734 DERDecodedInfo dpContent
;
736 while ((drtn
= DERDecodeSeqNext(&crlDPSeq
, &dpContent
)) == DR_Success
) {
737 require_quiet(dpContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
738 DERDistributionPoint dp
;
739 drtn
= DERParseSequenceContent(&dpContent
.content
, DERNumDistributionPointItemSpecs
,
740 DERDistributionPointItemSpecs
, &dp
, sizeof(dp
));
741 require_noerr_quiet(drtn
, badDER
);
742 require_quiet(dp
.distributionPoint
.data
|| dp
.cRLIssuer
.data
, badDER
);
743 if (dp
.distributionPoint
.data
) {
744 DERDecodedInfo dpName
;
745 drtn
= DERDecodeItem(&dp
.distributionPoint
, &dpName
);
746 require_noerr_quiet(drtn
, badDER
);
747 switch (dpName
.tag
) {
748 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
749 drtn
= parseGeneralNamesContent(&dpName
.content
, &certificate
->_crlDistributionPoints
,
750 appendCRLDPFromGeneralNames
);
751 require_noerr_quiet(drtn
, badDER
);
753 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1:
754 /* RelativeDistinguishName. Nothing we can do with that. */
760 if (dp
.cRLIssuer
.data
) {
761 drtn
= parseGeneralNamesContent(&dp
.cRLIssuer
, &certificate
->_crlDistributionPoints
,
762 appendCRLDPFromGeneralNames
);
763 require_noerr_quiet(drtn
, badDER
);
766 require_quiet(crldp_count
< MAX_CRL_DPS
, badDER
);
768 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
771 secwarning("Invalid CRL Distribution Points extension");
776 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
778 PolicyInformation ::= SEQUENCE {
779 policyIdentifier CertPolicyId,
780 policyQualifiers SEQUENCE SIZE (1..MAX) OF
781 PolicyQualifierInfo OPTIONAL }
783 CertPolicyId ::= OBJECT IDENTIFIER
785 PolicyQualifierInfo ::= SEQUENCE {
786 policyQualifierId PolicyQualifierId,
787 qualifier ANY DEFINED BY policyQualifierId }
789 static bool SecCEPCertificatePolicies(SecCertificateRef certificate
,
790 const SecCertificateExtension
*extn
) {
791 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
794 SecCEPolicyInformation
*policies
= NULL
;
795 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
796 require_noerr_quiet(drtn
, badDER
);
797 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
798 DERDecodedInfo piContent
;
799 DERSize policy_count
= 0;
800 while ((policy_count
< MAX_CERTIFICATE_POLICIES
) &&
801 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
802 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
805 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
806 require_quiet(policy_count
> 0, badDER
);
807 require_quiet(policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
) * policy_count
),
809 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
810 require_noerr_quiet(drtn
, badDER
);
811 DERSize policy_ix
= 0;
812 while ((policy_ix
< policy_count
) &&
813 (DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
814 DERPolicyInformation pi
;
815 drtn
= DERParseSequenceContent(&piContent
.content
,
816 DERNumPolicyInformationItemSpecs
,
817 DERPolicyInformationItemSpecs
,
819 require_noerr_quiet(drtn
, badDER
);
820 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
821 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
823 certificate
->_certificatePolicies
.present
= true;
824 certificate
->_certificatePolicies
.critical
= extn
->critical
;
825 certificate
->_certificatePolicies
.numPolicies
= policy_count
;
826 certificate
->_certificatePolicies
.policies
= policies
;
831 certificate
->_certificatePolicies
.present
= false;
832 secwarning("Invalid CertificatePolicies Extension");
837 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
839 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
840 issuerDomainPolicy CertPolicyId,
841 subjectDomainPolicy CertPolicyId }
843 static bool SecCEPPolicyMappings(SecCertificateRef certificate
,
844 const SecCertificateExtension
*extn
) {
845 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
848 SecCEPolicyMapping
*mappings
= NULL
;
849 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
850 require_noerr_quiet(drtn
, badDER
);
851 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
852 DERDecodedInfo pmContent
;
853 DERSize mapping_count
= 0;
854 while ((mapping_count
< MAX_POLICY_MAPPINGS
) &&
855 (drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
856 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
859 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
860 require_quiet(mapping_count
> 0, badDER
); // PolicyMappings ::= SEQUENCE SIZE (1..MAX)
861 require_quiet(mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
) * mapping_count
),
863 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
864 require_noerr_quiet(drtn
, badDER
);
865 DERSize mapping_ix
= 0;
866 while ((mapping_ix
< mapping_count
) &&
867 (DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
868 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
870 drtn
= DERParseSequenceContent(&pmContent
.content
,
871 DERNumPolicyMappingItemSpecs
,
872 DERPolicyMappingItemSpecs
,
874 require_noerr_quiet(drtn
, badDER
);
875 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
876 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
878 certificate
->_policyMappings
.present
= true;
879 certificate
->_policyMappings
.critical
= extn
->critical
;
880 certificate
->_policyMappings
.numMappings
= mapping_count
;
881 certificate
->_policyMappings
.mappings
= mappings
;
887 certificate
->_policyMappings
.present
= false;
888 secwarning("Invalid PolicyMappings Extension");
893 AuthorityKeyIdentifier ::= SEQUENCE {
894 keyIdentifier [0] KeyIdentifier OPTIONAL,
895 authorityCertIssuer [1] GeneralNames OPTIONAL,
896 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
897 -- authorityCertIssuer and authorityCertSerialNumber MUST both
898 -- be present or both be absent
900 KeyIdentifier ::= OCTET STRING
902 static bool SecCEPAuthorityKeyIdentifier(SecCertificateRef certificate
,
903 const SecCertificateExtension
*extn
) {
904 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
905 DERAuthorityKeyIdentifier akid
;
907 drtn
= DERParseSequence(&extn
->extnValue
,
908 DERNumAuthorityKeyIdentifierItemSpecs
,
909 DERAuthorityKeyIdentifierItemSpecs
,
910 &akid
, sizeof(akid
));
911 require_noerr_quiet(drtn
, badDER
);
912 if (akid
.keyIdentifier
.length
) {
913 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
915 if (akid
.authorityCertIssuer
.length
||
916 akid
.authorityCertSerialNumber
.length
) {
917 require_quiet(akid
.authorityCertIssuer
.length
&&
918 akid
.authorityCertSerialNumber
.length
, badDER
);
919 /* Perhaps put in a subsection called Authority Certificate Issuer. */
920 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
921 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
926 secwarning("Invalid AuthorityKeyIdentifier Extension");
930 static bool SecCEPPolicyConstraints(SecCertificateRef certificate
,
931 const SecCertificateExtension
*extn
) {
932 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
933 DERPolicyConstraints pc
;
935 drtn
= DERParseSequence(&extn
->extnValue
,
936 DERNumPolicyConstraintsItemSpecs
,
937 DERPolicyConstraintsItemSpecs
,
939 require_noerr_quiet(drtn
, badDER
);
940 if (pc
.requireExplicitPolicy
.length
) {
941 require_noerr_quiet(DERParseInteger(
942 &pc
.requireExplicitPolicy
,
943 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
944 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
946 if (pc
.inhibitPolicyMapping
.length
) {
947 require_noerr_quiet(DERParseInteger(
948 &pc
.inhibitPolicyMapping
,
949 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
950 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
953 certificate
->_policyConstraints
.present
= true;
954 certificate
->_policyConstraints
.critical
= extn
->critical
;
958 certificate
->_policyConstraints
.present
= false;
959 secwarning("Invalid PolicyConstraints Extension");
963 static bool SecCEPExtendedKeyUsage(SecCertificateRef certificate
,
964 const SecCertificateExtension
*extn
) {
965 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
968 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &ekuTag
, &ekuSeq
);
969 require_quiet((drtn
== DR_Success
) && (ekuTag
== ASN1_CONSTR_SEQUENCE
), badDER
);
970 require_quiet(ekuSeq
.nextItem
!= ekuSeq
.end
, badDER
); // ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
971 DERDecodedInfo ekuContent
;
973 while ((drtn
= DERDecodeSeqNext(&ekuSeq
, &ekuContent
)) == DR_Success
) {
974 require_quiet(ekuContent
.tag
== ASN1_OBJECT_ID
, badDER
);
976 require_quiet(eku_count
< MAX_EKUS
, badDER
);
978 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
981 secwarning("Invalidate EKU Extension");
986 InhibitAnyPolicy ::= SkipCerts
988 SkipCerts ::= INTEGER (0..MAX)
990 static bool SecCEPInhibitAnyPolicy(SecCertificateRef certificate
,
991 const SecCertificateExtension
*extn
) {
992 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
993 DERDecodedInfo iapContent
;
994 require_noerr_quiet(DERDecodeItem(&extn
->extnValue
, &iapContent
), badDER
);
995 require_quiet(iapContent
.tag
== ASN1_INTEGER
, badDER
);
996 require_noerr_quiet(DERParseInteger(
998 &certificate
->_inhibitAnyPolicySkipCerts
.skipCerts
), badDER
);
1000 certificate
->_inhibitAnyPolicySkipCerts
.present
= true;
1001 certificate
->_inhibitAnyPolicySkipCerts
.critical
= extn
->critical
;
1004 certificate
->_inhibitAnyPolicySkipCerts
.present
= false;
1005 secwarning("Invalid InhibitAnyPolicy Extension");
1010 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
1012 AuthorityInfoAccessSyntax ::=
1013 SEQUENCE SIZE (1..MAX) OF AccessDescription
1015 AccessDescription ::= SEQUENCE {
1016 accessMethod OBJECT IDENTIFIER,
1017 accessLocation GeneralName }
1019 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
1021 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
1023 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
1025 static bool SecCEPAuthorityInfoAccess(SecCertificateRef certificate
,
1026 const SecCertificateExtension
*extn
) {
1027 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1030 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
1031 require_noerr_quiet(drtn
, badDER
);
1032 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1033 require_quiet(adSeq
.nextItem
!= adSeq
.end
, badDER
);
1034 DERDecodedInfo adContent
;
1036 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
1037 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1038 DERAccessDescription ad
;
1039 drtn
= DERParseSequenceContent(&adContent
.content
,
1040 DERNumAccessDescriptionItemSpecs
,
1041 DERAccessDescriptionItemSpecs
,
1043 require_noerr_quiet(drtn
, badDER
);
1045 require_quiet(aia_count
< MAX_AIAS
, badDER
);
1046 CFMutableArrayRef
*urls
;
1047 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
1048 urls
= &certificate
->_ocspResponders
;
1049 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
1050 urls
= &certificate
->_caIssuers
;
1054 DERDecodedInfo generalNameContent
;
1055 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
1056 require_noerr_quiet(drtn
, badDER
);
1057 switch (generalNameContent
.tag
) {
1059 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
1060 /* Technically I don't think this is valid, but there are certs out
1061 in the wild that use a constructed IA5String. In particular the
1062 VeriSign Time Stamping Authority CA.cer does this. */
1064 case ASN1_CONTEXT_SPECIFIC
| 6:
1066 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1067 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
1068 kCFStringEncodingASCII
, NULL
);
1071 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1072 CFArrayAppendValue(*urls
, url
);
1078 secdebug("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02llx v: %.*s",
1079 generalNameContent
.tag
, (int) generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1084 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1087 secwarning("Invalid Authority Information Access extension");
1091 /* Apple Worldwide Developer Relations Certificate Authority subject name.
1092 * This is a DER sequence with the leading tag and length bytes removed,
1093 * to match what tbsCert.issuer contains.
1095 static const unsigned char Apple_WWDR_CA_Subject_Name
[]={
1096 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,
1097 0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0A,0x41,0x70,0x70,0x6C,0x65,
1098 0x20,0x49,0x6E,0x63,0x2E,0x31,0x2C,0x30,0x2A,0x06,0x03,0x55,0x04,0x0B,0x0C,0x23,
1099 0x41,0x70,0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,
1100 0x44,0x65,0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,
1101 0x6F,0x6E,0x73,0x31,0x44,0x30,0x42,0x06,0x03,0x55,0x04,0x03,0x0C,0x3B,0x41,0x70,
1102 0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,0x44,0x65,
1103 0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,0x6F,0x6E,
1104 0x73,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,
1105 0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79
1108 static void checkForMissingRevocationInfo(SecCertificateRef certificate
) {
1110 certificate
->_crlDistributionPoints
||
1111 certificate
->_ocspResponders
) {
1112 /* We already have an OCSP or CRL URI (or no cert) */
1115 /* Specify an appropriate OCSP responder if we recognize the issuer. */
1116 CFURLRef url
= NULL
;
1117 if (sizeof(Apple_WWDR_CA_Subject_Name
) == certificate
->_issuer
.length
&&
1118 !memcmp(certificate
->_issuer
.data
, Apple_WWDR_CA_Subject_Name
,
1119 sizeof(Apple_WWDR_CA_Subject_Name
))) {
1120 const char *WWDR_OCSP_URI
= "http://ocsp.apple.com/ocsp-wwdr01";
1121 url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1122 (const UInt8
*)WWDR_OCSP_URI
, strlen(WWDR_OCSP_URI
),
1123 kCFStringEncodingASCII
, NULL
);
1126 CFMutableArrayRef
*urls
= &certificate
->_ocspResponders
;
1127 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1128 CFArrayAppendValue(*urls
, url
);
1133 static bool SecCEPSubjectInfoAccess(SecCertificateRef certificate
,
1134 const SecCertificateExtension
*extn
) {
1135 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1139 static bool SecCEPNetscapeCertType(SecCertificateRef certificate
,
1140 const SecCertificateExtension
*extn
) {
1141 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1145 static bool SecCEPEntrustVersInfo(SecCertificateRef certificate
,
1146 const SecCertificateExtension
*extn
) {
1147 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1151 static bool SecCEPEscrowMarker(SecCertificateRef certificate
,
1152 const SecCertificateExtension
*extn
) {
1153 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1157 static bool SecCEPOCSPNoCheck(SecCertificateRef certificate
,
1158 const SecCertificateExtension
*extn
) {
1159 secdebug("cert", "ocsp-nocheck critical: %s", extn
->critical
? "yes" : "no");
1163 /* Dictionary key callback for comparing to DERItems. */
1164 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1165 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1168 /* Dictionary key callback calculating the hash of a DERItem. */
1169 static CFHashCode
SecDERItemHash(const void *value
) {
1170 const DERItem
*derItem
= (const DERItem
*)value
;
1171 CFHashCode hash
= derItem
->length
;
1172 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1173 for (; ix
< derItem
->length
; ++ix
) {
1174 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1180 /* Dictionary key callbacks using the above 2 functions. */
1181 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1185 NULL
, /* copyDescription */
1186 SecDERItemEqual
, /* equal */
1187 SecDERItemHash
/* hash */
1190 static void SecCertificateInitializeExtensionParsers(void) {
1191 /* Build a dictionary that maps from extension OIDs to callback functions
1192 which can parse the extension of the type given. */
1193 static const void *extnOIDs
[] = {
1194 &oidSubjectKeyIdentifier
,
1196 &oidPrivateKeyUsagePeriod
,
1199 &oidBasicConstraints
,
1200 &oidNameConstraints
,
1201 &oidCrlDistributionPoints
,
1202 &oidCertificatePolicies
,
1204 &oidAuthorityKeyIdentifier
,
1205 &oidPolicyConstraints
,
1206 &oidExtendedKeyUsage
,
1207 &oidInhibitAnyPolicy
,
1208 &oidAuthorityInfoAccess
,
1209 &oidSubjectInfoAccess
,
1210 &oidNetscapeCertType
,
1211 &oidEntrustVersInfo
,
1212 &oidApplePolicyEscrowService
,
1215 static const void *extnParsers
[] = {
1216 SecCEPSubjectKeyIdentifier
,
1218 SecCEPPrivateKeyUsagePeriod
,
1219 SecCEPSubjectAltName
,
1220 SecCEPIssuerAltName
,
1221 SecCEPBasicConstraints
,
1222 SecCEPNameConstraints
,
1223 SecCEPCrlDistributionPoints
,
1224 SecCEPCertificatePolicies
,
1225 SecCEPPolicyMappings
,
1226 SecCEPAuthorityKeyIdentifier
,
1227 SecCEPPolicyConstraints
,
1228 SecCEPExtendedKeyUsage
,
1229 SecCEPInhibitAnyPolicy
,
1230 SecCEPAuthorityInfoAccess
,
1231 SecCEPSubjectInfoAccess
,
1232 SecCEPNetscapeCertType
,
1233 SecCEPEntrustVersInfo
,
1237 sExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1238 extnParsers
, array_size(extnOIDs
),
1239 &SecDERItemKeyCallBacks
, NULL
);
1242 CFGiblisWithFunctions(SecCertificate
, NULL
, NULL
, SecCertificateDestroy
, SecCertificateEqual
, SecCertificateHash
, NULL
, SecCertificateCopyDescription
, NULL
, NULL
, ^{
1243 SecCertificateInitializeExtensionParsers();
1246 static bool isAppleExtensionOID(const DERItem
*extnID
)
1248 static const uint8_t appleExtensionArc
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x06 };
1249 static const uint8_t appleComponentExtensionArc
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0b };
1250 static const uint8_t appleSigningExtensionArc
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0c };
1251 static const uint8_t appleEncryptionExtensionArc
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0d };
1252 if (!extnID
|| !extnID
->data
|| (extnID
->length
<= sizeof(appleExtensionArc
))) {
1255 return (!memcmp(extnID
->data
, appleExtensionArc
, sizeof(appleExtensionArc
)) ||
1256 !memcmp(extnID
->data
, appleComponentExtensionArc
, sizeof(appleComponentExtensionArc
)) ||
1257 !memcmp(extnID
->data
, appleSigningExtensionArc
, sizeof(appleSigningExtensionArc
)) ||
1258 !memcmp(extnID
->data
, appleEncryptionExtensionArc
, sizeof(appleEncryptionExtensionArc
)));
1261 static bool isCCCExtensionOID(const DERItem
*extnID
)
1263 static const uint8_t cccVehicleCA
[] = { 0x2B,0x06,0x01,0x04,0x01,0x82,0xC4,0x69,0x05,0x09 };
1264 static const uint8_t cccIntermediateCA
[] = { 0x2B,0x06,0x01,0x04,0x01,0x82,0xC4,0x69,0x05,0x08 };
1265 static const uint8_t cccVehicle
[] = { 0x2B,0x06,0x01,0x04,0x01,0x82,0xC4,0x69,0x05,0x01 };
1266 if (!extnID
|| !extnID
->data
|| (extnID
->length
!= sizeof(cccVehicleCA
))) {
1269 return (!memcmp(extnID
->data
, cccVehicleCA
, sizeof(cccVehicleCA
)) ||
1270 !memcmp(extnID
->data
, cccIntermediateCA
, sizeof(cccIntermediateCA
)) ||
1271 !memcmp(extnID
->data
, cccVehicle
, sizeof(cccVehicle
)));
1274 /* Given the contents of an X.501 Name return the contents of a normalized
1276 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1277 const DERItem
*x501name
) {
1278 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1279 CFIndex length
= x501name
->length
;
1280 CFDataSetLength(result
, length
);
1281 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1284 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1286 require_noerr_quiet(drtn
, badDER
);
1289 /* Always points to last rdn tag. */
1290 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1291 /* Offset relative to base of current rdn set tag. */
1292 CFIndex rdnTagLocation
= 0;
1293 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1294 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1295 /* We don't allow empty RDNs. */
1296 require_quiet(rdn
.content
.length
!= 0, badDER
);
1297 /* Length of the tag and length of the current rdn. */
1298 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1299 CFIndex rdnContentLength
= rdn
.content
.length
;
1300 /* Copy the tag and length of the RDN. */
1301 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1304 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1305 require_quiet(drtn
== DR_Success
, badDER
);
1308 /* Always points to tag of current atv sequence. */
1309 const DERByte
*atvTag
= atvSeq
.nextItem
;
1310 /* Offset relative to base of current atv sequence tag. */
1311 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1312 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1313 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1314 /* Length of the tag and length of the current atv. */
1315 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1316 CFIndex atvContentLength
= atv
.content
.length
;
1317 /* Copy the tag and length of the atv and the atv itself. */
1318 memcpy(base
+ atvTagLocation
, atvTag
,
1319 atvTLLength
+ atv
.content
.length
);
1321 /* Now decode the atv sequence. */
1322 DERAttributeTypeAndValue atvPair
;
1323 drtn
= DERParseSequenceContent(&atv
.content
,
1324 DERNumAttributeTypeAndValueItemSpecs
,
1325 DERAttributeTypeAndValueItemSpecs
,
1326 &atvPair
, sizeof(atvPair
));
1327 require_noerr_quiet(drtn
, badDER
);
1328 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1329 DERDecodedInfo value
;
1330 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1331 require_noerr_quiet(drtn
, badDER
);
1333 /* (c) attribute values in PrintableString are not case sensitive
1334 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1336 (d) attribute values in PrintableString are compared after
1337 removing leading and trailing white space and converting internal
1338 substrings of one or more consecutive white space characters to a
1340 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1341 /* Offset relative to base of current value tag. */
1342 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1343 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1344 CFIndex valueContentLength
= value
.content
.length
;
1346 /* Now copy all the bytes, but convert to upper case while
1347 doing so and convert multiple whitespace chars into a
1349 bool lastWasBlank
= false;
1350 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1351 CFIndex valueCurrentLocation
= valueLocation
;
1353 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1354 UInt8 ch
= value
.content
.data
[ix
];
1359 /* Don't insert a space for first character
1361 if (valueCurrentLocation
> valueLocation
) {
1362 base
[valueCurrentLocation
++] = ' ';
1364 lastWasBlank
= true;
1367 lastWasBlank
= false;
1368 if ('a' <= ch
&& ch
<= 'z') {
1369 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1371 base
[valueCurrentLocation
++] = ch
;
1375 /* Finally if lastWasBlank remove the trailing space. */
1376 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1377 valueCurrentLocation
--;
1379 /* Adjust content length to normalized length. */
1380 valueContentLength
= valueCurrentLocation
- valueLocation
;
1382 /* Number of bytes by which the length should be shorted. */
1383 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1384 if (lengthDiff
== 0) {
1385 /* Easy case no need to adjust lengths. */
1387 /* Hard work we need to go back and fix up length fields
1389 1) The value itself.
1390 2) The ATV Sequence containing type/value
1391 3) The RDN Set containing one or more atv pairs.
1395 /* Step 1 fix up length of value. */
1396 /* Length of value tag and length minus the tag. */
1397 DERSize newValueTLLength
= valueTLLength
- 1;
1398 drtn
= DEREncodeLength(valueContentLength
,
1399 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1400 require(drtn
== DR_Success
, badDER
);
1401 /* Add the length of the tag back in. */
1403 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1405 /* The size of the length field changed, let's slide
1406 the value back by valueLLDiff bytes. */
1407 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1408 base
+ valueTagLocation
+ valueTLLength
,
1409 valueContentLength
);
1410 /* The length diff for the enclosing object. */
1411 lengthDiff
+= valueLLDiff
;
1414 /* Step 2 fix up length of the enclosing ATV Sequence. */
1415 atvContentLength
-= lengthDiff
;
1416 DERSize newATVTLLength
= atvTLLength
- 1;
1417 drtn
= DEREncodeLength(atvContentLength
,
1418 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1419 require(drtn
== DR_Success
, badDER
);
1420 /* Add the length of the tag back in. */
1422 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1424 /* The size of the length field changed, let's slide
1425 the value back by valueLLDiff bytes. */
1426 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1427 base
+ atvTagLocation
+ atvTLLength
,
1429 /* The length diff for the enclosing object. */
1430 lengthDiff
+= atvLLDiff
;
1431 atvTLLength
= newATVTLLength
;
1434 /* Step 3 fix up length of enclosing RDN Set. */
1435 rdnContentLength
-= lengthDiff
;
1436 DERSize newRDNTLLength
= rdnTLLength
- 1;
1437 drtn
= DEREncodeLength(rdnContentLength
,
1438 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1439 require_quiet(drtn
== DR_Success
, badDER
);
1440 /* Add the length of the tag back in. */
1442 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1444 /* The size of the length field changed, let's slide
1445 the value back by valueLLDiff bytes. */
1446 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1447 base
+ rdnTagLocation
+ rdnTLLength
,
1449 /* The length diff for the enclosing object. */
1450 lengthDiff
+= rdnLLDiff
;
1451 rdnTLLength
= newRDNTLLength
;
1453 /* Adjust the locations that might have changed due to
1455 atvTagLocation
-= rdnLLDiff
;
1457 (void) lengthDiff
; // No next object, silence analyzer
1460 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1461 atvTag
= atvSeq
.nextItem
;
1463 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1464 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1465 rdnTag
= rdnSeq
.nextItem
;
1467 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1468 /* Truncate the result to the proper length. */
1469 CFDataSetLength(result
, rdnTagLocation
);
1478 static CFDataRef
SecDERItemCopySequence(DERItem
*content
) {
1479 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
1480 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
1481 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
1483 CFDataSetLength(sequence
, sequence_length
);
1484 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
1485 *sequence_ptr
++ = ONE_BYTE_ASN1_CONSTR_SEQUENCE
;
1486 require_noerr_quiet(DEREncodeLength(content
->length
,
1487 sequence_ptr
, &seq_len_length
), out
);
1488 sequence_ptr
+= seq_len_length
;
1489 memcpy(sequence_ptr
, content
->data
, content
->length
);
1492 CFReleaseSafe(sequence
);
1496 static CFDataRef
SecCopySequenceFromContent(CFDataRef content
) {
1498 tmpItem
.data
= (void *)CFDataGetBytePtr(content
);
1499 tmpItem
.length
= CFDataGetLength(content
);
1501 return SecDERItemCopySequence(&tmpItem
);
1504 CFDataRef
SecDistinguishedNameCopyNormalizedContent(CFDataRef distinguished_name
)
1506 const DERItem name
= { (unsigned char *)CFDataGetBytePtr(distinguished_name
), CFDataGetLength(distinguished_name
) };
1507 DERDecodedInfo content
;
1508 /* Decode top level sequence into DERItem */
1509 if (!DERDecodeItem(&name
, &content
) && (content
.tag
== ASN1_CONSTR_SEQUENCE
))
1510 return createNormalizedX501Name(kCFAllocatorDefault
, &content
.content
);
1514 CFDataRef
SecDistinguishedNameCopyNormalizedSequence(CFDataRef distinguished_name
)
1516 if (!distinguished_name
) { return NULL
; }
1517 CFDataRef normalizedContent
= SecDistinguishedNameCopyNormalizedContent(distinguished_name
);
1518 if (!normalizedContent
) { return NULL
; }
1519 CFDataRef result
= SecCopySequenceFromContent(normalizedContent
);
1520 CFReleaseNull(normalizedContent
);
1524 /* AUDIT[securityd]:
1525 certificate->_der is a caller provided data of any length (might be 0).
1527 Top level certificate decode.
1529 static bool SecCertificateParse(SecCertificateRef certificate
)
1534 require_quiet(certificate
, badCert
);
1535 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1537 /* top level decode */
1538 DERSignedCertCrl signedCert
;
1539 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1540 DERSignedCertCrlItemSpecs
, &signedCert
,
1541 sizeof(signedCert
));
1542 require_noerr_quiet(drtn
, badCert
);
1543 /* Store tbs since we need to digest it for verification later on. */
1544 certificate
->_tbs
= signedCert
.tbs
;
1546 /* decode the TBSCert - it was saved in full DER form */
1548 drtn
= DERParseSequence(&signedCert
.tbs
,
1549 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1550 &tbsCert
, sizeof(tbsCert
));
1551 require_noerr_quiet(drtn
, badCert
);
1553 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1554 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1555 of the params field. */
1556 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1557 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1558 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1559 require_noerr_quiet(drtn
, badCert
);
1561 /* The contents of signedCert.sig is a bit string whose contents
1562 are the signature itself. */
1563 DERByte numUnusedBits
;
1564 drtn
= DERParseBitString(&signedCert
.sig
,
1565 &certificate
->_signature
, &numUnusedBits
);
1566 require_noerr_quiet(drtn
, badCert
);
1568 /* Now decode the tbsCert. */
1570 /* First we turn the optional version into an int. */
1571 if (tbsCert
.version
.length
) {
1572 DERDecodedInfo decoded
;
1573 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1574 require_noerr_quiet(drtn
, badCert
);
1575 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1576 require_quiet(decoded
.content
.length
== 1, badCert
);
1577 certificate
->_version
= decoded
.content
.data
[0];
1578 if (certificate
->_version
> 2) {
1579 secwarning("Invalid certificate version (%d), must be 0..2",
1580 certificate
->_version
);
1582 require_quiet(certificate
->_version
> 0, badCert
);
1583 require_quiet(certificate
->_version
< 3, badCert
);
1585 certificate
->_version
= 0;
1588 /* The serial number is in the tbsCert.serialNum - it was saved in
1589 INTEGER form without the tag and length. */
1590 certificate
->_serialNum
= tbsCert
.serialNum
;
1592 /* Note: RFC5280 4.1.2.2 limits serial number values to 20 octets.
1593 For now, we warn about larger values, but will still create the
1594 certificate with values up to 36 octets to avoid breaking some
1595 nonconforming certs with slightly longer serial numbers.
1596 We also explicitly allow serial numbers of 21 octets where the
1597 leading byte is 0x00 which is used to make a negative 20 octet
1600 if (tbsCert
.serialNum
.length
< 1 || tbsCert
.serialNum
.length
> 21 ||
1601 (tbsCert
.serialNum
.length
== 21 && tbsCert
.serialNum
.data
[0] != 0x00)) {
1602 secwarning("Invalid serial number length (%ld), must be 1..20",
1603 tbsCert
.serialNum
.length
);
1605 require_quiet(tbsCert
.serialNum
.data
!= NULL
&&
1606 tbsCert
.serialNum
.length
>= 1 &&
1607 tbsCert
.serialNum
.length
<= 37, badCert
);
1608 certificate
->_serialNumber
= CFDataCreate(allocator
,
1609 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1611 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1612 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1613 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1614 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1615 require_noerr_quiet(drtn
, badCert
);
1617 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1618 and length fields. */
1619 certificate
->_issuer
= tbsCert
.issuer
;
1620 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1623 /* sequence we're given: decode the tbsCerts Validity sequence. */
1624 DERValidity validity
;
1625 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1626 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1627 &validity
, sizeof(validity
));
1628 require_noerr_quiet(drtn
, badCert
);
1629 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1630 &certificate
->_notBefore
), badCert
);
1631 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1632 &certificate
->_notAfter
), badCert
);
1634 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1635 and length fields. */
1636 certificate
->_subject
= tbsCert
.subject
;
1637 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1640 /* Keep the SPKI around for CT */
1641 certificate
->_subjectPublicKeyInfo
= tbsCert
.subjectPubKey
;
1643 /* sequence we're given: encoded DERSubjPubKeyInfo */
1644 DERSubjPubKeyInfo pubKeyInfo
;
1645 drtn
= DERParseSequenceContent(&tbsCert
.subjectPubKey
,
1646 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1647 &pubKeyInfo
, sizeof(pubKeyInfo
));
1648 require_noerr_quiet(drtn
, badCert
);
1650 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1651 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1652 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1653 &certificate
->_algId
, sizeof(certificate
->_algId
));
1654 require_noerr_quiet(drtn
, badCert
);
1656 /* Now we can figure out the key's algorithm id and params based on
1657 certificate->_algId.oid. */
1659 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1660 are a PKCS1 format RSA key. */
1661 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1662 &certificate
->_pubKeyDER
, &numUnusedBits
);
1663 require_noerr_quiet(drtn
, badCert
);
1665 /* The contents of tbsCert.issuerID is a bit string. */
1666 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1668 /* The contents of tbsCert.subjectID is a bit string. */
1669 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1672 certificate
->_unparseableKnownExtensionIndex
= kCFNotFound
;
1673 if (tbsCert
.extensions
.length
) {
1674 CFIndex extensionCount
= 0;
1677 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1678 require_noerr_quiet(drtn
, badCert
);
1679 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1680 DERDecodedInfo currDecoded
;
1681 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1683 /* ! = MUST recognize ? = SHOULD recognize
1686 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1687 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1688 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1689 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1690 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1691 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1692 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1693 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1695 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1696 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1697 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1698 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1699 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1700 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1701 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1702 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1704 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1705 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1710 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1711 require_quiet(extensionCount
> 0, badCert
); // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
1713 /* Put some upper limit on the number of extensions allowed. */
1714 require_quiet(extensionCount
< MAX_EXTENSIONS
, badCert
);
1715 certificate
->_extensionCount
= extensionCount
;
1716 certificate
->_extensions
= malloc(sizeof(SecCertificateExtension
) * (extensionCount
> 0 ? extensionCount
: 1));
1717 require_quiet(certificate
->_extensions
, badCert
);
1720 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1721 require_noerr_quiet(drtn
, badCert
);
1722 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1723 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1724 require_quiet(drtn
== DR_Success
|| (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1725 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1727 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1728 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1729 &extn
, sizeof(extn
));
1730 require_noerr_quiet(drtn
, badCert
);
1731 /* Copy stuff into certificate->extensions[ix]. */
1732 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1733 require_noerr_quiet(drtn
= DERParseBooleanWithDefault(&extn
.critical
, false,
1734 &certificate
->_extensions
[ix
].critical
), badCert
);
1735 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1737 SecCertificateExtensionParser parser
=
1738 (SecCertificateExtensionParser
)CFDictionaryGetValue(sExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1740 /* Invoke the parser. If the extension is critical and the
1741 * parser fails, fail the cert. */
1742 bool parseResult
= parser(certificate
, &certificate
->_extensions
[ix
]);
1744 certificate
->_unparseableKnownExtensionIndex
= ix
;
1746 require_quiet(parseResult
|| !certificate
->_extensions
[ix
].critical
, badCert
);
1747 } else if (certificate
->_extensions
[ix
].critical
) {
1748 if (isAppleExtensionOID(&extn
.extnID
) || isCCCExtensionOID(&extn
.extnID
)) {
1751 secdebug("cert", "Found unknown critical extension");
1752 certificate
->_foundUnknownCriticalExtension
= true;
1754 secdebug("cert", "Found unknown non critical extension");
1758 checkForMissingRevocationInfo(certificate
);
1767 /* Public API functions. */
1768 SecCertificateRef
SecCertificateCreateWithBytes(CFAllocatorRef allocator
,
1769 const UInt8
*der_bytes
, CFIndex der_length
) {
1770 if (der_bytes
== NULL
) return NULL
;
1771 if (der_length
== 0) return NULL
;
1773 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1774 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1775 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1777 memset((char*)result
+ sizeof(result
->_base
), 0,
1778 sizeof(*result
) - sizeof(result
->_base
));
1779 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1780 result
->_der
.length
= der_length
;
1781 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1782 if (!SecCertificateParse(result
)) {
1790 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1791 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1792 const UInt8
*der_bytes
, CFIndex der_length
);
1794 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1795 const UInt8
*der_bytes
, CFIndex der_length
) {
1796 return SecCertificateCreateWithBytes(allocator
, der_bytes
, der_length
);
1798 /* @@@ End of placeholder. */
1800 /* AUDIT[securityd](done):
1801 der_certificate is a caller provided data of any length (might be 0), only
1802 its cf type has been checked.
1804 SecCertificateRef
SecCertificateCreateWithData(CFAllocatorRef allocator
,
1805 CFDataRef der_certificate
) {
1806 if (!der_certificate
) {
1809 CFIndex size
= sizeof(struct __SecCertificate
);
1810 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1811 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1813 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1814 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1815 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1816 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1817 if (!SecCertificateParse(result
)) {
1825 SecCertificateRef
SecCertificateCreateWithKeychainItem(CFAllocatorRef allocator
,
1826 CFDataRef der_certificate
,
1827 CFTypeRef keychain_item
)
1829 SecCertificateRef result
= SecCertificateCreateWithData(allocator
, der_certificate
);
1831 CFRetainSafe(keychain_item
);
1832 result
->_keychain_item
= keychain_item
;
1837 OSStatus
SecCertificateSetKeychainItem(SecCertificateRef certificate
,
1838 CFTypeRef keychain_item
)
1843 CFRetainSafe(keychain_item
);
1844 CFReleaseSafe(certificate
->_keychain_item
);
1845 certificate
->_keychain_item
= keychain_item
;
1846 return errSecSuccess
;
1849 CFDataRef
SecCertificateCopyData(SecCertificateRef certificate
) {
1851 CFDataRef result
= NULL
;
1855 if (certificate
->_der_data
) {
1856 CFRetain(certificate
->_der_data
);
1857 result
= certificate
->_der_data
;
1859 result
= CFDataCreate(CFGetAllocator(certificate
),
1860 certificate
->_der
.data
, certificate
->_der
.length
);
1862 /* FIXME: If we wish to cache result we need to lock the certificate.
1863 Also this create 2 copies of the certificate data which is somewhat
1866 certificate
->_der_data
= result
;
1873 CFIndex
SecCertificateGetLength(SecCertificateRef certificate
) {
1874 return certificate
->_der
.length
;
1877 const UInt8
*SecCertificateGetBytePtr(SecCertificateRef certificate
) {
1878 return certificate
->_der
.data
;
1881 static bool SecCertificateIsCertificate(SecCertificateRef certificate
) {
1885 #ifndef IS_TRUSTTESTS
1886 /* TrustTests registers two SecCertificate TypeIDs, so we'll skip this check
1887 * in the tests and just let the tests crash if they pass the wrong object type. */
1888 if (CFGetTypeID(certificate
) != SecCertificateGetTypeID()) {
1895 /* Used to recreate preCert from cert for Certificate Transparency */
1896 CFDataRef
SecCertificateCopyPrecertTBS(SecCertificateRef certificate
)
1898 CFDataRef outData
= NULL
;
1899 DERItem tbsIn
= certificate
->_tbs
;
1900 DERItem tbsOut
= {0,};
1901 DERItem extensionsOut
= {0,};
1902 DERItem
*extensionsList
= malloc(sizeof(DERItem
)*certificate
->_extensionCount
); /* This maybe one too many */
1903 DERItemSpec
*extensionsListSpecs
= malloc(sizeof(DERItemSpec
)*certificate
->_extensionCount
);
1907 require_quiet(extensionsList
&& extensionsListSpecs
, out
);
1909 /* decode the TBSCert - it was saved in full DER form */
1910 drtn
= DERParseSequence(&tbsIn
,
1911 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1912 &tbsCert
, sizeof(tbsCert
));
1913 require_noerr_quiet(drtn
, out
);
1915 /* Go over extensions and filter any SCT extension */
1916 CFIndex extensionsCount
= 0;
1918 if (tbsCert
.extensions
.length
) {
1921 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1923 require_noerr_quiet(drtn
, out
);
1924 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
1925 DERDecodedInfo currDecoded
;
1926 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1928 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, out
);
1930 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1931 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1932 &extn
, sizeof(extn
));
1933 require_noerr_quiet(drtn
, out
);
1935 if (extn
.extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
1936 !memcmp(extn
.extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
.extnID
.length
))
1939 extensionsList
[extensionsCount
] = currDecoded
.content
;
1940 extensionsListSpecs
[extensionsCount
].offset
= sizeof(DERItem
)*extensionsCount
;
1941 extensionsListSpecs
[extensionsCount
].options
= 0;
1942 extensionsListSpecs
[extensionsCount
].tag
= ASN1_CONSTR_SEQUENCE
;
1947 require_quiet(drtn
== DR_EndOfSequence
, out
);
1951 /* Encode extensions */
1952 extensionsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
);
1953 extensionsOut
.data
= malloc(extensionsOut
.length
);
1954 require_quiet(extensionsOut
.data
, out
);
1955 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
, extensionsOut
.data
, &extensionsOut
.length
);
1956 require_noerr_quiet(drtn
, out
);
1958 tbsCert
.extensions
= extensionsOut
;
1960 tbsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
);
1961 tbsOut
.data
= malloc(tbsOut
.length
);
1962 require_quiet(tbsOut
.data
, out
);
1963 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
, tbsOut
.data
, &tbsOut
.length
);
1964 require_noerr_quiet(drtn
, out
);
1966 outData
= CFDataCreate(kCFAllocatorDefault
, tbsOut
.data
, tbsOut
.length
);
1969 if (extensionsOut
.data
) free(extensionsOut
.data
);
1970 if (tbsOut
.data
) free(tbsOut
.data
);
1971 if (extensionsList
) free(extensionsList
);
1972 if (extensionsListSpecs
) free(extensionsListSpecs
);
1977 /* From rfc3280 - Appendix B. ASN.1 Notes
1979 Object Identifiers (OIDs) are used throughout this specification to
1980 identify certificate policies, public key and signature algorithms,
1981 certificate extensions, etc. There is no maximum size for OIDs.
1982 This specification mandates support for OIDs which have arc elements
1983 with values that are less than 2^28, that is, they MUST be between 0
1984 and 268,435,455, inclusive. This allows each arc element to be
1985 represented within a single 32 bit word. Implementations MUST also
1986 support OIDs where the length of the dotted decimal (see [RFC 2252],
1987 section 4.1) string representation can be up to 100 bytes
1988 (inclusive). Implementations MUST be able to handle OIDs with up to
1989 20 elements (inclusive). CAs SHOULD NOT issue certificates which
1990 contain OIDs that exceed these requirements. Likewise, CRL issuers
1991 SHOULD NOT issue CRLs which contain OIDs that exceed these
1995 /* Oids longer than this are considered invalid. */
1996 #define MAX_OID_SIZE 32
1998 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
1999 const DERItem
*oid
) {
2001 if (oid
->length
== 0) {
2002 return SecCopyCertString(SEC_NULL_KEY
);
2004 if (oid
->length
> MAX_OID_SIZE
) {
2005 return SecCopyCertString(SEC_OID_TOO_LONG_KEY
);
2008 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
2010 // The first two levels are encoded into one byte, since the root level
2011 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
2012 // y may be > 39, so we have to add special-case handling for this.
2013 uint32_t x
= oid
->data
[0] / 40;
2014 uint32_t y
= oid
->data
[0] % 40;
2017 // Handle special case for large y if x = 2
2021 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
2024 for (x
= 1; x
< oid
->length
; ++x
)
2026 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
2027 /* @@@ value may not span more than 4 bytes. */
2028 /* A max number of 20 values is allowed. */
2029 if (!(oid
->data
[x
] & 0x80))
2031 CFStringAppendFormat(result
, NULL
, CFSTR(".%" PRIu32
), value
);
2038 static CFStringRef
copyOidDescription(CFAllocatorRef allocator
,
2039 const DERItem
*oid
, bool localized
) {
2040 if (!oid
|| oid
->length
== 0) {
2041 return (localized
) ? SecCopyCertString(SEC_NULL_KEY
) : SEC_NULL_KEY
;
2044 CFStringRef name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
2049 /* Build the key we use to lookup the localized OID description. */
2050 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
2051 oid
->length
* 3 + 5);
2052 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), oid
->length
);
2053 for (DERSize ix
= 0; ix
< oid
->length
; ++ix
) {
2054 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
2056 CFStringRef locname
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
2057 if (locname
&& !CFEqual(oidKey
, locname
)) {
2058 /* Found localized description string, so use it instead of OID. */
2059 CFReleaseSafe(name
);
2062 CFReleaseSafe(locname
);
2069 /* Return the ipAddress as a dotted quad for ipv4, or as 8 colon separated
2070 4 digit hex strings for ipv6. Return NULL if the provided IP doesn't
2071 have a length of exactly 4 or 16 octets.
2072 Note: hex values are normalized to uppercase.
2074 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
2075 const DERItem
*ip
) {
2076 /* This is the IP Address as an OCTET STRING.
2077 For IPv4 it's 4 octets addr, or 8 octets, addr/mask.
2078 For IPv6 it's 16 octets addr, or 32 octets addr/mask.
2080 CFStringRef value
= NULL
;
2081 if (ip
->length
== IPv4ADDRLEN
) {
2082 value
= CFStringCreateWithFormat(allocator
, NULL
,
2083 CFSTR("%u.%u.%u.%u"),
2084 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
2085 } else if (ip
->length
== IPv6ADDRLEN
) {
2086 value
= CFStringCreateWithFormat(allocator
, NULL
,
2087 CFSTR("%02X%02X:%02X%02X:%02X%02X:%02X%02X:"
2088 "%02X%02X:%02X%02X:%02X%02X:%02X%02X"),
2089 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
2090 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
2091 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
2092 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
2098 void appendProperty(CFMutableArrayRef properties
, CFStringRef propertyType
,
2099 CFStringRef label
, CFStringRef localizedLabel
, CFTypeRef value
,
2101 CFDictionaryRef property
;
2103 CFStringRef ll
= NULL
;
2105 /* use unlocalized label, overriding localizedLabel */
2106 ll
= localizedLabel
= (CFStringRef
) CFRetainSafe(label
);
2107 } else if (!localizedLabel
) {
2108 /* copy localized label for unlocalized label */
2109 ll
= localizedLabel
= SecCopyCertString(label
);
2111 const void *all_keys
[4];
2112 all_keys
[0] = kSecPropertyKeyType
;
2113 all_keys
[1] = kSecPropertyKeyLabel
;
2114 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
2115 all_keys
[3] = kSecPropertyKeyValue
;
2116 const void *property_values
[] = {
2122 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2123 all_keys
, property_values
, value
? 4 : 3,
2124 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2127 const void *nolabel_keys
[2];
2128 nolabel_keys
[0] = kSecPropertyKeyType
;
2129 nolabel_keys
[1] = kSecPropertyKeyValue
;
2130 const void *property_values
[] = {
2134 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2135 nolabel_keys
, property_values
, 2,
2136 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2139 CFArrayAppendValue(properties
, property
);
2140 CFRelease(property
);
2144 #define UTC_TIME_NOSEC_ZULU_LEN 11
2146 #define UTC_TIME_ZULU_LEN 13
2147 /* YYMMDDhhmmssThhmm */
2148 #define UTC_TIME_LOCALIZED_LEN 17
2149 /* YYYYMMDDhhmmssZ */
2150 #define GENERALIZED_TIME_ZULU_LEN 15
2151 /* YYYYMMDDhhmmssThhmm */
2152 #define GENERALIZED_TIME_LOCALIZED_LEN 19
2154 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
2156 static inline int parseDecimalPair(const DERByte
**p
) {
2157 const DERByte
*cp
= *p
;
2159 return 10 * (cp
[0] - '0') + cp
[1] - '0';
2162 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime.
2163 Return a CFErrorRef in the error parameter if decoding fails.
2164 Note that this is needed to distinguish an error condition from a
2165 valid time which specifies 2001-01-01 00:00:00 (i.e. a value of 0).
2167 CFAbsoluteTime
SecAbsoluteTimeFromDateContentWithError(DERTag tag
,
2168 const uint8_t *bytes
,
2170 CFErrorRef
*error
) {
2174 if (NULL
== bytes
|| 0 == length
) {
2178 bool isUtcLength
= false;
2179 bool isLocalized
= false;
2180 bool noSeconds
= false;
2182 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
2186 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
2189 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
2191 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
2194 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
2197 default: /* unknown format */
2201 /* Make sure the der tag fits the thing inside it. */
2202 if (tag
== ASN1_UTC_TIME
) {
2206 } else if (tag
== ASN1_GENERALIZED_TIME
) {
2214 const DERByte
*cp
= bytes
;
2215 /* Check that all characters are digits, except if localized the timezone
2216 indicator or if not localized the 'Z' at the end. */
2218 for (ix
= 0; ix
< length
; ++ix
) {
2219 if (!(isdigit(cp
[ix
]))) {
2220 if ((isLocalized
&& ix
== length
- 5 &&
2221 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
2222 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
2229 /* Parse the date and time fields. */
2230 int year
, month
, day
, hour
, minute
, second
;
2232 year
= parseDecimalPair(&cp
);
2234 /* 0 <= year < 50 : assume century 21 */
2236 } else if (year
< 70) {
2237 /* 50 <= year < 70 : illegal per PKIX */
2240 /* 70 < year <= 99 : assume century 20 */
2244 year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
2246 month
= parseDecimalPair(&cp
);
2247 day
= parseDecimalPair(&cp
);
2248 hour
= parseDecimalPair(&cp
);
2249 minute
= parseDecimalPair(&cp
);
2253 second
= parseDecimalPair(&cp
);
2256 CFTimeInterval timeZoneOffset
;
2258 /* ZONE INDICATOR */
2259 int multiplier
= *cp
++ == '+' ? 60 : -60;
2260 timeZoneOffset
= multiplier
*
2261 (parseDecimalPair(&cp
) * 60 + parseDecimalPair(&cp
));
2266 secdebug("dateparse",
2267 "date %.*s year: %04d%02d%02d%02d%02d%02d%+05g",
2268 (int) length
, bytes
, year
, month
,
2269 day
, hour
, minute
, second
,
2270 timeZoneOffset
/ 60);
2272 static int mdays
[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
2273 int is_leap_year
= year
% 4 == 0 && (year
% 100 != 0 || year
% 400 == 0) ? 1 : 0;
2274 /* Some basic checks on the date, allowing leap seconds */
2275 if (month
< 1 || month
> 12 || day
< 1 || day
> 31 || hour
> 23 || minute
> 59 || second
> 60
2276 || (month
== 2 && day
> mdays
[month
] - mdays
[month
- 1] + is_leap_year
)
2277 || (month
!= 2 && day
> mdays
[month
] - mdays
[month
- 1])) {
2282 int dy
= year
- 2001;
2287 int leap_days
= dy
/ 4 - dy
/ 100 + dy
/ 400;
2288 day
+= ((year
- 2001) * 365 + leap_days
) + mdays
[month
- 1] - 1;
2290 day
+= is_leap_year
;
2292 CFAbsoluteTime absTime
= (CFAbsoluteTime
)((day
* 24.0 + hour
) * 60.0 + minute
) * 60.0 + second
;
2293 return absTime
- timeZoneOffset
;
2297 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
2302 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
2304 return SecAbsoluteTimeFromDateContentWithError(tag
, bytes
, length
, NULL
);
2307 __attribute__((__nonnull__
)) static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
2308 CFAbsoluteTime
*pabsTime
) {
2309 CFErrorRef error
= NULL
;
2310 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContentWithError(tag
, date
->data
,
2311 date
->length
, &error
);
2313 secwarning("Invalid date specification in certificate (see RFC5280 4.1.2.5)");
2318 *pabsTime
= absTime
;
2322 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
2323 true if the date was valid and properly decoded, also return the result in
2324 absTime. Return false otherwise. */
2325 __attribute__((__nonnull__
)) static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
2326 CFAbsoluteTime
*absTime
) {
2327 if (dateChoice
->length
== 0) return false;
2329 DERDecodedInfo decoded
;
2330 if (DERDecodeItem(dateChoice
, &decoded
))
2333 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
2337 static void appendDataProperty(CFMutableArrayRef properties
,
2338 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
,
2340 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
2341 der_data
->data
, der_data
->length
);
2342 appendProperty(properties
, kSecPropertyTypeData
, label
, localizedLabel
,
2347 static void appendRelabeledProperty(CFMutableArrayRef properties
,
2349 CFStringRef localizedLabel
,
2350 const DERItem
*der_data
,
2351 CFStringRef labelFormat
,
2353 CFStringRef newLabel
=
2354 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2355 labelFormat
, label
);
2356 CFStringRef ll
= NULL
;
2357 CFStringRef localizedLabelFormat
= NULL
;
2359 /* use provided label and format strings; do not localize */
2360 ll
= localizedLabel
= (CFStringRef
) CFRetainSafe(label
);
2361 localizedLabelFormat
= (CFStringRef
) CFRetainSafe(labelFormat
);
2363 if (!localizedLabel
) {
2364 /* copy localized label for provided label */
2365 ll
= localizedLabel
= SecCopyCertString(label
);
2367 /* copy localized format for provided format */
2368 localizedLabelFormat
= SecCopyCertString(labelFormat
);
2371 CFStringRef newLocalizedLabel
=
2372 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2373 localizedLabelFormat
, localizedLabel
);
2375 CFReleaseSafe(localizedLabelFormat
);
2376 appendDataProperty(properties
, newLabel
, newLocalizedLabel
, der_data
, localized
);
2377 CFReleaseSafe(newLabel
);
2378 CFReleaseSafe(newLocalizedLabel
);
2382 static void appendUnparsedProperty(CFMutableArrayRef properties
,
2383 CFStringRef label
, CFStringRef localizedLabel
,
2384 const DERItem
*der_data
, bool localized
) {
2385 appendRelabeledProperty(properties
, label
, localizedLabel
, der_data
,
2386 SEC_UNPARSED_KEY
, localized
);
2389 static void appendInvalidProperty(CFMutableArrayRef properties
,
2390 CFStringRef label
, const DERItem
*der_data
, bool localized
) {
2391 appendRelabeledProperty(properties
, label
, NULL
, der_data
,
2392 SEC_INVALID_KEY
, localized
);
2395 static void appendDateContentProperty(CFMutableArrayRef properties
,
2396 CFStringRef label
, DERTag tag
,
2397 const DERItem
*dateContent
, bool localized
) {
2398 CFAbsoluteTime absTime
;
2399 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
2400 /* Date decode failure; insert hex bytes instead. */
2401 return appendInvalidProperty(properties
, label
, dateContent
, localized
);
2403 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2404 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
, localized
);
2408 static void appendDateProperty(CFMutableArrayRef properties
,
2409 CFStringRef label
, CFAbsoluteTime absTime
, bool localized
) {
2410 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2411 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
, localized
);
2415 static void appendValidityPeriodProperty(CFMutableArrayRef parent
, CFStringRef label
,
2416 SecCertificateRef certificate
, bool localized
) {
2417 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2418 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2420 appendDateProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2421 certificate
->_notBefore
, localized
);
2422 appendDateProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2423 certificate
->_notAfter
, localized
);
2425 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
, localized
);
2426 CFReleaseNull(properties
);
2429 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
2430 CFStringRef label
, const DERItem
*ip
, bool localized
) {
2432 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
2434 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
, localized
);
2437 appendUnparsedProperty(properties
, label
, NULL
, ip
, localized
);
2441 static void appendURLContentProperty(CFMutableArrayRef properties
,
2442 CFStringRef label
, const DERItem
*urlContent
, bool localized
) {
2443 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
2444 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2446 appendProperty(properties
, kSecPropertyTypeURL
, label
, NULL
, url
, localized
);
2449 appendInvalidProperty(properties
, label
, urlContent
, localized
);
2453 static void appendURLProperty(CFMutableArrayRef properties
,
2454 CFStringRef label
, const DERItem
*url
, bool localized
) {
2455 DERDecodedInfo decoded
;
2458 drtn
= DERDecodeItem(url
, &decoded
);
2459 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2460 appendInvalidProperty(properties
, label
, url
, localized
);
2462 appendURLContentProperty(properties
, label
, &decoded
.content
, localized
);
2466 static void appendOIDProperty(CFMutableArrayRef properties
,
2467 CFStringRef label
, CFStringRef llabel
, const DERItem
*oid
, bool localized
) {
2468 CFStringRef oid_string
=
2469 copyOidDescription(CFGetAllocator(properties
), oid
, localized
);
2470 appendProperty(properties
, kSecPropertyTypeString
, label
, llabel
,
2471 oid_string
, localized
);
2472 CFRelease(oid_string
);
2475 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2476 CFStringRef label
, const DERAlgorithmId
*algorithm
, bool localized
) {
2477 CFMutableArrayRef alg_props
=
2478 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2479 &kCFTypeArrayCallBacks
);
2480 appendOIDProperty(alg_props
, SEC_ALGORITHM_KEY
, NULL
,
2481 &algorithm
->oid
, localized
);
2482 if (algorithm
->params
.length
) {
2483 if (algorithm
->params
.length
== 2 &&
2484 algorithm
->params
.data
[0] == ASN1_NULL
&&
2485 algorithm
->params
.data
[1] == 0) {
2486 CFStringRef value
= SecCopyCertString(SEC_NONE_KEY
);
2487 appendProperty(alg_props
, kSecPropertyTypeString
,
2488 SEC_PARAMETERS_KEY
, NULL
, value
, localized
);
2491 appendUnparsedProperty(alg_props
, SEC_PARAMETERS_KEY
, NULL
,
2492 &algorithm
->params
, localized
);
2495 appendProperty(properties
, kSecPropertyTypeSection
, label
, NULL
,
2496 alg_props
, localized
);
2497 CFRelease(alg_props
);
2500 static void appendPublicKeyProperty(CFMutableArrayRef parent
, CFStringRef label
,
2501 SecCertificateRef certificate
, bool localized
) {
2502 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2503 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2505 /* Public key algorithm. */
2506 appendAlgorithmProperty(properties
, SEC_PUBLIC_KEY_ALG_KEY
,
2507 &certificate
->_algId
, localized
);
2509 /* Public Key Size */
2510 SecKeyRef publicKey
= SecCertificateCopyKey(certificate
);
2512 size_t sizeInBytes
= SecKeyGetBlockSize(publicKey
);
2513 CFStringRef sizeInBitsString
= CFStringCreateWithFormat(allocator
, NULL
,
2514 CFSTR("%ld"), (sizeInBytes
*8));
2515 if (sizeInBitsString
) {
2516 appendProperty(properties
, kSecPropertyTypeString
, SEC_PUBLIC_KEY_SIZE_KEY
,
2517 NULL
, sizeInBitsString
, localized
);
2519 CFReleaseNull(sizeInBitsString
);
2521 CFReleaseNull(publicKey
);
2523 /* Consider breaking down an RSA public key into modulus and
2525 appendDataProperty(properties
, SEC_PUBLIC_KEY_DATA_KEY
, NULL
,
2526 &certificate
->_pubKeyDER
, localized
);
2528 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2529 properties
, localized
);
2530 CFReleaseNull(properties
);
2533 static void appendSignatureProperty(CFMutableArrayRef parent
, CFStringRef label
,
2534 SecCertificateRef certificate
, bool localized
) {
2535 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2536 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2538 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
2539 &certificate
->_tbsSigAlg
, localized
);
2541 appendDataProperty(properties
, SEC_SIGNATURE_DATA_KEY
, NULL
,
2542 &certificate
->_signature
, localized
);
2544 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2545 properties
, localized
);
2546 CFReleaseNull(properties
);
2549 static void appendFingerprintsProperty(CFMutableArrayRef parent
, CFStringRef label
,
2550 SecCertificateRef certificate
, bool localized
) {
2551 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2552 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2554 CFDataRef sha256Fingerprint
= SecCertificateCopySHA256Digest(certificate
);
2555 if (sha256Fingerprint
) {
2556 appendProperty(properties
, kSecPropertyTypeData
, SEC_SHA2_FINGERPRINT_KEY
,
2557 NULL
, sha256Fingerprint
, localized
);
2559 CFReleaseNull(sha256Fingerprint
);
2561 appendProperty(properties
, kSecPropertyTypeData
, SEC_SHA1_FINGERPRINT_KEY
,
2562 NULL
, SecCertificateGetSHA1Digest(certificate
), localized
);
2564 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2565 properties
, localized
);
2566 CFReleaseNull(properties
);
2569 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2570 const DERItem
*blob
) {
2571 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2572 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2573 blob
->length
* 3 - 1);
2574 for (ix
= 0; ix
< length
; ++ix
)
2576 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2578 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2583 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2584 CFStringRef blobType
, CFStringRef quanta
,
2585 const DERItem
*blob
, bool localized
) {
2586 CFStringRef localizedBlobType
= (localized
) ?
2587 SecCopyCertString(blobType
) : (CFStringRef
) CFRetainSafe(blobType
);
2588 CFStringRef localizedQuanta
= (localized
) ?
2589 SecCopyCertString(quanta
) : (CFStringRef
) CFRetainSafe(quanta
);
2590 /* "format string for encoded field data (e.g. Sequence; 128 bytes; "
2591 "data = 00 00 ...)" */
2592 CFStringRef blobFormat
= (localized
) ?
2593 SecCopyCertString(SEC_BLOB_KEY
) : SEC_BLOB_KEY
;
2594 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2595 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2596 blobFormat
, localizedBlobType
, blob
->length
, localizedQuanta
, hex
);
2598 CFRelease(blobFormat
);
2599 CFReleaseSafe(localizedQuanta
);
2600 CFReleaseSafe(localizedBlobType
);
2605 /* Return a string verbatim (unlocalized) from a DER field. */
2606 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2607 const DERItem
*string
, CFStringEncoding encoding
,
2608 bool printableOnly
) {
2609 /* Strip potential bogus trailing zero from printable strings. */
2610 DERSize length
= string
->length
;
2611 if (length
&& string
->data
[length
- 1] == 0) {
2612 /* Don't mess with the length of UTF16 strings though. */
2613 if (encoding
!= kCFStringEncodingUTF16
)
2616 /* A zero length string isn't considered printable. */
2617 if (!length
&& printableOnly
)
2620 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2621 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2622 passing false makes it treat it as native endian by default. */
2623 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2624 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2628 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2631 /* From rfc3280 - Appendix B. ASN.1 Notes
2633 CAs MUST force the serialNumber to be a non-negative integer, that
2634 is, the sign bit in the DER encoding of the INTEGER value MUST be
2635 zero - this can be done by adding a leading (leftmost) `00'H octet if
2636 necessary. This removes a potential ambiguity in mapping between a
2637 string of octets and an integer value.
2639 As noted in section 4.1.2.2, serial numbers can be expected to
2640 contain long integers. Certificate users MUST be able to handle
2641 serialNumber values up to 20 octets in length. Conformant CAs MUST
2642 NOT use serialNumber values longer than 20 octets.
2645 /* Return the given numeric data as a string: decimal up to 64 bits,
2648 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2649 const DERItem
*integer
) {
2651 CFIndex ix
, length
= integer
->length
;
2653 if (length
== 0 || length
> 8)
2654 return copyHexDescription(allocator
, integer
);
2656 for(ix
= 0; ix
< length
; ++ix
) {
2658 value
+= integer
->data
[ix
];
2661 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2664 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2665 DERTag tag
, const DERItem
*derThing
, bool printableOnly
, bool localized
) {
2666 if (!derThing
) { return NULL
; }
2670 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2671 case ASN1_PRINTABLE_STRING
:
2672 case ASN1_IA5_STRING
:
2673 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2674 case ASN1_UTF8_STRING
:
2675 case ASN1_GENERAL_STRING
:
2676 case ASN1_UNIVERSAL_STRING
:
2677 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2678 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2679 case ASN1_VIDEOTEX_STRING
: // 21
2680 case ASN1_VISIBLE_STRING
: // 26
2681 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2682 case ASN1_BMP_STRING
: // 30
2683 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2684 case ASN1_OCTET_STRING
:
2685 return printableOnly
? NULL
:
2686 copyBlobString(allocator
, SEC_BYTE_STRING_KEY
, SEC_BYTES_KEY
,
2687 derThing
, localized
);
2688 case ASN1_BIT_STRING
:
2689 return printableOnly
? NULL
:
2690 copyBlobString(allocator
, SEC_BIT_STRING_KEY
, SEC_BITS_KEY
,
2691 derThing
, localized
);
2692 case ASN1_CONSTR_SEQUENCE
:
2693 return printableOnly
? NULL
:
2694 copyBlobString(allocator
, SEC_SEQUENCE_KEY
, SEC_BYTES_KEY
,
2695 derThing
, localized
);
2696 case ASN1_CONSTR_SET
:
2697 return printableOnly
? NULL
:
2698 copyBlobString(allocator
, SEC_SET_KEY
, SEC_BYTES_KEY
,
2699 derThing
, localized
);
2700 case ASN1_OBJECT_ID
:
2701 return printableOnly
? NULL
: copyOidDescription(allocator
, derThing
, localized
);
2703 if (printableOnly
) {
2706 CFStringRef fmt
= (localized
) ?
2707 SecCopyCertString(SEC_NOT_DISPLAYED_KEY
) : SEC_NOT_DISPLAYED_KEY
;
2708 if (!fmt
) { return NULL
; }
2709 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
2710 (unsigned long)tag
, (unsigned long)derThing
->length
);
2717 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2718 const DERItem
*derThing
, bool printableOnly
, bool localized
) {
2719 DERDecodedInfo decoded
;
2722 drtn
= DERDecodeItem(derThing
, &decoded
);
2724 /* TODO: Perhaps put something in the label saying we couldn't parse
2726 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2728 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2729 &decoded
.content
, false, localized
);
2733 static void appendDERThingProperty(CFMutableArrayRef properties
,
2734 CFStringRef label
, CFStringRef localizedLabel
,
2735 const DERItem
*derThing
, bool localized
) {
2736 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2737 derThing
, false, localized
);
2739 appendProperty(properties
, kSecPropertyTypeString
, label
, localizedLabel
,
2742 CFReleaseSafe(value
);
2745 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2746 const DERItem
*rdnValue
, CFIndex rdnIX
,
2748 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2750 /* If there is more than one value pair we create a subsection for the
2751 second pair, and append things to the subsection for subsequent
2753 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2754 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2756 /* Since this is the second rdn pair for a given rdn, we setup a
2757 new subsection for this rdn. We remove the first property
2758 from the properties array and make it the first element in the
2759 subsection instead. */
2760 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2761 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2762 CFArrayAppendValue(rdn_props
, lastValue
);
2763 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2764 appendProperty(properties
, kSecPropertyTypeSection
, NULL
, NULL
,
2765 rdn_props
, localized
);
2766 properties
= rdn_props
;
2767 // rdn_props is now retained by the original properties array
2768 CFReleaseSafe(rdn_props
);
2770 /* Since this is the third or later rdn pair we have already
2771 created a subsection in the top level properties array. Instead
2772 of appending to that directly we append to the array inside the
2774 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2775 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2779 /* Finally we append the new rdn value to the property array. */
2781 SecDERItemCopyOIDDecimalRepresentation(CFGetAllocator(properties
),
2783 CFStringRef localizedLabel
= copyOidDescription(CFGetAllocator(properties
),
2784 rdnType
, localized
);
2785 appendDERThingProperty(properties
, label
, localizedLabel
,
2786 rdnValue
, localized
);
2787 CFReleaseSafe(label
);
2788 CFReleaseSafe(localizedLabel
);
2789 return errSecSuccess
;
2792 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2793 const DERItem
*rdnSetContent
, bool localized
) {
2794 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2795 &kCFTypeArrayCallBacks
);
2796 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2797 appendRDNProperty
, localized
);
2799 CFArrayRemoveAllValues(properties
);
2800 appendInvalidProperty(properties
, SEC_RDN_KEY
, rdnSetContent
,
2808 From rfc3739 - 3.1.2. Subject
2810 When parsing the subject here are some tips for a short name of the cert.
2811 Choice I: commonName
2812 Choice II: givenName
2813 Choice III: pseudonym
2815 The commonName attribute value SHALL, when present, contain a name
2816 of the subject. This MAY be in the subject's preferred
2817 presentation format, or a format preferred by the CA, or some
2818 other format. Pseudonyms, nicknames, and names with spelling
2819 other than defined by the registered name MAY be used. To
2820 understand the nature of the name presented in commonName,
2821 complying applications MAY have to examine present values of the
2822 givenName and surname attributes, or the pseudonym attribute.
2825 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2826 const DERItem
*x501NameContent
, bool localized
) {
2827 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2828 &kCFTypeArrayCallBacks
);
2829 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2830 appendRDNProperty
, localized
);
2832 CFArrayRemoveAllValues(properties
);
2833 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
,
2834 x501NameContent
, localized
);
2840 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2841 const DERItem
*x501Name
, bool localized
) {
2842 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2843 &kCFTypeArrayCallBacks
);
2844 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
, localized
);
2846 CFArrayRemoveAllValues(properties
);
2847 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
,
2848 x501Name
, localized
);
2854 static void appendIntegerProperty(CFMutableArrayRef properties
,
2855 CFStringRef label
, const DERItem
*integer
, bool localized
) {
2856 CFStringRef string
= copyIntegerContentDescription(
2857 CFGetAllocator(properties
), integer
);
2858 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2863 static void appendBoolProperty(CFMutableArrayRef properties
,
2864 CFStringRef label
, bool boolean
, bool localized
) {
2865 CFStringRef key
= (boolean
) ? SEC_YES_KEY
: SEC_NO_KEY
;
2866 CFStringRef value
= (localized
) ? SecCopyCertString(key
) : key
;
2867 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2872 static void appendBooleanProperty(CFMutableArrayRef properties
,
2873 CFStringRef label
, const DERItem
*boolean
,
2874 bool defaultValue
, bool localized
) {
2876 DERReturn drtn
= DERParseBooleanWithDefault(boolean
, defaultValue
, &result
);
2878 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2879 appendInvalidProperty(properties
, label
, boolean
, localized
);
2881 appendBoolProperty(properties
, label
, result
, localized
);
2885 static void appendSerialNumberProperty(CFMutableArrayRef parent
, CFStringRef label
,
2886 DERItem
*serialNum
, bool localized
) {
2887 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2888 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2890 if (serialNum
->length
) {
2891 appendIntegerProperty(properties
, SEC_SERIAL_NUMBER_KEY
,
2892 serialNum
, localized
);
2893 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2894 properties
, localized
);
2897 CFReleaseNull(properties
);
2900 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2901 CFStringRef label
, const DERItem
*bitStringContent
,
2902 const __nonnull CFStringRef
*names
, CFIndex namesCount
,
2904 DERSize len
= bitStringContent
->length
- 1;
2905 require_quiet(len
== 1 || len
== 2, badDER
);
2906 DERByte numUnusedBits
= bitStringContent
->data
[0];
2907 require_quiet(numUnusedBits
< 8, badDER
);
2908 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
2909 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
2910 uint_fast16_t value
= bitStringContent
->data
[1];
2913 value
= (value
<< 8) + bitStringContent
->data
[2];
2919 CFStringRef fmt
= (localized
) ?
2920 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
2921 CFStringRef string
= NULL
;
2922 for (ix
= 0; ix
< bits
; ++ix
) {
2923 CFStringRef localizedName
= (localized
) ? SecCopyCertString(names
[ix
]) : CFRetainSafe(names
[ix
]);
2927 CFStringCreateWithFormat(CFGetAllocator(properties
),
2928 NULL
, fmt
, string
, localizedName
);
2932 string
= localizedName
;
2933 CFRetainSafe(string
);
2937 CFReleaseNull(localizedName
);
2940 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2941 string
? string
: CFSTR(""), localized
);
2942 CFReleaseSafe(string
);
2945 appendInvalidProperty(properties
, label
, bitStringContent
, localized
);
2948 static void appendBitStringNames(CFMutableArrayRef properties
,
2949 CFStringRef label
, const DERItem
*bitString
,
2950 const __nonnull CFStringRef
*names
, CFIndex namesCount
,
2952 DERDecodedInfo bitStringContent
;
2953 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
2954 require_noerr_quiet(drtn
, badDER
);
2955 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
2956 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
2957 names
, namesCount
, localized
);
2960 appendInvalidProperty(properties
, label
, bitString
, localized
);
2963 static void appendKeyUsage(CFMutableArrayRef properties
,
2964 const DERItem
*extnValue
, bool localized
) {
2965 static const CFStringRef usageNames
[] = {
2966 SEC_DIGITAL_SIGNATURE_KEY
,
2967 SEC_NON_REPUDIATION_KEY
,
2968 SEC_KEY_ENCIPHERMENT_KEY
,
2969 SEC_DATA_ENCIPHERMENT_KEY
,
2970 SEC_KEY_AGREEMENT_KEY
,
2973 SEC_ENCIPHER_ONLY_KEY
,
2974 SEC_DECIPHER_ONLY_KEY
2976 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
2977 usageNames
, array_size(usageNames
), localized
);
2980 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
2981 const DERItem
*extnValue
, bool localized
) {
2982 DERPrivateKeyUsagePeriod pkup
;
2983 DERReturn drtn
= DERParseSequence(extnValue
,
2984 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
2985 &pkup
, sizeof(pkup
));
2986 require_noerr_quiet(drtn
, badDER
);
2987 if (pkup
.notBefore
.length
) {
2988 appendDateContentProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2989 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
, localized
);
2991 if (pkup
.notAfter
.length
) {
2992 appendDateContentProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2993 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
, localized
);
2997 appendInvalidProperty(properties
, SEC_PRIVATE_KU_PERIOD_KEY
,
2998 extnValue
, localized
);
3001 static void appendStringContentProperty(CFMutableArrayRef properties
,
3002 CFStringRef label
, const DERItem
*stringContent
,
3003 CFStringEncoding encoding
, bool localized
) {
3004 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
3005 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
3007 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3011 appendInvalidProperty(properties
, label
, stringContent
, localized
);
3016 OtherName ::= SEQUENCE {
3017 type-id OBJECT IDENTIFIER,
3018 value [0] EXPLICIT ANY DEFINED BY type-id }
3020 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
3021 const DERItem
*otherNameContent
, bool localized
) {
3023 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
3024 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
3026 require_noerr_quiet(drtn
, badDER
);
3027 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3029 SecDERItemCopyOIDDecimalRepresentation(allocator
, &on
.typeIdentifier
);
3030 CFStringRef localizedLabel
=
3031 copyOidDescription(allocator
, &on
.typeIdentifier
, localized
);
3032 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
,
3035 appendProperty(properties
, kSecPropertyTypeString
, label
,
3036 localizedLabel
, value_string
, localized
);
3038 appendUnparsedProperty(properties
, label
, localizedLabel
,
3039 &on
.value
, localized
);
3041 CFReleaseSafe(value_string
);
3042 CFReleaseSafe(label
);
3043 CFReleaseSafe(localizedLabel
);
3046 appendInvalidProperty(properties
, SEC_OTHER_NAME_KEY
,
3047 otherNameContent
, localized
);
3051 GeneralName ::= CHOICE {
3052 otherName [0] OtherName,
3053 rfc822Name [1] IA5String,
3054 dNSName [2] IA5String,
3055 x400Address [3] ORAddress,
3056 directoryName [4] Name,
3057 ediPartyName [5] EDIPartyName,
3058 uniformResourceIdentifier [6] IA5String,
3059 iPAddress [7] OCTET STRING,
3060 registeredID [8] OBJECT IDENTIFIER}
3062 EDIPartyName ::= SEQUENCE {
3063 nameAssigner [0] DirectoryString OPTIONAL,
3064 partyName [1] DirectoryString }
3066 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
3067 DERTag tag
, const DERItem
*generalName
, bool localized
) {
3069 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
3070 appendOtherNameContentProperty(properties
, generalName
, localized
);
3072 case ASN1_CONTEXT_SPECIFIC
| 1:
3074 appendStringContentProperty(properties
, SEC_EMAIL_ADDRESS_KEY
,
3075 generalName
, kCFStringEncodingASCII
, localized
);
3077 case ASN1_CONTEXT_SPECIFIC
| 2:
3079 appendStringContentProperty(properties
, SEC_DNS_NAME_KEY
, generalName
,
3080 kCFStringEncodingASCII
, localized
);
3082 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
3083 appendUnparsedProperty(properties
, SEC_X400_ADDRESS_KEY
, NULL
,
3084 generalName
, localized
);
3086 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
3088 CFArrayRef directory_plist
=
3089 createPropertiesForX501Name(CFGetAllocator(properties
),
3090 generalName
, localized
);
3091 appendProperty(properties
, kSecPropertyTypeSection
,
3092 SEC_DIRECTORY_NAME_KEY
, NULL
, directory_plist
, localized
);
3093 CFRelease(directory_plist
);
3096 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
3097 appendUnparsedProperty(properties
, SEC_EDI_PARTY_NAME_KEY
, NULL
,
3098 generalName
, localized
);
3100 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
3101 /* Technically I don't think this is valid, but there are certs out
3102 in the wild that use a constructed IA5String. In particular the
3103 VeriSign Time Stamping Authority CA.cer does this. */
3104 appendURLProperty(properties
, SEC_URI_KEY
, generalName
, localized
);
3106 case ASN1_CONTEXT_SPECIFIC
| 6:
3107 appendURLContentProperty(properties
, SEC_URI_KEY
, generalName
, localized
);
3109 case ASN1_CONTEXT_SPECIFIC
| 7:
3110 appendIPAddressContentProperty(properties
, SEC_IP_ADDRESS_KEY
,
3111 generalName
, localized
);
3113 case ASN1_CONTEXT_SPECIFIC
| 8:
3114 appendOIDProperty(properties
, SEC_REGISTERED_ID_KEY
, NULL
,
3115 generalName
, localized
);
3126 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
3127 const DERItem
*generalName
, bool localized
) {
3128 DERDecodedInfo generalNameContent
;
3129 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
3130 require_noerr_quiet(drtn
, badDER
);
3131 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
3132 &generalNameContent
.content
, localized
))
3135 appendInvalidProperty(properties
, SEC_GENERAL_NAME_KEY
,
3136 generalName
, localized
);
3141 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
3143 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
3144 const DERItem
*generalNamesContent
, bool localized
) {
3146 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
3147 require_noerr_quiet(drtn
, badDER
);
3148 DERDecodedInfo generalNameContent
;
3149 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
3151 if (!appendGeneralNameContentProperty(properties
,
3152 generalNameContent
.tag
, &generalNameContent
.content
, localized
)) {
3156 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3159 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
3160 generalNamesContent
, localized
);
3163 static void appendGeneralNames(CFMutableArrayRef properties
,
3164 const DERItem
*generalNames
, bool localized
) {
3165 DERDecodedInfo generalNamesContent
;
3166 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
3167 require_noerr_quiet(drtn
, badDER
);
3168 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
3170 appendGeneralNamesContent(properties
, &generalNamesContent
.content
,
3174 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
3175 generalNames
, localized
);
3179 BasicConstraints ::= SEQUENCE {
3180 cA BOOLEAN DEFAULT FALSE,
3181 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
3183 static void appendBasicConstraints(CFMutableArrayRef properties
,
3184 const DERItem
*extnValue
, bool localized
) {
3185 DERBasicConstraints basicConstraints
;
3186 DERReturn drtn
= DERParseSequence(extnValue
,
3187 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
3188 &basicConstraints
, sizeof(basicConstraints
));
3189 require_noerr_quiet(drtn
, badDER
);
3191 appendBooleanProperty(properties
, SEC_CERT_AUTHORITY_KEY
,
3192 &basicConstraints
.cA
, false, localized
);
3194 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
3195 appendIntegerProperty(properties
, SEC_PATH_LEN_CONSTRAINT_KEY
,
3196 &basicConstraints
.pathLenConstraint
, localized
);
3200 appendInvalidProperty(properties
, SEC_BASIC_CONSTRAINTS_KEY
,
3201 extnValue
, localized
);
3205 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
3207 * NameConstraints ::= SEQUENCE {
3208 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
3209 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
3211 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
3213 * GeneralSubtree ::= SEQUENCE {
3215 * minimum [0] BaseDistance DEFAULT 0,
3216 * maximum [1] BaseDistance OPTIONAL }
3218 * BaseDistance ::= INTEGER (0..MAX)
3220 static void appendNameConstraints(CFMutableArrayRef properties
,
3221 const DERItem
*extnValue
, bool localized
) {
3222 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3223 DERNameConstraints nc
;
3225 drtn
= DERParseSequence(extnValue
,
3226 DERNumNameConstraintsItemSpecs
,
3227 DERNameConstraintsItemSpecs
,
3229 require_noerr_quiet(drtn
, badDER
);
3230 if (nc
.permittedSubtrees
.length
) {
3232 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.permittedSubtrees
, &gsSeq
), badDER
);
3233 DERDecodedInfo gsContent
;
3234 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3235 DERGeneralSubtree derGS
;
3236 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3237 drtn
= DERParseSequenceContent(&gsContent
.content
,
3238 DERNumGeneralSubtreeItemSpecs
,
3239 DERGeneralSubtreeItemSpecs
,
3240 &derGS
, sizeof(derGS
));
3241 require_noerr_quiet(drtn
, badDER
);
3242 if (derGS
.minimum
.length
) {
3243 appendIntegerProperty(properties
, SEC_PERMITTED_MINIMUM_KEY
,
3244 &derGS
.minimum
, localized
);
3246 if (derGS
.maximum
.length
) {
3247 appendIntegerProperty(properties
, SEC_PERMITTED_MAXIMUM_KEY
,
3248 &derGS
.maximum
, localized
);
3250 if (derGS
.generalName
.length
) {
3251 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3252 &kCFTypeArrayCallBacks
);
3253 appendProperty(properties
, kSecPropertyTypeSection
,
3254 SEC_PERMITTED_NAME_KEY
, NULL
, base
, localized
);
3255 appendGeneralNameProperty(base
, &derGS
.generalName
, localized
);
3259 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3261 if (nc
.excludedSubtrees
.length
) {
3263 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.excludedSubtrees
, &gsSeq
), badDER
);
3264 DERDecodedInfo gsContent
;
3265 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3266 DERGeneralSubtree derGS
;
3267 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3268 drtn
= DERParseSequenceContent(&gsContent
.content
,
3269 DERNumGeneralSubtreeItemSpecs
,
3270 DERGeneralSubtreeItemSpecs
,
3271 &derGS
, sizeof(derGS
));
3272 require_noerr_quiet(drtn
, badDER
);
3273 if (derGS
.minimum
.length
) {
3274 appendIntegerProperty(properties
, SEC_EXCLUDED_MINIMUM_KEY
,
3275 &derGS
.minimum
, localized
);
3277 if (derGS
.maximum
.length
) {
3278 appendIntegerProperty(properties
, SEC_EXCLUDED_MAXIMUM_KEY
,
3279 &derGS
.maximum
, localized
);
3281 if (derGS
.generalName
.length
) {
3282 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3283 &kCFTypeArrayCallBacks
);
3284 appendProperty(properties
, kSecPropertyTypeSection
,
3285 SEC_EXCLUDED_NAME_KEY
, NULL
, base
, localized
);
3286 appendGeneralNameProperty(base
, &derGS
.generalName
, localized
);
3290 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3295 appendInvalidProperty(properties
, SEC_NAME_CONSTRAINTS_KEY
,
3296 extnValue
, localized
);
3300 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
3302 DistributionPoint ::= SEQUENCE {
3303 distributionPoint [0] DistributionPointName OPTIONAL,
3304 reasons [1] ReasonFlags OPTIONAL,
3305 cRLIssuer [2] GeneralNames OPTIONAL }
3307 DistributionPointName ::= CHOICE {
3308 fullName [0] GeneralNames,
3309 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
3311 ReasonFlags ::= BIT STRING {
3315 affiliationChanged (3),
3317 cessationOfOperation (5),
3318 certificateHold (6),
3319 privilegeWithdrawn (7),
3322 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
3323 const DERItem
*extnValue
, bool localized
) {
3324 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3327 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
3328 require_noerr_quiet(drtn
, badDER
);
3329 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3330 DERDecodedInfo dpSeqContent
;
3331 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
3332 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3333 DERDistributionPoint dp
;
3334 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
3335 DERNumDistributionPointItemSpecs
,
3336 DERDistributionPointItemSpecs
,
3338 require_noerr_quiet(drtn
, badDER
);
3339 if (dp
.distributionPoint
.length
) {
3340 DERDecodedInfo distributionPointName
;
3341 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
3342 require_noerr_quiet(drtn
, badDER
);
3343 if (distributionPointName
.tag
==
3344 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
3346 appendGeneralNamesContent(properties
,
3347 &distributionPointName
.content
, localized
);
3348 } else if (distributionPointName
.tag
==
3349 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
3350 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
3351 &dp
.reasons
, localized
);
3352 appendProperty(properties
, kSecPropertyTypeSection
,
3353 SEC_NAME_REL_CRL_ISSUER_KEY
, NULL
, rdn_props
, localized
);
3354 CFRelease(rdn_props
);
3359 if (dp
.reasons
.length
) {
3360 static const CFStringRef reasonNames
[] = {
3362 SEC_KEY_COMPROMISE_KEY
,
3363 SEC_CA_COMPROMISE_KEY
,
3364 SEC_AFFILIATION_CHANGED_KEY
,
3366 SEC_CESSATION_OF_OPER_KEY
,
3367 SEC_CERTIFICATE_HOLD_KEY
,
3368 SEC_PRIV_WITHDRAWN_KEY
,
3369 SEC_AA_COMPROMISE_KEY
3371 appendBitStringContentNames(properties
, SEC_REASONS_KEY
,
3373 reasonNames
, array_size(reasonNames
), localized
);
3375 if (dp
.cRLIssuer
.length
) {
3376 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
3377 &kCFTypeArrayCallBacks
);
3378 appendProperty(properties
, kSecPropertyTypeSection
,
3379 SEC_CRL_ISSUER_KEY
, NULL
, crlIssuer
, localized
);
3380 CFRelease(crlIssuer
);
3381 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
, localized
);
3384 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3387 appendInvalidProperty(properties
, SEC_CRL_DISTR_POINTS_KEY
,
3388 extnValue
, localized
);
3392 Decode a sequence of integers into a comma separated list of ints.
3394 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
3395 CFStringRef label
, const DERItem
*intSequenceContent
,
3397 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3399 CFStringRef fmt
= NULL
, value
= NULL
, intDesc
= NULL
, v
= NULL
;
3400 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
3401 require_noerr_quiet(drtn
, badDER
);
3402 DERDecodedInfo intContent
;
3404 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
3405 require_quiet(fmt
, badDER
);
3406 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
)) == DR_Success
) {
3407 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
3408 intDesc
= copyIntegerContentDescription(
3409 allocator
, &intContent
.content
);
3410 require_quiet(intDesc
, badDER
);
3412 v
= CFStringCreateWithFormat(allocator
, NULL
, fmt
, value
, intDesc
);
3413 CFReleaseNull(value
);
3414 require_quiet(v
, badDER
);
3417 value
= CFStringCreateMutableCopy(allocator
, 0, intDesc
);
3418 require_quiet(value
, badDER
);
3420 CFReleaseNull(intDesc
);
3423 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3425 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3430 /* DROPTHOUGH if !value. */
3433 CFReleaseNull(intDesc
);
3434 CFReleaseNull(value
);
3435 appendInvalidProperty(properties
, label
, intSequenceContent
, localized
);
3438 static void appendCertificatePolicies(CFMutableArrayRef properties
,
3439 const DERItem
*extnValue
, bool localized
) {
3440 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3441 CFStringRef piLabel
= NULL
, piFmt
= NULL
, lpiLabel
= NULL
;
3442 CFStringRef pqLabel
= NULL
, pqFmt
= NULL
, lpqLabel
= NULL
;
3445 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
3446 require_noerr_quiet(drtn
, badDER
);
3447 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3448 DERDecodedInfo piContent
;
3450 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
3451 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3452 DERPolicyInformation pi
;
3453 drtn
= DERParseSequenceContent(&piContent
.content
,
3454 DERNumPolicyInformationItemSpecs
,
3455 DERPolicyInformationItemSpecs
,
3457 require_noerr_quiet(drtn
, badDER
);
3458 piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3459 SEC_POLICY_IDENTIFIER_KEY
, pin
);
3460 require_quiet(piLabel
, badDER
);
3461 piFmt
= (localized
) ?
3462 SecCopyCertString(SEC_POLICY_IDENTIFIER_KEY
) : SEC_POLICY_IDENTIFIER_KEY
;
3463 require_quiet(piFmt
, badDER
);
3464 lpiLabel
= CFStringCreateWithFormat(allocator
, NULL
, piFmt
, pin
++);
3465 require_quiet(lpiLabel
, badDER
);
3466 CFReleaseNull(piFmt
);
3467 appendOIDProperty(properties
, piLabel
, lpiLabel
,
3468 &pi
.policyIdentifier
, localized
);
3469 CFReleaseNull(piLabel
);
3470 CFReleaseNull(lpiLabel
);
3471 if (pi
.policyQualifiers
.length
== 0)
3475 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
3476 require_noerr_quiet(drtn
, badDER
);
3477 DERDecodedInfo pqContent
;
3479 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
3480 DERPolicyQualifierInfo pqi
;
3481 drtn
= DERParseSequenceContent(&pqContent
.content
,
3482 DERNumPolicyQualifierInfoItemSpecs
,
3483 DERPolicyQualifierInfoItemSpecs
,
3485 require_noerr_quiet(drtn
, badDER
);
3486 DERDecodedInfo qualifierContent
;
3487 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
3488 require_noerr_quiet(drtn
, badDER
);
3489 pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3490 SEC_POLICY_QUALIFIER_KEY
, pqn
);
3491 require_quiet(pqLabel
, badDER
);
3492 pqFmt
= (localized
) ?
3493 SecCopyCertString(SEC_POLICY_QUALIFIER_KEY
) : SEC_POLICY_QUALIFIER_KEY
;
3494 require_quiet(pqFmt
, badDER
);
3495 lpqLabel
= CFStringCreateWithFormat(allocator
, NULL
, pqFmt
, pqn
++);
3496 require_quiet(lpqLabel
, badDER
);
3497 CFReleaseNull(pqFmt
);
3498 appendOIDProperty(properties
, pqLabel
, lpqLabel
,
3499 &pqi
.policyQualifierID
, localized
);
3500 CFReleaseNull(pqLabel
);
3501 CFReleaseNull(lpqLabel
);
3502 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
3503 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
3504 appendURLContentProperty(properties
, SEC_CPS_URI_KEY
,
3505 &qualifierContent
.content
, localized
);
3506 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
3507 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3509 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
3510 DERNumUserNoticeItemSpecs
,
3511 DERUserNoticeItemSpecs
,
3513 require_noerr_quiet(drtn
, badDER
);
3514 if (un
.noticeRef
.length
) {
3515 DERNoticeReference nr
;
3516 drtn
= DERParseSequenceContent(&un
.noticeRef
,
3517 DERNumNoticeReferenceItemSpecs
,
3518 DERNoticeReferenceItemSpecs
,
3520 require_noerr_quiet(drtn
, badDER
);
3521 appendDERThingProperty(properties
,
3522 SEC_ORGANIZATION_KEY
, NULL
,
3523 &nr
.organization
, localized
);
3524 appendIntegerSequenceContent(properties
,
3525 SEC_NOTICE_NUMBERS_KEY
, &nr
.noticeNumbers
, localized
);
3527 if (un
.explicitText
.length
) {
3528 appendDERThingProperty(properties
, SEC_EXPLICIT_TEXT_KEY
,
3529 NULL
, &un
.explicitText
, localized
);
3532 appendUnparsedProperty(properties
, SEC_QUALIFIER_KEY
, NULL
,
3533 &pqi
.qualifier
, localized
);
3536 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3538 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3541 CFReleaseNull(piFmt
);
3542 CFReleaseNull(piLabel
);
3543 CFReleaseNull(lpiLabel
);
3544 CFReleaseNull(pqFmt
);
3545 CFReleaseNull(pqLabel
);
3546 CFReleaseNull(lpqLabel
);
3547 appendInvalidProperty(properties
, SEC_CERT_POLICIES_KEY
,
3548 extnValue
, localized
);
3551 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
3552 const DERItem
*extnValue
, bool localized
) {
3554 DERDecodedInfo keyIdentifier
;
3555 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
3556 require_noerr_quiet(drtn
, badDER
);
3557 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
3558 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3559 &keyIdentifier
.content
, localized
);
3563 appendInvalidProperty(properties
, SEC_SUBJ_KEY_ID_KEY
,
3564 extnValue
, localized
);
3568 AuthorityKeyIdentifier ::= SEQUENCE {
3569 keyIdentifier [0] KeyIdentifier OPTIONAL,
3570 authorityCertIssuer [1] GeneralNames OPTIONAL,
3571 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
3572 -- authorityCertIssuer and authorityCertSerialNumber MUST both
3573 -- be present or both be absent
3575 KeyIdentifier ::= OCTET STRING
3577 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
3578 const DERItem
*extnValue
, bool localized
) {
3579 DERAuthorityKeyIdentifier akid
;
3581 drtn
= DERParseSequence(extnValue
,
3582 DERNumAuthorityKeyIdentifierItemSpecs
,
3583 DERAuthorityKeyIdentifierItemSpecs
,
3584 &akid
, sizeof(akid
));
3585 require_noerr_quiet(drtn
, badDER
);
3586 if (akid
.keyIdentifier
.length
) {
3587 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3588 &akid
.keyIdentifier
, localized
);
3590 if (akid
.authorityCertIssuer
.length
||
3591 akid
.authorityCertSerialNumber
.length
) {
3592 require_quiet(akid
.authorityCertIssuer
.length
&&
3593 akid
.authorityCertSerialNumber
.length
, badDER
);
3594 /* Perhaps put in a subsection called Authority Certificate Issuer. */
3595 appendGeneralNamesContent(properties
,
3596 &akid
.authorityCertIssuer
, localized
);
3597 appendIntegerProperty(properties
, SEC_AUTH_CERT_SERIAL_KEY
,
3598 &akid
.authorityCertSerialNumber
, localized
);
3603 appendInvalidProperty(properties
, SEC_AUTHORITY_KEY_ID_KEY
,
3604 extnValue
, localized
);
3608 PolicyConstraints ::= SEQUENCE {
3609 requireExplicitPolicy [0] SkipCerts OPTIONAL,
3610 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
3612 SkipCerts ::= INTEGER (0..MAX)
3614 static void appendPolicyConstraints(CFMutableArrayRef properties
,
3615 const DERItem
*extnValue
, bool localized
) {
3616 DERPolicyConstraints pc
;
3618 drtn
= DERParseSequence(extnValue
,
3619 DERNumPolicyConstraintsItemSpecs
,
3620 DERPolicyConstraintsItemSpecs
,
3622 require_noerr_quiet(drtn
, badDER
);
3623 if (pc
.requireExplicitPolicy
.length
) {
3624 appendIntegerProperty(properties
, SEC_REQUIRE_EXPL_POLICY_KEY
,
3625 &pc
.requireExplicitPolicy
, localized
);
3627 if (pc
.inhibitPolicyMapping
.length
) {
3628 appendIntegerProperty(properties
, SEC_INHIBIT_POLICY_MAP_KEY
,
3629 &pc
.inhibitPolicyMapping
, localized
);
3635 appendInvalidProperty(properties
, SEC_POLICY_CONSTRAINTS_KEY
,
3636 extnValue
, localized
);
3640 extendedKeyUsage EXTENSION ::= {
3641 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
3642 IDENTIFIED BY id-ce-extKeyUsage }
3644 KeyPurposeId ::= OBJECT IDENTIFIER
3646 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
3647 const DERItem
*extnValue
, bool localized
) {
3650 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
3651 require_noerr_quiet(drtn
, badDER
);
3652 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3653 DERDecodedInfo currDecoded
;
3654 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3655 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
3656 appendOIDProperty(properties
, SEC_PURPOSE_KEY
, NULL
,
3657 &currDecoded
.content
, localized
);
3659 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3662 appendInvalidProperty(properties
, SEC_EXTENDED_KEY_USAGE_KEY
,
3663 extnValue
, localized
);
3667 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3669 AuthorityInfoAccessSyntax ::=
3670 SEQUENCE SIZE (1..MAX) OF AccessDescription
3672 AccessDescription ::= SEQUENCE {
3673 accessMethod OBJECT IDENTIFIER,
3674 accessLocation GeneralName }
3676 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3678 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3680 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3682 static void appendInfoAccess(CFMutableArrayRef properties
,
3683 const DERItem
*extnValue
, bool localized
) {
3686 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3687 require_noerr_quiet(drtn
, badDER
);
3688 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3689 DERDecodedInfo adContent
;
3690 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3691 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3692 DERAccessDescription ad
;
3693 drtn
= DERParseSequenceContent(&adContent
.content
,
3694 DERNumAccessDescriptionItemSpecs
,
3695 DERAccessDescriptionItemSpecs
,
3697 require_noerr_quiet(drtn
, badDER
);
3698 appendOIDProperty(properties
, SEC_ACCESS_METHOD_KEY
, NULL
,
3699 &ad
.accessMethod
, localized
);
3700 //TODO: Do something with SEC_ACCESS_LOCATION_KEY
3701 appendGeneralNameProperty(properties
, &ad
.accessLocation
, localized
);
3703 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3706 appendInvalidProperty(properties
, SEC_AUTH_INFO_ACCESS_KEY
,
3707 extnValue
, localized
);
3710 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3711 const DERItem
*extnValue
, bool localized
) {
3712 static const CFStringRef certTypes
[] = {
3716 SEC_OBJECT_SIGNING_KEY
,
3720 SEC_OBJECT_SIGNING_CA_KEY
3722 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3723 certTypes
, array_size(certTypes
), localized
);
3726 static bool appendPrintableDERSequence(CFMutableArrayRef properties
,
3727 CFStringRef label
, const DERItem
*sequence
, bool localized
) {
3730 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3731 require_noerr_quiet(drtn
, badSequence
);
3732 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3733 DERDecodedInfo currDecoded
;
3734 bool appendedSomething
= false;
3735 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3736 switch (currDecoded
.tag
)
3739 case ASN1_SEQUENCE
: // 16
3740 case ASN1_SET
: // 17
3741 // skip constructed object lengths
3744 case ASN1_UTF8_STRING
: // 12
3745 case ASN1_NUMERIC_STRING
: // 18
3746 case ASN1_PRINTABLE_STRING
: // 19
3747 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3748 case ASN1_VIDEOTEX_STRING
: // 21
3749 case ASN1_IA5_STRING
: // 22
3750 case ASN1_GRAPHIC_STRING
: // 25
3751 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3752 case ASN1_GENERAL_STRING
: // 27
3753 case ASN1_UNIVERSAL_STRING
: // 28
3755 CFStringRef string
=
3756 copyDERThingContentDescription(CFGetAllocator(properties
),
3757 currDecoded
.tag
, &currDecoded
.content
, false, localized
);
3758 require_quiet(string
, badSequence
);
3760 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3762 CFReleaseNull(string
);
3763 appendedSomething
= true;
3770 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3771 return appendedSomething
;
3776 static void appendExtension(CFMutableArrayRef parent
,
3777 const SecCertificateExtension
*extn
,
3779 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3780 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3781 &kCFTypeArrayCallBacks
);
3783 *extnID
= &extn
->extnID
,
3784 *extnValue
= &extn
->extnValue
;
3785 CFStringRef label
= NULL
;
3786 CFStringRef localizedLabel
= NULL
;
3788 appendBoolProperty(properties
, SEC_CRITICAL_KEY
, extn
->critical
, localized
);
3789 require_quiet(extnID
, xit
);
3791 bool handled
= true;
3792 /* Extensions that we know how to handle ourselves... */
3793 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3794 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3796 switch (extnID
->data
[extnID
->length
- 1]) {
3797 case 14: /* SubjectKeyIdentifier id-ce 14 */
3798 appendSubjectKeyIdentifier(properties
, extnValue
, localized
);
3800 case 15: /* KeyUsage id-ce 15 */
3801 appendKeyUsage(properties
, extnValue
, localized
);
3803 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3804 appendPrivateKeyUsagePeriod(properties
, extnValue
, localized
);
3806 case 17: /* SubjectAltName id-ce 17 */
3807 case 18: /* IssuerAltName id-ce 18 */
3808 appendGeneralNames(properties
, extnValue
, localized
);
3810 case 19: /* BasicConstraints id-ce 19 */
3811 appendBasicConstraints(properties
, extnValue
, localized
);
3813 case 30: /* NameConstraints id-ce 30 */
3814 appendNameConstraints(properties
, extnValue
, localized
);
3816 case 31: /* CRLDistributionPoints id-ce 31 */
3817 appendCrlDistributionPoints(properties
, extnValue
, localized
);
3819 case 32: /* CertificatePolicies id-ce 32 */
3820 appendCertificatePolicies(properties
, extnValue
, localized
);
3822 case 33: /* PolicyMappings id-ce 33 */
3825 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3826 appendAuthorityKeyIdentifier(properties
, extnValue
, localized
);
3828 case 36: /* PolicyConstraints id-ce 36 */
3829 appendPolicyConstraints(properties
, extnValue
, localized
);
3831 case 37: /* ExtKeyUsage id-ce 37 */
3832 appendExtendedKeyUsage(properties
, extnValue
, localized
);
3834 case 46: /* FreshestCRL id-ce 46 */
3837 case 54: /* InhibitAnyPolicy id-ce 54 */
3844 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3845 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3847 switch (extnID
->data
[extnID
->length
- 1]) {
3848 case 1: /* AuthorityInfoAccess id-pe 1 */
3849 appendInfoAccess(properties
, extnValue
, localized
);
3851 case 3: /* QCStatements id-pe 3 */
3854 case 11: /* SubjectInfoAccess id-pe 11 */
3855 appendInfoAccess(properties
, extnValue
, localized
);
3861 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3862 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3863 appendNetscapeCertType(properties
, extnValue
, localized
);
3869 /* Try to parse and display printable string(s). */
3870 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
, localized
)) {
3871 /* Nothing to do here appendPrintableDERSequence did the work. */
3873 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3874 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
, localized
);
3877 label
= SecDERItemCopyOIDDecimalRepresentation(allocator
, extnID
);
3878 localizedLabel
= copyOidDescription(allocator
, extnID
, localized
);
3879 appendProperty(parent
, kSecPropertyTypeSection
, label
, localizedLabel
,
3880 properties
, localized
);
3882 CFReleaseSafe(localizedLabel
);
3883 CFReleaseSafe(label
);
3884 CFReleaseSafe(properties
);
3887 /* Different types of summary types from least desired to most desired. */
3890 kSummaryTypePrintable
,
3891 kSummaryTypeOrganizationName
,
3892 kSummaryTypeOrganizationalUnitName
,
3893 kSummaryTypeCommonName
,
3897 enum SummaryType type
;
3898 CFStringRef summary
;
3899 CFStringRef description
;
3902 static OSStatus
obtainSummaryFromX501Name(void *context
,
3903 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
,
3905 struct Summary
*summary
= (struct Summary
*)context
;
3906 enum SummaryType stype
= kSummaryTypeNone
;
3907 CFStringRef string
= NULL
;
3908 if (DEROidCompare(type
, &oidCommonName
)) {
3909 stype
= kSummaryTypeCommonName
;
3910 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
3911 stype
= kSummaryTypeOrganizationalUnitName
;
3912 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
3913 stype
= kSummaryTypeOrganizationName
;
3914 } else if (DEROidCompare(type
, &oidDescription
)) {
3915 string
= copyDERThingDescription(kCFAllocatorDefault
, value
,
3918 if (summary
->description
) {
3919 CFStringRef fmt
= (localized
) ?
3920 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
3921 CFStringRef newDescription
= CFStringCreateWithFormat(kCFAllocatorDefault
,
3922 NULL
, fmt
, string
, summary
->description
);
3924 CFRelease(summary
->description
);
3925 summary
->description
= newDescription
;
3927 summary
->description
= string
;
3930 stype
= kSummaryTypePrintable
;
3933 stype
= kSummaryTypePrintable
;
3936 /* Build a string with all instances of the most desired
3937 component type in reverse order encountered comma separated list,
3938 The order of desirability is defined by enum SummaryType. */
3939 if (summary
->type
<= stype
) {
3941 string
= copyDERThingDescription(kCFAllocatorDefault
, value
,
3945 if (summary
->type
== stype
) {
3946 CFStringRef fmt
= (localized
) ?
3947 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
3948 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
,
3949 NULL
, fmt
, string
, summary
->summary
);
3952 string
= newSummary
;
3954 summary
->type
= stype
;
3956 CFReleaseSafe(summary
->summary
);
3957 summary
->summary
= string
;
3960 CFReleaseSafe(string
);
3963 return errSecSuccess
;
3966 CFStringRef
SecCertificateCopySubjectSummary(SecCertificateRef certificate
) {
3967 struct Summary summary
= {};
3968 OSStatus status
= parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
, true);
3969 if (status
!= errSecSuccess
) {
3972 /* If we found a description and a common name we change the summary to
3973 CommonName (Description). */
3974 if (summary
.description
) {
3975 if (summary
.type
== kSummaryTypeCommonName
) {
3976 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3977 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3979 CFRelease(summary
.summary
);
3980 summary
.summary
= newSummary
;
3982 CFRelease(summary
.description
);
3985 if (!summary
.summary
) {
3986 /* If we didn't find a suitable printable string in the subject at all, we try
3987 the first email address in the certificate instead. */
3988 CFArrayRef names
= SecCertificateCopyRFC822Names(certificate
);
3990 /* If we didn't find any email addresses in the certificate, we try finding
3991 a DNS name instead. */
3992 names
= SecCertificateCopyDNSNames(certificate
);
3995 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
3996 CFRetain(summary
.summary
);
4001 return summary
.summary
;
4004 CFStringRef
SecCertificateCopyIssuerSummary(SecCertificateRef certificate
) {
4005 struct Summary summary
= {};
4006 OSStatus status
= parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
, true);
4007 if (status
!= errSecSuccess
) {
4010 /* If we found a description and a common name we change the summary to
4011 CommonName (Description). */
4012 if (summary
.description
) {
4013 if (summary
.type
== kSummaryTypeCommonName
) {
4014 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
4015 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
4017 CFRelease(summary
.summary
);
4018 summary
.summary
= newSummary
;
4020 CFRelease(summary
.description
);
4023 return summary
.summary
;
4026 /* Return the earliest date on which all certificates in this chain are still
4028 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
4029 SecCertificateRef certificate
) {
4030 CFAbsoluteTime earliest
= certificate
->_notAfter
;
4032 while (certificate
->_parent
) {
4033 certificate
= certificate
->_parent
;
4034 if (earliest
> certificate
->_notAfter
)
4035 earliest
= certificate
->_notAfter
;
4042 /* Return the latest date on which all certificates in this chain will be
4044 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
4045 SecCertificateRef certificate
) {
4046 CFAbsoluteTime latest
= certificate
->_notBefore
;
4048 while (certificate
->_parent
) {
4049 certificate
= certificate
->_parent
;
4050 if (latest
< certificate
->_notBefore
)
4051 latest
= certificate
->_notBefore
;
4058 bool SecCertificateIsValid(SecCertificateRef certificate
,
4059 CFAbsoluteTime verifyTime
) {
4060 return certificate
&& certificate
->_notBefore
<= verifyTime
&&
4061 verifyTime
<= certificate
->_notAfter
;
4064 CFIndex
SecCertificateVersion(SecCertificateRef certificate
) {
4065 return certificate
->_version
+ 1;
4068 CFAbsoluteTime
SecCertificateNotValidBefore(SecCertificateRef certificate
) {
4069 return certificate
->_notBefore
;
4072 CFAbsoluteTime
SecCertificateNotValidAfter(SecCertificateRef certificate
) {
4073 return certificate
->_notAfter
;
4076 CFMutableArrayRef
SecCertificateCopySummaryProperties(
4077 SecCertificateRef certificate
, CFAbsoluteTime verifyTime
) {
4078 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4079 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
4080 &kCFTypeArrayCallBacks
);
4081 bool localized
= true;
4083 /* First we put the subject summary name. */
4084 CFStringRef ssummary
= SecCertificateCopySubjectSummary(certificate
);
4086 appendProperty(summary
, kSecPropertyTypeTitle
,
4087 NULL
, NULL
, ssummary
, localized
);
4088 CFRelease(ssummary
);
4091 /* Let see if this certificate is currently valid. */
4093 CFAbsoluteTime when
;
4094 CFStringRef message
;
4096 if (verifyTime
> certificate
->_notAfter
) {
4097 label
= SEC_EXPIRED_KEY
;
4098 when
= certificate
->_notAfter
;
4099 ptype
= kSecPropertyTypeError
;
4100 message
= SEC_CERT_EXPIRED_KEY
;
4101 } else if (certificate
->_notBefore
> verifyTime
) {
4102 label
= SEC_VALID_FROM_KEY
;
4103 when
= certificate
->_notBefore
;
4104 ptype
= kSecPropertyTypeError
;
4105 message
= SEC_CERT_NOT_YET_VALID_KEY
;
4107 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
4108 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
4109 if (verifyTime
> last
) {
4110 label
= SEC_EXPIRED_KEY
;
4112 ptype
= kSecPropertyTypeError
;
4113 message
= SEC_ISSUER_EXPIRED_KEY
;
4114 } else if (verifyTime
< first
) {
4115 label
= SEC_VALID_FROM_KEY
;
4117 ptype
= kSecPropertyTypeError
;
4118 message
= SEC_ISSR_NOT_YET_VALID_KEY
;
4120 label
= SEC_EXPIRES_KEY
;
4121 when
= certificate
->_notAfter
;
4122 ptype
= kSecPropertyTypeSuccess
;
4123 message
= SEC_CERT_VALID_KEY
;
4127 appendDateProperty(summary
, label
, when
, localized
);
4128 CFStringRef lmessage
= SecCopyCertString(message
);
4129 appendProperty(summary
, ptype
, NULL
, NULL
, lmessage
, localized
);
4130 CFRelease(lmessage
);
4135 CFArrayRef
SecCertificateCopyLegacyProperties(SecCertificateRef certificate
) {
4137 This function replicates the content returned by SecCertificateCopyProperties
4138 prior to 10.12.4, providing stable return values for SecCertificateCopyValues.
4139 Unlike SecCertificateCopyProperties, it does not cache the result and
4140 assumes the caller will do so.
4142 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4143 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
,
4144 0, &kCFTypeArrayCallBacks
);
4147 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
4148 &certificate
->_subject
, false);
4149 appendProperty(properties
, kSecPropertyTypeSection
, CFSTR("Subject Name"),
4150 NULL
, subject_plist
, false);
4151 CFRelease(subject_plist
);
4154 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
4155 &certificate
->_issuer
, false);
4156 appendProperty(properties
, kSecPropertyTypeSection
, CFSTR("Issuer Name"),
4157 NULL
, issuer_plist
, false);
4158 CFRelease(issuer_plist
);
4161 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
4162 NULL
, CFSTR("%d"), certificate
->_version
+ 1);
4163 appendProperty(properties
, kSecPropertyTypeString
, CFSTR("Version"),
4164 NULL
, versionString
, false);
4165 CFRelease(versionString
);
4168 if (certificate
->_serialNum
.length
) {
4169 appendIntegerProperty(properties
, CFSTR("Serial Number"),
4170 &certificate
->_serialNum
, false);
4173 /* Signature Algorithm */
4174 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
4175 &certificate
->_tbsSigAlg
, false);
4177 /* Validity dates */
4178 appendDateProperty(properties
, CFSTR("Not Valid Before"), certificate
->_notBefore
, false);
4179 appendDateProperty(properties
, CFSTR("Not Valid After"), certificate
->_notAfter
, false);
4181 if (certificate
->_subjectUniqueID
.length
) {
4182 appendDataProperty(properties
, CFSTR("Subject Unique ID"),
4183 NULL
, &certificate
->_subjectUniqueID
, false);
4185 if (certificate
->_issuerUniqueID
.length
) {
4186 appendDataProperty(properties
, CFSTR("Issuer Unique ID"),
4187 NULL
, &certificate
->_issuerUniqueID
, false);
4190 /* Public Key Algorithm */
4191 appendAlgorithmProperty(properties
, CFSTR("Public Key Algorithm"),
4192 &certificate
->_algId
, false);
4194 /* Public Key Data */
4195 appendDataProperty(properties
, CFSTR("Public Key Data"),
4196 NULL
, &certificate
->_pubKeyDER
, false);
4199 appendDataProperty(properties
, CFSTR("Signature"),
4200 NULL
, &certificate
->_signature
, false);
4204 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4205 appendExtension(properties
, &certificate
->_extensions
[ix
], false);
4209 appendFingerprintsProperty(properties
, CFSTR("Fingerprints"), certificate
, false);
4214 static CFArrayRef
CopyProperties(SecCertificateRef certificate
, Boolean localized
) {
4215 if (!certificate
->_properties
) {
4216 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4217 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
4218 &kCFTypeArrayCallBacks
);
4219 require_quiet(properties
, out
);
4221 /* First we put the Subject Name in the property list. */
4222 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
4223 &certificate
->_subject
,
4225 if (subject_plist
) {
4226 appendProperty(properties
, kSecPropertyTypeSection
,
4227 SEC_SUBJECT_NAME_KEY
, NULL
, subject_plist
, localized
);
4229 CFReleaseNull(subject_plist
);
4231 /* Next we put the Issuer Name in the property list. */
4232 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
4233 &certificate
->_issuer
,
4236 appendProperty(properties
, kSecPropertyTypeSection
,
4237 SEC_ISSUER_NAME_KEY
, NULL
, issuer_plist
, localized
);
4239 CFReleaseNull(issuer_plist
);
4242 CFStringRef fmt
= SecCopyCertString(SEC_CERT_VERSION_VALUE_KEY
);
4243 CFStringRef versionString
= NULL
;
4245 versionString
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
4246 certificate
->_version
+ 1);
4249 if (versionString
) {
4250 appendProperty(properties
, kSecPropertyTypeString
,
4251 SEC_VERSION_KEY
, NULL
, versionString
, localized
);
4253 CFReleaseNull(versionString
);
4256 appendSerialNumberProperty(properties
, SEC_SERIAL_NUMBER_KEY
, &certificate
->_serialNum
, localized
);
4258 /* Validity dates. */
4259 appendValidityPeriodProperty(properties
, SEC_VALIDITY_PERIOD_KEY
, certificate
, localized
);
4261 if (certificate
->_subjectUniqueID
.length
) {
4262 appendDataProperty(properties
, SEC_SUBJECT_UNIQUE_ID_KEY
, NULL
,
4263 &certificate
->_subjectUniqueID
, localized
);
4265 if (certificate
->_issuerUniqueID
.length
) {
4266 appendDataProperty(properties
, SEC_ISSUER_UNIQUE_ID_KEY
, NULL
,
4267 &certificate
->_issuerUniqueID
, localized
);
4270 appendPublicKeyProperty(properties
, SEC_PUBLIC_KEY_KEY
, certificate
, localized
);
4273 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4274 appendExtension(properties
, &certificate
->_extensions
[ix
], localized
);
4278 appendSignatureProperty(properties
, SEC_SIGNATURE_KEY
, certificate
, localized
);
4280 appendFingerprintsProperty(properties
, SEC_FINGERPRINTS_KEY
, certificate
, localized
);
4282 certificate
->_properties
= properties
;
4286 CFRetainSafe(certificate
->_properties
);
4287 return certificate
->_properties
;
4290 CFArrayRef
SecCertificateCopyProperties(SecCertificateRef certificate
) {
4292 Wrapper function which defaults to localized string properties
4293 for compatibility with prior releases.
4295 return CopyProperties(certificate
, true);
4298 CFArrayRef
SecCertificateCopyLocalizedProperties(SecCertificateRef certificate
, Boolean localized
) {
4300 Wrapper function which permits caller to specify whether
4301 localized string properties are used.
4303 return CopyProperties(certificate
, localized
);
4306 /* Unified serial number API */
4307 CFDataRef
SecCertificateCopySerialNumberData(
4308 SecCertificateRef certificate
,
4313 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
4317 if (certificate
->_serialNumber
) {
4318 CFRetain(certificate
->_serialNumber
);
4320 return certificate
->_serialNumber
;
4323 #if TARGET_OS_OSX && TARGET_CPU_ARM64
4324 /* force this implementation to be _SecCertificateCopySerialNumber on arm64 macOS.
4325 note: the legacy function in SecCertificate.cpp is now _SecCertificateCopySerialNumber$LEGACYMAC
4326 when both TARGET_OS_OSX and TARGET_CPU_ARM64 are true.
4328 extern CFDataRef
SecCertificateCopySerialNumber_ios(SecCertificateRef certificate
) __asm("_SecCertificateCopySerialNumber");
4329 CFDataRef
SecCertificateCopySerialNumber_ios(SecCertificateRef certificate
) {
4330 return SecCertificateCopySerialNumberData(certificate
, NULL
);
4335 CFDataRef
SecCertificateCopySerialNumber(SecCertificateRef certificate
)
4337 return SecCertificateCopySerialNumberData(certificate
, NULL
);
4341 CFDataRef
SecCertificateGetNormalizedIssuerContent(
4342 SecCertificateRef certificate
) {
4343 return certificate
->_normalizedIssuer
;
4346 CFDataRef
SecCertificateGetNormalizedSubjectContent(
4347 SecCertificateRef certificate
) {
4348 return certificate
->_normalizedSubject
;
4351 /* Verify that certificate was signed by issuerKey. */
4352 OSStatus
SecCertificateIsSignedBy(SecCertificateRef certificate
,
4353 SecKeyRef issuerKey
) {
4354 #pragma clang diagnostic push
4355 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
4356 /* Setup algId in SecAsn1AlgId format. */
4358 #pragma clang diagnostic pop
4359 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
4360 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
4361 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
4362 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
4364 /* RFC5280 4.1.1.2, 4.1.2.3 requires the actual signature algorithm
4365 must match the specified algorithm in the TBSCertificate. */
4366 bool sigAlgMatch
= DEROidCompare(&certificate
->_sigAlg
.oid
,
4367 &certificate
->_tbsSigAlg
.oid
);
4369 secwarning("Signature algorithm mismatch in certificate (see RFC5280 4.1.1.2)");
4372 CFErrorRef error
= NULL
;
4374 !SecVerifySignatureWithPublicKey(issuerKey
, &algId
,
4375 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
4376 certificate
->_signature
.data
, certificate
->_signature
.length
, &error
))
4378 #if !defined(NDEBUG)
4379 secdebug("verify", "signature verify failed: %" PRIdOSStatus
, (error
) ? (OSStatus
)CFErrorGetCode(error
) : errSecNotSigner
);
4381 CFReleaseSafe(error
);
4382 return errSecNotSigner
;
4385 return errSecSuccess
;
4388 const DERItem
* SecCertificateGetSubjectAltName(SecCertificateRef certificate
) {
4389 if (!certificate
->_subjectAltName
) {
4392 return &certificate
->_subjectAltName
->extnValue
;
4395 /* Convert IPv4 address string to canonical data format (4 bytes) */
4396 static bool convertIPv4Address(CFStringRef name
, CFDataRef
*dataIP
) {
4397 /* IPv4: 4 octets in decimal separated by dots. */
4398 bool result
= false;
4400 if (CFStringGetLength(name
) < 7 || /* min size is #.#.#.# */
4401 CFStringGetLength(name
) > 15) { /* max size is ###.###.###.### */
4405 CFCharacterSetRef allowed
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789."));
4406 CFCharacterSetRef disallowed
= CFCharacterSetCreateInvertedSet(NULL
, allowed
);
4407 CFMutableDataRef data
= CFDataCreateMutable(NULL
, 0);
4408 CFArrayRef parts
= CFStringCreateArrayBySeparatingStrings(NULL
, name
, CFSTR("."));
4409 CFIndex i
, count
= (parts
) ? CFArrayGetCount(parts
) : 0;
4411 /* Check character set */
4412 if (CFStringFindCharacterFromSet(name
, disallowed
,
4413 CFRangeMake(0, CFStringGetLength(name
)),
4414 kCFCompareForcedOrdering
, NULL
)) {
4418 /* Check number of labels */
4419 if (CFArrayGetCount(parts
) != 4) {
4423 /* Check each label and convert */
4424 for (i
= 0; i
< count
; i
++) {
4425 CFStringRef octet
= (CFStringRef
) CFArrayGetValueAtIndex(parts
, i
);
4426 char *cString
= CFStringToCString(octet
);
4427 uint32_t value
= atoi(cString
);
4432 uint8_t byte
= value
;
4433 CFDataAppendBytes(data
, &byte
, 1);
4438 *dataIP
= (CFDataRef
) CFRetain(data
);
4442 CFReleaseNull(data
);
4443 CFReleaseNull(parts
);
4444 CFReleaseNull(allowed
);
4445 CFReleaseNull(disallowed
);
4449 /* Convert IPv6 address string to canonical data format (16 bytes) */
4450 static bool convertIPv6Address(CFStringRef name
, CFDataRef
*dataIP
) {
4451 /* IPv6: 8 16-bit fields with colon delimiters. */
4452 /* Note: we don't support conversion of hybrid IPv4-mapped addresses here. */
4453 bool result
= false;
4454 CFMutableStringRef addr
= NULL
;
4455 CFIndex length
= (name
) ? CFStringGetLength(name
) : 0;
4456 /* Sanity check size */
4457 if (length
< 2 || /* min size is '::' */
4458 length
> 41) { /* max size is '[####:####:####:####:####:####:####:####]' */
4461 /* Remove literal brackets, if present */
4462 if (CFStringHasPrefix(name
, CFSTR("[")) && CFStringHasSuffix(name
, CFSTR("]"))) {
4463 CFStringRef tmpName
= CFStringCreateWithSubstring(NULL
, name
, CFRangeMake(1, length
-2));
4465 addr
= CFStringCreateMutableCopy(NULL
, 0, tmpName
);
4470 addr
= CFStringCreateMutableCopy(NULL
, 0, name
);
4472 CFStringUppercase(addr
, CFLocaleGetSystem());
4474 CFCharacterSetRef allowed
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789ABCDEF:"));
4475 CFCharacterSetRef disallowed
= CFCharacterSetCreateInvertedSet(NULL
, allowed
);
4476 CFMutableDataRef data
= CFDataCreateMutable(NULL
, 0);
4477 CFArrayRef parts
= CFStringCreateArrayBySeparatingStrings(NULL
, addr
, CFSTR(":"));
4478 CFIndex i
, count
= (parts
) ? CFArrayGetCount(parts
) : 0;
4480 /* Check character set */
4481 if (CFStringFindCharacterFromSet(addr
, disallowed
,
4482 CFRangeMake(0, CFStringGetLength(addr
)),
4483 kCFCompareForcedOrdering
, NULL
)) {
4487 /* Check number of fields (no fewer than 3, no more than 8) */
4488 if (CFArrayGetCount(parts
) < 3 || CFArrayGetCount(parts
) > 8) {
4492 /* Check each field and convert to network-byte-order value */
4493 for (i
= 0; i
< count
; i
++) {
4494 uint16_t svalue
= 0;
4495 CFStringRef fieldValue
= (CFStringRef
) CFArrayGetValueAtIndex(parts
, i
);
4496 char *cString
= CFStringToCString(fieldValue
);
4497 length
= (cString
) ? strlen(cString
) : 0;
4499 /* empty value indicates one or more zeros in the address */
4500 if (i
== 0 || i
== count
-1) { /* leading or trailing part of '::' */
4501 CFDataAppendBytes(data
, (const UInt8
*)&svalue
, 2);
4502 } else { /* determine how many fields are missing, then zero-fill */
4503 CFIndex z
, missing
= (8 - count
) + 1;
4504 for (z
= 0; z
< missing
; z
++) {
4505 CFDataAppendBytes(data
, (const UInt8
*)&svalue
, 2);
4508 } else if (length
<= 4) {
4509 /* valid field value is 4 characters or less */
4510 unsigned long value
= strtoul(cString
, NULL
, 16);
4511 svalue
= htons(value
& 0xFFFF);
4512 CFDataAppendBytes(data
, (const UInt8
*)&svalue
, 2);
4516 if (CFDataGetLength(data
) != IPv6ADDRLEN
) {
4517 goto out
; /* after expansion, data must be exactly 16 bytes */
4522 *dataIP
= (CFDataRef
) CFRetain(data
);
4526 CFReleaseNull(data
);
4527 CFReleaseNull(parts
);
4528 CFReleaseNull(allowed
);
4529 CFReleaseNull(disallowed
);
4530 CFReleaseNull(addr
);
4534 static bool convertIPAddress(CFStringRef string
, CFDataRef
*dataIP
) {
4535 if (NULL
== string
) {
4538 if (convertIPv4Address(string
, dataIP
) ||
4539 convertIPv6Address(string
, dataIP
)) {
4545 bool SecFrameworkIsIPAddress(CFStringRef string
) {
4546 return convertIPAddress(string
, NULL
);
4549 CFDataRef
SecFrameworkCopyIPAddressData(CFStringRef string
) {
4550 CFDataRef data
= NULL
;
4551 if (!convertIPAddress(string
, &data
)) {
4557 static OSStatus
appendIPAddressesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4558 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
4559 if (gnType
== GNT_IPAddress
) {
4560 if (generalName
->length
== IPv4ADDRLEN
|| generalName
->length
== IPv6ADDRLEN
) {
4561 CFDataRef address
= CFDataCreate(NULL
, generalName
->data
, generalName
->length
);
4562 CFArrayAppendValue(ipAddresses
, address
);
4563 CFReleaseNull(address
);
4565 return errSecInvalidCertificate
;
4568 return errSecSuccess
;
4571 CFArrayRef
SecCertificateCopyIPAddresses(SecCertificateRef certificate
) {
4572 CFArrayRef ipAddresses
= SecCertificateCopyIPAddressDatas(certificate
);
4577 // Convert data IP addresses to strings
4578 __block CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4579 CFArrayForEach(ipAddresses
, ^(const void *value
) {
4580 CFDataRef address
= (CFDataRef
)value
;
4581 DERItem der_address
= { (unsigned char *)CFDataGetBytePtr(address
), CFDataGetLength(address
) };
4582 CFStringRef string
= copyIPAddressContentDescription(NULL
, &der_address
);
4584 CFArrayAppendValue(result
, string
);
4588 CFReleaseNull(ipAddresses
);
4589 if (CFArrayGetCount(result
) == 0) {
4590 CFReleaseNull(result
);
4596 CFArrayRef
SecCertificateCopyIPAddressDatas(SecCertificateRef certificate
) {
4597 /* These can only exist in the subject alt name. */
4598 if (!certificate
->_subjectAltName
)
4601 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
4602 0, &kCFTypeArrayCallBacks
);
4603 OSStatus status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4604 ipAddresses
, appendIPAddressesFromGeneralNames
);
4605 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
4606 CFRelease(ipAddresses
);
4612 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
4613 const DERItem
*generalName
) {
4614 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4615 if (gnType
== GNT_DNSName
) {
4616 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4617 generalName
->data
, generalName
->length
,
4618 kCFStringEncodingUTF8
, FALSE
);
4620 CFArrayAppendValue(dnsNames
, string
);
4623 return errSecInvalidCertificate
;
4626 return errSecSuccess
;
4629 /* Return true if the passed in string matches the
4630 Preferred name syntax from sections 2.3.1. in RFC 1035.
4631 With the added check that we disallow empty dns names.
4632 Also in order to support wildcard DNSNames we allow for the '*'
4633 character anywhere in a dns component where we currently allow
4636 <domain> ::= <subdomain> | " "
4638 <subdomain> ::= <label> | <subdomain> "." <label>
4640 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
4642 RFC 3696 redefined labels as:
4643 <label> ::= <let-dig> [ [ <ldh-str> ] <let-dig> ]
4644 with the caveat that the highest-level labels is never all-numeric.
4646 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
4648 <let-dig-hyp> ::= <let-dig> | "-"
4650 <let-dig> ::= <letter> | <digit>
4652 <letter> ::= any one of the 52 alphabetic characters A through Z in
4653 upper case and a through z in lower case
4655 <digit> ::= any one of the ten digits 0 through 9
4657 bool SecFrameworkIsDNSName(CFStringRef string
) {
4658 CFStringInlineBuffer buf
= {};
4659 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
4660 /* From RFC 1035 2.3.4. Size limits:
4661 labels 63 octets or less
4662 names 255 octets or less */
4663 require_quiet(length
<= 255, notDNS
);
4664 CFRange range
= { 0, length
};
4665 CFStringInitInlineBuffer(string
, &buf
, range
);
4669 kDNSStateAfterAlpha
,
4670 kDNSStateAfterDigit
,
4672 } state
= kDNSStateInital
;
4673 Boolean labelHasAlpha
= false;
4675 for (ix
= 0; ix
< length
; ++ix
) {
4676 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
4679 require_quiet(labelLength
<= 64 &&
4680 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4682 state
= kDNSStateAfterDot
;
4683 labelHasAlpha
= false;
4685 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
4687 state
= kDNSStateAfterAlpha
;
4688 labelHasAlpha
= true;
4689 } else if ('0' <= ch
&& ch
<= '9') {
4690 state
= kDNSStateAfterDigit
;
4691 } else if (ch
== '-') {
4692 require_quiet(state
== kDNSStateAfterAlpha
||
4693 state
== kDNSStateAfterDigit
||
4694 state
== kDNSStateAfterDash
, notDNS
);
4695 state
= kDNSStateAfterDash
;
4701 /* We don't allow a dns name to end in a dot or dash. */
4702 require_quiet(labelLength
<= 63 &&
4703 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4706 /* Additionally, the rightmost label must have letters in it. */
4707 require_quiet(labelHasAlpha
== true, notDNS
);
4714 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4715 const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4716 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4717 if (DEROidCompare(type
, &oidCommonName
)) {
4718 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4719 value
, true, localized
);
4721 if (SecFrameworkIsDNSName(string
)) {
4722 /* We found a common name that is formatted like a valid
4724 CFArrayAppendValue(dnsNames
, string
);
4728 return errSecInvalidCertificate
;
4731 return errSecSuccess
;
4734 static CF_RETURNS_RETAINED CFArrayRef
filterIPAddresses(CFArrayRef CF_CONSUMED dnsNames
)
4736 __block CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4737 CFArrayForEach(dnsNames
, ^(const void *value
) {
4738 CFStringRef name
= (CFStringRef
)value
;
4739 if (!SecFrameworkIsIPAddress(name
)) {
4740 CFArrayAppendValue(result
, name
);
4743 CFReleaseNull(dnsNames
);
4744 if (CFArrayGetCount(result
) == 0) {
4745 CFReleaseNull(result
);
4751 CFArrayRef
SecCertificateCopyDNSNamesFromSAN(SecCertificateRef certificate
) {
4752 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4753 0, &kCFTypeArrayCallBacks
);
4754 if (certificate
->_subjectAltName
) {
4755 if (SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4756 dnsNames
, appendDNSNamesFromGeneralNames
) != errSecSuccess
) {
4757 CFReleaseNull(dnsNames
);
4762 /* appendDNSNamesFromGeneralNames allows IP addresses, we don't want those for this function */
4763 return filterIPAddresses(dnsNames
);
4766 /* Not everything returned by this function is going to be a proper DNS name,
4767 we also return the certificates common name entries from the subject,
4768 assuming they look like dns names as specified in RFC 1035.
4770 To preserve bug for bug compatibility, we can't use SecCertificateCopyDNSNamesFromSAN
4771 because that function filters out IP Addresses. This function is Private, but
4772 SecCertificateCopyValues uses it and that's Public. */
4773 CFArrayRef
SecCertificateCopyDNSNames(SecCertificateRef certificate
) {
4774 /* RFC 2818 section 3.1. Server Identity
4776 If a subjectAltName extension of type dNSName is present, that MUST
4777 be used as the identity. Otherwise, the (most specific) Common Name
4778 field in the Subject field of the certificate MUST be used. Although
4779 the use of the Common Name is existing practice, it is deprecated and
4780 Certification Authorities are encouraged to use the dNSName instead.
4783 This implies that if we found 1 or more DNSNames in the
4784 subjectAltName, we should not use the Common Name of the subject as
4788 /* return SAN DNS names if they exist */
4789 if (certificate
->_subjectAltName
) {
4790 CFMutableArrayRef sanNames
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4791 OSStatus status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4792 sanNames
, appendDNSNamesFromGeneralNames
);
4793 if (status
== errSecSuccess
&& sanNames
&& CFArrayGetCount(sanNames
) > 0) {
4796 CFReleaseNull(sanNames
);
4799 /* fall back to return DNS names in the Common Name */
4800 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4801 0, &kCFTypeArrayCallBacks
);
4802 OSStatus status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4803 appendDNSNamesFromX501Name
, true);
4804 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4805 CFReleaseNull(dnsNames
);
4810 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4811 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4812 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4813 if (gnType
== GNT_RFC822Name
) {
4814 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4815 generalName
->data
, generalName
->length
,
4816 kCFStringEncodingASCII
, FALSE
);
4818 CFArrayAppendValue(dnsNames
, string
);
4821 return errSecInvalidCertificate
;
4824 return errSecSuccess
;
4827 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4828 const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4829 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4830 if (DEROidCompare(type
, &oidEmailAddress
)) {
4831 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4832 value
, true, localized
);
4834 CFArrayAppendValue(dnsNames
, string
);
4837 return errSecInvalidCertificate
;
4840 return errSecSuccess
;
4843 CFArrayRef
SecCertificateCopyRFC822Names(SecCertificateRef certificate
) {
4844 /* These can exist in the subject alt name or in the subject. */
4845 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4846 0, &kCFTypeArrayCallBacks
);
4847 OSStatus status
= errSecSuccess
;
4848 if (certificate
->_subjectAltName
) {
4849 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4850 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4853 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4854 appendRFC822NamesFromX501Name
, true);
4856 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4857 CFRelease(rfc822Names
);
4863 OSStatus
SecCertificateCopyEmailAddresses(SecCertificateRef certificate
, CFArrayRef
* __nonnull CF_RETURNS_RETAINED emailAddresses
) {
4864 if (!certificate
|| !emailAddresses
) {
4867 *emailAddresses
= SecCertificateCopyRFC822Names(certificate
);
4868 if (*emailAddresses
== NULL
) {
4869 *emailAddresses
= CFArrayCreate(NULL
, NULL
, 0, &kCFTypeArrayCallBacks
);
4871 return errSecSuccess
;
4874 CFArrayRef
SecCertificateCopyRFC822NamesFromSubject(SecCertificateRef certificate
) {
4875 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4876 0, &kCFTypeArrayCallBacks
);
4877 OSStatus status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4878 appendRFC822NamesFromX501Name
, true);
4879 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4880 CFRelease(rfc822Names
);
4886 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4887 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4888 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4889 if (DEROidCompare(type
, &oidCommonName
)) {
4890 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4891 value
, true, localized
);
4893 CFArrayAppendValue(commonNames
, string
);
4896 return errSecInvalidCertificate
;
4899 return errSecSuccess
;
4902 CFArrayRef
SecCertificateCopyCommonNames(SecCertificateRef certificate
) {
4903 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4904 0, &kCFTypeArrayCallBacks
);
4906 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4907 appendCommonNamesFromX501Name
, true);
4908 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4909 CFRelease(commonNames
);
4915 OSStatus
SecCertificateCopyCommonName(SecCertificateRef certificate
, CFStringRef
*commonName
)
4920 CFArrayRef commonNames
= SecCertificateCopyCommonNames(certificate
);
4922 return errSecInternal
;
4926 CFIndex count
= CFArrayGetCount(commonNames
);
4927 *commonName
= CFRetainSafe(CFArrayGetValueAtIndex(commonNames
, count
-1));
4929 CFReleaseSafe(commonNames
);
4930 return errSecSuccess
;
4933 static OSStatus
appendOrganizationFromX501Name(void *context
,
4934 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4935 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4936 if (DEROidCompare(type
, &oidOrganizationName
)) {
4937 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4938 value
, true, localized
);
4940 CFArrayAppendValue(organization
, string
);
4943 return errSecInvalidCertificate
;
4946 return errSecSuccess
;
4949 CFArrayRef
SecCertificateCopyOrganizationFromX501NameContent(const DERItem
*nameContent
) {
4950 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4951 0, &kCFTypeArrayCallBacks
);
4953 status
= parseX501NameContent(nameContent
, organization
,
4954 appendOrganizationFromX501Name
, true);
4955 if (status
|| CFArrayGetCount(organization
) == 0) {
4956 CFRelease(organization
);
4957 organization
= NULL
;
4959 return organization
;
4962 CFArrayRef
SecCertificateCopyOrganization(SecCertificateRef certificate
) {
4963 return SecCertificateCopyOrganizationFromX501NameContent(&certificate
->_subject
);
4966 static OSStatus
appendOrganizationalUnitFromX501Name(void *context
,
4967 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4968 CFMutableArrayRef organizationalUnit
= (CFMutableArrayRef
)context
;
4969 if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4970 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4971 value
, true, localized
);
4973 CFArrayAppendValue(organizationalUnit
, string
);
4976 return errSecInvalidCertificate
;
4979 return errSecSuccess
;
4982 CFArrayRef
SecCertificateCopyOrganizationalUnit(SecCertificateRef certificate
) {
4983 CFMutableArrayRef organizationalUnit
= CFArrayCreateMutable(kCFAllocatorDefault
,
4984 0, &kCFTypeArrayCallBacks
);
4986 status
= parseX501NameContent(&certificate
->_subject
, organizationalUnit
,
4987 appendOrganizationalUnitFromX501Name
, true);
4988 if (status
|| CFArrayGetCount(organizationalUnit
) == 0) {
4989 CFRelease(organizationalUnit
);
4990 organizationalUnit
= NULL
;
4992 return organizationalUnit
;
4995 static OSStatus
appendCountryFromX501Name(void *context
,
4996 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4997 CFMutableArrayRef countries
= (CFMutableArrayRef
)context
;
4998 if (DEROidCompare(type
, &oidCountryName
)) {
4999 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
5000 value
, true, localized
);
5002 CFArrayAppendValue(countries
, string
);
5005 return errSecInvalidCertificate
;
5008 return errSecSuccess
;
5011 CFArrayRef
SecCertificateCopyCountry(SecCertificateRef certificate
) {
5012 CFMutableArrayRef countries
= CFArrayCreateMutable(kCFAllocatorDefault
,
5013 0, &kCFTypeArrayCallBacks
);
5015 status
= parseX501NameContent(&certificate
->_subject
, countries
,
5016 appendCountryFromX501Name
, true);
5017 if (status
|| CFArrayGetCount(countries
) == 0) {
5018 CFRelease(countries
);
5025 DERItem
*attributeOID
;
5026 CFStringRef
*result
;
5029 static OSStatus
copyAttributeValueFromX501Name(void *context
, const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5030 ATV_Context
*ctx
= (ATV_Context
*)context
;
5031 if (DEROidCompare(type
, ctx
->attributeOID
)) {
5032 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true, localized
);
5034 CFAssignRetained(*ctx
->result
, string
);
5036 return errSecInvalidCertificate
;
5039 return errSecSuccess
;
5042 CFStringRef
SecCertificateCopySubjectAttributeValue(SecCertificateRef cert
, DERItem
*attributeOID
) {
5043 CFStringRef result
= NULL
;
5044 ATV_Context context
= {
5045 .attributeOID
= attributeOID
,
5048 OSStatus status
= parseX501NameContent(&cert
->_subject
, &context
, copyAttributeValueFromX501Name
, false);
5050 CFReleaseNull(result
);
5055 const SecCEBasicConstraints
*
5056 SecCertificateGetBasicConstraints(SecCertificateRef certificate
) {
5057 if (certificate
->_basicConstraints
.present
)
5058 return &certificate
->_basicConstraints
;
5063 CFArrayRef
SecCertificateGetPermittedSubtrees(SecCertificateRef certificate
) {
5064 return (certificate
->_permittedSubtrees
);
5067 CFArrayRef
SecCertificateGetExcludedSubtrees(SecCertificateRef certificate
) {
5068 return (certificate
->_excludedSubtrees
);
5071 const SecCEPolicyConstraints
*
5072 SecCertificateGetPolicyConstraints(SecCertificateRef certificate
) {
5073 if (certificate
->_policyConstraints
.present
)
5074 return &certificate
->_policyConstraints
;
5079 const SecCEPolicyMappings
*
5080 SecCertificateGetPolicyMappings(SecCertificateRef certificate
) {
5081 if (certificate
->_policyMappings
.present
) {
5082 return &certificate
->_policyMappings
;
5088 const SecCECertificatePolicies
*
5089 SecCertificateGetCertificatePolicies(SecCertificateRef certificate
) {
5090 if (certificate
->_certificatePolicies
.present
)
5091 return &certificate
->_certificatePolicies
;
5096 const SecCEInhibitAnyPolicy
*
5097 SecCertificateGetInhibitAnyPolicySkipCerts(SecCertificateRef certificate
) {
5098 if (certificate
->_inhibitAnyPolicySkipCerts
.present
) {
5099 return &certificate
->_inhibitAnyPolicySkipCerts
;
5105 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
5106 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
5107 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
5108 if (gnType
== GNT_OtherName
) {
5110 DERReturn drtn
= DERParseSequenceContent(generalName
,
5111 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
5113 require_noerr_quiet(drtn
, badDER
);
5114 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
5116 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
5117 &on
.value
, true, true), badDER
);
5118 CFArrayAppendValue(ntPrincipalNames
, string
);
5122 return errSecSuccess
;
5125 return errSecInvalidCertificate
;
5129 CFArrayRef
SecCertificateCopyNTPrincipalNames(SecCertificateRef certificate
) {
5130 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
5131 0, &kCFTypeArrayCallBacks
);
5132 OSStatus status
= errSecSuccess
;
5133 if (certificate
->_subjectAltName
) {
5134 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
5135 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
5137 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
5138 CFRelease(ntPrincipalNames
);
5139 ntPrincipalNames
= NULL
;
5141 return ntPrincipalNames
;
5144 static OSStatus
appendToRFC2253String(void *context
,
5145 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5146 CFMutableStringRef string
= (CFMutableStringRef
)context
;
5150 ST stateOrProvinceName
5152 OU organizationalUnitName
5154 STREET streetAddress
5158 /* Prepend a + if this is not the first RDN in an RDN set.
5159 Otherwise prepend a , if this is not the first RDN. */
5161 CFStringAppend(string
, CFSTR("+"));
5162 else if (CFStringGetLength(string
)) {
5163 CFStringAppend(string
, CFSTR(","));
5166 CFStringRef label
, oid
= NULL
;
5167 /* @@@ Consider changing this to a dictionary lookup keyed by the
5168 decimal representation. */
5169 if (DEROidCompare(type
, &oidCommonName
)) {
5170 label
= CFSTR("CN");
5171 } else if (DEROidCompare(type
, &oidLocalityName
)) {
5173 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
5174 label
= CFSTR("ST");
5175 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
5177 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
5178 label
= CFSTR("OU");
5179 } else if (DEROidCompare(type
, &oidCountryName
)) {
5182 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
5183 label
= CFSTR("STREET");
5184 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
5185 label
= CFSTR("DC");
5186 } else if (DEROidCompare(type
, &oidUserID
)) {
5187 label
= CFSTR("UID");
5190 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
5193 CFStringAppend(string
, label
);
5194 CFStringAppend(string
, CFSTR("="));
5195 CFStringRef raw
= NULL
;
5197 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true, localized
);
5200 /* Append raw to string while escaping:
5201 a space or "#" character occurring at the beginning of the string
5202 a space character occurring at the end of the string
5203 one of the characters ",", "+", """, "\", "<", ">" or ";"
5205 CFStringInlineBuffer buffer
= {};
5206 CFIndex ix
, length
= CFStringGetLength(raw
);
5207 CFRange range
= { 0, length
};
5208 CFStringInitInlineBuffer(raw
, &buffer
, range
);
5209 for (ix
= 0; ix
< length
; ++ix
) {
5210 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
5212 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
5213 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
5214 ch
== '<' || ch
== '>' || ch
== ';' ||
5215 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
5216 (ch
== '#' && ix
== 0)) {
5217 UniChar chars
[] = { '\\', ch
};
5218 CFStringAppendCharacters(string
, chars
, 2);
5220 CFStringAppendCharacters(string
, &ch
, 1);
5225 /* Append the value in hex. */
5226 CFStringAppend(string
, CFSTR("#"));
5228 for (ix
= 0; ix
< value
->length
; ++ix
)
5229 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
5234 return errSecSuccess
;
5237 CFStringRef
SecCertificateCopySubjectString(SecCertificateRef certificate
) {
5238 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
5239 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
, true);
5240 if (status
|| CFStringGetLength(string
) == 0) {
5247 static OSStatus
appendToCompanyNameString(void *context
,
5248 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5249 CFMutableStringRef string
= (CFMutableStringRef
)context
;
5250 if (CFStringGetLength(string
) != 0)
5251 return errSecSuccess
;
5253 if (!DEROidCompare(type
, &oidOrganizationName
))
5254 return errSecSuccess
;
5257 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true, localized
);
5259 return errSecSuccess
;
5260 CFStringAppend(string
, raw
);
5263 return errSecSuccess
;
5266 CFStringRef
SecCertificateCopyCompanyName(SecCertificateRef certificate
) {
5267 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
5268 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
5269 appendToCompanyNameString
, true);
5270 if (status
|| CFStringGetLength(string
) == 0) {
5277 CFDataRef
SecCertificateCopyIssuerSequence(
5278 SecCertificateRef certificate
) {
5279 return SecDERItemCopySequence(&certificate
->_issuer
);
5282 CFDataRef
SecCertificateCopySubjectSequence(
5283 SecCertificateRef certificate
) {
5284 return SecDERItemCopySequence(&certificate
->_subject
);
5287 CFDataRef
SecCertificateCopyNormalizedIssuerSequence(SecCertificateRef certificate
) {
5288 if (!certificate
|| !certificate
->_normalizedIssuer
) {
5291 return SecCopySequenceFromContent(certificate
->_normalizedIssuer
);
5294 CFDataRef
SecCertificateCopyNormalizedSubjectSequence(SecCertificateRef certificate
) {
5295 if (!certificate
|| !certificate
->_normalizedSubject
) {
5298 return SecCopySequenceFromContent(certificate
->_normalizedSubject
);
5301 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithm(
5302 SecCertificateRef certificate
) {
5303 return &certificate
->_algId
;
5306 const DERItem
*SecCertificateGetPublicKeyData(SecCertificateRef certificate
) {
5307 return &certificate
->_pubKeyDER
;
5311 #if TARGET_CPU_ARM64
5312 /* force this implementation to be _SecCertificateCopyPublicKey on arm64 macOS.
5313 note: the legacy function in SecCertificate.cpp is now _SecCertificateCopyPublicKey$LEGACYMAC
5314 when both TARGET_OS_OSX and TARGET_CPU_ARM64 are true.
5316 extern __nullable SecKeyRef
SecCertificateCopyPublicKey_ios(SecCertificateRef certificate
) __asm("_SecCertificateCopyPublicKey");
5317 #endif /* TARGET_CPU_ARM64 */
5318 __nullable SecKeyRef
SecCertificateCopyPublicKey_ios(SecCertificateRef certificate
)
5319 #else /* !TARGET_OS_OSX */
5320 __nullable SecKeyRef
SecCertificateCopyPublicKey(SecCertificateRef certificate
)
5323 return SecCertificateCopyKey(certificate
);
5326 SecKeyRef
SecCertificateCopyKey(SecCertificateRef certificate
) {
5327 if (certificate
->_pubKey
== NULL
) {
5328 const DERAlgorithmId
*algId
=
5329 SecCertificateGetPublicKeyAlgorithm(certificate
);
5330 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
5331 const DERItem
*params
= NULL
;
5332 if (algId
->params
.length
!= 0) {
5333 params
= &algId
->params
;
5335 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
5336 SecAsn1Item params1
= {
5337 .Data
= params
? params
->data
: NULL
,
5338 .Length
= params
? params
->length
: 0
5340 SecAsn1Item keyData1
= {
5341 .Data
= keyData
? keyData
->data
: NULL
,
5342 .Length
= keyData
? keyData
->length
: 0
5344 certificate
->_pubKey
= SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
5348 return CFRetainSafe(certificate
->_pubKey
);
5351 static CFIndex
SecCertificateGetPublicKeyAlgorithmIdAndSize(SecCertificateRef certificate
, size_t *keySizeInBytes
) {
5352 CFIndex keyAlgID
= kSecNullAlgorithmID
;
5355 SecKeyRef pubKey
= NULL
;
5356 require_quiet(certificate
, out
);
5357 require_quiet(pubKey
= SecCertificateCopyKey(certificate
) ,out
);
5358 size
= SecKeyGetBlockSize(pubKey
);
5359 keyAlgID
= SecKeyGetAlgorithmId(pubKey
);
5362 CFReleaseNull(pubKey
);
5363 if (keySizeInBytes
) { *keySizeInBytes
= size
; }
5368 * Public keys in certificates may be considered "weak" or "strong" or neither
5369 * (that is, in between). Certificates using weak keys are not trusted at all.
5370 * Certificates using neither strong nor weak keys are only trusted in certain
5371 * contexts. SecPolicy and SecPolicyServer define the contexts by which we enforce
5372 * these (or stronger) key size trust policies.
5374 bool SecCertificateIsWeakKey(SecCertificateRef certificate
) {
5375 if (!certificate
) { return true; }
5379 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5380 case kSecRSAAlgorithmID
:
5381 if (MIN_RSA_KEY_SIZE
<= size
) weak
= false;
5383 case kSecECDSAAlgorithmID
:
5384 if (MIN_EC_KEY_SIZE
<= size
) weak
= false;
5392 bool SecCertificateIsStrongKey(SecCertificateRef certificate
) {
5393 if (!certificate
) { return false; }
5395 bool strong
= false;
5397 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5398 case kSecRSAAlgorithmID
:
5399 if (MIN_STRONG_RSA_KEY_SIZE
<= size
) strong
= true;
5401 case kSecECDSAAlgorithmID
:
5402 if (MIN_STRONG_EC_KEY_SIZE
<= size
) strong
= true;
5410 bool SecCertificateIsWeakHash(SecCertificateRef certificate
) {
5411 if (!certificate
) { return true; }
5412 SecSignatureHashAlgorithm certAlg
= 0;
5413 certAlg
= SecCertificateGetSignatureHashAlgorithm(certificate
);
5414 if (certAlg
== kSecSignatureHashAlgorithmUnknown
||
5415 certAlg
== kSecSignatureHashAlgorithmMD2
||
5416 certAlg
== kSecSignatureHashAlgorithmMD4
||
5417 certAlg
== kSecSignatureHashAlgorithmMD5
||
5418 certAlg
== kSecSignatureHashAlgorithmSHA1
) {
5424 bool SecCertificateIsAtLeastMinKeySize(SecCertificateRef certificate
,
5425 CFDictionaryRef keySizes
) {
5426 if (!certificate
) { return false; }
5428 bool goodSize
= false;
5430 CFNumberRef minSize
;
5431 size_t minSizeInBits
;
5432 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5433 case kSecRSAAlgorithmID
:
5434 if(CFDictionaryGetValueIfPresent(keySizes
, kSecAttrKeyTypeRSA
, (const void**)&minSize
)
5435 && minSize
&& CFNumberGetValue(minSize
, kCFNumberLongType
, &minSizeInBits
)) {
5436 if (size
>= (size_t)(minSizeInBits
+7)/8) goodSize
= true;
5439 case kSecECDSAAlgorithmID
:
5440 if(CFDictionaryGetValueIfPresent(keySizes
, kSecAttrKeyTypeEC
, (const void**)&minSize
)
5441 && minSize
&& CFNumberGetValue(minSize
, kCFNumberLongType
, &minSizeInBits
)) {
5442 if (size
>= (size_t)(minSizeInBits
+7)/8) goodSize
= true;
5451 CFDataRef
SecCertificateGetSHA1Digest(SecCertificateRef certificate
) {
5452 if (!certificate
|| !certificate
->_der
.data
) {
5455 if (!certificate
->_sha1Digest
) {
5456 certificate
->_sha1Digest
=
5457 SecSHA1DigestCreate(CFGetAllocator(certificate
),
5458 certificate
->_der
.data
, certificate
->_der
.length
);
5460 return certificate
->_sha1Digest
;
5463 CFDataRef
SecCertificateCopySHA256Digest(SecCertificateRef certificate
) {
5464 if (!certificate
|| !certificate
->_der
.data
) {
5467 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
5468 certificate
->_der
.data
, certificate
->_der
.length
);
5471 CFDataRef
SecCertificateCopyIssuerSHA1Digest(SecCertificateRef certificate
) {
5472 CFDataRef digest
= NULL
;
5473 CFDataRef issuer
= SecCertificateCopyIssuerSequence(certificate
);
5475 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
5476 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
5482 CFDataRef
SecCertificateCopyPublicKeySHA1Digest(SecCertificateRef certificate
) {
5483 if (!certificate
|| !certificate
->_pubKeyDER
.data
) {
5486 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
5487 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
5490 static CFDataRef
SecCertificateCopySPKIEncoded(SecCertificateRef certificate
) {
5491 /* SPKI is saved without the tag/length by libDER, so we need to re-encode */
5492 if (!certificate
|| !certificate
->_subjectPublicKeyInfo
.data
) {
5495 DERSize size
= DERLengthOfItem(ASN1_CONSTR_SEQUENCE
, certificate
->_subjectPublicKeyInfo
.length
);
5496 if (size
< certificate
->_subjectPublicKeyInfo
.length
) {
5499 uint8_t *temp
= malloc(size
);
5503 DERReturn drtn
= DEREncodeItem(ASN1_CONSTR_SEQUENCE
,
5504 certificate
->_subjectPublicKeyInfo
.length
,
5505 certificate
->_subjectPublicKeyInfo
.data
,
5507 CFDataRef encodedSPKI
= NULL
;
5508 if (drtn
== DR_Success
) {
5509 encodedSPKI
= CFDataCreate(NULL
, temp
, size
);
5515 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA1Digest(SecCertificateRef certificate
) {
5516 CFDataRef encodedSPKI
= SecCertificateCopySPKIEncoded(certificate
);
5517 if (!encodedSPKI
) { return NULL
; }
5518 CFDataRef hash
= SecSHA1DigestCreate(CFGetAllocator(certificate
),
5519 CFDataGetBytePtr(encodedSPKI
),
5520 CFDataGetLength(encodedSPKI
));
5521 CFReleaseNull(encodedSPKI
);
5525 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA256Digest(SecCertificateRef certificate
) {
5526 CFDataRef encodedSPKI
= SecCertificateCopySPKIEncoded(certificate
);
5527 if (!encodedSPKI
) { return NULL
; }
5528 CFDataRef hash
= SecSHA256DigestCreate(CFGetAllocator(certificate
),
5529 CFDataGetBytePtr(encodedSPKI
),
5530 CFDataGetLength(encodedSPKI
));
5531 CFReleaseNull(encodedSPKI
);
5535 CFTypeRef
SecCertificateCopyKeychainItem(SecCertificateRef certificate
)
5540 CFRetainSafe(certificate
->_keychain_item
);
5541 return certificate
->_keychain_item
;
5544 CFDataRef
SecCertificateGetAuthorityKeyID(SecCertificateRef certificate
) {
5548 if (!certificate
->_authorityKeyID
&&
5549 certificate
->_authorityKeyIdentifier
.length
) {
5550 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
5551 certificate
->_authorityKeyIdentifier
.data
,
5552 certificate
->_authorityKeyIdentifier
.length
);
5555 return certificate
->_authorityKeyID
;
5558 CFDataRef
SecCertificateGetSubjectKeyID(SecCertificateRef certificate
) {
5562 if (!certificate
->_subjectKeyID
&&
5563 certificate
->_subjectKeyIdentifier
.length
) {
5564 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
5565 certificate
->_subjectKeyIdentifier
.data
,
5566 certificate
->_subjectKeyIdentifier
.length
);
5569 return certificate
->_subjectKeyID
;
5572 CFArrayRef
SecCertificateGetCRLDistributionPoints(SecCertificateRef certificate
) {
5576 return certificate
->_crlDistributionPoints
;
5579 CFArrayRef
SecCertificateGetOCSPResponders(SecCertificateRef certificate
) {
5583 return certificate
->_ocspResponders
;
5586 CFArrayRef
SecCertificateGetCAIssuers(SecCertificateRef certificate
) {
5590 return certificate
->_caIssuers
;
5593 bool SecCertificateHasCriticalSubjectAltName(SecCertificateRef certificate
) {
5597 return certificate
->_subjectAltName
&&
5598 certificate
->_subjectAltName
->critical
;
5601 bool SecCertificateHasSubject(SecCertificateRef certificate
) {
5605 /* Since the _subject field is the content of the subject and not the
5606 whole thing, we can simply check for a 0 length subject here. */
5607 return certificate
->_subject
.length
!= 0;
5610 bool SecCertificateHasUnknownCriticalExtension(SecCertificateRef certificate
) {
5614 return certificate
->_foundUnknownCriticalExtension
;
5617 /* Private API functions. */
5618 void SecCertificateShow(SecCertificateRef certificate
) {
5620 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
5621 fprintf(stderr
, "\n");
5625 CFDictionaryRef
SecCertificateCopyAttributeDictionary(
5626 SecCertificateRef certificate
) {
5627 if (!SecCertificateIsCertificate(certificate
)) {
5630 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
5631 CFNumberRef certificateType
= NULL
;
5632 CFNumberRef certificateEncoding
= NULL
;
5633 CFStringRef label
= NULL
;
5634 CFStringRef alias
= NULL
;
5635 CFDataRef skid
= NULL
;
5636 CFDataRef pubKeyDigest
= NULL
;
5637 CFDataRef certData
= NULL
;
5638 CFDictionaryRef dict
= NULL
;
5642 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
5643 SInt32 ctv
= certificate
->_version
+ 1;
5644 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
5645 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
5646 require_quiet(certificateType
!= NULL
, out
);
5647 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
5648 require_quiet(certificateEncoding
!= NULL
, out
);
5649 certData
= SecCertificateCopyData(certificate
);
5650 require_quiet(certData
!= NULL
, out
);
5651 skid
= SecCertificateGetSubjectKeyID(certificate
);
5652 require_quiet(certificate
->_pubKeyDER
.data
!= NULL
&& certificate
->_pubKeyDER
.length
> 0, out
);
5653 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
5654 certificate
->_pubKeyDER
.length
);
5655 require_quiet(pubKeyDigest
!= NULL
, out
);
5657 /* We still need to figure out how to deal with multi valued attributes. */
5658 alias
= SecCertificateCopyRFC822Names(certificate
);
5659 label
= SecCertificateCopySubjectSummary(certificate
);
5665 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
5666 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
5667 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
5669 DICT_ADDPAIR(kSecAttrLabel
, label
);
5672 DICT_ADDPAIR(kSecAttrAlias
, alias
);
5674 if (isData(certificate
->_normalizedSubject
)) {
5675 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
5677 require_quiet(isData(certificate
->_normalizedIssuer
), out
);
5678 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
5679 require_quiet(isData(certificate
->_serialNumber
), out
);
5680 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
5682 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
5684 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
5685 DICT_ADDPAIR(kSecValueData
, certData
);
5686 dict
= DICT_CREATE(allocator
);
5689 CFReleaseSafe(label
);
5690 CFReleaseSafe(alias
);
5691 CFReleaseSafe(pubKeyDigest
);
5692 CFReleaseSafe(certData
);
5693 CFReleaseSafe(certificateEncoding
);
5694 CFReleaseSafe(certificateType
);
5699 SecCertificateRef
SecCertificateCreateFromAttributeDictionary(
5700 CFDictionaryRef refAttributes
) {
5701 /* @@@ Support having an allocator in refAttributes. */
5702 CFAllocatorRef allocator
= NULL
;
5703 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
5704 return data
? SecCertificateCreateWithData(allocator
, data
) : NULL
;
5708 static bool _SecCertificateIsSelfSigned(SecCertificateRef certificate
) {
5709 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
5710 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
5711 SecKeyRef publicKey
= NULL
;
5712 require(SecCertificateIsCertificate(certificate
), out
);
5713 require(publicKey
= SecCertificateCopyKey(certificate
), out
);
5714 CFDataRef normalizedIssuer
=
5715 SecCertificateGetNormalizedIssuerContent(certificate
);
5716 CFDataRef normalizedSubject
=
5717 SecCertificateGetNormalizedSubjectContent(certificate
);
5718 require_quiet(normalizedIssuer
&& normalizedSubject
&&
5719 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
5721 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(certificate
);
5722 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyID(certificate
);
5723 if (authorityKeyID
) {
5724 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
5727 require_noerr_quiet(SecCertificateIsSignedBy(certificate
, publicKey
), out
);
5729 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
5731 CFReleaseSafe(publicKey
);
5734 return (certificate
->_isSelfSigned
== kSecSelfSignedTrue
);
5737 bool SecCertificateIsCA(SecCertificateRef certificate
) {
5738 bool result
= false;
5739 require(SecCertificateIsCertificate(certificate
), out
);
5740 if (SecCertificateVersion(certificate
) >= 3) {
5741 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraints(certificate
);
5742 result
= (basicConstraints
&& basicConstraints
->isCA
);
5745 result
= _SecCertificateIsSelfSigned(certificate
);
5751 bool SecCertificateIsSelfSignedCA(SecCertificateRef certificate
) {
5752 return (_SecCertificateIsSelfSigned(certificate
) && SecCertificateIsCA(certificate
));
5755 OSStatus
SecCertificateIsSelfSigned(SecCertificateRef certificate
, Boolean
*isSelfSigned
) {
5756 if (!SecCertificateIsCertificate(certificate
)) {
5757 return errSecInvalidCertificate
;
5759 if (!isSelfSigned
) {
5762 *isSelfSigned
= _SecCertificateIsSelfSigned(certificate
);
5763 return errSecSuccess
;
5766 SecKeyUsage
SecCertificateGetKeyUsage(SecCertificateRef certificate
) {
5768 return kSecKeyUsageUnspecified
;
5770 return certificate
->_keyUsage
;
5773 CFArrayRef
SecCertificateCopyExtendedKeyUsage(SecCertificateRef certificate
)
5775 CFMutableArrayRef extended_key_usage_oids
=
5776 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
5777 require_quiet(certificate
&& extended_key_usage_oids
, out
);
5779 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5780 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5781 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
5782 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
5785 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
5786 require_noerr_quiet(drtn
, out
);
5787 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
5788 DERDecodedInfo currDecoded
;
5790 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
5791 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
5792 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
5793 currDecoded
.content
.data
, currDecoded
.content
.length
);
5794 require_quiet(oid
, out
);
5795 CFArrayAppendValue(extended_key_usage_oids
, oid
);
5798 require_quiet(drtn
== DR_EndOfSequence
, out
);
5799 return extended_key_usage_oids
;
5803 CFReleaseSafe(extended_key_usage_oids
);
5807 CFArrayRef
SecCertificateCopySignedCertificateTimestamps(SecCertificateRef certificate
)
5809 require_quiet(certificate
, out
);
5812 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5813 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5814 if (extn
->extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
5815 !memcmp(extn
->extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
->extnID
.length
)) {
5816 /* Got the SCT oid */
5817 DERDecodedInfo sctList
;
5818 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &sctList
);
5819 require_noerr_quiet(drtn
, out
);
5820 require_quiet(sctList
.tag
== ASN1_OCTET_STRING
, out
);
5821 return SecCreateSignedCertificateTimestampsArrayFromSerializedSCTList(sctList
.content
.data
, sctList
.content
.length
);
5829 static bool matches_expected(DERItem der
, CFTypeRef expected
) {
5830 if (der
.length
> 1) {
5831 DERDecodedInfo decoded
;
5832 DERDecodeItem(&der
, &decoded
);
5833 switch (decoded
.tag
) {
5836 return decoded
.content
.length
== 0 && expected
== NULL
;
5840 case ASN1_IA5_STRING
:
5841 case ASN1_UTF8_STRING
: {
5842 if (isString(expected
)) {
5843 CFStringRef expectedString
= (CFStringRef
) expected
;
5844 CFStringRef itemString
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFStringEncodingUTF8
, false, kCFAllocatorNull
);
5846 bool result
= (kCFCompareEqualTo
== CFStringCompare(expectedString
, itemString
, 0));
5847 CFReleaseNull(itemString
);
5853 case ASN1_OCTET_STRING
: {
5854 if (isData(expected
)) {
5855 CFDataRef expectedData
= (CFDataRef
) expected
;
5856 CFDataRef itemData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFAllocatorNull
);
5858 bool result
= CFEqual(expectedData
, itemData
);
5859 CFReleaseNull(itemData
);
5865 case ASN1_INTEGER
: {
5866 SInt32 expected_value
= 0;
5867 if (isString(expected
))
5869 CFStringRef aStr
= (CFStringRef
)expected
;
5870 expected_value
= CFStringGetIntValue(aStr
);
5872 else if (isNumber(expected
))
5874 CFNumberGetValue(expected
, kCFNumberSInt32Type
, &expected_value
);
5877 uint32_t num_value
= 0;
5878 if (!DERParseInteger(&decoded
.content
, &num_value
))
5880 return ((uint32_t)expected_value
== num_value
);
5893 static bool cert_contains_marker_extension_value(SecCertificateRef certificate
, CFDataRef oid
, CFTypeRef expectedValue
)
5896 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
5897 size_t oid_len
= CFDataGetLength(oid
);
5899 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5900 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5901 if (extn
->extnID
.length
== oid_len
5902 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
5904 return matches_expected(extn
->extnValue
, expectedValue
);
5910 static bool cert_contains_marker_extension(SecCertificateRef certificate
, CFTypeRef oid
)
5912 return cert_contains_marker_extension_value(certificate
, oid
, NULL
);
5915 struct search_context
{
5917 SecCertificateRef certificate
;
5920 static bool GetDecimalValueOfString(CFStringRef string
, uint32_t* value
)
5922 CFCharacterSetRef nonDecimalDigit
= CFCharacterSetCreateInvertedSet(NULL
, CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
));
5923 bool result
= false;
5925 if ( CFStringGetLength(string
) > 0
5926 && !CFStringFindCharacterFromSet(string
, nonDecimalDigit
, CFRangeMake(0, CFStringGetLength(string
)), kCFCompareForcedOrdering
, NULL
))
5929 *value
= CFStringGetIntValue(string
);
5933 CFReleaseNull(nonDecimalDigit
);
5938 bool SecCertificateIsOidString(CFStringRef oid
)
5940 if (!oid
) return false;
5941 if (2 >= CFStringGetLength(oid
)) return false;
5944 /* oid string only has the allowed characters */
5945 CFCharacterSetRef decimalOid
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789."));
5946 CFCharacterSetRef nonDecimalOid
= CFCharacterSetCreateInvertedSet(NULL
, decimalOid
);
5947 if (CFStringFindCharacterFromSet(oid
, nonDecimalOid
, CFRangeMake(0, CFStringGetLength(oid
)), kCFCompareForcedOrdering
, NULL
)) {
5951 /* first arc is allowed */
5952 UniChar firstArc
[2];
5953 CFRange firstTwo
= {0, 2};
5954 CFStringGetCharacters(oid
, firstTwo
, firstArc
);
5955 if (firstArc
[1] != '.' ||
5956 (firstArc
[0] != '0' && firstArc
[0] != '1' && firstArc
[0] != '2')) {
5960 CFReleaseNull(decimalOid
);
5961 CFReleaseNull(nonDecimalOid
);
5966 CFDataRef
SecCertificateCreateOidDataFromString(CFAllocatorRef allocator
, CFStringRef string
)
5968 CFMutableDataRef currentResult
= NULL
;
5969 CFDataRef encodedResult
= NULL
;
5971 CFArrayRef parts
= NULL
;
5974 if (!string
|| !SecCertificateIsOidString(string
))
5977 parts
= CFStringCreateArrayBySeparatingStrings(allocator
, string
, CFSTR("."));
5982 count
= CFArrayGetCount(parts
);
5986 // assume no more than 5 bytes needed to represent any part of the oid,
5987 // since we limit parts to 32-bit values,
5988 // but the first two parts only need 1 byte
5989 currentResult
= CFDataCreateMutable(allocator
, 1+(count
-2)*5);
5995 part
= CFArrayGetValueAtIndex(parts
, 0);
5997 if (!GetDecimalValueOfString(part
, &x
) || x
> 6)
6004 part
= CFArrayGetValueAtIndex(parts
, 1);
6006 if (!GetDecimalValueOfString(part
, &x
) || x
> 39)
6012 CFDataAppendBytes(currentResult
, &firstByte
, 1);
6014 for (CFIndex i
= 2; i
< count
&& GetDecimalValueOfString(CFArrayGetValueAtIndex(parts
, i
), &x
); ++i
) {
6015 uint8_t b
[5] = {0, 0, 0, 0, 0};
6017 b
[3] = 0x80 | ((x
>> 7) & 0x7F);
6018 b
[2] = 0x80 | ((x
>> 14) & 0x7F);
6019 b
[1] = 0x80 | ((x
>> 21) & 0x7F);
6020 b
[0] = 0x80 | ((x
>> 28) & 0x7F);
6022 // Skip the unused extension bytes.
6023 size_t skipBytes
= 0;
6024 while (b
[skipBytes
] == 0x80)
6027 CFDataAppendBytes(currentResult
, b
+ skipBytes
, sizeof(b
) - skipBytes
);
6030 encodedResult
= currentResult
;
6031 currentResult
= NULL
;
6034 CFReleaseNull(parts
);
6035 CFReleaseNull(currentResult
);
6037 return encodedResult
;
6040 static void check_for_marker(const void *key
, const void *value
, void *context
)
6042 struct search_context
* search_ctx
= (struct search_context
*) context
;
6043 CFStringRef key_string
= (CFStringRef
) key
;
6044 CFTypeRef value_ref
= (CFTypeRef
) value
;
6046 // If we could have short-circuited the iteration
6047 // we would have, but the best we can do
6048 // is not waste time comparing once a match
6050 if (search_ctx
->found
)
6053 if (!isString(key_string
))
6056 CFDataRef key_data
= SecCertificateCreateOidDataFromString(NULL
, key_string
);
6058 if (!isData(key_data
)) {
6059 CFReleaseNull(key_data
);
6063 if (cert_contains_marker_extension_value(search_ctx
->certificate
, key_data
, value_ref
))
6064 search_ctx
->found
= true;
6066 CFReleaseNull(key_data
);
6070 // CFType Ref is either:
6072 // CFData - OID to match with no data permitted
6073 // CFString - decimal OID to match
6074 // CFDictionary - OID -> Value table for expected values Single Object or Array
6075 // CFArray - Array of the above.
6077 // This returns true if any of the requirements are met.
6078 bool SecCertificateHasMarkerExtension(SecCertificateRef certificate
, CFTypeRef oids
)
6080 if (NULL
== certificate
|| NULL
== oids
) {
6082 } else if (CFGetTypeID(oids
) == CFArrayGetTypeID()) {
6083 CFIndex ix
, length
= CFArrayGetCount(oids
);
6084 for (ix
= 0; ix
< length
; ix
++)
6085 if (SecCertificateHasMarkerExtension(certificate
, CFArrayGetValueAtIndex((CFArrayRef
)oids
, ix
)))
6087 } else if (CFGetTypeID(oids
) == CFDictionaryGetTypeID()) {
6088 struct search_context context
= { .found
= false, .certificate
= certificate
};
6089 CFDictionaryApplyFunction((CFDictionaryRef
) oids
, &check_for_marker
, &context
);
6090 return context
.found
;
6091 } else if (CFGetTypeID(oids
) == CFDataGetTypeID()) {
6092 return cert_contains_marker_extension(certificate
, oids
);
6093 } else if (CFGetTypeID(oids
) == CFStringGetTypeID()) {
6094 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, oids
);
6095 if (dataOid
== NULL
) return false;
6096 bool result
= cert_contains_marker_extension(certificate
, dataOid
);
6097 CFReleaseNull(dataOid
);
6103 // Since trust evaluation checks for the id-pkix-ocsp-nocheck OID marker
6104 // in every certificate, this function caches the OID data once instead of
6105 // parsing the same OID string each time.
6107 bool SecCertificateHasOCSPNoCheckMarkerExtension(SecCertificateRef certificate
)
6109 static CFDataRef sOCSPNoCheckOIDData
= NULL
;
6110 static dispatch_once_t onceToken
;
6112 dispatch_once(&onceToken
, ^{
6113 sOCSPNoCheckOIDData
= SecCertificateCreateOidDataFromString(NULL
, CFSTR("1.3.6.1.5.5.7.48.1.5"));
6115 return SecCertificateHasMarkerExtension(certificate
, sOCSPNoCheckOIDData
);
6118 static DERItem
*cert_extension_value_for_marker(SecCertificateRef certificate
, CFDataRef oid
) {
6120 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
6121 size_t oid_len
= CFDataGetLength(oid
);
6123 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
6124 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
6125 if (extn
->extnID
.length
== oid_len
6126 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
6128 return (DERItem
*)&extn
->extnValue
;
6135 // CFType Ref is either:
6137 // CFData - OID to match with no data permitted
6138 // CFString - decimal OID to match
6140 DERItem
*SecCertificateGetExtensionValue(SecCertificateRef certificate
, CFTypeRef oid
) {
6141 if (!certificate
|| !oid
) {
6145 if(CFGetTypeID(oid
) == CFDataGetTypeID()) {
6146 return cert_extension_value_for_marker(certificate
, oid
);
6147 } else if (CFGetTypeID(oid
) == CFStringGetTypeID()) {
6148 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, oid
);
6149 if (dataOid
== NULL
) return NULL
;
6150 DERItem
*result
= cert_extension_value_for_marker(certificate
, dataOid
);
6151 CFReleaseNull(dataOid
);
6158 CFDataRef
SecCertificateCopyExtensionValue(SecCertificateRef certificate
, CFTypeRef extensionOID
, bool *isCritical
) {
6159 if (!certificate
|| !extensionOID
) {
6163 CFDataRef oid
= NULL
, extensionValue
= NULL
;
6164 if (CFGetTypeID(extensionOID
) == CFDataGetTypeID()) {
6165 oid
= CFRetainSafe(extensionOID
);
6166 } else if (CFGetTypeID(extensionOID
) == CFStringGetTypeID()) {
6167 oid
= SecCertificateCreateOidDataFromString(NULL
, extensionOID
);
6174 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
6175 size_t oid_len
= CFDataGetLength(oid
);
6177 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
6178 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
6179 if (extn
->extnID
.length
== oid_len
6180 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
6183 *isCritical
= extn
->critical
;
6185 extensionValue
= CFDataCreate(NULL
, extn
->extnValue
.data
, extn
->extnValue
.length
);
6191 return extensionValue
;
6194 CFDataRef
SecCertificateCopyiAPAuthCapabilities(SecCertificateRef certificate
) {
6198 CFDataRef extensionData
= NULL
;
6199 DERItem
*extensionValue
= NULL
;
6200 extensionValue
= SecCertificateGetExtensionValue(certificate
,
6201 CFSTR("1.2.840.113635.100.6.36"));
6202 require_quiet(extensionValue
, out
);
6203 /* The extension is a octet string containing the DER-encoded 32-byte octet string */
6204 require_quiet(extensionValue
->length
== 34, out
);
6205 DERDecodedInfo decodedValue
;
6206 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
6207 if (decodedValue
.tag
== ASN1_OCTET_STRING
) {
6208 require_quiet(decodedValue
.content
.length
== 32, out
);
6209 extensionData
= CFDataCreate(NULL
, decodedValue
.content
.data
,
6210 decodedValue
.content
.length
);
6212 require_quiet(extensionValue
->data
[33] == 0x00 &&
6213 extensionValue
->data
[32] == 0x00, out
);
6214 extensionData
= CFDataCreate(NULL
, extensionValue
->data
, 32);
6217 return extensionData
;
6221 /* From iapd IAPAuthenticationTypes.h */
6222 typedef struct IapCertSerialNumber
6224 uint8_t xservID
; // Xserver ID
6225 uint8_t hsmID
; // Hardware security module ID (generated cert)
6226 uint8_t delimiter01
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6227 uint8_t dateYear
; // Date year cert was issued
6228 uint8_t dateMonth
; // Date month cert was issued
6229 uint8_t dateDay
; // Date day cert was issued
6230 uint8_t delimiter02
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6231 uint8_t devClass
; // iAP device class (maps to lingo permissions)
6232 uint8_t delimiter03
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6233 uint8_t batchNumHi
; // Batch number high byte (15:08)
6234 uint8_t batchNumLo
; // Batch number low byte (07:00)
6235 uint8_t delimiter04
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6236 uint8_t serialNumHi
; // Serial number high byte (23:16)
6237 uint8_t serialNumMid
; // Serial number middle byte (15:08)
6238 uint8_t serialNumLo
; // Serial number low byte (07:00)
6240 } IapCertSerialNumber_t
, *pIapCertSerialNumber_t
;
6243 #define IAP_CERT_FIELD_DELIMITER 0xAA // "Apple_Accessory" delimiter
6244 SeciAuthVersion
SecCertificateGetiAuthVersion(SecCertificateRef certificate
) {
6246 return kSeciAuthInvalid
;
6248 if (NULL
!= SecCertificateGetExtensionValue(certificate
,
6249 CFSTR("1.2.840.113635.100.6.36"))) {
6250 /* v3 Capabilities Extension */
6251 return kSeciAuthVersion3
;
6253 if (NULL
!= SecCertificateGetExtensionValue(certificate
,
6254 CFSTR("1.2.840.113635.100.6.59.1"))) {
6255 /* SW Auth General Capabilities Extension */
6256 return kSeciAuthVersionSW
;
6258 DERItem serialNumber
= certificate
->_serialNum
;
6259 require_quiet(serialNumber
.data
, out
);
6260 require_quiet(serialNumber
.length
== 15, out
);
6261 require_quiet(serialNumber
.data
[2] == IAP_CERT_FIELD_DELIMITER
&&
6262 serialNumber
.data
[6] == IAP_CERT_FIELD_DELIMITER
&&
6263 serialNumber
.data
[8] == IAP_CERT_FIELD_DELIMITER
&&
6264 serialNumber
.data
[11] == IAP_CERT_FIELD_DELIMITER
, out
);
6265 return kSeciAuthVersion2
;
6267 return kSeciAuthInvalid
;
6270 static CFStringRef
SecCertificateiAPSWAuthCapabilitiesTypeToOID(SeciAPSWAuthCapabilitiesType type
) {
6271 CFStringRef extensionOID
= NULL
;
6272 /* Get the oid for the type */
6273 if (type
== kSeciAPSWAuthGeneralCapabilities
) {
6274 extensionOID
= CFSTR("1.2.840.113635.100.6.59.1");
6275 } else if (type
== kSeciAPSWAuthAirPlayCapabilities
) {
6276 extensionOID
= CFSTR("1.2.840.113635.100.6.59.2");
6277 } else if (type
== kSeciAPSWAuthHomeKitCapabilities
) {
6278 extensionOID
= CFSTR("1.2.840.113635.100.6.59.3");
6280 return extensionOID
;
6283 CFDataRef
SecCertificateCopyiAPSWAuthCapabilities(SecCertificateRef certificate
, SeciAPSWAuthCapabilitiesType type
) {
6287 CFDataRef extensionData
= NULL
;
6288 DERItem
*extensionValue
= NULL
;
6289 CFStringRef extensionOID
= SecCertificateiAPSWAuthCapabilitiesTypeToOID(type
);
6290 require_quiet(extensionOID
, out
);
6291 extensionValue
= SecCertificateGetExtensionValue(certificate
, extensionOID
);
6292 require_quiet(extensionValue
, out
);
6293 /* The extension is a octet string containing the DER-encoded variable-length octet string */
6294 DERDecodedInfo decodedValue
;
6295 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
6296 if (decodedValue
.tag
== ASN1_OCTET_STRING
) {
6297 extensionData
= CFDataCreate(NULL
, decodedValue
.content
.data
,
6298 decodedValue
.content
.length
);
6301 return extensionData
;
6304 CFStringRef
SecCertificateCopyComponentType(SecCertificateRef certificate
) {
6308 CFStringRef componentType
= NULL
;
6309 DERItem
*extensionValue
= SecCertificateGetExtensionValue(certificate
, CFSTR("1.2.840.113635.100.11.1"));
6310 require_quiet(extensionValue
, out
);
6311 /* The componentType is an IA5String */
6312 DERDecodedInfo decodedValue
;
6313 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
6314 if (decodedValue
.tag
== ASN1_IA5_STRING
) {
6315 componentType
= CFStringCreateWithBytes(NULL
, decodedValue
.content
.data
, decodedValue
.content
.length
, kCFStringEncodingASCII
, false);
6318 return componentType
;
6321 SecCertificateRef
SecCertificateCreateWithPEM(CFAllocatorRef allocator
,
6322 CFDataRef pem_certificate
)
6324 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
6325 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
6326 uint8_t *base64_data
= NULL
;
6327 SecCertificateRef cert
= NULL
;
6328 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
6329 const size_t length
= CFDataGetLength(pem_certificate
);
6330 char *begin
= strnstr((const char *)data
, begin_cert
, length
);
6331 char *end
= strnstr((const char *)data
, end_cert
, length
);
6334 begin
+= sizeof(begin_cert
) - 1;
6335 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
6336 if (base64_length
&& (base64_length
< (size_t)CFDataGetLength(pem_certificate
))) {
6337 require_quiet(base64_data
= calloc(1, base64_length
), out
);
6338 require_action_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
, free(base64_data
));
6339 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, base64_data
, base64_length
);
6348 // -- MARK -- XPC encoding/decoding
6351 bool SecCertificateAppendToXPCArray(SecCertificateRef certificate
, xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
6353 return true; // NOOP
6355 size_t length
= SecCertificateGetLength(certificate
);
6356 const uint8_t *bytes
= SecCertificateGetBytePtr(certificate
);
6357 #if SECTRUST_VERBOSE_DEBUG
6358 secerror("cert=0x%lX length=%d bytes=0x%lX", (uintptr_t)certificate
, (int)length
, (uintptr_t)bytes
);
6360 if (!length
|| !bytes
) {
6361 return SecError(errSecParam
, error
, CFSTR("failed to der encode certificate"));
6363 xpc_array_set_data(xpc_certificates
, XPC_ARRAY_APPEND
, bytes
, length
);
6367 SecCertificateRef
SecCertificateCreateWithXPCArrayAtIndex(xpc_object_t xpc_certificates
, size_t index
, CFErrorRef
*error
) {
6368 SecCertificateRef certificate
= NULL
;
6370 const uint8_t *bytes
= xpc_array_get_data(xpc_certificates
, index
, &length
);
6372 certificate
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, bytes
, length
);
6375 SecError(errSecParam
, error
, CFSTR("certificates[%zu] failed to decode"), index
);
6380 xpc_object_t
SecCertificateArrayCopyXPCArray(CFArrayRef certificates
, CFErrorRef
*error
) {
6381 xpc_object_t xpc_certificates
;
6382 require_action_quiet(xpc_certificates
= xpc_array_create(NULL
, 0), exit
,
6383 SecError(errSecAllocate
, error
, CFSTR("failed to create xpc_array")));
6384 CFIndex ix
, count
= CFArrayGetCount(certificates
);
6385 for (ix
= 0; ix
< count
; ++ix
) {
6386 SecCertificateRef certificate
= (SecCertificateRef
) CFArrayGetValueAtIndex(certificates
, ix
);
6387 #if SECTRUST_VERBOSE_DEBUG
6388 CFIndex length
= SecCertificateGetLength(certificate
);
6389 const UInt8
*bytes
= SecCertificateGetBytePtr(certificate
);
6390 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
);
6392 if (!SecCertificateAppendToXPCArray(certificate
, xpc_certificates
, error
)) {
6393 xpc_release(xpc_certificates
);
6394 xpc_certificates
= NULL
;
6400 return xpc_certificates
;
6403 CFArrayRef
SecCertificateXPCArrayCopyArray(xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
6404 CFMutableArrayRef certificates
= NULL
;
6405 require_action_quiet(xpc_get_type(xpc_certificates
) == XPC_TYPE_ARRAY
, exit
,
6406 SecError(errSecParam
, error
, CFSTR("certificates xpc value is not an array")));
6407 size_t count
= xpc_array_get_count(xpc_certificates
);
6408 require_action_quiet(certificates
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
), exit
,
6409 SecError(errSecAllocate
, error
, CFSTR("failed to create CFArray of capacity %zu"), count
));
6412 for (ix
= 0; ix
< count
; ++ix
) {
6413 SecCertificateRef cert
= SecCertificateCreateWithXPCArrayAtIndex(xpc_certificates
, ix
, error
);
6415 CFRelease(certificates
);
6418 CFArraySetValueAtIndex(certificates
, ix
, cert
);
6423 return certificates
;
6426 #define do_if_registered(sdp, ...) if (gTrustd && gTrustd->sdp) { return gTrustd->sdp(__VA_ARGS__); }
6429 static CFArrayRef
CopyEscrowCertificates(SecCertificateEscrowRootType escrowRootType
, CFErrorRef
* error
)
6431 __block CFArrayRef result
= NULL
;
6433 do_if_registered(ota_CopyEscrowCertificates
, escrowRootType
, error
);
6435 securityd_send_sync_and_do(kSecXPCOpOTAGetEscrowCertificates
, error
,
6436 ^bool(xpc_object_t message
, CFErrorRef
*blockError
)
6438 xpc_dictionary_set_uint64(message
, "escrowType", (uint64_t)escrowRootType
);
6441 ^bool(xpc_object_t response
, CFErrorRef
*blockError
)
6443 xpc_object_t xpc_array
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
6445 if (response
&& (NULL
!= xpc_array
)) {
6446 result
= (CFArrayRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_array
);
6449 return SecError(errSecInternal
, blockError
, CFSTR("Did not get the Escrow certificates"));
6451 return result
!= NULL
;
6456 CFArrayRef
SecCertificateCopyEscrowRoots(SecCertificateEscrowRootType escrowRootType
)
6458 CFArrayRef result
= NULL
;
6460 CFDataRef certData
= NULL
;
6463 if (kSecCertificateBaselineEscrowRoot
== escrowRootType
||
6464 kSecCertificateBaselinePCSEscrowRoot
== escrowRootType
||
6465 kSecCertificateBaselineEscrowBackupRoot
== escrowRootType
||
6466 kSecCertificateBaselineEscrowEnrollmentRoot
== escrowRootType
)
6468 // The request is for the base line certificates.
6469 // Use the hard coded data to generate the return array.
6470 struct RootRecord
** pEscrowRoots
;
6471 switch (escrowRootType
) {
6472 case kSecCertificateBaselineEscrowRoot
:
6473 numRoots
= kNumberOfBaseLineEscrowRoots
;
6474 pEscrowRoots
= kBaseLineEscrowRoots
;
6476 case kSecCertificateBaselinePCSEscrowRoot
:
6477 numRoots
= kNumberOfBaseLinePCSEscrowRoots
;
6478 pEscrowRoots
= kBaseLinePCSEscrowRoots
;
6480 case kSecCertificateBaselineEscrowBackupRoot
:
6481 numRoots
= kNumberOfBaseLineEscrowBackupRoots
;
6482 pEscrowRoots
= kBaseLineEscrowBackupRoots
;
6484 case kSecCertificateBaselineEscrowEnrollmentRoot
:
6486 numRoots
= kNumberOfBaseLineEscrowEnrollmentRoots
;
6487 pEscrowRoots
= kBaseLineEscrowEnrollmentRoots
;
6491 // Get the hard coded set of roots
6492 SecCertificateRef baseLineCerts
[numRoots
];
6493 struct RootRecord
* pRootRecord
= NULL
;
6495 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6496 pRootRecord
= pEscrowRoots
[iCnt
];
6497 if (NULL
!= pRootRecord
&& pRootRecord
->_length
> 0 && NULL
!= pRootRecord
->_bytes
) {
6498 certData
= CFDataCreate(kCFAllocatorDefault
, pRootRecord
->_bytes
, pRootRecord
->_length
);
6499 if (NULL
!= certData
) {
6500 baseLineCerts
[iCnt
] = SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
6501 CFRelease(certData
);
6505 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)baseLineCerts
, numRoots
, &kCFTypeArrayCallBacks
);
6506 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6507 if (NULL
!= baseLineCerts
[iCnt
]) {
6508 CFRelease(baseLineCerts
[iCnt
]);
6512 // The request is for the current certificates.
6513 CFErrorRef error
= NULL
;
6514 CFArrayRef cert_datas
= CopyEscrowCertificates(escrowRootType
, &error
);
6515 if (NULL
!= error
|| NULL
== cert_datas
) {
6516 if (NULL
!= error
) {
6519 if (NULL
!= cert_datas
) {
6520 CFRelease(cert_datas
);
6525 numRoots
= (int)(CFArrayGetCount(cert_datas
));
6527 SecCertificateRef assetCerts
[numRoots
];
6528 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6529 certData
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, iCnt
);
6530 if (NULL
!= certData
) {
6531 SecCertificateRef aCertRef
= SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
6532 assetCerts
[iCnt
] = aCertRef
;
6535 assetCerts
[iCnt
] = NULL
;
6540 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)assetCerts
, numRoots
, &kCFTypeArrayCallBacks
);
6541 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6542 if (NULL
!= assetCerts
[iCnt
]) {
6543 CFRelease(assetCerts
[iCnt
]);
6547 CFReleaseSafe(cert_datas
);
6552 static CFDictionaryRef
CopyTrustedCTLogs(CFErrorRef
* error
)
6554 __block CFDictionaryRef result
= NULL
;
6556 // call function directly and return if we are built in server mode
6557 do_if_registered(sec_ota_pki_copy_trusted_ct_logs
, error
);
6559 securityd_send_sync_and_do(kSecXPCOpOTAPKICopyTrustedCTLogs
, error
,
6560 ^bool(xpc_object_t message
, CFErrorRef
*blockError
) {
6561 // input: set message parameters here
6563 }, ^bool(xpc_object_t response
, CFErrorRef
*blockError
) {
6564 // output: get dictionary from response object
6565 xpc_object_t xpc_dictionary
= NULL
;
6567 xpc_dictionary
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
6569 if (xpc_dictionary
&& (xpc_get_type(xpc_dictionary
) == XPC_TYPE_DICTIONARY
)) {
6570 result
= (CFDictionaryRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_dictionary
);
6572 return SecError(errSecInternal
, blockError
, CFSTR("Unable to get CT logs"));
6574 return result
!= NULL
;
6579 #define CTLOG_KEYID_LENGTH 32 /* key id data length */
6581 static CFDictionaryRef
CopyCTLogForKeyID(CFDataRef keyID
, CFErrorRef
* error
)
6583 __block CFDictionaryRef result
= NULL
;
6584 if (!isData(keyID
)) {
6585 (void) SecError(errSecParam
, error
, CFSTR("keyID was not a valid CFDataRef"));
6588 const void *p
= CFDataGetBytePtr(keyID
);
6589 if (!p
|| CFDataGetLength(keyID
) != CTLOG_KEYID_LENGTH
) {
6590 (void) SecError(errSecParam
, error
, CFSTR("keyID data was not the expected length"));
6593 // call function directly and return if we are built in server mode
6594 do_if_registered(sec_ota_pki_copy_ct_log_for_keyid
, keyID
, error
);
6596 securityd_send_sync_and_do(kSecXPCOpOTAPKICopyCTLogForKeyID
, error
,
6597 ^bool(xpc_object_t message
, CFErrorRef
*blockError
) {
6598 // input: set message parameters here
6599 xpc_dictionary_set_data(message
, kSecXPCData
, p
, CTLOG_KEYID_LENGTH
);
6601 }, ^bool(xpc_object_t response
, CFErrorRef
*blockError
) {
6602 // output: get dictionary from response object
6603 xpc_object_t xpc_dictionary
= NULL
;
6605 xpc_dictionary
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
6607 if (xpc_dictionary
&& (xpc_get_type(xpc_dictionary
) == XPC_TYPE_DICTIONARY
)) {
6608 result
= (CFDictionaryRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_dictionary
);
6610 return SecError(errSecInternal
, blockError
, CFSTR("Unable to match CT log"));
6612 return result
!= NULL
;
6617 CFDictionaryRef
SecCertificateCopyTrustedCTLogs(void)
6619 CFErrorRef localError
= NULL
;
6620 CFDictionaryRef result
= CopyTrustedCTLogs(&localError
);
6621 CFReleaseSafe(localError
);
6625 CFDictionaryRef
SecCertificateCopyCTLogForKeyID(CFDataRef keyID
)
6627 CFErrorRef localError
= NULL
;
6628 CFDictionaryRef result
= CopyCTLogForKeyID(keyID
, &localError
);
6629 CFReleaseSafe(localError
);
6633 SEC_CONST_DECL (kSecSignatureDigestAlgorithmUnknown
, "SignatureDigestUnknown");
6634 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD2
, "SignatureDigestMD2");
6635 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD4
, "SignatureDigestMD4");
6636 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD5
, "SignatureDigestMD5");
6637 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA1
, "SignatureDigestSHA1");
6638 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA224
, "SignatureDigestSHA224");
6639 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA256
, "SignatureDigestSHA256");
6640 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA384
, "SignatureDigestSHA284");
6641 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA512
, "SignatureDigestSHA512");
6643 SecSignatureHashAlgorithm
SecSignatureHashAlgorithmForAlgorithmOid(const DERItem
*algOid
)
6645 SecSignatureHashAlgorithm result
= kSecSignatureHashAlgorithmUnknown
;
6647 if (!algOid
->data
|| !algOid
->length
) {
6650 /* classify the signature algorithm OID into one of our known types */
6651 if (DEROidCompare(algOid
, &oidSha512Ecdsa
) ||
6652 DEROidCompare(algOid
, &oidSha512Rsa
) ||
6653 DEROidCompare(algOid
, &oidSha512
)) {
6654 result
= kSecSignatureHashAlgorithmSHA512
;
6657 if (DEROidCompare(algOid
, &oidSha384Ecdsa
) ||
6658 DEROidCompare(algOid
, &oidSha384Rsa
) ||
6659 DEROidCompare(algOid
, &oidSha384
)) {
6660 result
= kSecSignatureHashAlgorithmSHA384
;
6663 if (DEROidCompare(algOid
, &oidSha256Ecdsa
) ||
6664 DEROidCompare(algOid
, &oidSha256Rsa
) ||
6665 DEROidCompare(algOid
, &oidSha256
)) {
6666 result
= kSecSignatureHashAlgorithmSHA256
;
6669 if (DEROidCompare(algOid
, &oidSha224Ecdsa
) ||
6670 DEROidCompare(algOid
, &oidSha224Rsa
) ||
6671 DEROidCompare(algOid
, &oidSha224
)) {
6672 result
= kSecSignatureHashAlgorithmSHA224
;
6675 if (DEROidCompare(algOid
, &oidSha1Ecdsa
) ||
6676 DEROidCompare(algOid
, &oidSha1Rsa
) ||
6677 DEROidCompare(algOid
, &oidSha1Dsa
) ||
6678 DEROidCompare(algOid
, &oidSha1DsaOIW
) ||
6679 DEROidCompare(algOid
, &oidSha1DsaCommonOIW
) ||
6680 DEROidCompare(algOid
, &oidSha1RsaOIW
) ||
6681 DEROidCompare(algOid
, &oidSha1Fee
) ||
6682 DEROidCompare(algOid
, &oidSha1
)) {
6683 result
= kSecSignatureHashAlgorithmSHA1
;
6686 if (DEROidCompare(algOid
, &oidMd5Rsa
) ||
6687 DEROidCompare(algOid
, &oidMd5Fee
) ||
6688 DEROidCompare(algOid
, &oidMd5
)) {
6689 result
= kSecSignatureHashAlgorithmMD5
;
6692 if (DEROidCompare(algOid
, &oidMd4Rsa
) ||
6693 DEROidCompare(algOid
, &oidMd4
)) {
6694 result
= kSecSignatureHashAlgorithmMD4
;
6697 if (DEROidCompare(algOid
, &oidMd2Rsa
) ||
6698 DEROidCompare(algOid
, &oidMd2
)) {
6699 result
= kSecSignatureHashAlgorithmMD2
;
6708 SecSignatureHashAlgorithm
SecCertificateGetSignatureHashAlgorithm(SecCertificateRef certificate
)
6710 DERAlgorithmId
*algId
= (certificate
) ? &certificate
->_tbsSigAlg
: NULL
;
6711 const DERItem
*algOid
= (algId
) ? &algId
->oid
: NULL
;
6712 return SecSignatureHashAlgorithmForAlgorithmOid(algOid
);
6715 CFArrayRef
SecCertificateCopyiPhoneDeviceCAChain(void) {
6716 CFMutableArrayRef result
= NULL
;
6717 SecCertificateRef iPhoneDeviceCA
= NULL
, iPhoneCA
= NULL
, appleRoot
= NULL
;
6719 require_quiet(iPhoneDeviceCA
= SecCertificateCreateWithBytes(NULL
, _AppleiPhoneDeviceCA
, sizeof(_AppleiPhoneDeviceCA
)),
6721 require_quiet(iPhoneCA
= SecCertificateCreateWithBytes(NULL
, _AppleiPhoneCA
, sizeof(_AppleiPhoneCA
)),
6723 require_quiet(appleRoot
= SecCertificateCreateWithBytes(NULL
, _AppleRootCA
, sizeof(_AppleRootCA
)),
6726 require_quiet(result
= CFArrayCreateMutable(NULL
, 3, &kCFTypeArrayCallBacks
), errOut
);
6727 CFArrayAppendValue(result
, iPhoneDeviceCA
);
6728 CFArrayAppendValue(result
, iPhoneCA
);
6729 CFArrayAppendValue(result
, appleRoot
);
6732 CFReleaseNull(iPhoneDeviceCA
);
6733 CFReleaseNull(iPhoneCA
);
6734 CFReleaseNull(appleRoot
);
6738 bool SecCertificateGetDeveloperIDDate(SecCertificateRef certificate
, CFAbsoluteTime
*time
, CFErrorRef
*error
) {
6739 if (!certificate
|| !time
) {
6740 return SecError(errSecParam
, error
, CFSTR("DeveloperID Date parsing: missing required input"));
6742 DERItem
*extensionValue
= SecCertificateGetExtensionValue(certificate
, CFSTR("1.2.840.113635.100.6.1.33"));
6743 if (!extensionValue
) {
6744 return SecError(errSecMissingRequiredExtension
, error
, CFSTR("DeveloperID Date parsing: extension not found"));
6746 DERDecodedInfo decodedValue
;
6747 if (DERDecodeItem(extensionValue
, &decodedValue
) != DR_Success
) {
6748 return SecError(errSecDecode
, error
, CFSTR("DeveloperID Date parsing: extension value failed to decode"));
6750 /* The extension value is a DERGeneralizedTime encoded in a UTF8String */
6751 CFErrorRef localError
= NULL
;
6752 if (decodedValue
.tag
== ASN1_UTF8_STRING
) {
6753 *time
= SecAbsoluteTimeFromDateContentWithError(ASN1_GENERALIZED_TIME
, decodedValue
.content
.data
, decodedValue
.content
.length
, &localError
);
6755 return SecError(errSecDecode
, error
, CFSTR("DeveloperID Date parsing: extension value wrong tag"));
6757 return CFErrorPropagate(localError
, error
);
6760 CFIndex
SecCertificateGetUnparseableKnownExtension(SecCertificateRef certificate
) {
6764 return certificate
->_unparseableKnownExtensionIndex
;
6767 CFArrayRef
SecCertificateCopyAppleExternalRoots(void) {
6768 CFMutableArrayRef result
= NULL
;
6769 SecCertificateRef appleExternalECRoot
= NULL
, testAppleExternalECRoot
= NULL
;
6771 require_quiet(appleExternalECRoot
= SecCertificateCreateWithBytes(NULL
, _AppleExternalECRootCA
, sizeof(_AppleExternalECRootCA
)),
6773 require_quiet(testAppleExternalECRoot
= SecCertificateCreateWithBytes(NULL
, _TestAppleExternalECRootCA
,
6774 sizeof(_TestAppleExternalECRootCA
)),
6777 require_quiet(result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
), errOut
);
6778 CFArrayAppendValue(result
, appleExternalECRoot
);
6779 if (SecIsInternalRelease()) {
6780 CFArrayAppendValue(result
, testAppleExternalECRoot
);
6784 CFReleaseNull(appleExternalECRoot
);
6785 CFReleaseNull(testAppleExternalECRoot
);