2 * Copyright (c) 2006-2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecCertificate.c - CoreFoundation based certificate object
29 /* Allows us to build genanchors against the BaseSDK. */
30 #undef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
31 #undef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
34 #include <Security/SecCertificateInternal.h>
35 #include <utilities/SecIOFormat.h>
36 #include <CommonCrypto/CommonDigest.h>
37 #include <CoreFoundation/CFRuntime.h>
38 #include <CoreFoundation/CFString.h>
39 #include <CoreFoundation/CFBundle.h>
40 #include <CoreFoundation/CFDictionary.h>
41 #include <CoreFoundation/CFNumber.h>
42 #include <CoreFoundation/CFCalendar.h>
43 #include <CoreFoundation/CFTimeZone.h>
44 #include <CoreFoundation/CFXPCBridge.h>
46 #include <AssertMacros.h>
47 #include <libDER/DER_CertCrl.h>
48 #include <libDER/DER_Encode.h>
49 #include <libDER/DER_Keys.h>
50 #include <libDER/asn1Types.h>
51 #include <libDER/oidsPriv.h>
52 #include "SecBasePriv.h"
53 #include "SecRSAKey.h"
54 #include "SecFramework.h"
56 #include "SecItemPriv.h"
58 #include <utilities/debugging.h>
59 #include <utilities/SecCFWrappers.h>
60 #include <utilities/SecCFError.h>
61 #include <utilities/SecSCTUtils.h>
62 #include <utilities/array_size.h>
64 #include <libkern/OSByteOrder.h>
66 #include <Security/SecInternal.h>
67 #include <Security/SecFrameworkStrings.h>
68 #include "SecBase64.h"
69 #include "AppleBaselineEscrowCertificates.h"
70 #include <ipc/securityd_client.h>
72 typedef struct SecCertificateExtension
{
76 } SecCertificateExtension
;
79 typedef struct KnownExtension
{
85 kSecSelfSignedUnknown
= 0,
91 struct __SecCertificate
{
94 DERItem _der
; /* Entire certificate in DER form. */
95 DERItem _tbs
; /* To Be Signed cert DER bytes. */
96 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
97 DERItem _signature
; /* The content of the sig bit string. */
100 DERItem _serialNum
; /* Integer. */
101 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
102 DERItem _issuer
; /* Sequence of RDN. */
103 CFAbsoluteTime _notBefore
;
104 CFAbsoluteTime _notAfter
;
105 DERItem _subject
; /* Sequence of RDN. */
106 DERItem _subjectPublicKeyInfo
; /* SPKI */
107 DERAlgorithmId _algId
; /* oid and params of _pubKeyDER. */
108 DERItem _pubKeyDER
; /* contents of bit string */
109 DERItem _issuerUniqueID
; /* bit string, optional */
110 DERItem _subjectUniqueID
; /* bit string, optional */
113 /* Known extensions if the certificate contains them,
114 extnValue.length will be > 0. */
115 KnownExtension _authorityKeyID
;
117 /* This extension is used to uniquely identify a certificate from among
118 several that have the same subject name. If the extension is not
119 present, its value is calculated by performing a SHA-1 hash of the
120 certificate's DER encoded subjectPublicKeyInfo, as recommended by
122 KnownExtension _subjectKeyID
;
123 KnownExtension _keyUsage
;
124 KnownExtension _extendedKeyUsage
;
125 KnownExtension _basicConstraints
;
126 KnownExtension _netscapeCertType
;
127 KnownExtension _subjectAltName
;
128 KnownExtension _qualCertStatements
;
131 bool _foundUnknownCriticalExtension
;
133 /* Well known certificate extensions. */
134 SecCEBasicConstraints _basicConstraints
;
135 SecCEPolicyConstraints _policyConstraints
;
136 CFDictionaryRef _policyMappings
;
137 SecCECertificatePolicies _certificatePolicies
;
139 /* If InhibitAnyPolicy extension is not present or invalid UINT32_MAX,
140 value of the SkipCerts field of the InhibitAnyPolicy extension
142 uint32_t _inhibitAnyPolicySkipCerts
;
144 /* If KeyUsage extension is not present this is 0, otherwise it's
145 the value of the extension. */
146 SecKeyUsage _keyUsage
;
148 /* OCTECTS of SubjectKeyIdentifier extensions KeyIdentifier.
149 Length = 0 if not present. */
150 DERItem _subjectKeyIdentifier
;
152 /* OCTECTS of AuthorityKeyIdentifier extensions KeyIdentifier.
153 Length = 0 if not present. */
154 DERItem _authorityKeyIdentifier
;
155 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
156 _authorityKeyIdentifierSerialNumber have non zero length if present.
157 Both are either present or absent together. */
158 DERItem _authorityKeyIdentifierIssuer
;
159 DERItem _authorityKeyIdentifierSerialNumber
;
161 /* Subject alt name extension, if present. Not malloced, it's just a
162 pointer to an element in the _extensions array. */
163 const SecCertificateExtension
*_subjectAltName
;
165 /* Parsed extension values. */
167 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
168 CFMutableArrayRef _crlDistributionPoints
;
170 /* Array of CFURLRefs containing the URI values of accessLocations of each
171 id-ad-ocsp AccessDescription in the Authority Information Access
173 CFMutableArrayRef _ocspResponders
;
175 /* Array of CFURLRefs containing the URI values of accessLocations of each
176 id-ad-caIssuers AccessDescription in the Authority Information Access
178 CFMutableArrayRef _caIssuers
;
180 /* Array of CFDataRefs containing the generalNames for permittedSubtrees
182 CFArrayRef _permittedSubtrees
;
184 /* Array of CFDataRefs containing the generalNames for excludedSubtrees
186 CFArrayRef _excludedSubtrees
;
188 CFMutableArrayRef _embeddedSCTs
;
190 /* All other (non known) extensions. The _extensions array is malloced. */
191 CFIndex _extensionCount
;
192 SecCertificateExtension
*_extensions
;
194 /* Optional cached fields. */
197 CFArrayRef _properties
;
198 CFDataRef _serialNumber
;
199 CFDataRef _normalizedIssuer
;
200 CFDataRef _normalizedSubject
;
201 CFDataRef _authorityKeyID
;
202 CFDataRef _subjectKeyID
;
204 CFDataRef _sha1Digest
;
205 CFTypeRef _keychain_item
;
206 uint8_t _isSelfSigned
;
210 #define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v);
212 SEC_CONST_DECL (kSecCertificateProductionEscrowKey
, "ProductionEscrowKey");
213 SEC_CONST_DECL (kSecCertificateProductionPCSEscrowKey
, "ProductionPCSEscrowKey");
214 SEC_CONST_DECL (kSecCertificateEscrowFileName
, "AppleESCertificates");
216 /* Public Constants for property list keys. */
217 SEC_CONST_DECL (kSecPropertyKeyType
, "type");
218 SEC_CONST_DECL (kSecPropertyKeyLabel
, "label");
219 SEC_CONST_DECL (kSecPropertyKeyLocalizedLabel
, "localized label");
220 SEC_CONST_DECL (kSecPropertyKeyValue
, "value");
222 /* Public Constants for property list values. */
223 SEC_CONST_DECL (kSecPropertyTypeWarning
, "warning");
224 SEC_CONST_DECL (kSecPropertyTypeError
, "error");
225 SEC_CONST_DECL (kSecPropertyTypeSuccess
, "success");
226 SEC_CONST_DECL (kSecPropertyTypeTitle
, "title");
227 SEC_CONST_DECL (kSecPropertyTypeSection
, "section");
228 SEC_CONST_DECL (kSecPropertyTypeData
, "data");
229 SEC_CONST_DECL (kSecPropertyTypeString
, "string");
230 SEC_CONST_DECL (kSecPropertyTypeURL
, "url");
231 SEC_CONST_DECL (kSecPropertyTypeDate
, "date");
233 /* Extension parsing routine. */
234 typedef void (*SecCertificateExtensionParser
)(SecCertificateRef certificate
,
235 const SecCertificateExtension
*extn
);
237 /* Mapping from extension OIDs (as a DERItem *) to
238 SecCertificateExtensionParser extension parsing routines. */
239 static CFDictionaryRef sExtensionParsers
;
241 /* Forward declarations of static functions. */
242 static CFStringRef
SecCertificateDescribe(CFTypeRef cf
);
243 static void SecCertificateDestroy(CFTypeRef cf
);
244 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
245 CFAbsoluteTime
*absTime
) __attribute__((__nonnull__
));
247 /* Static functions. */
248 static CF_RETURNS_RETAINED CFStringRef
SecCertificateDescribe(CFTypeRef cf
) {
249 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
250 CFStringRef subject
= SecCertificateCopySubjectSummary(certificate
);
251 CFStringRef issuer
= SecCertificateCopyIssuerSummary(certificate
);
252 CFStringRef desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
253 CFSTR("<cert(%p) s: %@ i: %@>"), certificate
, subject
, issuer
);
254 CFReleaseSafe(issuer
);
255 CFReleaseSafe(subject
);
259 static void SecCertificateDestroy(CFTypeRef cf
) {
260 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
261 if (certificate
->_certificatePolicies
.policies
)
262 free(certificate
->_certificatePolicies
.policies
);
263 CFReleaseSafe(certificate
->_policyMappings
);
264 CFReleaseSafe(certificate
->_crlDistributionPoints
);
265 CFReleaseSafe(certificate
->_ocspResponders
);
266 CFReleaseSafe(certificate
->_caIssuers
);
267 if (certificate
->_extensions
) {
268 free(certificate
->_extensions
);
270 CFReleaseSafe(certificate
->_pubKey
);
271 CFReleaseSafe(certificate
->_der_data
);
272 CFReleaseSafe(certificate
->_properties
);
273 CFReleaseSafe(certificate
->_serialNumber
);
274 CFReleaseSafe(certificate
->_normalizedIssuer
);
275 CFReleaseSafe(certificate
->_normalizedSubject
);
276 CFReleaseSafe(certificate
->_authorityKeyID
);
277 CFReleaseSafe(certificate
->_subjectKeyID
);
278 CFReleaseSafe(certificate
->_sha1Digest
);
279 CFReleaseSafe(certificate
->_keychain_item
);
280 CFReleaseSafe(certificate
->_permittedSubtrees
);
281 CFReleaseSafe(certificate
->_excludedSubtrees
);
284 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
285 SecCertificateRef cert1
= (SecCertificateRef
)cf1
;
286 SecCertificateRef cert2
= (SecCertificateRef
)cf2
;
289 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
291 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
294 /* Hash of the certificate is der length + signature length + last 4 bytes
296 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
297 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
298 size_t der_length
= certificate
->_der
.length
;
299 size_t sig_length
= certificate
->_signature
.length
;
300 size_t ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
301 CFHashCode hashCode
= 0;
302 for (; ix
< sig_length
; ++ix
)
303 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
305 return (hashCode
+ der_length
+ sig_length
);
310 /************************************************************************/
311 /************************* General Name Parsing *************************/
312 /************************************************************************/
314 GeneralName ::= CHOICE {
315 otherName [0] OtherName,
316 rfc822Name [1] IA5String,
317 dNSName [2] IA5String,
318 x400Address [3] ORAddress,
319 directoryName [4] Name,
320 ediPartyName [5] EDIPartyName,
321 uniformResourceIdentifier [6] IA5String,
322 iPAddress [7] OCTET STRING,
323 registeredID [8] OBJECT IDENTIFIER}
325 OtherName ::= SEQUENCE {
326 type-id OBJECT IDENTIFIER,
327 value [0] EXPLICIT ANY DEFINED BY type-id }
329 EDIPartyName ::= SEQUENCE {
330 nameAssigner [0] DirectoryString OPTIONAL,
331 partyName [1] DirectoryString }
333 OSStatus
SecCertificateParseGeneralNameContentProperty(DERTag tag
,
334 const DERItem
*generalNameContent
,
335 void *context
, parseGeneralNameCallback callback
) {
337 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
338 return callback(context
, GNT_OtherName
, generalNameContent
);
339 case ASN1_CONTEXT_SPECIFIC
| 1:
340 return callback(context
, GNT_RFC822Name
, generalNameContent
);
341 case ASN1_CONTEXT_SPECIFIC
| 2:
342 return callback(context
, GNT_DNSName
, generalNameContent
);
343 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
344 return callback(context
, GNT_X400Address
, generalNameContent
);
345 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
346 return callback(context
, GNT_DirectoryName
, generalNameContent
);
347 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
348 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
349 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
351 /* Technically I don't think this is valid, but there are certs out
352 in the wild that use a constructed IA5String. In particular the
353 VeriSign Time Stamping Authority CA.cer does this. */
354 DERDecodedInfo uriContent
;
355 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
356 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
357 return callback(context
, GNT_URI
, &uriContent
.content
);
359 case ASN1_CONTEXT_SPECIFIC
| 6:
360 return callback(context
, GNT_URI
, generalNameContent
);
361 case ASN1_CONTEXT_SPECIFIC
| 7:
362 return callback(context
, GNT_IPAddress
, generalNameContent
);
363 case ASN1_CONTEXT_SPECIFIC
| 8:
364 return callback(context
, GNT_RegisteredID
, generalNameContent
);
369 return errSecInvalidCertificate
;
372 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
373 void *context
, parseGeneralNameCallback callback
) {
375 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
376 require_noerr_quiet(drtn
, badDER
);
377 DERDecodedInfo generalNameContent
;
378 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
380 OSStatus status
= SecCertificateParseGeneralNameContentProperty(
381 generalNameContent
.tag
, &generalNameContent
.content
, context
,
386 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
387 return errSecSuccess
;
390 return errSecInvalidCertificate
;
393 OSStatus
SecCertificateParseGeneralNames(const DERItem
*generalNames
, void *context
,
394 parseGeneralNameCallback callback
) {
395 DERDecodedInfo generalNamesContent
;
396 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
397 require_noerr_quiet(drtn
, badDER
);
398 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
399 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
402 return errSecInvalidCertificate
;
408 GeneralName ::= CHOICE {
409 otherName [0] OtherName,
410 rfc822Name [1] IA5String,
411 dNSName [2] IA5String,
412 x400Address [3] ORAddress,
413 directoryName [4] Name,
414 ediPartyName [5] EDIPartyName,
415 uniformResourceIdentifier [6] IA5String,
416 iPAddress [7] OCTET STRING,
417 registeredID [8] OBJECT IDENTIFIER}
419 EDIPartyName ::= SEQUENCE {
420 nameAssigner [0] DirectoryString OPTIONAL,
421 partyName [1] DirectoryString }
423 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
424 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
426 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
427 generalName
->nameType
= GNT_OtherName
;
428 generalName
->berEncoded
= true;
429 generalName
->name
= *generalNameContent
;
431 case ASN1_CONTEXT_SPECIFIC
| 1:
433 generalName
->nameType
= GNT_RFC822Name
;
434 generalName
->berEncoded
= false;
435 generalName
->name
= *generalNameContent
;
437 case ASN1_CONTEXT_SPECIFIC
| 2:
439 generalName
->nameType
= GNT_DNSName
;
440 generalName
->berEncoded
= false;
441 generalName
->name
= *generalNameContent
;
443 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
444 generalName
->nameType
= GNT_X400Address
;
445 generalName
->berEncoded
= true;
446 generalName
->name
= *generalNameContent
;
448 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
449 generalName
->nameType
= GNT_DirectoryName
;
450 generalName
->berEncoded
= true;
451 generalName
->name
= *generalNameContent
;
453 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
454 generalName
->nameType
= GNT_EdiPartyName
;
455 generalName
->berEncoded
= true;
456 generalName
->name
= *generalNameContent
;
458 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
460 /* Technically I don't think this is valid, but there are certs out
461 in the wild that use a constructed IA5String. In particular the
462 VeriSign Time Stamping Authority CA.cer does this. */
463 DERDecodedInfo decoded
;
464 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
465 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
466 generalName
->nameType
= GNT_URI
;
467 generalName
->berEncoded
= false;
468 generalName
->name
= decoded
.content
;
471 case ASN1_CONTEXT_SPECIFIC
| 6:
472 generalName
->nameType
= GNT_URI
;
473 generalName
->berEncoded
= false;
474 generalName
->name
= *generalNameContent
;
476 case ASN1_CONTEXT_SPECIFIC
| 7:
477 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
478 8 octects, addr/mask for ipv6 it's 32. */
479 generalName
->nameType
= GNT_IPAddress
;
480 generalName
->berEncoded
= false;
481 generalName
->name
= *generalNameContent
;
483 case ASN1_CONTEXT_SPECIFIC
| 8:
484 /* name is the content of an OID. */
485 generalName
->nameType
= GNT_RegisteredID
;
486 generalName
->berEncoded
= false;
487 generalName
->name
= *generalNameContent
;
493 return errSecSuccess
;
495 return errSecInvalidCertificate
;
499 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
501 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
502 CFIndex
*count
, SecCEGeneralName
**name
) {
503 SecCEGeneralName
*generalNames
= NULL
;
505 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
506 require_noerr_quiet(drtn
, badDER
);
507 DERDecodedInfo generalNameContent
;
508 CFIndex generalNamesCount
= 0;
509 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
513 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
515 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
517 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
519 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
521 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
522 &generalNameContent
.content
, &generalNames
[ix
])) {
527 *count
= generalNamesCount
;
528 *name
= generalNames
;
529 return errSecSuccess
;
534 return errSecInvalidCertificate
;
537 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
538 CFIndex
*count
, SecCEGeneralName
**name
) {
539 DERDecodedInfo generalNamesContent
;
540 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
541 require_noerr_quiet(drtn
, badDER
);
542 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
544 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
545 return errSecSuccess
;
547 return errSecInvalidCertificate
;
551 /************************************************************************/
552 /************************** X.509 Name Parsing **************************/
553 /************************************************************************/
555 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
556 const DERItem
*value
, CFIndex rdnIX
);
558 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
559 parseX501NameCallback callback
) {
561 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
562 require_noerr_quiet(drtn
, badDER
);
563 DERDecodedInfo atvContent
;
565 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
566 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
567 DERAttributeTypeAndValue atv
;
568 drtn
= DERParseSequenceContent(&atvContent
.content
,
569 DERNumAttributeTypeAndValueItemSpecs
,
570 DERAttributeTypeAndValueItemSpecs
,
572 require_noerr_quiet(drtn
, badDER
);
573 require_quiet(atv
.type
.length
!= 0, badDER
);
574 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++);
578 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
580 return errSecSuccess
;
582 return errSecInvalidCertificate
;
585 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
586 parseX501NameCallback callback
) {
588 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
589 require_noerr_quiet(drtn
, badDER
);
590 DERDecodedInfo currDecoded
;
591 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
592 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
593 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
598 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
600 return errSecSuccess
;
603 return errSecInvalidCertificate
;
606 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
607 parseX501NameCallback callback
) {
608 DERDecodedInfo x501NameContent
;
609 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
610 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
611 return errSecInvalidCertificate
;
613 return parseX501NameContent(&x501NameContent
.content
, context
,
618 /************************************************************************/
619 /********************** Extension Parsing Routines **********************/
620 /************************************************************************/
622 static void SecCEPSubjectKeyIdentifier(SecCertificateRef certificate
,
623 const SecCertificateExtension
*extn
) {
624 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
625 DERDecodedInfo keyIdentifier
;
626 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
627 require_noerr_quiet(drtn
, badDER
);
628 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
629 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
633 secwarning("Invalid SubjectKeyIdentifier Extension");
636 static void SecCEPKeyUsage(SecCertificateRef certificate
,
637 const SecCertificateExtension
*extn
) {
638 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
639 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
640 DERDecodedInfo bitStringContent
;
641 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
642 require_noerr_quiet(drtn
, badDER
);
643 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
644 DERSize len
= bitStringContent
.content
.length
- 1;
645 require_quiet(len
== 1 || len
== 2, badDER
);
646 DERByte numUnusedBits
= bitStringContent
.content
.data
[0];
647 require_quiet(numUnusedBits
< 8, badDER
);
648 /* Flip the bits in the bit string so the first bit in the lsb. */
649 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
650 uint_fast16_t value
= bitStringContent
.content
.data
[1];
653 value
= (value
<< 8) + bitStringContent
.content
.data
[2];
659 for (ix
= 0; ix
< bits
; ++ix
) {
665 certificate
->_keyUsage
= keyUsage
;
668 certificate
->_keyUsage
= kSecKeyUsageUnspecified
;
671 static void SecCEPPrivateKeyUsagePeriod(SecCertificateRef certificate
,
672 const SecCertificateExtension
*extn
) {
673 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
676 static void SecCEPSubjectAltName(SecCertificateRef certificate
,
677 const SecCertificateExtension
*extn
) {
678 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
679 certificate
->_subjectAltName
= extn
;
682 static void SecCEPIssuerAltName(SecCertificateRef certificate
,
683 const SecCertificateExtension
*extn
) {
684 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
687 static void SecCEPBasicConstraints(SecCertificateRef certificate
,
688 const SecCertificateExtension
*extn
) {
689 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
690 DERBasicConstraints basicConstraints
;
691 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
692 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
693 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
694 require_noerr_quiet(DERParseBoolean(&basicConstraints
.cA
, false,
695 &certificate
->_basicConstraints
.isCA
), badDER
);
696 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
697 require_noerr_quiet(DERParseInteger(
698 &basicConstraints
.pathLenConstraint
,
699 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
700 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
702 certificate
->_basicConstraints
.present
= true;
703 certificate
->_basicConstraints
.critical
= extn
->critical
;
706 certificate
->_basicConstraints
.present
= false;
707 secwarning("Invalid BasicConstraints Extension");
712 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
714 * NameConstraints ::= SEQUENCE {
715 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
716 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
718 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
720 * GeneralSubtree ::= SEQUENCE {
722 * minimum [0] BaseDistance DEFAULT 0,
723 * maximum [1] BaseDistance OPTIONAL }
725 * BaseDistance ::= INTEGER (0..MAX)
727 static DERReturn
parseGeneralSubtrees(DERItem
*derSubtrees
, CFArrayRef
*generalSubtrees
) {
728 CFMutableArrayRef gs
= NULL
;
730 DERReturn drtn
= DERDecodeSeqContentInit(derSubtrees
, &gsSeq
);
731 require_noerr_quiet(drtn
, badDER
);
732 DERDecodedInfo gsContent
;
733 require_quiet(gs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
734 &kCFTypeArrayCallBacks
),
736 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
737 DERGeneralSubtree derGS
;
738 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
739 drtn
= DERParseSequenceContent(&gsContent
.content
,
740 DERNumGeneralSubtreeItemSpecs
,
741 DERGeneralSubtreeItemSpecs
,
742 &derGS
, sizeof(derGS
));
743 require_noerr_quiet(drtn
, badDER
);
746 * Within this profile, the minimum and maximum fields are not used with
747 * any name forms, thus, the minimum MUST be zero, and maximum MUST be
750 * Because minimum DEFAULT 0, absence equivalent to present and 0.
752 if (derGS
.minimum
.length
) {
754 require_noerr_quiet(DERParseInteger(&derGS
.minimum
, &minimum
),
756 require_quiet(minimum
== 0, badDER
);
758 require_quiet(derGS
.maximum
.length
== 0, badDER
);
759 require_quiet(derGS
.generalName
.length
!= 0, badDER
);
761 CFDataRef generalName
= NULL
;
762 require_quiet(generalName
= CFDataCreate(kCFAllocatorDefault
,
763 derGS
.generalName
.data
,
764 derGS
.generalName
.length
),
766 CFArrayAppendValue(gs
, generalName
);
767 CFReleaseNull(generalName
);
770 *generalSubtrees
= gs
;
772 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
777 secdebug("cert","failed to parse GeneralSubtrees");
781 static void SecCEPNameConstraints(SecCertificateRef certificate
,
782 const SecCertificateExtension
*extn
) {
783 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
784 DERNameConstraints nc
;
786 drtn
= DERParseSequence(&extn
->extnValue
,
787 DERNumNameConstraintsItemSpecs
,
788 DERNameConstraintsItemSpecs
,
790 require_noerr_quiet(drtn
, badDER
);
791 if (nc
.permittedSubtrees
.length
) {
792 require_noerr_quiet(parseGeneralSubtrees(&nc
.permittedSubtrees
, &certificate
->_permittedSubtrees
), badDER
);
794 if (nc
.excludedSubtrees
.length
) {
795 require_noerr_quiet(parseGeneralSubtrees(&nc
.excludedSubtrees
, &certificate
->_excludedSubtrees
), badDER
);
800 secdebug("cert", "failed to parse Name Constraints extension");
803 static void SecCEPCrlDistributionPoints(SecCertificateRef certificate
,
804 const SecCertificateExtension
*extn
) {
805 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
809 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
811 PolicyInformation ::= SEQUENCE {
812 policyIdentifier CertPolicyId,
813 policyQualifiers SEQUENCE SIZE (1..MAX) OF
814 PolicyQualifierInfo OPTIONAL }
816 CertPolicyId ::= OBJECT IDENTIFIER
818 PolicyQualifierInfo ::= SEQUENCE {
819 policyQualifierId PolicyQualifierId,
820 qualifier ANY DEFINED BY policyQualifierId }
822 /* maximum number of policies of 8192 seems more than adequate */
823 #define MAX_CERTIFICATE_POLICIES 8192
824 static void SecCEPCertificatePolicies(SecCertificateRef certificate
,
825 const SecCertificateExtension
*extn
) {
826 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
829 SecCEPolicyInformation
*policies
= NULL
;
830 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
831 require_noerr_quiet(drtn
, badDER
);
832 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
833 DERDecodedInfo piContent
;
834 DERSize policy_count
= 0;
835 while ((policy_count
< MAX_CERTIFICATE_POLICIES
) &&
836 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
837 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
840 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
841 policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
)
842 * (policy_count
> 0 ? policy_count
: 1));
843 DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
844 DERSize policy_ix
= 0;
845 while ((policy_ix
< (policy_count
> 0 ? policy_count
: 1)) &&
846 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
847 DERPolicyInformation pi
;
848 drtn
= DERParseSequenceContent(&piContent
.content
,
849 DERNumPolicyInformationItemSpecs
,
850 DERPolicyInformationItemSpecs
,
852 require_noerr_quiet(drtn
, badDER
);
853 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
854 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
856 certificate
->_certificatePolicies
.present
= true;
857 certificate
->_certificatePolicies
.critical
= extn
->critical
;
858 certificate
->_certificatePolicies
.numPolicies
= policy_count
;
859 certificate
->_certificatePolicies
.policies
= policies
;
864 certificate
->_certificatePolicies
.present
= false;
865 secwarning("Invalid CertificatePolicies Extension");
869 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
871 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
872 issuerDomainPolicy CertPolicyId,
873 subjectDomainPolicy CertPolicyId }
876 static void SecCEPPolicyMappings(SecCertificateRef certificate
,
877 const SecCertificateExtension
*extn
) {
878 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
881 SecCEPolicyMapping
*mappings
= NULL
;
882 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
883 require_noerr_quiet(drtn
, badDER
);
884 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
885 DERDecodedInfo pmContent
;
886 DERSize mapping_count
= 0;
887 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
888 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
891 mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
893 DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
894 DERSize mapping_ix
= 0;
895 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
897 drtn
= DERParseSequenceContent(&pmContent
.content
,
898 DERNumPolicyMappingItemSpecs
,
899 DERPolicyMappingItemSpecs
,
901 require_noerr_quiet(drtn
, badDER
);
902 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
903 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
905 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
906 certificate
->_policyMappings
.present
= true;
907 certificate
->_policyMappings
.critical
= extn
->critical
;
908 certificate
->_policyMappings
.numMappings
= mapping_count
;
909 certificate
->_policyMappings
.mappings
= mappings
;
914 CFReleaseSafe(mappings
);
915 certificate
->_policyMappings
.present
= false;
916 secwarning("Invalid CertificatePolicies Extension");
919 static void SecCEPPolicyMappings(SecCertificateRef certificate
,
920 const SecCertificateExtension
*extn
) {
921 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
924 CFMutableDictionaryRef mappings
= NULL
;
925 CFDataRef idp
= NULL
, sdp
= NULL
;
926 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
927 require_noerr_quiet(drtn
, badDER
);
928 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
929 DERDecodedInfo pmContent
;
930 require_quiet(mappings
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
931 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
),
933 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
934 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
936 drtn
= DERParseSequenceContent(&pmContent
.content
,
937 DERNumPolicyMappingItemSpecs
,
938 DERPolicyMappingItemSpecs
,
940 require_noerr_quiet(drtn
, badDER
);
941 require_quiet(idp
= CFDataCreate(kCFAllocatorDefault
,
942 pm
.issuerDomainPolicy
.data
, pm
.issuerDomainPolicy
.length
), badDER
);
943 require_quiet(sdp
= CFDataCreate(kCFAllocatorDefault
,
944 pm
.subjectDomainPolicy
.data
, pm
.subjectDomainPolicy
.length
), badDER
);
945 CFMutableArrayRef sdps
=
946 (CFMutableArrayRef
)CFDictionaryGetValue(mappings
, idp
);
948 CFArrayAppendValue(sdps
, sdp
);
950 require_quiet(sdps
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
951 &kCFTypeArrayCallBacks
), badDER
);
952 CFDictionarySetValue(mappings
, idp
, sdps
);
958 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
959 certificate
->_policyMappings
= mappings
;
964 CFReleaseSafe(mappings
);
965 certificate
->_policyMappings
= NULL
;
966 secwarning("Invalid CertificatePolicies Extension");
971 AuthorityKeyIdentifier ::= SEQUENCE {
972 keyIdentifier [0] KeyIdentifier OPTIONAL,
973 authorityCertIssuer [1] GeneralNames OPTIONAL,
974 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
975 -- authorityCertIssuer and authorityCertSerialNumber MUST both
976 -- be present or both be absent
978 KeyIdentifier ::= OCTET STRING
980 static void SecCEPAuthorityKeyIdentifier(SecCertificateRef certificate
,
981 const SecCertificateExtension
*extn
) {
982 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
983 DERAuthorityKeyIdentifier akid
;
985 drtn
= DERParseSequence(&extn
->extnValue
,
986 DERNumAuthorityKeyIdentifierItemSpecs
,
987 DERAuthorityKeyIdentifierItemSpecs
,
988 &akid
, sizeof(akid
));
989 require_noerr_quiet(drtn
, badDER
);
990 if (akid
.keyIdentifier
.length
) {
991 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
993 if (akid
.authorityCertIssuer
.length
||
994 akid
.authorityCertSerialNumber
.length
) {
995 require_quiet(akid
.authorityCertIssuer
.length
&&
996 akid
.authorityCertSerialNumber
.length
, badDER
);
997 /* Perhaps put in a subsection called Authority Certificate Issuer. */
998 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
999 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
1004 secwarning("Invalid AuthorityKeyIdentifier Extension");
1007 static void SecCEPPolicyConstraints(SecCertificateRef certificate
,
1008 const SecCertificateExtension
*extn
) {
1009 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1010 DERPolicyConstraints pc
;
1012 drtn
= DERParseSequence(&extn
->extnValue
,
1013 DERNumPolicyConstraintsItemSpecs
,
1014 DERPolicyConstraintsItemSpecs
,
1016 require_noerr_quiet(drtn
, badDER
);
1017 if (pc
.requireExplicitPolicy
.length
) {
1018 require_noerr_quiet(DERParseInteger(
1019 &pc
.requireExplicitPolicy
,
1020 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
1021 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
1023 if (pc
.inhibitPolicyMapping
.length
) {
1024 require_noerr_quiet(DERParseInteger(
1025 &pc
.inhibitPolicyMapping
,
1026 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
1027 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
1030 certificate
->_policyConstraints
.present
= true;
1031 certificate
->_policyConstraints
.critical
= extn
->critical
;
1035 certificate
->_policyConstraints
.present
= false;
1036 secwarning("Invalid PolicyConstraints Extension");
1039 static void SecCEPExtendedKeyUsage(SecCertificateRef certificate
,
1040 const SecCertificateExtension
*extn
) {
1041 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1045 InhibitAnyPolicy ::= SkipCerts
1047 SkipCerts ::= INTEGER (0..MAX)
1049 static void SecCEPInhibitAnyPolicy(SecCertificateRef certificate
,
1050 const SecCertificateExtension
*extn
) {
1051 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1052 require_noerr_quiet(DERParseInteger(
1054 &certificate
->_inhibitAnyPolicySkipCerts
), badDER
);
1057 certificate
->_inhibitAnyPolicySkipCerts
= UINT32_MAX
;
1058 secwarning("Invalid InhibitAnyPolicy Extension");
1062 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
1064 AuthorityInfoAccessSyntax ::=
1065 SEQUENCE SIZE (1..MAX) OF AccessDescription
1067 AccessDescription ::= SEQUENCE {
1068 accessMethod OBJECT IDENTIFIER,
1069 accessLocation GeneralName }
1071 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
1073 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
1075 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
1077 static void SecCEPAuthorityInfoAccess(SecCertificateRef certificate
,
1078 const SecCertificateExtension
*extn
) {
1079 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1082 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
1083 require_noerr_quiet(drtn
, badDER
);
1084 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1085 DERDecodedInfo adContent
;
1086 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
1087 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1088 DERAccessDescription ad
;
1089 drtn
= DERParseSequenceContent(&adContent
.content
,
1090 DERNumAccessDescriptionItemSpecs
,
1091 DERAccessDescriptionItemSpecs
,
1093 require_noerr_quiet(drtn
, badDER
);
1094 CFMutableArrayRef
*urls
;
1095 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
1096 urls
= &certificate
->_ocspResponders
;
1097 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
1098 urls
= &certificate
->_caIssuers
;
1102 DERDecodedInfo generalNameContent
;
1103 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
1104 require_noerr_quiet(drtn
, badDER
);
1105 switch (generalNameContent
.tag
) {
1107 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
1108 /* Technically I don't think this is valid, but there are certs out
1109 in the wild that use a constructed IA5String. In particular the
1110 VeriSign Time Stamping Authority CA.cer does this. */
1112 case ASN1_CONTEXT_SPECIFIC
| 6:
1114 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1115 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
1116 kCFStringEncodingASCII
, NULL
);
1119 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1120 CFArrayAppendValue(*urls
, url
);
1126 secdebug("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02x v: %.*s",
1127 generalNameContent
.tag
, (int) generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1132 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1135 secdebug("cert", "failed to parse Authority Information Access extension");
1138 /* Apple Worldwide Developer Relations Certificate Authority subject name.
1139 * This is a DER sequence with the leading tag and length bytes removed,
1140 * to match what tbsCert.issuer contains.
1142 static const unsigned char Apple_WWDR_CA_Subject_Name
[]={
1143 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,
1144 0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0A,0x41,0x70,0x70,0x6C,0x65,
1145 0x20,0x49,0x6E,0x63,0x2E,0x31,0x2C,0x30,0x2A,0x06,0x03,0x55,0x04,0x0B,0x0C,0x23,
1146 0x41,0x70,0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,
1147 0x44,0x65,0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,
1148 0x6F,0x6E,0x73,0x31,0x44,0x30,0x42,0x06,0x03,0x55,0x04,0x03,0x0C,0x3B,0x41,0x70,
1149 0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,0x44,0x65,
1150 0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,0x6F,0x6E,
1151 0x73,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,
1152 0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79
1155 static void checkForMissingRevocationInfo(SecCertificateRef certificate
) {
1157 certificate
->_crlDistributionPoints
||
1158 certificate
->_ocspResponders
) {
1159 /* We already have an OCSP or CRL URI (or no cert) */
1162 /* Specify an appropriate OCSP responder if we recognize the issuer. */
1163 CFURLRef url
= NULL
;
1164 if (sizeof(Apple_WWDR_CA_Subject_Name
) == certificate
->_issuer
.length
&&
1165 !memcmp(certificate
->_issuer
.data
, Apple_WWDR_CA_Subject_Name
,
1166 sizeof(Apple_WWDR_CA_Subject_Name
))) {
1167 const char *WWDR_OCSP_URI
= "http://ocsp.apple.com/ocsp-wwdr01";
1168 url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1169 (const UInt8
*)WWDR_OCSP_URI
, strlen(WWDR_OCSP_URI
),
1170 kCFStringEncodingASCII
, NULL
);
1173 CFMutableArrayRef
*urls
= &certificate
->_ocspResponders
;
1174 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1175 CFArrayAppendValue(*urls
, url
);
1180 static void SecCEPSubjectInfoAccess(SecCertificateRef certificate
,
1181 const SecCertificateExtension
*extn
) {
1182 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1185 static void SecCEPNetscapeCertType(SecCertificateRef certificate
,
1186 const SecCertificateExtension
*extn
) {
1187 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1190 static void SecCEPEntrustVersInfo(SecCertificateRef certificate
,
1191 const SecCertificateExtension
*extn
) {
1192 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1195 static void SecCEPEscrowMarker(SecCertificateRef certificate
,
1196 const SecCertificateExtension
*extn
) {
1197 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1201 /* Dictionary key callback for comparing to DERItems. */
1202 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1203 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1206 /* Dictionary key callback calculating the hash of a DERItem. */
1207 static CFHashCode
SecDERItemHash(const void *value
) {
1208 const DERItem
*derItem
= (const DERItem
*)value
;
1209 CFHashCode hash
= derItem
->length
;
1210 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1211 for (; ix
< derItem
->length
; ++ix
) {
1212 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1218 /* Dictionary key callbacks using the above 2 functions. */
1219 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1223 NULL
, /* copyDescription */
1224 SecDERItemEqual
, /* equal */
1225 SecDERItemHash
/* hash */
1228 static void SecCertificateInitializeExtensionParsers(void) {
1229 /* Build a dictionary that maps from extension OIDs to callback functions
1230 which can parse the extension of the type given. */
1231 static const void *extnOIDs
[] = {
1232 &oidSubjectKeyIdentifier
,
1234 &oidPrivateKeyUsagePeriod
,
1237 &oidBasicConstraints
,
1238 &oidNameConstraints
,
1239 &oidCrlDistributionPoints
,
1240 &oidCertificatePolicies
,
1242 &oidAuthorityKeyIdentifier
,
1243 &oidPolicyConstraints
,
1244 &oidExtendedKeyUsage
,
1245 &oidInhibitAnyPolicy
,
1246 &oidAuthorityInfoAccess
,
1247 &oidSubjectInfoAccess
,
1248 &oidNetscapeCertType
,
1249 &oidEntrustVersInfo
,
1250 &oidApplePolicyEscrowService
1252 static const void *extnParsers
[] = {
1253 SecCEPSubjectKeyIdentifier
,
1255 SecCEPPrivateKeyUsagePeriod
,
1256 SecCEPSubjectAltName
,
1257 SecCEPIssuerAltName
,
1258 SecCEPBasicConstraints
,
1259 SecCEPNameConstraints
,
1260 SecCEPCrlDistributionPoints
,
1261 SecCEPCertificatePolicies
,
1262 SecCEPPolicyMappings
,
1263 SecCEPAuthorityKeyIdentifier
,
1264 SecCEPPolicyConstraints
,
1265 SecCEPExtendedKeyUsage
,
1266 SecCEPInhibitAnyPolicy
,
1267 SecCEPAuthorityInfoAccess
,
1268 SecCEPSubjectInfoAccess
,
1269 SecCEPNetscapeCertType
,
1270 SecCEPEntrustVersInfo
,
1273 sExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1274 extnParsers
, array_size(extnOIDs
),
1275 &SecDERItemKeyCallBacks
, NULL
);
1278 CFGiblisWithFunctions(SecCertificate
, NULL
, NULL
, SecCertificateDestroy
, SecCertificateEqual
, SecCertificateHash
, NULL
, SecCertificateDescribe
, NULL
, NULL
, ^{
1279 SecCertificateInitializeExtensionParsers();
1282 static bool isAppleExtensionOID(const DERItem
*extnID
)
1284 static const uint8_t appleExtension
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x06 };
1285 return (extnID
&& extnID
->data
&&
1286 extnID
->length
> sizeof(appleExtension
) &&
1287 !memcmp(extnID
->data
, appleExtension
, sizeof(appleExtension
)));
1290 /* Given the contents of an X.501 Name return the contents of a normalized
1292 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1293 const DERItem
*x501name
) {
1294 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1295 CFIndex length
= x501name
->length
;
1296 CFDataSetLength(result
, length
);
1297 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1300 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1302 require_noerr_quiet(drtn
, badDER
);
1305 /* Always points to last rdn tag. */
1306 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1307 /* Offset relative to base of current rdn set tag. */
1308 CFIndex rdnTagLocation
= 0;
1309 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1310 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1311 /* We don't allow empty RDNs. */
1312 require_quiet(rdn
.content
.length
!= 0, badDER
);
1313 /* Length of the tag and length of the current rdn. */
1314 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1315 CFIndex rdnContentLength
= rdn
.content
.length
;
1316 /* Copy the tag and length of the RDN. */
1317 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1320 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1321 require_quiet(drtn
== DR_Success
, badDER
);
1324 /* Always points to tag of current atv sequence. */
1325 const DERByte
*atvTag
= atvSeq
.nextItem
;
1326 /* Offset relative to base of current atv sequence tag. */
1327 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1328 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1329 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1330 /* Length of the tag and length of the current atv. */
1331 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1332 CFIndex atvContentLength
= atv
.content
.length
;
1333 /* Copy the tag and length of the atv and the atv itself. */
1334 memcpy(base
+ atvTagLocation
, atvTag
,
1335 atvTLLength
+ atv
.content
.length
);
1337 /* Now decode the atv sequence. */
1338 DERAttributeTypeAndValue atvPair
;
1339 drtn
= DERParseSequenceContent(&atv
.content
,
1340 DERNumAttributeTypeAndValueItemSpecs
,
1341 DERAttributeTypeAndValueItemSpecs
,
1342 &atvPair
, sizeof(atvPair
));
1343 require_noerr_quiet(drtn
, badDER
);
1344 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1345 DERDecodedInfo value
;
1346 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1347 require_noerr_quiet(drtn
, badDER
);
1349 /* (c) attribute values in PrintableString are not case sensitive
1350 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1352 (d) attribute values in PrintableString are compared after
1353 removing leading and trailing white space and converting internal
1354 substrings of one or more consecutive white space characters to a
1356 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1357 /* Offset relative to base of current value tag. */
1358 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1359 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1360 CFIndex valueContentLength
= value
.content
.length
;
1362 /* Now copy all the bytes, but convert to upper case while
1363 doing so and convert multiple whitespace chars into a
1365 bool lastWasBlank
= false;
1366 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1367 CFIndex valueCurrentLocation
= valueLocation
;
1369 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1370 UInt8 ch
= value
.content
.data
[ix
];
1375 /* Don't insert a space for first character
1377 if (valueCurrentLocation
> valueLocation
) {
1378 base
[valueCurrentLocation
++] = ' ';
1380 lastWasBlank
= true;
1383 lastWasBlank
= false;
1384 if ('a' <= ch
&& ch
<= 'z') {
1385 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1387 base
[valueCurrentLocation
++] = ch
;
1391 /* Finally if lastWasBlank remove the trailing space. */
1392 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1393 valueCurrentLocation
--;
1395 /* Adjust content length to normalized length. */
1396 valueContentLength
= valueCurrentLocation
- valueLocation
;
1398 /* Number of bytes by which the length should be shorted. */
1399 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1400 if (lengthDiff
== 0) {
1401 /* Easy case no need to adjust lengths. */
1403 /* Hard work we need to go back and fix up length fields
1405 1) The value itself.
1406 2) The ATV Sequence containing type/value
1407 3) The RDN Set containing one or more atv pairs.
1411 /* Step 1 fix up length of value. */
1412 /* Length of value tag and length minus the tag. */
1413 DERSize newValueTLLength
= valueTLLength
- 1;
1414 drtn
= DEREncodeLength(valueContentLength
,
1415 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1416 require(drtn
== DR_Success
, badDER
);
1417 /* Add the length of the tag back in. */
1419 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1421 /* The size of the length field changed, let's slide
1422 the value back by valueLLDiff bytes. */
1423 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1424 base
+ valueTagLocation
+ valueTLLength
,
1425 valueContentLength
);
1426 /* The length diff for the enclosing object. */
1427 lengthDiff
+= valueLLDiff
;
1430 /* Step 2 fix up length of the enclosing ATV Sequence. */
1431 atvContentLength
-= lengthDiff
;
1432 DERSize newATVTLLength
= atvTLLength
- 1;
1433 drtn
= DEREncodeLength(atvContentLength
,
1434 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1435 require(drtn
== DR_Success
, badDER
);
1436 /* Add the length of the tag back in. */
1438 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1440 /* The size of the length field changed, let's slide
1441 the value back by valueLLDiff bytes. */
1442 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1443 base
+ atvTagLocation
+ atvTLLength
,
1445 /* The length diff for the enclosing object. */
1446 lengthDiff
+= atvLLDiff
;
1447 atvTLLength
= newATVTLLength
;
1450 /* Step 3 fix up length of enclosing RDN Set. */
1451 rdnContentLength
-= lengthDiff
;
1452 DERSize newRDNTLLength
= rdnTLLength
- 1;
1453 drtn
= DEREncodeLength(rdnContentLength
,
1454 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1455 require_quiet(drtn
== DR_Success
, badDER
);
1456 /* Add the length of the tag back in. */
1458 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1460 /* The size of the length field changed, let's slide
1461 the value back by valueLLDiff bytes. */
1462 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1463 base
+ rdnTagLocation
+ rdnTLLength
,
1465 /* The length diff for the enclosing object. */
1466 lengthDiff
+= rdnLLDiff
;
1467 rdnTLLength
= newRDNTLLength
;
1469 /* Adjust the locations that might have changed due to
1471 atvTagLocation
-= rdnLLDiff
;
1473 (void) lengthDiff
; // No next object, silence analyzer
1476 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1477 atvTag
= atvSeq
.nextItem
;
1479 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1480 rdnTag
= rdnSeq
.nextItem
;
1482 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1483 /* Truncate the result to the proper length. */
1484 CFDataSetLength(result
, rdnTagLocation
);
1493 CFDataRef
SecDistinguishedNameCopyNormalizedContent(CFDataRef distinguished_name
)
1495 const DERItem name
= { (unsigned char *)CFDataGetBytePtr(distinguished_name
), CFDataGetLength(distinguished_name
) };
1496 DERDecodedInfo content
;
1497 /* Decode top level sequence into DERItem */
1498 if (!DERDecodeItem(&name
, &content
) && (content
.tag
== ASN1_CONSTR_SEQUENCE
))
1499 return createNormalizedX501Name(kCFAllocatorDefault
, &content
.content
);
1503 /* AUDIT[securityd]:
1504 certificate->_der is a caller provided data of any length (might be 0).
1506 Top level certificate decode.
1508 static bool SecCertificateParse(SecCertificateRef certificate
)
1513 require_quiet(certificate
, badCert
);
1514 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1516 /* top level decode */
1517 DERSignedCertCrl signedCert
;
1518 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1519 DERSignedCertCrlItemSpecs
, &signedCert
,
1520 sizeof(signedCert
));
1521 require_noerr_quiet(drtn
, badCert
);
1522 /* Store tbs since we need to digest it for verification later on. */
1523 certificate
->_tbs
= signedCert
.tbs
;
1525 /* decode the TBSCert - it was saved in full DER form */
1527 drtn
= DERParseSequence(&signedCert
.tbs
,
1528 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1529 &tbsCert
, sizeof(tbsCert
));
1530 require_noerr_quiet(drtn
, badCert
);
1532 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1533 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1534 of the params field. */
1535 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1536 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1537 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1538 require_noerr_quiet(drtn
, badCert
);
1540 /* The contents of signedCert.sig is a bit string whose contents
1541 are the signature itself. */
1542 DERByte numUnusedBits
;
1543 drtn
= DERParseBitString(&signedCert
.sig
,
1544 &certificate
->_signature
, &numUnusedBits
);
1545 require_noerr_quiet(drtn
, badCert
);
1547 /* Now decode the tbsCert. */
1549 /* First we turn the optional version into an int. */
1550 if (tbsCert
.version
.length
) {
1551 DERDecodedInfo decoded
;
1552 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1553 require_noerr_quiet(drtn
, badCert
);
1554 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1555 require_quiet(decoded
.content
.length
== 1, badCert
);
1556 certificate
->_version
= decoded
.content
.data
[0];
1557 require_quiet(certificate
->_version
> 0, badCert
);
1558 require_quiet(certificate
->_version
< 3, badCert
);
1560 certificate
->_version
= 0;
1563 /* The serial number is in the tbsCert.serialNum - it was saved in
1564 INTEGER form without the tag and length. */
1565 certificate
->_serialNum
= tbsCert
.serialNum
;
1566 certificate
->_serialNumber
= CFDataCreate(allocator
,
1567 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1569 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1570 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1571 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1572 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1573 require_noerr_quiet(drtn
, badCert
);
1575 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1576 and length fields. */
1577 certificate
->_issuer
= tbsCert
.issuer
;
1578 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1581 /* sequence we're given: decode the tbsCerts Validity sequence. */
1582 DERValidity validity
;
1583 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1584 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1585 &validity
, sizeof(validity
));
1586 require_noerr_quiet(drtn
, badCert
);
1587 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1588 &certificate
->_notBefore
), badCert
);
1589 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1590 &certificate
->_notAfter
), badCert
);
1592 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1593 and length fields. */
1594 certificate
->_subject
= tbsCert
.subject
;
1595 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1598 /* Keep the SPKI around for CT */
1599 certificate
->_subjectPublicKeyInfo
= tbsCert
.subjectPubKey
;
1601 /* sequence we're given: encoded DERSubjPubKeyInfo - it was saved in full DER form */
1602 DERSubjPubKeyInfo pubKeyInfo
;
1603 drtn
= DERParseSequence(&tbsCert
.subjectPubKey
,
1604 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1605 &pubKeyInfo
, sizeof(pubKeyInfo
));
1606 require_noerr_quiet(drtn
, badCert
);
1608 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1609 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1610 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1611 &certificate
->_algId
, sizeof(certificate
->_algId
));
1612 require_noerr_quiet(drtn
, badCert
);
1614 /* Now we can figure out the key's algorithm id and params based on
1615 certificate->_algId.oid. */
1617 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1618 are a PKCS1 format RSA key. */
1619 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1620 &certificate
->_pubKeyDER
, &numUnusedBits
);
1621 require_noerr_quiet(drtn
, badCert
);
1623 /* The contents of tbsCert.issuerID is a bit string. */
1624 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1626 /* The contents of tbsCert.subjectID is a bit string. */
1627 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1630 if (tbsCert
.extensions
.length
) {
1631 CFIndex extensionCount
= 0;
1634 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1636 require_noerr_quiet(drtn
, badCert
);
1637 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1638 DERDecodedInfo currDecoded
;
1639 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1641 /* ! = MUST recognize ? = SHOULD recognize
1644 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1645 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1646 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1647 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1648 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1649 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1650 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1651 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1653 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1654 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1655 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1656 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1657 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1658 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1659 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1660 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1662 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1663 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1668 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1670 /* Put some upper limit on the number of extensions allowed. */
1671 require_quiet(extensionCount
< 10000, badCert
);
1672 certificate
->_extensionCount
= extensionCount
;
1673 certificate
->_extensions
=
1674 malloc(sizeof(SecCertificateExtension
) * (extensionCount
> 0 ? extensionCount
: 1));
1677 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1678 require_noerr_quiet(drtn
, badCert
);
1679 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1680 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1681 require_quiet(drtn
== DR_Success
||
1682 (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1683 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1685 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1686 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1687 &extn
, sizeof(extn
));
1688 require_noerr_quiet(drtn
, badCert
);
1689 /* Copy stuff into certificate->extensions[ix]. */
1690 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1691 require_noerr_quiet(drtn
= DERParseBoolean(&extn
.critical
, false,
1692 &certificate
->_extensions
[ix
].critical
), badCert
);
1693 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1695 SecCertificateExtensionParser parser
=
1696 (SecCertificateExtensionParser
)CFDictionaryGetValue(
1697 sExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1699 /* Invoke the parser. */
1700 parser(certificate
, &certificate
->_extensions
[ix
]);
1701 } else if (certificate
->_extensions
[ix
].critical
) {
1702 if (isAppleExtensionOID(&extn
.extnID
)) {
1705 secdebug("cert", "Found unknown critical extension");
1706 certificate
->_foundUnknownCriticalExtension
= true;
1708 secdebug("cert", "Found unknown non critical extension");
1712 checkForMissingRevocationInfo(certificate
);
1721 /* Public API functions. */
1722 SecCertificateRef
SecCertificateCreateWithBytes(CFAllocatorRef allocator
,
1723 const UInt8
*der_bytes
, CFIndex der_length
) {
1724 if (der_bytes
== NULL
) return NULL
;
1725 if (der_length
== 0) return NULL
;
1727 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1728 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1729 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1731 memset((char*)result
+ sizeof(result
->_base
), 0,
1732 sizeof(*result
) - sizeof(result
->_base
));
1733 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1734 result
->_der
.length
= der_length
;
1735 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1736 if (!SecCertificateParse(result
)) {
1744 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1745 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1746 const UInt8
*der_bytes
, CFIndex der_length
);
1748 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1749 const UInt8
*der_bytes
, CFIndex der_length
) {
1750 return SecCertificateCreateWithBytes(allocator
, der_bytes
, der_length
);
1752 /* @@@ End of placeholder. */
1754 /* AUDIT[securityd](done):
1755 der_certificate is a caller provided data of any length (might be 0), only
1756 its cf type has been checked.
1758 SecCertificateRef
SecCertificateCreateWithData(CFAllocatorRef allocator
,
1759 CFDataRef der_certificate
) {
1760 check(der_certificate
);
1761 CFIndex size
= sizeof(struct __SecCertificate
);
1762 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1763 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1765 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1766 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1767 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1768 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1769 if (!SecCertificateParse(result
)) {
1777 SecCertificateRef
SecCertificateCreateWithKeychainItem(CFAllocatorRef allocator
,
1778 CFDataRef der_certificate
,
1779 CFTypeRef keychain_item
)
1781 SecCertificateRef result
= SecCertificateCreateWithData(allocator
, der_certificate
);
1783 CFRetainSafe(keychain_item
);
1784 result
->_keychain_item
= keychain_item
;
1789 CFDataRef
SecCertificateCopyData(SecCertificateRef certificate
) {
1791 CFDataRef result
= NULL
;
1795 if (certificate
->_der_data
) {
1796 CFRetain(certificate
->_der_data
);
1797 result
= certificate
->_der_data
;
1799 result
= CFDataCreate(CFGetAllocator(certificate
),
1800 certificate
->_der
.data
, certificate
->_der
.length
);
1802 /* FIXME: If we wish to cache result we need to lock the certificate.
1803 Also this create 2 copies of the certificate data which is somewhat
1806 certificate
->_der_data
= result
;
1813 CFIndex
SecCertificateGetLength(SecCertificateRef certificate
) {
1814 return certificate
->_der
.length
;
1817 const UInt8
*SecCertificateGetBytePtr(SecCertificateRef certificate
) {
1818 return certificate
->_der
.data
;
1821 /* Used to recreate preCert from cert for Certificate Transparency */
1822 CFDataRef
SecCertificateCopyPrecertTBS(SecCertificateRef certificate
)
1824 CFDataRef outData
= NULL
;
1825 DERItem tbsIn
= certificate
->_tbs
;
1826 DERItem tbsOut
= {0,};
1827 DERItem extensionsOut
= {0,};
1828 DERItem
*extensionsList
= malloc(sizeof(DERItem
)*certificate
->_extensionCount
); /* This maybe one too many */
1829 DERItemSpec
*extensionsListSpecs
= malloc(sizeof(DERItemSpec
)*certificate
->_extensionCount
);
1833 /* decode the TBSCert - it was saved in full DER form */
1834 drtn
= DERParseSequence(&tbsIn
,
1835 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1836 &tbsCert
, sizeof(tbsCert
));
1837 require_noerr_quiet(drtn
, out
);
1839 /* Go over extensions and filter any SCT extension */
1840 CFIndex extensionsCount
= 0;
1842 if (tbsCert
.extensions
.length
) {
1845 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1847 require_noerr_quiet(drtn
, out
);
1848 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
1849 DERDecodedInfo currDecoded
;
1850 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1852 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, out
);
1854 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1855 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1856 &extn
, sizeof(extn
));
1857 require_noerr_quiet(drtn
, out
);
1859 if (extn
.extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
1860 !memcmp(extn
.extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
.extnID
.length
))
1863 extensionsList
[extensionsCount
] = currDecoded
.content
;
1864 extensionsListSpecs
[extensionsCount
].offset
= sizeof(DERItem
)*extensionsCount
;
1865 extensionsListSpecs
[extensionsCount
].options
= 0;
1866 extensionsListSpecs
[extensionsCount
].tag
= ASN1_CONSTR_SEQUENCE
;
1871 require_quiet(drtn
== DR_EndOfSequence
, out
);
1875 /* Encode extensions */
1876 extensionsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
);
1877 extensionsOut
.data
= malloc(extensionsOut
.length
);
1878 require_quiet(extensionsOut
.data
, out
);
1879 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
, extensionsOut
.data
, &extensionsOut
.length
);
1880 require_noerr_quiet(drtn
, out
);
1882 tbsCert
.extensions
= extensionsOut
;
1884 tbsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
);
1885 tbsOut
.data
= malloc(tbsOut
.length
);
1886 require_quiet(tbsOut
.data
, out
);
1887 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
, tbsOut
.data
, &tbsOut
.length
);
1888 require_noerr_quiet(drtn
, out
);
1890 outData
= CFDataCreate(kCFAllocatorDefault
, tbsOut
.data
, tbsOut
.length
);
1893 free(extensionsOut
.data
);
1895 free(extensionsList
);
1896 free(extensionsListSpecs
);
1901 /* From rfc3280 - Appendix B. ASN.1 Notes
1903 Object Identifiers (OIDs) are used throughout this specification to
1904 identify certificate policies, public key and signature algorithms,
1905 certificate extensions, etc. There is no maximum size for OIDs.
1906 This specification mandates support for OIDs which have arc elements
1907 with values that are less than 2^28, that is, they MUST be between 0
1908 and 268,435,455, inclusive. This allows each arc element to be
1909 represented within a single 32 bit word. Implementations MUST also
1910 support OIDs where the length of the dotted decimal (see [RFC 2252],
1911 section 4.1) string representation can be up to 100 bytes
1912 (inclusive). Implementations MUST be able to handle OIDs with up to
1913 20 elements (inclusive). CAs SHOULD NOT issue certificates which
1914 contain OIDs that exceed these requirements. Likewise, CRL issuers
1915 SHOULD NOT issue CRLs which contain OIDs that exceed these
1919 /* Oids longer than this are considered invalid. */
1920 #define MAX_OID_SIZE 32
1922 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
1923 const DERItem
*oid
) {
1925 if (oid
->length
== 0) {
1926 return SecCopyCertString(SEC_NULL_KEY
);
1928 if (oid
->length
> MAX_OID_SIZE
) {
1929 return SecCopyCertString(SEC_OID_TOO_LONG_KEY
);
1932 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
1934 // The first two levels are encoded into one byte, since the root level
1935 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
1936 // y may be > 39, so we have to add special-case handling for this.
1937 uint32_t x
= oid
->data
[0] / 40;
1938 uint32_t y
= oid
->data
[0] % 40;
1941 // Handle special case for large y if x = 2
1945 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
1948 for (x
= 1; x
< oid
->length
; ++x
)
1950 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
1951 /* @@@ value may not span more than 4 bytes. */
1952 /* A max number of 20 values is allowed. */
1953 if (!(oid
->data
[x
] & 0x80))
1955 CFStringAppendFormat(result
, NULL
, CFSTR(".%" PRIu32
), value
);
1962 static CFStringRef
copyLocalizedOidDescription(CFAllocatorRef allocator
,
1963 const DERItem
*oid
) {
1964 if (oid
->length
== 0) {
1965 return SecCopyCertString(SEC_NULL_KEY
);
1968 /* Build the key we use to lookup the localized OID description. */
1969 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
1970 oid
->length
* 3 + 5);
1971 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), oid
->length
);
1973 for (ix
= 0; ix
< oid
->length
; ++ix
)
1974 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
1976 CFStringRef name
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
1977 if (CFEqual(oidKey
, name
)) {
1979 name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1986 /* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated
1987 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't
1988 have a length of exactly 4 or 16 octects. */
1989 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
1990 const DERItem
*ip
) {
1991 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
1992 4 octects addr, or 8 octects, addr/mask for ipv6 it's
1993 16 octects addr, or 32 octects addr/mask. */
1994 CFStringRef value
= NULL
;
1995 if (ip
->length
== 4) {
1996 value
= CFStringCreateWithFormat(allocator
, NULL
,
1997 CFSTR("%u.%u.%u.%u"),
1998 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
1999 } else if (ip
->length
== 16) {
2000 value
= CFStringCreateWithFormat(allocator
, NULL
,
2001 CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
2002 "%02x%02x:%02x%02x:%02x%02x:%02x%02x"),
2003 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
2004 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
2005 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
2006 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
2013 static CFStringRef
copyFullOidDescription(CFAllocatorRef allocator
,
2014 const DERItem
*oid
) {
2015 CFStringRef decimal
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
2016 CFStringRef name
= copyLocalizedOidDescription(allocator
, oid
);
2017 CFStringRef oid_string
= CFStringCreateWithFormat(allocator
, NULL
,
2018 CFSTR("%@ (%@)"), name
, decimal
);
2025 void appendProperty(CFMutableArrayRef properties
, CFStringRef propertyType
,
2026 CFStringRef label
, CFStringRef localizedLabel
, CFTypeRef value
) {
2027 CFDictionaryRef property
;
2030 if (localizedLabel
) {
2033 ll
= localizedLabel
= SecCopyCertString(label
);
2035 const void *all_keys
[4];
2036 all_keys
[0] = kSecPropertyKeyType
;
2037 all_keys
[1] = kSecPropertyKeyLabel
;
2038 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
2039 all_keys
[3] = kSecPropertyKeyValue
;
2040 const void *property_values
[] = {
2046 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2047 all_keys
, property_values
, value
? 4 : 3,
2048 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2051 const void *nolabel_keys
[2];
2052 nolabel_keys
[0] = kSecPropertyKeyType
;
2053 nolabel_keys
[1] = kSecPropertyKeyValue
;
2054 const void *property_values
[] = {
2058 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2059 nolabel_keys
, property_values
, 2,
2060 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2063 CFArrayAppendValue(properties
, property
);
2064 CFRelease(property
);
2068 #define UTC_TIME_NOSEC_ZULU_LEN 11
2070 #define UTC_TIME_ZULU_LEN 13
2071 /* YYMMDDhhmmssThhmm */
2072 #define UTC_TIME_LOCALIZED_LEN 17
2073 /* YYYYMMDDhhmmssZ */
2074 #define GENERALIZED_TIME_ZULU_LEN 15
2075 /* YYYYMMDDhhmmssThhmm */
2076 #define GENERALIZED_TIME_LOCALIZED_LEN 19
2078 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
2080 static inline int parseDecimalPair(const DERByte
**p
) {
2081 const DERByte
*cp
= *p
;
2083 return 10 * (cp
[0] - '0') + cp
[1] - '0';
2086 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
2087 true if the date was valid and properly decoded, also return the result in
2088 absTime. Return false otherwise. */
2089 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
2096 bool isUtcLength
= false;
2097 bool isLocalized
= false;
2098 bool noSeconds
= false;
2100 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
2104 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
2107 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
2109 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
2112 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
2115 default: /* unknown format */
2119 /* Make sure the der tag fits the thing inside it. */
2120 if (tag
== ASN1_UTC_TIME
) {
2123 } else if (tag
== ASN1_GENERALIZED_TIME
) {
2130 const DERByte
*cp
= bytes
;
2131 /* Check that all characters are digits, except if localized the timezone
2132 indicator or if not localized the 'Z' at the end. */
2134 for (ix
= 0; ix
< length
; ++ix
) {
2135 if (!(isdigit(cp
[ix
]))) {
2136 if ((isLocalized
&& ix
== length
- 5 &&
2137 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
2138 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
2145 /* Parse the date and time fields. */
2146 int year
, month
, day
, hour
, minute
, second
;
2148 year
= parseDecimalPair(&cp
);
2150 /* 0 <= year < 50 : assume century 21 */
2152 } else if (year
< 70) {
2153 /* 50 <= year < 70 : illegal per PKIX */
2156 /* 70 < year <= 99 : assume century 20 */
2160 year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
2162 month
= parseDecimalPair(&cp
);
2163 day
= parseDecimalPair(&cp
);
2164 hour
= parseDecimalPair(&cp
);
2165 minute
= parseDecimalPair(&cp
);
2169 second
= parseDecimalPair(&cp
);
2172 CFTimeInterval timeZoneOffset
;
2174 /* ZONE INDICATOR */
2175 int multiplier
= *cp
++ == '+' ? 60 : -60;
2176 timeZoneOffset
= multiplier
*
2177 (parseDecimalPair(&cp
) * 60 + parseDecimalPair(&cp
));
2182 secdebug("dateparse",
2183 "date %.*s year: %04d%02d%02d%02d%02d%02d%+05g",
2184 (int) length
, bytes
, year
, month
,
2185 day
, hour
, minute
, second
,
2186 timeZoneOffset
/ 60);
2188 static int mdays
[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
2189 int is_leap_year
= year
% 4 == 0 && (year
% 100 != 0 || year
% 400 == 0) ? 1 : 0;
2190 if (month
< 1 || month
> 12 || day
< 1 || day
> 31 || hour
> 23 || minute
> 59 || second
> 59
2191 || (month
== 2 && day
> mdays
[month
] - mdays
[month
- 1] + is_leap_year
)
2192 || (month
!= 2 && day
> mdays
[month
] - mdays
[month
- 1])) {
2197 int dy
= year
- 2001;
2202 int leap_days
= dy
/ 4 - dy
/ 100 + dy
/ 400;
2203 day
+= ((year
- 2001) * 365 + leap_days
) + mdays
[month
- 1] - 1;
2205 day
+= is_leap_year
;
2207 CFAbsoluteTime absTime
= (CFAbsoluteTime
)((day
* 24.0 + hour
) * 60.0 + minute
) * 60.0 + second
;
2208 return absTime
- timeZoneOffset
;
2211 __attribute__((__nonnull__
)) static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
2212 CFAbsoluteTime
*pabsTime
) {
2213 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContent(tag
, date
->data
,
2215 if (absTime
== NULL_TIME
)
2218 *pabsTime
= absTime
;
2222 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
2223 true if the date was valid and properly decoded, also return the result in
2224 absTime. Return false otherwise. */
2225 __attribute__((__nonnull__
)) static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
2226 CFAbsoluteTime
*absTime
) {
2227 if (dateChoice
->length
== 0) return false;
2229 DERDecodedInfo decoded
;
2230 if (DERDecodeItem(dateChoice
, &decoded
))
2233 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
2237 static void appendDataProperty(CFMutableArrayRef properties
,
2238 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
) {
2239 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
2240 der_data
->data
, der_data
->length
);
2241 appendProperty(properties
, kSecPropertyTypeData
, label
, localizedLabel
,
2246 static void appendRelabeledProperty(CFMutableArrayRef properties
,
2248 CFStringRef localizedLabel
,
2249 const DERItem
*der_data
,
2250 CFStringRef labelFormat
) {
2251 CFStringRef newLabel
=
2252 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2253 labelFormat
, label
);
2255 if (localizedLabel
) {
2258 ll
= localizedLabel
= SecCopyCertString(label
);
2260 CFStringRef localizedLabelFormat
= SecCopyCertString(labelFormat
);
2261 CFStringRef newLocalizedLabel
=
2262 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2263 localizedLabelFormat
, localizedLabel
);
2265 CFReleaseSafe(localizedLabelFormat
);
2266 appendDataProperty(properties
, newLabel
, newLocalizedLabel
, der_data
);
2267 CFReleaseSafe(newLabel
);
2268 CFReleaseSafe(newLocalizedLabel
);
2272 static void appendUnparsedProperty(CFMutableArrayRef properties
,
2273 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
) {
2274 appendRelabeledProperty(properties
, label
, localizedLabel
, der_data
,
2278 static void appendInvalidProperty(CFMutableArrayRef properties
,
2279 CFStringRef label
, const DERItem
*der_data
) {
2280 appendRelabeledProperty(properties
, label
, NULL
, der_data
, SEC_INVALID_KEY
);
2283 static void appendDateContentProperty(CFMutableArrayRef properties
,
2284 CFStringRef label
, DERTag tag
,
2285 const DERItem
*dateContent
) {
2286 CFAbsoluteTime absTime
;
2287 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
2288 /* Date decode failure insert hex bytes instead. */
2289 return appendInvalidProperty(properties
, label
, dateContent
);
2291 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2292 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
);
2296 static void appendDateProperty(CFMutableArrayRef properties
,
2297 CFStringRef label
, CFAbsoluteTime absTime
) {
2298 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2299 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
);
2303 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
2304 CFStringRef label
, const DERItem
*ip
) {
2306 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
2308 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2311 appendUnparsedProperty(properties
, label
, NULL
, ip
);
2315 static void appendURLContentProperty(CFMutableArrayRef properties
,
2316 CFStringRef label
, const DERItem
*urlContent
) {
2317 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
2318 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2320 appendProperty(properties
, kSecPropertyTypeURL
, label
, NULL
, url
);
2323 appendInvalidProperty(properties
, label
, urlContent
);
2327 static void appendURLProperty(CFMutableArrayRef properties
,
2328 CFStringRef label
, const DERItem
*url
) {
2329 DERDecodedInfo decoded
;
2332 drtn
= DERDecodeItem(url
, &decoded
);
2333 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2334 appendInvalidProperty(properties
, label
, url
);
2336 appendURLContentProperty(properties
, label
, &decoded
.content
);
2340 static void appendOIDProperty(CFMutableArrayRef properties
,
2341 CFStringRef label
, CFStringRef llabel
, const DERItem
*oid
) {
2342 CFStringRef oid_string
=
2343 copyLocalizedOidDescription(CFGetAllocator(properties
), oid
);
2344 appendProperty(properties
, kSecPropertyTypeString
, label
, llabel
,
2346 CFRelease(oid_string
);
2349 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2350 CFStringRef label
, const DERAlgorithmId
*algorithm
) {
2351 CFMutableArrayRef alg_props
=
2352 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2353 &kCFTypeArrayCallBacks
);
2354 appendOIDProperty(alg_props
, SEC_ALGORITHM_KEY
, NULL
, &algorithm
->oid
);
2355 if (algorithm
->params
.length
) {
2356 if (algorithm
->params
.length
== 2 &&
2357 algorithm
->params
.data
[0] == ASN1_NULL
&&
2358 algorithm
->params
.data
[1] == 0) {
2359 CFStringRef value
= SecCopyCertString(SEC_NONE_KEY
);
2360 appendProperty(alg_props
, kSecPropertyTypeString
,
2361 SEC_PARAMETERS_KEY
, NULL
, value
);
2364 appendUnparsedProperty(alg_props
, SEC_PARAMETERS_KEY
, NULL
,
2365 &algorithm
->params
);
2368 appendProperty(properties
, kSecPropertyTypeSection
, label
, NULL
, alg_props
);
2369 CFRelease(alg_props
);
2372 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2373 const DERItem
*blob
) {
2374 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2375 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2376 blob
->length
* 3 - 1);
2377 for (ix
= 0; ix
< length
; ++ix
)
2379 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2381 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2386 /* Returns a (localized) blob string. */
2387 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2388 CFStringRef blobType
, CFStringRef quanta
, const DERItem
*blob
) {
2389 CFStringRef localizedBlobType
= SecCopyCertString(blobType
);
2390 CFStringRef localizedQuanta
= SecCopyCertString(quanta
);
2391 /* "format string for encoded field data (e.g. Sequence; 128 bytes; "
2392 "data = 00 00 ...)" */
2393 CFStringRef blobFormat
= SecCopyCertString(SEC_BLOB_KEY
);
2394 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2395 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2396 blobFormat
, localizedBlobType
, blob
->length
, localizedQuanta
, hex
);
2398 CFRelease(blobFormat
);
2399 CFReleaseSafe(localizedQuanta
);
2400 CFReleaseSafe(localizedBlobType
);
2405 /* Return a string verbatim (unlocalized) from a DER field. */
2406 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2407 const DERItem
*string
, CFStringEncoding encoding
,
2408 bool printableOnly
) {
2409 /* Strip potential bogus trailing zero from printable strings. */
2410 DERSize length
= string
->length
;
2411 if (length
&& string
->data
[length
- 1] == 0) {
2412 /* Don't mess with the length of UTF16 strings though. */
2413 if (encoding
!= kCFStringEncodingUTF16
)
2416 /* A zero length string isn't considered printable. */
2417 if (!length
&& printableOnly
)
2420 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2421 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2422 passing false makes it treat it as native endian by default. */
2423 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2424 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2428 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2431 /* From rfc3280 - Appendix B. ASN.1 Notes
2433 CAs MUST force the serialNumber to be a non-negative integer, that
2434 is, the sign bit in the DER encoding of the INTEGER value MUST be
2435 zero - this can be done by adding a leading (leftmost) `00'H octet if
2436 necessary. This removes a potential ambiguity in mapping between a
2437 string of octets and an integer value.
2439 As noted in section 4.1.2.2, serial numbers can be expected to
2440 contain long integers. Certificate users MUST be able to handle
2441 serialNumber values up to 20 octets in length. Conformant CAs MUST
2442 NOT use serialNumber values longer than 20 octets.
2445 /* Return the given numeric data as a string: decimal up to 64 bits,
2447 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2448 const DERItem
*integer
) {
2450 CFIndex ix
, length
= integer
->length
;
2452 if (length
== 0 || length
> 8)
2453 return copyHexDescription(allocator
, integer
);
2455 for(ix
= 0; ix
< length
; ++ix
) {
2457 value
+= integer
->data
[ix
];
2460 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2463 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2464 DERTag tag
, const DERItem
*derThing
, bool printableOnly
) {
2468 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2469 case ASN1_PRINTABLE_STRING
:
2470 case ASN1_IA5_STRING
:
2471 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2472 case ASN1_UTF8_STRING
:
2473 case ASN1_GENERAL_STRING
:
2474 case ASN1_UNIVERSAL_STRING
:
2475 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2476 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2477 case ASN1_VIDEOTEX_STRING
: // 21
2478 case ASN1_VISIBLE_STRING
: // 26
2479 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2480 case ASN1_BMP_STRING
: // 30
2481 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2482 case ASN1_OCTET_STRING
:
2483 return printableOnly
? NULL
:
2484 copyBlobString(allocator
, SEC_BYTE_STRING_KEY
, SEC_BYTES_KEY
,
2486 //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing);
2487 case ASN1_BIT_STRING
:
2488 return printableOnly
? NULL
:
2489 copyBlobString(allocator
, SEC_BIT_STRING_KEY
, SEC_BITS_KEY
,
2491 case ASN1_CONSTR_SEQUENCE
:
2492 return printableOnly
? NULL
:
2493 copyBlobString(allocator
, SEC_SEQUENCE_KEY
, SEC_BYTES_KEY
,
2495 case ASN1_CONSTR_SET
:
2496 return printableOnly
? NULL
:
2497 copyBlobString(allocator
, SEC_SET_KEY
, SEC_BYTES_KEY
, derThing
);
2498 case ASN1_OBJECT_ID
:
2499 return printableOnly
? NULL
: copyLocalizedOidDescription(allocator
, derThing
);
2501 if (printableOnly
) {
2504 CFStringRef fmt
= SecCopyCertString(SEC_NOT_DISPLAYED_KEY
);
2505 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
2506 tag
, derThing
->length
);
2513 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2514 const DERItem
*derThing
, bool printableOnly
) {
2515 DERDecodedInfo decoded
;
2518 drtn
= DERDecodeItem(derThing
, &decoded
);
2520 /* TODO: Perhaps put something in the label saying we couldn't parse
2522 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2524 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2525 &decoded
.content
, false);
2529 static void appendDERThingProperty(CFMutableArrayRef properties
,
2530 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*derThing
) {
2531 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2533 appendProperty(properties
, kSecPropertyTypeString
, label
, localizedLabel
,
2535 CFReleaseSafe(value
);
2538 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2539 const DERItem
*rdnValue
, CFIndex rdnIX
) {
2540 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2542 /* If there is more than one value pair we create a subsection for the
2543 second pair, and append things to the subsection for subsequent
2545 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2546 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2548 /* Since this is the second rdn pair for a given rdn, we setup a
2549 new subsection for this rdn. We remove the first property
2550 from the properties array and make it the first element in the
2551 subsection instead. */
2552 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2553 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2554 CFArrayAppendValue(rdn_props
, lastValue
);
2555 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2556 appendProperty(properties
, kSecPropertyTypeSection
, NULL
, NULL
,
2558 properties
= rdn_props
;
2560 /* Since this is the third or later rdn pair we have already
2561 created a subsection in the top level properties array. Instead
2562 of appending to that directly we append to the array inside the
2564 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2565 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2569 /* Finally we append the new rdn value to the property array. */
2570 CFStringRef label
= SecDERItemCopyOIDDecimalRepresentation(
2571 CFGetAllocator(properties
), rdnType
);
2572 CFStringRef localizedLabel
=
2573 copyLocalizedOidDescription(CFGetAllocator(properties
), rdnType
);
2574 appendDERThingProperty(properties
, label
, localizedLabel
, rdnValue
);
2575 CFReleaseSafe(label
);
2576 CFReleaseSafe(localizedLabel
);
2577 return errSecSuccess
;
2580 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2581 const DERItem
*rdnSetContent
) {
2582 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2583 &kCFTypeArrayCallBacks
);
2584 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2587 CFArrayRemoveAllValues(properties
);
2588 appendInvalidProperty(properties
, SEC_RDN_KEY
, rdnSetContent
);
2595 From rfc3739 - 3.1.2. Subject
2597 When parsing the subject here are some tips for a short name of the cert.
2598 Choice I: commonName
2599 Choice II: givenName
2600 Choice III: pseudonym
2602 The commonName attribute value SHALL, when present, contain a name
2603 of the subject. This MAY be in the subject's preferred
2604 presentation format, or a format preferred by the CA, or some
2605 other format. Pseudonyms, nicknames, and names with spelling
2606 other than defined by the registered name MAY be used. To
2607 understand the nature of the name presented in commonName,
2608 complying applications MAY have to examine present values of the
2609 givenName and surname attributes, or the pseudonym attribute.
2612 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2613 const DERItem
*x501NameContent
) {
2614 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2615 &kCFTypeArrayCallBacks
);
2616 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2619 CFArrayRemoveAllValues(properties
);
2620 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
, x501NameContent
);
2626 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2627 const DERItem
*x501Name
) {
2628 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2629 &kCFTypeArrayCallBacks
);
2630 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
);
2632 CFArrayRemoveAllValues(properties
);
2633 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
, x501Name
);
2639 static void appendIntegerProperty(CFMutableArrayRef properties
,
2640 CFStringRef label
, const DERItem
*integer
) {
2641 CFStringRef string
= copyIntegerContentDescription(
2642 CFGetAllocator(properties
), integer
);
2643 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, string
);
2647 static void appendBoolProperty(CFMutableArrayRef properties
,
2648 CFStringRef label
, bool boolean
) {
2649 CFStringRef value
= SecCopyCertString(boolean
? SEC_YES_KEY
: SEC_NO_KEY
);
2650 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
2654 static void appendBooleanProperty(CFMutableArrayRef properties
,
2655 CFStringRef label
, const DERItem
*boolean
, bool defaultValue
) {
2657 DERReturn drtn
= DERParseBoolean(boolean
, defaultValue
, &result
);
2659 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2660 appendInvalidProperty(properties
, label
, boolean
);
2662 appendBoolProperty(properties
, label
, result
);
2666 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2667 CFStringRef label
, const DERItem
*bitStringContent
,
2668 const CFStringRef
*names
, CFIndex namesCount
) {
2669 DERSize len
= bitStringContent
->length
- 1;
2670 require_quiet(len
== 1 || len
== 2, badDER
);
2671 DERByte numUnusedBits
= bitStringContent
->data
[0];
2672 require_quiet(numUnusedBits
< 8, badDER
);
2673 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
2674 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
2675 uint_fast16_t value
= bitStringContent
->data
[1];
2678 value
= (value
<< 8) + bitStringContent
->data
[2];
2684 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
2685 CFStringRef string
= NULL
;
2686 for (ix
= 0; ix
< bits
; ++ix
) {
2690 CFStringCreateWithFormat(CFGetAllocator(properties
),
2691 NULL
, fmt
, string
, names
[ix
]);
2702 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2703 string
? string
: CFSTR(""));
2704 CFReleaseSafe(string
);
2707 appendInvalidProperty(properties
, label
, bitStringContent
);
2710 static void appendBitStringNames(CFMutableArrayRef properties
,
2711 CFStringRef label
, const DERItem
*bitString
,
2712 const CFStringRef
*names
, CFIndex namesCount
) {
2713 DERDecodedInfo bitStringContent
;
2714 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
2715 require_noerr_quiet(drtn
, badDER
);
2716 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
2717 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
2721 appendInvalidProperty(properties
, label
, bitString
);
2725 typedef uint16_t SecKeyUsage
;
2727 #define kSecKeyUsageDigitalSignature 0x8000
2728 #define kSecKeyUsageNonRepudiation 0x4000
2729 #define kSecKeyUsageKeyEncipherment 0x2000
2730 #define kSecKeyUsageDataEncipherment 0x1000
2731 #define kSecKeyUsageKeyAgreement 0x0800
2732 #define kSecKeyUsageKeyCertSign 0x0400
2733 #define kSecKeyUsageCRLSign 0x0200
2734 #define kSecKeyUsageEncipherOnly 0x0100
2735 #define kSecKeyUsageDecipherOnly 0x0080
2738 KeyUsage ::= BIT STRING {
2739 digitalSignature (0),
2741 keyEncipherment (2),
2742 dataEncipherment (3),
2749 static void appendKeyUsage(CFMutableArrayRef properties
,
2750 const DERItem
*extnValue
) {
2751 if ((extnValue
->length
!= 4 && extnValue
->length
!= 5) ||
2752 extnValue
->data
[0] != ASN1_BIT_STRING
||
2753 extnValue
->data
[1] < 2 || extnValue
->data
[1] > 3 ||
2754 extnValue
->data
[2] > 7) {
2755 appendInvalidProperty(properties
, CFSTR("KeyUsage Extension"),
2758 CFMutableStringRef string
=
2759 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2760 SecKeyUsage usage
= (extnValue
->data
[3] << 8);
2761 if (extnValue
->length
== 5)
2762 usage
+= extnValue
->data
[4];
2763 secdebug("keyusage", "keyusage: %04X", usage
);
2764 static const CFStringRef usageNames
[] = {
2765 CFSTR("Digital Signature"),
2766 CFSTR("Non-Repudiation"),
2767 CFSTR("Key Encipherment"),
2768 CFSTR("Data Encipherment"),
2769 CFSTR("Key Agreement"),
2775 bool didOne
= false;
2776 SecKeyUsage mask
= kSecKeyUsageDigitalSignature
;
2777 CFIndex ix
, bits
= (extnValue
->data
[1] - 1) * 8 - extnValue
->data
[2];
2778 for (ix
= 0; ix
< bits
; ++ix
) {
2781 CFStringAppend(string
, CFSTR(", "));
2785 /* @@@ Localize usageNames[ix]. */
2786 CFStringAppend(string
, usageNames
[ix
]);
2790 appendProperty(properties
, kSecPropertyTypeString
, CFSTR("Usage"),
2796 static void appendKeyUsage(CFMutableArrayRef properties
,
2797 const DERItem
*extnValue
) {
2798 static const CFStringRef usageNames
[] = {
2799 SEC_DIGITAL_SIGNATURE_KEY
,
2800 SEC_NON_REPUDIATION_KEY
,
2801 SEC_KEY_ENCIPHERMENT_KEY
,
2802 SEC_DATA_ENCIPHERMENT_KEY
,
2803 SEC_KEY_AGREEMENT_KEY
,
2806 SEC_ENCIPHER_ONLY_KEY
,
2807 SEC_DECIPHER_ONLY_KEY
2809 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
2810 usageNames
, array_size(usageNames
));
2814 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
2815 const DERItem
*extnValue
) {
2816 DERPrivateKeyUsagePeriod pkup
;
2817 DERReturn drtn
= DERParseSequence(extnValue
,
2818 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
2819 &pkup
, sizeof(pkup
));
2820 require_noerr_quiet(drtn
, badDER
);
2821 if (pkup
.notBefore
.length
) {
2822 appendDateContentProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2823 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
);
2825 if (pkup
.notAfter
.length
) {
2826 appendDateContentProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2827 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
);
2831 appendInvalidProperty(properties
, SEC_PRIVATE_KU_PERIOD_KEY
, extnValue
);
2834 static void appendStringContentProperty(CFMutableArrayRef properties
,
2835 CFStringRef label
, const DERItem
*stringContent
,
2836 CFStringEncoding encoding
) {
2837 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
2838 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
2840 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, string
);
2843 appendInvalidProperty(properties
, label
, stringContent
);
2848 OtherName ::= SEQUENCE {
2849 type-id OBJECT IDENTIFIER,
2850 value [0] EXPLICIT ANY DEFINED BY type-id }
2852 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
2853 const DERItem
*otherNameContent
) {
2855 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
2856 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
2858 require_noerr_quiet(drtn
, badDER
);
2859 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2861 SecDERItemCopyOIDDecimalRepresentation(allocator
, &on
.typeIdentifier
);
2862 CFStringRef localizedLabel
=
2863 copyLocalizedOidDescription(allocator
, &on
.typeIdentifier
);
2864 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
, false);
2866 appendProperty(properties
, kSecPropertyTypeString
, label
,
2867 localizedLabel
, value_string
);
2869 appendUnparsedProperty(properties
, label
, localizedLabel
, &on
.value
);
2871 CFReleaseSafe(value_string
);
2872 CFReleaseSafe(label
);
2873 CFReleaseSafe(localizedLabel
);
2876 appendInvalidProperty(properties
, SEC_OTHER_NAME_KEY
, otherNameContent
);
2880 GeneralName ::= CHOICE {
2881 otherName [0] OtherName,
2882 rfc822Name [1] IA5String,
2883 dNSName [2] IA5String,
2884 x400Address [3] ORAddress,
2885 directoryName [4] Name,
2886 ediPartyName [5] EDIPartyName,
2887 uniformResourceIdentifier [6] IA5String,
2888 iPAddress [7] OCTET STRING,
2889 registeredID [8] OBJECT IDENTIFIER}
2891 EDIPartyName ::= SEQUENCE {
2892 nameAssigner [0] DirectoryString OPTIONAL,
2893 partyName [1] DirectoryString }
2895 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
2896 DERTag tag
, const DERItem
*generalName
) {
2898 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
2899 appendOtherNameContentProperty(properties
, generalName
);
2901 case ASN1_CONTEXT_SPECIFIC
| 1:
2903 appendStringContentProperty(properties
, SEC_EMAIL_ADDRESS_KEY
,
2904 generalName
, kCFStringEncodingASCII
);
2906 case ASN1_CONTEXT_SPECIFIC
| 2:
2908 appendStringContentProperty(properties
, SEC_DNS_NAME_KEY
, generalName
,
2909 kCFStringEncodingASCII
);
2911 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
2912 appendUnparsedProperty(properties
, SEC_X400_ADDRESS_KEY
, NULL
,
2915 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
2917 CFArrayRef directory_plist
=
2918 createPropertiesForX501Name(CFGetAllocator(properties
),
2920 appendProperty(properties
, kSecPropertyTypeSection
,
2921 SEC_DIRECTORY_NAME_KEY
, NULL
, directory_plist
);
2922 CFRelease(directory_plist
);
2925 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
2926 appendUnparsedProperty(properties
, SEC_EDI_PARTY_NAME_KEY
, NULL
,
2929 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
2930 /* Technically I don't think this is valid, but there are certs out
2931 in the wild that use a constructed IA5String. In particular the
2932 VeriSign Time Stamping Authority CA.cer does this. */
2933 appendURLProperty(properties
, SEC_URI_KEY
, generalName
);
2935 case ASN1_CONTEXT_SPECIFIC
| 6:
2936 appendURLContentProperty(properties
, SEC_URI_KEY
, generalName
);
2938 case ASN1_CONTEXT_SPECIFIC
| 7:
2939 appendIPAddressContentProperty(properties
, SEC_IP_ADDRESS_KEY
,
2942 case ASN1_CONTEXT_SPECIFIC
| 8:
2943 appendOIDProperty(properties
, SEC_REGISTERED_ID_KEY
, NULL
, generalName
);
2954 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
2955 const DERItem
*generalName
) {
2956 DERDecodedInfo generalNameContent
;
2957 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
2958 require_noerr_quiet(drtn
, badDER
);
2959 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
2960 &generalNameContent
.content
))
2963 appendInvalidProperty(properties
, SEC_GENERAL_NAME_KEY
, generalName
);
2968 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
2970 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
2971 const DERItem
*generalNamesContent
) {
2973 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
2974 require_noerr_quiet(drtn
, badDER
);
2975 DERDecodedInfo generalNameContent
;
2976 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
2978 if (!appendGeneralNameContentProperty(properties
,
2979 generalNameContent
.tag
, &generalNameContent
.content
)) {
2983 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2986 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
2987 generalNamesContent
);
2990 static void appendGeneralNames(CFMutableArrayRef properties
,
2991 const DERItem
*generalNames
) {
2992 DERDecodedInfo generalNamesContent
;
2993 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
2994 require_noerr_quiet(drtn
, badDER
);
2995 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
2997 appendGeneralNamesContent(properties
, &generalNamesContent
.content
);
3000 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
, generalNames
);
3004 BasicConstraints ::= SEQUENCE {
3005 cA BOOLEAN DEFAULT FALSE,
3006 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
3008 static void appendBasicConstraints(CFMutableArrayRef properties
,
3009 const DERItem
*extnValue
) {
3010 DERBasicConstraints basicConstraints
;
3011 DERReturn drtn
= DERParseSequence(extnValue
,
3012 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
3013 &basicConstraints
, sizeof(basicConstraints
));
3014 require_noerr_quiet(drtn
, badDER
);
3016 appendBooleanProperty(properties
, SEC_CERT_AUTHORITY_KEY
,
3017 &basicConstraints
.cA
, false);
3019 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
3020 appendIntegerProperty(properties
, SEC_PATH_LEN_CONSTRAINT_KEY
,
3021 &basicConstraints
.pathLenConstraint
);
3025 appendInvalidProperty(properties
, SEC_BASIC_CONSTRAINTS_KEY
, extnValue
);
3029 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
3031 * NameConstraints ::= SEQUENCE {
3032 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
3033 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
3035 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
3037 * GeneralSubtree ::= SEQUENCE {
3039 * minimum [0] BaseDistance DEFAULT 0,
3040 * maximum [1] BaseDistance OPTIONAL }
3042 * BaseDistance ::= INTEGER (0..MAX)
3044 static void appendNameConstraints(CFMutableArrayRef properties
,
3045 const DERItem
*extnValue
) {
3046 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3047 DERNameConstraints nc
;
3049 drtn
= DERParseSequence(extnValue
,
3050 DERNumNameConstraintsItemSpecs
,
3051 DERNameConstraintsItemSpecs
,
3053 require_noerr_quiet(drtn
, badDER
);
3054 if (nc
.permittedSubtrees
.length
) {
3056 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.permittedSubtrees
, &gsSeq
), badDER
);
3057 DERDecodedInfo gsContent
;
3058 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3059 DERGeneralSubtree derGS
;
3060 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3061 drtn
= DERParseSequenceContent(&gsContent
.content
,
3062 DERNumGeneralSubtreeItemSpecs
,
3063 DERGeneralSubtreeItemSpecs
,
3064 &derGS
, sizeof(derGS
));
3065 require_noerr_quiet(drtn
, badDER
);
3066 if (derGS
.minimum
.length
) {
3067 appendIntegerProperty(properties
, SEC_PERMITTED_MINIMUM_KEY
, &derGS
.minimum
);
3069 if (derGS
.maximum
.length
) {
3070 appendIntegerProperty(properties
, SEC_PERMITTED_MAXIMUM_KEY
, &derGS
.maximum
);
3072 if (derGS
.generalName
.length
) {
3073 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3074 &kCFTypeArrayCallBacks
);
3075 appendProperty(properties
, kSecPropertyTypeSection
,
3076 SEC_PERMITTED_NAME_KEY
, NULL
, base
);
3077 appendGeneralNameProperty(base
, &derGS
.generalName
);
3082 if (nc
.excludedSubtrees
.length
) {
3084 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.excludedSubtrees
, &gsSeq
), badDER
);
3085 DERDecodedInfo gsContent
;
3086 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3087 DERGeneralSubtree derGS
;
3088 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3089 drtn
= DERParseSequenceContent(&gsContent
.content
,
3090 DERNumGeneralSubtreeItemSpecs
,
3091 DERGeneralSubtreeItemSpecs
,
3092 &derGS
, sizeof(derGS
));
3093 require_noerr_quiet(drtn
, badDER
);
3094 if (derGS
.minimum
.length
) {
3095 appendIntegerProperty(properties
, SEC_EXCLUDED_MINIMUM_KEY
, &derGS
.minimum
);
3097 if (derGS
.maximum
.length
) {
3098 appendIntegerProperty(properties
, SEC_EXCLUDED_MAXIMUM_KEY
, &derGS
.maximum
);
3100 if (derGS
.generalName
.length
) {
3101 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3102 &kCFTypeArrayCallBacks
);
3103 appendProperty(properties
, kSecPropertyTypeSection
,
3104 SEC_EXCLUDED_NAME_KEY
, NULL
, base
);
3105 appendGeneralNameProperty(base
, &derGS
.generalName
);
3113 appendInvalidProperty(properties
, SEC_NAME_CONSTRAINTS_KEY
, extnValue
);
3117 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
3119 DistributionPoint ::= SEQUENCE {
3120 distributionPoint [0] DistributionPointName OPTIONAL,
3121 reasons [1] ReasonFlags OPTIONAL,
3122 cRLIssuer [2] GeneralNames OPTIONAL }
3124 DistributionPointName ::= CHOICE {
3125 fullName [0] GeneralNames,
3126 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
3128 ReasonFlags ::= BIT STRING {
3132 affiliationChanged (3),
3134 cessationOfOperation (5),
3135 certificateHold (6),
3136 privilegeWithdrawn (7),
3139 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
3140 const DERItem
*extnValue
) {
3141 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3144 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
3145 require_noerr_quiet(drtn
, badDER
);
3146 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3147 DERDecodedInfo dpSeqContent
;
3148 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
3149 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3150 DERDistributionPoint dp
;
3151 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
3152 DERNumDistributionPointItemSpecs
,
3153 DERDistributionPointItemSpecs
,
3155 require_noerr_quiet(drtn
, badDER
);
3156 if (dp
.distributionPoint
.length
) {
3157 DERDecodedInfo distributionPointName
;
3158 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
3159 require_noerr_quiet(drtn
, badDER
);
3160 if (distributionPointName
.tag
==
3161 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
3163 appendGeneralNamesContent(properties
,
3164 &distributionPointName
.content
);
3165 } else if (distributionPointName
.tag
==
3166 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
3167 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
3169 appendProperty(properties
, kSecPropertyTypeSection
,
3170 SEC_NAME_REL_CRL_ISSUER_KEY
, NULL
, rdn_props
);
3171 CFRelease(rdn_props
);
3176 if (dp
.reasons
.length
) {
3177 static const CFStringRef reasonNames
[] = {
3179 SEC_KEY_COMPROMISE_KEY
,
3180 SEC_CA_COMPROMISE_KEY
,
3181 SEC_AFFILIATION_CHANGED_KEY
,
3183 SEC_CESSATION_OF_OPER_KEY
,
3184 SEC_CERTIFICATE_HOLD_KEY
,
3185 SEC_PRIV_WITHDRAWN_KEY
,
3186 SEC_AA_COMPROMISE_KEY
3188 appendBitStringContentNames(properties
, SEC_REASONS_KEY
,
3190 reasonNames
, array_size(reasonNames
));
3192 if (dp
.cRLIssuer
.length
) {
3193 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
3194 &kCFTypeArrayCallBacks
);
3195 appendProperty(properties
, kSecPropertyTypeSection
,
3196 SEC_CRL_ISSUER_KEY
, NULL
, crlIssuer
);
3197 CFRelease(crlIssuer
);
3198 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
);
3201 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3204 appendInvalidProperty(properties
, SEC_CRL_DISTR_POINTS_KEY
, extnValue
);
3207 /* Decode a sequence of integers into a comma separated list of ints. */
3208 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
3209 CFStringRef label
, const DERItem
*intSequenceContent
) {
3210 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3212 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
3213 require_noerr_quiet(drtn
, badDER
);
3214 DERDecodedInfo intContent
;
3215 CFStringRef value
= NULL
;
3216 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3217 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
)) == DR_Success
) {
3218 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
3219 CFStringRef intDesc
= copyIntegerContentDescription(
3220 allocator
, &intContent
.content
);
3223 v
= CFStringCreateWithFormat(allocator
, NULL
, fmt
, value
, intDesc
);
3232 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3234 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
);
3238 /* DROPTHOUGH if !value. */
3240 appendInvalidProperty(properties
, label
, intSequenceContent
);
3243 static void appendCertificatePolicies(CFMutableArrayRef properties
,
3244 const DERItem
*extnValue
) {
3245 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3248 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
3249 require_noerr_quiet(drtn
, badDER
);
3250 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3251 DERDecodedInfo piContent
;
3253 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
3254 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3255 DERPolicyInformation pi
;
3256 drtn
= DERParseSequenceContent(&piContent
.content
,
3257 DERNumPolicyInformationItemSpecs
,
3258 DERPolicyInformationItemSpecs
,
3260 require_noerr_quiet(drtn
, badDER
);
3261 CFStringRef piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3262 SEC_POLICY_IDENTIFIER_KEY
, pin
);
3263 CFStringRef piFmt
= SecCopyCertString(SEC_POLICY_IDENTIFIER_KEY
);
3264 CFStringRef lpiLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3267 appendOIDProperty(properties
, piLabel
, lpiLabel
, &pi
.policyIdentifier
);
3269 CFRelease(lpiLabel
);
3270 if (pi
.policyQualifiers
.length
== 0)
3274 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
3275 require_noerr_quiet(drtn
, badDER
);
3276 DERDecodedInfo pqContent
;
3278 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
3279 DERPolicyQualifierInfo pqi
;
3280 drtn
= DERParseSequenceContent(&pqContent
.content
,
3281 DERNumPolicyQualifierInfoItemSpecs
,
3282 DERPolicyQualifierInfoItemSpecs
,
3284 require_noerr_quiet(drtn
, badDER
);
3285 DERDecodedInfo qualifierContent
;
3286 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
3287 require_noerr_quiet(drtn
, badDER
);
3288 CFStringRef pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3289 SEC_POLICY_QUALIFIER_KEY
, pqn
);
3290 CFStringRef pqFmt
= SecCopyCertString(SEC_POLICY_QUALIFIER_KEY
);
3291 CFStringRef lpqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3294 appendOIDProperty(properties
, pqLabel
, lpqLabel
,
3295 &pqi
.policyQualifierID
);
3297 CFRelease(lpqLabel
);
3298 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
3299 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
3300 appendURLContentProperty(properties
, SEC_CPS_URI_KEY
,
3301 &qualifierContent
.content
);
3302 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
3303 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3305 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
3306 DERNumUserNoticeItemSpecs
,
3307 DERUserNoticeItemSpecs
,
3309 require_noerr_quiet(drtn
, badDER
);
3310 if (un
.noticeRef
.length
) {
3311 DERNoticeReference nr
;
3312 drtn
= DERParseSequenceContent(&un
.noticeRef
,
3313 DERNumNoticeReferenceItemSpecs
,
3314 DERNoticeReferenceItemSpecs
,
3316 require_noerr_quiet(drtn
, badDER
);
3317 appendDERThingProperty(properties
,
3318 SEC_ORGANIZATION_KEY
, NULL
,
3320 appendIntegerSequenceContent(properties
,
3321 SEC_NOTICE_NUMBERS_KEY
, &nr
.noticeNumbers
);
3323 if (un
.explicitText
.length
) {
3324 appendDERThingProperty(properties
, SEC_EXPLICIT_TEXT_KEY
,
3325 NULL
, &un
.explicitText
);
3328 appendUnparsedProperty(properties
, SEC_QUALIFIER_KEY
, NULL
,
3333 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3336 appendInvalidProperty(properties
, SEC_CERT_POLICIES_KEY
, extnValue
);
3339 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
3340 const DERItem
*extnValue
) {
3342 DERDecodedInfo keyIdentifier
;
3343 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
3344 require_noerr_quiet(drtn
, badDER
);
3345 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
3346 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3347 &keyIdentifier
.content
);
3351 appendInvalidProperty(properties
, SEC_SUBJ_KEY_ID_KEY
,
3356 AuthorityKeyIdentifier ::= SEQUENCE {
3357 keyIdentifier [0] KeyIdentifier OPTIONAL,
3358 authorityCertIssuer [1] GeneralNames OPTIONAL,
3359 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
3360 -- authorityCertIssuer and authorityCertSerialNumber MUST both
3361 -- be present or both be absent
3363 KeyIdentifier ::= OCTET STRING
3365 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
3366 const DERItem
*extnValue
) {
3367 DERAuthorityKeyIdentifier akid
;
3369 drtn
= DERParseSequence(extnValue
,
3370 DERNumAuthorityKeyIdentifierItemSpecs
,
3371 DERAuthorityKeyIdentifierItemSpecs
,
3372 &akid
, sizeof(akid
));
3373 require_noerr_quiet(drtn
, badDER
);
3374 if (akid
.keyIdentifier
.length
) {
3375 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3376 &akid
.keyIdentifier
);
3378 if (akid
.authorityCertIssuer
.length
||
3379 akid
.authorityCertSerialNumber
.length
) {
3380 require_quiet(akid
.authorityCertIssuer
.length
&&
3381 akid
.authorityCertSerialNumber
.length
, badDER
);
3382 /* Perhaps put in a subsection called Authority Certificate Issuer. */
3383 appendGeneralNamesContent(properties
,
3384 &akid
.authorityCertIssuer
);
3385 appendIntegerProperty(properties
, SEC_AUTH_CERT_SERIAL_KEY
,
3386 &akid
.authorityCertSerialNumber
);
3391 appendInvalidProperty(properties
, SEC_AUTHORITY_KEY_ID_KEY
, extnValue
);
3395 PolicyConstraints ::= SEQUENCE {
3396 requireExplicitPolicy [0] SkipCerts OPTIONAL,
3397 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
3399 SkipCerts ::= INTEGER (0..MAX)
3401 static void appendPolicyConstraints(CFMutableArrayRef properties
,
3402 const DERItem
*extnValue
) {
3403 DERPolicyConstraints pc
;
3405 drtn
= DERParseSequence(extnValue
,
3406 DERNumPolicyConstraintsItemSpecs
,
3407 DERPolicyConstraintsItemSpecs
,
3409 require_noerr_quiet(drtn
, badDER
);
3410 if (pc
.requireExplicitPolicy
.length
) {
3411 appendIntegerProperty(properties
, SEC_REQUIRE_EXPL_POLICY_KEY
,
3412 &pc
.requireExplicitPolicy
);
3414 if (pc
.inhibitPolicyMapping
.length
) {
3415 appendIntegerProperty(properties
, SEC_INHIBIT_POLICY_MAP_KEY
,
3416 &pc
.inhibitPolicyMapping
);
3422 appendInvalidProperty(properties
, SEC_POLICY_CONSTRAINTS_KEY
, extnValue
);
3426 extendedKeyUsage EXTENSION ::= {
3427 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
3428 IDENTIFIED BY id-ce-extKeyUsage }
3430 KeyPurposeId ::= OBJECT IDENTIFIER
3432 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
3433 const DERItem
*extnValue
) {
3436 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
3437 require_noerr_quiet(drtn
, badDER
);
3438 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3439 DERDecodedInfo currDecoded
;
3440 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3441 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
3442 appendOIDProperty(properties
, SEC_PURPOSE_KEY
, NULL
,
3443 &currDecoded
.content
);
3445 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3448 appendInvalidProperty(properties
, SEC_EXTENDED_KEY_USAGE_KEY
, extnValue
);
3452 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3454 AuthorityInfoAccessSyntax ::=
3455 SEQUENCE SIZE (1..MAX) OF AccessDescription
3457 AccessDescription ::= SEQUENCE {
3458 accessMethod OBJECT IDENTIFIER,
3459 accessLocation GeneralName }
3461 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3463 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3465 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3467 static void appendInfoAccess(CFMutableArrayRef properties
,
3468 const DERItem
*extnValue
) {
3471 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3472 require_noerr_quiet(drtn
, badDER
);
3473 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3474 DERDecodedInfo adContent
;
3475 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3476 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3477 DERAccessDescription ad
;
3478 drtn
= DERParseSequenceContent(&adContent
.content
,
3479 DERNumAccessDescriptionItemSpecs
,
3480 DERAccessDescriptionItemSpecs
,
3482 require_noerr_quiet(drtn
, badDER
);
3483 appendOIDProperty(properties
, SEC_ACCESS_METHOD_KEY
, NULL
,
3485 //TODO: Do something with SEC_ACCESS_LOCATION_KEY
3486 appendGeneralNameProperty(properties
, &ad
.accessLocation
);
3488 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3491 appendInvalidProperty(properties
, SEC_AUTH_INFO_ACCESS_KEY
, extnValue
);
3494 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3495 const DERItem
*extnValue
) {
3496 static const CFStringRef certTypes
[] = {
3500 SEC_OBJECT_SIGNING_KEY
,
3504 SEC_OBJECT_SIGNING_CA_KEY
3506 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3507 certTypes
, array_size(certTypes
));
3511 static void appendEntrustVersInfo(CFMutableArrayRef properties
,
3512 const DERItem
*extnValue
) {
3516 * The list of Qualified Cert Statement statementIds we understand, even though
3517 * we don't actually do anything with them; if these are found in a Qualified
3518 * Cert Statement that's critical, we can truthfully say "yes we understand this".
3520 static const CSSM_OID_PTR knownQualifiedCertStatements
[] =
3522 /* id-qcs := { id-pkix 11 } */
3523 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V1
, /* id-qcs 1 */
3524 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V2
, /* id-qcs 2 */
3525 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_COMPLIANCE
,
3526 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE
,
3527 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_RETENTION
,
3528 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_SSCD
3530 #define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
3532 static void appendQCCertStatements(CFMutableArrayRef properties
,
3533 const DERItem
*extnValue
) {
3538 static bool appendPrintableDERSequence(CFMutableArrayRef properties
,
3539 CFStringRef label
, const DERItem
*sequence
) {
3542 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3543 require_noerr_quiet(drtn
, badSequence
);
3544 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3545 DERDecodedInfo currDecoded
;
3546 bool appendedSomething
= false;
3547 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3548 switch (currDecoded
.tag
)
3551 case ASN1_SEQUENCE
: // 16
3552 case ASN1_SET
: // 17
3553 // skip constructed object lengths
3556 case ASN1_UTF8_STRING
: // 12
3557 case ASN1_NUMERIC_STRING
: // 18
3558 case ASN1_PRINTABLE_STRING
: // 19
3559 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3560 case ASN1_VIDEOTEX_STRING
: // 21
3561 case ASN1_IA5_STRING
: // 22
3562 case ASN1_GRAPHIC_STRING
: // 25
3563 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3564 case ASN1_GENERAL_STRING
: // 27
3565 case ASN1_UNIVERSAL_STRING
: // 28
3567 CFStringRef string
=
3568 copyDERThingContentDescription(CFGetAllocator(properties
),
3569 currDecoded
.tag
, &currDecoded
.content
, false);
3570 //CFStringRef cleanString = copyStringRemovingPercentEscapes(string);
3572 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3575 appendedSomething
= true;
3582 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3583 return appendedSomething
;
3588 static void appendExtension(CFMutableArrayRef parent
,
3589 const SecCertificateExtension
*extn
) {
3590 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3591 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3592 &kCFTypeArrayCallBacks
);
3594 *extnID
= &extn
->extnID
,
3595 *extnValue
= &extn
->extnValue
;
3596 CFStringRef label
= NULL
;
3597 CFStringRef localizedLabel
= NULL
;
3599 appendBoolProperty(properties
, SEC_CRITICAL_KEY
, extn
->critical
);
3600 require_quiet(extnID
, xit
);
3603 bool handled
= true;
3604 /* Extensions that we know how to handle ourselves... */
3605 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3606 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3608 switch (extnID
->data
[extnID
->length
- 1]) {
3609 case 14: /* SubjectKeyIdentifier id-ce 14 */
3610 appendSubjectKeyIdentifier(properties
, extnValue
);
3612 case 15: /* KeyUsage id-ce 15 */
3613 appendKeyUsage(properties
, extnValue
);
3615 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3616 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3618 case 17: /* SubjectAltName id-ce 17 */
3619 case 18: /* IssuerAltName id-ce 18 */
3620 appendGeneralNames(properties
, extnValue
);
3622 case 19: /* BasicConstraints id-ce 19 */
3623 appendBasicConstraints(properties
, extnValue
);
3625 case 30: /* NameConstraints id-ce 30 */
3626 appendNameConstraints(properties
, extnValue
);
3628 case 31: /* CRLDistributionPoints id-ce 31 */
3629 appendCrlDistributionPoints(properties
, extnValue
);
3631 case 32: /* CertificatePolicies id-ce 32 */
3632 appendCertificatePolicies(properties
, extnValue
);
3634 case 33: /* PolicyMappings id-ce 33 */
3637 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3638 appendAuthorityKeyIdentifier(properties
, extnValue
);
3640 case 36: /* PolicyConstraints id-ce 36 */
3641 appendPolicyConstraints(properties
, extnValue
);
3643 case 37: /* ExtKeyUsage id-ce 37 */
3644 appendExtendedKeyUsage(properties
, extnValue
);
3646 case 46: /* FreshestCRL id-ce 46 */
3649 case 54: /* InhibitAnyPolicy id-ce 54 */
3656 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3657 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3659 switch (extnID
->data
[extnID
->length
- 1]) {
3660 case 1: /* AuthorityInfoAccess id-pe 1 */
3661 appendInfoAccess(properties
, extnValue
);
3663 case 3: /* QCStatements id-pe 3 */
3666 case 11: /* SubjectInfoAccess id-pe 11 */
3667 appendInfoAccess(properties
, extnValue
);
3673 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3674 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3675 appendNetscapeCertType(properties
, extnValue
);
3681 /* Try to parse and display printable string(s). */
3682 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
)) {
3683 /* Nothing to do here appendPrintableDERSequence did the work. */
3685 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3686 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
);
3690 /* Extensions that we know how to handle ourselves... */
3691 if (DEROidCompare(extnID
, &oidSubjectKeyIdentifier
)) {
3692 appendSubjectKeyIdentifier(properties
, extnValue
);
3693 } else if (DEROidCompare(extnID
, &oidKeyUsage
)) {
3694 appendKeyUsage(properties
, extnValue
);
3695 } else if (DEROidCompare(extnID
, &oidPrivateKeyUsagePeriod
)) {
3696 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3697 } else if (DEROidCompare(extnID
, &oidSubjectAltName
)) {
3698 appendGeneralNames(properties
, extnValue
);
3699 } else if (DEROidCompare(extnID
, &oidIssuerAltName
)) {
3700 appendGeneralNames(properties
, extnValue
);
3701 } else if (DEROidCompare(extnID
, &oidBasicConstraints
)) {
3702 appendBasicConstraints(properties
, extnValue
);
3703 } else if (DEROidCompare(extnID
, &oidNameConstraints
)) {
3704 appendNameConstraints(properties
, extnValue
);
3705 } else if (DEROidCompare(extnID
, &oidCrlDistributionPoints
)) {
3706 appendCrlDistributionPoints(properties
, extnValue
);
3707 } else if (DEROidCompare(extnID
, &oidCertificatePolicies
)) {
3708 appendCertificatePolicies(properties
, extnValue
);
3709 } else if (DEROidCompare(extnID
, &oidAuthorityKeyIdentifier
)) {
3710 appendAuthorityKeyIdentifier(properties
, extnValue
);
3711 } else if (DEROidCompare(extnID
, &oidPolicyConstraints
)) {
3712 appendPolicyConstraints(properties
, extnValue
);
3713 } else if (DEROidCompare(extnID
, &oidExtendedKeyUsage
)) {
3714 appendExtendedKeyUsage(properties
, extnValue
);
3715 } else if (DEROidCompare(extnID
, &oidAuthorityInfoAccess
)) {
3716 appendInfoAccess(properties
, extnValue
);
3717 } else if (DEROidCompare(extnID
, &oidSubjectInfoAccess
)) {
3718 appendInfoAccess(properties
, extnValue
);
3719 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3720 appendNetscapeCertType(properties
, extnValue
);
3722 } else if (DEROidCompare(extnID
, &oidEntrustVersInfo
)) {
3723 appendEntrustVersInfo(properties
, extnValue
);
3726 /* Try to parse and display printable string(s). */
3727 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
)) {
3728 /* Nothing to do here appendPrintableDERSequence did the work. */
3730 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3731 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
);
3734 label
= SecDERItemCopyOIDDecimalRepresentation(allocator
, extnID
);
3735 localizedLabel
= copyLocalizedOidDescription(allocator
, extnID
);
3736 appendProperty(parent
, kSecPropertyTypeSection
, label
, localizedLabel
, properties
);
3739 CFReleaseSafe(localizedLabel
);
3740 CFReleaseSafe(label
);
3741 CFReleaseSafe(properties
);
3744 /* Different types of summary types from least desired to most desired. */
3747 kSummaryTypePrintable
,
3748 kSummaryTypeOrganizationName
,
3749 kSummaryTypeOrganizationalUnitName
,
3750 kSummaryTypeCommonName
,
3754 enum SummaryType type
;
3755 CFStringRef summary
;
3756 CFStringRef description
;
3759 static OSStatus
obtainSummaryFromX501Name(void *context
,
3760 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
3761 struct Summary
*summary
= (struct Summary
*)context
;
3762 enum SummaryType stype
= kSummaryTypeNone
;
3763 CFStringRef string
= NULL
;
3764 if (DEROidCompare(type
, &oidCommonName
)) {
3765 stype
= kSummaryTypeCommonName
;
3766 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
3767 stype
= kSummaryTypeOrganizationalUnitName
;
3768 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
3769 stype
= kSummaryTypeOrganizationName
;
3770 } else if (DEROidCompare(type
, &oidDescription
)) {
3771 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3773 if (summary
->description
) {
3774 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3775 CFStringRef newDescription
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, string
, summary
->description
);
3777 CFRelease(summary
->description
);
3778 summary
->description
= newDescription
;
3780 summary
->description
= string
;
3783 stype
= kSummaryTypePrintable
;
3786 stype
= kSummaryTypePrintable
;
3789 /* Build a string with all instances of the most desired
3790 component type in reverse order encountered comma separated list,
3791 The order of desirability is defined by enum SummaryType. */
3792 if (summary
->type
<= stype
) {
3794 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3797 if (summary
->type
== stype
) {
3798 CFStringRef fmt
= SecCopyCertString(SEC_STRING_LIST_KEY
);
3799 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, string
, summary
->summary
);
3802 string
= newSummary
;
3804 summary
->type
= stype
;
3806 CFReleaseSafe(summary
->summary
);
3807 summary
->summary
= string
;
3810 CFReleaseSafe(string
);
3813 return errSecSuccess
;
3816 CFStringRef
SecCertificateCopySubjectSummary(SecCertificateRef certificate
) {
3817 struct Summary summary
= {};
3818 parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
);
3819 /* If we found a description and a common name we change the summary to
3820 CommonName (Description). */
3821 if (summary
.description
) {
3822 if (summary
.type
== kSummaryTypeCommonName
) {
3823 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3824 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3826 CFRelease(summary
.summary
);
3827 summary
.summary
= newSummary
;
3829 CFRelease(summary
.description
);
3832 if (!summary
.summary
) {
3833 /* If we didn't find a suitable printable string in the subject at all, we try
3834 the first email address in the certificate instead. */
3835 CFArrayRef names
= SecCertificateCopyRFC822Names(certificate
);
3837 /* If we didn't find any email addresses in the certificate, we try finding
3838 a DNS name instead. */
3839 names
= SecCertificateCopyDNSNames(certificate
);
3842 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
3843 CFRetain(summary
.summary
);
3848 return summary
.summary
;
3851 CFStringRef
SecCertificateCopyIssuerSummary(SecCertificateRef certificate
) {
3852 struct Summary summary
= {};
3853 parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
);
3854 /* If we found a description and a common name we change the summary to
3855 CommonName (Description). */
3856 if (summary
.description
) {
3857 if (summary
.type
== kSummaryTypeCommonName
) {
3858 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
3859 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
3861 CFRelease(summary
.summary
);
3862 summary
.summary
= newSummary
;
3864 CFRelease(summary
.description
);
3867 return summary
.summary
;
3870 /* Return the earliest date on which all certificates in this chain are still
3872 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
3873 SecCertificateRef certificate
) {
3874 CFAbsoluteTime earliest
= certificate
->_notAfter
;
3876 while (certificate
->_parent
) {
3877 certificate
= certificate
->_parent
;
3878 if (earliest
> certificate
->_notAfter
)
3879 earliest
= certificate
->_notAfter
;
3886 /* Return the latest date on which all certificates in this chain will be
3888 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
3889 SecCertificateRef certificate
) {
3890 CFAbsoluteTime latest
= certificate
->_notBefore
;
3892 while (certificate
->_parent
) {
3893 certificate
= certificate
->_parent
;
3894 if (latest
< certificate
->_notBefore
)
3895 latest
= certificate
->_notBefore
;
3902 bool SecCertificateIsValid(SecCertificateRef certificate
,
3903 CFAbsoluteTime verifyTime
) {
3904 return certificate
&& certificate
->_notBefore
<= verifyTime
&&
3905 verifyTime
<= certificate
->_notAfter
;
3908 CFIndex
SecCertificateVersion(SecCertificateRef certificate
) {
3909 return certificate
->_version
+ 1;
3912 CFAbsoluteTime
SecCertificateNotValidBefore(SecCertificateRef certificate
) {
3913 return certificate
->_notBefore
;
3916 CFAbsoluteTime
SecCertificateNotValidAfter(SecCertificateRef certificate
) {
3917 return certificate
->_notAfter
;
3920 CFMutableArrayRef
SecCertificateCopySummaryProperties(
3921 SecCertificateRef certificate
, CFAbsoluteTime verifyTime
) {
3922 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3923 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
3924 &kCFTypeArrayCallBacks
);
3926 /* First we put the subject summary name. */
3927 CFStringRef ssummary
= SecCertificateCopySubjectSummary(certificate
);
3929 appendProperty(summary
, kSecPropertyTypeTitle
,
3930 NULL
, NULL
, ssummary
);
3931 CFRelease(ssummary
);
3934 CFStringRef isummary
= SEC_ISSUER_SUMMARY_KEY
;
3935 appendProperty(summary
, kSecPropertyTypeString
,
3936 SEC_ISSUED_BY_KEY
, isummary
);
3937 CFRelease(isummary
);
3940 /* Let see if this certificate is currently valid. */
3942 CFAbsoluteTime when
;
3943 CFStringRef message
;
3945 if (verifyTime
> certificate
->_notAfter
) {
3946 label
= SEC_EXPIRED_KEY
;
3947 when
= certificate
->_notAfter
;
3948 ptype
= kSecPropertyTypeError
;
3949 message
= SEC_CERT_EXPIRED_KEY
;
3950 } else if (certificate
->_notBefore
> verifyTime
) {
3951 label
= SEC_VALID_FROM_KEY
;
3952 when
= certificate
->_notBefore
;
3953 ptype
= kSecPropertyTypeError
;
3954 message
= SEC_CERT_NOT_YET_VALID_KEY
;
3956 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
3957 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
3958 if (verifyTime
> last
) {
3959 label
= SEC_EXPIRED_KEY
;
3961 ptype
= kSecPropertyTypeError
;
3962 message
= SEC_ISSUER_EXPIRED_KEY
;
3963 } else if (verifyTime
< first
) {
3964 label
= SEC_VALID_FROM_KEY
;
3966 ptype
= kSecPropertyTypeError
;
3967 message
= SEC_ISSR_NOT_YET_VALID_KEY
;
3969 label
= SEC_EXPIRES_KEY
;
3970 when
= certificate
->_notAfter
;
3971 ptype
= kSecPropertyTypeSuccess
;
3972 message
= SEC_CERT_VALID_KEY
;
3976 appendDateProperty(summary
, label
, when
);
3977 CFStringRef lmessage
= SecCopyCertString(message
);
3978 appendProperty(summary
, ptype
, NULL
, NULL
, lmessage
);
3979 CFRelease(lmessage
);
3984 CFArrayRef
SecCertificateCopyProperties(SecCertificateRef certificate
) {
3985 if (!certificate
->_properties
) {
3986 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3987 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3988 &kCFTypeArrayCallBacks
);
3990 /* First we put the Subject Name in the property list. */
3991 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
3992 &certificate
->_subject
);
3993 appendProperty(properties
, kSecPropertyTypeSection
,
3994 SEC_SUBJECT_NAME_KEY
, NULL
, subject_plist
);
3995 CFRelease(subject_plist
);
3998 /* Put Normalized subject in for testing. */
3999 if (certificate
->_normalizedSubject
) {
4000 DERItem nsubject
= {
4001 (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
),
4002 CFDataGetLength(certificate
->_normalizedSubject
)
4004 CFArrayRef nsubject_plist
= createPropertiesForX501NameContent(allocator
,
4006 appendProperty(properties
, kSecPropertyTypeSection
,
4007 CFSTR("Normalized Subject Name"), nsubject_plist
);
4008 CFRelease(nsubject_plist
);
4012 /* Next we put the Issuer Name in the property list. */
4013 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
4014 &certificate
->_issuer
);
4015 appendProperty(properties
, kSecPropertyTypeSection
,
4016 SEC_ISSUER_NAME_KEY
, NULL
, issuer_plist
);
4017 CFRelease(issuer_plist
);
4020 /* Certificate version/type. */
4021 bool isRoot
= false;
4022 CFStringRef fmt
= SecCopyCertString(SEC_X509_VERSION_KEY
);
4023 CFStringRef typeString
= CFStringCreateWithFormat(allocator
, NULL
,
4024 fmt
, certificate
->_version
+ 1, isRoot
? "root " : "");
4026 appendProperty(properties
, kSecPropertyTypeString
,
4027 SEC_CERTIFICATE_TYPE_KEY
, typeString
);
4028 CFRelease(typeString
);
4032 CFStringRef fmt
= SecCopyCertString(SEC_CERT_VERSION_VALUE_KEY
);
4033 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
4034 NULL
, fmt
, certificate
->_version
+ 1);
4036 appendProperty(properties
, kSecPropertyTypeString
,
4037 SEC_VERSION_KEY
, NULL
, versionString
);
4038 CFRelease(versionString
);
4041 if (certificate
->_serialNum
.length
) {
4042 appendIntegerProperty(properties
, SEC_SERIAL_NUMBER_KEY
,
4043 &certificate
->_serialNum
);
4046 /* Signature algorithm. */
4048 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
4049 &certificate
->_sigAlg
);
4051 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
4052 &certificate
->_tbsSigAlg
);
4055 /* Validity dates. */
4056 appendDateProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
4057 certificate
->_notBefore
);
4058 appendDateProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
4059 certificate
->_notAfter
);
4061 if (certificate
->_subjectUniqueID
.length
) {
4062 appendDataProperty(properties
, SEC_SUBJECT_UNIQUE_ID_KEY
, NULL
,
4063 &certificate
->_subjectUniqueID
);
4065 if (certificate
->_issuerUniqueID
.length
) {
4066 appendDataProperty(properties
, SEC_ISSUER_UNIQUE_ID_KEY
, NULL
,
4067 &certificate
->_issuerUniqueID
);
4070 /* Public key algorithm. */
4071 appendAlgorithmProperty(properties
, SEC_PUBLIC_KEY_ALG_KEY
,
4072 &certificate
->_algId
);
4074 /* Consider breaking down an RSA public key into modulus and
4076 appendDataProperty(properties
, SEC_PULIC_KEY_DATA_KEY
, NULL
,
4077 &certificate
->_pubKeyDER
);
4078 /* TODO: Add Key Size. */
4079 /* TODO: Add Key Usage. */
4081 appendDataProperty(properties
, SEC_SIGNATURE_KEY
, NULL
,
4082 &certificate
->_signature
);
4085 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4086 appendExtension(properties
, &certificate
->_extensions
[ix
]);
4089 /* TODO: Certificate/Key Fingerprints. */
4091 certificate
->_properties
= properties
;
4094 CFRetain(certificate
->_properties
);
4095 return certificate
->_properties
;
4098 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
4099 /* On OS X, the SecCertificateCopySerialNumber API takes two arguments. */
4100 CFDataRef
SecCertificateCopySerialNumber(
4101 SecCertificateRef certificate
,
4102 CFErrorRef
*error
) {
4105 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
4109 if (certificate
->_serialNumber
) {
4110 CFRetain(certificate
->_serialNumber
);
4112 return certificate
->_serialNumber
;
4115 /* On iOS, the SecCertificateCopySerialNumber API takes one argument. */
4116 CFDataRef
SecCertificateCopySerialNumber(
4117 SecCertificateRef certificate
) {
4118 if (certificate
->_serialNumber
) {
4119 CFRetain(certificate
->_serialNumber
);
4121 return certificate
->_serialNumber
;
4125 CFDataRef
SecCertificateGetNormalizedIssuerContent(
4126 SecCertificateRef certificate
) {
4127 return certificate
->_normalizedIssuer
;
4130 CFDataRef
SecCertificateGetNormalizedSubjectContent(
4131 SecCertificateRef certificate
) {
4132 return certificate
->_normalizedSubject
;
4135 /* Verify that certificate was signed by issuerKey. */
4136 OSStatus
SecCertificateIsSignedBy(SecCertificateRef certificate
,
4137 SecKeyRef issuerKey
) {
4138 /* Setup algId in SecAsn1AlgId format. */
4140 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
4141 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
4142 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
4143 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
4145 OSStatus status
= SecKeyDigestAndVerify(issuerKey
, &algId
,
4146 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
4147 certificate
->_signature
.data
, certificate
->_signature
.length
);
4149 secdebug("verify", "signature verify failed: %" PRIdOSStatus
, status
);
4150 return errSecNotSigner
;
4153 return errSecSuccess
;
4157 static OSStatus
SecCertificateIsIssuedBy(SecCertificateRef certificate
,
4158 SecCertificateRef issuer
, bool signatureCheckOnly
) {
4159 if (!signatureCheckOnly
) {
4160 /* It turns out we don't actually need to use normalized subject and
4161 issuer according to rfc2459. */
4163 /* If present we should check issuerID against the issuer subjectID. */
4165 /* If we have an AuthorityKeyIdentifier extension that has a keyIdentifier
4166 then we should look for a SubjectKeyIdentifier in the issuer
4168 If we have a authorityCertSerialNumber we can use that for chaining.
4169 If we have a authorityCertIssuer we can use that? (or not) */
4171 /* Verify that this cert was issued by issuer. Do so by chaining
4172 either issuerID to subjectID or normalized issuer to normalized
4174 CFDataRef normalizedIssuer
=
4175 SecCertificateGetNormalizedIssuerContent(certificate
);
4176 CFDataRef normalizedIssuerSubject
=
4177 SecCertificateGetNormalizedSubjectContent(issuer
);
4178 if (normalizedIssuer
&& normalizedIssuerSubject
&&
4179 !CFEqual(normalizedIssuer
, normalizedIssuerSubject
))
4180 return errSecIssuerMismatch
;
4183 /* Next verify that this cert was signed by issuer. */
4184 SecKeyRef issuerKey
= SecCertificateGetPublicKey(issuer
);
4186 /* Get the encodedDigestInfo from the digest of the subject's TBSCert */
4187 /* FIXME: We sould cache this (or at least the digest) until we find
4188 a suitable issuer. */
4189 uint8_t signedData
[DER_SHA1_DIGEST_INFO_LEN
];
4190 CFIndex signedDataLength
;
4191 CertVerifyReturn crtn
;
4192 if (DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidSha1Rsa
)) {
4193 signedDataLength
= DER_SHA1_DIGEST_INFO_LEN
;
4194 crtn
= sha1DigestInfo(&certificate
->_tbs
, signedData
);
4195 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd5Rsa
)) {
4196 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
4197 crtn
= mdDigestInfo(WD_MD5
, &certificate
->_tbs
, signedData
);
4198 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd2Rsa
)) {
4199 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
4200 crtn
= mdDigestInfo(WD_MD2
, &certificate
->_tbs
, signedData
);
4202 secdebug("verify", "unsupported algorithm");
4203 return errSecUnsupportedAlgorithm
;
4206 secdebug("verify", "*DigestInfo returned: %d", crtn
);
4207 /* FIXME: Do proper error code translation. */
4208 return errSecUnsupportedAlgorithm
;
4211 OSStatus status
= SecKeyRawVerify(issuerKey
, kSecPaddingPKCS1
,
4212 signedData
, signedDataLength
,
4213 certificate
->_signature
.data
, certificate
->_signature
.length
);
4215 secdebug("verify", "signature verify failed: %d", status
);
4216 return errSecNotSigner
;
4219 return errSecSuccess
;
4222 static OSStatus
_SecCertificateSetParent(SecCertificateRef certificate
,
4223 SecCertificateRef issuer
, bool signatureCheckOnly
) {
4225 if (certificate
->_parent
) {
4226 /* Setting a certificates issuer twice is only allowed if the new
4227 issuer is equal to the current one. */
4228 return issuer
&& CFEqual(certificate
->_parent
, issuer
);
4232 OSStatus status
= SecCertificateIsIssuedBy(certificate
, issuer
,
4233 signatureCheckOnly
);
4235 OSStatus status
= errSecSuccess
;
4238 if (CFEqual(certificate
, issuer
)) {
4239 /* We don't retain ourselves cause that would be bad mojo,
4240 however we do record that we are properly self signed. */
4241 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
4242 secdebug("cert", "set self as parent");
4243 return errSecSuccess
;
4247 certificate
->_parent
= issuer
;
4248 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
4254 /* Return true iff we were able to set our own parent from one of the
4255 certificates in other_certificates, return false otherwise. If
4256 signatureCheckOnly is true, we can skip the subject == issuer or
4257 authorityKeyIdentifier tests. */
4258 static bool SecCertificateSetParentFrom(SecCertificateRef certificate
,
4259 CFArrayRef other_certificates
, bool signatureCheckOnly
) {
4260 CFIndex count
= CFArrayGetCount(other_certificates
);
4262 for (ix
= 0; ix
< count
; ++ix
) {
4263 SecCertificateRef candidate
= (SecCertificateRef
)
4264 CFArrayGetValueAtIndex(other_certificates
, ix
);
4265 if (_SecCertificateSetParent(certificate
, candidate
,
4266 signatureCheckOnly
))
4272 /* Lookup the parent of certificate in the keychain and set it. */
4273 static bool SecCertificateFindParent(SecCertificateRef certificate
) {
4274 /* FIXME: Search for things other than just subject of our issuer if we
4275 have a subjectID or authorityKeyIdentifier. */
4276 CFDataRef normalizedIssuer
=
4277 SecCertificateGetNormalizedIssuerContent(certificate
);
4278 const void *keys
[] = {
4285 kSecClassCertificate
,
4290 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4,
4291 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4293 OSStatus status
= SecItemCopyMatching(query
, &results
);
4296 secdebug("cert", "SecCertificateFindParent: SecItemCopyMatching: %d",
4300 CFArrayRef certs
= (CFArrayRef
)results
;
4301 /* Since we already know the certificates we are providing as candidates
4302 have been checked for subject matching, we can ask
4303 SecCertificateSetParentFrom to skip everything except the signature
4305 bool result
= SecCertificateSetParentFrom(certificate
, certs
, true);
4310 OSStatus
SecCertificateCompleteChain(SecCertificateRef certificate
,
4311 CFArrayRef other_certificates
) {
4313 if (certificate
->_parent
== NULL
) {
4314 Boolean isSelfSigned
= false;
4315 OSStatus status
= SecCertificateIsSelfSigned(certificate
, &isSelfSigned
);
4316 if (!status
&& isSelfSigned
)
4317 return errSecSuccess
;
4318 if (!other_certificates
||
4319 !SecCertificateSetParentFrom(certificate
, other_certificates
,
4321 if (!SecCertificateFindParent(certificate
))
4322 return errSecIssuerNotFound
;
4325 certificate
= certificate
->_parent
;
4330 const DERItem
* SecCertificateGetSubjectAltName(SecCertificateRef certificate
) {
4331 if (!certificate
->_subjectAltName
) {
4334 return &certificate
->_subjectAltName
->extnValue
;
4337 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
4338 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4339 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
4340 if (gnType
== GNT_IPAddress
) {
4341 CFStringRef string
= copyIPAddressContentDescription(
4342 kCFAllocatorDefault
, generalName
);
4344 CFArrayAppendValue(ipAddresses
, string
);
4347 return errSecInvalidCertificate
;
4350 return errSecSuccess
;
4353 CFArrayRef
SecCertificateCopyIPAddresses(SecCertificateRef certificate
) {
4354 /* These can only exist in the subject alt name. */
4355 if (!certificate
->_subjectAltName
)
4358 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
4359 0, &kCFTypeArrayCallBacks
);
4360 OSStatus status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4361 ipAddresses
, appendIPAddressesFromGeneralNames
);
4362 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
4363 CFRelease(ipAddresses
);
4369 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
4370 const DERItem
*generalName
) {
4371 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4372 if (gnType
== GNT_DNSName
) {
4373 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4374 generalName
->data
, generalName
->length
,
4375 kCFStringEncodingUTF8
, FALSE
);
4377 CFArrayAppendValue(dnsNames
, string
);
4380 return errSecInvalidCertificate
;
4383 return errSecSuccess
;
4386 /* Return true if the passed in string matches the
4387 Preferred name syntax from sections 2.3.1. in RFC 1035.
4388 With the added check that we disallow empty dns names.
4389 Also in order to support wildcard DNSNames we allow for the '*'
4390 character anywhere in a dns component where we currently allow
4393 <domain> ::= <subdomain> | " "
4395 <subdomain> ::= <label> | <subdomain> "." <label>
4397 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
4399 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
4401 <let-dig-hyp> ::= <let-dig> | "-"
4403 <let-dig> ::= <letter> | <digit>
4405 <letter> ::= any one of the 52 alphabetic characters A through Z in
4406 upper case and a through z in lower case
4408 <digit> ::= any one of the ten digits 0 through 9
4410 static bool isDNSName(CFStringRef string
) {
4411 CFStringInlineBuffer buf
;
4412 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
4413 /* From RFC 1035 2.3.4. Size limits:
4414 labels 63 octets or less
4415 names 255 octets or less */
4416 require_quiet(length
<= 255, notDNS
);
4417 CFRange range
= { 0, length
};
4418 CFStringInitInlineBuffer(string
, &buf
, range
);
4422 kDNSStateAfterAlpha
,
4423 kDNSStateAfterDigit
,
4425 } state
= kDNSStateInital
;
4427 for (ix
= 0; ix
< length
; ++ix
) {
4428 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
4431 require_quiet(labelLength
<= 64 &&
4432 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4434 state
= kDNSStateAfterDot
;
4436 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
4438 state
= kDNSStateAfterAlpha
;
4439 } else if ('0' <= ch
&& ch
<= '9') {
4441 /* The requirement for labels to start with a letter was
4442 dropped so we don't check this anymore. */
4443 require_quiet(state
== kDNSStateAfterAlpha
||
4444 state
== kDNSStateAfterDigit
||
4445 state
== kDNSStateAfterDash
, notDNS
);
4447 state
= kDNSStateAfterDigit
;
4448 } else if (ch
== '-') {
4449 require_quiet(state
== kDNSStateAfterAlpha
||
4450 state
== kDNSStateAfterDigit
||
4451 state
== kDNSStateAfterDash
, notDNS
);
4452 state
= kDNSStateAfterDash
;
4458 /* We don't allow a dns name to end in a dot or dash. */
4459 require_quiet(labelLength
<= 63 &&
4460 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4468 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4469 const DERItem
*value
, CFIndex rdnIX
) {
4470 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4471 if (DEROidCompare(type
, &oidCommonName
)) {
4472 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4475 if (isDNSName(string
)) {
4476 /* We found a common name that is formatted like a valid
4478 CFArrayAppendValue(dnsNames
, string
);
4482 return errSecInvalidCertificate
;
4485 return errSecSuccess
;
4488 /* Not everything returned by this function is going to be a proper DNS name,
4489 we also return the certificates common name entries from the subject,
4490 assuming they look like dns names as specified in RFC 1035. */
4491 CFArrayRef
SecCertificateCopyDNSNames(SecCertificateRef certificate
) {
4492 /* These can exist in the subject alt name or in the subject. */
4493 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4494 0, &kCFTypeArrayCallBacks
);
4495 OSStatus status
= errSecSuccess
;
4496 if (certificate
->_subjectAltName
) {
4497 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4498 dnsNames
, appendDNSNamesFromGeneralNames
);
4500 /* RFC 2818 section 3.1. Server Identity
4502 If a subjectAltName extension of type dNSName is present, that MUST
4503 be used as the identity. Otherwise, the (most specific) Common Name
4504 field in the Subject field of the certificate MUST be used. Although
4505 the use of the Common Name is existing practice, it is deprecated and
4506 Certification Authorities are encouraged to use the dNSName instead.
4509 This implies that if we found 1 or more DNSNames in the
4510 subjectAltName, we should not use the Common Name of the subject as
4513 if (!status
&& CFArrayGetCount(dnsNames
) == 0) {
4514 status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4515 appendDNSNamesFromX501Name
);
4517 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4518 CFRelease(dnsNames
);
4524 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4525 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4526 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4527 if (gnType
== GNT_RFC822Name
) {
4528 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4529 generalName
->data
, generalName
->length
,
4530 kCFStringEncodingASCII
, FALSE
);
4532 CFArrayAppendValue(dnsNames
, string
);
4535 return errSecInvalidCertificate
;
4538 return errSecSuccess
;
4541 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4542 const DERItem
*value
, CFIndex rdnIX
) {
4543 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4544 if (DEROidCompare(type
, &oidEmailAddress
)) {
4545 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4548 CFArrayAppendValue(dnsNames
, string
);
4551 return errSecInvalidCertificate
;
4554 return errSecSuccess
;
4557 CFArrayRef
SecCertificateCopyRFC822Names(SecCertificateRef certificate
) {
4558 /* These can exist in the subject alt name or in the subject. */
4559 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4560 0, &kCFTypeArrayCallBacks
);
4561 OSStatus status
= errSecSuccess
;
4562 if (certificate
->_subjectAltName
) {
4563 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4564 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4567 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4568 appendRFC822NamesFromX501Name
);
4570 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4571 CFRelease(rfc822Names
);
4577 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4578 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4579 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4580 if (DEROidCompare(type
, &oidCommonName
)) {
4581 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4584 CFArrayAppendValue(commonNames
, string
);
4587 return errSecInvalidCertificate
;
4590 return errSecSuccess
;
4593 CFArrayRef
SecCertificateCopyCommonNames(SecCertificateRef certificate
) {
4594 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4595 0, &kCFTypeArrayCallBacks
);
4597 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4598 appendCommonNamesFromX501Name
);
4599 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4600 CFRelease(commonNames
);
4606 static OSStatus
appendOrganizationFromX501Name(void *context
,
4607 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4608 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4609 if (DEROidCompare(type
, &oidOrganizationName
)) {
4610 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4613 CFArrayAppendValue(organization
, string
);
4616 return errSecInvalidCertificate
;
4619 return errSecSuccess
;
4622 CFArrayRef
SecCertificateCopyOrganization(SecCertificateRef certificate
) {
4623 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4624 0, &kCFTypeArrayCallBacks
);
4626 status
= parseX501NameContent(&certificate
->_subject
, organization
,
4627 appendOrganizationFromX501Name
);
4628 if (status
|| CFArrayGetCount(organization
) == 0) {
4629 CFRelease(organization
);
4630 organization
= NULL
;
4632 return organization
;
4635 static OSStatus
appendOrganizationalUnitFromX501Name(void *context
,
4636 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4637 CFMutableArrayRef organizationalUnit
= (CFMutableArrayRef
)context
;
4638 if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4639 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4642 CFArrayAppendValue(organizationalUnit
, string
);
4645 return errSecInvalidCertificate
;
4648 return errSecSuccess
;
4651 CFArrayRef
SecCertificateCopyOrganizationalUnit(SecCertificateRef certificate
) {
4652 CFMutableArrayRef organizationalUnit
= CFArrayCreateMutable(kCFAllocatorDefault
,
4653 0, &kCFTypeArrayCallBacks
);
4655 status
= parseX501NameContent(&certificate
->_subject
, organizationalUnit
,
4656 appendOrganizationalUnitFromX501Name
);
4657 if (status
|| CFArrayGetCount(organizationalUnit
) == 0) {
4658 CFRelease(organizationalUnit
);
4659 organizationalUnit
= NULL
;
4661 return organizationalUnit
;
4664 const SecCEBasicConstraints
*
4665 SecCertificateGetBasicConstraints(SecCertificateRef certificate
) {
4666 if (certificate
->_basicConstraints
.present
)
4667 return &certificate
->_basicConstraints
;
4672 CFArrayRef
SecCertificateGetPermittedSubtrees(SecCertificateRef certificate
) {
4673 return (certificate
->_permittedSubtrees
);
4676 CFArrayRef
SecCertificateGetExcludedSubtrees(SecCertificateRef certificate
) {
4677 return (certificate
->_excludedSubtrees
);
4680 const SecCEPolicyConstraints
*
4681 SecCertificateGetPolicyConstraints(SecCertificateRef certificate
) {
4682 if (certificate
->_policyConstraints
.present
)
4683 return &certificate
->_policyConstraints
;
4689 SecCertificateGetPolicyMappings(SecCertificateRef certificate
) {
4690 return certificate
->_policyMappings
;
4693 const SecCECertificatePolicies
*
4694 SecCertificateGetCertificatePolicies(SecCertificateRef certificate
) {
4695 if (certificate
->_certificatePolicies
.present
)
4696 return &certificate
->_certificatePolicies
;
4702 SecCertificateGetInhibitAnyPolicySkipCerts(SecCertificateRef certificate
) {
4703 return certificate
->_inhibitAnyPolicySkipCerts
;
4706 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
4707 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4708 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
4709 if (gnType
== GNT_OtherName
) {
4711 DERReturn drtn
= DERParseSequenceContent(generalName
,
4712 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
4714 require_noerr_quiet(drtn
, badDER
);
4715 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
4717 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
4718 &on
.value
, true), badDER
);
4719 CFArrayAppendValue(ntPrincipalNames
, string
);
4723 return errSecSuccess
;
4726 return errSecInvalidCertificate
;
4730 CFArrayRef
SecCertificateCopyNTPrincipalNames(SecCertificateRef certificate
) {
4731 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4732 0, &kCFTypeArrayCallBacks
);
4733 OSStatus status
= errSecSuccess
;
4734 if (certificate
->_subjectAltName
) {
4735 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4736 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
4738 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
4739 CFRelease(ntPrincipalNames
);
4740 ntPrincipalNames
= NULL
;
4742 return ntPrincipalNames
;
4745 static OSStatus
appendToRFC2253String(void *context
,
4746 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4747 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4751 ST stateOrProvinceName
4753 OU organizationalUnitName
4755 STREET streetAddress
4759 /* Prepend a + if this is not the first RDN in an RDN set.
4760 Otherwise prepend a , if this is not the first RDN. */
4762 CFStringAppend(string
, CFSTR("+"));
4763 else if (CFStringGetLength(string
)) {
4764 CFStringAppend(string
, CFSTR(","));
4767 CFStringRef label
, oid
= NULL
;
4768 /* @@@ Consider changing this to a dictionary lookup keyed by the
4769 decimal representation. */
4770 if (DEROidCompare(type
, &oidCommonName
)) {
4771 label
= CFSTR("CN");
4772 } else if (DEROidCompare(type
, &oidLocalityName
)) {
4774 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
4775 label
= CFSTR("ST");
4776 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4778 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4779 label
= CFSTR("OU");
4780 } else if (DEROidCompare(type
, &oidCountryName
)) {
4783 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
4784 label
= CFSTR("STREET");
4785 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
4786 label
= CFSTR("DC");
4787 } else if (DEROidCompare(type
, &oidUserID
)) {
4788 label
= CFSTR("UID");
4791 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
4794 CFStringAppend(string
, label
);
4795 CFStringAppend(string
, CFSTR("="));
4796 CFStringRef raw
= NULL
;
4798 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4801 /* Append raw to string while escaping:
4802 a space or "#" character occurring at the beginning of the string
4803 a space character occurring at the end of the string
4804 one of the characters ",", "+", """, "\", "<", ">" or ";"
4806 CFStringInlineBuffer buffer
;
4807 CFIndex ix
, length
= CFStringGetLength(raw
);
4808 CFRange range
= { 0, length
};
4809 CFStringInitInlineBuffer(raw
, &buffer
, range
);
4810 for (ix
= 0; ix
< length
; ++ix
) {
4811 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
4813 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
4814 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
4815 ch
== '<' || ch
== '>' || ch
== ';' ||
4816 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
4817 (ch
== '#' && ix
== 0)) {
4818 UniChar chars
[] = { '\\', ch
};
4819 CFStringAppendCharacters(string
, chars
, 2);
4821 CFStringAppendCharacters(string
, &ch
, 1);
4826 /* Append the value in hex. */
4827 CFStringAppend(string
, CFSTR("#"));
4829 for (ix
= 0; ix
< value
->length
; ++ix
)
4830 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
4835 return errSecSuccess
;
4838 CFStringRef
SecCertificateCopySubjectString(SecCertificateRef certificate
) {
4839 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4840 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
);
4841 if (status
|| CFStringGetLength(string
) == 0) {
4848 static OSStatus
appendToCompanyNameString(void *context
,
4849 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4850 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4851 if (CFStringGetLength(string
) != 0)
4852 return errSecSuccess
;
4854 if (!DEROidCompare(type
, &oidOrganizationName
))
4855 return errSecSuccess
;
4858 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4860 return errSecSuccess
;
4861 CFStringAppend(string
, raw
);
4864 return errSecSuccess
;
4867 CFStringRef
SecCertificateCopyCompanyName(SecCertificateRef certificate
) {
4868 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4869 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
4870 appendToCompanyNameString
);
4871 if (status
|| CFStringGetLength(string
) == 0) {
4878 static CFDataRef
SecDERItemCopySequence(DERItem
*content
) {
4879 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
4880 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
4881 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
4883 CFDataSetLength(sequence
, sequence_length
);
4884 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
4885 *sequence_ptr
++ = 0x30; /* ASN1_CONSTR_SEQUENCE */
4886 require_noerr_quiet(DEREncodeLength(content
->length
,
4887 sequence_ptr
, &seq_len_length
), out
);
4888 sequence_ptr
+= seq_len_length
;
4889 memcpy(sequence_ptr
, content
->data
, content
->length
);
4892 CFReleaseSafe(sequence
);
4896 CFDataRef
SecCertificateCopyIssuerSequence(
4897 SecCertificateRef certificate
) {
4898 return SecDERItemCopySequence(&certificate
->_issuer
);
4901 CFDataRef
SecCertificateCopySubjectSequence(
4902 SecCertificateRef certificate
) {
4903 return SecDERItemCopySequence(&certificate
->_subject
);
4906 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithm(
4907 SecCertificateRef certificate
) {
4908 return &certificate
->_algId
;
4911 const DERItem
*SecCertificateGetPublicKeyData(SecCertificateRef certificate
) {
4912 return &certificate
->_pubKeyDER
;
4916 /* There is already a SecCertificateCopyPublicKey with different args on OS X,
4917 so we will refer to this one internally as SecCertificateCopyPublicKey_ios.
4919 SecKeyRef
SecCertificateCopyPublicKey_ios(SecCertificateRef certificate
)
4921 SecKeyRef
SecCertificateCopyPublicKey(SecCertificateRef certificate
)
4924 const DERAlgorithmId
*algId
=
4925 SecCertificateGetPublicKeyAlgorithm(certificate
);
4926 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
4927 const DERItem
*params
= NULL
;
4928 if (algId
->params
.length
!= 0) {
4929 params
= &algId
->params
;
4931 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
4932 SecAsn1Item params1
= {
4933 .Data
= params
? params
->data
: NULL
,
4934 .Length
= params
? params
->length
: 0
4936 SecAsn1Item keyData1
= {
4937 .Data
= keyData
? keyData
->data
: NULL
,
4938 .Length
= keyData
? keyData
->length
: 0
4940 return SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
4944 CFDataRef
SecCertificateGetSHA1Digest(SecCertificateRef certificate
) {
4945 if (!certificate
->_sha1Digest
) {
4946 certificate
->_sha1Digest
=
4947 SecSHA1DigestCreate(CFGetAllocator(certificate
),
4948 certificate
->_der
.data
, certificate
->_der
.length
);
4951 return certificate
->_sha1Digest
;
4954 CFDataRef
SecCertificateCopySHA256Digest(SecCertificateRef certificate
) {
4955 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
4956 certificate
->_der
.data
, certificate
->_der
.length
);
4959 CFDataRef
SecCertificateCopyIssuerSHA1Digest(SecCertificateRef certificate
) {
4960 CFDataRef digest
= NULL
;
4961 CFDataRef issuer
= SecCertificateCopyIssuerSequence(certificate
);
4963 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
4964 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
4970 CFDataRef
SecCertificateCopyPublicKeySHA1Digest(SecCertificateRef certificate
) {
4971 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
4972 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
4975 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA256Digest(SecCertificateRef certificate
) {
4976 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
4977 certificate
->_subjectPublicKeyInfo
.data
, certificate
->_subjectPublicKeyInfo
.length
);
4980 CFTypeRef
SecCertificateCopyKeychainItem(SecCertificateRef certificate
)
4985 CFRetainSafe(certificate
->_keychain_item
);
4986 return certificate
->_keychain_item
;
4989 CFDataRef
SecCertificateGetAuthorityKeyID(SecCertificateRef certificate
) {
4990 if (!certificate
->_authorityKeyID
&&
4991 certificate
->_authorityKeyIdentifier
.length
) {
4992 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
4993 certificate
->_authorityKeyIdentifier
.data
,
4994 certificate
->_authorityKeyIdentifier
.length
);
4997 return certificate
->_authorityKeyID
;
5000 CFDataRef
SecCertificateGetSubjectKeyID(SecCertificateRef certificate
) {
5001 if (!certificate
->_subjectKeyID
&&
5002 certificate
->_subjectKeyIdentifier
.length
) {
5003 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
5004 certificate
->_subjectKeyIdentifier
.data
,
5005 certificate
->_subjectKeyIdentifier
.length
);
5008 return certificate
->_subjectKeyID
;
5011 CFArrayRef
SecCertificateGetCRLDistributionPoints(SecCertificateRef certificate
) {
5012 return certificate
->_crlDistributionPoints
;
5015 CFArrayRef
SecCertificateGetOCSPResponders(SecCertificateRef certificate
) {
5016 return certificate
->_ocspResponders
;
5019 CFArrayRef
SecCertificateGetCAIssuers(SecCertificateRef certificate
) {
5020 return certificate
->_caIssuers
;
5023 bool SecCertificateHasCriticalSubjectAltName(SecCertificateRef certificate
) {
5024 return certificate
->_subjectAltName
&&
5025 certificate
->_subjectAltName
->critical
;
5028 bool SecCertificateHasSubject(SecCertificateRef certificate
) {
5029 /* Since the _subject field is the content of the subject and not the
5030 whole thing, we can simply check for a 0 length subject here. */
5031 return certificate
->_subject
.length
!= 0;
5034 bool SecCertificateHasUnknownCriticalExtension(SecCertificateRef certificate
) {
5035 return certificate
->_foundUnknownCriticalExtension
;
5038 /* Private API functions. */
5039 void SecCertificateShow(SecCertificateRef certificate
) {
5041 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
5042 fprintf(stderr
, "\n");
5046 CFDictionaryRef
SecCertificateCopyAttributeDictionary(
5047 SecCertificateRef certificate
) {
5048 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
5049 CFNumberRef certificateType
, certificateEncoding
;
5050 CFStringRef label
, alias
;
5051 CFDataRef skid
, pubKeyDigest
, certData
;
5052 CFDictionaryRef dict
= NULL
;
5056 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
5057 SInt32 ctv
= certificate
->_version
+ 1;
5058 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
5059 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
5060 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
5061 certData
= SecCertificateCopyData(certificate
);
5062 skid
= SecCertificateGetSubjectKeyID(certificate
);
5063 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
5064 certificate
->_pubKeyDER
.length
);
5066 /* We still need to figure out how to deal with multi valued attributes. */
5067 alias
= SecCertificateCopyRFC822Names(certificate
);
5068 label
= SecCertificateCopySubjectSummary(certificate
);
5074 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
5075 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
5076 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
5078 DICT_ADDPAIR(kSecAttrLabel
, label
);
5080 DICT_ADDPAIR(kSecAttrAlias
, alias
);
5081 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
5082 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
5083 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
5085 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
5086 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
5087 DICT_ADDPAIR(kSecValueData
, certData
);
5088 dict
= DICT_CREATE(allocator
);
5090 CFReleaseSafe(label
);
5091 CFReleaseSafe(pubKeyDigest
);
5092 CFReleaseSafe(certData
);
5093 CFReleaseSafe(certificateEncoding
);
5094 CFReleaseSafe(certificateType
);
5099 SecCertificateRef
SecCertificateCreateFromAttributeDictionary(
5100 CFDictionaryRef refAttributes
) {
5101 /* @@@ Support having an allocator in refAttributes. */
5102 CFAllocatorRef allocator
= NULL
;
5103 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
5104 return data
? SecCertificateCreateWithData(allocator
, data
) : NULL
;
5108 OSStatus
SecCertificateIsSelfSigned(SecCertificateRef certificate
, Boolean
*isSelfSigned
) {
5109 if (!certificate
|| (CFGetTypeID(certificate
) != SecCertificateGetTypeID())) {
5110 return errSecInvalidCertificate
;
5112 if (!isSelfSigned
) {
5116 // %%% TBD: IsSelfSigned doesn't require basicConstraints like IsSelfSignedCA,
5117 // which is actually what we want here. Probably need a separate version
5118 // of this function to do the signature comparison, and have the basicConstraints
5119 // check be implemented only in IsSelfSignedCA.
5121 if (certificate
->_isSelfSigned
== 0) {
5122 certificate
->_isSelfSigned
=
5123 (SecCertificateIsIssuedBy(certificate
, certificate
, 0) ?
5126 *isSelfSigned
= (certificate
->_isSelfSigned
== 1);
5128 *isSelfSigned
= SecCertificateIsSelfSignedCA(certificate
);
5130 return errSecSuccess
;
5133 bool SecCertificateIsSelfSignedCA(SecCertificateRef certificate
) {
5134 bool result
= false;
5135 SecKeyRef publicKey
= NULL
;
5137 require(publicKey
= SecCertificateCopyPublicKey_ios(certificate
), out
);
5139 require(publicKey
= SecCertificateCopyPublicKey(certificate
), out
);
5141 CFDataRef normalizedIssuer
=
5142 SecCertificateGetNormalizedIssuerContent(certificate
);
5143 CFDataRef normalizedSubject
=
5144 SecCertificateGetNormalizedSubjectContent(certificate
);
5145 require_quiet(normalizedIssuer
&& normalizedSubject
&&
5146 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
5148 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(certificate
);
5149 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyID(certificate
);
5150 if (authorityKeyID
) {
5151 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
5154 if (SecCertificateVersion(certificate
) >= 3) {
5155 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraints(certificate
);
5156 require_quiet(basicConstraints
&& basicConstraints
->isCA
, out
);
5157 require_noerr_quiet(SecCertificateIsSignedBy(certificate
, publicKey
), out
);
5162 CFReleaseSafe(publicKey
);
5166 SecKeyUsage
SecCertificateGetKeyUsage(SecCertificateRef certificate
) {
5167 return certificate
->_keyUsage
;
5170 CFArrayRef
SecCertificateCopyExtendedKeyUsage(SecCertificateRef certificate
)
5172 CFMutableArrayRef extended_key_usage_oids
=
5173 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
5174 require_quiet(extended_key_usage_oids
, out
);
5176 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5177 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5178 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
5179 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
5182 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
5183 require_noerr_quiet(drtn
, out
);
5184 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
5185 DERDecodedInfo currDecoded
;
5187 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
5188 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
5189 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
5190 currDecoded
.content
.data
, currDecoded
.content
.length
);
5192 CFArrayAppendValue(extended_key_usage_oids
, oid
);
5196 require_quiet(drtn
== DR_EndOfSequence
, out
);
5197 return extended_key_usage_oids
;
5201 CFReleaseSafe(extended_key_usage_oids
);
5205 CFArrayRef
SecCertificateCopySignedCertificateTimestamps(SecCertificateRef certificate
)
5209 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5210 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5211 if (extn
->extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
5212 !memcmp(extn
->extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
->extnID
.length
)) {
5213 /* Got the SCT oid */
5214 DERDecodedInfo sctList
;
5215 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &sctList
);
5216 require_noerr_quiet(drtn
, out
);
5217 require_quiet(sctList
.tag
== ASN1_OCTET_STRING
, out
);
5218 return SecCreateSignedCertificateTimestampsArrayFromSerializedSCTList(sctList
.content
.data
, sctList
.content
.length
);
5226 static bool matches_expected(DERItem der
, CFTypeRef expected
) {
5227 if (der
.length
> 1) {
5228 DERDecodedInfo decoded
;
5229 DERDecodeItem(&der
, &decoded
);
5230 switch (decoded
.tag
) {
5233 return decoded
.content
.length
== 0 && expected
== NULL
;
5237 case ASN1_UTF8_STRING
: {
5238 if (isString(expected
)) {
5239 CFStringRef expectedString
= (CFStringRef
) expected
;
5240 CFStringRef itemString
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFStringEncodingUTF8
, false, kCFAllocatorNull
);
5242 bool result
= (kCFCompareEqualTo
== CFStringCompare(expectedString
, itemString
, 0));
5243 CFReleaseNull(itemString
);
5249 case ASN1_OCTET_STRING
: {
5250 if (isData(expected
)) {
5251 CFDataRef expectedData
= (CFDataRef
) expected
;
5252 CFDataRef itemData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFAllocatorNull
);
5254 bool result
= CFEqual(expectedData
, itemData
);
5255 CFReleaseNull(itemData
);
5261 case ASN1_INTEGER
: {
5262 SInt32 expected_value
= 0;
5263 if (isString(expected
))
5265 CFStringRef aStr
= (CFStringRef
)expected
;
5266 expected_value
= CFStringGetIntValue(aStr
);
5268 else if (isNumber(expected
))
5270 CFNumberGetValue(expected
, kCFNumberSInt32Type
, &expected_value
);
5273 uint32_t num_value
= 0;
5274 if (!DERParseInteger(&decoded
.content
, &num_value
))
5276 return ((uint32_t)expected_value
== num_value
);
5289 static bool cert_contains_marker_extension_value(SecCertificateRef certificate
, CFDataRef oid
, CFTypeRef expectedValue
)
5292 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
5293 size_t oid_len
= CFDataGetLength(oid
);
5295 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5296 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5297 if (extn
->extnID
.length
== oid_len
5298 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
5300 return matches_expected(extn
->extnValue
, expectedValue
);
5306 static bool cert_contains_marker_extension(SecCertificateRef certificate
, CFTypeRef oid
)
5308 return cert_contains_marker_extension_value(certificate
, oid
, NULL
);
5311 struct search_context
{
5313 SecCertificateRef certificate
;
5316 static bool GetDecimalValueOfString(CFStringRef string
, uint32_t* value
)
5318 CFCharacterSetRef nonDecimalDigit
= CFCharacterSetCreateInvertedSet(NULL
, CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
));
5319 bool result
= false;
5321 if ( CFStringGetLength(string
) > 0
5322 && !CFStringFindCharacterFromSet(string
, nonDecimalDigit
, CFRangeMake(0, CFStringGetLength(string
)), kCFCompareForcedOrdering
, NULL
))
5325 *value
= CFStringGetIntValue(string
);
5329 CFReleaseNull(nonDecimalDigit
);
5334 static CFDataRef
CreateOidDataFromString(CFAllocatorRef allocator
, CFStringRef string
)
5336 CFMutableDataRef currentResult
= NULL
;
5337 CFDataRef encodedResult
= NULL
;
5339 CFArrayRef parts
= NULL
;
5345 parts
= CFStringCreateArrayBySeparatingStrings(NULL
, string
, CFSTR("."));
5350 count
= CFArrayGetCount(parts
);
5354 // assume no more than 5 bytes needed to represent any part of the oid,
5355 // since we limit parts to 32-bit values,
5356 // but the first two parts only need 1 byte
5357 currentResult
= CFDataCreateMutable(allocator
, 1+(count
-2)*5);
5363 part
= CFArrayGetValueAtIndex(parts
, 0);
5365 if (!GetDecimalValueOfString(part
, &x
) || x
> 6)
5372 part
= CFArrayGetValueAtIndex(parts
, 1);
5374 if (!GetDecimalValueOfString(part
, &x
) || x
> 39)
5380 CFDataAppendBytes(currentResult
, &firstByte
, 1);
5382 for (CFIndex i
= 2; i
< count
&& GetDecimalValueOfString(CFArrayGetValueAtIndex(parts
, i
), &x
); ++i
) {
5383 uint8_t b
[5] = {0, 0, 0, 0, 0};
5385 b
[3] = 0x80 | ((x
>> 7) & 0x7F);
5386 b
[2] = 0x80 | ((x
>> 14) & 0x7F);
5387 b
[1] = 0x80 | ((x
>> 21) & 0x7F);
5388 b
[0] = 0x80 | ((x
>> 28) & 0x7F);
5390 // Skip the unused extension bytes.
5391 size_t skipBytes
= 0;
5392 while (b
[skipBytes
] == 0x80)
5395 CFDataAppendBytes(currentResult
, b
+ skipBytes
, sizeof(b
) - skipBytes
);
5398 encodedResult
= currentResult
;
5399 currentResult
= NULL
;
5402 CFReleaseNull(parts
);
5403 CFReleaseNull(currentResult
);
5405 return encodedResult
;
5408 static void check_for_marker(const void *key
, const void *value
, void *context
)
5410 struct search_context
* search_ctx
= (struct search_context
*) context
;
5411 CFStringRef key_string
= (CFStringRef
) key
;
5412 CFTypeRef value_ref
= (CFTypeRef
) value
;
5414 // If we could have short circuted the iteration
5415 // we would have, but the best we can do
5416 // is not waste time comparing once a match
5418 if (search_ctx
->found
)
5421 if (CFGetTypeID(key_string
) != CFStringGetTypeID())
5424 CFDataRef key_data
= CreateOidDataFromString(NULL
, key_string
);
5426 if (NULL
== key_data
)
5429 if (cert_contains_marker_extension_value(search_ctx
->certificate
, key_data
, value_ref
))
5430 search_ctx
->found
= true;
5432 CFReleaseNull(key_data
);
5436 // CFType Ref is either:
5438 // CFData - OID to match with no data permitted
5439 // CFDictionary - OID -> Value table for expected values Single Object or Array
5440 // CFArray - Array of the above.
5442 // This returns true if any of the requirements are met.
5443 bool SecCertificateHasMarkerExtension(SecCertificateRef certificate
, CFTypeRef oids
)
5445 if (CFGetTypeID(oids
) == CFArrayGetTypeID()) {
5446 CFIndex ix
, length
= CFArrayGetCount(oids
);
5447 for (ix
= 0; ix
< length
; ix
++)
5448 if (SecCertificateHasMarkerExtension(certificate
, CFArrayGetValueAtIndex((CFArrayRef
)oids
, ix
)))
5450 } else if (CFGetTypeID(oids
) == CFDictionaryGetTypeID()) {
5451 struct search_context context
= { .found
= false, .certificate
= certificate
};
5452 CFDictionaryApplyFunction((CFDictionaryRef
) oids
, &check_for_marker
, &context
);
5453 return context
.found
;
5454 } else if (CFGetTypeID(oids
) == CFDataGetTypeID()) {
5455 return cert_contains_marker_extension(certificate
, oids
);
5460 SecCertificateRef
SecCertificateCreateWithPEM(CFAllocatorRef allocator
,
5461 CFDataRef pem_certificate
)
5463 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
5464 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
5465 uint8_t *base64_data
= NULL
;
5466 SecCertificateRef cert
= NULL
;
5467 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
5468 //const size_t length = CFDataGetLength(pem_certificate);
5469 char *begin
= strstr((const char *)data
, begin_cert
);
5470 char *end
= strstr((const char *)data
, end_cert
);
5473 begin
+= sizeof(begin_cert
) - 1;
5474 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
5475 if (base64_length
) {
5476 require_quiet(base64_data
= calloc(1, base64_length
), out
);
5477 require_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
);
5478 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, base64_data
, base64_length
);
5487 // -- MARK -- XPC encoding/decoding
5490 bool SecCertificateAppendToXPCArray(SecCertificateRef certificate
, xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
5492 return true; // NOOP
5494 size_t length
= SecCertificateGetLength(certificate
);
5495 const uint8_t *bytes
= SecCertificateGetBytePtr(certificate
);
5496 #if SECTRUST_VERBOSE_DEBUG
5497 secerror("cert=0x%lX length=%d bytes=0x%lX", (uintptr_t)certificate
, (int)length
, (uintptr_t)bytes
);
5499 if (!length
|| !bytes
) {
5500 return SecError(errSecParam
, error
, CFSTR("failed to der encode certificate"));
5502 xpc_array_set_data(xpc_certificates
, XPC_ARRAY_APPEND
, bytes
, length
);
5506 SecCertificateRef
SecCertificateCreateWithXPCArrayAtIndex(xpc_object_t xpc_certificates
, size_t index
, CFErrorRef
*error
) {
5507 SecCertificateRef certificate
= NULL
;
5509 const uint8_t *bytes
= xpc_array_get_data(xpc_certificates
, index
, &length
);
5511 certificate
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, bytes
, length
);
5514 SecError(errSecParam
, error
, CFSTR("certificates[%zu] failed to decode"), index
);
5519 xpc_object_t
SecCertificateArrayCopyXPCArray(CFArrayRef certificates
, CFErrorRef
*error
) {
5520 xpc_object_t xpc_certificates
;
5521 require_action_quiet(xpc_certificates
= xpc_array_create(NULL
, 0), exit
,
5522 SecError(errSecAllocate
, error
, CFSTR("failed to create xpc_array")));
5523 CFIndex ix
, count
= CFArrayGetCount(certificates
);
5524 for (ix
= 0; ix
< count
; ++ix
) {
5525 SecCertificateRef certificate
= (SecCertificateRef
) CFArrayGetValueAtIndex(certificates
, ix
);
5526 #if SECTRUST_VERBOSE_DEBUG
5527 CFIndex length
= SecCertificateGetLength(certificate
);
5528 const UInt8
*bytes
= SecCertificateGetBytePtr(certificate
);
5529 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
);
5531 if (!SecCertificateAppendToXPCArray(certificate
, xpc_certificates
, error
)) {
5532 xpc_release(xpc_certificates
);
5533 xpc_certificates
= NULL
;
5539 return xpc_certificates
;
5542 CFArrayRef
SecCertificateXPCArrayCopyArray(xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
5543 CFMutableArrayRef certificates
= NULL
;
5544 require_action_quiet(xpc_get_type(xpc_certificates
) == XPC_TYPE_ARRAY
, exit
,
5545 SecError(errSecParam
, error
, CFSTR("certificates xpc value is not an array")));
5546 size_t count
= xpc_array_get_count(xpc_certificates
);
5547 require_action_quiet(certificates
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
), exit
,
5548 SecError(errSecAllocate
, error
, CFSTR("failed to create CFArray of capacity %zu"), count
));
5551 for (ix
= 0; ix
< count
; ++ix
) {
5552 SecCertificateRef cert
= SecCertificateCreateWithXPCArrayAtIndex(xpc_certificates
, ix
, error
);
5554 CFRelease(certificates
);
5557 CFArraySetValueAtIndex(certificates
, ix
, cert
);
5562 return certificates
;
5565 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
5568 static CFArrayRef
CopyEscrowCertificates(SecCertificateEscrowRootType escrowRootType
, CFErrorRef
* error
)
5570 __block CFArrayRef result
= NULL
;
5572 do_if_registered(ota_CopyEscrowCertificates
, escrowRootType
, error
);
5574 securityd_send_sync_and_do(kSecXPCOpOTAGetEscrowCertificates
, error
,
5575 ^bool(xpc_object_t message
, CFErrorRef
*error
)
5577 xpc_dictionary_set_uint64(message
, "escrowType", (uint64_t)escrowRootType
);
5580 ^bool(xpc_object_t response
, CFErrorRef
*error
)
5582 xpc_object_t xpc_array
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
5584 if (response
&& (NULL
!= xpc_array
))
5586 result
= (CFArrayRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_array
);
5590 return SecError(errSecInternal
, error
, CFSTR("Did not get the Escrow certificates"));
5592 return result
!= NULL
;
5597 CFArrayRef
SecCertificateCopyEscrowRoots(SecCertificateEscrowRootType escrowRootType
)
5599 CFArrayRef result
= NULL
;
5601 CFDataRef certData
= NULL
;
5604 // The request is for the base line certificates.
5605 // Use the hard coded data to generate the return array
5606 if (kSecCertificateBaselineEscrowRoot
== escrowRootType
||
5607 kSecCertificateBaselinePCSEscrowRoot
== escrowRootType
)
5609 // Get the hard coded set of roots
5610 numRoots
= (kSecCertificateBaselineEscrowRoot
== escrowRootType
) ?
5611 kNumberOfBaseLineEscrowRoots
:
5612 kNumberOfBaseLinePCSEscrowRoots
;
5613 SecCertificateRef baseLineCerts
[numRoots
];
5614 struct RootRecord
** pEscrowRoots
= kBaseLineEscrowRoots
;
5615 struct RootRecord
* pRootRecord
= NULL
;
5617 if (kSecCertificateBaselinePCSEscrowRoot
== escrowRootType
) {
5618 pEscrowRoots
= kBaseLinePCSEscrowRoots
;
5621 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++)
5623 pRootRecord
= pEscrowRoots
[iCnt
];
5624 if (NULL
!= pRootRecord
&& pRootRecord
->_length
> 0 && NULL
!= pRootRecord
->_bytes
)
5626 certData
= CFDataCreate(kCFAllocatorDefault
, pRootRecord
->_bytes
, pRootRecord
->_length
);
5627 if (NULL
!= certData
)
5629 baseLineCerts
[iCnt
] = SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
5630 CFRelease(certData
);
5634 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)baseLineCerts
, numRoots
, &kCFTypeArrayCallBacks
);
5635 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++)
5637 if (NULL
!= baseLineCerts
[iCnt
])
5639 CFRelease(baseLineCerts
[iCnt
]);
5643 // The request is for the current certificates.
5646 CFErrorRef error
= NULL
;
5647 CFArrayRef cert_datas
= CopyEscrowCertificates(escrowRootType
, &error
);
5648 if (NULL
!= error
|| NULL
== cert_datas
)
5655 if (NULL
!= cert_datas
)
5657 CFRelease(cert_datas
);
5662 numRoots
= (int)(CFArrayGetCount(cert_datas
));
5664 SecCertificateRef assetCerts
[numRoots
];
5665 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++)
5667 certData
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, iCnt
);
5668 if (NULL
!= certData
)
5670 SecCertificateRef aCertRef
= SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
5671 assetCerts
[iCnt
] = aCertRef
;
5675 assetCerts
[iCnt
] = NULL
;
5681 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)assetCerts
, numRoots
, &kCFTypeArrayCallBacks
);
5682 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++)
5684 if (NULL
!= assetCerts
[iCnt
])
5686 CFRelease(assetCerts
[iCnt
]);
5690 CFReleaseSafe(cert_datas
);
5695 SecSignatureHashAlgorithm
SecCertificateGetSignatureHashAlgorithm(SecCertificateRef certificate
)
5697 SecSignatureHashAlgorithm result
= kSecSignatureHashAlgorithmUnknown
;
5698 DERAlgorithmId
*algId
= (certificate
) ? &certificate
->_tbsSigAlg
: NULL
;
5699 const DERItem
*algOid
= (algId
) ? &algId
->oid
: NULL
;
5701 if (!algOid
->data
|| !algOid
->length
) {
5704 /* classify the signature algorithm OID into one of our known types */
5705 if (DEROidCompare(algOid
, &oidSha512Ecdsa
) ||
5706 DEROidCompare(algOid
, &oidSha512Rsa
) ||
5707 DEROidCompare(algOid
, &oidSha512
)) {
5708 result
= kSecSignatureHashAlgorithmSHA512
;
5711 if (DEROidCompare(algOid
, &oidSha384Ecdsa
) ||
5712 DEROidCompare(algOid
, &oidSha384Rsa
) ||
5713 DEROidCompare(algOid
, &oidSha384
)) {
5714 result
= kSecSignatureHashAlgorithmSHA384
;
5717 if (DEROidCompare(algOid
, &oidSha256Ecdsa
) ||
5718 DEROidCompare(algOid
, &oidSha256Rsa
) ||
5719 DEROidCompare(algOid
, &oidSha256
)) {
5720 result
= kSecSignatureHashAlgorithmSHA256
;
5723 if (DEROidCompare(algOid
, &oidSha224Ecdsa
) ||
5724 DEROidCompare(algOid
, &oidSha224Rsa
) ||
5725 DEROidCompare(algOid
, &oidSha224
)) {
5726 result
= kSecSignatureHashAlgorithmSHA224
;
5729 if (DEROidCompare(algOid
, &oidSha1Ecdsa
) ||
5730 DEROidCompare(algOid
, &oidSha1Rsa
) ||
5731 DEROidCompare(algOid
, &oidSha1Dsa
) ||
5732 DEROidCompare(algOid
, &oidSha1DsaOIW
) ||
5733 DEROidCompare(algOid
, &oidSha1DsaCommonOIW
) ||
5734 DEROidCompare(algOid
, &oidSha1RsaOIW
) ||
5735 DEROidCompare(algOid
, &oidSha1Fee
) ||
5736 DEROidCompare(algOid
, &oidSha1
)) {
5737 result
= kSecSignatureHashAlgorithmSHA1
;
5740 if (DEROidCompare(algOid
, &oidMd5Rsa
) ||
5741 DEROidCompare(algOid
, &oidMd5Fee
) ||
5742 DEROidCompare(algOid
, &oidMd5
)) {
5743 result
= kSecSignatureHashAlgorithmMD5
;
5746 if (DEROidCompare(algOid
, &oidMd4Rsa
) ||
5747 DEROidCompare(algOid
, &oidMd4
)) {
5748 result
= kSecSignatureHashAlgorithmMD4
;
5751 if (DEROidCompare(algOid
, &oidMd2Rsa
) ||
5752 DEROidCompare(algOid
, &oidMd2
)) {
5753 result
= kSecSignatureHashAlgorithmMD2
;