2 * Copyright (c) 2006-2019 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecCertificate.c - CoreFoundation based certificate object
29 /* Allows us to build genanchors against the BaseSDK. */
30 #undef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
31 #undef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
34 #include <Security/SecCertificateInternal.h>
35 #include <utilities/SecIOFormat.h>
36 #include <CommonCrypto/CommonDigest.h>
37 #include <CoreFoundation/CFRuntime.h>
38 #include <CoreFoundation/CFString.h>
39 #include <CoreFoundation/CFBundle.h>
40 #include <CoreFoundation/CFDictionary.h>
41 #include <CoreFoundation/CFNumber.h>
42 #include <CoreFoundation/CFCalendar.h>
43 #include <CoreFoundation/CFTimeZone.h>
44 #include <CoreFoundation/CFXPCBridge.h>
46 #include <AssertMacros.h>
47 #include <libDER/DER_CertCrl.h>
48 #include <libDER/DER_Encode.h>
49 #include <libDER/DER_Keys.h>
50 #include <libDER/asn1Types.h>
51 #include <libDER/oids.h>
52 #include <Security/SecBasePriv.h>
53 #include "SecRSAKey.h"
54 #include "SecFramework.h"
55 #include <Security/SecItem.h>
56 #include <Security/SecItemPriv.h>
57 #include "SecSignatureVerificationSupport.h"
59 #include <utilities/debugging.h>
60 #include <utilities/SecCFWrappers.h>
61 #include <utilities/SecCFError.h>
62 #include <utilities/SecSCTUtils.h>
63 #include <utilities/array_size.h>
65 #include <libkern/OSByteOrder.h>
67 #include <Security/SecInternal.h>
68 #include <Security/SecFrameworkStrings.h>
69 #include "SecBase64.h"
70 #include "AppleBaselineEscrowCertificates.h"
71 #include "AppleiPhoneDeviceCACertificates.h"
72 #include <ipc/securityd_client.h>
73 #include <Security/SecKeyInternal.h>
75 #pragma clang diagnostic ignored "-Wformat=2"
77 /* The minimum key sizes necessary to not be considered "weak" */
78 #define MIN_RSA_KEY_SIZE 128 // 1024-bit
79 #define MIN_EC_KEY_SIZE 20 // 160-bit
81 /* The minimum key sizes necessary to be considered "strong" */
82 #define MIN_STRONG_RSA_KEY_SIZE 256 // 2048-bit
83 #define MIN_STRONG_EC_KEY_SIZE 28 // 224-bit
85 #define IPv4ADDRLEN 4 // 4 octets
86 #define IPv6ADDRLEN 16 // 16 octets
88 typedef struct SecCertificateExtension
{
92 } SecCertificateExtension
;
95 kSecSelfSignedUnknown
= 0,
100 struct __SecCertificate
{
103 DERItem _der
; /* Entire certificate in DER form. */
104 DERItem _tbs
; /* To Be Signed cert DER bytes. */
105 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
106 DERItem _signature
; /* The content of the sig bit string. */
109 DERItem _serialNum
; /* Integer. */
110 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
111 DERItem _issuer
; /* Sequence of RDN. */
112 CFAbsoluteTime _notBefore
;
113 CFAbsoluteTime _notAfter
;
114 DERItem _subject
; /* Sequence of RDN. */
115 DERItem _subjectPublicKeyInfo
; /* SPKI (without tag/length) */
116 DERAlgorithmId _algId
; /* oid and params of _pubKeyDER. */
117 DERItem _pubKeyDER
; /* contents of bit string */
118 DERItem _issuerUniqueID
; /* bit string, optional */
119 DERItem _subjectUniqueID
; /* bit string, optional */
121 bool _foundUnknownCriticalExtension
;
123 /* Well known certificate extensions. */
124 SecCEBasicConstraints _basicConstraints
;
125 SecCEPolicyConstraints _policyConstraints
;
126 SecCEPolicyMappings _policyMappings
;
127 SecCECertificatePolicies _certificatePolicies
;
128 SecCEInhibitAnyPolicy _inhibitAnyPolicySkipCerts
;
130 /* If KeyUsage extension is not present this is 0, otherwise it's
131 the value of the extension. */
132 SecKeyUsage _keyUsage
;
134 /* OCTETS of SubjectKeyIdentifier extensions KeyIdentifier.
135 Length = 0 if not present. */
136 DERItem _subjectKeyIdentifier
;
138 /* OCTETS of AuthorityKeyIdentifier extensions KeyIdentifier.
139 Length = 0 if not present. */
140 DERItem _authorityKeyIdentifier
;
141 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
142 _authorityKeyIdentifierSerialNumber have non zero length if present.
143 Both are either present or absent together. */
144 DERItem _authorityKeyIdentifierIssuer
;
145 DERItem _authorityKeyIdentifierSerialNumber
;
147 /* Subject alt name extension, if present. Not malloced, it's just a
148 pointer to an element in the _extensions array. */
149 const SecCertificateExtension
*_subjectAltName
;
151 /* Parsed extension values. */
153 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
154 CFMutableArrayRef _crlDistributionPoints
;
156 /* Array of CFURLRefs containing the URI values of accessLocations of each
157 id-ad-ocsp AccessDescription in the Authority Information Access
159 CFMutableArrayRef _ocspResponders
;
161 /* Array of CFURLRefs containing the URI values of accessLocations of each
162 id-ad-caIssuers AccessDescription in the Authority Information Access
164 CFMutableArrayRef _caIssuers
;
166 /* Array of CFDataRefs containing the generalNames for permittedSubtrees
168 CFArrayRef _permittedSubtrees
;
170 /* Array of CFDataRefs containing the generalNames for excludedSubtrees
172 CFArrayRef _excludedSubtrees
;
174 CFMutableArrayRef _embeddedSCTs
;
176 /* All other (non known) extensions. The _extensions array is malloced. */
177 CFIndex _extensionCount
;
178 SecCertificateExtension
*_extensions
;
179 CFIndex _unparseableKnownExtensionIndex
;
181 /* Optional cached fields. */
184 CFArrayRef _properties
;
185 CFDataRef _serialNumber
;
186 CFDataRef _normalizedIssuer
;
187 CFDataRef _normalizedSubject
;
188 CFDataRef _authorityKeyID
;
189 CFDataRef _subjectKeyID
;
191 CFDataRef _sha1Digest
;
192 CFTypeRef _keychain_item
;
193 uint8_t _isSelfSigned
;
197 #define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v);
199 SEC_CONST_DECL (kSecCertificateProductionEscrowKey
, "ProductionEscrowKey");
200 SEC_CONST_DECL (kSecCertificateProductionPCSEscrowKey
, "ProductionPCSEscrowKey");
201 SEC_CONST_DECL (kSecCertificateEscrowFileName
, "AppleESCertificates");
203 /* Public Constants for property list keys. */
204 SEC_CONST_DECL (kSecPropertyKeyType
, "type");
205 SEC_CONST_DECL (kSecPropertyKeyLabel
, "label");
206 SEC_CONST_DECL (kSecPropertyKeyLocalizedLabel
, "localized label");
207 SEC_CONST_DECL (kSecPropertyKeyValue
, "value");
209 /* Public Constants for property list values. */
210 SEC_CONST_DECL (kSecPropertyTypeWarning
, "warning");
211 SEC_CONST_DECL (kSecPropertyTypeError
, "error");
212 SEC_CONST_DECL (kSecPropertyTypeSuccess
, "success");
213 SEC_CONST_DECL (kSecPropertyTypeTitle
, "title");
214 SEC_CONST_DECL (kSecPropertyTypeSection
, "section");
215 SEC_CONST_DECL (kSecPropertyTypeData
, "data");
216 SEC_CONST_DECL (kSecPropertyTypeString
, "string");
217 SEC_CONST_DECL (kSecPropertyTypeURL
, "url");
218 SEC_CONST_DECL (kSecPropertyTypeDate
, "date");
219 SEC_CONST_DECL (kSecPropertyTypeArray
, "array");
220 SEC_CONST_DECL (kSecPropertyTypeNumber
, "number");
222 /* Extension parsing routine. */
223 typedef bool (*SecCertificateExtensionParser
)(SecCertificateRef certificate
,
224 const SecCertificateExtension
*extn
);
226 /* Mapping from extension OIDs (as a DERItem *) to
227 SecCertificateExtensionParser extension parsing routines. */
228 static CFDictionaryRef sExtensionParsers
;
230 /* Forward declarations of static functions. */
231 static CFStringRef
SecCertificateCopyDescription(CFTypeRef cf
);
232 static void SecCertificateDestroy(CFTypeRef cf
);
233 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
234 CFAbsoluteTime
*absTime
) __attribute__((__nonnull__
));
236 /* Static functions. */
237 static CFStringRef
SecCertificateCopyDescription(CFTypeRef cf
) {
238 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
239 CFStringRef subject
= SecCertificateCopySubjectSummary(certificate
);
240 CFStringRef issuer
= SecCertificateCopyIssuerSummary(certificate
);
241 CFStringRef desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
242 CFSTR("<cert(%p) s: %@ i: %@>"), certificate
, subject
, issuer
);
243 CFReleaseSafe(issuer
);
244 CFReleaseSafe(subject
);
248 static void SecCertificateDestroy(CFTypeRef cf
) {
249 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
250 if (certificate
->_certificatePolicies
.policies
) {
251 free(certificate
->_certificatePolicies
.policies
);
252 certificate
->_certificatePolicies
.policies
= NULL
;
254 if (certificate
->_policyMappings
.mappings
) {
255 free(certificate
->_policyMappings
.mappings
);
256 certificate
->_policyMappings
.mappings
= NULL
;
258 CFReleaseNull(certificate
->_crlDistributionPoints
);
259 CFReleaseNull(certificate
->_ocspResponders
);
260 CFReleaseNull(certificate
->_caIssuers
);
261 if (certificate
->_extensions
) {
262 free(certificate
->_extensions
);
263 certificate
->_extensions
= NULL
;
265 CFReleaseNull(certificate
->_pubKey
);
266 CFReleaseNull(certificate
->_der_data
);
267 CFReleaseNull(certificate
->_properties
);
268 CFReleaseNull(certificate
->_serialNumber
);
269 CFReleaseNull(certificate
->_normalizedIssuer
);
270 CFReleaseNull(certificate
->_normalizedSubject
);
271 CFReleaseNull(certificate
->_authorityKeyID
);
272 CFReleaseNull(certificate
->_subjectKeyID
);
273 CFReleaseNull(certificate
->_sha1Digest
);
274 CFReleaseNull(certificate
->_keychain_item
);
275 CFReleaseNull(certificate
->_permittedSubtrees
);
276 CFReleaseNull(certificate
->_excludedSubtrees
);
279 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
280 SecCertificateRef cert1
= (SecCertificateRef
)cf1
;
281 SecCertificateRef cert2
= (SecCertificateRef
)cf2
;
284 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
286 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
289 /* Hash of the certificate is der length + signature length + last 4 bytes
291 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
292 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
293 size_t der_length
= certificate
->_der
.length
;
294 size_t sig_length
= certificate
->_signature
.length
;
295 size_t ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
296 CFHashCode hashCode
= 0;
297 for (; ix
< sig_length
; ++ix
)
298 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
300 return (hashCode
+ der_length
+ sig_length
);
305 /************************************************************************/
306 /************************* General Name Parsing *************************/
307 /************************************************************************/
309 GeneralName ::= CHOICE {
310 otherName [0] OtherName,
311 rfc822Name [1] IA5String,
312 dNSName [2] IA5String,
313 x400Address [3] ORAddress,
314 directoryName [4] Name,
315 ediPartyName [5] EDIPartyName,
316 uniformResourceIdentifier [6] IA5String,
317 iPAddress [7] OCTET STRING,
318 registeredID [8] OBJECT IDENTIFIER}
320 OtherName ::= SEQUENCE {
321 type-id OBJECT IDENTIFIER,
322 value [0] EXPLICIT ANY DEFINED BY type-id }
324 EDIPartyName ::= SEQUENCE {
325 nameAssigner [0] DirectoryString OPTIONAL,
326 partyName [1] DirectoryString }
328 OSStatus
SecCertificateParseGeneralNameContentProperty(DERTag tag
,
329 const DERItem
*generalNameContent
,
330 void *context
, parseGeneralNameCallback callback
) {
332 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
333 return callback(context
, GNT_OtherName
, generalNameContent
);
334 case ASN1_CONTEXT_SPECIFIC
| 1:
335 return callback(context
, GNT_RFC822Name
, generalNameContent
);
336 case ASN1_CONTEXT_SPECIFIC
| 2:
337 return callback(context
, GNT_DNSName
, generalNameContent
);
338 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
339 return callback(context
, GNT_X400Address
, generalNameContent
);
340 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
341 return callback(context
, GNT_DirectoryName
, generalNameContent
);
342 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
343 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
344 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
346 /* Technically I don't think this is valid, but there are certs out
347 in the wild that use a constructed IA5String. In particular the
348 VeriSign Time Stamping Authority CA.cer does this. */
349 DERDecodedInfo uriContent
;
350 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
351 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
352 return callback(context
, GNT_URI
, &uriContent
.content
);
354 case ASN1_CONTEXT_SPECIFIC
| 6:
355 return callback(context
, GNT_URI
, generalNameContent
);
356 case ASN1_CONTEXT_SPECIFIC
| 7:
357 return callback(context
, GNT_IPAddress
, generalNameContent
);
358 case ASN1_CONTEXT_SPECIFIC
| 8:
359 return callback(context
, GNT_RegisteredID
, generalNameContent
);
364 return errSecInvalidCertificate
;
367 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
368 void *context
, parseGeneralNameCallback callback
) {
370 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
371 require_noerr_quiet(drtn
, badDER
);
372 DERDecodedInfo generalNameContent
;
373 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
375 OSStatus status
= SecCertificateParseGeneralNameContentProperty(
376 generalNameContent
.tag
, &generalNameContent
.content
, context
,
381 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
382 return errSecSuccess
;
385 return errSecInvalidCertificate
;
388 OSStatus
SecCertificateParseGeneralNames(const DERItem
*generalNames
, void *context
,
389 parseGeneralNameCallback callback
) {
390 DERDecodedInfo generalNamesContent
;
391 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
392 require_noerr_quiet(drtn
, badDER
);
393 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
394 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
397 return errSecInvalidCertificate
;
403 GeneralName ::= CHOICE {
404 otherName [0] OtherName,
405 rfc822Name [1] IA5String,
406 dNSName [2] IA5String,
407 x400Address [3] ORAddress,
408 directoryName [4] Name,
409 ediPartyName [5] EDIPartyName,
410 uniformResourceIdentifier [6] IA5String,
411 iPAddress [7] OCTET STRING,
412 registeredID [8] OBJECT IDENTIFIER}
414 EDIPartyName ::= SEQUENCE {
415 nameAssigner [0] DirectoryString OPTIONAL,
416 partyName [1] DirectoryString }
418 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
419 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
421 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
422 generalName
->nameType
= GNT_OtherName
;
423 generalName
->berEncoded
= true;
424 generalName
->name
= *generalNameContent
;
426 case ASN1_CONTEXT_SPECIFIC
| 1:
428 generalName
->nameType
= GNT_RFC822Name
;
429 generalName
->berEncoded
= false;
430 generalName
->name
= *generalNameContent
;
432 case ASN1_CONTEXT_SPECIFIC
| 2:
434 generalName
->nameType
= GNT_DNSName
;
435 generalName
->berEncoded
= false;
436 generalName
->name
= *generalNameContent
;
438 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
439 generalName
->nameType
= GNT_X400Address
;
440 generalName
->berEncoded
= true;
441 generalName
->name
= *generalNameContent
;
443 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
444 generalName
->nameType
= GNT_DirectoryName
;
445 generalName
->berEncoded
= true;
446 generalName
->name
= *generalNameContent
;
448 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
449 generalName
->nameType
= GNT_EdiPartyName
;
450 generalName
->berEncoded
= true;
451 generalName
->name
= *generalNameContent
;
453 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
455 /* Technically I don't think this is valid, but there are certs out
456 in the wild that use a constructed IA5String. In particular the
457 VeriSign Time Stamping Authority CA.cer does this. */
458 DERDecodedInfo decoded
;
459 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
460 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
461 generalName
->nameType
= GNT_URI
;
462 generalName
->berEncoded
= false;
463 generalName
->name
= decoded
.content
;
466 case ASN1_CONTEXT_SPECIFIC
| 6:
467 generalName
->nameType
= GNT_URI
;
468 generalName
->berEncoded
= false;
469 generalName
->name
= *generalNameContent
;
471 case ASN1_CONTEXT_SPECIFIC
| 7:
472 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
473 8 octects, addr/mask for ipv6 it's 32. */
474 generalName
->nameType
= GNT_IPAddress
;
475 generalName
->berEncoded
= false;
476 generalName
->name
= *generalNameContent
;
478 case ASN1_CONTEXT_SPECIFIC
| 8:
479 /* name is the content of an OID. */
480 generalName
->nameType
= GNT_RegisteredID
;
481 generalName
->berEncoded
= false;
482 generalName
->name
= *generalNameContent
;
488 return errSecSuccess
;
490 return errSecInvalidCertificate
;
494 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
496 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
497 CFIndex
*count
, SecCEGeneralName
**name
) {
498 SecCEGeneralName
*generalNames
= NULL
;
500 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
501 require_noerr_quiet(drtn
, badDER
);
502 DERDecodedInfo generalNameContent
;
503 CFIndex generalNamesCount
= 0;
504 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
508 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
510 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
512 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
514 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
516 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
517 &generalNameContent
.content
, &generalNames
[ix
])) {
522 *count
= generalNamesCount
;
523 *name
= generalNames
;
524 return errSecSuccess
;
529 return errSecInvalidCertificate
;
532 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
533 CFIndex
*count
, SecCEGeneralName
**name
) {
534 DERDecodedInfo generalNamesContent
;
535 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
536 require_noerr_quiet(drtn
, badDER
);
537 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
539 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
540 return errSecSuccess
;
542 return errSecInvalidCertificate
;
546 /************************************************************************/
547 /************************** X.509 Name Parsing **************************/
548 /************************************************************************/
550 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
551 const DERItem
*value
, CFIndex rdnIX
, bool localized
);
553 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
554 parseX501NameCallback callback
, bool localized
) {
556 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
557 require_noerr_quiet(drtn
, badDER
);
558 DERDecodedInfo atvContent
;
560 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
561 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
562 DERAttributeTypeAndValue atv
;
563 drtn
= DERParseSequenceContent(&atvContent
.content
,
564 DERNumAttributeTypeAndValueItemSpecs
,
565 DERAttributeTypeAndValueItemSpecs
,
567 require_noerr_quiet(drtn
, badDER
);
568 require_quiet(atv
.type
.length
!= 0, badDER
);
569 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++, localized
);
574 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
576 return errSecSuccess
;
578 return errSecInvalidCertificate
;
581 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
582 parseX501NameCallback callback
, bool localized
) {
584 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
585 require_noerr_quiet(drtn
, badDER
);
586 DERDecodedInfo currDecoded
;
587 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
588 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
589 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
590 callback
, localized
);
595 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
597 return errSecSuccess
;
600 return errSecInvalidCertificate
;
603 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
604 parseX501NameCallback callback
, bool localized
) {
605 DERDecodedInfo x501NameContent
;
606 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
607 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
608 return errSecInvalidCertificate
;
610 return parseX501NameContent(&x501NameContent
.content
, context
,
611 callback
, localized
);
615 /************************************************************************/
616 /********************** Extension Parsing Routines **********************/
617 /************************************************************************/
619 static bool SecCEPSubjectKeyIdentifier(SecCertificateRef certificate
,
620 const SecCertificateExtension
*extn
) {
621 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
622 DERDecodedInfo keyIdentifier
;
623 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
624 require_noerr_quiet(drtn
, badDER
);
625 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
626 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
630 secwarning("Invalid SubjectKeyIdentifier Extension");
634 static bool SecCEPKeyUsage(SecCertificateRef certificate
,
635 const SecCertificateExtension
*extn
) {
636 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
637 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
638 DERDecodedInfo bitStringContent
;
639 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
640 require_noerr_quiet(drtn
, badDER
);
641 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
642 /* check that there's no extra bytes at the end */
643 require_quiet(bitStringContent
.content
.data
+ bitStringContent
.content
.length
== extn
->extnValue
.data
+ extn
->extnValue
.length
, badDER
);
644 DERSize len
= bitStringContent
.content
.length
- 1;
645 require_quiet(len
== 1 || len
== 2, badDER
);
646 DERByte numUnusedBits
= bitStringContent
.content
.data
[0];
647 require_quiet(numUnusedBits
< 8, badDER
);
648 /* Flip the bits in the bit string so the first bit is the lsb. */
649 uint16_t bits
= 8 * len
- numUnusedBits
;
650 uint16_t value
= bitStringContent
.content
.data
[1];
653 value
= (value
<< 8) + bitStringContent
.content
.data
[2];
659 for (ix
= 0; ix
< bits
; ++ix
) {
665 certificate
->_keyUsage
= keyUsage
;
668 certificate
->_keyUsage
= kSecKeyUsageUnspecified
;
669 secwarning("Invalid KeyUsage Extension");
673 static bool SecCEPPrivateKeyUsagePeriod(SecCertificateRef certificate
,
674 const SecCertificateExtension
*extn
) {
675 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
679 static bool SecCEPSubjectAltName(SecCertificateRef certificate
,
680 const SecCertificateExtension
*extn
) {
681 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
682 certificate
->_subjectAltName
= extn
;
686 static bool SecCEPIssuerAltName(SecCertificateRef certificate
,
687 const SecCertificateExtension
*extn
) {
688 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
692 static bool SecCEPBasicConstraints(SecCertificateRef certificate
,
693 const SecCertificateExtension
*extn
) {
694 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
695 DERBasicConstraints basicConstraints
;
696 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
697 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
698 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
699 require_noerr_quiet(DERParseBooleanWithDefault(&basicConstraints
.cA
, false,
700 &certificate
->_basicConstraints
.isCA
), badDER
);
701 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
702 require_noerr_quiet(DERParseInteger(
703 &basicConstraints
.pathLenConstraint
,
704 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
705 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
707 certificate
->_basicConstraints
.present
= true;
708 certificate
->_basicConstraints
.critical
= extn
->critical
;
711 certificate
->_basicConstraints
.present
= false;
712 secwarning("Invalid BasicConstraints Extension");
718 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
720 * NameConstraints ::= SEQUENCE {
721 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
722 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
724 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
726 * GeneralSubtree ::= SEQUENCE {
728 * minimum [0] BaseDistance DEFAULT 0,
729 * maximum [1] BaseDistance OPTIONAL }
731 * BaseDistance ::= INTEGER (0..MAX)
733 static DERReturn
parseGeneralSubtrees(DERItem
*derSubtrees
, CFArrayRef
*generalSubtrees
) {
734 CFMutableArrayRef gs
= NULL
;
736 DERReturn drtn
= DERDecodeSeqContentInit(derSubtrees
, &gsSeq
);
737 require_noerr_quiet(drtn
, badDER
);
738 DERDecodedInfo gsContent
;
739 require_quiet(gs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
740 &kCFTypeArrayCallBacks
),
742 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
743 DERGeneralSubtree derGS
;
744 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
745 drtn
= DERParseSequenceContent(&gsContent
.content
,
746 DERNumGeneralSubtreeItemSpecs
,
747 DERGeneralSubtreeItemSpecs
,
748 &derGS
, sizeof(derGS
));
749 require_noerr_quiet(drtn
, badDER
);
752 * Within this profile, the minimum and maximum fields are not used with
753 * any name forms, thus, the minimum MUST be zero, and maximum MUST be
756 * Because minimum DEFAULT 0, absence equivalent to present and 0.
758 if (derGS
.minimum
.length
) {
760 require_noerr_quiet(DERParseInteger(&derGS
.minimum
, &minimum
),
762 require_quiet(minimum
== 0, badDER
);
764 require_quiet(derGS
.maximum
.length
== 0, badDER
);
765 require_quiet(derGS
.generalName
.length
!= 0, badDER
);
767 CFDataRef generalName
= NULL
;
768 require_quiet(generalName
= CFDataCreate(kCFAllocatorDefault
,
769 derGS
.generalName
.data
,
770 derGS
.generalName
.length
),
772 CFArrayAppendValue(gs
, generalName
);
773 CFReleaseNull(generalName
);
775 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
777 // since generalSubtrees is a pointer to an instance variable,
778 // make sure we release the existing array before assignment.
779 CFReleaseSafe(*generalSubtrees
);
780 *generalSubtrees
= gs
;
786 secdebug("cert","failed to parse GeneralSubtrees");
790 static bool SecCEPNameConstraints(SecCertificateRef certificate
,
791 const SecCertificateExtension
*extn
) {
792 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
793 DERNameConstraints nc
;
795 drtn
= DERParseSequence(&extn
->extnValue
,
796 DERNumNameConstraintsItemSpecs
,
797 DERNameConstraintsItemSpecs
,
799 require_noerr_quiet(drtn
, badDER
);
800 if (nc
.permittedSubtrees
.length
) {
801 require_noerr_quiet(parseGeneralSubtrees(&nc
.permittedSubtrees
, &certificate
->_permittedSubtrees
), badDER
);
803 if (nc
.excludedSubtrees
.length
) {
804 require_noerr_quiet(parseGeneralSubtrees(&nc
.excludedSubtrees
, &certificate
->_excludedSubtrees
), badDER
);
809 secwarning("Invalid Name Constraints extension");
813 static OSStatus
appendCRLDPFromGeneralNames(void *context
, SecCEGeneralNameType type
,
814 const DERItem
*value
) {
815 CFMutableArrayRef
*crlDPs
= (CFMutableArrayRef
*)context
;
816 if (type
== GNT_URI
) {
818 url
= CFURLCreateWithBytes(NULL
, value
->data
, value
->length
, kCFStringEncodingASCII
, NULL
);
821 *crlDPs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
823 CFArrayAppendValue(*crlDPs
, url
);
827 return errSecSuccess
;
831 id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= { id-ce 31 }
833 CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
835 DistributionPoint ::= SEQUENCE {
836 distributionPoint [0] DistributionPointName OPTIONAL,
837 reasons [1] ReasonFlags OPTIONAL,
838 cRLIssuer [2] GeneralNames OPTIONAL }
840 DistributionPointName ::= CHOICE {
841 fullName [0] GeneralNames,
842 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
844 static bool SecCEPCrlDistributionPoints(SecCertificateRef certificate
,
845 const SecCertificateExtension
*extn
) {
846 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
847 DERSequence crlDPSeq
;
849 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &crlDPSeq
);
850 require_noerr_quiet(drtn
, badDER
);
851 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
852 DERDecodedInfo dpContent
;
853 while ((drtn
= DERDecodeSeqNext(&crlDPSeq
, &dpContent
)) == DR_Success
) {
854 require_quiet(dpContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
855 DERDistributionPoint dp
;
856 drtn
= DERParseSequenceContent(&dpContent
.content
, DERNumDistributionPointItemSpecs
,
857 DERDistributionPointItemSpecs
, &dp
, sizeof(dp
));
858 require_noerr_quiet(drtn
, badDER
);
859 require_quiet(dp
.distributionPoint
.data
|| dp
.cRLIssuer
.data
, badDER
);
860 if (dp
.distributionPoint
.data
) {
861 DERDecodedInfo dpName
;
862 drtn
= DERDecodeItem(&dp
.distributionPoint
, &dpName
);
863 require_noerr_quiet(drtn
, badDER
);
864 switch (dpName
.tag
) {
865 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
866 drtn
= parseGeneralNamesContent(&dpName
.content
, &certificate
->_crlDistributionPoints
,
867 appendCRLDPFromGeneralNames
);
868 require_noerr_quiet(drtn
, badDER
);
870 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1:
871 /* RelativeDistinguishName. Nothing we can do with that. */
877 if (dp
.cRLIssuer
.data
) {
878 drtn
= parseGeneralNamesContent(&dp
.cRLIssuer
, &certificate
->_crlDistributionPoints
,
879 appendCRLDPFromGeneralNames
);
880 require_noerr_quiet(drtn
, badDER
);
883 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
886 secwarning("Invalid CRL Distribution Points extension");
891 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
893 PolicyInformation ::= SEQUENCE {
894 policyIdentifier CertPolicyId,
895 policyQualifiers SEQUENCE SIZE (1..MAX) OF
896 PolicyQualifierInfo OPTIONAL }
898 CertPolicyId ::= OBJECT IDENTIFIER
900 PolicyQualifierInfo ::= SEQUENCE {
901 policyQualifierId PolicyQualifierId,
902 qualifier ANY DEFINED BY policyQualifierId }
904 /* maximum number of policies of 8192 seems more than adequate */
905 #define MAX_CERTIFICATE_POLICIES 8192
906 static bool SecCEPCertificatePolicies(SecCertificateRef certificate
,
907 const SecCertificateExtension
*extn
) {
908 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
911 SecCEPolicyInformation
*policies
= NULL
;
912 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
913 require_noerr_quiet(drtn
, badDER
);
914 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
915 DERDecodedInfo piContent
;
916 DERSize policy_count
= 0;
917 while ((policy_count
< MAX_CERTIFICATE_POLICIES
) &&
918 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
919 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
922 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
923 require_quiet(policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
)
924 * (policy_count
> 0 ? policy_count
: 1)),
926 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
927 require_noerr_quiet(drtn
, badDER
);
928 DERSize policy_ix
= 0;
929 while ((policy_ix
< (policy_count
> 0 ? policy_count
: 1)) &&
930 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
931 DERPolicyInformation pi
;
932 drtn
= DERParseSequenceContent(&piContent
.content
,
933 DERNumPolicyInformationItemSpecs
,
934 DERPolicyInformationItemSpecs
,
936 require_noerr_quiet(drtn
, badDER
);
937 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
938 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
940 certificate
->_certificatePolicies
.present
= true;
941 certificate
->_certificatePolicies
.critical
= extn
->critical
;
942 certificate
->_certificatePolicies
.numPolicies
= policy_count
;
943 certificate
->_certificatePolicies
.policies
= policies
;
948 certificate
->_certificatePolicies
.present
= false;
949 secwarning("Invalid CertificatePolicies Extension");
954 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
956 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
957 issuerDomainPolicy CertPolicyId,
958 subjectDomainPolicy CertPolicyId }
960 #define MAX_POLICY_MAPPINGS 8192
961 static bool SecCEPPolicyMappings(SecCertificateRef certificate
,
962 const SecCertificateExtension
*extn
) {
963 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
966 SecCEPolicyMapping
*mappings
= NULL
;
967 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
968 require_noerr_quiet(drtn
, badDER
);
969 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
970 DERDecodedInfo pmContent
;
971 DERSize mapping_count
= 0;
972 while ((mapping_count
< MAX_POLICY_MAPPINGS
) &&
973 (drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
974 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
977 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
978 require_quiet(mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
979 * (mapping_count
> 0 ? mapping_count
: 1)),
981 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
982 require_noerr_quiet(drtn
, badDER
);
983 DERSize mapping_ix
= 0;
984 while ((mapping_ix
< (mapping_count
> 0 ? mapping_count
: 1)) &&
985 (drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
986 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
988 drtn
= DERParseSequenceContent(&pmContent
.content
,
989 DERNumPolicyMappingItemSpecs
,
990 DERPolicyMappingItemSpecs
,
992 require_noerr_quiet(drtn
, badDER
);
993 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
994 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
996 certificate
->_policyMappings
.present
= true;
997 certificate
->_policyMappings
.critical
= extn
->critical
;
998 certificate
->_policyMappings
.numMappings
= mapping_count
;
999 certificate
->_policyMappings
.mappings
= mappings
;
1005 certificate
->_policyMappings
.present
= false;
1006 secwarning("Invalid CertificatePolicies Extension");
1011 AuthorityKeyIdentifier ::= SEQUENCE {
1012 keyIdentifier [0] KeyIdentifier OPTIONAL,
1013 authorityCertIssuer [1] GeneralNames OPTIONAL,
1014 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
1015 -- authorityCertIssuer and authorityCertSerialNumber MUST both
1016 -- be present or both be absent
1018 KeyIdentifier ::= OCTET STRING
1020 static bool SecCEPAuthorityKeyIdentifier(SecCertificateRef certificate
,
1021 const SecCertificateExtension
*extn
) {
1022 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1023 DERAuthorityKeyIdentifier akid
;
1025 drtn
= DERParseSequence(&extn
->extnValue
,
1026 DERNumAuthorityKeyIdentifierItemSpecs
,
1027 DERAuthorityKeyIdentifierItemSpecs
,
1028 &akid
, sizeof(akid
));
1029 require_noerr_quiet(drtn
, badDER
);
1030 if (akid
.keyIdentifier
.length
) {
1031 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
1033 if (akid
.authorityCertIssuer
.length
||
1034 akid
.authorityCertSerialNumber
.length
) {
1035 require_quiet(akid
.authorityCertIssuer
.length
&&
1036 akid
.authorityCertSerialNumber
.length
, badDER
);
1037 /* Perhaps put in a subsection called Authority Certificate Issuer. */
1038 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
1039 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
1044 secwarning("Invalid AuthorityKeyIdentifier Extension");
1048 static bool SecCEPPolicyConstraints(SecCertificateRef certificate
,
1049 const SecCertificateExtension
*extn
) {
1050 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1051 DERPolicyConstraints pc
;
1053 drtn
= DERParseSequence(&extn
->extnValue
,
1054 DERNumPolicyConstraintsItemSpecs
,
1055 DERPolicyConstraintsItemSpecs
,
1057 require_noerr_quiet(drtn
, badDER
);
1058 if (pc
.requireExplicitPolicy
.length
) {
1059 require_noerr_quiet(DERParseInteger(
1060 &pc
.requireExplicitPolicy
,
1061 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
1062 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
1064 if (pc
.inhibitPolicyMapping
.length
) {
1065 require_noerr_quiet(DERParseInteger(
1066 &pc
.inhibitPolicyMapping
,
1067 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
1068 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
1071 certificate
->_policyConstraints
.present
= true;
1072 certificate
->_policyConstraints
.critical
= extn
->critical
;
1076 certificate
->_policyConstraints
.present
= false;
1077 secwarning("Invalid PolicyConstraints Extension");
1081 static bool SecCEPExtendedKeyUsage(SecCertificateRef certificate
,
1082 const SecCertificateExtension
*extn
) {
1083 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1086 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &ekuTag
, &ekuSeq
);
1087 require_quiet((drtn
== DR_Success
) && (ekuTag
== ASN1_CONSTR_SEQUENCE
), badDER
);
1088 DERDecodedInfo ekuContent
;
1089 while ((drtn
= DERDecodeSeqNext(&ekuSeq
, &ekuContent
)) == DR_Success
) {
1090 require_quiet(ekuContent
.tag
== ASN1_OBJECT_ID
, badDER
);
1092 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1095 secwarning("Invalidate EKU Extension");
1100 InhibitAnyPolicy ::= SkipCerts
1102 SkipCerts ::= INTEGER (0..MAX)
1104 static bool SecCEPInhibitAnyPolicy(SecCertificateRef certificate
,
1105 const SecCertificateExtension
*extn
) {
1106 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1107 DERDecodedInfo iapContent
;
1108 require_noerr_quiet(DERDecodeItem(&extn
->extnValue
, &iapContent
), badDER
);
1109 require_quiet(iapContent
.tag
== ASN1_INTEGER
, badDER
);
1110 require_noerr_quiet(DERParseInteger(
1111 &iapContent
.content
,
1112 &certificate
->_inhibitAnyPolicySkipCerts
.skipCerts
), badDER
);
1114 certificate
->_inhibitAnyPolicySkipCerts
.present
= true;
1115 certificate
->_inhibitAnyPolicySkipCerts
.critical
= extn
->critical
;
1118 certificate
->_inhibitAnyPolicySkipCerts
.present
= false;
1119 secwarning("Invalid InhibitAnyPolicy Extension");
1124 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
1126 AuthorityInfoAccessSyntax ::=
1127 SEQUENCE SIZE (1..MAX) OF AccessDescription
1129 AccessDescription ::= SEQUENCE {
1130 accessMethod OBJECT IDENTIFIER,
1131 accessLocation GeneralName }
1133 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
1135 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
1137 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
1139 static bool SecCEPAuthorityInfoAccess(SecCertificateRef certificate
,
1140 const SecCertificateExtension
*extn
) {
1141 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1144 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
1145 require_noerr_quiet(drtn
, badDER
);
1146 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1147 DERDecodedInfo adContent
;
1148 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
1149 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1150 DERAccessDescription ad
;
1151 drtn
= DERParseSequenceContent(&adContent
.content
,
1152 DERNumAccessDescriptionItemSpecs
,
1153 DERAccessDescriptionItemSpecs
,
1155 require_noerr_quiet(drtn
, badDER
);
1156 CFMutableArrayRef
*urls
;
1157 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
1158 urls
= &certificate
->_ocspResponders
;
1159 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
1160 urls
= &certificate
->_caIssuers
;
1164 DERDecodedInfo generalNameContent
;
1165 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
1166 require_noerr_quiet(drtn
, badDER
);
1167 switch (generalNameContent
.tag
) {
1169 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
1170 /* Technically I don't think this is valid, but there are certs out
1171 in the wild that use a constructed IA5String. In particular the
1172 VeriSign Time Stamping Authority CA.cer does this. */
1174 case ASN1_CONTEXT_SPECIFIC
| 6:
1176 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1177 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
1178 kCFStringEncodingASCII
, NULL
);
1181 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1182 CFArrayAppendValue(*urls
, url
);
1188 secdebug("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02llx v: %.*s",
1189 generalNameContent
.tag
, (int) generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1194 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1197 secwarning("Invalid Authority Information Access extension");
1201 /* Apple Worldwide Developer Relations Certificate Authority subject name.
1202 * This is a DER sequence with the leading tag and length bytes removed,
1203 * to match what tbsCert.issuer contains.
1205 static const unsigned char Apple_WWDR_CA_Subject_Name
[]={
1206 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,
1207 0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0A,0x41,0x70,0x70,0x6C,0x65,
1208 0x20,0x49,0x6E,0x63,0x2E,0x31,0x2C,0x30,0x2A,0x06,0x03,0x55,0x04,0x0B,0x0C,0x23,
1209 0x41,0x70,0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,
1210 0x44,0x65,0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,
1211 0x6F,0x6E,0x73,0x31,0x44,0x30,0x42,0x06,0x03,0x55,0x04,0x03,0x0C,0x3B,0x41,0x70,
1212 0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,0x44,0x65,
1213 0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,0x6F,0x6E,
1214 0x73,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,
1215 0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79
1218 static void checkForMissingRevocationInfo(SecCertificateRef certificate
) {
1220 certificate
->_crlDistributionPoints
||
1221 certificate
->_ocspResponders
) {
1222 /* We already have an OCSP or CRL URI (or no cert) */
1225 /* Specify an appropriate OCSP responder if we recognize the issuer. */
1226 CFURLRef url
= NULL
;
1227 if (sizeof(Apple_WWDR_CA_Subject_Name
) == certificate
->_issuer
.length
&&
1228 !memcmp(certificate
->_issuer
.data
, Apple_WWDR_CA_Subject_Name
,
1229 sizeof(Apple_WWDR_CA_Subject_Name
))) {
1230 const char *WWDR_OCSP_URI
= "http://ocsp.apple.com/ocsp-wwdr01";
1231 url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1232 (const UInt8
*)WWDR_OCSP_URI
, strlen(WWDR_OCSP_URI
),
1233 kCFStringEncodingASCII
, NULL
);
1236 CFMutableArrayRef
*urls
= &certificate
->_ocspResponders
;
1237 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1238 CFArrayAppendValue(*urls
, url
);
1243 static bool SecCEPSubjectInfoAccess(SecCertificateRef certificate
,
1244 const SecCertificateExtension
*extn
) {
1245 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1249 static bool SecCEPNetscapeCertType(SecCertificateRef certificate
,
1250 const SecCertificateExtension
*extn
) {
1251 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1255 static bool SecCEPEntrustVersInfo(SecCertificateRef certificate
,
1256 const SecCertificateExtension
*extn
) {
1257 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1261 static bool SecCEPEscrowMarker(SecCertificateRef certificate
,
1262 const SecCertificateExtension
*extn
) {
1263 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1267 static bool SecCEPOCSPNoCheck(SecCertificateRef certificate
,
1268 const SecCertificateExtension
*extn
) {
1269 secdebug("cert", "ocsp-nocheck critical: %s", extn
->critical
? "yes" : "no");
1273 /* Dictionary key callback for comparing to DERItems. */
1274 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1275 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1278 /* Dictionary key callback calculating the hash of a DERItem. */
1279 static CFHashCode
SecDERItemHash(const void *value
) {
1280 const DERItem
*derItem
= (const DERItem
*)value
;
1281 CFHashCode hash
= derItem
->length
;
1282 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1283 for (; ix
< derItem
->length
; ++ix
) {
1284 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1290 /* Dictionary key callbacks using the above 2 functions. */
1291 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1295 NULL
, /* copyDescription */
1296 SecDERItemEqual
, /* equal */
1297 SecDERItemHash
/* hash */
1300 static void SecCertificateInitializeExtensionParsers(void) {
1301 /* Build a dictionary that maps from extension OIDs to callback functions
1302 which can parse the extension of the type given. */
1303 static const void *extnOIDs
[] = {
1304 &oidSubjectKeyIdentifier
,
1306 &oidPrivateKeyUsagePeriod
,
1309 &oidBasicConstraints
,
1310 &oidNameConstraints
,
1311 &oidCrlDistributionPoints
,
1312 &oidCertificatePolicies
,
1314 &oidAuthorityKeyIdentifier
,
1315 &oidPolicyConstraints
,
1316 &oidExtendedKeyUsage
,
1317 &oidInhibitAnyPolicy
,
1318 &oidAuthorityInfoAccess
,
1319 &oidSubjectInfoAccess
,
1320 &oidNetscapeCertType
,
1321 &oidEntrustVersInfo
,
1322 &oidApplePolicyEscrowService
,
1325 static const void *extnParsers
[] = {
1326 SecCEPSubjectKeyIdentifier
,
1328 SecCEPPrivateKeyUsagePeriod
,
1329 SecCEPSubjectAltName
,
1330 SecCEPIssuerAltName
,
1331 SecCEPBasicConstraints
,
1332 SecCEPNameConstraints
,
1333 SecCEPCrlDistributionPoints
,
1334 SecCEPCertificatePolicies
,
1335 SecCEPPolicyMappings
,
1336 SecCEPAuthorityKeyIdentifier
,
1337 SecCEPPolicyConstraints
,
1338 SecCEPExtendedKeyUsage
,
1339 SecCEPInhibitAnyPolicy
,
1340 SecCEPAuthorityInfoAccess
,
1341 SecCEPSubjectInfoAccess
,
1342 SecCEPNetscapeCertType
,
1343 SecCEPEntrustVersInfo
,
1347 sExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1348 extnParsers
, array_size(extnOIDs
),
1349 &SecDERItemKeyCallBacks
, NULL
);
1352 CFGiblisWithFunctions(SecCertificate
, NULL
, NULL
, SecCertificateDestroy
, SecCertificateEqual
, SecCertificateHash
, NULL
, SecCertificateCopyDescription
, NULL
, NULL
, ^{
1353 SecCertificateInitializeExtensionParsers();
1356 static bool isAppleExtensionOID(const DERItem
*extnID
)
1358 static const uint8_t appleExtensionArc
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x06 };
1359 static const uint8_t appleComponentExtensionArc
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0b };
1360 static const uint8_t appleSigningExtensionArc
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0c };
1361 static const uint8_t appleEncryptionExtensionArc
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0d };
1362 if (!extnID
&& !extnID
->data
&& extnID
->length
<= sizeof(appleExtensionArc
)) {
1365 return (!memcmp(extnID
->data
, appleExtensionArc
, sizeof(appleExtensionArc
)) ||
1366 !memcmp(extnID
->data
, appleComponentExtensionArc
, sizeof(appleComponentExtensionArc
)) ||
1367 !memcmp(extnID
->data
, appleSigningExtensionArc
, sizeof(appleSigningExtensionArc
)) ||
1368 !memcmp(extnID
->data
, appleEncryptionExtensionArc
, sizeof(appleEncryptionExtensionArc
)));
1371 /* Given the contents of an X.501 Name return the contents of a normalized
1373 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1374 const DERItem
*x501name
) {
1375 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1376 CFIndex length
= x501name
->length
;
1377 CFDataSetLength(result
, length
);
1378 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1381 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1383 require_noerr_quiet(drtn
, badDER
);
1386 /* Always points to last rdn tag. */
1387 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1388 /* Offset relative to base of current rdn set tag. */
1389 CFIndex rdnTagLocation
= 0;
1390 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1391 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1392 /* We don't allow empty RDNs. */
1393 require_quiet(rdn
.content
.length
!= 0, badDER
);
1394 /* Length of the tag and length of the current rdn. */
1395 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1396 CFIndex rdnContentLength
= rdn
.content
.length
;
1397 /* Copy the tag and length of the RDN. */
1398 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1401 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1402 require_quiet(drtn
== DR_Success
, badDER
);
1405 /* Always points to tag of current atv sequence. */
1406 const DERByte
*atvTag
= atvSeq
.nextItem
;
1407 /* Offset relative to base of current atv sequence tag. */
1408 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1409 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1410 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1411 /* Length of the tag and length of the current atv. */
1412 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1413 CFIndex atvContentLength
= atv
.content
.length
;
1414 /* Copy the tag and length of the atv and the atv itself. */
1415 memcpy(base
+ atvTagLocation
, atvTag
,
1416 atvTLLength
+ atv
.content
.length
);
1418 /* Now decode the atv sequence. */
1419 DERAttributeTypeAndValue atvPair
;
1420 drtn
= DERParseSequenceContent(&atv
.content
,
1421 DERNumAttributeTypeAndValueItemSpecs
,
1422 DERAttributeTypeAndValueItemSpecs
,
1423 &atvPair
, sizeof(atvPair
));
1424 require_noerr_quiet(drtn
, badDER
);
1425 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1426 DERDecodedInfo value
;
1427 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1428 require_noerr_quiet(drtn
, badDER
);
1430 /* (c) attribute values in PrintableString are not case sensitive
1431 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1433 (d) attribute values in PrintableString are compared after
1434 removing leading and trailing white space and converting internal
1435 substrings of one or more consecutive white space characters to a
1437 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1438 /* Offset relative to base of current value tag. */
1439 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1440 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1441 CFIndex valueContentLength
= value
.content
.length
;
1443 /* Now copy all the bytes, but convert to upper case while
1444 doing so and convert multiple whitespace chars into a
1446 bool lastWasBlank
= false;
1447 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1448 CFIndex valueCurrentLocation
= valueLocation
;
1450 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1451 UInt8 ch
= value
.content
.data
[ix
];
1456 /* Don't insert a space for first character
1458 if (valueCurrentLocation
> valueLocation
) {
1459 base
[valueCurrentLocation
++] = ' ';
1461 lastWasBlank
= true;
1464 lastWasBlank
= false;
1465 if ('a' <= ch
&& ch
<= 'z') {
1466 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1468 base
[valueCurrentLocation
++] = ch
;
1472 /* Finally if lastWasBlank remove the trailing space. */
1473 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1474 valueCurrentLocation
--;
1476 /* Adjust content length to normalized length. */
1477 valueContentLength
= valueCurrentLocation
- valueLocation
;
1479 /* Number of bytes by which the length should be shorted. */
1480 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1481 if (lengthDiff
== 0) {
1482 /* Easy case no need to adjust lengths. */
1484 /* Hard work we need to go back and fix up length fields
1486 1) The value itself.
1487 2) The ATV Sequence containing type/value
1488 3) The RDN Set containing one or more atv pairs.
1492 /* Step 1 fix up length of value. */
1493 /* Length of value tag and length minus the tag. */
1494 DERSize newValueTLLength
= valueTLLength
- 1;
1495 drtn
= DEREncodeLength(valueContentLength
,
1496 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1497 require(drtn
== DR_Success
, badDER
);
1498 /* Add the length of the tag back in. */
1500 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1502 /* The size of the length field changed, let's slide
1503 the value back by valueLLDiff bytes. */
1504 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1505 base
+ valueTagLocation
+ valueTLLength
,
1506 valueContentLength
);
1507 /* The length diff for the enclosing object. */
1508 lengthDiff
+= valueLLDiff
;
1511 /* Step 2 fix up length of the enclosing ATV Sequence. */
1512 atvContentLength
-= lengthDiff
;
1513 DERSize newATVTLLength
= atvTLLength
- 1;
1514 drtn
= DEREncodeLength(atvContentLength
,
1515 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1516 require(drtn
== DR_Success
, badDER
);
1517 /* Add the length of the tag back in. */
1519 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1521 /* The size of the length field changed, let's slide
1522 the value back by valueLLDiff bytes. */
1523 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1524 base
+ atvTagLocation
+ atvTLLength
,
1526 /* The length diff for the enclosing object. */
1527 lengthDiff
+= atvLLDiff
;
1528 atvTLLength
= newATVTLLength
;
1531 /* Step 3 fix up length of enclosing RDN Set. */
1532 rdnContentLength
-= lengthDiff
;
1533 DERSize newRDNTLLength
= rdnTLLength
- 1;
1534 drtn
= DEREncodeLength(rdnContentLength
,
1535 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1536 require_quiet(drtn
== DR_Success
, badDER
);
1537 /* Add the length of the tag back in. */
1539 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1541 /* The size of the length field changed, let's slide
1542 the value back by valueLLDiff bytes. */
1543 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1544 base
+ rdnTagLocation
+ rdnTLLength
,
1546 /* The length diff for the enclosing object. */
1547 lengthDiff
+= rdnLLDiff
;
1548 rdnTLLength
= newRDNTLLength
;
1550 /* Adjust the locations that might have changed due to
1552 atvTagLocation
-= rdnLLDiff
;
1554 (void) lengthDiff
; // No next object, silence analyzer
1557 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1558 atvTag
= atvSeq
.nextItem
;
1560 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1561 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1562 rdnTag
= rdnSeq
.nextItem
;
1564 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1565 /* Truncate the result to the proper length. */
1566 CFDataSetLength(result
, rdnTagLocation
);
1575 static CFDataRef
SecDERItemCopySequence(DERItem
*content
) {
1576 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
1577 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
1578 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
1580 CFDataSetLength(sequence
, sequence_length
);
1581 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
1582 *sequence_ptr
++ = ONE_BYTE_ASN1_CONSTR_SEQUENCE
;
1583 require_noerr_quiet(DEREncodeLength(content
->length
,
1584 sequence_ptr
, &seq_len_length
), out
);
1585 sequence_ptr
+= seq_len_length
;
1586 memcpy(sequence_ptr
, content
->data
, content
->length
);
1589 CFReleaseSafe(sequence
);
1593 static CFDataRef
SecCopySequenceFromContent(CFDataRef content
) {
1595 tmpItem
.data
= (void *)CFDataGetBytePtr(content
);
1596 tmpItem
.length
= CFDataGetLength(content
);
1598 return SecDERItemCopySequence(&tmpItem
);
1601 CFDataRef
SecDistinguishedNameCopyNormalizedContent(CFDataRef distinguished_name
)
1603 const DERItem name
= { (unsigned char *)CFDataGetBytePtr(distinguished_name
), CFDataGetLength(distinguished_name
) };
1604 DERDecodedInfo content
;
1605 /* Decode top level sequence into DERItem */
1606 if (!DERDecodeItem(&name
, &content
) && (content
.tag
== ASN1_CONSTR_SEQUENCE
))
1607 return createNormalizedX501Name(kCFAllocatorDefault
, &content
.content
);
1611 CFDataRef
SecDistinguishedNameCopyNormalizedSequence(CFDataRef distinguished_name
)
1613 if (!distinguished_name
) { return NULL
; }
1614 CFDataRef normalizedContent
= SecDistinguishedNameCopyNormalizedContent(distinguished_name
);
1615 if (!normalizedContent
) { return NULL
; }
1616 CFDataRef result
= SecCopySequenceFromContent(normalizedContent
);
1617 CFReleaseNull(normalizedContent
);
1621 /* AUDIT[securityd]:
1622 certificate->_der is a caller provided data of any length (might be 0).
1624 Top level certificate decode.
1626 static bool SecCertificateParse(SecCertificateRef certificate
)
1631 require_quiet(certificate
, badCert
);
1632 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1634 /* top level decode */
1635 DERSignedCertCrl signedCert
;
1636 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1637 DERSignedCertCrlItemSpecs
, &signedCert
,
1638 sizeof(signedCert
));
1639 require_noerr_quiet(drtn
, badCert
);
1640 /* Store tbs since we need to digest it for verification later on. */
1641 certificate
->_tbs
= signedCert
.tbs
;
1643 /* decode the TBSCert - it was saved in full DER form */
1645 drtn
= DERParseSequence(&signedCert
.tbs
,
1646 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1647 &tbsCert
, sizeof(tbsCert
));
1648 require_noerr_quiet(drtn
, badCert
);
1650 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1651 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1652 of the params field. */
1653 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1654 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1655 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1656 require_noerr_quiet(drtn
, badCert
);
1658 /* The contents of signedCert.sig is a bit string whose contents
1659 are the signature itself. */
1660 DERByte numUnusedBits
;
1661 drtn
= DERParseBitString(&signedCert
.sig
,
1662 &certificate
->_signature
, &numUnusedBits
);
1663 require_noerr_quiet(drtn
, badCert
);
1665 /* Now decode the tbsCert. */
1667 /* First we turn the optional version into an int. */
1668 if (tbsCert
.version
.length
) {
1669 DERDecodedInfo decoded
;
1670 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1671 require_noerr_quiet(drtn
, badCert
);
1672 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1673 require_quiet(decoded
.content
.length
== 1, badCert
);
1674 certificate
->_version
= decoded
.content
.data
[0];
1675 if (certificate
->_version
> 2) {
1676 secwarning("Invalid certificate version (%d), must be 0..2",
1677 certificate
->_version
);
1679 require_quiet(certificate
->_version
> 0, badCert
);
1680 require_quiet(certificate
->_version
< 3, badCert
);
1682 certificate
->_version
= 0;
1685 /* The serial number is in the tbsCert.serialNum - it was saved in
1686 INTEGER form without the tag and length. */
1687 certificate
->_serialNum
= tbsCert
.serialNum
;
1689 /* Note: RFC5280 4.1.2.2 limits serial number values to 20 octets.
1690 For now, we warn about larger values, but will still create the
1691 certificate with values up to 36 octets to avoid breaking some
1692 nonconforming certs with slightly longer serial numbers.
1693 We also explicitly allow serial numbers of 21 octets where the
1694 leading byte is 0x00 which is used to make a negative 20 octet
1697 if (tbsCert
.serialNum
.length
< 1 || tbsCert
.serialNum
.length
> 21 ||
1698 (tbsCert
.serialNum
.length
== 21 && tbsCert
.serialNum
.data
[0] != 0x00)) {
1699 secwarning("Invalid serial number length (%ld), must be 1..20",
1700 tbsCert
.serialNum
.length
);
1702 require_quiet(tbsCert
.serialNum
.data
!= NULL
&&
1703 tbsCert
.serialNum
.length
>= 1 &&
1704 tbsCert
.serialNum
.length
<= 37, badCert
);
1705 certificate
->_serialNumber
= CFDataCreate(allocator
,
1706 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1708 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1709 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1710 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1711 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1712 require_noerr_quiet(drtn
, badCert
);
1714 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1715 and length fields. */
1716 certificate
->_issuer
= tbsCert
.issuer
;
1717 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1720 /* sequence we're given: decode the tbsCerts Validity sequence. */
1721 DERValidity validity
;
1722 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1723 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1724 &validity
, sizeof(validity
));
1725 require_noerr_quiet(drtn
, badCert
);
1726 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1727 &certificate
->_notBefore
), badCert
);
1728 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1729 &certificate
->_notAfter
), badCert
);
1731 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1732 and length fields. */
1733 certificate
->_subject
= tbsCert
.subject
;
1734 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1737 /* Keep the SPKI around for CT */
1738 certificate
->_subjectPublicKeyInfo
= tbsCert
.subjectPubKey
;
1740 /* sequence we're given: encoded DERSubjPubKeyInfo */
1741 DERSubjPubKeyInfo pubKeyInfo
;
1742 drtn
= DERParseSequenceContent(&tbsCert
.subjectPubKey
,
1743 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1744 &pubKeyInfo
, sizeof(pubKeyInfo
));
1745 require_noerr_quiet(drtn
, badCert
);
1747 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1748 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1749 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1750 &certificate
->_algId
, sizeof(certificate
->_algId
));
1751 require_noerr_quiet(drtn
, badCert
);
1753 /* Now we can figure out the key's algorithm id and params based on
1754 certificate->_algId.oid. */
1756 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1757 are a PKCS1 format RSA key. */
1758 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1759 &certificate
->_pubKeyDER
, &numUnusedBits
);
1760 require_noerr_quiet(drtn
, badCert
);
1762 /* The contents of tbsCert.issuerID is a bit string. */
1763 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1765 /* The contents of tbsCert.subjectID is a bit string. */
1766 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1769 certificate
->_unparseableKnownExtensionIndex
= kCFNotFound
;
1770 if (tbsCert
.extensions
.length
) {
1771 CFIndex extensionCount
= 0;
1774 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1775 require_noerr_quiet(drtn
, badCert
);
1776 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1777 DERDecodedInfo currDecoded
;
1778 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1780 /* ! = MUST recognize ? = SHOULD recognize
1783 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1784 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1785 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1786 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1787 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1788 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1789 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1790 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1792 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1793 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1794 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1795 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1796 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1797 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1798 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1799 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1801 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1802 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1807 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1809 /* Put some upper limit on the number of extensions allowed. */
1810 require_quiet(extensionCount
< 10000, badCert
);
1811 certificate
->_extensionCount
= extensionCount
;
1812 certificate
->_extensions
= malloc(sizeof(SecCertificateExtension
) * (extensionCount
> 0 ? extensionCount
: 1));
1813 require_quiet(certificate
->_extensions
, badCert
);
1816 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1817 require_noerr_quiet(drtn
, badCert
);
1818 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1819 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1820 require_quiet(drtn
== DR_Success
|| (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1821 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1823 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1824 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1825 &extn
, sizeof(extn
));
1826 require_noerr_quiet(drtn
, badCert
);
1827 /* Copy stuff into certificate->extensions[ix]. */
1828 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1829 require_noerr_quiet(drtn
= DERParseBooleanWithDefault(&extn
.critical
, false,
1830 &certificate
->_extensions
[ix
].critical
), badCert
);
1831 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1833 SecCertificateExtensionParser parser
=
1834 (SecCertificateExtensionParser
)CFDictionaryGetValue(sExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1836 /* Invoke the parser. If the extension is critical and the
1837 * parser fails, fail the cert. */
1838 bool parseResult
= parser(certificate
, &certificate
->_extensions
[ix
]);
1840 certificate
->_unparseableKnownExtensionIndex
= ix
;
1842 require_quiet(parseResult
|| !certificate
->_extensions
[ix
].critical
, badCert
);
1843 } else if (certificate
->_extensions
[ix
].critical
) {
1844 if (isAppleExtensionOID(&extn
.extnID
)) {
1847 secdebug("cert", "Found unknown critical extension");
1848 certificate
->_foundUnknownCriticalExtension
= true;
1850 secdebug("cert", "Found unknown non critical extension");
1854 checkForMissingRevocationInfo(certificate
);
1863 /* Public API functions. */
1864 SecCertificateRef
SecCertificateCreateWithBytes(CFAllocatorRef allocator
,
1865 const UInt8
*der_bytes
, CFIndex der_length
) {
1866 if (der_bytes
== NULL
) return NULL
;
1867 if (der_length
== 0) return NULL
;
1869 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1870 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1871 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1873 memset((char*)result
+ sizeof(result
->_base
), 0,
1874 sizeof(*result
) - sizeof(result
->_base
));
1875 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1876 result
->_der
.length
= der_length
;
1877 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1878 if (!SecCertificateParse(result
)) {
1886 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1887 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1888 const UInt8
*der_bytes
, CFIndex der_length
);
1890 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1891 const UInt8
*der_bytes
, CFIndex der_length
) {
1892 return SecCertificateCreateWithBytes(allocator
, der_bytes
, der_length
);
1894 /* @@@ End of placeholder. */
1896 /* AUDIT[securityd](done):
1897 der_certificate is a caller provided data of any length (might be 0), only
1898 its cf type has been checked.
1900 SecCertificateRef
SecCertificateCreateWithData(CFAllocatorRef allocator
,
1901 CFDataRef der_certificate
) {
1902 if (!der_certificate
) {
1905 CFIndex size
= sizeof(struct __SecCertificate
);
1906 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1907 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1909 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1910 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1911 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1912 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1913 if (!SecCertificateParse(result
)) {
1921 SecCertificateRef
SecCertificateCreateWithKeychainItem(CFAllocatorRef allocator
,
1922 CFDataRef der_certificate
,
1923 CFTypeRef keychain_item
)
1925 SecCertificateRef result
= SecCertificateCreateWithData(allocator
, der_certificate
);
1927 CFRetainSafe(keychain_item
);
1928 result
->_keychain_item
= keychain_item
;
1933 OSStatus
SecCertificateSetKeychainItem(SecCertificateRef certificate
,
1934 CFTypeRef keychain_item
)
1939 CFRetainSafe(keychain_item
);
1940 CFReleaseSafe(certificate
->_keychain_item
);
1941 certificate
->_keychain_item
= keychain_item
;
1942 return errSecSuccess
;
1945 CFDataRef
SecCertificateCopyData(SecCertificateRef certificate
) {
1947 CFDataRef result
= NULL
;
1951 if (certificate
->_der_data
) {
1952 CFRetain(certificate
->_der_data
);
1953 result
= certificate
->_der_data
;
1955 result
= CFDataCreate(CFGetAllocator(certificate
),
1956 certificate
->_der
.data
, certificate
->_der
.length
);
1958 /* FIXME: If we wish to cache result we need to lock the certificate.
1959 Also this create 2 copies of the certificate data which is somewhat
1962 certificate
->_der_data
= result
;
1969 CFIndex
SecCertificateGetLength(SecCertificateRef certificate
) {
1970 return certificate
->_der
.length
;
1973 const UInt8
*SecCertificateGetBytePtr(SecCertificateRef certificate
) {
1974 return certificate
->_der
.data
;
1977 static bool SecCertificateIsCertificate(SecCertificateRef certificate
) {
1981 #ifndef IS_TRUSTTESTS
1982 /* TrustTests registers two SecCertificate TypeIDs, so we'll skip this check
1983 * in the tests and just let the tests crash if they pass the wrong object type. */
1984 if (CFGetTypeID(certificate
) != SecCertificateGetTypeID()) {
1991 /* Used to recreate preCert from cert for Certificate Transparency */
1992 CFDataRef
SecCertificateCopyPrecertTBS(SecCertificateRef certificate
)
1994 CFDataRef outData
= NULL
;
1995 DERItem tbsIn
= certificate
->_tbs
;
1996 DERItem tbsOut
= {0,};
1997 DERItem extensionsOut
= {0,};
1998 DERItem
*extensionsList
= malloc(sizeof(DERItem
)*certificate
->_extensionCount
); /* This maybe one too many */
1999 DERItemSpec
*extensionsListSpecs
= malloc(sizeof(DERItemSpec
)*certificate
->_extensionCount
);
2003 require_quiet(extensionsList
&& extensionsListSpecs
, out
);
2005 /* decode the TBSCert - it was saved in full DER form */
2006 drtn
= DERParseSequence(&tbsIn
,
2007 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
2008 &tbsCert
, sizeof(tbsCert
));
2009 require_noerr_quiet(drtn
, out
);
2011 /* Go over extensions and filter any SCT extension */
2012 CFIndex extensionsCount
= 0;
2014 if (tbsCert
.extensions
.length
) {
2017 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
2019 require_noerr_quiet(drtn
, out
);
2020 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
2021 DERDecodedInfo currDecoded
;
2022 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
2024 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, out
);
2026 drtn
= DERParseSequenceContent(&currDecoded
.content
,
2027 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
2028 &extn
, sizeof(extn
));
2029 require_noerr_quiet(drtn
, out
);
2031 if (extn
.extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
2032 !memcmp(extn
.extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
.extnID
.length
))
2035 extensionsList
[extensionsCount
] = currDecoded
.content
;
2036 extensionsListSpecs
[extensionsCount
].offset
= sizeof(DERItem
)*extensionsCount
;
2037 extensionsListSpecs
[extensionsCount
].options
= 0;
2038 extensionsListSpecs
[extensionsCount
].tag
= ASN1_CONSTR_SEQUENCE
;
2043 require_quiet(drtn
== DR_EndOfSequence
, out
);
2047 /* Encode extensions */
2048 extensionsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
);
2049 extensionsOut
.data
= malloc(extensionsOut
.length
);
2050 require_quiet(extensionsOut
.data
, out
);
2051 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
, extensionsOut
.data
, &extensionsOut
.length
);
2052 require_noerr_quiet(drtn
, out
);
2054 tbsCert
.extensions
= extensionsOut
;
2056 tbsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
);
2057 tbsOut
.data
= malloc(tbsOut
.length
);
2058 require_quiet(tbsOut
.data
, out
);
2059 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
, tbsOut
.data
, &tbsOut
.length
);
2060 require_noerr_quiet(drtn
, out
);
2062 outData
= CFDataCreate(kCFAllocatorDefault
, tbsOut
.data
, tbsOut
.length
);
2065 if (extensionsOut
.data
) free(extensionsOut
.data
);
2066 if (tbsOut
.data
) free(tbsOut
.data
);
2067 if (extensionsList
) free(extensionsList
);
2068 if (extensionsListSpecs
) free(extensionsListSpecs
);
2073 /* From rfc3280 - Appendix B. ASN.1 Notes
2075 Object Identifiers (OIDs) are used throughout this specification to
2076 identify certificate policies, public key and signature algorithms,
2077 certificate extensions, etc. There is no maximum size for OIDs.
2078 This specification mandates support for OIDs which have arc elements
2079 with values that are less than 2^28, that is, they MUST be between 0
2080 and 268,435,455, inclusive. This allows each arc element to be
2081 represented within a single 32 bit word. Implementations MUST also
2082 support OIDs where the length of the dotted decimal (see [RFC 2252],
2083 section 4.1) string representation can be up to 100 bytes
2084 (inclusive). Implementations MUST be able to handle OIDs with up to
2085 20 elements (inclusive). CAs SHOULD NOT issue certificates which
2086 contain OIDs that exceed these requirements. Likewise, CRL issuers
2087 SHOULD NOT issue CRLs which contain OIDs that exceed these
2091 /* Oids longer than this are considered invalid. */
2092 #define MAX_OID_SIZE 32
2094 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
2095 const DERItem
*oid
) {
2097 if (oid
->length
== 0) {
2098 return SecCopyCertString(SEC_NULL_KEY
);
2100 if (oid
->length
> MAX_OID_SIZE
) {
2101 return SecCopyCertString(SEC_OID_TOO_LONG_KEY
);
2104 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
2106 // The first two levels are encoded into one byte, since the root level
2107 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
2108 // y may be > 39, so we have to add special-case handling for this.
2109 uint32_t x
= oid
->data
[0] / 40;
2110 uint32_t y
= oid
->data
[0] % 40;
2113 // Handle special case for large y if x = 2
2117 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
2120 for (x
= 1; x
< oid
->length
; ++x
)
2122 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
2123 /* @@@ value may not span more than 4 bytes. */
2124 /* A max number of 20 values is allowed. */
2125 if (!(oid
->data
[x
] & 0x80))
2127 CFStringAppendFormat(result
, NULL
, CFSTR(".%" PRIu32
), value
);
2134 static CFStringRef
copyOidDescription(CFAllocatorRef allocator
,
2135 const DERItem
*oid
, bool localized
) {
2136 if (!oid
|| oid
->length
== 0) {
2137 return (localized
) ? SecCopyCertString(SEC_NULL_KEY
) : SEC_NULL_KEY
;
2140 CFStringRef name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
2145 /* Build the key we use to lookup the localized OID description. */
2146 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
2147 oid
->length
* 3 + 5);
2148 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), oid
->length
);
2149 for (DERSize ix
= 0; ix
< oid
->length
; ++ix
) {
2150 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
2152 CFStringRef locname
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
2153 if (locname
&& !CFEqual(oidKey
, locname
)) {
2154 /* Found localized description string, so use it instead of OID. */
2155 CFReleaseSafe(name
);
2158 CFReleaseSafe(locname
);
2165 /* Return the ipAddress as a dotted quad for ipv4, or as 8 colon separated
2166 4 digit hex strings for ipv6. Return NULL if the provided IP doesn't
2167 have a length of exactly 4 or 16 octets.
2168 Note: hex values are normalized to uppercase.
2170 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
2171 const DERItem
*ip
) {
2172 /* This is the IP Address as an OCTET STRING.
2173 For IPv4 it's 4 octets addr, or 8 octets, addr/mask.
2174 For IPv6 it's 16 octets addr, or 32 octets addr/mask.
2176 CFStringRef value
= NULL
;
2177 if (ip
->length
== IPv4ADDRLEN
) {
2178 value
= CFStringCreateWithFormat(allocator
, NULL
,
2179 CFSTR("%u.%u.%u.%u"),
2180 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
2181 } else if (ip
->length
== IPv6ADDRLEN
) {
2182 value
= CFStringCreateWithFormat(allocator
, NULL
,
2183 CFSTR("%02X%02X:%02X%02X:%02X%02X:%02X%02X:"
2184 "%02X%02X:%02X%02X:%02X%02X:%02X%02X"),
2185 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
2186 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
2187 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
2188 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
2194 void appendProperty(CFMutableArrayRef properties
, CFStringRef propertyType
,
2195 CFStringRef label
, CFStringRef localizedLabel
, CFTypeRef value
,
2197 CFDictionaryRef property
;
2199 CFStringRef ll
= NULL
;
2201 /* use unlocalized label, overriding localizedLabel */
2202 ll
= localizedLabel
= (CFStringRef
) CFRetainSafe(label
);
2203 } else if (!localizedLabel
) {
2204 /* copy localized label for unlocalized label */
2205 ll
= localizedLabel
= SecCopyCertString(label
);
2207 const void *all_keys
[4];
2208 all_keys
[0] = kSecPropertyKeyType
;
2209 all_keys
[1] = kSecPropertyKeyLabel
;
2210 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
2211 all_keys
[3] = kSecPropertyKeyValue
;
2212 const void *property_values
[] = {
2218 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2219 all_keys
, property_values
, value
? 4 : 3,
2220 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2223 const void *nolabel_keys
[2];
2224 nolabel_keys
[0] = kSecPropertyKeyType
;
2225 nolabel_keys
[1] = kSecPropertyKeyValue
;
2226 const void *property_values
[] = {
2230 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2231 nolabel_keys
, property_values
, 2,
2232 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2235 CFArrayAppendValue(properties
, property
);
2236 CFRelease(property
);
2240 #define UTC_TIME_NOSEC_ZULU_LEN 11
2242 #define UTC_TIME_ZULU_LEN 13
2243 /* YYMMDDhhmmssThhmm */
2244 #define UTC_TIME_LOCALIZED_LEN 17
2245 /* YYYYMMDDhhmmssZ */
2246 #define GENERALIZED_TIME_ZULU_LEN 15
2247 /* YYYYMMDDhhmmssThhmm */
2248 #define GENERALIZED_TIME_LOCALIZED_LEN 19
2250 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
2252 static inline int parseDecimalPair(const DERByte
**p
) {
2253 const DERByte
*cp
= *p
;
2255 return 10 * (cp
[0] - '0') + cp
[1] - '0';
2258 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime.
2259 Return a CFErrorRef in the error parameter if decoding fails.
2260 Note that this is needed to distinguish an error condition from a
2261 valid time which specifies 2001-01-01 00:00:00 (i.e. a value of 0).
2263 static CFAbsoluteTime
SecAbsoluteTimeFromDateContentWithError(DERTag tag
,
2264 const uint8_t *bytes
,
2266 CFErrorRef
*error
) {
2270 if (NULL
== bytes
|| 0 == length
) {
2274 bool isUtcLength
= false;
2275 bool isLocalized
= false;
2276 bool noSeconds
= false;
2278 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
2282 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
2285 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
2287 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
2290 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
2293 default: /* unknown format */
2297 /* Make sure the der tag fits the thing inside it. */
2298 if (tag
== ASN1_UTC_TIME
) {
2302 } else if (tag
== ASN1_GENERALIZED_TIME
) {
2310 const DERByte
*cp
= bytes
;
2311 /* Check that all characters are digits, except if localized the timezone
2312 indicator or if not localized the 'Z' at the end. */
2314 for (ix
= 0; ix
< length
; ++ix
) {
2315 if (!(isdigit(cp
[ix
]))) {
2316 if ((isLocalized
&& ix
== length
- 5 &&
2317 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
2318 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
2325 /* Parse the date and time fields. */
2326 int year
, month
, day
, hour
, minute
, second
;
2328 year
= parseDecimalPair(&cp
);
2330 /* 0 <= year < 50 : assume century 21 */
2332 } else if (year
< 70) {
2333 /* 50 <= year < 70 : illegal per PKIX */
2336 /* 70 < year <= 99 : assume century 20 */
2340 year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
2342 month
= parseDecimalPair(&cp
);
2343 day
= parseDecimalPair(&cp
);
2344 hour
= parseDecimalPair(&cp
);
2345 minute
= parseDecimalPair(&cp
);
2349 second
= parseDecimalPair(&cp
);
2352 CFTimeInterval timeZoneOffset
;
2354 /* ZONE INDICATOR */
2355 int multiplier
= *cp
++ == '+' ? 60 : -60;
2356 timeZoneOffset
= multiplier
*
2357 (parseDecimalPair(&cp
) * 60 + parseDecimalPair(&cp
));
2362 secdebug("dateparse",
2363 "date %.*s year: %04d%02d%02d%02d%02d%02d%+05g",
2364 (int) length
, bytes
, year
, month
,
2365 day
, hour
, minute
, second
,
2366 timeZoneOffset
/ 60);
2368 static int mdays
[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
2369 int is_leap_year
= year
% 4 == 0 && (year
% 100 != 0 || year
% 400 == 0) ? 1 : 0;
2370 if (month
< 1 || month
> 12 || day
< 1 || day
> 31 || hour
> 23 || minute
> 59 || second
> 59
2371 || (month
== 2 && day
> mdays
[month
] - mdays
[month
- 1] + is_leap_year
)
2372 || (month
!= 2 && day
> mdays
[month
] - mdays
[month
- 1])) {
2377 int dy
= year
- 2001;
2382 int leap_days
= dy
/ 4 - dy
/ 100 + dy
/ 400;
2383 day
+= ((year
- 2001) * 365 + leap_days
) + mdays
[month
- 1] - 1;
2385 day
+= is_leap_year
;
2387 CFAbsoluteTime absTime
= (CFAbsoluteTime
)((day
* 24.0 + hour
) * 60.0 + minute
) * 60.0 + second
;
2388 return absTime
- timeZoneOffset
;
2392 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
2397 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
2399 return SecAbsoluteTimeFromDateContentWithError(tag
, bytes
, length
, NULL
);
2402 __attribute__((__nonnull__
)) static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
2403 CFAbsoluteTime
*pabsTime
) {
2404 CFErrorRef error
= NULL
;
2405 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContentWithError(tag
, date
->data
,
2406 date
->length
, &error
);
2408 secwarning("Invalid date specification in certificate (see RFC5280 4.1.2.5)");
2413 *pabsTime
= absTime
;
2417 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
2418 true if the date was valid and properly decoded, also return the result in
2419 absTime. Return false otherwise. */
2420 __attribute__((__nonnull__
)) static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
2421 CFAbsoluteTime
*absTime
) {
2422 if (dateChoice
->length
== 0) return false;
2424 DERDecodedInfo decoded
;
2425 if (DERDecodeItem(dateChoice
, &decoded
))
2428 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
2432 static void appendDataProperty(CFMutableArrayRef properties
,
2433 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
,
2435 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
2436 der_data
->data
, der_data
->length
);
2437 appendProperty(properties
, kSecPropertyTypeData
, label
, localizedLabel
,
2442 static void appendRelabeledProperty(CFMutableArrayRef properties
,
2444 CFStringRef localizedLabel
,
2445 const DERItem
*der_data
,
2446 CFStringRef labelFormat
,
2448 CFStringRef newLabel
=
2449 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2450 labelFormat
, label
);
2451 CFStringRef ll
= NULL
;
2452 CFStringRef localizedLabelFormat
= NULL
;
2454 /* use provided label and format strings; do not localize */
2455 ll
= localizedLabel
= (CFStringRef
) CFRetainSafe(label
);
2456 localizedLabelFormat
= (CFStringRef
) CFRetainSafe(labelFormat
);
2458 if (!localizedLabel
) {
2459 /* copy localized label for provided label */
2460 ll
= localizedLabel
= SecCopyCertString(label
);
2462 /* copy localized format for provided format */
2463 localizedLabelFormat
= SecCopyCertString(labelFormat
);
2466 CFStringRef newLocalizedLabel
=
2467 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2468 localizedLabelFormat
, localizedLabel
);
2470 CFReleaseSafe(localizedLabelFormat
);
2471 appendDataProperty(properties
, newLabel
, newLocalizedLabel
, der_data
, localized
);
2472 CFReleaseSafe(newLabel
);
2473 CFReleaseSafe(newLocalizedLabel
);
2477 static void appendUnparsedProperty(CFMutableArrayRef properties
,
2478 CFStringRef label
, CFStringRef localizedLabel
,
2479 const DERItem
*der_data
, bool localized
) {
2480 appendRelabeledProperty(properties
, label
, localizedLabel
, der_data
,
2481 SEC_UNPARSED_KEY
, localized
);
2484 static void appendInvalidProperty(CFMutableArrayRef properties
,
2485 CFStringRef label
, const DERItem
*der_data
, bool localized
) {
2486 appendRelabeledProperty(properties
, label
, NULL
, der_data
,
2487 SEC_INVALID_KEY
, localized
);
2490 static void appendDateContentProperty(CFMutableArrayRef properties
,
2491 CFStringRef label
, DERTag tag
,
2492 const DERItem
*dateContent
, bool localized
) {
2493 CFAbsoluteTime absTime
;
2494 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
2495 /* Date decode failure; insert hex bytes instead. */
2496 return appendInvalidProperty(properties
, label
, dateContent
, localized
);
2498 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2499 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
, localized
);
2503 static void appendDateProperty(CFMutableArrayRef properties
,
2504 CFStringRef label
, CFAbsoluteTime absTime
, bool localized
) {
2505 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2506 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
, localized
);
2510 static void appendValidityPeriodProperty(CFMutableArrayRef parent
, CFStringRef label
,
2511 SecCertificateRef certificate
, bool localized
) {
2512 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2513 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2515 appendDateProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2516 certificate
->_notBefore
, localized
);
2517 appendDateProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2518 certificate
->_notAfter
, localized
);
2520 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
, localized
);
2521 CFReleaseNull(properties
);
2524 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
2525 CFStringRef label
, const DERItem
*ip
, bool localized
) {
2527 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
2529 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
, localized
);
2532 appendUnparsedProperty(properties
, label
, NULL
, ip
, localized
);
2536 static void appendURLContentProperty(CFMutableArrayRef properties
,
2537 CFStringRef label
, const DERItem
*urlContent
, bool localized
) {
2538 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
2539 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2541 appendProperty(properties
, kSecPropertyTypeURL
, label
, NULL
, url
, localized
);
2544 appendInvalidProperty(properties
, label
, urlContent
, localized
);
2548 static void appendURLProperty(CFMutableArrayRef properties
,
2549 CFStringRef label
, const DERItem
*url
, bool localized
) {
2550 DERDecodedInfo decoded
;
2553 drtn
= DERDecodeItem(url
, &decoded
);
2554 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2555 appendInvalidProperty(properties
, label
, url
, localized
);
2557 appendURLContentProperty(properties
, label
, &decoded
.content
, localized
);
2561 static void appendOIDProperty(CFMutableArrayRef properties
,
2562 CFStringRef label
, CFStringRef llabel
, const DERItem
*oid
, bool localized
) {
2563 CFStringRef oid_string
=
2564 copyOidDescription(CFGetAllocator(properties
), oid
, localized
);
2565 appendProperty(properties
, kSecPropertyTypeString
, label
, llabel
,
2566 oid_string
, localized
);
2567 CFRelease(oid_string
);
2570 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2571 CFStringRef label
, const DERAlgorithmId
*algorithm
, bool localized
) {
2572 CFMutableArrayRef alg_props
=
2573 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2574 &kCFTypeArrayCallBacks
);
2575 appendOIDProperty(alg_props
, SEC_ALGORITHM_KEY
, NULL
,
2576 &algorithm
->oid
, localized
);
2577 if (algorithm
->params
.length
) {
2578 if (algorithm
->params
.length
== 2 &&
2579 algorithm
->params
.data
[0] == ASN1_NULL
&&
2580 algorithm
->params
.data
[1] == 0) {
2581 CFStringRef value
= SecCopyCertString(SEC_NONE_KEY
);
2582 appendProperty(alg_props
, kSecPropertyTypeString
,
2583 SEC_PARAMETERS_KEY
, NULL
, value
, localized
);
2586 appendUnparsedProperty(alg_props
, SEC_PARAMETERS_KEY
, NULL
,
2587 &algorithm
->params
, localized
);
2590 appendProperty(properties
, kSecPropertyTypeSection
, label
, NULL
,
2591 alg_props
, localized
);
2592 CFRelease(alg_props
);
2595 static void appendPublicKeyProperty(CFMutableArrayRef parent
, CFStringRef label
,
2596 SecCertificateRef certificate
, bool localized
) {
2597 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2598 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2600 /* Public key algorithm. */
2601 appendAlgorithmProperty(properties
, SEC_PUBLIC_KEY_ALG_KEY
,
2602 &certificate
->_algId
, localized
);
2604 /* Public Key Size */
2605 SecKeyRef publicKey
= SecCertificateCopyKey(certificate
);
2607 size_t sizeInBytes
= SecKeyGetBlockSize(publicKey
);
2608 CFStringRef sizeInBitsString
= CFStringCreateWithFormat(allocator
, NULL
,
2609 CFSTR("%ld"), (sizeInBytes
*8));
2610 if (sizeInBitsString
) {
2611 appendProperty(properties
, kSecPropertyTypeString
, SEC_PUBLIC_KEY_SIZE_KEY
,
2612 NULL
, sizeInBitsString
, localized
);
2614 CFReleaseNull(sizeInBitsString
);
2616 CFReleaseNull(publicKey
);
2618 /* Consider breaking down an RSA public key into modulus and
2620 appendDataProperty(properties
, SEC_PUBLIC_KEY_DATA_KEY
, NULL
,
2621 &certificate
->_pubKeyDER
, localized
);
2623 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2624 properties
, localized
);
2625 CFReleaseNull(properties
);
2628 static void appendSignatureProperty(CFMutableArrayRef parent
, CFStringRef label
,
2629 SecCertificateRef certificate
, bool localized
) {
2630 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2631 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2633 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
2634 &certificate
->_tbsSigAlg
, localized
);
2636 appendDataProperty(properties
, SEC_SIGNATURE_DATA_KEY
, NULL
,
2637 &certificate
->_signature
, localized
);
2639 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2640 properties
, localized
);
2641 CFReleaseNull(properties
);
2644 static void appendFingerprintsProperty(CFMutableArrayRef parent
, CFStringRef label
,
2645 SecCertificateRef certificate
, bool localized
) {
2646 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2647 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2649 CFDataRef sha256Fingerprint
= SecCertificateCopySHA256Digest(certificate
);
2650 if (sha256Fingerprint
) {
2651 appendProperty(properties
, kSecPropertyTypeData
, SEC_SHA2_FINGERPRINT_KEY
,
2652 NULL
, sha256Fingerprint
, localized
);
2654 CFReleaseNull(sha256Fingerprint
);
2656 appendProperty(properties
, kSecPropertyTypeData
, SEC_SHA1_FINGERPRINT_KEY
,
2657 NULL
, SecCertificateGetSHA1Digest(certificate
), localized
);
2659 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2660 properties
, localized
);
2661 CFReleaseNull(properties
);
2664 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2665 const DERItem
*blob
) {
2666 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2667 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2668 blob
->length
* 3 - 1);
2669 for (ix
= 0; ix
< length
; ++ix
)
2671 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2673 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2678 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2679 CFStringRef blobType
, CFStringRef quanta
,
2680 const DERItem
*blob
, bool localized
) {
2681 CFStringRef localizedBlobType
= (localized
) ?
2682 SecCopyCertString(blobType
) : (CFStringRef
) CFRetainSafe(blobType
);
2683 CFStringRef localizedQuanta
= (localized
) ?
2684 SecCopyCertString(quanta
) : (CFStringRef
) CFRetainSafe(quanta
);
2685 /* "format string for encoded field data (e.g. Sequence; 128 bytes; "
2686 "data = 00 00 ...)" */
2687 CFStringRef blobFormat
= (localized
) ?
2688 SecCopyCertString(SEC_BLOB_KEY
) : SEC_BLOB_KEY
;
2689 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2690 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2691 blobFormat
, localizedBlobType
, blob
->length
, localizedQuanta
, hex
);
2693 CFRelease(blobFormat
);
2694 CFReleaseSafe(localizedQuanta
);
2695 CFReleaseSafe(localizedBlobType
);
2700 /* Return a string verbatim (unlocalized) from a DER field. */
2701 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2702 const DERItem
*string
, CFStringEncoding encoding
,
2703 bool printableOnly
) {
2704 /* Strip potential bogus trailing zero from printable strings. */
2705 DERSize length
= string
->length
;
2706 if (length
&& string
->data
[length
- 1] == 0) {
2707 /* Don't mess with the length of UTF16 strings though. */
2708 if (encoding
!= kCFStringEncodingUTF16
)
2711 /* A zero length string isn't considered printable. */
2712 if (!length
&& printableOnly
)
2715 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2716 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2717 passing false makes it treat it as native endian by default. */
2718 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2719 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2723 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2726 /* From rfc3280 - Appendix B. ASN.1 Notes
2728 CAs MUST force the serialNumber to be a non-negative integer, that
2729 is, the sign bit in the DER encoding of the INTEGER value MUST be
2730 zero - this can be done by adding a leading (leftmost) `00'H octet if
2731 necessary. This removes a potential ambiguity in mapping between a
2732 string of octets and an integer value.
2734 As noted in section 4.1.2.2, serial numbers can be expected to
2735 contain long integers. Certificate users MUST be able to handle
2736 serialNumber values up to 20 octets in length. Conformant CAs MUST
2737 NOT use serialNumber values longer than 20 octets.
2740 /* Return the given numeric data as a string: decimal up to 64 bits,
2743 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2744 const DERItem
*integer
) {
2746 CFIndex ix
, length
= integer
->length
;
2748 if (length
== 0 || length
> 8)
2749 return copyHexDescription(allocator
, integer
);
2751 for(ix
= 0; ix
< length
; ++ix
) {
2753 value
+= integer
->data
[ix
];
2756 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2759 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2760 DERTag tag
, const DERItem
*derThing
, bool printableOnly
, bool localized
) {
2761 if (!derThing
) { return NULL
; }
2765 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2766 case ASN1_PRINTABLE_STRING
:
2767 case ASN1_IA5_STRING
:
2768 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2769 case ASN1_UTF8_STRING
:
2770 case ASN1_GENERAL_STRING
:
2771 case ASN1_UNIVERSAL_STRING
:
2772 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2773 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2774 case ASN1_VIDEOTEX_STRING
: // 21
2775 case ASN1_VISIBLE_STRING
: // 26
2776 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2777 case ASN1_BMP_STRING
: // 30
2778 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2779 case ASN1_OCTET_STRING
:
2780 return printableOnly
? NULL
:
2781 copyBlobString(allocator
, SEC_BYTE_STRING_KEY
, SEC_BYTES_KEY
,
2782 derThing
, localized
);
2783 case ASN1_BIT_STRING
:
2784 return printableOnly
? NULL
:
2785 copyBlobString(allocator
, SEC_BIT_STRING_KEY
, SEC_BITS_KEY
,
2786 derThing
, localized
);
2787 case ASN1_CONSTR_SEQUENCE
:
2788 return printableOnly
? NULL
:
2789 copyBlobString(allocator
, SEC_SEQUENCE_KEY
, SEC_BYTES_KEY
,
2790 derThing
, localized
);
2791 case ASN1_CONSTR_SET
:
2792 return printableOnly
? NULL
:
2793 copyBlobString(allocator
, SEC_SET_KEY
, SEC_BYTES_KEY
,
2794 derThing
, localized
);
2795 case ASN1_OBJECT_ID
:
2796 return printableOnly
? NULL
: copyOidDescription(allocator
, derThing
, localized
);
2798 if (printableOnly
) {
2801 CFStringRef fmt
= (localized
) ?
2802 SecCopyCertString(SEC_NOT_DISPLAYED_KEY
) : SEC_NOT_DISPLAYED_KEY
;
2803 if (!fmt
) { return NULL
; }
2804 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
2805 (unsigned long)tag
, (unsigned long)derThing
->length
);
2812 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2813 const DERItem
*derThing
, bool printableOnly
, bool localized
) {
2814 DERDecodedInfo decoded
;
2817 drtn
= DERDecodeItem(derThing
, &decoded
);
2819 /* TODO: Perhaps put something in the label saying we couldn't parse
2821 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2823 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2824 &decoded
.content
, false, localized
);
2828 static void appendDERThingProperty(CFMutableArrayRef properties
,
2829 CFStringRef label
, CFStringRef localizedLabel
,
2830 const DERItem
*derThing
, bool localized
) {
2831 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2832 derThing
, false, localized
);
2834 appendProperty(properties
, kSecPropertyTypeString
, label
, localizedLabel
,
2837 CFReleaseSafe(value
);
2840 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2841 const DERItem
*rdnValue
, CFIndex rdnIX
,
2843 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2845 /* If there is more than one value pair we create a subsection for the
2846 second pair, and append things to the subsection for subsequent
2848 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2849 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2851 /* Since this is the second rdn pair for a given rdn, we setup a
2852 new subsection for this rdn. We remove the first property
2853 from the properties array and make it the first element in the
2854 subsection instead. */
2855 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2856 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2857 CFArrayAppendValue(rdn_props
, lastValue
);
2858 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2859 appendProperty(properties
, kSecPropertyTypeSection
, NULL
, NULL
,
2860 rdn_props
, localized
);
2861 properties
= rdn_props
;
2862 // rdn_props is now retained by the original properties array
2863 CFReleaseSafe(rdn_props
);
2865 /* Since this is the third or later rdn pair we have already
2866 created a subsection in the top level properties array. Instead
2867 of appending to that directly we append to the array inside the
2869 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2870 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2874 /* Finally we append the new rdn value to the property array. */
2876 SecDERItemCopyOIDDecimalRepresentation(CFGetAllocator(properties
),
2878 CFStringRef localizedLabel
= copyOidDescription(CFGetAllocator(properties
),
2879 rdnType
, localized
);
2880 appendDERThingProperty(properties
, label
, localizedLabel
,
2881 rdnValue
, localized
);
2882 CFReleaseSafe(label
);
2883 CFReleaseSafe(localizedLabel
);
2884 return errSecSuccess
;
2887 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2888 const DERItem
*rdnSetContent
, bool localized
) {
2889 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2890 &kCFTypeArrayCallBacks
);
2891 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2892 appendRDNProperty
, localized
);
2894 CFArrayRemoveAllValues(properties
);
2895 appendInvalidProperty(properties
, SEC_RDN_KEY
, rdnSetContent
,
2903 From rfc3739 - 3.1.2. Subject
2905 When parsing the subject here are some tips for a short name of the cert.
2906 Choice I: commonName
2907 Choice II: givenName
2908 Choice III: pseudonym
2910 The commonName attribute value SHALL, when present, contain a name
2911 of the subject. This MAY be in the subject's preferred
2912 presentation format, or a format preferred by the CA, or some
2913 other format. Pseudonyms, nicknames, and names with spelling
2914 other than defined by the registered name MAY be used. To
2915 understand the nature of the name presented in commonName,
2916 complying applications MAY have to examine present values of the
2917 givenName and surname attributes, or the pseudonym attribute.
2920 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2921 const DERItem
*x501NameContent
, bool localized
) {
2922 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2923 &kCFTypeArrayCallBacks
);
2924 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2925 appendRDNProperty
, localized
);
2927 CFArrayRemoveAllValues(properties
);
2928 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
,
2929 x501NameContent
, localized
);
2935 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2936 const DERItem
*x501Name
, bool localized
) {
2937 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2938 &kCFTypeArrayCallBacks
);
2939 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
, localized
);
2941 CFArrayRemoveAllValues(properties
);
2942 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
,
2943 x501Name
, localized
);
2949 static void appendIntegerProperty(CFMutableArrayRef properties
,
2950 CFStringRef label
, const DERItem
*integer
, bool localized
) {
2951 CFStringRef string
= copyIntegerContentDescription(
2952 CFGetAllocator(properties
), integer
);
2953 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2958 static void appendBoolProperty(CFMutableArrayRef properties
,
2959 CFStringRef label
, bool boolean
, bool localized
) {
2960 CFStringRef key
= (boolean
) ? SEC_YES_KEY
: SEC_NO_KEY
;
2961 CFStringRef value
= (localized
) ? SecCopyCertString(key
) : key
;
2962 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2967 static void appendBooleanProperty(CFMutableArrayRef properties
,
2968 CFStringRef label
, const DERItem
*boolean
,
2969 bool defaultValue
, bool localized
) {
2971 DERReturn drtn
= DERParseBooleanWithDefault(boolean
, defaultValue
, &result
);
2973 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2974 appendInvalidProperty(properties
, label
, boolean
, localized
);
2976 appendBoolProperty(properties
, label
, result
, localized
);
2980 static void appendSerialNumberProperty(CFMutableArrayRef parent
, CFStringRef label
,
2981 DERItem
*serialNum
, bool localized
) {
2982 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2983 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2985 if (serialNum
->length
) {
2986 appendIntegerProperty(properties
, SEC_SERIAL_NUMBER_KEY
,
2987 serialNum
, localized
);
2988 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2989 properties
, localized
);
2992 CFReleaseNull(properties
);
2995 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2996 CFStringRef label
, const DERItem
*bitStringContent
,
2997 const CFStringRef
*names
, CFIndex namesCount
,
2999 DERSize len
= bitStringContent
->length
- 1;
3000 require_quiet(len
== 1 || len
== 2, badDER
);
3001 DERByte numUnusedBits
= bitStringContent
->data
[0];
3002 require_quiet(numUnusedBits
< 8, badDER
);
3003 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
3004 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
3005 uint_fast16_t value
= bitStringContent
->data
[1];
3008 value
= (value
<< 8) + bitStringContent
->data
[2];
3014 CFStringRef fmt
= (localized
) ?
3015 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
3016 CFStringRef string
= NULL
;
3017 for (ix
= 0; ix
< bits
; ++ix
) {
3018 CFStringRef localizedName
= (localized
) ? SecCopyCertString(names
[ix
]) : CFRetainSafe(names
[ix
]);
3022 CFStringCreateWithFormat(CFGetAllocator(properties
),
3023 NULL
, fmt
, string
, localizedName
);
3027 string
= localizedName
;
3032 CFReleaseNull(localizedName
);
3035 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3036 string
? string
: CFSTR(""), localized
);
3037 CFReleaseSafe(string
);
3040 appendInvalidProperty(properties
, label
, bitStringContent
, localized
);
3043 static void appendBitStringNames(CFMutableArrayRef properties
,
3044 CFStringRef label
, const DERItem
*bitString
,
3045 const CFStringRef
*names
, CFIndex namesCount
,
3047 DERDecodedInfo bitStringContent
;
3048 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
3049 require_noerr_quiet(drtn
, badDER
);
3050 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
3051 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
3052 names
, namesCount
, localized
);
3055 appendInvalidProperty(properties
, label
, bitString
, localized
);
3058 static void appendKeyUsage(CFMutableArrayRef properties
,
3059 const DERItem
*extnValue
, bool localized
) {
3060 static const CFStringRef usageNames
[] = {
3061 SEC_DIGITAL_SIGNATURE_KEY
,
3062 SEC_NON_REPUDIATION_KEY
,
3063 SEC_KEY_ENCIPHERMENT_KEY
,
3064 SEC_DATA_ENCIPHERMENT_KEY
,
3065 SEC_KEY_AGREEMENT_KEY
,
3068 SEC_ENCIPHER_ONLY_KEY
,
3069 SEC_DECIPHER_ONLY_KEY
3071 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3072 usageNames
, array_size(usageNames
), localized
);
3075 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
3076 const DERItem
*extnValue
, bool localized
) {
3077 DERPrivateKeyUsagePeriod pkup
;
3078 DERReturn drtn
= DERParseSequence(extnValue
,
3079 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
3080 &pkup
, sizeof(pkup
));
3081 require_noerr_quiet(drtn
, badDER
);
3082 if (pkup
.notBefore
.length
) {
3083 appendDateContentProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
3084 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
, localized
);
3086 if (pkup
.notAfter
.length
) {
3087 appendDateContentProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
3088 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
, localized
);
3092 appendInvalidProperty(properties
, SEC_PRIVATE_KU_PERIOD_KEY
,
3093 extnValue
, localized
);
3096 static void appendStringContentProperty(CFMutableArrayRef properties
,
3097 CFStringRef label
, const DERItem
*stringContent
,
3098 CFStringEncoding encoding
, bool localized
) {
3099 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
3100 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
3102 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3106 appendInvalidProperty(properties
, label
, stringContent
, localized
);
3111 OtherName ::= SEQUENCE {
3112 type-id OBJECT IDENTIFIER,
3113 value [0] EXPLICIT ANY DEFINED BY type-id }
3115 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
3116 const DERItem
*otherNameContent
, bool localized
) {
3118 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
3119 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
3121 require_noerr_quiet(drtn
, badDER
);
3122 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3124 SecDERItemCopyOIDDecimalRepresentation(allocator
, &on
.typeIdentifier
);
3125 CFStringRef localizedLabel
=
3126 copyOidDescription(allocator
, &on
.typeIdentifier
, localized
);
3127 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
,
3130 appendProperty(properties
, kSecPropertyTypeString
, label
,
3131 localizedLabel
, value_string
, localized
);
3133 appendUnparsedProperty(properties
, label
, localizedLabel
,
3134 &on
.value
, localized
);
3136 CFReleaseSafe(value_string
);
3137 CFReleaseSafe(label
);
3138 CFReleaseSafe(localizedLabel
);
3141 appendInvalidProperty(properties
, SEC_OTHER_NAME_KEY
,
3142 otherNameContent
, localized
);
3146 GeneralName ::= CHOICE {
3147 otherName [0] OtherName,
3148 rfc822Name [1] IA5String,
3149 dNSName [2] IA5String,
3150 x400Address [3] ORAddress,
3151 directoryName [4] Name,
3152 ediPartyName [5] EDIPartyName,
3153 uniformResourceIdentifier [6] IA5String,
3154 iPAddress [7] OCTET STRING,
3155 registeredID [8] OBJECT IDENTIFIER}
3157 EDIPartyName ::= SEQUENCE {
3158 nameAssigner [0] DirectoryString OPTIONAL,
3159 partyName [1] DirectoryString }
3161 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
3162 DERTag tag
, const DERItem
*generalName
, bool localized
) {
3164 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
3165 appendOtherNameContentProperty(properties
, generalName
, localized
);
3167 case ASN1_CONTEXT_SPECIFIC
| 1:
3169 appendStringContentProperty(properties
, SEC_EMAIL_ADDRESS_KEY
,
3170 generalName
, kCFStringEncodingASCII
, localized
);
3172 case ASN1_CONTEXT_SPECIFIC
| 2:
3174 appendStringContentProperty(properties
, SEC_DNS_NAME_KEY
, generalName
,
3175 kCFStringEncodingASCII
, localized
);
3177 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
3178 appendUnparsedProperty(properties
, SEC_X400_ADDRESS_KEY
, NULL
,
3179 generalName
, localized
);
3181 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
3183 CFArrayRef directory_plist
=
3184 createPropertiesForX501Name(CFGetAllocator(properties
),
3185 generalName
, localized
);
3186 appendProperty(properties
, kSecPropertyTypeSection
,
3187 SEC_DIRECTORY_NAME_KEY
, NULL
, directory_plist
, localized
);
3188 CFRelease(directory_plist
);
3191 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
3192 appendUnparsedProperty(properties
, SEC_EDI_PARTY_NAME_KEY
, NULL
,
3193 generalName
, localized
);
3195 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
3196 /* Technically I don't think this is valid, but there are certs out
3197 in the wild that use a constructed IA5String. In particular the
3198 VeriSign Time Stamping Authority CA.cer does this. */
3199 appendURLProperty(properties
, SEC_URI_KEY
, generalName
, localized
);
3201 case ASN1_CONTEXT_SPECIFIC
| 6:
3202 appendURLContentProperty(properties
, SEC_URI_KEY
, generalName
, localized
);
3204 case ASN1_CONTEXT_SPECIFIC
| 7:
3205 appendIPAddressContentProperty(properties
, SEC_IP_ADDRESS_KEY
,
3206 generalName
, localized
);
3208 case ASN1_CONTEXT_SPECIFIC
| 8:
3209 appendOIDProperty(properties
, SEC_REGISTERED_ID_KEY
, NULL
,
3210 generalName
, localized
);
3221 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
3222 const DERItem
*generalName
, bool localized
) {
3223 DERDecodedInfo generalNameContent
;
3224 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
3225 require_noerr_quiet(drtn
, badDER
);
3226 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
3227 &generalNameContent
.content
, localized
))
3230 appendInvalidProperty(properties
, SEC_GENERAL_NAME_KEY
,
3231 generalName
, localized
);
3236 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
3238 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
3239 const DERItem
*generalNamesContent
, bool localized
) {
3241 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
3242 require_noerr_quiet(drtn
, badDER
);
3243 DERDecodedInfo generalNameContent
;
3244 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
3246 if (!appendGeneralNameContentProperty(properties
,
3247 generalNameContent
.tag
, &generalNameContent
.content
, localized
)) {
3251 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3254 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
3255 generalNamesContent
, localized
);
3258 static void appendGeneralNames(CFMutableArrayRef properties
,
3259 const DERItem
*generalNames
, bool localized
) {
3260 DERDecodedInfo generalNamesContent
;
3261 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
3262 require_noerr_quiet(drtn
, badDER
);
3263 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
3265 appendGeneralNamesContent(properties
, &generalNamesContent
.content
,
3269 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
3270 generalNames
, localized
);
3274 BasicConstraints ::= SEQUENCE {
3275 cA BOOLEAN DEFAULT FALSE,
3276 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
3278 static void appendBasicConstraints(CFMutableArrayRef properties
,
3279 const DERItem
*extnValue
, bool localized
) {
3280 DERBasicConstraints basicConstraints
;
3281 DERReturn drtn
= DERParseSequence(extnValue
,
3282 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
3283 &basicConstraints
, sizeof(basicConstraints
));
3284 require_noerr_quiet(drtn
, badDER
);
3286 appendBooleanProperty(properties
, SEC_CERT_AUTHORITY_KEY
,
3287 &basicConstraints
.cA
, false, localized
);
3289 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
3290 appendIntegerProperty(properties
, SEC_PATH_LEN_CONSTRAINT_KEY
,
3291 &basicConstraints
.pathLenConstraint
, localized
);
3295 appendInvalidProperty(properties
, SEC_BASIC_CONSTRAINTS_KEY
,
3296 extnValue
, localized
);
3300 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
3302 * NameConstraints ::= SEQUENCE {
3303 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
3304 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
3306 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
3308 * GeneralSubtree ::= SEQUENCE {
3310 * minimum [0] BaseDistance DEFAULT 0,
3311 * maximum [1] BaseDistance OPTIONAL }
3313 * BaseDistance ::= INTEGER (0..MAX)
3315 static void appendNameConstraints(CFMutableArrayRef properties
,
3316 const DERItem
*extnValue
, bool localized
) {
3317 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3318 DERNameConstraints nc
;
3320 drtn
= DERParseSequence(extnValue
,
3321 DERNumNameConstraintsItemSpecs
,
3322 DERNameConstraintsItemSpecs
,
3324 require_noerr_quiet(drtn
, badDER
);
3325 if (nc
.permittedSubtrees
.length
) {
3327 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.permittedSubtrees
, &gsSeq
), badDER
);
3328 DERDecodedInfo gsContent
;
3329 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3330 DERGeneralSubtree derGS
;
3331 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3332 drtn
= DERParseSequenceContent(&gsContent
.content
,
3333 DERNumGeneralSubtreeItemSpecs
,
3334 DERGeneralSubtreeItemSpecs
,
3335 &derGS
, sizeof(derGS
));
3336 require_noerr_quiet(drtn
, badDER
);
3337 if (derGS
.minimum
.length
) {
3338 appendIntegerProperty(properties
, SEC_PERMITTED_MINIMUM_KEY
,
3339 &derGS
.minimum
, localized
);
3341 if (derGS
.maximum
.length
) {
3342 appendIntegerProperty(properties
, SEC_PERMITTED_MAXIMUM_KEY
,
3343 &derGS
.maximum
, localized
);
3345 if (derGS
.generalName
.length
) {
3346 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3347 &kCFTypeArrayCallBacks
);
3348 appendProperty(properties
, kSecPropertyTypeSection
,
3349 SEC_PERMITTED_NAME_KEY
, NULL
, base
, localized
);
3350 appendGeneralNameProperty(base
, &derGS
.generalName
, localized
);
3354 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3356 if (nc
.excludedSubtrees
.length
) {
3358 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.excludedSubtrees
, &gsSeq
), badDER
);
3359 DERDecodedInfo gsContent
;
3360 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3361 DERGeneralSubtree derGS
;
3362 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3363 drtn
= DERParseSequenceContent(&gsContent
.content
,
3364 DERNumGeneralSubtreeItemSpecs
,
3365 DERGeneralSubtreeItemSpecs
,
3366 &derGS
, sizeof(derGS
));
3367 require_noerr_quiet(drtn
, badDER
);
3368 if (derGS
.minimum
.length
) {
3369 appendIntegerProperty(properties
, SEC_EXCLUDED_MINIMUM_KEY
,
3370 &derGS
.minimum
, localized
);
3372 if (derGS
.maximum
.length
) {
3373 appendIntegerProperty(properties
, SEC_EXCLUDED_MAXIMUM_KEY
,
3374 &derGS
.maximum
, localized
);
3376 if (derGS
.generalName
.length
) {
3377 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3378 &kCFTypeArrayCallBacks
);
3379 appendProperty(properties
, kSecPropertyTypeSection
,
3380 SEC_EXCLUDED_NAME_KEY
, NULL
, base
, localized
);
3381 appendGeneralNameProperty(base
, &derGS
.generalName
, localized
);
3385 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3390 appendInvalidProperty(properties
, SEC_NAME_CONSTRAINTS_KEY
,
3391 extnValue
, localized
);
3395 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
3397 DistributionPoint ::= SEQUENCE {
3398 distributionPoint [0] DistributionPointName OPTIONAL,
3399 reasons [1] ReasonFlags OPTIONAL,
3400 cRLIssuer [2] GeneralNames OPTIONAL }
3402 DistributionPointName ::= CHOICE {
3403 fullName [0] GeneralNames,
3404 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
3406 ReasonFlags ::= BIT STRING {
3410 affiliationChanged (3),
3412 cessationOfOperation (5),
3413 certificateHold (6),
3414 privilegeWithdrawn (7),
3417 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
3418 const DERItem
*extnValue
, bool localized
) {
3419 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3422 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
3423 require_noerr_quiet(drtn
, badDER
);
3424 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3425 DERDecodedInfo dpSeqContent
;
3426 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
3427 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3428 DERDistributionPoint dp
;
3429 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
3430 DERNumDistributionPointItemSpecs
,
3431 DERDistributionPointItemSpecs
,
3433 require_noerr_quiet(drtn
, badDER
);
3434 if (dp
.distributionPoint
.length
) {
3435 DERDecodedInfo distributionPointName
;
3436 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
3437 require_noerr_quiet(drtn
, badDER
);
3438 if (distributionPointName
.tag
==
3439 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
3441 appendGeneralNamesContent(properties
,
3442 &distributionPointName
.content
, localized
);
3443 } else if (distributionPointName
.tag
==
3444 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
3445 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
3446 &dp
.reasons
, localized
);
3447 appendProperty(properties
, kSecPropertyTypeSection
,
3448 SEC_NAME_REL_CRL_ISSUER_KEY
, NULL
, rdn_props
, localized
);
3449 CFRelease(rdn_props
);
3454 if (dp
.reasons
.length
) {
3455 static const CFStringRef reasonNames
[] = {
3457 SEC_KEY_COMPROMISE_KEY
,
3458 SEC_CA_COMPROMISE_KEY
,
3459 SEC_AFFILIATION_CHANGED_KEY
,
3461 SEC_CESSATION_OF_OPER_KEY
,
3462 SEC_CERTIFICATE_HOLD_KEY
,
3463 SEC_PRIV_WITHDRAWN_KEY
,
3464 SEC_AA_COMPROMISE_KEY
3466 appendBitStringContentNames(properties
, SEC_REASONS_KEY
,
3468 reasonNames
, array_size(reasonNames
), localized
);
3470 if (dp
.cRLIssuer
.length
) {
3471 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
3472 &kCFTypeArrayCallBacks
);
3473 appendProperty(properties
, kSecPropertyTypeSection
,
3474 SEC_CRL_ISSUER_KEY
, NULL
, crlIssuer
, localized
);
3475 CFRelease(crlIssuer
);
3476 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
, localized
);
3479 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3482 appendInvalidProperty(properties
, SEC_CRL_DISTR_POINTS_KEY
,
3483 extnValue
, localized
);
3487 Decode a sequence of integers into a comma separated list of ints.
3489 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
3490 CFStringRef label
, const DERItem
*intSequenceContent
,
3492 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3494 CFStringRef fmt
= NULL
, value
= NULL
, intDesc
= NULL
, v
= NULL
;
3495 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
3496 require_noerr_quiet(drtn
, badDER
);
3497 DERDecodedInfo intContent
;
3499 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
3500 require_quiet(fmt
, badDER
);
3501 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
)) == DR_Success
) {
3502 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
3503 intDesc
= copyIntegerContentDescription(
3504 allocator
, &intContent
.content
);
3505 require_quiet(intDesc
, badDER
);
3507 v
= CFStringCreateWithFormat(allocator
, NULL
, fmt
, value
, intDesc
);
3508 CFReleaseNull(value
);
3509 require_quiet(v
, badDER
);
3512 value
= CFStringCreateMutableCopy(allocator
, 0, intDesc
);
3513 require_quiet(value
, badDER
);
3515 CFReleaseNull(intDesc
);
3518 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3520 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3525 /* DROPTHOUGH if !value. */
3528 CFReleaseNull(intDesc
);
3529 CFReleaseNull(value
);
3530 appendInvalidProperty(properties
, label
, intSequenceContent
, localized
);
3533 static void appendCertificatePolicies(CFMutableArrayRef properties
,
3534 const DERItem
*extnValue
, bool localized
) {
3535 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3536 CFStringRef piLabel
= NULL
, piFmt
= NULL
, lpiLabel
= NULL
;
3537 CFStringRef pqLabel
= NULL
, pqFmt
= NULL
, lpqLabel
= NULL
;
3540 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
3541 require_noerr_quiet(drtn
, badDER
);
3542 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3543 DERDecodedInfo piContent
;
3545 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
3546 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3547 DERPolicyInformation pi
;
3548 drtn
= DERParseSequenceContent(&piContent
.content
,
3549 DERNumPolicyInformationItemSpecs
,
3550 DERPolicyInformationItemSpecs
,
3552 require_noerr_quiet(drtn
, badDER
);
3553 piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3554 SEC_POLICY_IDENTIFIER_KEY
, pin
);
3555 require_quiet(piLabel
, badDER
);
3556 piFmt
= (localized
) ?
3557 SecCopyCertString(SEC_POLICY_IDENTIFIER_KEY
) : SEC_POLICY_IDENTIFIER_KEY
;
3558 require_quiet(piFmt
, badDER
);
3559 lpiLabel
= CFStringCreateWithFormat(allocator
, NULL
, piFmt
, pin
++);
3560 require_quiet(lpiLabel
, badDER
);
3561 CFReleaseNull(piFmt
);
3562 appendOIDProperty(properties
, piLabel
, lpiLabel
,
3563 &pi
.policyIdentifier
, localized
);
3564 CFReleaseNull(piLabel
);
3565 CFReleaseNull(lpiLabel
);
3566 if (pi
.policyQualifiers
.length
== 0)
3570 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
3571 require_noerr_quiet(drtn
, badDER
);
3572 DERDecodedInfo pqContent
;
3574 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
3575 DERPolicyQualifierInfo pqi
;
3576 drtn
= DERParseSequenceContent(&pqContent
.content
,
3577 DERNumPolicyQualifierInfoItemSpecs
,
3578 DERPolicyQualifierInfoItemSpecs
,
3580 require_noerr_quiet(drtn
, badDER
);
3581 DERDecodedInfo qualifierContent
;
3582 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
3583 require_noerr_quiet(drtn
, badDER
);
3584 pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3585 SEC_POLICY_QUALIFIER_KEY
, pqn
);
3586 require_quiet(pqLabel
, badDER
);
3587 pqFmt
= (localized
) ?
3588 SecCopyCertString(SEC_POLICY_QUALIFIER_KEY
) : SEC_POLICY_QUALIFIER_KEY
;
3589 require_quiet(pqFmt
, badDER
);
3590 lpqLabel
= CFStringCreateWithFormat(allocator
, NULL
, pqFmt
, pqn
++);
3591 require_quiet(lpqLabel
, badDER
);
3592 CFReleaseNull(pqFmt
);
3593 appendOIDProperty(properties
, pqLabel
, lpqLabel
,
3594 &pqi
.policyQualifierID
, localized
);
3595 CFReleaseNull(pqLabel
);
3596 CFReleaseNull(lpqLabel
);
3597 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
3598 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
3599 appendURLContentProperty(properties
, SEC_CPS_URI_KEY
,
3600 &qualifierContent
.content
, localized
);
3601 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
3602 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3604 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
3605 DERNumUserNoticeItemSpecs
,
3606 DERUserNoticeItemSpecs
,
3608 require_noerr_quiet(drtn
, badDER
);
3609 if (un
.noticeRef
.length
) {
3610 DERNoticeReference nr
;
3611 drtn
= DERParseSequenceContent(&un
.noticeRef
,
3612 DERNumNoticeReferenceItemSpecs
,
3613 DERNoticeReferenceItemSpecs
,
3615 require_noerr_quiet(drtn
, badDER
);
3616 appendDERThingProperty(properties
,
3617 SEC_ORGANIZATION_KEY
, NULL
,
3618 &nr
.organization
, localized
);
3619 appendIntegerSequenceContent(properties
,
3620 SEC_NOTICE_NUMBERS_KEY
, &nr
.noticeNumbers
, localized
);
3622 if (un
.explicitText
.length
) {
3623 appendDERThingProperty(properties
, SEC_EXPLICIT_TEXT_KEY
,
3624 NULL
, &un
.explicitText
, localized
);
3627 appendUnparsedProperty(properties
, SEC_QUALIFIER_KEY
, NULL
,
3628 &pqi
.qualifier
, localized
);
3631 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3633 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3636 CFReleaseNull(piFmt
);
3637 CFReleaseNull(piLabel
);
3638 CFReleaseNull(lpiLabel
);
3639 CFReleaseNull(pqFmt
);
3640 CFReleaseNull(pqLabel
);
3641 CFReleaseNull(lpqLabel
);
3642 appendInvalidProperty(properties
, SEC_CERT_POLICIES_KEY
,
3643 extnValue
, localized
);
3646 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
3647 const DERItem
*extnValue
, bool localized
) {
3649 DERDecodedInfo keyIdentifier
;
3650 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
3651 require_noerr_quiet(drtn
, badDER
);
3652 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
3653 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3654 &keyIdentifier
.content
, localized
);
3658 appendInvalidProperty(properties
, SEC_SUBJ_KEY_ID_KEY
,
3659 extnValue
, localized
);
3663 AuthorityKeyIdentifier ::= SEQUENCE {
3664 keyIdentifier [0] KeyIdentifier OPTIONAL,
3665 authorityCertIssuer [1] GeneralNames OPTIONAL,
3666 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
3667 -- authorityCertIssuer and authorityCertSerialNumber MUST both
3668 -- be present or both be absent
3670 KeyIdentifier ::= OCTET STRING
3672 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
3673 const DERItem
*extnValue
, bool localized
) {
3674 DERAuthorityKeyIdentifier akid
;
3676 drtn
= DERParseSequence(extnValue
,
3677 DERNumAuthorityKeyIdentifierItemSpecs
,
3678 DERAuthorityKeyIdentifierItemSpecs
,
3679 &akid
, sizeof(akid
));
3680 require_noerr_quiet(drtn
, badDER
);
3681 if (akid
.keyIdentifier
.length
) {
3682 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3683 &akid
.keyIdentifier
, localized
);
3685 if (akid
.authorityCertIssuer
.length
||
3686 akid
.authorityCertSerialNumber
.length
) {
3687 require_quiet(akid
.authorityCertIssuer
.length
&&
3688 akid
.authorityCertSerialNumber
.length
, badDER
);
3689 /* Perhaps put in a subsection called Authority Certificate Issuer. */
3690 appendGeneralNamesContent(properties
,
3691 &akid
.authorityCertIssuer
, localized
);
3692 appendIntegerProperty(properties
, SEC_AUTH_CERT_SERIAL_KEY
,
3693 &akid
.authorityCertSerialNumber
, localized
);
3698 appendInvalidProperty(properties
, SEC_AUTHORITY_KEY_ID_KEY
,
3699 extnValue
, localized
);
3703 PolicyConstraints ::= SEQUENCE {
3704 requireExplicitPolicy [0] SkipCerts OPTIONAL,
3705 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
3707 SkipCerts ::= INTEGER (0..MAX)
3709 static void appendPolicyConstraints(CFMutableArrayRef properties
,
3710 const DERItem
*extnValue
, bool localized
) {
3711 DERPolicyConstraints pc
;
3713 drtn
= DERParseSequence(extnValue
,
3714 DERNumPolicyConstraintsItemSpecs
,
3715 DERPolicyConstraintsItemSpecs
,
3717 require_noerr_quiet(drtn
, badDER
);
3718 if (pc
.requireExplicitPolicy
.length
) {
3719 appendIntegerProperty(properties
, SEC_REQUIRE_EXPL_POLICY_KEY
,
3720 &pc
.requireExplicitPolicy
, localized
);
3722 if (pc
.inhibitPolicyMapping
.length
) {
3723 appendIntegerProperty(properties
, SEC_INHIBIT_POLICY_MAP_KEY
,
3724 &pc
.inhibitPolicyMapping
, localized
);
3730 appendInvalidProperty(properties
, SEC_POLICY_CONSTRAINTS_KEY
,
3731 extnValue
, localized
);
3735 extendedKeyUsage EXTENSION ::= {
3736 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
3737 IDENTIFIED BY id-ce-extKeyUsage }
3739 KeyPurposeId ::= OBJECT IDENTIFIER
3741 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
3742 const DERItem
*extnValue
, bool localized
) {
3745 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
3746 require_noerr_quiet(drtn
, badDER
);
3747 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3748 DERDecodedInfo currDecoded
;
3749 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3750 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
3751 appendOIDProperty(properties
, SEC_PURPOSE_KEY
, NULL
,
3752 &currDecoded
.content
, localized
);
3754 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3757 appendInvalidProperty(properties
, SEC_EXTENDED_KEY_USAGE_KEY
,
3758 extnValue
, localized
);
3762 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3764 AuthorityInfoAccessSyntax ::=
3765 SEQUENCE SIZE (1..MAX) OF AccessDescription
3767 AccessDescription ::= SEQUENCE {
3768 accessMethod OBJECT IDENTIFIER,
3769 accessLocation GeneralName }
3771 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3773 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3775 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3777 static void appendInfoAccess(CFMutableArrayRef properties
,
3778 const DERItem
*extnValue
, bool localized
) {
3781 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3782 require_noerr_quiet(drtn
, badDER
);
3783 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3784 DERDecodedInfo adContent
;
3785 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3786 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3787 DERAccessDescription ad
;
3788 drtn
= DERParseSequenceContent(&adContent
.content
,
3789 DERNumAccessDescriptionItemSpecs
,
3790 DERAccessDescriptionItemSpecs
,
3792 require_noerr_quiet(drtn
, badDER
);
3793 appendOIDProperty(properties
, SEC_ACCESS_METHOD_KEY
, NULL
,
3794 &ad
.accessMethod
, localized
);
3795 //TODO: Do something with SEC_ACCESS_LOCATION_KEY
3796 appendGeneralNameProperty(properties
, &ad
.accessLocation
, localized
);
3798 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3801 appendInvalidProperty(properties
, SEC_AUTH_INFO_ACCESS_KEY
,
3802 extnValue
, localized
);
3805 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3806 const DERItem
*extnValue
, bool localized
) {
3807 static const CFStringRef certTypes
[] = {
3811 SEC_OBJECT_SIGNING_KEY
,
3815 SEC_OBJECT_SIGNING_CA_KEY
3817 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3818 certTypes
, array_size(certTypes
), localized
);
3821 static bool appendPrintableDERSequence(CFMutableArrayRef properties
,
3822 CFStringRef label
, const DERItem
*sequence
, bool localized
) {
3825 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3826 require_noerr_quiet(drtn
, badSequence
);
3827 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3828 DERDecodedInfo currDecoded
;
3829 bool appendedSomething
= false;
3830 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3831 switch (currDecoded
.tag
)
3834 case ASN1_SEQUENCE
: // 16
3835 case ASN1_SET
: // 17
3836 // skip constructed object lengths
3839 case ASN1_UTF8_STRING
: // 12
3840 case ASN1_NUMERIC_STRING
: // 18
3841 case ASN1_PRINTABLE_STRING
: // 19
3842 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3843 case ASN1_VIDEOTEX_STRING
: // 21
3844 case ASN1_IA5_STRING
: // 22
3845 case ASN1_GRAPHIC_STRING
: // 25
3846 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3847 case ASN1_GENERAL_STRING
: // 27
3848 case ASN1_UNIVERSAL_STRING
: // 28
3850 CFStringRef string
=
3851 copyDERThingContentDescription(CFGetAllocator(properties
),
3852 currDecoded
.tag
, &currDecoded
.content
, false, localized
);
3853 require_quiet(string
, badSequence
);
3855 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3857 CFReleaseNull(string
);
3858 appendedSomething
= true;
3865 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3866 return appendedSomething
;
3871 static void appendExtension(CFMutableArrayRef parent
,
3872 const SecCertificateExtension
*extn
,
3874 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3875 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3876 &kCFTypeArrayCallBacks
);
3878 *extnID
= &extn
->extnID
,
3879 *extnValue
= &extn
->extnValue
;
3880 CFStringRef label
= NULL
;
3881 CFStringRef localizedLabel
= NULL
;
3883 appendBoolProperty(properties
, SEC_CRITICAL_KEY
, extn
->critical
, localized
);
3884 require_quiet(extnID
, xit
);
3886 bool handled
= true;
3887 /* Extensions that we know how to handle ourselves... */
3888 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3889 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3891 switch (extnID
->data
[extnID
->length
- 1]) {
3892 case 14: /* SubjectKeyIdentifier id-ce 14 */
3893 appendSubjectKeyIdentifier(properties
, extnValue
, localized
);
3895 case 15: /* KeyUsage id-ce 15 */
3896 appendKeyUsage(properties
, extnValue
, localized
);
3898 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3899 appendPrivateKeyUsagePeriod(properties
, extnValue
, localized
);
3901 case 17: /* SubjectAltName id-ce 17 */
3902 case 18: /* IssuerAltName id-ce 18 */
3903 appendGeneralNames(properties
, extnValue
, localized
);
3905 case 19: /* BasicConstraints id-ce 19 */
3906 appendBasicConstraints(properties
, extnValue
, localized
);
3908 case 30: /* NameConstraints id-ce 30 */
3909 appendNameConstraints(properties
, extnValue
, localized
);
3911 case 31: /* CRLDistributionPoints id-ce 31 */
3912 appendCrlDistributionPoints(properties
, extnValue
, localized
);
3914 case 32: /* CertificatePolicies id-ce 32 */
3915 appendCertificatePolicies(properties
, extnValue
, localized
);
3917 case 33: /* PolicyMappings id-ce 33 */
3920 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3921 appendAuthorityKeyIdentifier(properties
, extnValue
, localized
);
3923 case 36: /* PolicyConstraints id-ce 36 */
3924 appendPolicyConstraints(properties
, extnValue
, localized
);
3926 case 37: /* ExtKeyUsage id-ce 37 */
3927 appendExtendedKeyUsage(properties
, extnValue
, localized
);
3929 case 46: /* FreshestCRL id-ce 46 */
3932 case 54: /* InhibitAnyPolicy id-ce 54 */
3939 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3940 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3942 switch (extnID
->data
[extnID
->length
- 1]) {
3943 case 1: /* AuthorityInfoAccess id-pe 1 */
3944 appendInfoAccess(properties
, extnValue
, localized
);
3946 case 3: /* QCStatements id-pe 3 */
3949 case 11: /* SubjectInfoAccess id-pe 11 */
3950 appendInfoAccess(properties
, extnValue
, localized
);
3956 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3957 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3958 appendNetscapeCertType(properties
, extnValue
, localized
);
3964 /* Try to parse and display printable string(s). */
3965 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
, localized
)) {
3966 /* Nothing to do here appendPrintableDERSequence did the work. */
3968 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3969 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
, localized
);
3972 label
= SecDERItemCopyOIDDecimalRepresentation(allocator
, extnID
);
3973 localizedLabel
= copyOidDescription(allocator
, extnID
, localized
);
3974 appendProperty(parent
, kSecPropertyTypeSection
, label
, localizedLabel
,
3975 properties
, localized
);
3977 CFReleaseSafe(localizedLabel
);
3978 CFReleaseSafe(label
);
3979 CFReleaseSafe(properties
);
3982 /* Different types of summary types from least desired to most desired. */
3985 kSummaryTypePrintable
,
3986 kSummaryTypeOrganizationName
,
3987 kSummaryTypeOrganizationalUnitName
,
3988 kSummaryTypeCommonName
,
3992 enum SummaryType type
;
3993 CFStringRef summary
;
3994 CFStringRef description
;
3997 static OSStatus
obtainSummaryFromX501Name(void *context
,
3998 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
,
4000 struct Summary
*summary
= (struct Summary
*)context
;
4001 enum SummaryType stype
= kSummaryTypeNone
;
4002 CFStringRef string
= NULL
;
4003 if (DEROidCompare(type
, &oidCommonName
)) {
4004 stype
= kSummaryTypeCommonName
;
4005 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4006 stype
= kSummaryTypeOrganizationalUnitName
;
4007 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4008 stype
= kSummaryTypeOrganizationName
;
4009 } else if (DEROidCompare(type
, &oidDescription
)) {
4010 string
= copyDERThingDescription(kCFAllocatorDefault
, value
,
4013 if (summary
->description
) {
4014 CFStringRef fmt
= (localized
) ?
4015 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
4016 CFStringRef newDescription
= CFStringCreateWithFormat(kCFAllocatorDefault
,
4017 NULL
, fmt
, string
, summary
->description
);
4019 CFRelease(summary
->description
);
4020 summary
->description
= newDescription
;
4022 summary
->description
= string
;
4025 stype
= kSummaryTypePrintable
;
4028 stype
= kSummaryTypePrintable
;
4031 /* Build a string with all instances of the most desired
4032 component type in reverse order encountered comma separated list,
4033 The order of desirability is defined by enum SummaryType. */
4034 if (summary
->type
<= stype
) {
4036 string
= copyDERThingDescription(kCFAllocatorDefault
, value
,
4040 if (summary
->type
== stype
) {
4041 CFStringRef fmt
= (localized
) ?
4042 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
4043 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
,
4044 NULL
, fmt
, string
, summary
->summary
);
4047 string
= newSummary
;
4049 summary
->type
= stype
;
4051 CFReleaseSafe(summary
->summary
);
4052 summary
->summary
= string
;
4055 CFReleaseSafe(string
);
4058 return errSecSuccess
;
4061 CFStringRef
SecCertificateCopySubjectSummary(SecCertificateRef certificate
) {
4062 struct Summary summary
= {};
4063 OSStatus status
= parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
, true);
4064 if (status
!= errSecSuccess
) {
4067 /* If we found a description and a common name we change the summary to
4068 CommonName (Description). */
4069 if (summary
.description
) {
4070 if (summary
.type
== kSummaryTypeCommonName
) {
4071 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
4072 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
4074 CFRelease(summary
.summary
);
4075 summary
.summary
= newSummary
;
4077 CFRelease(summary
.description
);
4080 if (!summary
.summary
) {
4081 /* If we didn't find a suitable printable string in the subject at all, we try
4082 the first email address in the certificate instead. */
4083 CFArrayRef names
= SecCertificateCopyRFC822Names(certificate
);
4085 /* If we didn't find any email addresses in the certificate, we try finding
4086 a DNS name instead. */
4087 names
= SecCertificateCopyDNSNames(certificate
);
4090 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
4091 CFRetain(summary
.summary
);
4096 return summary
.summary
;
4099 CFStringRef
SecCertificateCopyIssuerSummary(SecCertificateRef certificate
) {
4100 struct Summary summary
= {};
4101 OSStatus status
= parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
, true);
4102 if (status
!= errSecSuccess
) {
4105 /* If we found a description and a common name we change the summary to
4106 CommonName (Description). */
4107 if (summary
.description
) {
4108 if (summary
.type
== kSummaryTypeCommonName
) {
4109 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
4110 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
4112 CFRelease(summary
.summary
);
4113 summary
.summary
= newSummary
;
4115 CFRelease(summary
.description
);
4118 return summary
.summary
;
4121 /* Return the earliest date on which all certificates in this chain are still
4123 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
4124 SecCertificateRef certificate
) {
4125 CFAbsoluteTime earliest
= certificate
->_notAfter
;
4127 while (certificate
->_parent
) {
4128 certificate
= certificate
->_parent
;
4129 if (earliest
> certificate
->_notAfter
)
4130 earliest
= certificate
->_notAfter
;
4137 /* Return the latest date on which all certificates in this chain will be
4139 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
4140 SecCertificateRef certificate
) {
4141 CFAbsoluteTime latest
= certificate
->_notBefore
;
4143 while (certificate
->_parent
) {
4144 certificate
= certificate
->_parent
;
4145 if (latest
< certificate
->_notBefore
)
4146 latest
= certificate
->_notBefore
;
4153 bool SecCertificateIsValid(SecCertificateRef certificate
,
4154 CFAbsoluteTime verifyTime
) {
4155 return certificate
&& certificate
->_notBefore
<= verifyTime
&&
4156 verifyTime
<= certificate
->_notAfter
;
4159 CFIndex
SecCertificateVersion(SecCertificateRef certificate
) {
4160 return certificate
->_version
+ 1;
4163 CFAbsoluteTime
SecCertificateNotValidBefore(SecCertificateRef certificate
) {
4164 return certificate
->_notBefore
;
4167 CFAbsoluteTime
SecCertificateNotValidAfter(SecCertificateRef certificate
) {
4168 return certificate
->_notAfter
;
4171 CFMutableArrayRef
SecCertificateCopySummaryProperties(
4172 SecCertificateRef certificate
, CFAbsoluteTime verifyTime
) {
4173 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4174 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
4175 &kCFTypeArrayCallBacks
);
4176 bool localized
= true;
4178 /* First we put the subject summary name. */
4179 CFStringRef ssummary
= SecCertificateCopySubjectSummary(certificate
);
4181 appendProperty(summary
, kSecPropertyTypeTitle
,
4182 NULL
, NULL
, ssummary
, localized
);
4183 CFRelease(ssummary
);
4186 /* Let see if this certificate is currently valid. */
4188 CFAbsoluteTime when
;
4189 CFStringRef message
;
4191 if (verifyTime
> certificate
->_notAfter
) {
4192 label
= SEC_EXPIRED_KEY
;
4193 when
= certificate
->_notAfter
;
4194 ptype
= kSecPropertyTypeError
;
4195 message
= SEC_CERT_EXPIRED_KEY
;
4196 } else if (certificate
->_notBefore
> verifyTime
) {
4197 label
= SEC_VALID_FROM_KEY
;
4198 when
= certificate
->_notBefore
;
4199 ptype
= kSecPropertyTypeError
;
4200 message
= SEC_CERT_NOT_YET_VALID_KEY
;
4202 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
4203 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
4204 if (verifyTime
> last
) {
4205 label
= SEC_EXPIRED_KEY
;
4207 ptype
= kSecPropertyTypeError
;
4208 message
= SEC_ISSUER_EXPIRED_KEY
;
4209 } else if (verifyTime
< first
) {
4210 label
= SEC_VALID_FROM_KEY
;
4212 ptype
= kSecPropertyTypeError
;
4213 message
= SEC_ISSR_NOT_YET_VALID_KEY
;
4215 label
= SEC_EXPIRES_KEY
;
4216 when
= certificate
->_notAfter
;
4217 ptype
= kSecPropertyTypeSuccess
;
4218 message
= SEC_CERT_VALID_KEY
;
4222 appendDateProperty(summary
, label
, when
, localized
);
4223 CFStringRef lmessage
= SecCopyCertString(message
);
4224 appendProperty(summary
, ptype
, NULL
, NULL
, lmessage
, localized
);
4225 CFRelease(lmessage
);
4230 CFArrayRef
SecCertificateCopyLegacyProperties(SecCertificateRef certificate
) {
4232 This function replicates the content returned by SecCertificateCopyProperties
4233 prior to 10.12.4, providing stable return values for SecCertificateCopyValues.
4234 Unlike SecCertificateCopyProperties, it does not cache the result and
4235 assumes the caller will do so.
4237 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4238 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
,
4239 0, &kCFTypeArrayCallBacks
);
4242 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
4243 &certificate
->_subject
, false);
4244 appendProperty(properties
, kSecPropertyTypeSection
, CFSTR("Subject Name"),
4245 NULL
, subject_plist
, false);
4246 CFRelease(subject_plist
);
4249 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
4250 &certificate
->_issuer
, false);
4251 appendProperty(properties
, kSecPropertyTypeSection
, CFSTR("Issuer Name"),
4252 NULL
, issuer_plist
, false);
4253 CFRelease(issuer_plist
);
4256 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
4257 NULL
, CFSTR("%d"), certificate
->_version
+ 1);
4258 appendProperty(properties
, kSecPropertyTypeString
, CFSTR("Version"),
4259 NULL
, versionString
, false);
4260 CFRelease(versionString
);
4263 if (certificate
->_serialNum
.length
) {
4264 appendIntegerProperty(properties
, CFSTR("Serial Number"),
4265 &certificate
->_serialNum
, false);
4268 /* Signature Algorithm */
4269 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
4270 &certificate
->_tbsSigAlg
, false);
4272 /* Validity dates */
4273 appendDateProperty(properties
, CFSTR("Not Valid Before"), certificate
->_notBefore
, false);
4274 appendDateProperty(properties
, CFSTR("Not Valid After"), certificate
->_notAfter
, false);
4276 if (certificate
->_subjectUniqueID
.length
) {
4277 appendDataProperty(properties
, CFSTR("Subject Unique ID"),
4278 NULL
, &certificate
->_subjectUniqueID
, false);
4280 if (certificate
->_issuerUniqueID
.length
) {
4281 appendDataProperty(properties
, CFSTR("Issuer Unique ID"),
4282 NULL
, &certificate
->_issuerUniqueID
, false);
4285 /* Public Key Algorithm */
4286 appendAlgorithmProperty(properties
, CFSTR("Public Key Algorithm"),
4287 &certificate
->_algId
, false);
4289 /* Public Key Data */
4290 appendDataProperty(properties
, CFSTR("Public Key Data"),
4291 NULL
, &certificate
->_pubKeyDER
, false);
4294 appendDataProperty(properties
, CFSTR("Signature"),
4295 NULL
, &certificate
->_signature
, false);
4299 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4300 appendExtension(properties
, &certificate
->_extensions
[ix
], false);
4304 appendFingerprintsProperty(properties
, CFSTR("Fingerprints"), certificate
, false);
4309 CFArrayRef
SecCertificateCopyProperties(SecCertificateRef certificate
) {
4310 if (!certificate
->_properties
) {
4311 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4312 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
4313 &kCFTypeArrayCallBacks
);
4314 require_quiet(properties
, out
);
4315 bool localized
= true;
4317 /* First we put the Subject Name in the property list. */
4318 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
4319 &certificate
->_subject
,
4321 if (subject_plist
) {
4322 appendProperty(properties
, kSecPropertyTypeSection
,
4323 SEC_SUBJECT_NAME_KEY
, NULL
, subject_plist
, localized
);
4325 CFReleaseNull(subject_plist
);
4327 /* Next we put the Issuer Name in the property list. */
4328 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
4329 &certificate
->_issuer
,
4332 appendProperty(properties
, kSecPropertyTypeSection
,
4333 SEC_ISSUER_NAME_KEY
, NULL
, issuer_plist
, localized
);
4335 CFReleaseNull(issuer_plist
);
4338 CFStringRef fmt
= SecCopyCertString(SEC_CERT_VERSION_VALUE_KEY
);
4339 CFStringRef versionString
= NULL
;
4341 versionString
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
4342 certificate
->_version
+ 1);
4345 if (versionString
) {
4346 appendProperty(properties
, kSecPropertyTypeString
,
4347 SEC_VERSION_KEY
, NULL
, versionString
, localized
);
4349 CFReleaseNull(versionString
);
4352 appendSerialNumberProperty(properties
, SEC_SERIAL_NUMBER_KEY
, &certificate
->_serialNum
, localized
);
4354 /* Validity dates. */
4355 appendValidityPeriodProperty(properties
, SEC_VALIDITY_PERIOD_KEY
, certificate
, localized
);
4357 if (certificate
->_subjectUniqueID
.length
) {
4358 appendDataProperty(properties
, SEC_SUBJECT_UNIQUE_ID_KEY
, NULL
,
4359 &certificate
->_subjectUniqueID
, localized
);
4361 if (certificate
->_issuerUniqueID
.length
) {
4362 appendDataProperty(properties
, SEC_ISSUER_UNIQUE_ID_KEY
, NULL
,
4363 &certificate
->_issuerUniqueID
, localized
);
4366 appendPublicKeyProperty(properties
, SEC_PUBLIC_KEY_KEY
, certificate
, localized
);
4369 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4370 appendExtension(properties
, &certificate
->_extensions
[ix
], localized
);
4374 appendSignatureProperty(properties
, SEC_SIGNATURE_KEY
, certificate
, localized
);
4376 appendFingerprintsProperty(properties
, SEC_FINGERPRINTS_KEY
, certificate
, localized
);
4378 certificate
->_properties
= properties
;
4382 CFRetainSafe(certificate
->_properties
);
4383 return certificate
->_properties
;
4386 /* Unified serial number API */
4387 CFDataRef
SecCertificateCopySerialNumberData(
4388 SecCertificateRef certificate
,
4393 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
4397 if (certificate
->_serialNumber
) {
4398 CFRetain(certificate
->_serialNumber
);
4400 return certificate
->_serialNumber
;
4404 /* On iOS, the SecCertificateCopySerialNumber API takes one argument. */
4405 CFDataRef
SecCertificateCopySerialNumber(
4406 SecCertificateRef certificate
) {
4407 return SecCertificateCopySerialNumberData(certificate
, NULL
);
4411 CFDataRef
SecCertificateGetNormalizedIssuerContent(
4412 SecCertificateRef certificate
) {
4413 return certificate
->_normalizedIssuer
;
4416 CFDataRef
SecCertificateGetNormalizedSubjectContent(
4417 SecCertificateRef certificate
) {
4418 return certificate
->_normalizedSubject
;
4421 /* Verify that certificate was signed by issuerKey. */
4422 OSStatus
SecCertificateIsSignedBy(SecCertificateRef certificate
,
4423 SecKeyRef issuerKey
) {
4424 #pragma clang diagnostic push
4425 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
4426 /* Setup algId in SecAsn1AlgId format. */
4428 #pragma clang diagnostic pop
4429 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
4430 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
4431 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
4432 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
4434 /* RFC5280 4.1.1.2, 4.1.2.3 requires the actual signature algorithm
4435 must match the specified algorithm in the TBSCertificate. */
4436 bool sigAlgMatch
= DEROidCompare(&certificate
->_sigAlg
.oid
,
4437 &certificate
->_tbsSigAlg
.oid
);
4439 secwarning("Signature algorithm mismatch in certificate (see RFC5280 4.1.1.2)");
4442 CFErrorRef error
= NULL
;
4444 !SecVerifySignatureWithPublicKey(issuerKey
, &algId
,
4445 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
4446 certificate
->_signature
.data
, certificate
->_signature
.length
, &error
))
4448 #if !defined(NDEBUG)
4449 secdebug("verify", "signature verify failed: %" PRIdOSStatus
, (error
) ? (OSStatus
)CFErrorGetCode(error
) : errSecNotSigner
);
4451 CFReleaseSafe(error
);
4452 return errSecNotSigner
;
4455 return errSecSuccess
;
4458 const DERItem
* SecCertificateGetSubjectAltName(SecCertificateRef certificate
) {
4459 if (!certificate
->_subjectAltName
) {
4462 return &certificate
->_subjectAltName
->extnValue
;
4465 /* Convert IPv4 address string to canonical data format (4 bytes) */
4466 static bool convertIPv4Address(CFStringRef name
, CFDataRef
*dataIP
) {
4467 /* IPv4: 4 octets in decimal separated by dots. */
4468 bool result
= false;
4470 if (CFStringGetLength(name
) < 7 || /* min size is #.#.#.# */
4471 CFStringGetLength(name
) > 15) { /* max size is ###.###.###.### */
4475 CFCharacterSetRef allowed
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789."));
4476 CFCharacterSetRef disallowed
= CFCharacterSetCreateInvertedSet(NULL
, allowed
);
4477 CFMutableDataRef data
= CFDataCreateMutable(NULL
, 0);
4478 CFArrayRef parts
= CFStringCreateArrayBySeparatingStrings(NULL
, name
, CFSTR("."));
4479 CFIndex i
, count
= (parts
) ? CFArrayGetCount(parts
) : 0;
4481 /* Check character set */
4482 if (CFStringFindCharacterFromSet(name
, disallowed
,
4483 CFRangeMake(0, CFStringGetLength(name
)),
4484 kCFCompareForcedOrdering
, NULL
)) {
4488 /* Check number of labels */
4489 if (CFArrayGetCount(parts
) != 4) {
4493 /* Check each label and convert */
4494 for (i
= 0; i
< count
; i
++) {
4495 CFStringRef octet
= (CFStringRef
) CFArrayGetValueAtIndex(parts
, i
);
4496 char *cString
= CFStringToCString(octet
);
4497 uint32_t value
= atoi(cString
);
4502 uint8_t byte
= value
;
4503 CFDataAppendBytes(data
, &byte
, 1);
4508 *dataIP
= (CFDataRef
) CFRetain(data
);
4512 CFReleaseNull(data
);
4513 CFReleaseNull(parts
);
4514 CFReleaseNull(allowed
);
4515 CFReleaseNull(disallowed
);
4519 /* Convert IPv6 address string to canonical data format (16 bytes) */
4520 static bool convertIPv6Address(CFStringRef name
, CFDataRef
*dataIP
) {
4521 /* IPv6: 8 16-bit fields with colon delimiters. */
4522 /* Note: we don't support conversion of hybrid IPv4-mapped addresses here. */
4523 bool result
= false;
4524 CFMutableStringRef addr
= NULL
;
4525 CFIndex length
= (name
) ? CFStringGetLength(name
) : 0;
4526 /* Sanity check size */
4527 if (length
< 2 || /* min size is '::' */
4528 length
> 41) { /* max size is '[####:####:####:####:####:####:####:####]' */
4531 /* Remove literal brackets, if present */
4532 if (CFStringHasPrefix(name
, CFSTR("[")) && CFStringHasSuffix(name
, CFSTR("]"))) {
4533 CFStringRef tmpName
= CFStringCreateWithSubstring(NULL
, name
, CFRangeMake(1, length
-2));
4535 addr
= CFStringCreateMutableCopy(NULL
, 0, tmpName
);
4540 addr
= CFStringCreateMutableCopy(NULL
, 0, name
);
4542 CFStringUppercase(addr
, CFLocaleGetSystem());
4544 CFCharacterSetRef allowed
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789ABCDEF:"));
4545 CFCharacterSetRef disallowed
= CFCharacterSetCreateInvertedSet(NULL
, allowed
);
4546 CFMutableDataRef data
= CFDataCreateMutable(NULL
, 0);
4547 CFArrayRef parts
= CFStringCreateArrayBySeparatingStrings(NULL
, addr
, CFSTR(":"));
4548 CFIndex i
, count
= (parts
) ? CFArrayGetCount(parts
) : 0;
4550 /* Check character set */
4551 if (CFStringFindCharacterFromSet(addr
, disallowed
,
4552 CFRangeMake(0, CFStringGetLength(addr
)),
4553 kCFCompareForcedOrdering
, NULL
)) {
4557 /* Check number of fields (no fewer than 3, no more than 8) */
4558 if (CFArrayGetCount(parts
) < 3 || CFArrayGetCount(parts
) > 8) {
4562 /* Check each field and convert to network-byte-order value */
4563 for (i
= 0; i
< count
; i
++) {
4564 uint16_t svalue
= 0;
4565 CFStringRef fieldValue
= (CFStringRef
) CFArrayGetValueAtIndex(parts
, i
);
4566 char *cString
= CFStringToCString(fieldValue
);
4567 length
= (cString
) ? strlen(cString
) : 0;
4569 /* empty value indicates one or more zeros in the address */
4570 if (i
== 0 || i
== count
-1) { /* leading or trailing part of '::' */
4571 CFDataAppendBytes(data
, (const UInt8
*)&svalue
, 2);
4572 } else { /* determine how many fields are missing, then zero-fill */
4573 CFIndex z
, missing
= (8 - count
) + 1;
4574 for (z
= 0; z
< missing
; z
++) {
4575 CFDataAppendBytes(data
, (const UInt8
*)&svalue
, 2);
4578 } else if (length
<= 4) {
4579 /* valid field value is 4 characters or less */
4580 unsigned long value
= strtoul(cString
, NULL
, 16);
4581 svalue
= htons(value
& 0xFFFF);
4582 CFDataAppendBytes(data
, (const UInt8
*)&svalue
, 2);
4586 if (CFDataGetLength(data
) != IPv6ADDRLEN
) {
4587 goto out
; /* after expansion, data must be exactly 16 bytes */
4592 *dataIP
= (CFDataRef
) CFRetain(data
);
4596 CFReleaseNull(data
);
4597 CFReleaseNull(parts
);
4598 CFReleaseNull(allowed
);
4599 CFReleaseNull(disallowed
);
4600 CFReleaseNull(addr
);
4604 static bool convertIPAddress(CFStringRef string
, CFDataRef
*dataIP
) {
4605 if (NULL
== string
) {
4608 if (convertIPv4Address(string
, dataIP
) ||
4609 convertIPv6Address(string
, dataIP
)) {
4615 bool SecFrameworkIsIPAddress(CFStringRef string
) {
4616 return convertIPAddress(string
, NULL
);
4619 CFDataRef
SecFrameworkCopyIPAddressData(CFStringRef string
) {
4620 CFDataRef data
= NULL
;
4621 if (!convertIPAddress(string
, &data
)) {
4627 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
4628 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4629 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
4630 if (gnType
== GNT_IPAddress
) {
4631 CFStringRef string
= copyIPAddressContentDescription(
4632 kCFAllocatorDefault
, generalName
);
4634 CFArrayAppendValue(ipAddresses
, string
);
4637 return errSecInvalidCertificate
;
4640 return errSecSuccess
;
4643 CFArrayRef
SecCertificateCopyIPAddresses(SecCertificateRef certificate
) {
4644 /* These can only exist in the subject alt name. */
4645 if (!certificate
->_subjectAltName
)
4648 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
4649 0, &kCFTypeArrayCallBacks
);
4650 OSStatus status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4651 ipAddresses
, appendIPAddressesFromGeneralNames
);
4652 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
4653 CFRelease(ipAddresses
);
4659 static OSStatus
appendIPAddressesFromX501Name(void *context
, const DERItem
*type
,
4660 const DERItem
*value
, CFIndex rdnIX
,
4662 CFMutableArrayRef addrs
= (CFMutableArrayRef
)context
;
4663 if (DEROidCompare(type
, &oidCommonName
)) {
4664 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4665 value
, true, localized
);
4667 CFDataRef data
= NULL
;
4668 if (convertIPAddress(string
, &data
)) {
4669 CFArrayAppendValue(addrs
, data
);
4670 CFReleaseNull(data
);
4674 return errSecInvalidCertificate
;
4677 return errSecSuccess
;
4680 CFArrayRef
SecCertificateCopyIPAddressesFromSubject(SecCertificateRef certificate
) {
4681 CFMutableArrayRef addrs
= CFArrayCreateMutable(kCFAllocatorDefault
,
4682 0, &kCFTypeArrayCallBacks
);
4683 OSStatus status
= parseX501NameContent(&certificate
->_subject
, addrs
,
4684 appendIPAddressesFromX501Name
, true);
4685 if (status
|| CFArrayGetCount(addrs
) == 0) {
4686 CFReleaseNull(addrs
);
4692 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
4693 const DERItem
*generalName
) {
4694 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4695 if (gnType
== GNT_DNSName
) {
4696 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4697 generalName
->data
, generalName
->length
,
4698 kCFStringEncodingUTF8
, FALSE
);
4700 CFArrayAppendValue(dnsNames
, string
);
4703 return errSecInvalidCertificate
;
4706 return errSecSuccess
;
4709 /* Return true if the passed in string matches the
4710 Preferred name syntax from sections 2.3.1. in RFC 1035.
4711 With the added check that we disallow empty dns names.
4712 Also in order to support wildcard DNSNames we allow for the '*'
4713 character anywhere in a dns component where we currently allow
4716 <domain> ::= <subdomain> | " "
4718 <subdomain> ::= <label> | <subdomain> "." <label>
4720 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
4722 RFC 3696 redefined labels as:
4723 <label> ::= <let-dig> [ [ <ldh-str> ] <let-dig> ]
4724 with the caveat that the highest-level labels is never all-numeric.
4726 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
4728 <let-dig-hyp> ::= <let-dig> | "-"
4730 <let-dig> ::= <letter> | <digit>
4732 <letter> ::= any one of the 52 alphabetic characters A through Z in
4733 upper case and a through z in lower case
4735 <digit> ::= any one of the ten digits 0 through 9
4737 bool SecFrameworkIsDNSName(CFStringRef string
) {
4738 CFStringInlineBuffer buf
= {};
4739 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
4740 /* From RFC 1035 2.3.4. Size limits:
4741 labels 63 octets or less
4742 names 255 octets or less */
4743 require_quiet(length
<= 255, notDNS
);
4744 CFRange range
= { 0, length
};
4745 CFStringInitInlineBuffer(string
, &buf
, range
);
4749 kDNSStateAfterAlpha
,
4750 kDNSStateAfterDigit
,
4752 } state
= kDNSStateInital
;
4753 Boolean labelHasAlpha
= false;
4755 for (ix
= 0; ix
< length
; ++ix
) {
4756 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
4759 require_quiet(labelLength
<= 64 &&
4760 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4762 state
= kDNSStateAfterDot
;
4763 labelHasAlpha
= false;
4765 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
4767 state
= kDNSStateAfterAlpha
;
4768 labelHasAlpha
= true;
4769 } else if ('0' <= ch
&& ch
<= '9') {
4770 state
= kDNSStateAfterDigit
;
4771 } else if (ch
== '-') {
4772 require_quiet(state
== kDNSStateAfterAlpha
||
4773 state
== kDNSStateAfterDigit
||
4774 state
== kDNSStateAfterDash
, notDNS
);
4775 state
= kDNSStateAfterDash
;
4781 /* We don't allow a dns name to end in a dot or dash. */
4782 require_quiet(labelLength
<= 63 &&
4783 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4786 /* Additionally, the rightmost label must have letters in it. */
4787 require_quiet(labelHasAlpha
== true, notDNS
);
4794 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4795 const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4796 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4797 if (DEROidCompare(type
, &oidCommonName
)) {
4798 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4799 value
, true, localized
);
4801 if (SecFrameworkIsDNSName(string
)) {
4802 /* We found a common name that is formatted like a valid
4804 CFArrayAppendValue(dnsNames
, string
);
4808 return errSecInvalidCertificate
;
4811 return errSecSuccess
;
4814 CFArrayRef
SecCertificateCopyDNSNamesFromSubject(SecCertificateRef certificate
) {
4815 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4816 0, &kCFTypeArrayCallBacks
);
4817 OSStatus status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4818 appendDNSNamesFromX501Name
, true);
4819 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4820 CFReleaseNull(dnsNames
);
4824 /* appendDNSNamesFromX501Name allows IP addresses, we don't want those for this function */
4825 __block CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4826 CFArrayForEach(dnsNames
, ^(const void *value
) {
4827 CFStringRef name
= (CFStringRef
)value
;
4828 if (!convertIPAddress(name
, NULL
)) {
4829 CFArrayAppendValue(result
, name
);
4832 CFReleaseNull(dnsNames
);
4833 if (CFArrayGetCount(result
) == 0) {
4834 CFReleaseNull(result
);
4840 CFArrayRef
SecCertificateCopyDNSNamesFromSAN(SecCertificateRef certificate
) {
4841 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4842 0, &kCFTypeArrayCallBacks
);
4843 OSStatus status
= errSecSuccess
;
4844 if (certificate
->_subjectAltName
) {
4845 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4846 dnsNames
, appendDNSNamesFromGeneralNames
);
4849 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4850 CFReleaseNull(dnsNames
);
4855 /* Not everything returned by this function is going to be a proper DNS name,
4856 we also return the certificates common name entries from the subject,
4857 assuming they look like dns names as specified in RFC 1035. */
4858 CFArrayRef
SecCertificateCopyDNSNames(SecCertificateRef certificate
) {
4859 /* These can exist in the subject alt name or in the subject. */
4860 CFArrayRef sanNames
= SecCertificateCopyDNSNamesFromSAN(certificate
);
4861 if (sanNames
&& CFArrayGetCount(sanNames
) > 0) {
4864 CFReleaseNull(sanNames
);
4866 /* RFC 2818 section 3.1. Server Identity
4868 If a subjectAltName extension of type dNSName is present, that MUST
4869 be used as the identity. Otherwise, the (most specific) Common Name
4870 field in the Subject field of the certificate MUST be used. Although
4871 the use of the Common Name is existing practice, it is deprecated and
4872 Certification Authorities are encouraged to use the dNSName instead.
4875 This implies that if we found 1 or more DNSNames in the
4876 subjectAltName, we should not use the Common Name of the subject as
4880 /* To preserve bug for bug compatibility, we can't use SecCertificateCopyDNSNamesFromSubject
4881 * because that function filters out IP Addresses. This function is Private, but
4882 * SecCertificateCopyValues uses it and that's Public. */
4883 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4884 0, &kCFTypeArrayCallBacks
);
4885 OSStatus status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4886 appendDNSNamesFromX501Name
, true);
4887 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4888 CFReleaseNull(dnsNames
);
4893 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4894 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4895 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4896 if (gnType
== GNT_RFC822Name
) {
4897 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4898 generalName
->data
, generalName
->length
,
4899 kCFStringEncodingASCII
, FALSE
);
4901 CFArrayAppendValue(dnsNames
, string
);
4904 return errSecInvalidCertificate
;
4907 return errSecSuccess
;
4910 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4911 const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4912 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4913 if (DEROidCompare(type
, &oidEmailAddress
)) {
4914 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4915 value
, true, localized
);
4917 CFArrayAppendValue(dnsNames
, string
);
4920 return errSecInvalidCertificate
;
4923 return errSecSuccess
;
4926 CFArrayRef
SecCertificateCopyRFC822Names(SecCertificateRef certificate
) {
4927 /* These can exist in the subject alt name or in the subject. */
4928 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4929 0, &kCFTypeArrayCallBacks
);
4930 OSStatus status
= errSecSuccess
;
4931 if (certificate
->_subjectAltName
) {
4932 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4933 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4936 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4937 appendRFC822NamesFromX501Name
, true);
4939 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4940 CFRelease(rfc822Names
);
4946 OSStatus
SecCertificateCopyEmailAddresses(SecCertificateRef certificate
, CFArrayRef
* __nonnull CF_RETURNS_RETAINED emailAddresses
) {
4947 if (!certificate
|| !emailAddresses
) {
4950 *emailAddresses
= SecCertificateCopyRFC822Names(certificate
);
4951 if (*emailAddresses
== NULL
) {
4952 *emailAddresses
= CFArrayCreate(NULL
, NULL
, 0, &kCFTypeArrayCallBacks
);
4954 return errSecSuccess
;
4957 CFArrayRef
SecCertificateCopyRFC822NamesFromSubject(SecCertificateRef certificate
) {
4958 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4959 0, &kCFTypeArrayCallBacks
);
4960 OSStatus status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4961 appendRFC822NamesFromX501Name
, true);
4962 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4963 CFRelease(rfc822Names
);
4969 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4970 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4971 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4972 if (DEROidCompare(type
, &oidCommonName
)) {
4973 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4974 value
, true, localized
);
4976 CFArrayAppendValue(commonNames
, string
);
4979 return errSecInvalidCertificate
;
4982 return errSecSuccess
;
4985 CFArrayRef
SecCertificateCopyCommonNames(SecCertificateRef certificate
) {
4986 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4987 0, &kCFTypeArrayCallBacks
);
4989 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4990 appendCommonNamesFromX501Name
, true);
4991 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4992 CFRelease(commonNames
);
4998 OSStatus
SecCertificateCopyCommonName(SecCertificateRef certificate
, CFStringRef
*commonName
)
5003 CFArrayRef commonNames
= SecCertificateCopyCommonNames(certificate
);
5005 return errSecInternal
;
5009 CFIndex count
= CFArrayGetCount(commonNames
);
5010 *commonName
= CFRetainSafe(CFArrayGetValueAtIndex(commonNames
, count
-1));
5012 CFReleaseSafe(commonNames
);
5013 return errSecSuccess
;
5016 static OSStatus
appendOrganizationFromX501Name(void *context
,
5017 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5018 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
5019 if (DEROidCompare(type
, &oidOrganizationName
)) {
5020 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
5021 value
, true, localized
);
5023 CFArrayAppendValue(organization
, string
);
5026 return errSecInvalidCertificate
;
5029 return errSecSuccess
;
5032 CFArrayRef
SecCertificateCopyOrganizationFromX501NameContent(const DERItem
*nameContent
) {
5033 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
5034 0, &kCFTypeArrayCallBacks
);
5036 status
= parseX501NameContent(nameContent
, organization
,
5037 appendOrganizationFromX501Name
, true);
5038 if (status
|| CFArrayGetCount(organization
) == 0) {
5039 CFRelease(organization
);
5040 organization
= NULL
;
5042 return organization
;
5045 CFArrayRef
SecCertificateCopyOrganization(SecCertificateRef certificate
) {
5046 return SecCertificateCopyOrganizationFromX501NameContent(&certificate
->_subject
);
5049 static OSStatus
appendOrganizationalUnitFromX501Name(void *context
,
5050 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5051 CFMutableArrayRef organizationalUnit
= (CFMutableArrayRef
)context
;
5052 if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
5053 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
5054 value
, true, localized
);
5056 CFArrayAppendValue(organizationalUnit
, string
);
5059 return errSecInvalidCertificate
;
5062 return errSecSuccess
;
5065 CFArrayRef
SecCertificateCopyOrganizationalUnit(SecCertificateRef certificate
) {
5066 CFMutableArrayRef organizationalUnit
= CFArrayCreateMutable(kCFAllocatorDefault
,
5067 0, &kCFTypeArrayCallBacks
);
5069 status
= parseX501NameContent(&certificate
->_subject
, organizationalUnit
,
5070 appendOrganizationalUnitFromX501Name
, true);
5071 if (status
|| CFArrayGetCount(organizationalUnit
) == 0) {
5072 CFRelease(organizationalUnit
);
5073 organizationalUnit
= NULL
;
5075 return organizationalUnit
;
5078 static OSStatus
appendCountryFromX501Name(void *context
,
5079 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5080 CFMutableArrayRef countries
= (CFMutableArrayRef
)context
;
5081 if (DEROidCompare(type
, &oidCountryName
)) {
5082 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
5083 value
, true, localized
);
5085 CFArrayAppendValue(countries
, string
);
5088 return errSecInvalidCertificate
;
5091 return errSecSuccess
;
5094 CFArrayRef
SecCertificateCopyCountry(SecCertificateRef certificate
) {
5095 CFMutableArrayRef countries
= CFArrayCreateMutable(kCFAllocatorDefault
,
5096 0, &kCFTypeArrayCallBacks
);
5098 status
= parseX501NameContent(&certificate
->_subject
, countries
,
5099 appendCountryFromX501Name
, true);
5100 if (status
|| CFArrayGetCount(countries
) == 0) {
5101 CFRelease(countries
);
5107 const SecCEBasicConstraints
*
5108 SecCertificateGetBasicConstraints(SecCertificateRef certificate
) {
5109 if (certificate
->_basicConstraints
.present
)
5110 return &certificate
->_basicConstraints
;
5115 CFArrayRef
SecCertificateGetPermittedSubtrees(SecCertificateRef certificate
) {
5116 return (certificate
->_permittedSubtrees
);
5119 CFArrayRef
SecCertificateGetExcludedSubtrees(SecCertificateRef certificate
) {
5120 return (certificate
->_excludedSubtrees
);
5123 const SecCEPolicyConstraints
*
5124 SecCertificateGetPolicyConstraints(SecCertificateRef certificate
) {
5125 if (certificate
->_policyConstraints
.present
)
5126 return &certificate
->_policyConstraints
;
5131 const SecCEPolicyMappings
*
5132 SecCertificateGetPolicyMappings(SecCertificateRef certificate
) {
5133 if (certificate
->_policyMappings
.present
) {
5134 return &certificate
->_policyMappings
;
5140 const SecCECertificatePolicies
*
5141 SecCertificateGetCertificatePolicies(SecCertificateRef certificate
) {
5142 if (certificate
->_certificatePolicies
.present
)
5143 return &certificate
->_certificatePolicies
;
5148 const SecCEInhibitAnyPolicy
*
5149 SecCertificateGetInhibitAnyPolicySkipCerts(SecCertificateRef certificate
) {
5150 if (certificate
->_inhibitAnyPolicySkipCerts
.present
) {
5151 return &certificate
->_inhibitAnyPolicySkipCerts
;
5157 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
5158 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
5159 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
5160 if (gnType
== GNT_OtherName
) {
5162 DERReturn drtn
= DERParseSequenceContent(generalName
,
5163 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
5165 require_noerr_quiet(drtn
, badDER
);
5166 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
5168 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
5169 &on
.value
, true, true), badDER
);
5170 CFArrayAppendValue(ntPrincipalNames
, string
);
5174 return errSecSuccess
;
5177 return errSecInvalidCertificate
;
5181 CFArrayRef
SecCertificateCopyNTPrincipalNames(SecCertificateRef certificate
) {
5182 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
5183 0, &kCFTypeArrayCallBacks
);
5184 OSStatus status
= errSecSuccess
;
5185 if (certificate
->_subjectAltName
) {
5186 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
5187 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
5189 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
5190 CFRelease(ntPrincipalNames
);
5191 ntPrincipalNames
= NULL
;
5193 return ntPrincipalNames
;
5196 static OSStatus
appendToRFC2253String(void *context
,
5197 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5198 CFMutableStringRef string
= (CFMutableStringRef
)context
;
5202 ST stateOrProvinceName
5204 OU organizationalUnitName
5206 STREET streetAddress
5210 /* Prepend a + if this is not the first RDN in an RDN set.
5211 Otherwise prepend a , if this is not the first RDN. */
5213 CFStringAppend(string
, CFSTR("+"));
5214 else if (CFStringGetLength(string
)) {
5215 CFStringAppend(string
, CFSTR(","));
5218 CFStringRef label
, oid
= NULL
;
5219 /* @@@ Consider changing this to a dictionary lookup keyed by the
5220 decimal representation. */
5221 if (DEROidCompare(type
, &oidCommonName
)) {
5222 label
= CFSTR("CN");
5223 } else if (DEROidCompare(type
, &oidLocalityName
)) {
5225 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
5226 label
= CFSTR("ST");
5227 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
5229 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
5230 label
= CFSTR("OU");
5231 } else if (DEROidCompare(type
, &oidCountryName
)) {
5234 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
5235 label
= CFSTR("STREET");
5236 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
5237 label
= CFSTR("DC");
5238 } else if (DEROidCompare(type
, &oidUserID
)) {
5239 label
= CFSTR("UID");
5242 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
5245 CFStringAppend(string
, label
);
5246 CFStringAppend(string
, CFSTR("="));
5247 CFStringRef raw
= NULL
;
5249 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true, localized
);
5252 /* Append raw to string while escaping:
5253 a space or "#" character occurring at the beginning of the string
5254 a space character occurring at the end of the string
5255 one of the characters ",", "+", """, "\", "<", ">" or ";"
5257 CFStringInlineBuffer buffer
= {};
5258 CFIndex ix
, length
= CFStringGetLength(raw
);
5259 CFRange range
= { 0, length
};
5260 CFStringInitInlineBuffer(raw
, &buffer
, range
);
5261 for (ix
= 0; ix
< length
; ++ix
) {
5262 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
5264 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
5265 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
5266 ch
== '<' || ch
== '>' || ch
== ';' ||
5267 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
5268 (ch
== '#' && ix
== 0)) {
5269 UniChar chars
[] = { '\\', ch
};
5270 CFStringAppendCharacters(string
, chars
, 2);
5272 CFStringAppendCharacters(string
, &ch
, 1);
5277 /* Append the value in hex. */
5278 CFStringAppend(string
, CFSTR("#"));
5280 for (ix
= 0; ix
< value
->length
; ++ix
)
5281 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
5286 return errSecSuccess
;
5289 CFStringRef
SecCertificateCopySubjectString(SecCertificateRef certificate
) {
5290 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
5291 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
, true);
5292 if (status
|| CFStringGetLength(string
) == 0) {
5299 static OSStatus
appendToCompanyNameString(void *context
,
5300 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5301 CFMutableStringRef string
= (CFMutableStringRef
)context
;
5302 if (CFStringGetLength(string
) != 0)
5303 return errSecSuccess
;
5305 if (!DEROidCompare(type
, &oidOrganizationName
))
5306 return errSecSuccess
;
5309 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true, localized
);
5311 return errSecSuccess
;
5312 CFStringAppend(string
, raw
);
5315 return errSecSuccess
;
5318 CFStringRef
SecCertificateCopyCompanyName(SecCertificateRef certificate
) {
5319 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
5320 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
5321 appendToCompanyNameString
, true);
5322 if (status
|| CFStringGetLength(string
) == 0) {
5329 CFDataRef
SecCertificateCopyIssuerSequence(
5330 SecCertificateRef certificate
) {
5331 return SecDERItemCopySequence(&certificate
->_issuer
);
5334 CFDataRef
SecCertificateCopySubjectSequence(
5335 SecCertificateRef certificate
) {
5336 return SecDERItemCopySequence(&certificate
->_subject
);
5339 CFDataRef
SecCertificateCopyNormalizedIssuerSequence(SecCertificateRef certificate
) {
5340 if (!certificate
|| !certificate
->_normalizedIssuer
) {
5343 return SecCopySequenceFromContent(certificate
->_normalizedIssuer
);
5346 CFDataRef
SecCertificateCopyNormalizedSubjectSequence(SecCertificateRef certificate
) {
5347 if (!certificate
|| !certificate
->_normalizedSubject
) {
5350 return SecCopySequenceFromContent(certificate
->_normalizedSubject
);
5353 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithm(
5354 SecCertificateRef certificate
) {
5355 return &certificate
->_algId
;
5358 const DERItem
*SecCertificateGetPublicKeyData(SecCertificateRef certificate
) {
5359 return &certificate
->_pubKeyDER
;
5363 /* There is already a SecCertificateCopyPublicKey with different args on OS X,
5364 so we will refer to this one internally as SecCertificateCopyPublicKey_ios.
5366 __nullable SecKeyRef
SecCertificateCopyPublicKey_ios(SecCertificateRef certificate
)
5368 __nullable SecKeyRef
SecCertificateCopyPublicKey(SecCertificateRef certificate
)
5371 return SecCertificateCopyKey(certificate
);
5374 SecKeyRef
SecCertificateCopyKey(SecCertificateRef certificate
) {
5375 if (certificate
->_pubKey
== NULL
) {
5376 const DERAlgorithmId
*algId
=
5377 SecCertificateGetPublicKeyAlgorithm(certificate
);
5378 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
5379 const DERItem
*params
= NULL
;
5380 if (algId
->params
.length
!= 0) {
5381 params
= &algId
->params
;
5383 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
5384 SecAsn1Item params1
= {
5385 .Data
= params
? params
->data
: NULL
,
5386 .Length
= params
? params
->length
: 0
5388 SecAsn1Item keyData1
= {
5389 .Data
= keyData
? keyData
->data
: NULL
,
5390 .Length
= keyData
? keyData
->length
: 0
5392 certificate
->_pubKey
= SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
5396 return CFRetainSafe(certificate
->_pubKey
);
5399 static CFIndex
SecCertificateGetPublicKeyAlgorithmIdAndSize(SecCertificateRef certificate
, size_t *keySizeInBytes
) {
5400 CFIndex keyAlgID
= kSecNullAlgorithmID
;
5403 SecKeyRef pubKey
= NULL
;
5404 require_quiet(certificate
, out
);
5405 require_quiet(pubKey
= SecCertificateCopyKey(certificate
) ,out
);
5406 size
= SecKeyGetBlockSize(pubKey
);
5407 keyAlgID
= SecKeyGetAlgorithmId(pubKey
);
5410 CFReleaseNull(pubKey
);
5411 if (keySizeInBytes
) { *keySizeInBytes
= size
; }
5416 * Public keys in certificates may be considered "weak" or "strong" or neither
5417 * (that is, in between). Certificates using weak keys are not trusted at all.
5418 * Certificates using neither strong nor weak keys are only trusted in certain
5419 * contexts. SecPolicy and SecPolicyServer define the contexts by which we enforce
5420 * these (or stronger) key size trust policies.
5422 bool SecCertificateIsWeakKey(SecCertificateRef certificate
) {
5423 if (!certificate
) { return true; }
5427 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5428 case kSecRSAAlgorithmID
:
5429 if (MIN_RSA_KEY_SIZE
<= size
) weak
= false;
5431 case kSecECDSAAlgorithmID
:
5432 if (MIN_EC_KEY_SIZE
<= size
) weak
= false;
5440 bool SecCertificateIsStrongKey(SecCertificateRef certificate
) {
5441 if (!certificate
) { return false; }
5443 bool strong
= false;
5445 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5446 case kSecRSAAlgorithmID
:
5447 if (MIN_STRONG_RSA_KEY_SIZE
<= size
) strong
= true;
5449 case kSecECDSAAlgorithmID
:
5450 if (MIN_STRONG_EC_KEY_SIZE
<= size
) strong
= true;
5458 bool SecCertificateIsWeakHash(SecCertificateRef certificate
) {
5459 if (!certificate
) { return true; }
5460 SecSignatureHashAlgorithm certAlg
= 0;
5461 certAlg
= SecCertificateGetSignatureHashAlgorithm(certificate
);
5462 if (certAlg
== kSecSignatureHashAlgorithmUnknown
||
5463 certAlg
== kSecSignatureHashAlgorithmMD2
||
5464 certAlg
== kSecSignatureHashAlgorithmMD4
||
5465 certAlg
== kSecSignatureHashAlgorithmMD5
||
5466 certAlg
== kSecSignatureHashAlgorithmSHA1
) {
5472 bool SecCertificateIsAtLeastMinKeySize(SecCertificateRef certificate
,
5473 CFDictionaryRef keySizes
) {
5474 if (!certificate
) { return false; }
5476 bool goodSize
= false;
5478 CFNumberRef minSize
;
5479 size_t minSizeInBits
;
5480 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5481 case kSecRSAAlgorithmID
:
5482 if(CFDictionaryGetValueIfPresent(keySizes
, kSecAttrKeyTypeRSA
, (const void**)&minSize
)
5483 && minSize
&& CFNumberGetValue(minSize
, kCFNumberLongType
, &minSizeInBits
)) {
5484 if (size
>= (size_t)(minSizeInBits
+7)/8) goodSize
= true;
5487 case kSecECDSAAlgorithmID
:
5488 if(CFDictionaryGetValueIfPresent(keySizes
, kSecAttrKeyTypeEC
, (const void**)&minSize
)
5489 && minSize
&& CFNumberGetValue(minSize
, kCFNumberLongType
, &minSizeInBits
)) {
5490 if (size
>= (size_t)(minSizeInBits
+7)/8) goodSize
= true;
5499 CFDataRef
SecCertificateGetSHA1Digest(SecCertificateRef certificate
) {
5500 if (!certificate
|| !certificate
->_der
.data
) {
5503 if (!certificate
->_sha1Digest
) {
5504 certificate
->_sha1Digest
=
5505 SecSHA1DigestCreate(CFGetAllocator(certificate
),
5506 certificate
->_der
.data
, certificate
->_der
.length
);
5508 return certificate
->_sha1Digest
;
5511 CFDataRef
SecCertificateCopySHA256Digest(SecCertificateRef certificate
) {
5512 if (!certificate
|| !certificate
->_der
.data
) {
5515 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
5516 certificate
->_der
.data
, certificate
->_der
.length
);
5519 CFDataRef
SecCertificateCopyIssuerSHA1Digest(SecCertificateRef certificate
) {
5520 CFDataRef digest
= NULL
;
5521 CFDataRef issuer
= SecCertificateCopyIssuerSequence(certificate
);
5523 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
5524 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
5530 CFDataRef
SecCertificateCopyPublicKeySHA1Digest(SecCertificateRef certificate
) {
5531 if (!certificate
|| !certificate
->_pubKeyDER
.data
) {
5534 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
5535 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
5538 static CFDataRef
SecCertificateCopySPKIEncoded(SecCertificateRef certificate
) {
5539 /* SPKI is saved without the tag/length by libDER, so we need to re-encode */
5540 if (!certificate
|| !certificate
->_subjectPublicKeyInfo
.data
) {
5543 DERSize size
= DERLengthOfItem(ASN1_CONSTR_SEQUENCE
, certificate
->_subjectPublicKeyInfo
.length
);
5544 if (size
< certificate
->_subjectPublicKeyInfo
.length
) {
5547 uint8_t *temp
= malloc(size
);
5551 DERReturn drtn
= DEREncodeItem(ASN1_CONSTR_SEQUENCE
,
5552 certificate
->_subjectPublicKeyInfo
.length
,
5553 certificate
->_subjectPublicKeyInfo
.data
,
5555 CFDataRef encodedSPKI
= NULL
;
5556 if (drtn
== DR_Success
) {
5557 encodedSPKI
= CFDataCreate(NULL
, temp
, size
);
5563 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA1Digest(SecCertificateRef certificate
) {
5564 CFDataRef encodedSPKI
= SecCertificateCopySPKIEncoded(certificate
);
5565 if (!encodedSPKI
) { return NULL
; }
5566 CFDataRef hash
= SecSHA1DigestCreate(CFGetAllocator(certificate
),
5567 CFDataGetBytePtr(encodedSPKI
),
5568 CFDataGetLength(encodedSPKI
));
5569 CFReleaseNull(encodedSPKI
);
5573 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA256Digest(SecCertificateRef certificate
) {
5574 CFDataRef encodedSPKI
= SecCertificateCopySPKIEncoded(certificate
);
5575 if (!encodedSPKI
) { return NULL
; }
5576 CFDataRef hash
= SecSHA256DigestCreate(CFGetAllocator(certificate
),
5577 CFDataGetBytePtr(encodedSPKI
),
5578 CFDataGetLength(encodedSPKI
));
5579 CFReleaseNull(encodedSPKI
);
5583 CFTypeRef
SecCertificateCopyKeychainItem(SecCertificateRef certificate
)
5588 CFRetainSafe(certificate
->_keychain_item
);
5589 return certificate
->_keychain_item
;
5592 CFDataRef
SecCertificateGetAuthorityKeyID(SecCertificateRef certificate
) {
5596 if (!certificate
->_authorityKeyID
&&
5597 certificate
->_authorityKeyIdentifier
.length
) {
5598 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
5599 certificate
->_authorityKeyIdentifier
.data
,
5600 certificate
->_authorityKeyIdentifier
.length
);
5603 return certificate
->_authorityKeyID
;
5606 CFDataRef
SecCertificateGetSubjectKeyID(SecCertificateRef certificate
) {
5610 if (!certificate
->_subjectKeyID
&&
5611 certificate
->_subjectKeyIdentifier
.length
) {
5612 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
5613 certificate
->_subjectKeyIdentifier
.data
,
5614 certificate
->_subjectKeyIdentifier
.length
);
5617 return certificate
->_subjectKeyID
;
5620 CFArrayRef
SecCertificateGetCRLDistributionPoints(SecCertificateRef certificate
) {
5624 return certificate
->_crlDistributionPoints
;
5627 CFArrayRef
SecCertificateGetOCSPResponders(SecCertificateRef certificate
) {
5631 return certificate
->_ocspResponders
;
5634 CFArrayRef
SecCertificateGetCAIssuers(SecCertificateRef certificate
) {
5638 return certificate
->_caIssuers
;
5641 bool SecCertificateHasCriticalSubjectAltName(SecCertificateRef certificate
) {
5645 return certificate
->_subjectAltName
&&
5646 certificate
->_subjectAltName
->critical
;
5649 bool SecCertificateHasSubject(SecCertificateRef certificate
) {
5653 /* Since the _subject field is the content of the subject and not the
5654 whole thing, we can simply check for a 0 length subject here. */
5655 return certificate
->_subject
.length
!= 0;
5658 bool SecCertificateHasUnknownCriticalExtension(SecCertificateRef certificate
) {
5662 return certificate
->_foundUnknownCriticalExtension
;
5665 /* Private API functions. */
5666 void SecCertificateShow(SecCertificateRef certificate
) {
5668 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
5669 fprintf(stderr
, "\n");
5673 CFDictionaryRef
SecCertificateCopyAttributeDictionary(
5674 SecCertificateRef certificate
) {
5675 if (!SecCertificateIsCertificate(certificate
)) {
5678 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
5679 CFNumberRef certificateType
= NULL
;
5680 CFNumberRef certificateEncoding
= NULL
;
5681 CFStringRef label
= NULL
;
5682 CFStringRef alias
= NULL
;
5683 CFDataRef skid
= NULL
;
5684 CFDataRef pubKeyDigest
= NULL
;
5685 CFDataRef certData
= NULL
;
5686 CFDictionaryRef dict
= NULL
;
5690 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
5691 SInt32 ctv
= certificate
->_version
+ 1;
5692 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
5693 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
5694 require_quiet(certificateType
!= NULL
, out
);
5695 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
5696 require_quiet(certificateEncoding
!= NULL
, out
);
5697 certData
= SecCertificateCopyData(certificate
);
5698 require_quiet(certData
!= NULL
, out
);
5699 skid
= SecCertificateGetSubjectKeyID(certificate
);
5700 require_quiet(certificate
->_pubKeyDER
.data
!= NULL
&& certificate
->_pubKeyDER
.length
> 0, out
);
5701 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
5702 certificate
->_pubKeyDER
.length
);
5703 require_quiet(pubKeyDigest
!= NULL
, out
);
5705 /* We still need to figure out how to deal with multi valued attributes. */
5706 alias
= SecCertificateCopyRFC822Names(certificate
);
5707 label
= SecCertificateCopySubjectSummary(certificate
);
5713 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
5714 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
5715 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
5717 DICT_ADDPAIR(kSecAttrLabel
, label
);
5720 DICT_ADDPAIR(kSecAttrAlias
, alias
);
5722 if (isData(certificate
->_normalizedSubject
)) {
5723 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
5725 require_quiet(isData(certificate
->_normalizedIssuer
), out
);
5726 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
5727 require_quiet(isData(certificate
->_serialNumber
), out
);
5728 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
5730 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
5732 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
5733 DICT_ADDPAIR(kSecValueData
, certData
);
5734 dict
= DICT_CREATE(allocator
);
5737 CFReleaseSafe(label
);
5738 CFReleaseSafe(alias
);
5739 CFReleaseSafe(pubKeyDigest
);
5740 CFReleaseSafe(certData
);
5741 CFReleaseSafe(certificateEncoding
);
5742 CFReleaseSafe(certificateType
);
5747 SecCertificateRef
SecCertificateCreateFromAttributeDictionary(
5748 CFDictionaryRef refAttributes
) {
5749 /* @@@ Support having an allocator in refAttributes. */
5750 CFAllocatorRef allocator
= NULL
;
5751 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
5752 return data
? SecCertificateCreateWithData(allocator
, data
) : NULL
;
5756 static bool _SecCertificateIsSelfSigned(SecCertificateRef certificate
) {
5757 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
5758 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
5759 SecKeyRef publicKey
= NULL
;
5760 require(SecCertificateIsCertificate(certificate
), out
);
5761 require(publicKey
= SecCertificateCopyKey(certificate
), out
);
5762 CFDataRef normalizedIssuer
=
5763 SecCertificateGetNormalizedIssuerContent(certificate
);
5764 CFDataRef normalizedSubject
=
5765 SecCertificateGetNormalizedSubjectContent(certificate
);
5766 require_quiet(normalizedIssuer
&& normalizedSubject
&&
5767 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
5769 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(certificate
);
5770 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyID(certificate
);
5771 if (authorityKeyID
) {
5772 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
5775 require_noerr_quiet(SecCertificateIsSignedBy(certificate
, publicKey
), out
);
5777 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
5779 CFReleaseSafe(publicKey
);
5782 return (certificate
->_isSelfSigned
== kSecSelfSignedTrue
);
5785 bool SecCertificateIsCA(SecCertificateRef certificate
) {
5786 bool result
= false;
5787 require(SecCertificateIsCertificate(certificate
), out
);
5788 if (SecCertificateVersion(certificate
) >= 3) {
5789 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraints(certificate
);
5790 result
= (basicConstraints
&& basicConstraints
->isCA
);
5793 result
= _SecCertificateIsSelfSigned(certificate
);
5799 bool SecCertificateIsSelfSignedCA(SecCertificateRef certificate
) {
5800 return (_SecCertificateIsSelfSigned(certificate
) && SecCertificateIsCA(certificate
));
5803 OSStatus
SecCertificateIsSelfSigned(SecCertificateRef certificate
, Boolean
*isSelfSigned
) {
5804 if (!SecCertificateIsCertificate(certificate
)) {
5805 return errSecInvalidCertificate
;
5807 if (!isSelfSigned
) {
5810 *isSelfSigned
= _SecCertificateIsSelfSigned(certificate
);
5811 return errSecSuccess
;
5814 SecKeyUsage
SecCertificateGetKeyUsage(SecCertificateRef certificate
) {
5816 return kSecKeyUsageUnspecified
;
5818 return certificate
->_keyUsage
;
5821 CFArrayRef
SecCertificateCopyExtendedKeyUsage(SecCertificateRef certificate
)
5823 CFMutableArrayRef extended_key_usage_oids
=
5824 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
5825 require_quiet(certificate
&& extended_key_usage_oids
, out
);
5827 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5828 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5829 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
5830 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
5833 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
5834 require_noerr_quiet(drtn
, out
);
5835 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
5836 DERDecodedInfo currDecoded
;
5838 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
5839 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
5840 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
5841 currDecoded
.content
.data
, currDecoded
.content
.length
);
5842 require_quiet(oid
, out
);
5843 CFArrayAppendValue(extended_key_usage_oids
, oid
);
5846 require_quiet(drtn
== DR_EndOfSequence
, out
);
5847 return extended_key_usage_oids
;
5851 CFReleaseSafe(extended_key_usage_oids
);
5855 CFArrayRef
SecCertificateCopySignedCertificateTimestamps(SecCertificateRef certificate
)
5857 require_quiet(certificate
, out
);
5860 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5861 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5862 if (extn
->extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
5863 !memcmp(extn
->extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
->extnID
.length
)) {
5864 /* Got the SCT oid */
5865 DERDecodedInfo sctList
;
5866 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &sctList
);
5867 require_noerr_quiet(drtn
, out
);
5868 require_quiet(sctList
.tag
== ASN1_OCTET_STRING
, out
);
5869 return SecCreateSignedCertificateTimestampsArrayFromSerializedSCTList(sctList
.content
.data
, sctList
.content
.length
);
5877 static bool matches_expected(DERItem der
, CFTypeRef expected
) {
5878 if (der
.length
> 1) {
5879 DERDecodedInfo decoded
;
5880 DERDecodeItem(&der
, &decoded
);
5881 switch (decoded
.tag
) {
5884 return decoded
.content
.length
== 0 && expected
== NULL
;
5888 case ASN1_IA5_STRING
:
5889 case ASN1_UTF8_STRING
: {
5890 if (isString(expected
)) {
5891 CFStringRef expectedString
= (CFStringRef
) expected
;
5892 CFStringRef itemString
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFStringEncodingUTF8
, false, kCFAllocatorNull
);
5894 bool result
= (kCFCompareEqualTo
== CFStringCompare(expectedString
, itemString
, 0));
5895 CFReleaseNull(itemString
);
5901 case ASN1_OCTET_STRING
: {
5902 if (isData(expected
)) {
5903 CFDataRef expectedData
= (CFDataRef
) expected
;
5904 CFDataRef itemData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFAllocatorNull
);
5906 bool result
= CFEqual(expectedData
, itemData
);
5907 CFReleaseNull(itemData
);
5913 case ASN1_INTEGER
: {
5914 SInt32 expected_value
= 0;
5915 if (isString(expected
))
5917 CFStringRef aStr
= (CFStringRef
)expected
;
5918 expected_value
= CFStringGetIntValue(aStr
);
5920 else if (isNumber(expected
))
5922 CFNumberGetValue(expected
, kCFNumberSInt32Type
, &expected_value
);
5925 uint32_t num_value
= 0;
5926 if (!DERParseInteger(&decoded
.content
, &num_value
))
5928 return ((uint32_t)expected_value
== num_value
);
5941 static bool cert_contains_marker_extension_value(SecCertificateRef certificate
, CFDataRef oid
, CFTypeRef expectedValue
)
5944 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
5945 size_t oid_len
= CFDataGetLength(oid
);
5947 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5948 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5949 if (extn
->extnID
.length
== oid_len
5950 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
5952 return matches_expected(extn
->extnValue
, expectedValue
);
5958 static bool cert_contains_marker_extension(SecCertificateRef certificate
, CFTypeRef oid
)
5960 return cert_contains_marker_extension_value(certificate
, oid
, NULL
);
5963 struct search_context
{
5965 SecCertificateRef certificate
;
5968 static bool GetDecimalValueOfString(CFStringRef string
, uint32_t* value
)
5970 CFCharacterSetRef nonDecimalDigit
= CFCharacterSetCreateInvertedSet(NULL
, CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
));
5971 bool result
= false;
5973 if ( CFStringGetLength(string
) > 0
5974 && !CFStringFindCharacterFromSet(string
, nonDecimalDigit
, CFRangeMake(0, CFStringGetLength(string
)), kCFCompareForcedOrdering
, NULL
))
5977 *value
= CFStringGetIntValue(string
);
5981 CFReleaseNull(nonDecimalDigit
);
5986 bool SecCertificateIsOidString(CFStringRef oid
)
5988 if (!oid
) return false;
5989 if (2 >= CFStringGetLength(oid
)) return false;
5992 /* oid string only has the allowed characters */
5993 CFCharacterSetRef decimalOid
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789."));
5994 CFCharacterSetRef nonDecimalOid
= CFCharacterSetCreateInvertedSet(NULL
, decimalOid
);
5995 if (CFStringFindCharacterFromSet(oid
, nonDecimalOid
, CFRangeMake(0, CFStringGetLength(oid
)), kCFCompareForcedOrdering
, NULL
)) {
5999 /* first arc is allowed */
6000 UniChar firstArc
[2];
6001 CFRange firstTwo
= {0, 2};
6002 CFStringGetCharacters(oid
, firstTwo
, firstArc
);
6003 if (firstArc
[1] != '.' ||
6004 (firstArc
[0] != '0' && firstArc
[0] != '1' && firstArc
[0] != '2')) {
6008 CFReleaseNull(decimalOid
);
6009 CFReleaseNull(nonDecimalOid
);
6014 CFDataRef
SecCertificateCreateOidDataFromString(CFAllocatorRef allocator
, CFStringRef string
)
6016 CFMutableDataRef currentResult
= NULL
;
6017 CFDataRef encodedResult
= NULL
;
6019 CFArrayRef parts
= NULL
;
6022 if (!string
|| !SecCertificateIsOidString(string
))
6025 parts
= CFStringCreateArrayBySeparatingStrings(allocator
, string
, CFSTR("."));
6030 count
= CFArrayGetCount(parts
);
6034 // assume no more than 5 bytes needed to represent any part of the oid,
6035 // since we limit parts to 32-bit values,
6036 // but the first two parts only need 1 byte
6037 currentResult
= CFDataCreateMutable(allocator
, 1+(count
-2)*5);
6043 part
= CFArrayGetValueAtIndex(parts
, 0);
6045 if (!GetDecimalValueOfString(part
, &x
) || x
> 6)
6052 part
= CFArrayGetValueAtIndex(parts
, 1);
6054 if (!GetDecimalValueOfString(part
, &x
) || x
> 39)
6060 CFDataAppendBytes(currentResult
, &firstByte
, 1);
6062 for (CFIndex i
= 2; i
< count
&& GetDecimalValueOfString(CFArrayGetValueAtIndex(parts
, i
), &x
); ++i
) {
6063 uint8_t b
[5] = {0, 0, 0, 0, 0};
6065 b
[3] = 0x80 | ((x
>> 7) & 0x7F);
6066 b
[2] = 0x80 | ((x
>> 14) & 0x7F);
6067 b
[1] = 0x80 | ((x
>> 21) & 0x7F);
6068 b
[0] = 0x80 | ((x
>> 28) & 0x7F);
6070 // Skip the unused extension bytes.
6071 size_t skipBytes
= 0;
6072 while (b
[skipBytes
] == 0x80)
6075 CFDataAppendBytes(currentResult
, b
+ skipBytes
, sizeof(b
) - skipBytes
);
6078 encodedResult
= currentResult
;
6079 currentResult
= NULL
;
6082 CFReleaseNull(parts
);
6083 CFReleaseNull(currentResult
);
6085 return encodedResult
;
6088 static void check_for_marker(const void *key
, const void *value
, void *context
)
6090 struct search_context
* search_ctx
= (struct search_context
*) context
;
6091 CFStringRef key_string
= (CFStringRef
) key
;
6092 CFTypeRef value_ref
= (CFTypeRef
) value
;
6094 // If we could have short-circuited the iteration
6095 // we would have, but the best we can do
6096 // is not waste time comparing once a match
6098 if (search_ctx
->found
)
6101 if (!isString(key_string
))
6104 CFDataRef key_data
= SecCertificateCreateOidDataFromString(NULL
, key_string
);
6106 if (!isData(key_data
))
6109 if (cert_contains_marker_extension_value(search_ctx
->certificate
, key_data
, value_ref
))
6110 search_ctx
->found
= true;
6112 CFReleaseNull(key_data
);
6116 // CFType Ref is either:
6118 // CFData - OID to match with no data permitted
6119 // CFString - decimal OID to match
6120 // CFDictionary - OID -> Value table for expected values Single Object or Array
6121 // CFArray - Array of the above.
6123 // This returns true if any of the requirements are met.
6124 bool SecCertificateHasMarkerExtension(SecCertificateRef certificate
, CFTypeRef oids
)
6126 if (NULL
== certificate
|| NULL
== oids
) {
6128 } else if (CFGetTypeID(oids
) == CFArrayGetTypeID()) {
6129 CFIndex ix
, length
= CFArrayGetCount(oids
);
6130 for (ix
= 0; ix
< length
; ix
++)
6131 if (SecCertificateHasMarkerExtension(certificate
, CFArrayGetValueAtIndex((CFArrayRef
)oids
, ix
)))
6133 } else if (CFGetTypeID(oids
) == CFDictionaryGetTypeID()) {
6134 struct search_context context
= { .found
= false, .certificate
= certificate
};
6135 CFDictionaryApplyFunction((CFDictionaryRef
) oids
, &check_for_marker
, &context
);
6136 return context
.found
;
6137 } else if (CFGetTypeID(oids
) == CFDataGetTypeID()) {
6138 return cert_contains_marker_extension(certificate
, oids
);
6139 } else if (CFGetTypeID(oids
) == CFStringGetTypeID()) {
6140 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, oids
);
6141 if (dataOid
== NULL
) return false;
6142 bool result
= cert_contains_marker_extension(certificate
, dataOid
);
6143 CFReleaseNull(dataOid
);
6149 // Since trust evaluation checks for the id-pkix-ocsp-nocheck OID marker
6150 // in every certificate, this function caches the OID data once instead of
6151 // parsing the same OID string each time.
6153 bool SecCertificateHasOCSPNoCheckMarkerExtension(SecCertificateRef certificate
)
6155 static CFDataRef sOCSPNoCheckOIDData
= NULL
;
6156 static dispatch_once_t onceToken
;
6158 dispatch_once(&onceToken
, ^{
6159 sOCSPNoCheckOIDData
= SecCertificateCreateOidDataFromString(NULL
, CFSTR("1.3.6.1.5.5.7.48.1.5"));
6161 return SecCertificateHasMarkerExtension(certificate
, sOCSPNoCheckOIDData
);
6164 static DERItem
*cert_extension_value_for_marker(SecCertificateRef certificate
, CFDataRef oid
) {
6166 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
6167 size_t oid_len
= CFDataGetLength(oid
);
6169 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
6170 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
6171 if (extn
->extnID
.length
== oid_len
6172 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
6174 return (DERItem
*)&extn
->extnValue
;
6181 // CFType Ref is either:
6183 // CFData - OID to match with no data permitted
6184 // CFString - decimal OID to match
6186 DERItem
*SecCertificateGetExtensionValue(SecCertificateRef certificate
, CFTypeRef oid
) {
6187 if (!certificate
|| !oid
) {
6191 if(CFGetTypeID(oid
) == CFDataGetTypeID()) {
6192 return cert_extension_value_for_marker(certificate
, oid
);
6193 } else if (CFGetTypeID(oid
) == CFStringGetTypeID()) {
6194 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, oid
);
6195 if (dataOid
== NULL
) return NULL
;
6196 DERItem
*result
= cert_extension_value_for_marker(certificate
, dataOid
);
6197 CFReleaseNull(dataOid
);
6204 CFDataRef
SecCertificateCopyExtensionValue(SecCertificateRef certificate
, CFTypeRef extensionOID
, bool *isCritical
) {
6205 if (!certificate
|| !extensionOID
) {
6209 CFDataRef oid
= NULL
, extensionValue
= NULL
;
6210 if (CFGetTypeID(extensionOID
) == CFDataGetTypeID()) {
6211 oid
= CFRetainSafe(extensionOID
);
6212 } else if (CFGetTypeID(extensionOID
) == CFStringGetTypeID()) {
6213 oid
= SecCertificateCreateOidDataFromString(NULL
, extensionOID
);
6220 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
6221 size_t oid_len
= CFDataGetLength(oid
);
6223 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
6224 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
6225 if (extn
->extnID
.length
== oid_len
6226 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
6229 *isCritical
= extn
->critical
;
6231 extensionValue
= CFDataCreate(NULL
, extn
->extnValue
.data
, extn
->extnValue
.length
);
6237 return extensionValue
;
6240 CFDataRef
SecCertificateCopyiAPAuthCapabilities(SecCertificateRef certificate
) {
6244 CFDataRef extensionData
= NULL
;
6245 DERItem
*extensionValue
= NULL
;
6246 extensionValue
= SecCertificateGetExtensionValue(certificate
,
6247 CFSTR("1.2.840.113635.100.6.36"));
6248 require_quiet(extensionValue
, out
);
6249 /* The extension is a octet string containing the DER-encoded 32-byte octet string */
6250 require_quiet(extensionValue
->length
== 34, out
);
6251 DERDecodedInfo decodedValue
;
6252 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
6253 if (decodedValue
.tag
== ASN1_OCTET_STRING
) {
6254 require_quiet(decodedValue
.content
.length
== 32, out
);
6255 extensionData
= CFDataCreate(NULL
, decodedValue
.content
.data
,
6256 decodedValue
.content
.length
);
6258 require_quiet(extensionValue
->data
[33] == 0x00 &&
6259 extensionValue
->data
[32] == 0x00, out
);
6260 extensionData
= CFDataCreate(NULL
, extensionValue
->data
, 32);
6263 return extensionData
;
6267 /* From iapd IAPAuthenticationTypes.h */
6268 typedef struct IapCertSerialNumber
6270 uint8_t xservID
; // Xserver ID
6271 uint8_t hsmID
; // Hardware security module ID (generated cert)
6272 uint8_t delimiter01
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6273 uint8_t dateYear
; // Date year cert was issued
6274 uint8_t dateMonth
; // Date month cert was issued
6275 uint8_t dateDay
; // Date day cert was issued
6276 uint8_t delimiter02
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6277 uint8_t devClass
; // iAP device class (maps to lingo permissions)
6278 uint8_t delimiter03
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6279 uint8_t batchNumHi
; // Batch number high byte (15:08)
6280 uint8_t batchNumLo
; // Batch number low byte (07:00)
6281 uint8_t delimiter04
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6282 uint8_t serialNumHi
; // Serial number high byte (23:16)
6283 uint8_t serialNumMid
; // Serial number middle byte (15:08)
6284 uint8_t serialNumLo
; // Serial number low byte (07:00)
6286 } IapCertSerialNumber_t
, *pIapCertSerialNumber_t
;
6289 #define IAP_CERT_FIELD_DELIMITER 0xAA // "Apple_Accessory" delimiter
6290 SeciAuthVersion
SecCertificateGetiAuthVersion(SecCertificateRef certificate
) {
6292 return kSeciAuthInvalid
;
6294 if (NULL
!= SecCertificateGetExtensionValue(certificate
,
6295 CFSTR("1.2.840.113635.100.6.36"))) {
6296 /* v3 Capabilities Extension */
6297 return kSeciAuthVersion3
;
6299 if (NULL
!= SecCertificateGetExtensionValue(certificate
,
6300 CFSTR("1.2.840.113635.100.6.59.1"))) {
6301 /* SW Auth General Capabilities Extension */
6302 return kSeciAuthVersionSW
;
6304 DERItem serialNumber
= certificate
->_serialNum
;
6305 require_quiet(serialNumber
.data
, out
);
6306 require_quiet(serialNumber
.length
== 15, out
);
6307 require_quiet(serialNumber
.data
[2] == IAP_CERT_FIELD_DELIMITER
&&
6308 serialNumber
.data
[6] == IAP_CERT_FIELD_DELIMITER
&&
6309 serialNumber
.data
[8] == IAP_CERT_FIELD_DELIMITER
&&
6310 serialNumber
.data
[11] == IAP_CERT_FIELD_DELIMITER
, out
);
6311 return kSeciAuthVersion2
;
6313 return kSeciAuthInvalid
;
6316 static CFStringRef
SecCertificateiAPSWAuthCapabilitiesTypeToOID(SeciAPSWAuthCapabilitiesType type
) {
6317 CFStringRef extensionOID
= NULL
;
6318 /* Get the oid for the type */
6319 if (type
== kSeciAPSWAuthGeneralCapabilities
) {
6320 extensionOID
= CFSTR("1.2.840.113635.100.6.59.1");
6321 } else if (type
== kSeciAPSWAuthAirPlayCapabilities
) {
6322 extensionOID
= CFSTR("1.2.840.113635.100.6.59.2");
6323 } else if (type
== kSeciAPSWAuthHomeKitCapabilities
) {
6324 extensionOID
= CFSTR("1.2.840.113635.100.6.59.3");
6326 return extensionOID
;
6329 CFDataRef
SecCertificateCopyiAPSWAuthCapabilities(SecCertificateRef certificate
, SeciAPSWAuthCapabilitiesType type
) {
6333 CFDataRef extensionData
= NULL
;
6334 DERItem
*extensionValue
= NULL
;
6335 CFStringRef extensionOID
= SecCertificateiAPSWAuthCapabilitiesTypeToOID(type
);
6336 require_quiet(extensionOID
, out
);
6337 extensionValue
= SecCertificateGetExtensionValue(certificate
, extensionOID
);
6338 require_quiet(extensionValue
, out
);
6339 /* The extension is a octet string containing the DER-encoded variable-length octet string */
6340 DERDecodedInfo decodedValue
;
6341 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
6342 if (decodedValue
.tag
== ASN1_OCTET_STRING
) {
6343 extensionData
= CFDataCreate(NULL
, decodedValue
.content
.data
,
6344 decodedValue
.content
.length
);
6347 return extensionData
;
6350 CFStringRef
SecCertificateCopyComponentType(SecCertificateRef certificate
) {
6354 CFStringRef componentType
= NULL
;
6355 DERItem
*extensionValue
= SecCertificateGetExtensionValue(certificate
, CFSTR("1.2.840.113635.100.11.1"));
6356 require_quiet(extensionValue
, out
);
6357 /* The componentType is an IA5String */
6358 DERDecodedInfo decodedValue
;
6359 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
6360 if (decodedValue
.tag
== ASN1_IA5_STRING
) {
6361 componentType
= CFStringCreateWithBytes(NULL
, decodedValue
.content
.data
, decodedValue
.content
.length
, kCFStringEncodingASCII
, false);
6364 return componentType
;
6367 SecCertificateRef
SecCertificateCreateWithPEM(CFAllocatorRef allocator
,
6368 CFDataRef pem_certificate
)
6370 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
6371 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
6372 uint8_t *base64_data
= NULL
;
6373 SecCertificateRef cert
= NULL
;
6374 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
6375 const size_t length
= CFDataGetLength(pem_certificate
);
6376 char *begin
= strnstr((const char *)data
, begin_cert
, length
);
6377 char *end
= strnstr((const char *)data
, end_cert
, length
);
6380 begin
+= sizeof(begin_cert
) - 1;
6381 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
6382 if (base64_length
&& (base64_length
< (size_t)CFDataGetLength(pem_certificate
))) {
6383 require_quiet(base64_data
= calloc(1, base64_length
), out
);
6384 require_action_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
, free(base64_data
));
6385 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, base64_data
, base64_length
);
6394 // -- MARK -- XPC encoding/decoding
6397 bool SecCertificateAppendToXPCArray(SecCertificateRef certificate
, xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
6399 return true; // NOOP
6401 size_t length
= SecCertificateGetLength(certificate
);
6402 const uint8_t *bytes
= SecCertificateGetBytePtr(certificate
);
6403 #if SECTRUST_VERBOSE_DEBUG
6404 secerror("cert=0x%lX length=%d bytes=0x%lX", (uintptr_t)certificate
, (int)length
, (uintptr_t)bytes
);
6406 if (!length
|| !bytes
) {
6407 return SecError(errSecParam
, error
, CFSTR("failed to der encode certificate"));
6409 xpc_array_set_data(xpc_certificates
, XPC_ARRAY_APPEND
, bytes
, length
);
6413 SecCertificateRef
SecCertificateCreateWithXPCArrayAtIndex(xpc_object_t xpc_certificates
, size_t index
, CFErrorRef
*error
) {
6414 SecCertificateRef certificate
= NULL
;
6416 const uint8_t *bytes
= xpc_array_get_data(xpc_certificates
, index
, &length
);
6418 certificate
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, bytes
, length
);
6421 SecError(errSecParam
, error
, CFSTR("certificates[%zu] failed to decode"), index
);
6426 xpc_object_t
SecCertificateArrayCopyXPCArray(CFArrayRef certificates
, CFErrorRef
*error
) {
6427 xpc_object_t xpc_certificates
;
6428 require_action_quiet(xpc_certificates
= xpc_array_create(NULL
, 0), exit
,
6429 SecError(errSecAllocate
, error
, CFSTR("failed to create xpc_array")));
6430 CFIndex ix
, count
= CFArrayGetCount(certificates
);
6431 for (ix
= 0; ix
< count
; ++ix
) {
6432 SecCertificateRef certificate
= (SecCertificateRef
) CFArrayGetValueAtIndex(certificates
, ix
);
6433 #if SECTRUST_VERBOSE_DEBUG
6434 CFIndex length
= SecCertificateGetLength(certificate
);
6435 const UInt8
*bytes
= SecCertificateGetBytePtr(certificate
);
6436 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
);
6438 if (!SecCertificateAppendToXPCArray(certificate
, xpc_certificates
, error
)) {
6439 xpc_release(xpc_certificates
);
6440 xpc_certificates
= NULL
;
6446 return xpc_certificates
;
6449 CFArrayRef
SecCertificateXPCArrayCopyArray(xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
6450 CFMutableArrayRef certificates
= NULL
;
6451 require_action_quiet(xpc_get_type(xpc_certificates
) == XPC_TYPE_ARRAY
, exit
,
6452 SecError(errSecParam
, error
, CFSTR("certificates xpc value is not an array")));
6453 size_t count
= xpc_array_get_count(xpc_certificates
);
6454 require_action_quiet(certificates
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
), exit
,
6455 SecError(errSecAllocate
, error
, CFSTR("failed to create CFArray of capacity %zu"), count
));
6458 for (ix
= 0; ix
< count
; ++ix
) {
6459 SecCertificateRef cert
= SecCertificateCreateWithXPCArrayAtIndex(xpc_certificates
, ix
, error
);
6461 CFRelease(certificates
);
6464 CFArraySetValueAtIndex(certificates
, ix
, cert
);
6469 return certificates
;
6472 #define do_if_registered(sdp, ...) if (gTrustd && gTrustd->sdp) { return gTrustd->sdp(__VA_ARGS__); }
6475 static CFArrayRef
CopyEscrowCertificates(SecCertificateEscrowRootType escrowRootType
, CFErrorRef
* error
)
6477 __block CFArrayRef result
= NULL
;
6479 do_if_registered(ota_CopyEscrowCertificates
, escrowRootType
, error
);
6481 securityd_send_sync_and_do(kSecXPCOpOTAGetEscrowCertificates
, error
,
6482 ^bool(xpc_object_t message
, CFErrorRef
*blockError
)
6484 xpc_dictionary_set_uint64(message
, "escrowType", (uint64_t)escrowRootType
);
6487 ^bool(xpc_object_t response
, CFErrorRef
*blockError
)
6489 xpc_object_t xpc_array
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
6491 if (response
&& (NULL
!= xpc_array
)) {
6492 result
= (CFArrayRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_array
);
6495 return SecError(errSecInternal
, blockError
, CFSTR("Did not get the Escrow certificates"));
6497 return result
!= NULL
;
6502 CFArrayRef
SecCertificateCopyEscrowRoots(SecCertificateEscrowRootType escrowRootType
)
6504 CFArrayRef result
= NULL
;
6506 CFDataRef certData
= NULL
;
6509 if (kSecCertificateBaselineEscrowRoot
== escrowRootType
||
6510 kSecCertificateBaselinePCSEscrowRoot
== escrowRootType
||
6511 kSecCertificateBaselineEscrowBackupRoot
== escrowRootType
||
6512 kSecCertificateBaselineEscrowEnrollmentRoot
== escrowRootType
)
6514 // The request is for the base line certificates.
6515 // Use the hard coded data to generate the return array.
6516 struct RootRecord
** pEscrowRoots
;
6517 switch (escrowRootType
) {
6518 case kSecCertificateBaselineEscrowRoot
:
6519 numRoots
= kNumberOfBaseLineEscrowRoots
;
6520 pEscrowRoots
= kBaseLineEscrowRoots
;
6522 case kSecCertificateBaselinePCSEscrowRoot
:
6523 numRoots
= kNumberOfBaseLinePCSEscrowRoots
;
6524 pEscrowRoots
= kBaseLinePCSEscrowRoots
;
6526 case kSecCertificateBaselineEscrowBackupRoot
:
6527 numRoots
= kNumberOfBaseLineEscrowBackupRoots
;
6528 pEscrowRoots
= kBaseLineEscrowBackupRoots
;
6530 case kSecCertificateBaselineEscrowEnrollmentRoot
:
6532 numRoots
= kNumberOfBaseLineEscrowEnrollmentRoots
;
6533 pEscrowRoots
= kBaseLineEscrowEnrollmentRoots
;
6537 // Get the hard coded set of roots
6538 SecCertificateRef baseLineCerts
[numRoots
];
6539 struct RootRecord
* pRootRecord
= NULL
;
6541 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6542 pRootRecord
= pEscrowRoots
[iCnt
];
6543 if (NULL
!= pRootRecord
&& pRootRecord
->_length
> 0 && NULL
!= pRootRecord
->_bytes
) {
6544 certData
= CFDataCreate(kCFAllocatorDefault
, pRootRecord
->_bytes
, pRootRecord
->_length
);
6545 if (NULL
!= certData
) {
6546 baseLineCerts
[iCnt
] = SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
6547 CFRelease(certData
);
6551 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)baseLineCerts
, numRoots
, &kCFTypeArrayCallBacks
);
6552 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6553 if (NULL
!= baseLineCerts
[iCnt
]) {
6554 CFRelease(baseLineCerts
[iCnt
]);
6558 // The request is for the current certificates.
6559 CFErrorRef error
= NULL
;
6560 CFArrayRef cert_datas
= CopyEscrowCertificates(escrowRootType
, &error
);
6561 if (NULL
!= error
|| NULL
== cert_datas
) {
6562 if (NULL
!= error
) {
6565 if (NULL
!= cert_datas
) {
6566 CFRelease(cert_datas
);
6571 numRoots
= (int)(CFArrayGetCount(cert_datas
));
6573 SecCertificateRef assetCerts
[numRoots
];
6574 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6575 certData
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, iCnt
);
6576 if (NULL
!= certData
) {
6577 SecCertificateRef aCertRef
= SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
6578 assetCerts
[iCnt
] = aCertRef
;
6581 assetCerts
[iCnt
] = NULL
;
6586 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)assetCerts
, numRoots
, &kCFTypeArrayCallBacks
);
6587 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6588 if (NULL
!= assetCerts
[iCnt
]) {
6589 CFRelease(assetCerts
[iCnt
]);
6593 CFReleaseSafe(cert_datas
);
6598 static CFDictionaryRef
CopyTrustedCTLogs(CFErrorRef
* error
)
6600 __block CFDictionaryRef result
= NULL
;
6602 // call function directly and return if we are built in server mode
6603 do_if_registered(sec_ota_pki_copy_trusted_ct_logs
, error
);
6605 securityd_send_sync_and_do(kSecXPCOpOTAPKICopyTrustedCTLogs
, error
,
6606 ^bool(xpc_object_t message
, CFErrorRef
*blockError
) {
6607 // input: set message parameters here
6609 }, ^bool(xpc_object_t response
, CFErrorRef
*blockError
) {
6610 // output: get dictionary from response object
6611 xpc_object_t xpc_dictionary
= NULL
;
6613 xpc_dictionary
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
6615 if (xpc_dictionary
&& (xpc_get_type(xpc_dictionary
) == XPC_TYPE_DICTIONARY
)) {
6616 result
= (CFDictionaryRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_dictionary
);
6618 return SecError(errSecInternal
, blockError
, CFSTR("Unable to get CT logs"));
6620 return result
!= NULL
;
6625 #define CTLOG_KEYID_LENGTH 32 /* key id data length */
6627 static CFDictionaryRef
CopyCTLogForKeyID(CFDataRef keyID
, CFErrorRef
* error
)
6629 __block CFDictionaryRef result
= NULL
;
6630 if (!isData(keyID
)) {
6631 (void) SecError(errSecParam
, error
, CFSTR("keyID was not a valid CFDataRef"));
6634 const void *p
= CFDataGetBytePtr(keyID
);
6635 if (!p
|| CFDataGetLength(keyID
) != CTLOG_KEYID_LENGTH
) {
6636 (void) SecError(errSecParam
, error
, CFSTR("keyID data was not the expected length"));
6639 // call function directly and return if we are built in server mode
6640 do_if_registered(sec_ota_pki_copy_ct_log_for_keyid
, keyID
, error
);
6642 securityd_send_sync_and_do(kSecXPCOpOTAPKICopyCTLogForKeyID
, error
,
6643 ^bool(xpc_object_t message
, CFErrorRef
*blockError
) {
6644 // input: set message parameters here
6645 xpc_dictionary_set_data(message
, kSecXPCData
, p
, CTLOG_KEYID_LENGTH
);
6647 }, ^bool(xpc_object_t response
, CFErrorRef
*blockError
) {
6648 // output: get dictionary from response object
6649 xpc_object_t xpc_dictionary
= NULL
;
6651 xpc_dictionary
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
6653 if (xpc_dictionary
&& (xpc_get_type(xpc_dictionary
) == XPC_TYPE_DICTIONARY
)) {
6654 result
= (CFDictionaryRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_dictionary
);
6656 return SecError(errSecInternal
, blockError
, CFSTR("Unable to match CT log"));
6658 return result
!= NULL
;
6663 CFDictionaryRef
SecCertificateCopyTrustedCTLogs(void)
6665 CFErrorRef localError
= NULL
;
6666 CFDictionaryRef result
= CopyTrustedCTLogs(&localError
);
6667 CFReleaseSafe(localError
);
6671 CFDictionaryRef
SecCertificateCopyCTLogForKeyID(CFDataRef keyID
)
6673 CFErrorRef localError
= NULL
;
6674 CFDictionaryRef result
= CopyCTLogForKeyID(keyID
, &localError
);
6675 CFReleaseSafe(localError
);
6679 SEC_CONST_DECL (kSecSignatureDigestAlgorithmUnknown
, "SignatureDigestUnknown");
6680 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD2
, "SignatureDigestMD2");
6681 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD4
, "SignatureDigestMD4");
6682 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD5
, "SignatureDigestMD5");
6683 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA1
, "SignatureDigestSHA1");
6684 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA224
, "SignatureDigestSHA224");
6685 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA256
, "SignatureDigestSHA256");
6686 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA384
, "SignatureDigestSHA284");
6687 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA512
, "SignatureDigestSHA512");
6689 SecSignatureHashAlgorithm
SecCertificateGetSignatureHashAlgorithm(SecCertificateRef certificate
)
6691 SecSignatureHashAlgorithm result
= kSecSignatureHashAlgorithmUnknown
;
6692 DERAlgorithmId
*algId
= (certificate
) ? &certificate
->_tbsSigAlg
: NULL
;
6693 const DERItem
*algOid
= (algId
) ? &algId
->oid
: NULL
;
6695 if (!algOid
->data
|| !algOid
->length
) {
6698 /* classify the signature algorithm OID into one of our known types */
6699 if (DEROidCompare(algOid
, &oidSha512Ecdsa
) ||
6700 DEROidCompare(algOid
, &oidSha512Rsa
) ||
6701 DEROidCompare(algOid
, &oidSha512
)) {
6702 result
= kSecSignatureHashAlgorithmSHA512
;
6705 if (DEROidCompare(algOid
, &oidSha384Ecdsa
) ||
6706 DEROidCompare(algOid
, &oidSha384Rsa
) ||
6707 DEROidCompare(algOid
, &oidSha384
)) {
6708 result
= kSecSignatureHashAlgorithmSHA384
;
6711 if (DEROidCompare(algOid
, &oidSha256Ecdsa
) ||
6712 DEROidCompare(algOid
, &oidSha256Rsa
) ||
6713 DEROidCompare(algOid
, &oidSha256
)) {
6714 result
= kSecSignatureHashAlgorithmSHA256
;
6717 if (DEROidCompare(algOid
, &oidSha224Ecdsa
) ||
6718 DEROidCompare(algOid
, &oidSha224Rsa
) ||
6719 DEROidCompare(algOid
, &oidSha224
)) {
6720 result
= kSecSignatureHashAlgorithmSHA224
;
6723 if (DEROidCompare(algOid
, &oidSha1Ecdsa
) ||
6724 DEROidCompare(algOid
, &oidSha1Rsa
) ||
6725 DEROidCompare(algOid
, &oidSha1Dsa
) ||
6726 DEROidCompare(algOid
, &oidSha1DsaOIW
) ||
6727 DEROidCompare(algOid
, &oidSha1DsaCommonOIW
) ||
6728 DEROidCompare(algOid
, &oidSha1RsaOIW
) ||
6729 DEROidCompare(algOid
, &oidSha1Fee
) ||
6730 DEROidCompare(algOid
, &oidSha1
)) {
6731 result
= kSecSignatureHashAlgorithmSHA1
;
6734 if (DEROidCompare(algOid
, &oidMd5Rsa
) ||
6735 DEROidCompare(algOid
, &oidMd5Fee
) ||
6736 DEROidCompare(algOid
, &oidMd5
)) {
6737 result
= kSecSignatureHashAlgorithmMD5
;
6740 if (DEROidCompare(algOid
, &oidMd4Rsa
) ||
6741 DEROidCompare(algOid
, &oidMd4
)) {
6742 result
= kSecSignatureHashAlgorithmMD4
;
6745 if (DEROidCompare(algOid
, &oidMd2Rsa
) ||
6746 DEROidCompare(algOid
, &oidMd2
)) {
6747 result
= kSecSignatureHashAlgorithmMD2
;
6756 CFArrayRef
SecCertificateCopyiPhoneDeviceCAChain(void) {
6757 CFMutableArrayRef result
= NULL
;
6758 SecCertificateRef iPhoneDeviceCA
= NULL
, iPhoneCA
= NULL
, appleRoot
= NULL
;
6760 require_quiet(iPhoneDeviceCA
= SecCertificateCreateWithBytes(NULL
, _AppleiPhoneDeviceCA
, sizeof(_AppleiPhoneDeviceCA
)),
6762 require_quiet(iPhoneCA
= SecCertificateCreateWithBytes(NULL
, _AppleiPhoneCA
, sizeof(_AppleiPhoneCA
)),
6764 require_quiet(appleRoot
= SecCertificateCreateWithBytes(NULL
, _AppleRootCA
, sizeof(_AppleRootCA
)),
6767 require_quiet(result
= CFArrayCreateMutable(NULL
, 3, &kCFTypeArrayCallBacks
), errOut
);
6768 CFArrayAppendValue(result
, iPhoneDeviceCA
);
6769 CFArrayAppendValue(result
, iPhoneCA
);
6770 CFArrayAppendValue(result
, appleRoot
);
6773 CFReleaseNull(iPhoneDeviceCA
);
6774 CFReleaseNull(iPhoneCA
);
6775 CFReleaseNull(appleRoot
);
6779 bool SecCertificateGetDeveloperIDDate(SecCertificateRef certificate
, CFAbsoluteTime
*time
, CFErrorRef
*error
) {
6780 if (!certificate
|| !time
) {
6781 return SecError(errSecParam
, error
, CFSTR("DeveloperID Date parsing: missing required input"));
6783 DERItem
*extensionValue
= SecCertificateGetExtensionValue(certificate
, CFSTR("1.2.840.113635.100.6.1.33"));
6784 if (!extensionValue
) {
6785 return SecError(errSecMissingRequiredExtension
, error
, CFSTR("DeveloperID Date parsing: extension not found"));
6787 DERDecodedInfo decodedValue
;
6788 if (DERDecodeItem(extensionValue
, &decodedValue
) != DR_Success
) {
6789 return SecError(errSecDecode
, error
, CFSTR("DeveloperID Date parsing: extension value failed to decode"));
6791 /* The extension value is a DERGeneralizedTime encoded in a UTF8String */
6792 CFErrorRef localError
= NULL
;
6793 if (decodedValue
.tag
== ASN1_UTF8_STRING
) {
6794 *time
= SecAbsoluteTimeFromDateContentWithError(ASN1_GENERALIZED_TIME
, decodedValue
.content
.data
, decodedValue
.content
.length
, &localError
);
6796 return SecError(errSecDecode
, error
, CFSTR("DeveloperID Date parsing: extension value wrong tag"));
6798 return CFErrorPropagate(localError
, error
);
6801 CFIndex
SecCertificateGetUnparseableKnownExtension(SecCertificateRef certificate
) {
6805 return certificate
->_unparseableKnownExtensionIndex
;