2 * Copyright (c) 2006-2017 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecCertificate.c - CoreFoundation based certificate object
29 /* Allows us to build genanchors against the BaseSDK. */
30 #undef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
31 #undef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
34 #include <Security/SecCertificateInternal.h>
35 #include <utilities/SecIOFormat.h>
36 #include <CommonCrypto/CommonDigest.h>
37 #include <CoreFoundation/CFRuntime.h>
38 #include <CoreFoundation/CFString.h>
39 #include <CoreFoundation/CFBundle.h>
40 #include <CoreFoundation/CFDictionary.h>
41 #include <CoreFoundation/CFNumber.h>
42 #include <CoreFoundation/CFCalendar.h>
43 #include <CoreFoundation/CFTimeZone.h>
44 #include <CoreFoundation/CFXPCBridge.h>
46 #include <AssertMacros.h>
47 #include <libDER/DER_CertCrl.h>
48 #include <libDER/DER_Encode.h>
49 #include <libDER/DER_Keys.h>
50 #include <libDER/asn1Types.h>
51 #include <libDER/oids.h>
52 #include "SecBasePriv.h"
53 #include "SecRSAKey.h"
54 #include "SecFramework.h"
56 #include "SecItemPriv.h"
57 #include "SecSignatureVerificationSupport.h"
59 #include <utilities/debugging.h>
60 #include <utilities/SecCFWrappers.h>
61 #include <utilities/SecCFError.h>
62 #include <utilities/SecSCTUtils.h>
63 #include <utilities/array_size.h>
65 #include <libkern/OSByteOrder.h>
67 #include <Security/SecInternal.h>
68 #include <Security/SecFrameworkStrings.h>
69 #include "SecBase64.h"
70 #include "AppleBaselineEscrowCertificates.h"
71 #include "AppleiPhoneDeviceCACertificates.h"
72 #include <ipc/securityd_client.h>
73 #include <Security/SecKeyInternal.h>
75 #pragma clang diagnostic ignored "-Wformat=2"
77 /* The minimum key sizes necessary to not be considered "weak" */
78 #define MIN_RSA_KEY_SIZE 128 // 1024-bit
79 #define MIN_EC_KEY_SIZE 20 // 160-bit
81 /* The minimum key sizes necessary to be considered "strong" */
82 #define MIN_STRONG_RSA_KEY_SIZE 256 // 2048-bit
83 #define MIN_STRONG_EC_KEY_SIZE 28 // 224-bit
85 typedef struct SecCertificateExtension
{
89 } SecCertificateExtension
;
92 kSecSelfSignedUnknown
= 0,
97 struct __SecCertificate
{
100 DERItem _der
; /* Entire certificate in DER form. */
101 DERItem _tbs
; /* To Be Signed cert DER bytes. */
102 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
103 DERItem _signature
; /* The content of the sig bit string. */
106 DERItem _serialNum
; /* Integer. */
107 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
108 DERItem _issuer
; /* Sequence of RDN. */
109 CFAbsoluteTime _notBefore
;
110 CFAbsoluteTime _notAfter
;
111 DERItem _subject
; /* Sequence of RDN. */
112 DERItem _subjectPublicKeyInfo
; /* SPKI (without tag/length) */
113 DERAlgorithmId _algId
; /* oid and params of _pubKeyDER. */
114 DERItem _pubKeyDER
; /* contents of bit string */
115 DERItem _issuerUniqueID
; /* bit string, optional */
116 DERItem _subjectUniqueID
; /* bit string, optional */
118 bool _foundUnknownCriticalExtension
;
120 /* Well known certificate extensions. */
121 SecCEBasicConstraints _basicConstraints
;
122 SecCEPolicyConstraints _policyConstraints
;
123 SecCEPolicyMappings _policyMappings
;
124 SecCECertificatePolicies _certificatePolicies
;
125 SecCEInhibitAnyPolicy _inhibitAnyPolicySkipCerts
;
127 /* If KeyUsage extension is not present this is 0, otherwise it's
128 the value of the extension. */
129 SecKeyUsage _keyUsage
;
131 /* OCTETS of SubjectKeyIdentifier extensions KeyIdentifier.
132 Length = 0 if not present. */
133 DERItem _subjectKeyIdentifier
;
135 /* OCTETS of AuthorityKeyIdentifier extensions KeyIdentifier.
136 Length = 0 if not present. */
137 DERItem _authorityKeyIdentifier
;
138 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
139 _authorityKeyIdentifierSerialNumber have non zero length if present.
140 Both are either present or absent together. */
141 DERItem _authorityKeyIdentifierIssuer
;
142 DERItem _authorityKeyIdentifierSerialNumber
;
144 /* Subject alt name extension, if present. Not malloced, it's just a
145 pointer to an element in the _extensions array. */
146 const SecCertificateExtension
*_subjectAltName
;
148 /* Parsed extension values. */
150 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
151 CFMutableArrayRef _crlDistributionPoints
;
153 /* Array of CFURLRefs containing the URI values of accessLocations of each
154 id-ad-ocsp AccessDescription in the Authority Information Access
156 CFMutableArrayRef _ocspResponders
;
158 /* Array of CFURLRefs containing the URI values of accessLocations of each
159 id-ad-caIssuers AccessDescription in the Authority Information Access
161 CFMutableArrayRef _caIssuers
;
163 /* Array of CFDataRefs containing the generalNames for permittedSubtrees
165 CFArrayRef _permittedSubtrees
;
167 /* Array of CFDataRefs containing the generalNames for excludedSubtrees
169 CFArrayRef _excludedSubtrees
;
171 CFMutableArrayRef _embeddedSCTs
;
173 /* All other (non known) extensions. The _extensions array is malloced. */
174 CFIndex _extensionCount
;
175 SecCertificateExtension
*_extensions
;
177 /* Optional cached fields. */
180 CFArrayRef _properties
;
181 CFDataRef _serialNumber
;
182 CFDataRef _normalizedIssuer
;
183 CFDataRef _normalizedSubject
;
184 CFDataRef _authorityKeyID
;
185 CFDataRef _subjectKeyID
;
187 CFDataRef _sha1Digest
;
188 CFTypeRef _keychain_item
;
189 uint8_t _isSelfSigned
;
193 #define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v);
195 SEC_CONST_DECL (kSecCertificateProductionEscrowKey
, "ProductionEscrowKey");
196 SEC_CONST_DECL (kSecCertificateProductionPCSEscrowKey
, "ProductionPCSEscrowKey");
197 SEC_CONST_DECL (kSecCertificateEscrowFileName
, "AppleESCertificates");
199 /* Public Constants for property list keys. */
200 SEC_CONST_DECL (kSecPropertyKeyType
, "type");
201 SEC_CONST_DECL (kSecPropertyKeyLabel
, "label");
202 SEC_CONST_DECL (kSecPropertyKeyLocalizedLabel
, "localized label");
203 SEC_CONST_DECL (kSecPropertyKeyValue
, "value");
205 /* Public Constants for property list values. */
206 SEC_CONST_DECL (kSecPropertyTypeWarning
, "warning");
207 SEC_CONST_DECL (kSecPropertyTypeError
, "error");
208 SEC_CONST_DECL (kSecPropertyTypeSuccess
, "success");
209 SEC_CONST_DECL (kSecPropertyTypeTitle
, "title");
210 SEC_CONST_DECL (kSecPropertyTypeSection
, "section");
211 SEC_CONST_DECL (kSecPropertyTypeData
, "data");
212 SEC_CONST_DECL (kSecPropertyTypeString
, "string");
213 SEC_CONST_DECL (kSecPropertyTypeURL
, "url");
214 SEC_CONST_DECL (kSecPropertyTypeDate
, "date");
216 /* Extension parsing routine. */
217 typedef bool (*SecCertificateExtensionParser
)(SecCertificateRef certificate
,
218 const SecCertificateExtension
*extn
);
220 /* Mapping from extension OIDs (as a DERItem *) to
221 SecCertificateExtensionParser extension parsing routines. */
222 static CFDictionaryRef sExtensionParsers
;
224 /* Forward declarations of static functions. */
225 static CFStringRef
SecCertificateCopyDescription(CFTypeRef cf
);
226 static void SecCertificateDestroy(CFTypeRef cf
);
227 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
228 CFAbsoluteTime
*absTime
) __attribute__((__nonnull__
));
230 /* Static functions. */
231 static CFStringRef
SecCertificateCopyDescription(CFTypeRef cf
) {
232 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
233 CFStringRef subject
= SecCertificateCopySubjectSummary(certificate
);
234 CFStringRef issuer
= SecCertificateCopyIssuerSummary(certificate
);
235 CFStringRef desc
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
236 CFSTR("<cert(%p) s: %@ i: %@>"), certificate
, subject
, issuer
);
237 CFReleaseSafe(issuer
);
238 CFReleaseSafe(subject
);
242 static void SecCertificateDestroy(CFTypeRef cf
) {
243 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
244 if (certificate
->_certificatePolicies
.policies
) {
245 free(certificate
->_certificatePolicies
.policies
);
246 certificate
->_certificatePolicies
.policies
= NULL
;
248 if (certificate
->_policyMappings
.mappings
) {
249 free(certificate
->_policyMappings
.mappings
);
250 certificate
->_policyMappings
.mappings
= NULL
;
252 CFReleaseNull(certificate
->_crlDistributionPoints
);
253 CFReleaseNull(certificate
->_ocspResponders
);
254 CFReleaseNull(certificate
->_caIssuers
);
255 if (certificate
->_extensions
) {
256 free(certificate
->_extensions
);
257 certificate
->_extensions
= NULL
;
259 CFReleaseNull(certificate
->_pubKey
);
260 CFReleaseNull(certificate
->_der_data
);
261 CFReleaseNull(certificate
->_properties
);
262 CFReleaseNull(certificate
->_serialNumber
);
263 CFReleaseNull(certificate
->_normalizedIssuer
);
264 CFReleaseNull(certificate
->_normalizedSubject
);
265 CFReleaseNull(certificate
->_authorityKeyID
);
266 CFReleaseNull(certificate
->_subjectKeyID
);
267 CFReleaseNull(certificate
->_sha1Digest
);
268 CFReleaseNull(certificate
->_keychain_item
);
269 CFReleaseNull(certificate
->_permittedSubtrees
);
270 CFReleaseNull(certificate
->_excludedSubtrees
);
273 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
274 SecCertificateRef cert1
= (SecCertificateRef
)cf1
;
275 SecCertificateRef cert2
= (SecCertificateRef
)cf2
;
278 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
280 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
283 /* Hash of the certificate is der length + signature length + last 4 bytes
285 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
286 SecCertificateRef certificate
= (SecCertificateRef
)cf
;
287 size_t der_length
= certificate
->_der
.length
;
288 size_t sig_length
= certificate
->_signature
.length
;
289 size_t ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
290 CFHashCode hashCode
= 0;
291 for (; ix
< sig_length
; ++ix
)
292 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
294 return (hashCode
+ der_length
+ sig_length
);
299 /************************************************************************/
300 /************************* General Name Parsing *************************/
301 /************************************************************************/
303 GeneralName ::= CHOICE {
304 otherName [0] OtherName,
305 rfc822Name [1] IA5String,
306 dNSName [2] IA5String,
307 x400Address [3] ORAddress,
308 directoryName [4] Name,
309 ediPartyName [5] EDIPartyName,
310 uniformResourceIdentifier [6] IA5String,
311 iPAddress [7] OCTET STRING,
312 registeredID [8] OBJECT IDENTIFIER}
314 OtherName ::= SEQUENCE {
315 type-id OBJECT IDENTIFIER,
316 value [0] EXPLICIT ANY DEFINED BY type-id }
318 EDIPartyName ::= SEQUENCE {
319 nameAssigner [0] DirectoryString OPTIONAL,
320 partyName [1] DirectoryString }
322 OSStatus
SecCertificateParseGeneralNameContentProperty(DERTag tag
,
323 const DERItem
*generalNameContent
,
324 void *context
, parseGeneralNameCallback callback
) {
326 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
327 return callback(context
, GNT_OtherName
, generalNameContent
);
328 case ASN1_CONTEXT_SPECIFIC
| 1:
329 return callback(context
, GNT_RFC822Name
, generalNameContent
);
330 case ASN1_CONTEXT_SPECIFIC
| 2:
331 return callback(context
, GNT_DNSName
, generalNameContent
);
332 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
333 return callback(context
, GNT_X400Address
, generalNameContent
);
334 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
335 return callback(context
, GNT_DirectoryName
, generalNameContent
);
336 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
337 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
338 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
340 /* Technically I don't think this is valid, but there are certs out
341 in the wild that use a constructed IA5String. In particular the
342 VeriSign Time Stamping Authority CA.cer does this. */
343 DERDecodedInfo uriContent
;
344 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
345 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
346 return callback(context
, GNT_URI
, &uriContent
.content
);
348 case ASN1_CONTEXT_SPECIFIC
| 6:
349 return callback(context
, GNT_URI
, generalNameContent
);
350 case ASN1_CONTEXT_SPECIFIC
| 7:
351 return callback(context
, GNT_IPAddress
, generalNameContent
);
352 case ASN1_CONTEXT_SPECIFIC
| 8:
353 return callback(context
, GNT_RegisteredID
, generalNameContent
);
358 return errSecInvalidCertificate
;
361 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
362 void *context
, parseGeneralNameCallback callback
) {
364 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
365 require_noerr_quiet(drtn
, badDER
);
366 DERDecodedInfo generalNameContent
;
367 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
369 OSStatus status
= SecCertificateParseGeneralNameContentProperty(
370 generalNameContent
.tag
, &generalNameContent
.content
, context
,
375 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
376 return errSecSuccess
;
379 return errSecInvalidCertificate
;
382 OSStatus
SecCertificateParseGeneralNames(const DERItem
*generalNames
, void *context
,
383 parseGeneralNameCallback callback
) {
384 DERDecodedInfo generalNamesContent
;
385 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
386 require_noerr_quiet(drtn
, badDER
);
387 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
388 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
391 return errSecInvalidCertificate
;
397 GeneralName ::= CHOICE {
398 otherName [0] OtherName,
399 rfc822Name [1] IA5String,
400 dNSName [2] IA5String,
401 x400Address [3] ORAddress,
402 directoryName [4] Name,
403 ediPartyName [5] EDIPartyName,
404 uniformResourceIdentifier [6] IA5String,
405 iPAddress [7] OCTET STRING,
406 registeredID [8] OBJECT IDENTIFIER}
408 EDIPartyName ::= SEQUENCE {
409 nameAssigner [0] DirectoryString OPTIONAL,
410 partyName [1] DirectoryString }
412 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
413 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
415 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
416 generalName
->nameType
= GNT_OtherName
;
417 generalName
->berEncoded
= true;
418 generalName
->name
= *generalNameContent
;
420 case ASN1_CONTEXT_SPECIFIC
| 1:
422 generalName
->nameType
= GNT_RFC822Name
;
423 generalName
->berEncoded
= false;
424 generalName
->name
= *generalNameContent
;
426 case ASN1_CONTEXT_SPECIFIC
| 2:
428 generalName
->nameType
= GNT_DNSName
;
429 generalName
->berEncoded
= false;
430 generalName
->name
= *generalNameContent
;
432 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
433 generalName
->nameType
= GNT_X400Address
;
434 generalName
->berEncoded
= true;
435 generalName
->name
= *generalNameContent
;
437 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
438 generalName
->nameType
= GNT_DirectoryName
;
439 generalName
->berEncoded
= true;
440 generalName
->name
= *generalNameContent
;
442 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
443 generalName
->nameType
= GNT_EdiPartyName
;
444 generalName
->berEncoded
= true;
445 generalName
->name
= *generalNameContent
;
447 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
449 /* Technically I don't think this is valid, but there are certs out
450 in the wild that use a constructed IA5String. In particular the
451 VeriSign Time Stamping Authority CA.cer does this. */
452 DERDecodedInfo decoded
;
453 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
454 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
455 generalName
->nameType
= GNT_URI
;
456 generalName
->berEncoded
= false;
457 generalName
->name
= decoded
.content
;
460 case ASN1_CONTEXT_SPECIFIC
| 6:
461 generalName
->nameType
= GNT_URI
;
462 generalName
->berEncoded
= false;
463 generalName
->name
= *generalNameContent
;
465 case ASN1_CONTEXT_SPECIFIC
| 7:
466 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
467 8 octects, addr/mask for ipv6 it's 32. */
468 generalName
->nameType
= GNT_IPAddress
;
469 generalName
->berEncoded
= false;
470 generalName
->name
= *generalNameContent
;
472 case ASN1_CONTEXT_SPECIFIC
| 8:
473 /* name is the content of an OID. */
474 generalName
->nameType
= GNT_RegisteredID
;
475 generalName
->berEncoded
= false;
476 generalName
->name
= *generalNameContent
;
482 return errSecSuccess
;
484 return errSecInvalidCertificate
;
488 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
490 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
491 CFIndex
*count
, SecCEGeneralName
**name
) {
492 SecCEGeneralName
*generalNames
= NULL
;
494 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
495 require_noerr_quiet(drtn
, badDER
);
496 DERDecodedInfo generalNameContent
;
497 CFIndex generalNamesCount
= 0;
498 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
502 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
504 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
506 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
508 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
510 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
511 &generalNameContent
.content
, &generalNames
[ix
])) {
516 *count
= generalNamesCount
;
517 *name
= generalNames
;
518 return errSecSuccess
;
523 return errSecInvalidCertificate
;
526 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
527 CFIndex
*count
, SecCEGeneralName
**name
) {
528 DERDecodedInfo generalNamesContent
;
529 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
530 require_noerr_quiet(drtn
, badDER
);
531 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
533 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
534 return errSecSuccess
;
536 return errSecInvalidCertificate
;
540 /************************************************************************/
541 /************************** X.509 Name Parsing **************************/
542 /************************************************************************/
544 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
545 const DERItem
*value
, CFIndex rdnIX
, bool localized
);
547 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
548 parseX501NameCallback callback
, bool localized
) {
550 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
551 require_noerr_quiet(drtn
, badDER
);
552 DERDecodedInfo atvContent
;
554 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
555 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
556 DERAttributeTypeAndValue atv
;
557 drtn
= DERParseSequenceContent(&atvContent
.content
,
558 DERNumAttributeTypeAndValueItemSpecs
,
559 DERAttributeTypeAndValueItemSpecs
,
561 require_noerr_quiet(drtn
, badDER
);
562 require_quiet(atv
.type
.length
!= 0, badDER
);
563 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++, localized
);
568 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
570 return errSecSuccess
;
572 return errSecInvalidCertificate
;
575 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
576 parseX501NameCallback callback
, bool localized
) {
578 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
579 require_noerr_quiet(drtn
, badDER
);
580 DERDecodedInfo currDecoded
;
581 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
582 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
583 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
584 callback
, localized
);
589 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
591 return errSecSuccess
;
594 return errSecInvalidCertificate
;
597 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
598 parseX501NameCallback callback
, bool localized
) {
599 DERDecodedInfo x501NameContent
;
600 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
601 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
602 return errSecInvalidCertificate
;
604 return parseX501NameContent(&x501NameContent
.content
, context
,
605 callback
, localized
);
609 /************************************************************************/
610 /********************** Extension Parsing Routines **********************/
611 /************************************************************************/
613 static bool SecCEPSubjectKeyIdentifier(SecCertificateRef certificate
,
614 const SecCertificateExtension
*extn
) {
615 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
616 DERDecodedInfo keyIdentifier
;
617 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
618 require_noerr_quiet(drtn
, badDER
);
619 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
620 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
624 secwarning("Invalid SubjectKeyIdentifier Extension");
628 static bool SecCEPKeyUsage(SecCertificateRef certificate
,
629 const SecCertificateExtension
*extn
) {
630 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
631 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
632 DERDecodedInfo bitStringContent
;
633 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
634 require_noerr_quiet(drtn
, badDER
);
635 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
636 DERSize len
= bitStringContent
.content
.length
- 1;
637 require_quiet(len
== 1 || len
== 2, badDER
);
638 DERByte numUnusedBits
= bitStringContent
.content
.data
[0];
639 require_quiet(numUnusedBits
< 8, badDER
);
640 /* Flip the bits in the bit string so the first bit in the lsb. */
641 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
642 uint_fast16_t value
= bitStringContent
.content
.data
[1];
645 value
= (value
<< 8) + bitStringContent
.content
.data
[2];
651 for (ix
= 0; ix
< bits
; ++ix
) {
657 certificate
->_keyUsage
= keyUsage
;
660 certificate
->_keyUsage
= kSecKeyUsageUnspecified
;
661 secwarning("Invalid KeyUsage Extension");
665 static bool SecCEPPrivateKeyUsagePeriod(SecCertificateRef certificate
,
666 const SecCertificateExtension
*extn
) {
667 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
671 static bool SecCEPSubjectAltName(SecCertificateRef certificate
,
672 const SecCertificateExtension
*extn
) {
673 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
674 certificate
->_subjectAltName
= extn
;
678 static bool SecCEPIssuerAltName(SecCertificateRef certificate
,
679 const SecCertificateExtension
*extn
) {
680 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
684 static bool SecCEPBasicConstraints(SecCertificateRef certificate
,
685 const SecCertificateExtension
*extn
) {
686 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
687 DERBasicConstraints basicConstraints
;
688 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
689 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
690 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
691 require_noerr_quiet(DERParseBooleanWithDefault(&basicConstraints
.cA
, false,
692 &certificate
->_basicConstraints
.isCA
), badDER
);
693 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
694 require_noerr_quiet(DERParseInteger(
695 &basicConstraints
.pathLenConstraint
,
696 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
697 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
699 certificate
->_basicConstraints
.present
= true;
700 certificate
->_basicConstraints
.critical
= extn
->critical
;
703 certificate
->_basicConstraints
.present
= false;
704 secwarning("Invalid BasicConstraints Extension");
710 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
712 * NameConstraints ::= SEQUENCE {
713 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
714 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
716 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
718 * GeneralSubtree ::= SEQUENCE {
720 * minimum [0] BaseDistance DEFAULT 0,
721 * maximum [1] BaseDistance OPTIONAL }
723 * BaseDistance ::= INTEGER (0..MAX)
725 static DERReturn
parseGeneralSubtrees(DERItem
*derSubtrees
, CFArrayRef
*generalSubtrees
) {
726 CFMutableArrayRef gs
= NULL
;
728 DERReturn drtn
= DERDecodeSeqContentInit(derSubtrees
, &gsSeq
);
729 require_noerr_quiet(drtn
, badDER
);
730 DERDecodedInfo gsContent
;
731 require_quiet(gs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
732 &kCFTypeArrayCallBacks
),
734 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
735 DERGeneralSubtree derGS
;
736 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
737 drtn
= DERParseSequenceContent(&gsContent
.content
,
738 DERNumGeneralSubtreeItemSpecs
,
739 DERGeneralSubtreeItemSpecs
,
740 &derGS
, sizeof(derGS
));
741 require_noerr_quiet(drtn
, badDER
);
744 * Within this profile, the minimum and maximum fields are not used with
745 * any name forms, thus, the minimum MUST be zero, and maximum MUST be
748 * Because minimum DEFAULT 0, absence equivalent to present and 0.
750 if (derGS
.minimum
.length
) {
752 require_noerr_quiet(DERParseInteger(&derGS
.minimum
, &minimum
),
754 require_quiet(minimum
== 0, badDER
);
756 require_quiet(derGS
.maximum
.length
== 0, badDER
);
757 require_quiet(derGS
.generalName
.length
!= 0, badDER
);
759 CFDataRef generalName
= NULL
;
760 require_quiet(generalName
= CFDataCreate(kCFAllocatorDefault
,
761 derGS
.generalName
.data
,
762 derGS
.generalName
.length
),
764 CFArrayAppendValue(gs
, generalName
);
765 CFReleaseNull(generalName
);
767 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
769 // since generalSubtrees is a pointer to an instance variable,
770 // make sure we release the existing array before assignment.
771 CFReleaseSafe(*generalSubtrees
);
772 *generalSubtrees
= gs
;
778 secdebug("cert","failed to parse GeneralSubtrees");
782 static bool SecCEPNameConstraints(SecCertificateRef certificate
,
783 const SecCertificateExtension
*extn
) {
784 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
785 DERNameConstraints nc
;
787 drtn
= DERParseSequence(&extn
->extnValue
,
788 DERNumNameConstraintsItemSpecs
,
789 DERNameConstraintsItemSpecs
,
791 require_noerr_quiet(drtn
, badDER
);
792 if (nc
.permittedSubtrees
.length
) {
793 require_noerr_quiet(parseGeneralSubtrees(&nc
.permittedSubtrees
, &certificate
->_permittedSubtrees
), badDER
);
795 if (nc
.excludedSubtrees
.length
) {
796 require_noerr_quiet(parseGeneralSubtrees(&nc
.excludedSubtrees
, &certificate
->_excludedSubtrees
), badDER
);
801 secwarning("Invalid Name Constraints extension");
805 static OSStatus
appendCRLDPFromGeneralNames(void *context
, SecCEGeneralNameType type
,
806 const DERItem
*value
) {
807 CFMutableArrayRef
*crlDPs
= (CFMutableArrayRef
*)context
;
808 if (type
== GNT_URI
) {
810 url
= CFURLCreateWithBytes(NULL
, value
->data
, value
->length
, kCFStringEncodingASCII
, NULL
);
813 *crlDPs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
815 CFArrayAppendValue(*crlDPs
, url
);
819 return errSecSuccess
;
823 id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= { id-ce 31 }
825 CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
827 DistributionPoint ::= SEQUENCE {
828 distributionPoint [0] DistributionPointName OPTIONAL,
829 reasons [1] ReasonFlags OPTIONAL,
830 cRLIssuer [2] GeneralNames OPTIONAL }
832 DistributionPointName ::= CHOICE {
833 fullName [0] GeneralNames,
834 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
836 static bool SecCEPCrlDistributionPoints(SecCertificateRef certificate
,
837 const SecCertificateExtension
*extn
) {
838 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
839 DERSequence crlDPSeq
;
841 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &crlDPSeq
);
842 require_noerr_quiet(drtn
, badDER
);
843 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
844 DERDecodedInfo dpContent
;
845 while ((drtn
= DERDecodeSeqNext(&crlDPSeq
, &dpContent
)) == DR_Success
) {
846 require_quiet(dpContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
847 DERDistributionPoint dp
;
848 drtn
= DERParseSequenceContent(&dpContent
.content
, DERNumDistributionPointItemSpecs
,
849 DERDistributionPointItemSpecs
, &dp
, sizeof(dp
));
850 require_noerr_quiet(drtn
, badDER
);
851 require_quiet(dp
.distributionPoint
.data
|| dp
.cRLIssuer
.data
, badDER
);
852 if (dp
.distributionPoint
.data
) {
853 DERDecodedInfo dpName
;
854 drtn
= DERDecodeItem(&dp
.distributionPoint
, &dpName
);
855 require_noerr_quiet(drtn
, badDER
);
856 switch (dpName
.tag
) {
857 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
858 drtn
= parseGeneralNamesContent(&dpName
.content
, &certificate
->_crlDistributionPoints
,
859 appendCRLDPFromGeneralNames
);
860 require_noerr_quiet(drtn
, badDER
);
862 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1:
863 /* RelativeDistinguishName. Nothing we can do with that. */
869 if (dp
.cRLIssuer
.data
) {
870 drtn
= SecCertificateParseGeneralNames(&dp
.cRLIssuer
, &certificate
->_crlDistributionPoints
,
871 appendCRLDPFromGeneralNames
);
872 require_noerr_quiet(drtn
, badDER
);
875 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
878 secwarning("Invalid CRL Distribution Points extension");
883 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
885 PolicyInformation ::= SEQUENCE {
886 policyIdentifier CertPolicyId,
887 policyQualifiers SEQUENCE SIZE (1..MAX) OF
888 PolicyQualifierInfo OPTIONAL }
890 CertPolicyId ::= OBJECT IDENTIFIER
892 PolicyQualifierInfo ::= SEQUENCE {
893 policyQualifierId PolicyQualifierId,
894 qualifier ANY DEFINED BY policyQualifierId }
896 /* maximum number of policies of 8192 seems more than adequate */
897 #define MAX_CERTIFICATE_POLICIES 8192
898 static bool SecCEPCertificatePolicies(SecCertificateRef certificate
,
899 const SecCertificateExtension
*extn
) {
900 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
903 SecCEPolicyInformation
*policies
= NULL
;
904 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
905 require_noerr_quiet(drtn
, badDER
);
906 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
907 DERDecodedInfo piContent
;
908 DERSize policy_count
= 0;
909 while ((policy_count
< MAX_CERTIFICATE_POLICIES
) &&
910 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
911 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
914 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
915 require_quiet(policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
)
916 * (policy_count
> 0 ? policy_count
: 1)),
918 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
919 require_noerr_quiet(drtn
, badDER
);
920 DERSize policy_ix
= 0;
921 while ((policy_ix
< (policy_count
> 0 ? policy_count
: 1)) &&
922 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
923 DERPolicyInformation pi
;
924 drtn
= DERParseSequenceContent(&piContent
.content
,
925 DERNumPolicyInformationItemSpecs
,
926 DERPolicyInformationItemSpecs
,
928 require_noerr_quiet(drtn
, badDER
);
929 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
930 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
932 certificate
->_certificatePolicies
.present
= true;
933 certificate
->_certificatePolicies
.critical
= extn
->critical
;
934 certificate
->_certificatePolicies
.numPolicies
= policy_count
;
935 certificate
->_certificatePolicies
.policies
= policies
;
940 certificate
->_certificatePolicies
.present
= false;
941 secwarning("Invalid CertificatePolicies Extension");
946 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
948 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
949 issuerDomainPolicy CertPolicyId,
950 subjectDomainPolicy CertPolicyId }
952 #define MAX_POLICY_MAPPINGS 8192
953 static bool SecCEPPolicyMappings(SecCertificateRef certificate
,
954 const SecCertificateExtension
*extn
) {
955 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
958 SecCEPolicyMapping
*mappings
= NULL
;
959 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
960 require_noerr_quiet(drtn
, badDER
);
961 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
962 DERDecodedInfo pmContent
;
963 DERSize mapping_count
= 0;
964 while ((mapping_count
< MAX_POLICY_MAPPINGS
) &&
965 (drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
966 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
969 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
970 require_quiet(mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
971 * (mapping_count
> 0 ? mapping_count
: 1)),
973 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
974 require_noerr_quiet(drtn
, badDER
);
975 DERSize mapping_ix
= 0;
976 while ((mapping_ix
< (mapping_count
> 0 ? mapping_count
: 1)) &&
977 (drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
978 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
980 drtn
= DERParseSequenceContent(&pmContent
.content
,
981 DERNumPolicyMappingItemSpecs
,
982 DERPolicyMappingItemSpecs
,
984 require_noerr_quiet(drtn
, badDER
);
985 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
986 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
988 certificate
->_policyMappings
.present
= true;
989 certificate
->_policyMappings
.critical
= extn
->critical
;
990 certificate
->_policyMappings
.numMappings
= mapping_count
;
991 certificate
->_policyMappings
.mappings
= mappings
;
997 certificate
->_policyMappings
.present
= false;
998 secwarning("Invalid CertificatePolicies Extension");
1003 AuthorityKeyIdentifier ::= SEQUENCE {
1004 keyIdentifier [0] KeyIdentifier OPTIONAL,
1005 authorityCertIssuer [1] GeneralNames OPTIONAL,
1006 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
1007 -- authorityCertIssuer and authorityCertSerialNumber MUST both
1008 -- be present or both be absent
1010 KeyIdentifier ::= OCTET STRING
1012 static bool SecCEPAuthorityKeyIdentifier(SecCertificateRef certificate
,
1013 const SecCertificateExtension
*extn
) {
1014 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1015 DERAuthorityKeyIdentifier akid
;
1017 drtn
= DERParseSequence(&extn
->extnValue
,
1018 DERNumAuthorityKeyIdentifierItemSpecs
,
1019 DERAuthorityKeyIdentifierItemSpecs
,
1020 &akid
, sizeof(akid
));
1021 require_noerr_quiet(drtn
, badDER
);
1022 if (akid
.keyIdentifier
.length
) {
1023 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
1025 if (akid
.authorityCertIssuer
.length
||
1026 akid
.authorityCertSerialNumber
.length
) {
1027 require_quiet(akid
.authorityCertIssuer
.length
&&
1028 akid
.authorityCertSerialNumber
.length
, badDER
);
1029 /* Perhaps put in a subsection called Authority Certificate Issuer. */
1030 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
1031 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
1036 secwarning("Invalid AuthorityKeyIdentifier Extension");
1040 static bool SecCEPPolicyConstraints(SecCertificateRef certificate
,
1041 const SecCertificateExtension
*extn
) {
1042 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1043 DERPolicyConstraints pc
;
1045 drtn
= DERParseSequence(&extn
->extnValue
,
1046 DERNumPolicyConstraintsItemSpecs
,
1047 DERPolicyConstraintsItemSpecs
,
1049 require_noerr_quiet(drtn
, badDER
);
1050 if (pc
.requireExplicitPolicy
.length
) {
1051 require_noerr_quiet(DERParseInteger(
1052 &pc
.requireExplicitPolicy
,
1053 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
1054 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
1056 if (pc
.inhibitPolicyMapping
.length
) {
1057 require_noerr_quiet(DERParseInteger(
1058 &pc
.inhibitPolicyMapping
,
1059 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
1060 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
1063 certificate
->_policyConstraints
.present
= true;
1064 certificate
->_policyConstraints
.critical
= extn
->critical
;
1068 certificate
->_policyConstraints
.present
= false;
1069 secwarning("Invalid PolicyConstraints Extension");
1073 static bool SecCEPExtendedKeyUsage(SecCertificateRef certificate
,
1074 const SecCertificateExtension
*extn
) {
1075 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1080 InhibitAnyPolicy ::= SkipCerts
1082 SkipCerts ::= INTEGER (0..MAX)
1084 static bool SecCEPInhibitAnyPolicy(SecCertificateRef certificate
,
1085 const SecCertificateExtension
*extn
) {
1086 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1087 DERDecodedInfo iapContent
;
1088 require_noerr_quiet(DERDecodeItem(&extn
->extnValue
, &iapContent
), badDER
);
1089 require_quiet(iapContent
.tag
== ASN1_INTEGER
, badDER
);
1090 require_noerr_quiet(DERParseInteger(
1091 &iapContent
.content
,
1092 &certificate
->_inhibitAnyPolicySkipCerts
.skipCerts
), badDER
);
1094 certificate
->_inhibitAnyPolicySkipCerts
.present
= true;
1095 certificate
->_inhibitAnyPolicySkipCerts
.critical
= extn
->critical
;
1098 certificate
->_inhibitAnyPolicySkipCerts
.present
= false;
1099 secwarning("Invalid InhibitAnyPolicy Extension");
1104 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
1106 AuthorityInfoAccessSyntax ::=
1107 SEQUENCE SIZE (1..MAX) OF AccessDescription
1109 AccessDescription ::= SEQUENCE {
1110 accessMethod OBJECT IDENTIFIER,
1111 accessLocation GeneralName }
1113 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
1115 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
1117 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
1119 static bool SecCEPAuthorityInfoAccess(SecCertificateRef certificate
,
1120 const SecCertificateExtension
*extn
) {
1121 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1124 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
1125 require_noerr_quiet(drtn
, badDER
);
1126 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1127 DERDecodedInfo adContent
;
1128 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
1129 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1130 DERAccessDescription ad
;
1131 drtn
= DERParseSequenceContent(&adContent
.content
,
1132 DERNumAccessDescriptionItemSpecs
,
1133 DERAccessDescriptionItemSpecs
,
1135 require_noerr_quiet(drtn
, badDER
);
1136 CFMutableArrayRef
*urls
;
1137 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
1138 urls
= &certificate
->_ocspResponders
;
1139 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
1140 urls
= &certificate
->_caIssuers
;
1144 DERDecodedInfo generalNameContent
;
1145 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
1146 require_noerr_quiet(drtn
, badDER
);
1147 switch (generalNameContent
.tag
) {
1149 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
1150 /* Technically I don't think this is valid, but there are certs out
1151 in the wild that use a constructed IA5String. In particular the
1152 VeriSign Time Stamping Authority CA.cer does this. */
1154 case ASN1_CONTEXT_SPECIFIC
| 6:
1156 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1157 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
1158 kCFStringEncodingASCII
, NULL
);
1161 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1162 CFArrayAppendValue(*urls
, url
);
1168 secdebug("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02llx v: %.*s",
1169 generalNameContent
.tag
, (int) generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1174 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1177 secwarning("Invalid Authority Information Access extension");
1181 /* Apple Worldwide Developer Relations Certificate Authority subject name.
1182 * This is a DER sequence with the leading tag and length bytes removed,
1183 * to match what tbsCert.issuer contains.
1185 static const unsigned char Apple_WWDR_CA_Subject_Name
[]={
1186 0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,
1187 0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x0C,0x0A,0x41,0x70,0x70,0x6C,0x65,
1188 0x20,0x49,0x6E,0x63,0x2E,0x31,0x2C,0x30,0x2A,0x06,0x03,0x55,0x04,0x0B,0x0C,0x23,
1189 0x41,0x70,0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,
1190 0x44,0x65,0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,
1191 0x6F,0x6E,0x73,0x31,0x44,0x30,0x42,0x06,0x03,0x55,0x04,0x03,0x0C,0x3B,0x41,0x70,
1192 0x70,0x6C,0x65,0x20,0x57,0x6F,0x72,0x6C,0x64,0x77,0x69,0x64,0x65,0x20,0x44,0x65,
1193 0x76,0x65,0x6C,0x6F,0x70,0x65,0x72,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,0x6F,0x6E,
1194 0x73,0x20,0x43,0x65,0x72,0x74,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,
1195 0x41,0x75,0x74,0x68,0x6F,0x72,0x69,0x74,0x79
1198 static void checkForMissingRevocationInfo(SecCertificateRef certificate
) {
1200 certificate
->_crlDistributionPoints
||
1201 certificate
->_ocspResponders
) {
1202 /* We already have an OCSP or CRL URI (or no cert) */
1205 /* Specify an appropriate OCSP responder if we recognize the issuer. */
1206 CFURLRef url
= NULL
;
1207 if (sizeof(Apple_WWDR_CA_Subject_Name
) == certificate
->_issuer
.length
&&
1208 !memcmp(certificate
->_issuer
.data
, Apple_WWDR_CA_Subject_Name
,
1209 sizeof(Apple_WWDR_CA_Subject_Name
))) {
1210 const char *WWDR_OCSP_URI
= "http://ocsp.apple.com/ocsp-wwdr01";
1211 url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1212 (const UInt8
*)WWDR_OCSP_URI
, strlen(WWDR_OCSP_URI
),
1213 kCFStringEncodingASCII
, NULL
);
1216 CFMutableArrayRef
*urls
= &certificate
->_ocspResponders
;
1217 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1218 CFArrayAppendValue(*urls
, url
);
1223 static bool SecCEPSubjectInfoAccess(SecCertificateRef certificate
,
1224 const SecCertificateExtension
*extn
) {
1225 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1229 static bool SecCEPNetscapeCertType(SecCertificateRef certificate
,
1230 const SecCertificateExtension
*extn
) {
1231 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1235 static bool SecCEPEntrustVersInfo(SecCertificateRef certificate
,
1236 const SecCertificateExtension
*extn
) {
1237 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1241 static bool SecCEPEscrowMarker(SecCertificateRef certificate
,
1242 const SecCertificateExtension
*extn
) {
1243 secdebug("cert", "critical: %s", extn
->critical
? "yes" : "no");
1247 static bool SecCEPOCSPNoCheck(SecCertificateRef certificate
,
1248 const SecCertificateExtension
*extn
) {
1249 secdebug("cert", "ocsp-nocheck critical: %s", extn
->critical
? "yes" : "no");
1253 /* Dictionary key callback for comparing to DERItems. */
1254 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1255 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1258 /* Dictionary key callback calculating the hash of a DERItem. */
1259 static CFHashCode
SecDERItemHash(const void *value
) {
1260 const DERItem
*derItem
= (const DERItem
*)value
;
1261 CFHashCode hash
= derItem
->length
;
1262 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1263 for (; ix
< derItem
->length
; ++ix
) {
1264 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1270 /* Dictionary key callbacks using the above 2 functions. */
1271 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1275 NULL
, /* copyDescription */
1276 SecDERItemEqual
, /* equal */
1277 SecDERItemHash
/* hash */
1280 static void SecCertificateInitializeExtensionParsers(void) {
1281 /* Build a dictionary that maps from extension OIDs to callback functions
1282 which can parse the extension of the type given. */
1283 static const void *extnOIDs
[] = {
1284 &oidSubjectKeyIdentifier
,
1286 &oidPrivateKeyUsagePeriod
,
1289 &oidBasicConstraints
,
1290 &oidNameConstraints
,
1291 &oidCrlDistributionPoints
,
1292 &oidCertificatePolicies
,
1294 &oidAuthorityKeyIdentifier
,
1295 &oidPolicyConstraints
,
1296 &oidExtendedKeyUsage
,
1297 &oidInhibitAnyPolicy
,
1298 &oidAuthorityInfoAccess
,
1299 &oidSubjectInfoAccess
,
1300 &oidNetscapeCertType
,
1301 &oidEntrustVersInfo
,
1302 &oidApplePolicyEscrowService
,
1305 static const void *extnParsers
[] = {
1306 SecCEPSubjectKeyIdentifier
,
1308 SecCEPPrivateKeyUsagePeriod
,
1309 SecCEPSubjectAltName
,
1310 SecCEPIssuerAltName
,
1311 SecCEPBasicConstraints
,
1312 SecCEPNameConstraints
,
1313 SecCEPCrlDistributionPoints
,
1314 SecCEPCertificatePolicies
,
1315 SecCEPPolicyMappings
,
1316 SecCEPAuthorityKeyIdentifier
,
1317 SecCEPPolicyConstraints
,
1318 SecCEPExtendedKeyUsage
,
1319 SecCEPInhibitAnyPolicy
,
1320 SecCEPAuthorityInfoAccess
,
1321 SecCEPSubjectInfoAccess
,
1322 SecCEPNetscapeCertType
,
1323 SecCEPEntrustVersInfo
,
1327 sExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1328 extnParsers
, array_size(extnOIDs
),
1329 &SecDERItemKeyCallBacks
, NULL
);
1332 CFGiblisWithFunctions(SecCertificate
, NULL
, NULL
, SecCertificateDestroy
, SecCertificateEqual
, SecCertificateHash
, NULL
, SecCertificateCopyDescription
, NULL
, NULL
, ^{
1333 SecCertificateInitializeExtensionParsers();
1336 static bool isAppleExtensionOID(const DERItem
*extnID
)
1338 static const uint8_t appleExtension
[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x06 };
1339 return (extnID
&& extnID
->data
&&
1340 extnID
->length
> sizeof(appleExtension
) &&
1341 !memcmp(extnID
->data
, appleExtension
, sizeof(appleExtension
)));
1344 /* Given the contents of an X.501 Name return the contents of a normalized
1346 CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1347 const DERItem
*x501name
) {
1348 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1349 CFIndex length
= x501name
->length
;
1350 CFDataSetLength(result
, length
);
1351 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1354 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1356 require_noerr_quiet(drtn
, badDER
);
1359 /* Always points to last rdn tag. */
1360 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1361 /* Offset relative to base of current rdn set tag. */
1362 CFIndex rdnTagLocation
= 0;
1363 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1364 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1365 /* We don't allow empty RDNs. */
1366 require_quiet(rdn
.content
.length
!= 0, badDER
);
1367 /* Length of the tag and length of the current rdn. */
1368 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1369 CFIndex rdnContentLength
= rdn
.content
.length
;
1370 /* Copy the tag and length of the RDN. */
1371 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1374 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1375 require_quiet(drtn
== DR_Success
, badDER
);
1378 /* Always points to tag of current atv sequence. */
1379 const DERByte
*atvTag
= atvSeq
.nextItem
;
1380 /* Offset relative to base of current atv sequence tag. */
1381 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1382 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1383 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1384 /* Length of the tag and length of the current atv. */
1385 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1386 CFIndex atvContentLength
= atv
.content
.length
;
1387 /* Copy the tag and length of the atv and the atv itself. */
1388 memcpy(base
+ atvTagLocation
, atvTag
,
1389 atvTLLength
+ atv
.content
.length
);
1391 /* Now decode the atv sequence. */
1392 DERAttributeTypeAndValue atvPair
;
1393 drtn
= DERParseSequenceContent(&atv
.content
,
1394 DERNumAttributeTypeAndValueItemSpecs
,
1395 DERAttributeTypeAndValueItemSpecs
,
1396 &atvPair
, sizeof(atvPair
));
1397 require_noerr_quiet(drtn
, badDER
);
1398 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1399 DERDecodedInfo value
;
1400 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1401 require_noerr_quiet(drtn
, badDER
);
1403 /* (c) attribute values in PrintableString are not case sensitive
1404 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1406 (d) attribute values in PrintableString are compared after
1407 removing leading and trailing white space and converting internal
1408 substrings of one or more consecutive white space characters to a
1410 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1411 /* Offset relative to base of current value tag. */
1412 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1413 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1414 CFIndex valueContentLength
= value
.content
.length
;
1416 /* Now copy all the bytes, but convert to upper case while
1417 doing so and convert multiple whitespace chars into a
1419 bool lastWasBlank
= false;
1420 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1421 CFIndex valueCurrentLocation
= valueLocation
;
1423 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1424 UInt8 ch
= value
.content
.data
[ix
];
1429 /* Don't insert a space for first character
1431 if (valueCurrentLocation
> valueLocation
) {
1432 base
[valueCurrentLocation
++] = ' ';
1434 lastWasBlank
= true;
1437 lastWasBlank
= false;
1438 if ('a' <= ch
&& ch
<= 'z') {
1439 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1441 base
[valueCurrentLocation
++] = ch
;
1445 /* Finally if lastWasBlank remove the trailing space. */
1446 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1447 valueCurrentLocation
--;
1449 /* Adjust content length to normalized length. */
1450 valueContentLength
= valueCurrentLocation
- valueLocation
;
1452 /* Number of bytes by which the length should be shorted. */
1453 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1454 if (lengthDiff
== 0) {
1455 /* Easy case no need to adjust lengths. */
1457 /* Hard work we need to go back and fix up length fields
1459 1) The value itself.
1460 2) The ATV Sequence containing type/value
1461 3) The RDN Set containing one or more atv pairs.
1465 /* Step 1 fix up length of value. */
1466 /* Length of value tag and length minus the tag. */
1467 DERSize newValueTLLength
= valueTLLength
- 1;
1468 drtn
= DEREncodeLength(valueContentLength
,
1469 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1470 require(drtn
== DR_Success
, badDER
);
1471 /* Add the length of the tag back in. */
1473 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1475 /* The size of the length field changed, let's slide
1476 the value back by valueLLDiff bytes. */
1477 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1478 base
+ valueTagLocation
+ valueTLLength
,
1479 valueContentLength
);
1480 /* The length diff for the enclosing object. */
1481 lengthDiff
+= valueLLDiff
;
1484 /* Step 2 fix up length of the enclosing ATV Sequence. */
1485 atvContentLength
-= lengthDiff
;
1486 DERSize newATVTLLength
= atvTLLength
- 1;
1487 drtn
= DEREncodeLength(atvContentLength
,
1488 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1489 require(drtn
== DR_Success
, badDER
);
1490 /* Add the length of the tag back in. */
1492 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1494 /* The size of the length field changed, let's slide
1495 the value back by valueLLDiff bytes. */
1496 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1497 base
+ atvTagLocation
+ atvTLLength
,
1499 /* The length diff for the enclosing object. */
1500 lengthDiff
+= atvLLDiff
;
1501 atvTLLength
= newATVTLLength
;
1504 /* Step 3 fix up length of enclosing RDN Set. */
1505 rdnContentLength
-= lengthDiff
;
1506 DERSize newRDNTLLength
= rdnTLLength
- 1;
1507 drtn
= DEREncodeLength(rdnContentLength
,
1508 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1509 require_quiet(drtn
== DR_Success
, badDER
);
1510 /* Add the length of the tag back in. */
1512 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1514 /* The size of the length field changed, let's slide
1515 the value back by valueLLDiff bytes. */
1516 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1517 base
+ rdnTagLocation
+ rdnTLLength
,
1519 /* The length diff for the enclosing object. */
1520 lengthDiff
+= rdnLLDiff
;
1521 rdnTLLength
= newRDNTLLength
;
1523 /* Adjust the locations that might have changed due to
1525 atvTagLocation
-= rdnLLDiff
;
1527 (void) lengthDiff
; // No next object, silence analyzer
1530 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1531 atvTag
= atvSeq
.nextItem
;
1533 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1534 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1535 rdnTag
= rdnSeq
.nextItem
;
1537 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1538 /* Truncate the result to the proper length. */
1539 CFDataSetLength(result
, rdnTagLocation
);
1548 static CFDataRef
SecDERItemCopySequence(DERItem
*content
) {
1549 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
1550 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
1551 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
1553 CFDataSetLength(sequence
, sequence_length
);
1554 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
1555 *sequence_ptr
++ = ONE_BYTE_ASN1_CONSTR_SEQUENCE
;
1556 require_noerr_quiet(DEREncodeLength(content
->length
,
1557 sequence_ptr
, &seq_len_length
), out
);
1558 sequence_ptr
+= seq_len_length
;
1559 memcpy(sequence_ptr
, content
->data
, content
->length
);
1562 CFReleaseSafe(sequence
);
1566 static CFDataRef
SecCopySequenceFromContent(CFDataRef content
) {
1568 tmpItem
.data
= (void *)CFDataGetBytePtr(content
);
1569 tmpItem
.length
= CFDataGetLength(content
);
1571 return SecDERItemCopySequence(&tmpItem
);
1574 CFDataRef
SecDistinguishedNameCopyNormalizedContent(CFDataRef distinguished_name
)
1576 const DERItem name
= { (unsigned char *)CFDataGetBytePtr(distinguished_name
), CFDataGetLength(distinguished_name
) };
1577 DERDecodedInfo content
;
1578 /* Decode top level sequence into DERItem */
1579 if (!DERDecodeItem(&name
, &content
) && (content
.tag
== ASN1_CONSTR_SEQUENCE
))
1580 return createNormalizedX501Name(kCFAllocatorDefault
, &content
.content
);
1584 CFDataRef
SecDistinguishedNameCopyNormalizedSequence(CFDataRef distinguished_name
)
1586 if (!distinguished_name
) { return NULL
; }
1587 CFDataRef normalizedContent
= SecDistinguishedNameCopyNormalizedContent(distinguished_name
);
1588 if (!normalizedContent
) { return NULL
; }
1589 CFDataRef result
= SecCopySequenceFromContent(normalizedContent
);
1590 CFReleaseNull(normalizedContent
);
1594 /* AUDIT[securityd]:
1595 certificate->_der is a caller provided data of any length (might be 0).
1597 Top level certificate decode.
1599 static bool SecCertificateParse(SecCertificateRef certificate
)
1604 require_quiet(certificate
, badCert
);
1605 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1607 /* top level decode */
1608 DERSignedCertCrl signedCert
;
1609 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1610 DERSignedCertCrlItemSpecs
, &signedCert
,
1611 sizeof(signedCert
));
1612 require_noerr_quiet(drtn
, badCert
);
1613 /* Store tbs since we need to digest it for verification later on. */
1614 certificate
->_tbs
= signedCert
.tbs
;
1616 /* decode the TBSCert - it was saved in full DER form */
1618 drtn
= DERParseSequence(&signedCert
.tbs
,
1619 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1620 &tbsCert
, sizeof(tbsCert
));
1621 require_noerr_quiet(drtn
, badCert
);
1623 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1624 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1625 of the params field. */
1626 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1627 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1628 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1629 require_noerr_quiet(drtn
, badCert
);
1631 /* The contents of signedCert.sig is a bit string whose contents
1632 are the signature itself. */
1633 DERByte numUnusedBits
;
1634 drtn
= DERParseBitString(&signedCert
.sig
,
1635 &certificate
->_signature
, &numUnusedBits
);
1636 require_noerr_quiet(drtn
, badCert
);
1638 /* Now decode the tbsCert. */
1640 /* First we turn the optional version into an int. */
1641 if (tbsCert
.version
.length
) {
1642 DERDecodedInfo decoded
;
1643 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1644 require_noerr_quiet(drtn
, badCert
);
1645 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1646 require_quiet(decoded
.content
.length
== 1, badCert
);
1647 certificate
->_version
= decoded
.content
.data
[0];
1648 if (certificate
->_version
> 2) {
1649 secwarning("Invalid certificate version (%d), must be 0..2",
1650 certificate
->_version
);
1652 require_quiet(certificate
->_version
> 0, badCert
);
1653 require_quiet(certificate
->_version
< 3, badCert
);
1655 certificate
->_version
= 0;
1658 /* The serial number is in the tbsCert.serialNum - it was saved in
1659 INTEGER form without the tag and length. */
1660 certificate
->_serialNum
= tbsCert
.serialNum
;
1662 /* Note: RFC5280 4.1.2.2 limits serial number values to 20 octets.
1663 For now, we warn about larger values, but will still create the
1664 certificate with values up to 36 octets to avoid breaking some
1665 nonconforming certs with slightly longer serial numbers.
1666 We also explicitly allow serial numbers of 21 octets where the
1667 leading byte is 0x00 which is used to make a negative 20 octet
1670 if (tbsCert
.serialNum
.length
< 1 || tbsCert
.serialNum
.length
> 21 ||
1671 (tbsCert
.serialNum
.length
== 21 && tbsCert
.serialNum
.data
[0] != 0x00)) {
1672 secwarning("Invalid serial number length (%ld), must be 1..20",
1673 tbsCert
.serialNum
.length
);
1675 require_quiet(tbsCert
.serialNum
.data
!= NULL
&&
1676 tbsCert
.serialNum
.length
>= 1 &&
1677 tbsCert
.serialNum
.length
<= 37, badCert
);
1678 certificate
->_serialNumber
= CFDataCreate(allocator
,
1679 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1681 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1682 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1683 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1684 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1685 require_noerr_quiet(drtn
, badCert
);
1687 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1688 and length fields. */
1689 certificate
->_issuer
= tbsCert
.issuer
;
1690 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1693 /* sequence we're given: decode the tbsCerts Validity sequence. */
1694 DERValidity validity
;
1695 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1696 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1697 &validity
, sizeof(validity
));
1698 require_noerr_quiet(drtn
, badCert
);
1699 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1700 &certificate
->_notBefore
), badCert
);
1701 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1702 &certificate
->_notAfter
), badCert
);
1704 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1705 and length fields. */
1706 certificate
->_subject
= tbsCert
.subject
;
1707 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1710 /* Keep the SPKI around for CT */
1711 certificate
->_subjectPublicKeyInfo
= tbsCert
.subjectPubKey
;
1713 /* sequence we're given: encoded DERSubjPubKeyInfo */
1714 DERSubjPubKeyInfo pubKeyInfo
;
1715 drtn
= DERParseSequenceContent(&tbsCert
.subjectPubKey
,
1716 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1717 &pubKeyInfo
, sizeof(pubKeyInfo
));
1718 require_noerr_quiet(drtn
, badCert
);
1720 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1721 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1722 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1723 &certificate
->_algId
, sizeof(certificate
->_algId
));
1724 require_noerr_quiet(drtn
, badCert
);
1726 /* Now we can figure out the key's algorithm id and params based on
1727 certificate->_algId.oid. */
1729 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1730 are a PKCS1 format RSA key. */
1731 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1732 &certificate
->_pubKeyDER
, &numUnusedBits
);
1733 require_noerr_quiet(drtn
, badCert
);
1735 /* The contents of tbsCert.issuerID is a bit string. */
1736 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1738 /* The contents of tbsCert.subjectID is a bit string. */
1739 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1742 if (tbsCert
.extensions
.length
) {
1743 CFIndex extensionCount
= 0;
1746 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1748 require_noerr_quiet(drtn
, badCert
);
1749 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1750 DERDecodedInfo currDecoded
;
1751 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1753 /* ! = MUST recognize ? = SHOULD recognize
1756 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1757 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1758 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1759 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1760 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1761 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1762 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1763 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1765 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1766 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1767 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1768 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1769 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1770 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1771 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1772 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1774 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1775 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1780 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1782 /* Put some upper limit on the number of extensions allowed. */
1783 require_quiet(extensionCount
< 10000, badCert
);
1784 certificate
->_extensionCount
= extensionCount
;
1785 certificate
->_extensions
=
1786 malloc(sizeof(SecCertificateExtension
) * (extensionCount
> 0 ? extensionCount
: 1));
1787 require_quiet(certificate
->_extensions
, badCert
);
1790 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1791 require_noerr_quiet(drtn
, badCert
);
1792 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1793 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1794 require_quiet(drtn
== DR_Success
||
1795 (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1796 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1798 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1799 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1800 &extn
, sizeof(extn
));
1801 require_noerr_quiet(drtn
, badCert
);
1802 /* Copy stuff into certificate->extensions[ix]. */
1803 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1804 require_noerr_quiet(drtn
= DERParseBooleanWithDefault(&extn
.critical
, false,
1805 &certificate
->_extensions
[ix
].critical
), badCert
);
1806 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1808 SecCertificateExtensionParser parser
=
1809 (SecCertificateExtensionParser
)CFDictionaryGetValue(
1810 sExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1812 /* Invoke the parser. If the extension is critical and the
1813 * parser fails, fail the cert. */
1814 require_quiet(parser(certificate
, &certificate
->_extensions
[ix
]) ||
1815 !certificate
->_extensions
[ix
].critical
, badCert
);
1816 } else if (certificate
->_extensions
[ix
].critical
) {
1817 if (isAppleExtensionOID(&extn
.extnID
)) {
1820 secdebug("cert", "Found unknown critical extension");
1821 certificate
->_foundUnknownCriticalExtension
= true;
1823 secdebug("cert", "Found unknown non critical extension");
1827 checkForMissingRevocationInfo(certificate
);
1836 /* Public API functions. */
1837 SecCertificateRef
SecCertificateCreateWithBytes(CFAllocatorRef allocator
,
1838 const UInt8
*der_bytes
, CFIndex der_length
) {
1839 if (der_bytes
== NULL
) return NULL
;
1840 if (der_length
== 0) return NULL
;
1842 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1843 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1844 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1846 memset((char*)result
+ sizeof(result
->_base
), 0,
1847 sizeof(*result
) - sizeof(result
->_base
));
1848 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1849 result
->_der
.length
= der_length
;
1850 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1851 if (!SecCertificateParse(result
)) {
1859 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1860 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1861 const UInt8
*der_bytes
, CFIndex der_length
);
1863 SecCertificateRef
SecCertificateCreate(CFAllocatorRef allocator
,
1864 const UInt8
*der_bytes
, CFIndex der_length
) {
1865 return SecCertificateCreateWithBytes(allocator
, der_bytes
, der_length
);
1867 /* @@@ End of placeholder. */
1869 /* AUDIT[securityd](done):
1870 der_certificate is a caller provided data of any length (might be 0), only
1871 its cf type has been checked.
1873 SecCertificateRef
SecCertificateCreateWithData(CFAllocatorRef allocator
,
1874 CFDataRef der_certificate
) {
1875 if (!der_certificate
) {
1878 CFIndex size
= sizeof(struct __SecCertificate
);
1879 SecCertificateRef result
= (SecCertificateRef
)_CFRuntimeCreateInstance(
1880 allocator
, SecCertificateGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1882 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1883 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1884 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1885 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1886 if (!SecCertificateParse(result
)) {
1894 SecCertificateRef
SecCertificateCreateWithKeychainItem(CFAllocatorRef allocator
,
1895 CFDataRef der_certificate
,
1896 CFTypeRef keychain_item
)
1898 SecCertificateRef result
= SecCertificateCreateWithData(allocator
, der_certificate
);
1900 CFRetainSafe(keychain_item
);
1901 result
->_keychain_item
= keychain_item
;
1906 OSStatus
SecCertificateSetKeychainItem(SecCertificateRef certificate
,
1907 CFTypeRef keychain_item
)
1912 CFRetainSafe(keychain_item
);
1913 CFReleaseSafe(certificate
->_keychain_item
);
1914 certificate
->_keychain_item
= keychain_item
;
1915 return errSecSuccess
;
1918 CFDataRef
SecCertificateCopyData(SecCertificateRef certificate
) {
1920 CFDataRef result
= NULL
;
1924 if (certificate
->_der_data
) {
1925 CFRetain(certificate
->_der_data
);
1926 result
= certificate
->_der_data
;
1928 result
= CFDataCreate(CFGetAllocator(certificate
),
1929 certificate
->_der
.data
, certificate
->_der
.length
);
1931 /* FIXME: If we wish to cache result we need to lock the certificate.
1932 Also this create 2 copies of the certificate data which is somewhat
1935 certificate
->_der_data
= result
;
1942 CFIndex
SecCertificateGetLength(SecCertificateRef certificate
) {
1943 return certificate
->_der
.length
;
1946 const UInt8
*SecCertificateGetBytePtr(SecCertificateRef certificate
) {
1947 return certificate
->_der
.data
;
1950 /* Used to recreate preCert from cert for Certificate Transparency */
1951 CFDataRef
SecCertificateCopyPrecertTBS(SecCertificateRef certificate
)
1953 CFDataRef outData
= NULL
;
1954 DERItem tbsIn
= certificate
->_tbs
;
1955 DERItem tbsOut
= {0,};
1956 DERItem extensionsOut
= {0,};
1957 DERItem
*extensionsList
= malloc(sizeof(DERItem
)*certificate
->_extensionCount
); /* This maybe one too many */
1958 DERItemSpec
*extensionsListSpecs
= malloc(sizeof(DERItemSpec
)*certificate
->_extensionCount
);
1962 require_quiet(extensionsList
&& extensionsListSpecs
, out
);
1964 /* decode the TBSCert - it was saved in full DER form */
1965 drtn
= DERParseSequence(&tbsIn
,
1966 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1967 &tbsCert
, sizeof(tbsCert
));
1968 require_noerr_quiet(drtn
, out
);
1970 /* Go over extensions and filter any SCT extension */
1971 CFIndex extensionsCount
= 0;
1973 if (tbsCert
.extensions
.length
) {
1976 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1978 require_noerr_quiet(drtn
, out
);
1979 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
1980 DERDecodedInfo currDecoded
;
1981 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1983 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, out
);
1985 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1986 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1987 &extn
, sizeof(extn
));
1988 require_noerr_quiet(drtn
, out
);
1990 if (extn
.extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
1991 !memcmp(extn
.extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
.extnID
.length
))
1994 extensionsList
[extensionsCount
] = currDecoded
.content
;
1995 extensionsListSpecs
[extensionsCount
].offset
= sizeof(DERItem
)*extensionsCount
;
1996 extensionsListSpecs
[extensionsCount
].options
= 0;
1997 extensionsListSpecs
[extensionsCount
].tag
= ASN1_CONSTR_SEQUENCE
;
2002 require_quiet(drtn
== DR_EndOfSequence
, out
);
2006 /* Encode extensions */
2007 extensionsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
);
2008 extensionsOut
.data
= malloc(extensionsOut
.length
);
2009 require_quiet(extensionsOut
.data
, out
);
2010 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, extensionsList
, extensionsCount
, extensionsListSpecs
, extensionsOut
.data
, &extensionsOut
.length
);
2011 require_noerr_quiet(drtn
, out
);
2013 tbsCert
.extensions
= extensionsOut
;
2015 tbsOut
.length
= DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
);
2016 tbsOut
.data
= malloc(tbsOut
.length
);
2017 require_quiet(tbsOut
.data
, out
);
2018 drtn
= DEREncodeSequence(ASN1_CONSTR_SEQUENCE
, &tbsCert
, DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
, tbsOut
.data
, &tbsOut
.length
);
2019 require_noerr_quiet(drtn
, out
);
2021 outData
= CFDataCreate(kCFAllocatorDefault
, tbsOut
.data
, tbsOut
.length
);
2024 if (extensionsOut
.data
) free(extensionsOut
.data
);
2025 if (tbsOut
.data
) free(tbsOut
.data
);
2026 if (extensionsList
) free(extensionsList
);
2027 if (extensionsListSpecs
) free(extensionsListSpecs
);
2032 /* From rfc3280 - Appendix B. ASN.1 Notes
2034 Object Identifiers (OIDs) are used throughout this specification to
2035 identify certificate policies, public key and signature algorithms,
2036 certificate extensions, etc. There is no maximum size for OIDs.
2037 This specification mandates support for OIDs which have arc elements
2038 with values that are less than 2^28, that is, they MUST be between 0
2039 and 268,435,455, inclusive. This allows each arc element to be
2040 represented within a single 32 bit word. Implementations MUST also
2041 support OIDs where the length of the dotted decimal (see [RFC 2252],
2042 section 4.1) string representation can be up to 100 bytes
2043 (inclusive). Implementations MUST be able to handle OIDs with up to
2044 20 elements (inclusive). CAs SHOULD NOT issue certificates which
2045 contain OIDs that exceed these requirements. Likewise, CRL issuers
2046 SHOULD NOT issue CRLs which contain OIDs that exceed these
2050 /* Oids longer than this are considered invalid. */
2051 #define MAX_OID_SIZE 32
2053 CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
2054 const DERItem
*oid
) {
2056 if (oid
->length
== 0) {
2057 return SecCopyCertString(SEC_NULL_KEY
);
2059 if (oid
->length
> MAX_OID_SIZE
) {
2060 return SecCopyCertString(SEC_OID_TOO_LONG_KEY
);
2063 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
2065 // The first two levels are encoded into one byte, since the root level
2066 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
2067 // y may be > 39, so we have to add special-case handling for this.
2068 uint32_t x
= oid
->data
[0] / 40;
2069 uint32_t y
= oid
->data
[0] % 40;
2072 // Handle special case for large y if x = 2
2076 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
2079 for (x
= 1; x
< oid
->length
; ++x
)
2081 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
2082 /* @@@ value may not span more than 4 bytes. */
2083 /* A max number of 20 values is allowed. */
2084 if (!(oid
->data
[x
] & 0x80))
2086 CFStringAppendFormat(result
, NULL
, CFSTR(".%" PRIu32
), value
);
2093 static CFStringRef
copyOidDescription(CFAllocatorRef allocator
,
2094 const DERItem
*oid
, bool localized
) {
2095 if (!oid
|| oid
->length
== 0) {
2096 return (localized
) ? SecCopyCertString(SEC_NULL_KEY
) : SEC_NULL_KEY
;
2099 CFStringRef name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
2104 /* Build the key we use to lookup the localized OID description. */
2105 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
2106 oid
->length
* 3 + 5);
2107 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), oid
->length
);
2108 for (DERSize ix
= 0; ix
< oid
->length
; ++ix
) {
2109 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
2111 CFStringRef locname
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
2112 if (locname
&& !CFEqual(oidKey
, locname
)) {
2113 /* Found localized description string, so use it instead of OID. */
2114 CFReleaseSafe(name
);
2117 CFReleaseSafe(locname
);
2124 /* Return the ipAddress as a dotted quad for ipv4, or as 8 colon separated
2125 4 digit hex strings for ipv6. Return NULL if the provided IP doesn't
2126 have a length of exactly 4 or 16 octets.
2128 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
2129 const DERItem
*ip
) {
2130 /* This is the IP Address as an OCTET STRING.
2131 For IPv4 it's 4 octets addr, or 8 octets, addr/mask.
2132 For IPv6 it's 16 octets addr, or 32 octets addr/mask.
2134 CFStringRef value
= NULL
;
2135 if (ip
->length
== 4) {
2136 value
= CFStringCreateWithFormat(allocator
, NULL
,
2137 CFSTR("%u.%u.%u.%u"),
2138 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
2139 } else if (ip
->length
== 16) {
2140 value
= CFStringCreateWithFormat(allocator
, NULL
,
2141 CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
2142 "%02x%02x:%02x%02x:%02x%02x:%02x%02x"),
2143 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
2144 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
2145 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
2146 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
2152 void appendProperty(CFMutableArrayRef properties
, CFStringRef propertyType
,
2153 CFStringRef label
, CFStringRef localizedLabel
, CFTypeRef value
,
2155 CFDictionaryRef property
;
2157 CFStringRef ll
= NULL
;
2159 /* use unlocalized label, overriding localizedLabel */
2160 ll
= localizedLabel
= (CFStringRef
) CFRetainSafe(label
);
2161 } else if (!localizedLabel
) {
2162 /* copy localized label for unlocalized label */
2163 ll
= localizedLabel
= SecCopyCertString(label
);
2165 const void *all_keys
[4];
2166 all_keys
[0] = kSecPropertyKeyType
;
2167 all_keys
[1] = kSecPropertyKeyLabel
;
2168 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
2169 all_keys
[3] = kSecPropertyKeyValue
;
2170 const void *property_values
[] = {
2176 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2177 all_keys
, property_values
, value
? 4 : 3,
2178 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2181 const void *nolabel_keys
[2];
2182 nolabel_keys
[0] = kSecPropertyKeyType
;
2183 nolabel_keys
[1] = kSecPropertyKeyValue
;
2184 const void *property_values
[] = {
2188 property
= CFDictionaryCreate(CFGetAllocator(properties
),
2189 nolabel_keys
, property_values
, 2,
2190 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2193 CFArrayAppendValue(properties
, property
);
2194 CFRelease(property
);
2198 #define UTC_TIME_NOSEC_ZULU_LEN 11
2200 #define UTC_TIME_ZULU_LEN 13
2201 /* YYMMDDhhmmssThhmm */
2202 #define UTC_TIME_LOCALIZED_LEN 17
2203 /* YYYYMMDDhhmmssZ */
2204 #define GENERALIZED_TIME_ZULU_LEN 15
2205 /* YYYYMMDDhhmmssThhmm */
2206 #define GENERALIZED_TIME_LOCALIZED_LEN 19
2208 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
2210 static inline int parseDecimalPair(const DERByte
**p
) {
2211 const DERByte
*cp
= *p
;
2213 return 10 * (cp
[0] - '0') + cp
[1] - '0';
2216 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime.
2217 Return a CFErrorRef in the error parameter if decoding fails.
2218 Note that this is needed to distinguish an error condition from a
2219 valid time which specifies 2001-01-01 00:00:00 (i.e. a value of 0).
2221 static CFAbsoluteTime
SecAbsoluteTimeFromDateContentWithError(DERTag tag
,
2222 const uint8_t *bytes
,
2224 CFErrorRef
*error
) {
2228 if (NULL
== bytes
|| 0 == length
) {
2232 bool isUtcLength
= false;
2233 bool isLocalized
= false;
2234 bool noSeconds
= false;
2236 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
2240 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
2243 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
2245 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
2248 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
2251 default: /* unknown format */
2255 /* Make sure the der tag fits the thing inside it. */
2256 if (tag
== ASN1_UTC_TIME
) {
2260 } else if (tag
== ASN1_GENERALIZED_TIME
) {
2268 const DERByte
*cp
= bytes
;
2269 /* Check that all characters are digits, except if localized the timezone
2270 indicator or if not localized the 'Z' at the end. */
2272 for (ix
= 0; ix
< length
; ++ix
) {
2273 if (!(isdigit(cp
[ix
]))) {
2274 if ((isLocalized
&& ix
== length
- 5 &&
2275 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
2276 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
2283 /* Parse the date and time fields. */
2284 int year
, month
, day
, hour
, minute
, second
;
2286 year
= parseDecimalPair(&cp
);
2288 /* 0 <= year < 50 : assume century 21 */
2290 } else if (year
< 70) {
2291 /* 50 <= year < 70 : illegal per PKIX */
2294 /* 70 < year <= 99 : assume century 20 */
2298 year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
2300 month
= parseDecimalPair(&cp
);
2301 day
= parseDecimalPair(&cp
);
2302 hour
= parseDecimalPair(&cp
);
2303 minute
= parseDecimalPair(&cp
);
2307 second
= parseDecimalPair(&cp
);
2310 CFTimeInterval timeZoneOffset
;
2312 /* ZONE INDICATOR */
2313 int multiplier
= *cp
++ == '+' ? 60 : -60;
2314 timeZoneOffset
= multiplier
*
2315 (parseDecimalPair(&cp
) * 60 + parseDecimalPair(&cp
));
2320 secdebug("dateparse",
2321 "date %.*s year: %04d%02d%02d%02d%02d%02d%+05g",
2322 (int) length
, bytes
, year
, month
,
2323 day
, hour
, minute
, second
,
2324 timeZoneOffset
/ 60);
2326 static int mdays
[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
2327 int is_leap_year
= year
% 4 == 0 && (year
% 100 != 0 || year
% 400 == 0) ? 1 : 0;
2328 if (month
< 1 || month
> 12 || day
< 1 || day
> 31 || hour
> 23 || minute
> 59 || second
> 59
2329 || (month
== 2 && day
> mdays
[month
] - mdays
[month
- 1] + is_leap_year
)
2330 || (month
!= 2 && day
> mdays
[month
] - mdays
[month
- 1])) {
2335 int dy
= year
- 2001;
2340 int leap_days
= dy
/ 4 - dy
/ 100 + dy
/ 400;
2341 day
+= ((year
- 2001) * 365 + leap_days
) + mdays
[month
- 1] - 1;
2343 day
+= is_leap_year
;
2345 CFAbsoluteTime absTime
= (CFAbsoluteTime
)((day
* 24.0 + hour
) * 60.0 + minute
) * 60.0 + second
;
2346 return absTime
- timeZoneOffset
;
2350 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
2355 CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
2357 return SecAbsoluteTimeFromDateContentWithError(tag
, bytes
, length
, NULL
);
2360 __attribute__((__nonnull__
)) static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
2361 CFAbsoluteTime
*pabsTime
) {
2362 CFErrorRef error
= NULL
;
2363 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContentWithError(tag
, date
->data
,
2364 date
->length
, &error
);
2366 secwarning("Invalid date specification in certificate (see RFC5280 4.1.2.5)");
2371 *pabsTime
= absTime
;
2375 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
2376 true if the date was valid and properly decoded, also return the result in
2377 absTime. Return false otherwise. */
2378 __attribute__((__nonnull__
)) static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
2379 CFAbsoluteTime
*absTime
) {
2380 if (dateChoice
->length
== 0) return false;
2382 DERDecodedInfo decoded
;
2383 if (DERDecodeItem(dateChoice
, &decoded
))
2386 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
2390 static void appendDataProperty(CFMutableArrayRef properties
,
2391 CFStringRef label
, CFStringRef localizedLabel
, const DERItem
*der_data
,
2393 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
2394 der_data
->data
, der_data
->length
);
2395 appendProperty(properties
, kSecPropertyTypeData
, label
, localizedLabel
,
2400 static void appendRelabeledProperty(CFMutableArrayRef properties
,
2402 CFStringRef localizedLabel
,
2403 const DERItem
*der_data
,
2404 CFStringRef labelFormat
,
2406 CFStringRef newLabel
=
2407 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2408 labelFormat
, label
);
2409 CFStringRef ll
= NULL
;
2410 CFStringRef localizedLabelFormat
= NULL
;
2412 /* use provided label and format strings; do not localize */
2413 ll
= localizedLabel
= (CFStringRef
) CFRetainSafe(label
);
2414 localizedLabelFormat
= (CFStringRef
) CFRetainSafe(labelFormat
);
2416 if (!localizedLabel
) {
2417 /* copy localized label for provided label */
2418 ll
= localizedLabel
= SecCopyCertString(label
);
2420 /* copy localized format for provided format */
2421 localizedLabelFormat
= SecCopyCertString(labelFormat
);
2424 CFStringRef newLocalizedLabel
=
2425 CFStringCreateWithFormat(CFGetAllocator(properties
), NULL
,
2426 localizedLabelFormat
, localizedLabel
);
2428 CFReleaseSafe(localizedLabelFormat
);
2429 appendDataProperty(properties
, newLabel
, newLocalizedLabel
, der_data
, localized
);
2430 CFReleaseSafe(newLabel
);
2431 CFReleaseSafe(newLocalizedLabel
);
2435 static void appendUnparsedProperty(CFMutableArrayRef properties
,
2436 CFStringRef label
, CFStringRef localizedLabel
,
2437 const DERItem
*der_data
, bool localized
) {
2438 appendRelabeledProperty(properties
, label
, localizedLabel
, der_data
,
2439 SEC_UNPARSED_KEY
, localized
);
2442 static void appendInvalidProperty(CFMutableArrayRef properties
,
2443 CFStringRef label
, const DERItem
*der_data
, bool localized
) {
2444 appendRelabeledProperty(properties
, label
, NULL
, der_data
,
2445 SEC_INVALID_KEY
, localized
);
2448 static void appendDateContentProperty(CFMutableArrayRef properties
,
2449 CFStringRef label
, DERTag tag
,
2450 const DERItem
*dateContent
, bool localized
) {
2451 CFAbsoluteTime absTime
;
2452 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
2453 /* Date decode failure; insert hex bytes instead. */
2454 return appendInvalidProperty(properties
, label
, dateContent
, localized
);
2456 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2457 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
, localized
);
2461 static void appendDateProperty(CFMutableArrayRef properties
,
2462 CFStringRef label
, CFAbsoluteTime absTime
, bool localized
) {
2463 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
2464 appendProperty(properties
, kSecPropertyTypeDate
, label
, NULL
, date
, localized
);
2468 static void appendValidityPeriodProperty(CFMutableArrayRef parent
, CFStringRef label
,
2469 SecCertificateRef certificate
, bool localized
) {
2470 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2471 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2473 appendDateProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
2474 certificate
->_notBefore
, localized
);
2475 appendDateProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
2476 certificate
->_notAfter
, localized
);
2478 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
, properties
, localized
);
2479 CFReleaseNull(properties
);
2482 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
2483 CFStringRef label
, const DERItem
*ip
, bool localized
) {
2485 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
2487 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
, value
, localized
);
2490 appendUnparsedProperty(properties
, label
, NULL
, ip
, localized
);
2494 static void appendURLContentProperty(CFMutableArrayRef properties
,
2495 CFStringRef label
, const DERItem
*urlContent
, bool localized
) {
2496 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
2497 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2499 appendProperty(properties
, kSecPropertyTypeURL
, label
, NULL
, url
, localized
);
2502 appendInvalidProperty(properties
, label
, urlContent
, localized
);
2506 static void appendURLProperty(CFMutableArrayRef properties
,
2507 CFStringRef label
, const DERItem
*url
, bool localized
) {
2508 DERDecodedInfo decoded
;
2511 drtn
= DERDecodeItem(url
, &decoded
);
2512 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2513 appendInvalidProperty(properties
, label
, url
, localized
);
2515 appendURLContentProperty(properties
, label
, &decoded
.content
, localized
);
2519 static void appendOIDProperty(CFMutableArrayRef properties
,
2520 CFStringRef label
, CFStringRef llabel
, const DERItem
*oid
, bool localized
) {
2521 CFStringRef oid_string
=
2522 copyOidDescription(CFGetAllocator(properties
), oid
, localized
);
2523 appendProperty(properties
, kSecPropertyTypeString
, label
, llabel
,
2524 oid_string
, localized
);
2525 CFRelease(oid_string
);
2528 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2529 CFStringRef label
, const DERAlgorithmId
*algorithm
, bool localized
) {
2530 CFMutableArrayRef alg_props
=
2531 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2532 &kCFTypeArrayCallBacks
);
2533 appendOIDProperty(alg_props
, SEC_ALGORITHM_KEY
, NULL
,
2534 &algorithm
->oid
, localized
);
2535 if (algorithm
->params
.length
) {
2536 if (algorithm
->params
.length
== 2 &&
2537 algorithm
->params
.data
[0] == ASN1_NULL
&&
2538 algorithm
->params
.data
[1] == 0) {
2539 CFStringRef value
= SecCopyCertString(SEC_NONE_KEY
);
2540 appendProperty(alg_props
, kSecPropertyTypeString
,
2541 SEC_PARAMETERS_KEY
, NULL
, value
, localized
);
2544 appendUnparsedProperty(alg_props
, SEC_PARAMETERS_KEY
, NULL
,
2545 &algorithm
->params
, localized
);
2548 appendProperty(properties
, kSecPropertyTypeSection
, label
, NULL
,
2549 alg_props
, localized
);
2550 CFRelease(alg_props
);
2553 static void appendPublicKeyProperty(CFMutableArrayRef parent
, CFStringRef label
,
2554 SecCertificateRef certificate
, bool localized
) {
2555 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2556 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2558 /* Public key algorithm. */
2559 appendAlgorithmProperty(properties
, SEC_PUBLIC_KEY_ALG_KEY
,
2560 &certificate
->_algId
, localized
);
2562 /* Public Key Size */
2563 SecKeyRef publicKey
= SecCertificateCopyKey(certificate
);
2565 size_t sizeInBytes
= SecKeyGetBlockSize(publicKey
);
2566 CFStringRef sizeInBitsString
= CFStringCreateWithFormat(allocator
, NULL
,
2567 CFSTR("%ld"), (sizeInBytes
*8));
2568 if (sizeInBitsString
) {
2569 appendProperty(properties
, kSecPropertyTypeString
, SEC_PUBLIC_KEY_SIZE_KEY
,
2570 NULL
, sizeInBitsString
, localized
);
2572 CFReleaseNull(sizeInBitsString
);
2574 CFReleaseNull(publicKey
);
2576 /* Consider breaking down an RSA public key into modulus and
2578 appendDataProperty(properties
, SEC_PUBLIC_KEY_DATA_KEY
, NULL
,
2579 &certificate
->_pubKeyDER
, localized
);
2581 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2582 properties
, localized
);
2583 CFReleaseNull(properties
);
2586 static void appendSignatureProperty(CFMutableArrayRef parent
, CFStringRef label
,
2587 SecCertificateRef certificate
, bool localized
) {
2588 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2589 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2591 appendAlgorithmProperty(properties
, SEC_SIGNATURE_ALGORITHM_KEY
,
2592 &certificate
->_tbsSigAlg
, localized
);
2594 appendDataProperty(properties
, SEC_SIGNATURE_DATA_KEY
, NULL
,
2595 &certificate
->_signature
, localized
);
2597 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2598 properties
, localized
);
2599 CFReleaseNull(properties
);
2602 static void appendFingerprintsProperty(CFMutableArrayRef parent
, CFStringRef label
,
2603 SecCertificateRef certificate
, bool localized
) {
2604 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2605 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2607 CFDataRef sha256Fingerprint
= SecCertificateCopySHA256Digest(certificate
);
2608 if (sha256Fingerprint
) {
2609 appendProperty(properties
, kSecPropertyTypeData
, SEC_SHA2_FINGERPRINT_KEY
,
2610 NULL
, sha256Fingerprint
, localized
);
2612 CFReleaseNull(sha256Fingerprint
);
2614 appendProperty(properties
, kSecPropertyTypeData
, SEC_SHA1_FINGERPRINT_KEY
,
2615 NULL
, SecCertificateGetSHA1Digest(certificate
), localized
);
2617 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2618 properties
, localized
);
2619 CFReleaseNull(properties
);
2622 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2623 const DERItem
*blob
) {
2624 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2625 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2626 blob
->length
* 3 - 1);
2627 for (ix
= 0; ix
< length
; ++ix
)
2629 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2631 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2636 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2637 CFStringRef blobType
, CFStringRef quanta
,
2638 const DERItem
*blob
, bool localized
) {
2639 CFStringRef localizedBlobType
= (localized
) ?
2640 SecCopyCertString(blobType
) : (CFStringRef
) CFRetainSafe(blobType
);
2641 CFStringRef localizedQuanta
= (localized
) ?
2642 SecCopyCertString(quanta
) : (CFStringRef
) CFRetainSafe(quanta
);
2643 /* "format string for encoded field data (e.g. Sequence; 128 bytes; "
2644 "data = 00 00 ...)" */
2645 CFStringRef blobFormat
= (localized
) ?
2646 SecCopyCertString(SEC_BLOB_KEY
) : SEC_BLOB_KEY
;
2647 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2648 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2649 blobFormat
, localizedBlobType
, blob
->length
, localizedQuanta
, hex
);
2651 CFRelease(blobFormat
);
2652 CFReleaseSafe(localizedQuanta
);
2653 CFReleaseSafe(localizedBlobType
);
2658 /* Return a string verbatim (unlocalized) from a DER field. */
2659 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2660 const DERItem
*string
, CFStringEncoding encoding
,
2661 bool printableOnly
) {
2662 /* Strip potential bogus trailing zero from printable strings. */
2663 DERSize length
= string
->length
;
2664 if (length
&& string
->data
[length
- 1] == 0) {
2665 /* Don't mess with the length of UTF16 strings though. */
2666 if (encoding
!= kCFStringEncodingUTF16
)
2669 /* A zero length string isn't considered printable. */
2670 if (!length
&& printableOnly
)
2673 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2674 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2675 passing false makes it treat it as native endian by default. */
2676 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2677 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2681 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2684 /* From rfc3280 - Appendix B. ASN.1 Notes
2686 CAs MUST force the serialNumber to be a non-negative integer, that
2687 is, the sign bit in the DER encoding of the INTEGER value MUST be
2688 zero - this can be done by adding a leading (leftmost) `00'H octet if
2689 necessary. This removes a potential ambiguity in mapping between a
2690 string of octets and an integer value.
2692 As noted in section 4.1.2.2, serial numbers can be expected to
2693 contain long integers. Certificate users MUST be able to handle
2694 serialNumber values up to 20 octets in length. Conformant CAs MUST
2695 NOT use serialNumber values longer than 20 octets.
2698 /* Return the given numeric data as a string: decimal up to 64 bits,
2701 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2702 const DERItem
*integer
) {
2704 CFIndex ix
, length
= integer
->length
;
2706 if (length
== 0 || length
> 8)
2707 return copyHexDescription(allocator
, integer
);
2709 for(ix
= 0; ix
< length
; ++ix
) {
2711 value
+= integer
->data
[ix
];
2714 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2717 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2718 DERTag tag
, const DERItem
*derThing
, bool printableOnly
, bool localized
) {
2719 if (!derThing
) { return NULL
; }
2723 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2724 case ASN1_PRINTABLE_STRING
:
2725 case ASN1_IA5_STRING
:
2726 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2727 case ASN1_UTF8_STRING
:
2728 case ASN1_GENERAL_STRING
:
2729 case ASN1_UNIVERSAL_STRING
:
2730 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2731 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2732 case ASN1_VIDEOTEX_STRING
: // 21
2733 case ASN1_VISIBLE_STRING
: // 26
2734 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2735 case ASN1_BMP_STRING
: // 30
2736 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2737 case ASN1_OCTET_STRING
:
2738 return printableOnly
? NULL
:
2739 copyBlobString(allocator
, SEC_BYTE_STRING_KEY
, SEC_BYTES_KEY
,
2740 derThing
, localized
);
2741 case ASN1_BIT_STRING
:
2742 return printableOnly
? NULL
:
2743 copyBlobString(allocator
, SEC_BIT_STRING_KEY
, SEC_BITS_KEY
,
2744 derThing
, localized
);
2745 case ASN1_CONSTR_SEQUENCE
:
2746 return printableOnly
? NULL
:
2747 copyBlobString(allocator
, SEC_SEQUENCE_KEY
, SEC_BYTES_KEY
,
2748 derThing
, localized
);
2749 case ASN1_CONSTR_SET
:
2750 return printableOnly
? NULL
:
2751 copyBlobString(allocator
, SEC_SET_KEY
, SEC_BYTES_KEY
,
2752 derThing
, localized
);
2753 case ASN1_OBJECT_ID
:
2754 return printableOnly
? NULL
: copyOidDescription(allocator
, derThing
, localized
);
2756 if (printableOnly
) {
2759 CFStringRef fmt
= (localized
) ?
2760 SecCopyCertString(SEC_NOT_DISPLAYED_KEY
) : SEC_NOT_DISPLAYED_KEY
;
2761 if (!fmt
) { return NULL
; }
2762 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
2763 (unsigned long)tag
, (unsigned long)derThing
->length
);
2770 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2771 const DERItem
*derThing
, bool printableOnly
, bool localized
) {
2772 DERDecodedInfo decoded
;
2775 drtn
= DERDecodeItem(derThing
, &decoded
);
2777 /* TODO: Perhaps put something in the label saying we couldn't parse
2779 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2781 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2782 &decoded
.content
, false, localized
);
2786 static void appendDERThingProperty(CFMutableArrayRef properties
,
2787 CFStringRef label
, CFStringRef localizedLabel
,
2788 const DERItem
*derThing
, bool localized
) {
2789 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2790 derThing
, false, localized
);
2792 appendProperty(properties
, kSecPropertyTypeString
, label
, localizedLabel
,
2795 CFReleaseSafe(value
);
2798 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2799 const DERItem
*rdnValue
, CFIndex rdnIX
,
2801 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2803 /* If there is more than one value pair we create a subsection for the
2804 second pair, and append things to the subsection for subsequent
2806 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2807 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2809 /* Since this is the second rdn pair for a given rdn, we setup a
2810 new subsection for this rdn. We remove the first property
2811 from the properties array and make it the first element in the
2812 subsection instead. */
2813 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2814 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2815 CFArrayAppendValue(rdn_props
, lastValue
);
2816 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2817 appendProperty(properties
, kSecPropertyTypeSection
, NULL
, NULL
,
2818 rdn_props
, localized
);
2819 properties
= rdn_props
;
2820 // rdn_props is now retained by the original properties array
2821 CFReleaseSafe(rdn_props
);
2823 /* Since this is the third or later rdn pair we have already
2824 created a subsection in the top level properties array. Instead
2825 of appending to that directly we append to the array inside the
2827 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2828 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2832 /* Finally we append the new rdn value to the property array. */
2834 SecDERItemCopyOIDDecimalRepresentation(CFGetAllocator(properties
),
2836 CFStringRef localizedLabel
= copyOidDescription(CFGetAllocator(properties
),
2837 rdnType
, localized
);
2838 appendDERThingProperty(properties
, label
, localizedLabel
,
2839 rdnValue
, localized
);
2840 CFReleaseSafe(label
);
2841 CFReleaseSafe(localizedLabel
);
2842 return errSecSuccess
;
2845 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2846 const DERItem
*rdnSetContent
, bool localized
) {
2847 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2848 &kCFTypeArrayCallBacks
);
2849 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2850 appendRDNProperty
, localized
);
2852 CFArrayRemoveAllValues(properties
);
2853 appendInvalidProperty(properties
, SEC_RDN_KEY
, rdnSetContent
,
2861 From rfc3739 - 3.1.2. Subject
2863 When parsing the subject here are some tips for a short name of the cert.
2864 Choice I: commonName
2865 Choice II: givenName
2866 Choice III: pseudonym
2868 The commonName attribute value SHALL, when present, contain a name
2869 of the subject. This MAY be in the subject's preferred
2870 presentation format, or a format preferred by the CA, or some
2871 other format. Pseudonyms, nicknames, and names with spelling
2872 other than defined by the registered name MAY be used. To
2873 understand the nature of the name presented in commonName,
2874 complying applications MAY have to examine present values of the
2875 givenName and surname attributes, or the pseudonym attribute.
2878 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2879 const DERItem
*x501NameContent
, bool localized
) {
2880 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2881 &kCFTypeArrayCallBacks
);
2882 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2883 appendRDNProperty
, localized
);
2885 CFArrayRemoveAllValues(properties
);
2886 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
,
2887 x501NameContent
, localized
);
2893 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2894 const DERItem
*x501Name
, bool localized
) {
2895 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2896 &kCFTypeArrayCallBacks
);
2897 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
, localized
);
2899 CFArrayRemoveAllValues(properties
);
2900 appendInvalidProperty(properties
, SEC_X501_NAME_KEY
,
2901 x501Name
, localized
);
2907 static void appendIntegerProperty(CFMutableArrayRef properties
,
2908 CFStringRef label
, const DERItem
*integer
, bool localized
) {
2909 CFStringRef string
= copyIntegerContentDescription(
2910 CFGetAllocator(properties
), integer
);
2911 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2916 static void appendBoolProperty(CFMutableArrayRef properties
,
2917 CFStringRef label
, bool boolean
, bool localized
) {
2918 CFStringRef key
= (boolean
) ? SEC_YES_KEY
: SEC_NO_KEY
;
2919 CFStringRef value
= (localized
) ? SecCopyCertString(key
) : key
;
2920 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2925 static void appendBooleanProperty(CFMutableArrayRef properties
,
2926 CFStringRef label
, const DERItem
*boolean
,
2927 bool defaultValue
, bool localized
) {
2929 DERReturn drtn
= DERParseBooleanWithDefault(boolean
, defaultValue
, &result
);
2931 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2932 appendInvalidProperty(properties
, label
, boolean
, localized
);
2934 appendBoolProperty(properties
, label
, result
, localized
);
2938 static void appendSerialNumberProperty(CFMutableArrayRef parent
, CFStringRef label
,
2939 DERItem
*serialNum
, bool localized
) {
2940 CFAllocatorRef allocator
= CFGetAllocator(parent
);
2941 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
2943 if (serialNum
->length
) {
2944 appendIntegerProperty(properties
, SEC_SERIAL_NUMBER_KEY
,
2945 serialNum
, localized
);
2946 appendProperty(parent
, kSecPropertyTypeSection
, label
, NULL
,
2947 properties
, localized
);
2950 CFReleaseNull(properties
);
2953 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2954 CFStringRef label
, const DERItem
*bitStringContent
,
2955 const CFStringRef
*names
, CFIndex namesCount
,
2957 DERSize len
= bitStringContent
->length
- 1;
2958 require_quiet(len
== 1 || len
== 2, badDER
);
2959 DERByte numUnusedBits
= bitStringContent
->data
[0];
2960 require_quiet(numUnusedBits
< 8, badDER
);
2961 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
2962 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
2963 uint_fast16_t value
= bitStringContent
->data
[1];
2966 value
= (value
<< 8) + bitStringContent
->data
[2];
2972 CFStringRef fmt
= (localized
) ?
2973 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
2974 CFStringRef string
= NULL
;
2975 for (ix
= 0; ix
< bits
; ++ix
) {
2979 CFStringCreateWithFormat(CFGetAllocator(properties
),
2980 NULL
, fmt
, string
, names
[ix
]);
2991 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
2992 string
? string
: CFSTR(""), localized
);
2993 CFReleaseSafe(string
);
2996 appendInvalidProperty(properties
, label
, bitStringContent
, localized
);
2999 static void appendBitStringNames(CFMutableArrayRef properties
,
3000 CFStringRef label
, const DERItem
*bitString
,
3001 const CFStringRef
*names
, CFIndex namesCount
,
3003 DERDecodedInfo bitStringContent
;
3004 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
3005 require_noerr_quiet(drtn
, badDER
);
3006 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
3007 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
3008 names
, namesCount
, localized
);
3011 appendInvalidProperty(properties
, label
, bitString
, localized
);
3014 static void appendKeyUsage(CFMutableArrayRef properties
,
3015 const DERItem
*extnValue
, bool localized
) {
3016 static const CFStringRef usageNames
[] = {
3017 SEC_DIGITAL_SIGNATURE_KEY
,
3018 SEC_NON_REPUDIATION_KEY
,
3019 SEC_KEY_ENCIPHERMENT_KEY
,
3020 SEC_DATA_ENCIPHERMENT_KEY
,
3021 SEC_KEY_AGREEMENT_KEY
,
3024 SEC_ENCIPHER_ONLY_KEY
,
3025 SEC_DECIPHER_ONLY_KEY
3027 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3028 usageNames
, array_size(usageNames
), localized
);
3031 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
3032 const DERItem
*extnValue
, bool localized
) {
3033 DERPrivateKeyUsagePeriod pkup
;
3034 DERReturn drtn
= DERParseSequence(extnValue
,
3035 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
3036 &pkup
, sizeof(pkup
));
3037 require_noerr_quiet(drtn
, badDER
);
3038 if (pkup
.notBefore
.length
) {
3039 appendDateContentProperty(properties
, SEC_NOT_VALID_BEFORE_KEY
,
3040 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
, localized
);
3042 if (pkup
.notAfter
.length
) {
3043 appendDateContentProperty(properties
, SEC_NOT_VALID_AFTER_KEY
,
3044 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
, localized
);
3048 appendInvalidProperty(properties
, SEC_PRIVATE_KU_PERIOD_KEY
,
3049 extnValue
, localized
);
3052 static void appendStringContentProperty(CFMutableArrayRef properties
,
3053 CFStringRef label
, const DERItem
*stringContent
,
3054 CFStringEncoding encoding
, bool localized
) {
3055 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
3056 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
3058 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3062 appendInvalidProperty(properties
, label
, stringContent
, localized
);
3067 OtherName ::= SEQUENCE {
3068 type-id OBJECT IDENTIFIER,
3069 value [0] EXPLICIT ANY DEFINED BY type-id }
3071 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
3072 const DERItem
*otherNameContent
, bool localized
) {
3074 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
3075 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
3077 require_noerr_quiet(drtn
, badDER
);
3078 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3080 SecDERItemCopyOIDDecimalRepresentation(allocator
, &on
.typeIdentifier
);
3081 CFStringRef localizedLabel
=
3082 copyOidDescription(allocator
, &on
.typeIdentifier
, localized
);
3083 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
,
3086 appendProperty(properties
, kSecPropertyTypeString
, label
,
3087 localizedLabel
, value_string
, localized
);
3089 appendUnparsedProperty(properties
, label
, localizedLabel
,
3090 &on
.value
, localized
);
3092 CFReleaseSafe(value_string
);
3093 CFReleaseSafe(label
);
3094 CFReleaseSafe(localizedLabel
);
3097 appendInvalidProperty(properties
, SEC_OTHER_NAME_KEY
,
3098 otherNameContent
, localized
);
3102 GeneralName ::= CHOICE {
3103 otherName [0] OtherName,
3104 rfc822Name [1] IA5String,
3105 dNSName [2] IA5String,
3106 x400Address [3] ORAddress,
3107 directoryName [4] Name,
3108 ediPartyName [5] EDIPartyName,
3109 uniformResourceIdentifier [6] IA5String,
3110 iPAddress [7] OCTET STRING,
3111 registeredID [8] OBJECT IDENTIFIER}
3113 EDIPartyName ::= SEQUENCE {
3114 nameAssigner [0] DirectoryString OPTIONAL,
3115 partyName [1] DirectoryString }
3117 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
3118 DERTag tag
, const DERItem
*generalName
, bool localized
) {
3120 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
3121 appendOtherNameContentProperty(properties
, generalName
, localized
);
3123 case ASN1_CONTEXT_SPECIFIC
| 1:
3125 appendStringContentProperty(properties
, SEC_EMAIL_ADDRESS_KEY
,
3126 generalName
, kCFStringEncodingASCII
, localized
);
3128 case ASN1_CONTEXT_SPECIFIC
| 2:
3130 appendStringContentProperty(properties
, SEC_DNS_NAME_KEY
, generalName
,
3131 kCFStringEncodingASCII
, localized
);
3133 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
3134 appendUnparsedProperty(properties
, SEC_X400_ADDRESS_KEY
, NULL
,
3135 generalName
, localized
);
3137 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
3139 CFArrayRef directory_plist
=
3140 createPropertiesForX501Name(CFGetAllocator(properties
),
3141 generalName
, localized
);
3142 appendProperty(properties
, kSecPropertyTypeSection
,
3143 SEC_DIRECTORY_NAME_KEY
, NULL
, directory_plist
, localized
);
3144 CFRelease(directory_plist
);
3147 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
3148 appendUnparsedProperty(properties
, SEC_EDI_PARTY_NAME_KEY
, NULL
,
3149 generalName
, localized
);
3151 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
3152 /* Technically I don't think this is valid, but there are certs out
3153 in the wild that use a constructed IA5String. In particular the
3154 VeriSign Time Stamping Authority CA.cer does this. */
3155 appendURLProperty(properties
, SEC_URI_KEY
, generalName
, localized
);
3157 case ASN1_CONTEXT_SPECIFIC
| 6:
3158 appendURLContentProperty(properties
, SEC_URI_KEY
, generalName
, localized
);
3160 case ASN1_CONTEXT_SPECIFIC
| 7:
3161 appendIPAddressContentProperty(properties
, SEC_IP_ADDRESS_KEY
,
3162 generalName
, localized
);
3164 case ASN1_CONTEXT_SPECIFIC
| 8:
3165 appendOIDProperty(properties
, SEC_REGISTERED_ID_KEY
, NULL
,
3166 generalName
, localized
);
3177 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
3178 const DERItem
*generalName
, bool localized
) {
3179 DERDecodedInfo generalNameContent
;
3180 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
3181 require_noerr_quiet(drtn
, badDER
);
3182 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
3183 &generalNameContent
.content
, localized
))
3186 appendInvalidProperty(properties
, SEC_GENERAL_NAME_KEY
,
3187 generalName
, localized
);
3192 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
3194 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
3195 const DERItem
*generalNamesContent
, bool localized
) {
3197 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
3198 require_noerr_quiet(drtn
, badDER
);
3199 DERDecodedInfo generalNameContent
;
3200 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
3202 if (!appendGeneralNameContentProperty(properties
,
3203 generalNameContent
.tag
, &generalNameContent
.content
, localized
)) {
3207 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3210 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
3211 generalNamesContent
, localized
);
3214 static void appendGeneralNames(CFMutableArrayRef properties
,
3215 const DERItem
*generalNames
, bool localized
) {
3216 DERDecodedInfo generalNamesContent
;
3217 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
3218 require_noerr_quiet(drtn
, badDER
);
3219 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
3221 appendGeneralNamesContent(properties
, &generalNamesContent
.content
,
3225 appendInvalidProperty(properties
, SEC_GENERAL_NAMES_KEY
,
3226 generalNames
, localized
);
3230 BasicConstraints ::= SEQUENCE {
3231 cA BOOLEAN DEFAULT FALSE,
3232 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
3234 static void appendBasicConstraints(CFMutableArrayRef properties
,
3235 const DERItem
*extnValue
, bool localized
) {
3236 DERBasicConstraints basicConstraints
;
3237 DERReturn drtn
= DERParseSequence(extnValue
,
3238 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
3239 &basicConstraints
, sizeof(basicConstraints
));
3240 require_noerr_quiet(drtn
, badDER
);
3242 appendBooleanProperty(properties
, SEC_CERT_AUTHORITY_KEY
,
3243 &basicConstraints
.cA
, false, localized
);
3245 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
3246 appendIntegerProperty(properties
, SEC_PATH_LEN_CONSTRAINT_KEY
,
3247 &basicConstraints
.pathLenConstraint
, localized
);
3251 appendInvalidProperty(properties
, SEC_BASIC_CONSTRAINTS_KEY
,
3252 extnValue
, localized
);
3256 * id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
3258 * NameConstraints ::= SEQUENCE {
3259 * permittedSubtrees [0] GeneralSubtrees OPTIONAL,
3260 * excludedSubtrees [1] GeneralSubtrees OPTIONAL }
3262 * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
3264 * GeneralSubtree ::= SEQUENCE {
3266 * minimum [0] BaseDistance DEFAULT 0,
3267 * maximum [1] BaseDistance OPTIONAL }
3269 * BaseDistance ::= INTEGER (0..MAX)
3271 static void appendNameConstraints(CFMutableArrayRef properties
,
3272 const DERItem
*extnValue
, bool localized
) {
3273 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3274 DERNameConstraints nc
;
3276 drtn
= DERParseSequence(extnValue
,
3277 DERNumNameConstraintsItemSpecs
,
3278 DERNameConstraintsItemSpecs
,
3280 require_noerr_quiet(drtn
, badDER
);
3281 if (nc
.permittedSubtrees
.length
) {
3283 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.permittedSubtrees
, &gsSeq
), badDER
);
3284 DERDecodedInfo gsContent
;
3285 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3286 DERGeneralSubtree derGS
;
3287 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3288 drtn
= DERParseSequenceContent(&gsContent
.content
,
3289 DERNumGeneralSubtreeItemSpecs
,
3290 DERGeneralSubtreeItemSpecs
,
3291 &derGS
, sizeof(derGS
));
3292 require_noerr_quiet(drtn
, badDER
);
3293 if (derGS
.minimum
.length
) {
3294 appendIntegerProperty(properties
, SEC_PERMITTED_MINIMUM_KEY
,
3295 &derGS
.minimum
, localized
);
3297 if (derGS
.maximum
.length
) {
3298 appendIntegerProperty(properties
, SEC_PERMITTED_MAXIMUM_KEY
,
3299 &derGS
.maximum
, localized
);
3301 if (derGS
.generalName
.length
) {
3302 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3303 &kCFTypeArrayCallBacks
);
3304 appendProperty(properties
, kSecPropertyTypeSection
,
3305 SEC_PERMITTED_NAME_KEY
, NULL
, base
, localized
);
3306 appendGeneralNameProperty(base
, &derGS
.generalName
, localized
);
3310 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3312 if (nc
.excludedSubtrees
.length
) {
3314 require_noerr_quiet(DERDecodeSeqContentInit(&nc
.excludedSubtrees
, &gsSeq
), badDER
);
3315 DERDecodedInfo gsContent
;
3316 while ((drtn
= DERDecodeSeqNext(&gsSeq
, &gsContent
)) == DR_Success
) {
3317 DERGeneralSubtree derGS
;
3318 require_quiet(gsContent
.tag
==ASN1_CONSTR_SEQUENCE
, badDER
);
3319 drtn
= DERParseSequenceContent(&gsContent
.content
,
3320 DERNumGeneralSubtreeItemSpecs
,
3321 DERGeneralSubtreeItemSpecs
,
3322 &derGS
, sizeof(derGS
));
3323 require_noerr_quiet(drtn
, badDER
);
3324 if (derGS
.minimum
.length
) {
3325 appendIntegerProperty(properties
, SEC_EXCLUDED_MINIMUM_KEY
,
3326 &derGS
.minimum
, localized
);
3328 if (derGS
.maximum
.length
) {
3329 appendIntegerProperty(properties
, SEC_EXCLUDED_MAXIMUM_KEY
,
3330 &derGS
.maximum
, localized
);
3332 if (derGS
.generalName
.length
) {
3333 CFMutableArrayRef base
= CFArrayCreateMutable(allocator
, 0,
3334 &kCFTypeArrayCallBacks
);
3335 appendProperty(properties
, kSecPropertyTypeSection
,
3336 SEC_EXCLUDED_NAME_KEY
, NULL
, base
, localized
);
3337 appendGeneralNameProperty(base
, &derGS
.generalName
, localized
);
3341 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3346 appendInvalidProperty(properties
, SEC_NAME_CONSTRAINTS_KEY
,
3347 extnValue
, localized
);
3351 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
3353 DistributionPoint ::= SEQUENCE {
3354 distributionPoint [0] DistributionPointName OPTIONAL,
3355 reasons [1] ReasonFlags OPTIONAL,
3356 cRLIssuer [2] GeneralNames OPTIONAL }
3358 DistributionPointName ::= CHOICE {
3359 fullName [0] GeneralNames,
3360 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
3362 ReasonFlags ::= BIT STRING {
3366 affiliationChanged (3),
3368 cessationOfOperation (5),
3369 certificateHold (6),
3370 privilegeWithdrawn (7),
3373 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
3374 const DERItem
*extnValue
, bool localized
) {
3375 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3378 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
3379 require_noerr_quiet(drtn
, badDER
);
3380 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3381 DERDecodedInfo dpSeqContent
;
3382 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
3383 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3384 DERDistributionPoint dp
;
3385 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
3386 DERNumDistributionPointItemSpecs
,
3387 DERDistributionPointItemSpecs
,
3389 require_noerr_quiet(drtn
, badDER
);
3390 if (dp
.distributionPoint
.length
) {
3391 DERDecodedInfo distributionPointName
;
3392 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
3393 require_noerr_quiet(drtn
, badDER
);
3394 if (distributionPointName
.tag
==
3395 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
3397 appendGeneralNamesContent(properties
,
3398 &distributionPointName
.content
, localized
);
3399 } else if (distributionPointName
.tag
==
3400 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
3401 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
3402 &dp
.reasons
, localized
);
3403 appendProperty(properties
, kSecPropertyTypeSection
,
3404 SEC_NAME_REL_CRL_ISSUER_KEY
, NULL
, rdn_props
, localized
);
3405 CFRelease(rdn_props
);
3410 if (dp
.reasons
.length
) {
3411 static const CFStringRef reasonNames
[] = {
3413 SEC_KEY_COMPROMISE_KEY
,
3414 SEC_CA_COMPROMISE_KEY
,
3415 SEC_AFFILIATION_CHANGED_KEY
,
3417 SEC_CESSATION_OF_OPER_KEY
,
3418 SEC_CERTIFICATE_HOLD_KEY
,
3419 SEC_PRIV_WITHDRAWN_KEY
,
3420 SEC_AA_COMPROMISE_KEY
3422 appendBitStringContentNames(properties
, SEC_REASONS_KEY
,
3424 reasonNames
, array_size(reasonNames
), localized
);
3426 if (dp
.cRLIssuer
.length
) {
3427 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
3428 &kCFTypeArrayCallBacks
);
3429 appendProperty(properties
, kSecPropertyTypeSection
,
3430 SEC_CRL_ISSUER_KEY
, NULL
, crlIssuer
, localized
);
3431 CFRelease(crlIssuer
);
3432 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
, localized
);
3435 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3438 appendInvalidProperty(properties
, SEC_CRL_DISTR_POINTS_KEY
,
3439 extnValue
, localized
);
3443 Decode a sequence of integers into a comma separated list of ints.
3445 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
3446 CFStringRef label
, const DERItem
*intSequenceContent
,
3448 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3450 CFStringRef fmt
= NULL
, value
= NULL
, intDesc
= NULL
, v
= NULL
;
3451 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
3452 require_noerr_quiet(drtn
, badDER
);
3453 DERDecodedInfo intContent
;
3455 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
3456 require_quiet(fmt
, badDER
);
3457 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
)) == DR_Success
) {
3458 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
3459 intDesc
= copyIntegerContentDescription(
3460 allocator
, &intContent
.content
);
3461 require_quiet(intDesc
, badDER
);
3463 v
= CFStringCreateWithFormat(allocator
, NULL
, fmt
, value
, intDesc
);
3464 CFReleaseNull(value
);
3465 require_quiet(v
, badDER
);
3468 value
= CFStringCreateMutableCopy(allocator
, 0, intDesc
);
3469 require_quiet(value
, badDER
);
3471 CFReleaseNull(intDesc
);
3474 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3476 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3481 /* DROPTHOUGH if !value. */
3484 CFReleaseNull(intDesc
);
3485 CFReleaseNull(value
);
3486 appendInvalidProperty(properties
, label
, intSequenceContent
, localized
);
3489 static void appendCertificatePolicies(CFMutableArrayRef properties
,
3490 const DERItem
*extnValue
, bool localized
) {
3491 CFAllocatorRef allocator
= CFGetAllocator(properties
);
3492 CFStringRef piLabel
= NULL
, piFmt
= NULL
, lpiLabel
= NULL
;
3493 CFStringRef pqLabel
= NULL
, pqFmt
= NULL
, lpqLabel
= NULL
;
3496 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
3497 require_noerr_quiet(drtn
, badDER
);
3498 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3499 DERDecodedInfo piContent
;
3501 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
3502 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3503 DERPolicyInformation pi
;
3504 drtn
= DERParseSequenceContent(&piContent
.content
,
3505 DERNumPolicyInformationItemSpecs
,
3506 DERPolicyInformationItemSpecs
,
3508 require_noerr_quiet(drtn
, badDER
);
3509 piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3510 SEC_POLICY_IDENTIFIER_KEY
, pin
);
3511 require_quiet(piLabel
, badDER
);
3512 piFmt
= (localized
) ?
3513 SecCopyCertString(SEC_POLICY_IDENTIFIER_KEY
) : SEC_POLICY_IDENTIFIER_KEY
;
3514 require_quiet(piFmt
, badDER
);
3515 lpiLabel
= CFStringCreateWithFormat(allocator
, NULL
, piFmt
, pin
++);
3516 require_quiet(lpiLabel
, badDER
);
3517 CFReleaseNull(piFmt
);
3518 appendOIDProperty(properties
, piLabel
, lpiLabel
,
3519 &pi
.policyIdentifier
, localized
);
3520 CFReleaseNull(piLabel
);
3521 CFReleaseNull(lpiLabel
);
3522 if (pi
.policyQualifiers
.length
== 0)
3526 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
3527 require_noerr_quiet(drtn
, badDER
);
3528 DERDecodedInfo pqContent
;
3530 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
3531 DERPolicyQualifierInfo pqi
;
3532 drtn
= DERParseSequenceContent(&pqContent
.content
,
3533 DERNumPolicyQualifierInfoItemSpecs
,
3534 DERPolicyQualifierInfoItemSpecs
,
3536 require_noerr_quiet(drtn
, badDER
);
3537 DERDecodedInfo qualifierContent
;
3538 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
3539 require_noerr_quiet(drtn
, badDER
);
3540 pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
3541 SEC_POLICY_QUALIFIER_KEY
, pqn
);
3542 require_quiet(pqLabel
, badDER
);
3543 pqFmt
= (localized
) ?
3544 SecCopyCertString(SEC_POLICY_QUALIFIER_KEY
) : SEC_POLICY_QUALIFIER_KEY
;
3545 require_quiet(pqFmt
, badDER
);
3546 lpqLabel
= CFStringCreateWithFormat(allocator
, NULL
, pqFmt
, pqn
++);
3547 require_quiet(lpqLabel
, badDER
);
3548 CFReleaseNull(pqFmt
);
3549 appendOIDProperty(properties
, pqLabel
, lpqLabel
,
3550 &pqi
.policyQualifierID
, localized
);
3551 CFReleaseNull(pqLabel
);
3552 CFReleaseNull(lpqLabel
);
3553 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
3554 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
3555 appendURLContentProperty(properties
, SEC_CPS_URI_KEY
,
3556 &qualifierContent
.content
, localized
);
3557 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
3558 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3560 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
3561 DERNumUserNoticeItemSpecs
,
3562 DERUserNoticeItemSpecs
,
3564 require_noerr_quiet(drtn
, badDER
);
3565 if (un
.noticeRef
.length
) {
3566 DERNoticeReference nr
;
3567 drtn
= DERParseSequenceContent(&un
.noticeRef
,
3568 DERNumNoticeReferenceItemSpecs
,
3569 DERNoticeReferenceItemSpecs
,
3571 require_noerr_quiet(drtn
, badDER
);
3572 appendDERThingProperty(properties
,
3573 SEC_ORGANIZATION_KEY
, NULL
,
3574 &nr
.organization
, localized
);
3575 appendIntegerSequenceContent(properties
,
3576 SEC_NOTICE_NUMBERS_KEY
, &nr
.noticeNumbers
, localized
);
3578 if (un
.explicitText
.length
) {
3579 appendDERThingProperty(properties
, SEC_EXPLICIT_TEXT_KEY
,
3580 NULL
, &un
.explicitText
, localized
);
3583 appendUnparsedProperty(properties
, SEC_QUALIFIER_KEY
, NULL
,
3584 &pqi
.qualifier
, localized
);
3587 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3589 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3592 CFReleaseNull(piFmt
);
3593 CFReleaseNull(piLabel
);
3594 CFReleaseNull(lpiLabel
);
3595 CFReleaseNull(pqFmt
);
3596 CFReleaseNull(pqLabel
);
3597 CFReleaseNull(lpqLabel
);
3598 appendInvalidProperty(properties
, SEC_CERT_POLICIES_KEY
,
3599 extnValue
, localized
);
3602 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
3603 const DERItem
*extnValue
, bool localized
) {
3605 DERDecodedInfo keyIdentifier
;
3606 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
3607 require_noerr_quiet(drtn
, badDER
);
3608 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
3609 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3610 &keyIdentifier
.content
, localized
);
3614 appendInvalidProperty(properties
, SEC_SUBJ_KEY_ID_KEY
,
3615 extnValue
, localized
);
3619 AuthorityKeyIdentifier ::= SEQUENCE {
3620 keyIdentifier [0] KeyIdentifier OPTIONAL,
3621 authorityCertIssuer [1] GeneralNames OPTIONAL,
3622 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
3623 -- authorityCertIssuer and authorityCertSerialNumber MUST both
3624 -- be present or both be absent
3626 KeyIdentifier ::= OCTET STRING
3628 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
3629 const DERItem
*extnValue
, bool localized
) {
3630 DERAuthorityKeyIdentifier akid
;
3632 drtn
= DERParseSequence(extnValue
,
3633 DERNumAuthorityKeyIdentifierItemSpecs
,
3634 DERAuthorityKeyIdentifierItemSpecs
,
3635 &akid
, sizeof(akid
));
3636 require_noerr_quiet(drtn
, badDER
);
3637 if (akid
.keyIdentifier
.length
) {
3638 appendDataProperty(properties
, SEC_KEY_IDENTIFIER_KEY
, NULL
,
3639 &akid
.keyIdentifier
, localized
);
3641 if (akid
.authorityCertIssuer
.length
||
3642 akid
.authorityCertSerialNumber
.length
) {
3643 require_quiet(akid
.authorityCertIssuer
.length
&&
3644 akid
.authorityCertSerialNumber
.length
, badDER
);
3645 /* Perhaps put in a subsection called Authority Certificate Issuer. */
3646 appendGeneralNamesContent(properties
,
3647 &akid
.authorityCertIssuer
, localized
);
3648 appendIntegerProperty(properties
, SEC_AUTH_CERT_SERIAL_KEY
,
3649 &akid
.authorityCertSerialNumber
, localized
);
3654 appendInvalidProperty(properties
, SEC_AUTHORITY_KEY_ID_KEY
,
3655 extnValue
, localized
);
3659 PolicyConstraints ::= SEQUENCE {
3660 requireExplicitPolicy [0] SkipCerts OPTIONAL,
3661 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
3663 SkipCerts ::= INTEGER (0..MAX)
3665 static void appendPolicyConstraints(CFMutableArrayRef properties
,
3666 const DERItem
*extnValue
, bool localized
) {
3667 DERPolicyConstraints pc
;
3669 drtn
= DERParseSequence(extnValue
,
3670 DERNumPolicyConstraintsItemSpecs
,
3671 DERPolicyConstraintsItemSpecs
,
3673 require_noerr_quiet(drtn
, badDER
);
3674 if (pc
.requireExplicitPolicy
.length
) {
3675 appendIntegerProperty(properties
, SEC_REQUIRE_EXPL_POLICY_KEY
,
3676 &pc
.requireExplicitPolicy
, localized
);
3678 if (pc
.inhibitPolicyMapping
.length
) {
3679 appendIntegerProperty(properties
, SEC_INHIBIT_POLICY_MAP_KEY
,
3680 &pc
.inhibitPolicyMapping
, localized
);
3686 appendInvalidProperty(properties
, SEC_POLICY_CONSTRAINTS_KEY
,
3687 extnValue
, localized
);
3691 extendedKeyUsage EXTENSION ::= {
3692 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
3693 IDENTIFIED BY id-ce-extKeyUsage }
3695 KeyPurposeId ::= OBJECT IDENTIFIER
3697 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
3698 const DERItem
*extnValue
, bool localized
) {
3701 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
3702 require_noerr_quiet(drtn
, badDER
);
3703 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3704 DERDecodedInfo currDecoded
;
3705 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3706 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
3707 appendOIDProperty(properties
, SEC_PURPOSE_KEY
, NULL
,
3708 &currDecoded
.content
, localized
);
3710 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3713 appendInvalidProperty(properties
, SEC_EXTENDED_KEY_USAGE_KEY
,
3714 extnValue
, localized
);
3718 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3720 AuthorityInfoAccessSyntax ::=
3721 SEQUENCE SIZE (1..MAX) OF AccessDescription
3723 AccessDescription ::= SEQUENCE {
3724 accessMethod OBJECT IDENTIFIER,
3725 accessLocation GeneralName }
3727 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3729 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3731 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3733 static void appendInfoAccess(CFMutableArrayRef properties
,
3734 const DERItem
*extnValue
, bool localized
) {
3737 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3738 require_noerr_quiet(drtn
, badDER
);
3739 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3740 DERDecodedInfo adContent
;
3741 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3742 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3743 DERAccessDescription ad
;
3744 drtn
= DERParseSequenceContent(&adContent
.content
,
3745 DERNumAccessDescriptionItemSpecs
,
3746 DERAccessDescriptionItemSpecs
,
3748 require_noerr_quiet(drtn
, badDER
);
3749 appendOIDProperty(properties
, SEC_ACCESS_METHOD_KEY
, NULL
,
3750 &ad
.accessMethod
, localized
);
3751 //TODO: Do something with SEC_ACCESS_LOCATION_KEY
3752 appendGeneralNameProperty(properties
, &ad
.accessLocation
, localized
);
3754 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3757 appendInvalidProperty(properties
, SEC_AUTH_INFO_ACCESS_KEY
,
3758 extnValue
, localized
);
3761 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3762 const DERItem
*extnValue
, bool localized
) {
3763 static const CFStringRef certTypes
[] = {
3767 SEC_OBJECT_SIGNING_KEY
,
3771 SEC_OBJECT_SIGNING_CA_KEY
3773 appendBitStringNames(properties
, SEC_USAGE_KEY
, extnValue
,
3774 certTypes
, array_size(certTypes
), localized
);
3777 static bool appendPrintableDERSequence(CFMutableArrayRef properties
,
3778 CFStringRef label
, const DERItem
*sequence
, bool localized
) {
3781 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3782 require_noerr_quiet(drtn
, badSequence
);
3783 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3784 DERDecodedInfo currDecoded
;
3785 bool appendedSomething
= false;
3786 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3787 switch (currDecoded
.tag
)
3790 case ASN1_SEQUENCE
: // 16
3791 case ASN1_SET
: // 17
3792 // skip constructed object lengths
3795 case ASN1_UTF8_STRING
: // 12
3796 case ASN1_NUMERIC_STRING
: // 18
3797 case ASN1_PRINTABLE_STRING
: // 19
3798 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3799 case ASN1_VIDEOTEX_STRING
: // 21
3800 case ASN1_IA5_STRING
: // 22
3801 case ASN1_GRAPHIC_STRING
: // 25
3802 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3803 case ASN1_GENERAL_STRING
: // 27
3804 case ASN1_UNIVERSAL_STRING
: // 28
3806 CFStringRef string
=
3807 copyDERThingContentDescription(CFGetAllocator(properties
),
3808 currDecoded
.tag
, &currDecoded
.content
, false, localized
);
3809 require_quiet(string
, badSequence
);
3811 appendProperty(properties
, kSecPropertyTypeString
, label
, NULL
,
3813 CFReleaseNull(string
);
3814 appendedSomething
= true;
3821 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3822 return appendedSomething
;
3827 static void appendExtension(CFMutableArrayRef parent
,
3828 const SecCertificateExtension
*extn
,
3830 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3831 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3832 &kCFTypeArrayCallBacks
);
3834 *extnID
= &extn
->extnID
,
3835 *extnValue
= &extn
->extnValue
;
3836 CFStringRef label
= NULL
;
3837 CFStringRef localizedLabel
= NULL
;
3839 appendBoolProperty(properties
, SEC_CRITICAL_KEY
, extn
->critical
, localized
);
3840 require_quiet(extnID
, xit
);
3842 bool handled
= true;
3843 /* Extensions that we know how to handle ourselves... */
3844 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3845 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3847 switch (extnID
->data
[extnID
->length
- 1]) {
3848 case 14: /* SubjectKeyIdentifier id-ce 14 */
3849 appendSubjectKeyIdentifier(properties
, extnValue
, localized
);
3851 case 15: /* KeyUsage id-ce 15 */
3852 appendKeyUsage(properties
, extnValue
, localized
);
3854 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3855 appendPrivateKeyUsagePeriod(properties
, extnValue
, localized
);
3857 case 17: /* SubjectAltName id-ce 17 */
3858 case 18: /* IssuerAltName id-ce 18 */
3859 appendGeneralNames(properties
, extnValue
, localized
);
3861 case 19: /* BasicConstraints id-ce 19 */
3862 appendBasicConstraints(properties
, extnValue
, localized
);
3864 case 30: /* NameConstraints id-ce 30 */
3865 appendNameConstraints(properties
, extnValue
, localized
);
3867 case 31: /* CRLDistributionPoints id-ce 31 */
3868 appendCrlDistributionPoints(properties
, extnValue
, localized
);
3870 case 32: /* CertificatePolicies id-ce 32 */
3871 appendCertificatePolicies(properties
, extnValue
, localized
);
3873 case 33: /* PolicyMappings id-ce 33 */
3876 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3877 appendAuthorityKeyIdentifier(properties
, extnValue
, localized
);
3879 case 36: /* PolicyConstraints id-ce 36 */
3880 appendPolicyConstraints(properties
, extnValue
, localized
);
3882 case 37: /* ExtKeyUsage id-ce 37 */
3883 appendExtendedKeyUsage(properties
, extnValue
, localized
);
3885 case 46: /* FreshestCRL id-ce 46 */
3888 case 54: /* InhibitAnyPolicy id-ce 54 */
3895 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3896 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3898 switch (extnID
->data
[extnID
->length
- 1]) {
3899 case 1: /* AuthorityInfoAccess id-pe 1 */
3900 appendInfoAccess(properties
, extnValue
, localized
);
3902 case 3: /* QCStatements id-pe 3 */
3905 case 11: /* SubjectInfoAccess id-pe 11 */
3906 appendInfoAccess(properties
, extnValue
, localized
);
3912 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3913 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3914 appendNetscapeCertType(properties
, extnValue
, localized
);
3920 /* Try to parse and display printable string(s). */
3921 if (appendPrintableDERSequence(properties
, SEC_DATA_KEY
, extnValue
, localized
)) {
3922 /* Nothing to do here appendPrintableDERSequence did the work. */
3924 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3925 appendUnparsedProperty(properties
, SEC_DATA_KEY
, NULL
, extnValue
, localized
);
3928 label
= SecDERItemCopyOIDDecimalRepresentation(allocator
, extnID
);
3929 localizedLabel
= copyOidDescription(allocator
, extnID
, localized
);
3930 appendProperty(parent
, kSecPropertyTypeSection
, label
, localizedLabel
,
3931 properties
, localized
);
3933 CFReleaseSafe(localizedLabel
);
3934 CFReleaseSafe(label
);
3935 CFReleaseSafe(properties
);
3938 /* Different types of summary types from least desired to most desired. */
3941 kSummaryTypePrintable
,
3942 kSummaryTypeOrganizationName
,
3943 kSummaryTypeOrganizationalUnitName
,
3944 kSummaryTypeCommonName
,
3948 enum SummaryType type
;
3949 CFStringRef summary
;
3950 CFStringRef description
;
3953 static OSStatus
obtainSummaryFromX501Name(void *context
,
3954 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
,
3956 struct Summary
*summary
= (struct Summary
*)context
;
3957 enum SummaryType stype
= kSummaryTypeNone
;
3958 CFStringRef string
= NULL
;
3959 if (DEROidCompare(type
, &oidCommonName
)) {
3960 stype
= kSummaryTypeCommonName
;
3961 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
3962 stype
= kSummaryTypeOrganizationalUnitName
;
3963 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
3964 stype
= kSummaryTypeOrganizationName
;
3965 } else if (DEROidCompare(type
, &oidDescription
)) {
3966 string
= copyDERThingDescription(kCFAllocatorDefault
, value
,
3969 if (summary
->description
) {
3970 CFStringRef fmt
= (localized
) ?
3971 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
3972 CFStringRef newDescription
= CFStringCreateWithFormat(kCFAllocatorDefault
,
3973 NULL
, fmt
, string
, summary
->description
);
3975 CFRelease(summary
->description
);
3976 summary
->description
= newDescription
;
3978 summary
->description
= string
;
3981 stype
= kSummaryTypePrintable
;
3984 stype
= kSummaryTypePrintable
;
3987 /* Build a string with all instances of the most desired
3988 component type in reverse order encountered comma separated list,
3989 The order of desirability is defined by enum SummaryType. */
3990 if (summary
->type
<= stype
) {
3992 string
= copyDERThingDescription(kCFAllocatorDefault
, value
,
3996 if (summary
->type
== stype
) {
3997 CFStringRef fmt
= (localized
) ?
3998 SecCopyCertString(SEC_STRING_LIST_KEY
) : SEC_STRING_LIST_KEY
;
3999 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
,
4000 NULL
, fmt
, string
, summary
->summary
);
4003 string
= newSummary
;
4005 summary
->type
= stype
;
4007 CFReleaseSafe(summary
->summary
);
4008 summary
->summary
= string
;
4011 CFReleaseSafe(string
);
4014 return errSecSuccess
;
4017 CFStringRef
SecCertificateCopySubjectSummary(SecCertificateRef certificate
) {
4018 struct Summary summary
= {};
4019 OSStatus status
= parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
, true);
4020 if (status
!= errSecSuccess
) {
4023 /* If we found a description and a common name we change the summary to
4024 CommonName (Description). */
4025 if (summary
.description
) {
4026 if (summary
.type
== kSummaryTypeCommonName
) {
4027 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
4028 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
4030 CFRelease(summary
.summary
);
4031 summary
.summary
= newSummary
;
4033 CFRelease(summary
.description
);
4036 if (!summary
.summary
) {
4037 /* If we didn't find a suitable printable string in the subject at all, we try
4038 the first email address in the certificate instead. */
4039 CFArrayRef names
= SecCertificateCopyRFC822Names(certificate
);
4041 /* If we didn't find any email addresses in the certificate, we try finding
4042 a DNS name instead. */
4043 names
= SecCertificateCopyDNSNames(certificate
);
4046 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
4047 CFRetain(summary
.summary
);
4052 return summary
.summary
;
4055 CFStringRef
SecCertificateCopyIssuerSummary(SecCertificateRef certificate
) {
4056 struct Summary summary
= {};
4057 OSStatus status
= parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
, true);
4058 if (status
!= errSecSuccess
) {
4061 /* If we found a description and a common name we change the summary to
4062 CommonName (Description). */
4063 if (summary
.description
) {
4064 if (summary
.type
== kSummaryTypeCommonName
) {
4065 CFStringRef fmt
= SecCopyCertString(SEC_COMMON_NAME_DESC_KEY
);
4066 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, fmt
, summary
.summary
, summary
.description
);
4068 CFRelease(summary
.summary
);
4069 summary
.summary
= newSummary
;
4071 CFRelease(summary
.description
);
4074 return summary
.summary
;
4077 /* Return the earliest date on which all certificates in this chain are still
4079 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
4080 SecCertificateRef certificate
) {
4081 CFAbsoluteTime earliest
= certificate
->_notAfter
;
4083 while (certificate
->_parent
) {
4084 certificate
= certificate
->_parent
;
4085 if (earliest
> certificate
->_notAfter
)
4086 earliest
= certificate
->_notAfter
;
4093 /* Return the latest date on which all certificates in this chain will be
4095 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
4096 SecCertificateRef certificate
) {
4097 CFAbsoluteTime latest
= certificate
->_notBefore
;
4099 while (certificate
->_parent
) {
4100 certificate
= certificate
->_parent
;
4101 if (latest
< certificate
->_notBefore
)
4102 latest
= certificate
->_notBefore
;
4109 bool SecCertificateIsValid(SecCertificateRef certificate
,
4110 CFAbsoluteTime verifyTime
) {
4111 return certificate
&& certificate
->_notBefore
<= verifyTime
&&
4112 verifyTime
<= certificate
->_notAfter
;
4115 CFIndex
SecCertificateVersion(SecCertificateRef certificate
) {
4116 return certificate
->_version
+ 1;
4119 CFAbsoluteTime
SecCertificateNotValidBefore(SecCertificateRef certificate
) {
4120 return certificate
->_notBefore
;
4123 CFAbsoluteTime
SecCertificateNotValidAfter(SecCertificateRef certificate
) {
4124 return certificate
->_notAfter
;
4127 CFMutableArrayRef
SecCertificateCopySummaryProperties(
4128 SecCertificateRef certificate
, CFAbsoluteTime verifyTime
) {
4129 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4130 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
4131 &kCFTypeArrayCallBacks
);
4132 bool localized
= true;
4134 /* First we put the subject summary name. */
4135 CFStringRef ssummary
= SecCertificateCopySubjectSummary(certificate
);
4137 appendProperty(summary
, kSecPropertyTypeTitle
,
4138 NULL
, NULL
, ssummary
, localized
);
4139 CFRelease(ssummary
);
4142 /* Let see if this certificate is currently valid. */
4144 CFAbsoluteTime when
;
4145 CFStringRef message
;
4147 if (verifyTime
> certificate
->_notAfter
) {
4148 label
= SEC_EXPIRED_KEY
;
4149 when
= certificate
->_notAfter
;
4150 ptype
= kSecPropertyTypeError
;
4151 message
= SEC_CERT_EXPIRED_KEY
;
4152 } else if (certificate
->_notBefore
> verifyTime
) {
4153 label
= SEC_VALID_FROM_KEY
;
4154 when
= certificate
->_notBefore
;
4155 ptype
= kSecPropertyTypeError
;
4156 message
= SEC_CERT_NOT_YET_VALID_KEY
;
4158 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
4159 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
4160 if (verifyTime
> last
) {
4161 label
= SEC_EXPIRED_KEY
;
4163 ptype
= kSecPropertyTypeError
;
4164 message
= SEC_ISSUER_EXPIRED_KEY
;
4165 } else if (verifyTime
< first
) {
4166 label
= SEC_VALID_FROM_KEY
;
4168 ptype
= kSecPropertyTypeError
;
4169 message
= SEC_ISSR_NOT_YET_VALID_KEY
;
4171 label
= SEC_EXPIRES_KEY
;
4172 when
= certificate
->_notAfter
;
4173 ptype
= kSecPropertyTypeSuccess
;
4174 message
= SEC_CERT_VALID_KEY
;
4178 appendDateProperty(summary
, label
, when
, localized
);
4179 CFStringRef lmessage
= SecCopyCertString(message
);
4180 appendProperty(summary
, ptype
, NULL
, NULL
, lmessage
, localized
);
4181 CFRelease(lmessage
);
4186 CFArrayRef
SecCertificateCopyLegacyProperties(SecCertificateRef certificate
) {
4188 This function replicates the content returned by SecCertificateCopyProperties
4189 prior to 10.12.4, providing stable return values for SecCertificateCopyValues.
4190 Unlike SecCertificateCopyProperties, it does not cache the result and
4191 assumes the caller will do so.
4193 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4194 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
,
4195 0, &kCFTypeArrayCallBacks
);
4198 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
4199 &certificate
->_subject
, false);
4200 appendProperty(properties
, kSecPropertyTypeSection
, CFSTR("Subject Name"),
4201 NULL
, subject_plist
, false);
4202 CFRelease(subject_plist
);
4205 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
4206 &certificate
->_issuer
, false);
4207 appendProperty(properties
, kSecPropertyTypeSection
, CFSTR("Issuer Name"),
4208 NULL
, issuer_plist
, false);
4209 CFRelease(issuer_plist
);
4212 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
4213 NULL
, CFSTR("%d"), certificate
->_version
+ 1);
4214 appendProperty(properties
, kSecPropertyTypeString
, CFSTR("Version"),
4215 NULL
, versionString
, false);
4216 CFRelease(versionString
);
4219 if (certificate
->_serialNum
.length
) {
4220 appendIntegerProperty(properties
, CFSTR("Serial Number"),
4221 &certificate
->_serialNum
, false);
4224 /* Signature Algorithm */
4225 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
4226 &certificate
->_tbsSigAlg
, false);
4228 /* Validity dates */
4229 appendDateProperty(properties
, CFSTR("Not Valid Before"), certificate
->_notBefore
, false);
4230 appendDateProperty(properties
, CFSTR("Not Valid After"), certificate
->_notAfter
, false);
4232 if (certificate
->_subjectUniqueID
.length
) {
4233 appendDataProperty(properties
, CFSTR("Subject Unique ID"),
4234 NULL
, &certificate
->_subjectUniqueID
, false);
4236 if (certificate
->_issuerUniqueID
.length
) {
4237 appendDataProperty(properties
, CFSTR("Issuer Unique ID"),
4238 NULL
, &certificate
->_issuerUniqueID
, false);
4241 /* Public Key Algorithm */
4242 appendAlgorithmProperty(properties
, CFSTR("Public Key Algorithm"),
4243 &certificate
->_algId
, false);
4245 /* Public Key Data */
4246 appendDataProperty(properties
, CFSTR("Public Key Data"),
4247 NULL
, &certificate
->_pubKeyDER
, false);
4250 appendDataProperty(properties
, CFSTR("Signature"),
4251 NULL
, &certificate
->_signature
, false);
4255 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4256 appendExtension(properties
, &certificate
->_extensions
[ix
], false);
4260 appendFingerprintsProperty(properties
, CFSTR("Fingerprints"), certificate
, false);
4265 CFArrayRef
SecCertificateCopyProperties(SecCertificateRef certificate
) {
4266 if (!certificate
->_properties
) {
4267 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4268 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
4269 &kCFTypeArrayCallBacks
);
4270 require_quiet(properties
, out
);
4271 bool localized
= true;
4273 /* First we put the Subject Name in the property list. */
4274 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
4275 &certificate
->_subject
,
4277 if (subject_plist
) {
4278 appendProperty(properties
, kSecPropertyTypeSection
,
4279 SEC_SUBJECT_NAME_KEY
, NULL
, subject_plist
, localized
);
4281 CFReleaseNull(subject_plist
);
4283 /* Next we put the Issuer Name in the property list. */
4284 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
4285 &certificate
->_issuer
,
4288 appendProperty(properties
, kSecPropertyTypeSection
,
4289 SEC_ISSUER_NAME_KEY
, NULL
, issuer_plist
, localized
);
4291 CFReleaseNull(issuer_plist
);
4294 CFStringRef fmt
= SecCopyCertString(SEC_CERT_VERSION_VALUE_KEY
);
4295 CFStringRef versionString
= NULL
;
4297 versionString
= CFStringCreateWithFormat(allocator
, NULL
, fmt
,
4298 certificate
->_version
+ 1);
4301 if (versionString
) {
4302 appendProperty(properties
, kSecPropertyTypeString
,
4303 SEC_VERSION_KEY
, NULL
, versionString
, localized
);
4305 CFReleaseNull(versionString
);
4308 appendSerialNumberProperty(properties
, SEC_SERIAL_NUMBER_KEY
, &certificate
->_serialNum
, localized
);
4310 /* Validity dates. */
4311 appendValidityPeriodProperty(properties
, SEC_VALIDITY_PERIOD_KEY
, certificate
, localized
);
4313 if (certificate
->_subjectUniqueID
.length
) {
4314 appendDataProperty(properties
, SEC_SUBJECT_UNIQUE_ID_KEY
, NULL
,
4315 &certificate
->_subjectUniqueID
, localized
);
4317 if (certificate
->_issuerUniqueID
.length
) {
4318 appendDataProperty(properties
, SEC_ISSUER_UNIQUE_ID_KEY
, NULL
,
4319 &certificate
->_issuerUniqueID
, localized
);
4322 appendPublicKeyProperty(properties
, SEC_PUBLIC_KEY_KEY
, certificate
, localized
);
4325 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4326 appendExtension(properties
, &certificate
->_extensions
[ix
], localized
);
4330 appendSignatureProperty(properties
, SEC_SIGNATURE_KEY
, certificate
, localized
);
4332 appendFingerprintsProperty(properties
, SEC_FINGERPRINTS_KEY
, certificate
, localized
);
4334 certificate
->_properties
= properties
;
4338 CFRetainSafe(certificate
->_properties
);
4339 return certificate
->_properties
;
4342 /* Unified serial number API */
4343 CFDataRef
SecCertificateCopySerialNumberData(
4344 SecCertificateRef certificate
,
4349 *error
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
4353 if (certificate
->_serialNumber
) {
4354 CFRetain(certificate
->_serialNumber
);
4356 return certificate
->_serialNumber
;
4360 /* On iOS, the SecCertificateCopySerialNumber API takes one argument. */
4361 CFDataRef
SecCertificateCopySerialNumber(
4362 SecCertificateRef certificate
) {
4363 return SecCertificateCopySerialNumberData(certificate
, NULL
);
4367 CFDataRef
SecCertificateGetNormalizedIssuerContent(
4368 SecCertificateRef certificate
) {
4369 return certificate
->_normalizedIssuer
;
4372 CFDataRef
SecCertificateGetNormalizedSubjectContent(
4373 SecCertificateRef certificate
) {
4374 return certificate
->_normalizedSubject
;
4377 /* Verify that certificate was signed by issuerKey. */
4378 OSStatus
SecCertificateIsSignedBy(SecCertificateRef certificate
,
4379 SecKeyRef issuerKey
) {
4380 /* Setup algId in SecAsn1AlgId format. */
4382 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
4383 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
4384 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
4385 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
4387 /* RFC5280 4.1.1.2, 4.1.2.3 requires the actual signature algorithm
4388 must match the specified algorithm in the TBSCertificate. */
4389 bool sigAlgMatch
= DEROidCompare(&certificate
->_sigAlg
.oid
,
4390 &certificate
->_tbsSigAlg
.oid
);
4392 secwarning("Signature algorithm mismatch in certificate (see RFC5280 4.1.1.2)");
4395 CFErrorRef error
= NULL
;
4397 !SecVerifySignatureWithPublicKey(issuerKey
, &algId
,
4398 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
4399 certificate
->_signature
.data
, certificate
->_signature
.length
, &error
))
4401 #if !defined(NDEBUG)
4402 secdebug("verify", "signature verify failed: %" PRIdOSStatus
, (error
) ? (OSStatus
)CFErrorGetCode(error
) : errSecNotSigner
);
4404 CFReleaseSafe(error
);
4405 return errSecNotSigner
;
4408 return errSecSuccess
;
4411 const DERItem
* SecCertificateGetSubjectAltName(SecCertificateRef certificate
) {
4412 if (!certificate
->_subjectAltName
) {
4415 return &certificate
->_subjectAltName
->extnValue
;
4418 static bool convertIPAddress(CFStringRef name
, CFDataRef
*dataIP
) {
4419 /* IPv4: 4 octets in decimal separated by dots. We don't support matching IPv6 already. */
4420 bool result
= false;
4422 if (CFStringGetLength(name
) < 7 || /* min size is #.#.#.# */
4423 CFStringGetLength(name
) > 15) { /* max size is ###.###.###.### */
4427 CFCharacterSetRef decimals
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789."));
4428 CFCharacterSetRef nonDecimals
= CFCharacterSetCreateInvertedSet(NULL
, decimals
);
4429 CFMutableDataRef data
= CFDataCreateMutable(NULL
, 0);
4430 CFArrayRef parts
= CFStringCreateArrayBySeparatingStrings(NULL
, name
, CFSTR("."));
4432 /* Check character set */
4433 if (CFStringFindCharacterFromSet(name
, nonDecimals
,
4434 CFRangeMake(0, CFStringGetLength(name
)),
4435 kCFCompareForcedOrdering
, NULL
)) {
4439 /* Check number of labels */
4440 if (CFArrayGetCount(parts
) != 4) {
4444 /* Check each label and convert */
4445 CFIndex i
, count
= CFArrayGetCount(parts
);
4446 for (i
= 0; i
< count
; i
++) {
4447 CFStringRef octet
= CFArrayGetValueAtIndex(parts
, i
);
4448 char *cString
= CFStringToCString(octet
);
4449 uint32_t value
= atoi(cString
);
4454 uint8_t byte
= value
;
4455 CFDataAppendBytes(data
, &byte
, 1);
4460 *dataIP
= CFRetain(data
);
4464 CFReleaseNull(data
);
4465 CFReleaseNull(parts
);
4466 CFReleaseNull(decimals
);
4467 CFReleaseNull(nonDecimals
);
4471 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
4472 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4473 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
4474 if (gnType
== GNT_IPAddress
) {
4475 CFStringRef string
= copyIPAddressContentDescription(
4476 kCFAllocatorDefault
, generalName
);
4478 CFArrayAppendValue(ipAddresses
, string
);
4481 return errSecInvalidCertificate
;
4484 return errSecSuccess
;
4487 CFArrayRef
SecCertificateCopyIPAddresses(SecCertificateRef certificate
) {
4488 /* These can only exist in the subject alt name. */
4489 if (!certificate
->_subjectAltName
)
4492 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
4493 0, &kCFTypeArrayCallBacks
);
4494 OSStatus status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4495 ipAddresses
, appendIPAddressesFromGeneralNames
);
4496 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
4497 CFRelease(ipAddresses
);
4503 static OSStatus
appendIPAddressesFromX501Name(void *context
, const DERItem
*type
,
4504 const DERItem
*value
, CFIndex rdnIX
,
4506 CFMutableArrayRef addrs
= (CFMutableArrayRef
)context
;
4507 if (DEROidCompare(type
, &oidCommonName
)) {
4508 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4509 value
, true, localized
);
4511 CFDataRef data
= NULL
;
4512 if (convertIPAddress(string
, &data
)) {
4513 CFArrayAppendValue(addrs
, data
);
4514 CFReleaseNull(data
);
4518 return errSecInvalidCertificate
;
4521 return errSecSuccess
;
4524 CFArrayRef
SecCertificateCopyIPAddressesFromSubject(SecCertificateRef certificate
) {
4525 CFMutableArrayRef addrs
= CFArrayCreateMutable(kCFAllocatorDefault
,
4526 0, &kCFTypeArrayCallBacks
);
4527 OSStatus status
= parseX501NameContent(&certificate
->_subject
, addrs
,
4528 appendIPAddressesFromX501Name
, true);
4529 if (status
|| CFArrayGetCount(addrs
) == 0) {
4530 CFReleaseNull(addrs
);
4536 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
4537 const DERItem
*generalName
) {
4538 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4539 if (gnType
== GNT_DNSName
) {
4540 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4541 generalName
->data
, generalName
->length
,
4542 kCFStringEncodingUTF8
, FALSE
);
4544 CFArrayAppendValue(dnsNames
, string
);
4547 return errSecInvalidCertificate
;
4550 return errSecSuccess
;
4553 /* Return true if the passed in string matches the
4554 Preferred name syntax from sections 2.3.1. in RFC 1035.
4555 With the added check that we disallow empty dns names.
4556 Also in order to support wildcard DNSNames we allow for the '*'
4557 character anywhere in a dns component where we currently allow
4560 <domain> ::= <subdomain> | " "
4562 <subdomain> ::= <label> | <subdomain> "." <label>
4564 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
4566 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
4568 <let-dig-hyp> ::= <let-dig> | "-"
4570 <let-dig> ::= <letter> | <digit>
4572 <letter> ::= any one of the 52 alphabetic characters A through Z in
4573 upper case and a through z in lower case
4575 <digit> ::= any one of the ten digits 0 through 9
4577 static bool isDNSName(CFStringRef string
) {
4578 CFStringInlineBuffer buf
= {};
4579 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
4580 /* From RFC 1035 2.3.4. Size limits:
4581 labels 63 octets or less
4582 names 255 octets or less */
4583 require_quiet(length
<= 255, notDNS
);
4584 CFRange range
= { 0, length
};
4585 CFStringInitInlineBuffer(string
, &buf
, range
);
4589 kDNSStateAfterAlpha
,
4590 kDNSStateAfterDigit
,
4592 } state
= kDNSStateInital
;
4594 for (ix
= 0; ix
< length
; ++ix
) {
4595 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
4598 require_quiet(labelLength
<= 64 &&
4599 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4601 state
= kDNSStateAfterDot
;
4603 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
4605 state
= kDNSStateAfterAlpha
;
4606 } else if ('0' <= ch
&& ch
<= '9') {
4608 /* The requirement for labels to start with a letter was
4609 dropped so we don't check this anymore. */
4610 require_quiet(state
== kDNSStateAfterAlpha
||
4611 state
== kDNSStateAfterDigit
||
4612 state
== kDNSStateAfterDash
, notDNS
);
4614 state
= kDNSStateAfterDigit
;
4615 } else if (ch
== '-') {
4616 require_quiet(state
== kDNSStateAfterAlpha
||
4617 state
== kDNSStateAfterDigit
||
4618 state
== kDNSStateAfterDash
, notDNS
);
4619 state
= kDNSStateAfterDash
;
4625 /* We don't allow a dns name to end in a dot or dash. */
4626 require_quiet(labelLength
<= 63 &&
4627 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4635 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4636 const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4637 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4638 if (DEROidCompare(type
, &oidCommonName
)) {
4639 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4640 value
, true, localized
);
4642 if (isDNSName(string
)) {
4643 /* We found a common name that is formatted like a valid
4645 CFArrayAppendValue(dnsNames
, string
);
4649 return errSecInvalidCertificate
;
4652 return errSecSuccess
;
4655 CFArrayRef
SecCertificateCopyDNSNamesFromSubject(SecCertificateRef certificate
) {
4656 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4657 0, &kCFTypeArrayCallBacks
);
4658 OSStatus status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4659 appendDNSNamesFromX501Name
, true);
4660 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4661 CFReleaseNull(dnsNames
);
4665 /* appendDNSNamesFromX501Name allows IP addresses, we don't want those for this function */
4666 __block CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4667 CFArrayForEach(dnsNames
, ^(const void *value
) {
4668 CFStringRef name
= (CFStringRef
)value
;
4669 if (!convertIPAddress(name
, NULL
)) {
4670 CFArrayAppendValue(result
, name
);
4673 CFReleaseNull(dnsNames
);
4674 if (CFArrayGetCount(result
) == 0) {
4675 CFReleaseNull(result
);
4681 CFArrayRef
SecCertificateCopyDNSNamesFromSAN(SecCertificateRef certificate
) {
4682 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4683 0, &kCFTypeArrayCallBacks
);
4684 OSStatus status
= errSecSuccess
;
4685 if (certificate
->_subjectAltName
) {
4686 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4687 dnsNames
, appendDNSNamesFromGeneralNames
);
4690 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4691 CFReleaseNull(dnsNames
);
4696 /* Not everything returned by this function is going to be a proper DNS name,
4697 we also return the certificates common name entries from the subject,
4698 assuming they look like dns names as specified in RFC 1035. */
4699 CFArrayRef
SecCertificateCopyDNSNames(SecCertificateRef certificate
) {
4700 /* These can exist in the subject alt name or in the subject. */
4701 CFArrayRef sanNames
= SecCertificateCopyDNSNamesFromSAN(certificate
);
4702 if (sanNames
&& CFArrayGetCount(sanNames
) > 0) {
4705 CFReleaseNull(sanNames
);
4707 /* RFC 2818 section 3.1. Server Identity
4709 If a subjectAltName extension of type dNSName is present, that MUST
4710 be used as the identity. Otherwise, the (most specific) Common Name
4711 field in the Subject field of the certificate MUST be used. Although
4712 the use of the Common Name is existing practice, it is deprecated and
4713 Certification Authorities are encouraged to use the dNSName instead.
4716 This implies that if we found 1 or more DNSNames in the
4717 subjectAltName, we should not use the Common Name of the subject as
4721 /* To preserve bug for bug compatibility, we can't use SecCertificateCopyDNSNamesFromSubject
4722 * because that function filters out IP Addresses. This function is Private, but
4723 * SecCertificateCopyValues uses it and that's Public. */
4724 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4725 0, &kCFTypeArrayCallBacks
);
4726 OSStatus status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4727 appendDNSNamesFromX501Name
, true);
4728 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4729 CFReleaseNull(dnsNames
);
4734 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4735 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4736 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4737 if (gnType
== GNT_RFC822Name
) {
4738 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4739 generalName
->data
, generalName
->length
,
4740 kCFStringEncodingASCII
, FALSE
);
4742 CFArrayAppendValue(dnsNames
, string
);
4745 return errSecInvalidCertificate
;
4748 return errSecSuccess
;
4751 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4752 const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4753 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4754 if (DEROidCompare(type
, &oidEmailAddress
)) {
4755 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4756 value
, true, localized
);
4758 CFArrayAppendValue(dnsNames
, string
);
4761 return errSecInvalidCertificate
;
4764 return errSecSuccess
;
4767 CFArrayRef
SecCertificateCopyRFC822Names(SecCertificateRef certificate
) {
4768 /* These can exist in the subject alt name or in the subject. */
4769 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4770 0, &kCFTypeArrayCallBacks
);
4771 OSStatus status
= errSecSuccess
;
4772 if (certificate
->_subjectAltName
) {
4773 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4774 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4777 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4778 appendRFC822NamesFromX501Name
, true);
4780 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4781 CFRelease(rfc822Names
);
4787 OSStatus
SecCertificateCopyEmailAddresses(SecCertificateRef certificate
, CFArrayRef
* __nonnull CF_RETURNS_RETAINED emailAddresses
) {
4788 if (!certificate
|| !emailAddresses
) {
4791 *emailAddresses
= SecCertificateCopyRFC822Names(certificate
);
4792 if (*emailAddresses
== NULL
) {
4793 *emailAddresses
= CFArrayCreate(NULL
, NULL
, 0, &kCFTypeArrayCallBacks
);
4795 return errSecSuccess
;
4798 CFArrayRef
SecCertificateCopyRFC822NamesFromSubject(SecCertificateRef certificate
) {
4799 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4800 0, &kCFTypeArrayCallBacks
);
4801 OSStatus status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4802 appendRFC822NamesFromX501Name
, true);
4803 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4804 CFRelease(rfc822Names
);
4810 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4811 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4812 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4813 if (DEROidCompare(type
, &oidCommonName
)) {
4814 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4815 value
, true, localized
);
4817 CFArrayAppendValue(commonNames
, string
);
4820 return errSecInvalidCertificate
;
4823 return errSecSuccess
;
4826 CFArrayRef
SecCertificateCopyCommonNames(SecCertificateRef certificate
) {
4827 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4828 0, &kCFTypeArrayCallBacks
);
4830 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4831 appendCommonNamesFromX501Name
, true);
4832 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4833 CFRelease(commonNames
);
4839 OSStatus
SecCertificateCopyCommonName(SecCertificateRef certificate
, CFStringRef
*commonName
)
4844 CFArrayRef commonNames
= SecCertificateCopyCommonNames(certificate
);
4846 return errSecInternal
;
4850 CFIndex count
= CFArrayGetCount(commonNames
);
4851 *commonName
= CFRetainSafe(CFArrayGetValueAtIndex(commonNames
, count
-1));
4853 CFReleaseSafe(commonNames
);
4854 return errSecSuccess
;
4857 static OSStatus
appendOrganizationFromX501Name(void *context
,
4858 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4859 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4860 if (DEROidCompare(type
, &oidOrganizationName
)) {
4861 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4862 value
, true, localized
);
4864 CFArrayAppendValue(organization
, string
);
4867 return errSecInvalidCertificate
;
4870 return errSecSuccess
;
4873 CFArrayRef
SecCertificateCopyOrganizationFromX501NameContent(const DERItem
*nameContent
) {
4874 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4875 0, &kCFTypeArrayCallBacks
);
4877 status
= parseX501NameContent(nameContent
, organization
,
4878 appendOrganizationFromX501Name
, true);
4879 if (status
|| CFArrayGetCount(organization
) == 0) {
4880 CFRelease(organization
);
4881 organization
= NULL
;
4883 return organization
;
4886 CFArrayRef
SecCertificateCopyOrganization(SecCertificateRef certificate
) {
4887 return SecCertificateCopyOrganizationFromX501NameContent(&certificate
->_subject
);
4890 static OSStatus
appendOrganizationalUnitFromX501Name(void *context
,
4891 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4892 CFMutableArrayRef organizationalUnit
= (CFMutableArrayRef
)context
;
4893 if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4894 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4895 value
, true, localized
);
4897 CFArrayAppendValue(organizationalUnit
, string
);
4900 return errSecInvalidCertificate
;
4903 return errSecSuccess
;
4906 CFArrayRef
SecCertificateCopyOrganizationalUnit(SecCertificateRef certificate
) {
4907 CFMutableArrayRef organizationalUnit
= CFArrayCreateMutable(kCFAllocatorDefault
,
4908 0, &kCFTypeArrayCallBacks
);
4910 status
= parseX501NameContent(&certificate
->_subject
, organizationalUnit
,
4911 appendOrganizationalUnitFromX501Name
, true);
4912 if (status
|| CFArrayGetCount(organizationalUnit
) == 0) {
4913 CFRelease(organizationalUnit
);
4914 organizationalUnit
= NULL
;
4916 return organizationalUnit
;
4919 static OSStatus
appendCountryFromX501Name(void *context
,
4920 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
4921 CFMutableArrayRef countries
= (CFMutableArrayRef
)context
;
4922 if (DEROidCompare(type
, &oidCountryName
)) {
4923 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4924 value
, true, localized
);
4926 CFArrayAppendValue(countries
, string
);
4929 return errSecInvalidCertificate
;
4932 return errSecSuccess
;
4935 CFArrayRef
SecCertificateCopyCountry(SecCertificateRef certificate
) {
4936 CFMutableArrayRef countries
= CFArrayCreateMutable(kCFAllocatorDefault
,
4937 0, &kCFTypeArrayCallBacks
);
4939 status
= parseX501NameContent(&certificate
->_subject
, countries
,
4940 appendCountryFromX501Name
, true);
4941 if (status
|| CFArrayGetCount(countries
) == 0) {
4942 CFRelease(countries
);
4948 const SecCEBasicConstraints
*
4949 SecCertificateGetBasicConstraints(SecCertificateRef certificate
) {
4950 if (certificate
->_basicConstraints
.present
)
4951 return &certificate
->_basicConstraints
;
4956 CFArrayRef
SecCertificateGetPermittedSubtrees(SecCertificateRef certificate
) {
4957 return (certificate
->_permittedSubtrees
);
4960 CFArrayRef
SecCertificateGetExcludedSubtrees(SecCertificateRef certificate
) {
4961 return (certificate
->_excludedSubtrees
);
4964 const SecCEPolicyConstraints
*
4965 SecCertificateGetPolicyConstraints(SecCertificateRef certificate
) {
4966 if (certificate
->_policyConstraints
.present
)
4967 return &certificate
->_policyConstraints
;
4972 const SecCEPolicyMappings
*
4973 SecCertificateGetPolicyMappings(SecCertificateRef certificate
) {
4974 if (certificate
->_policyMappings
.present
) {
4975 return &certificate
->_policyMappings
;
4981 const SecCECertificatePolicies
*
4982 SecCertificateGetCertificatePolicies(SecCertificateRef certificate
) {
4983 if (certificate
->_certificatePolicies
.present
)
4984 return &certificate
->_certificatePolicies
;
4989 const SecCEInhibitAnyPolicy
*
4990 SecCertificateGetInhibitAnyPolicySkipCerts(SecCertificateRef certificate
) {
4991 if (certificate
->_inhibitAnyPolicySkipCerts
.present
) {
4992 return &certificate
->_inhibitAnyPolicySkipCerts
;
4998 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
4999 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
5000 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
5001 if (gnType
== GNT_OtherName
) {
5003 DERReturn drtn
= DERParseSequenceContent(generalName
,
5004 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
5006 require_noerr_quiet(drtn
, badDER
);
5007 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
5009 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
5010 &on
.value
, true, true), badDER
);
5011 CFArrayAppendValue(ntPrincipalNames
, string
);
5015 return errSecSuccess
;
5018 return errSecInvalidCertificate
;
5022 CFArrayRef
SecCertificateCopyNTPrincipalNames(SecCertificateRef certificate
) {
5023 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
5024 0, &kCFTypeArrayCallBacks
);
5025 OSStatus status
= errSecSuccess
;
5026 if (certificate
->_subjectAltName
) {
5027 status
= SecCertificateParseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
5028 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
5030 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
5031 CFRelease(ntPrincipalNames
);
5032 ntPrincipalNames
= NULL
;
5034 return ntPrincipalNames
;
5037 static OSStatus
appendToRFC2253String(void *context
,
5038 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5039 CFMutableStringRef string
= (CFMutableStringRef
)context
;
5043 ST stateOrProvinceName
5045 OU organizationalUnitName
5047 STREET streetAddress
5051 /* Prepend a + if this is not the first RDN in an RDN set.
5052 Otherwise prepend a , if this is not the first RDN. */
5054 CFStringAppend(string
, CFSTR("+"));
5055 else if (CFStringGetLength(string
)) {
5056 CFStringAppend(string
, CFSTR(","));
5059 CFStringRef label
, oid
= NULL
;
5060 /* @@@ Consider changing this to a dictionary lookup keyed by the
5061 decimal representation. */
5062 if (DEROidCompare(type
, &oidCommonName
)) {
5063 label
= CFSTR("CN");
5064 } else if (DEROidCompare(type
, &oidLocalityName
)) {
5066 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
5067 label
= CFSTR("ST");
5068 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
5070 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
5071 label
= CFSTR("OU");
5072 } else if (DEROidCompare(type
, &oidCountryName
)) {
5075 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
5076 label
= CFSTR("STREET");
5077 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
5078 label
= CFSTR("DC");
5079 } else if (DEROidCompare(type
, &oidUserID
)) {
5080 label
= CFSTR("UID");
5083 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
5086 CFStringAppend(string
, label
);
5087 CFStringAppend(string
, CFSTR("="));
5088 CFStringRef raw
= NULL
;
5090 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true, localized
);
5093 /* Append raw to string while escaping:
5094 a space or "#" character occurring at the beginning of the string
5095 a space character occurring at the end of the string
5096 one of the characters ",", "+", """, "\", "<", ">" or ";"
5098 CFStringInlineBuffer buffer
= {};
5099 CFIndex ix
, length
= CFStringGetLength(raw
);
5100 CFRange range
= { 0, length
};
5101 CFStringInitInlineBuffer(raw
, &buffer
, range
);
5102 for (ix
= 0; ix
< length
; ++ix
) {
5103 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
5105 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
5106 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
5107 ch
== '<' || ch
== '>' || ch
== ';' ||
5108 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
5109 (ch
== '#' && ix
== 0)) {
5110 UniChar chars
[] = { '\\', ch
};
5111 CFStringAppendCharacters(string
, chars
, 2);
5113 CFStringAppendCharacters(string
, &ch
, 1);
5118 /* Append the value in hex. */
5119 CFStringAppend(string
, CFSTR("#"));
5121 for (ix
= 0; ix
< value
->length
; ++ix
)
5122 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
5127 return errSecSuccess
;
5130 CFStringRef
SecCertificateCopySubjectString(SecCertificateRef certificate
) {
5131 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
5132 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
, true);
5133 if (status
|| CFStringGetLength(string
) == 0) {
5140 static OSStatus
appendToCompanyNameString(void *context
,
5141 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
, bool localized
) {
5142 CFMutableStringRef string
= (CFMutableStringRef
)context
;
5143 if (CFStringGetLength(string
) != 0)
5144 return errSecSuccess
;
5146 if (!DEROidCompare(type
, &oidOrganizationName
))
5147 return errSecSuccess
;
5150 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true, localized
);
5152 return errSecSuccess
;
5153 CFStringAppend(string
, raw
);
5156 return errSecSuccess
;
5159 CFStringRef
SecCertificateCopyCompanyName(SecCertificateRef certificate
) {
5160 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
5161 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
5162 appendToCompanyNameString
, true);
5163 if (status
|| CFStringGetLength(string
) == 0) {
5170 CFDataRef
SecCertificateCopyIssuerSequence(
5171 SecCertificateRef certificate
) {
5172 return SecDERItemCopySequence(&certificate
->_issuer
);
5175 CFDataRef
SecCertificateCopySubjectSequence(
5176 SecCertificateRef certificate
) {
5177 return SecDERItemCopySequence(&certificate
->_subject
);
5180 CFDataRef
SecCertificateCopyNormalizedIssuerSequence(SecCertificateRef certificate
) {
5181 if (!certificate
|| !certificate
->_normalizedIssuer
) {
5184 return SecCopySequenceFromContent(certificate
->_normalizedIssuer
);
5187 CFDataRef
SecCertificateCopyNormalizedSubjectSequence(SecCertificateRef certificate
) {
5188 if (!certificate
|| !certificate
->_normalizedSubject
) {
5191 return SecCopySequenceFromContent(certificate
->_normalizedSubject
);
5194 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithm(
5195 SecCertificateRef certificate
) {
5196 return &certificate
->_algId
;
5199 const DERItem
*SecCertificateGetPublicKeyData(SecCertificateRef certificate
) {
5200 return &certificate
->_pubKeyDER
;
5204 /* There is already a SecCertificateCopyPublicKey with different args on OS X,
5205 so we will refer to this one internally as SecCertificateCopyPublicKey_ios.
5207 __nullable SecKeyRef
SecCertificateCopyPublicKey_ios(SecCertificateRef certificate
)
5209 __nullable SecKeyRef
SecCertificateCopyPublicKey(SecCertificateRef certificate
)
5212 return SecCertificateCopyKey(certificate
);
5215 SecKeyRef
SecCertificateCopyKey(SecCertificateRef certificate
) {
5216 if (certificate
->_pubKey
== NULL
) {
5217 const DERAlgorithmId
*algId
=
5218 SecCertificateGetPublicKeyAlgorithm(certificate
);
5219 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
5220 const DERItem
*params
= NULL
;
5221 if (algId
->params
.length
!= 0) {
5222 params
= &algId
->params
;
5224 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
5225 SecAsn1Item params1
= {
5226 .Data
= params
? params
->data
: NULL
,
5227 .Length
= params
? params
->length
: 0
5229 SecAsn1Item keyData1
= {
5230 .Data
= keyData
? keyData
->data
: NULL
,
5231 .Length
= keyData
? keyData
->length
: 0
5233 certificate
->_pubKey
= SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
5237 return CFRetainSafe(certificate
->_pubKey
);
5240 static CFIndex
SecCertificateGetPublicKeyAlgorithmIdAndSize(SecCertificateRef certificate
, size_t *keySizeInBytes
) {
5241 CFIndex keyAlgID
= kSecNullAlgorithmID
;
5244 SecKeyRef pubKey
= NULL
;
5245 require_quiet(certificate
, out
);
5246 require_quiet(pubKey
= SecCertificateCopyKey(certificate
) ,out
);
5247 size
= SecKeyGetBlockSize(pubKey
);
5248 keyAlgID
= SecKeyGetAlgorithmId(pubKey
);
5251 CFReleaseNull(pubKey
);
5252 if (keySizeInBytes
) { *keySizeInBytes
= size
; }
5257 * Public keys in certificates may be considered "weak" or "strong" or neither
5258 * (that is, in between). Certificates using weak keys are not trusted at all.
5259 * Certificates using neither strong nor weak keys are only trusted in certain
5260 * contexts. SecPolicy and SecPolicyServer define the contexts by which we enforce
5261 * these (or stronger) key size trust policies.
5263 bool SecCertificateIsWeakKey(SecCertificateRef certificate
) {
5264 if (!certificate
) { return true; }
5268 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5269 case kSecRSAAlgorithmID
:
5270 if (MIN_RSA_KEY_SIZE
<= size
) weak
= false;
5272 case kSecECDSAAlgorithmID
:
5273 if (MIN_EC_KEY_SIZE
<= size
) weak
= false;
5281 bool SecCertificateIsStrongKey(SecCertificateRef certificate
) {
5282 if (!certificate
) { return false; }
5284 bool strong
= false;
5286 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5287 case kSecRSAAlgorithmID
:
5288 if (MIN_STRONG_RSA_KEY_SIZE
<= size
) strong
= true;
5290 case kSecECDSAAlgorithmID
:
5291 if (MIN_STRONG_EC_KEY_SIZE
<= size
) strong
= true;
5299 bool SecCertificateIsWeakHash(SecCertificateRef certificate
) {
5300 if (!certificate
) { return true; }
5301 SecSignatureHashAlgorithm certAlg
= 0;
5302 certAlg
= SecCertificateGetSignatureHashAlgorithm(certificate
);
5303 if (certAlg
== kSecSignatureHashAlgorithmUnknown
||
5304 certAlg
== kSecSignatureHashAlgorithmMD2
||
5305 certAlg
== kSecSignatureHashAlgorithmMD4
||
5306 certAlg
== kSecSignatureHashAlgorithmMD5
||
5307 certAlg
== kSecSignatureHashAlgorithmSHA1
) {
5313 bool SecCertificateIsAtLeastMinKeySize(SecCertificateRef certificate
,
5314 CFDictionaryRef keySizes
) {
5315 if (!certificate
) { return false; }
5317 bool goodSize
= false;
5319 CFNumberRef minSize
;
5320 size_t minSizeInBits
;
5321 switch (SecCertificateGetPublicKeyAlgorithmIdAndSize(certificate
, &size
)) {
5322 case kSecRSAAlgorithmID
:
5323 if(CFDictionaryGetValueIfPresent(keySizes
, kSecAttrKeyTypeRSA
, (const void**)&minSize
)
5324 && minSize
&& CFNumberGetValue(minSize
, kCFNumberLongType
, &minSizeInBits
)) {
5325 if (size
>= (size_t)(minSizeInBits
+7)/8) goodSize
= true;
5328 case kSecECDSAAlgorithmID
:
5329 if(CFDictionaryGetValueIfPresent(keySizes
, kSecAttrKeyTypeEC
, (const void**)&minSize
)
5330 && minSize
&& CFNumberGetValue(minSize
, kCFNumberLongType
, &minSizeInBits
)) {
5331 if (size
>= (size_t)(minSizeInBits
+7)/8) goodSize
= true;
5340 CFDataRef
SecCertificateGetSHA1Digest(SecCertificateRef certificate
) {
5341 if (!certificate
|| !certificate
->_der
.data
) {
5344 if (!certificate
->_sha1Digest
) {
5345 certificate
->_sha1Digest
=
5346 SecSHA1DigestCreate(CFGetAllocator(certificate
),
5347 certificate
->_der
.data
, certificate
->_der
.length
);
5349 return certificate
->_sha1Digest
;
5352 CFDataRef
SecCertificateCopySHA256Digest(SecCertificateRef certificate
) {
5353 if (!certificate
|| !certificate
->_der
.data
) {
5356 return SecSHA256DigestCreate(CFGetAllocator(certificate
),
5357 certificate
->_der
.data
, certificate
->_der
.length
);
5360 CFDataRef
SecCertificateCopyIssuerSHA1Digest(SecCertificateRef certificate
) {
5361 CFDataRef digest
= NULL
;
5362 CFDataRef issuer
= SecCertificateCopyIssuerSequence(certificate
);
5364 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
5365 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
5371 CFDataRef
SecCertificateCopyPublicKeySHA1Digest(SecCertificateRef certificate
) {
5372 if (!certificate
|| !certificate
->_pubKeyDER
.data
) {
5375 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
5376 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
5379 static CFDataRef
SecCertificateCopySPKIEncoded(SecCertificateRef certificate
) {
5380 /* SPKI is saved without the tag/length by libDER, so we need to re-encode */
5381 if (!certificate
|| !certificate
->_subjectPublicKeyInfo
.data
) {
5384 DERSize size
= DERLengthOfItem(ASN1_CONSTR_SEQUENCE
, certificate
->_subjectPublicKeyInfo
.length
);
5385 if (size
< certificate
->_subjectPublicKeyInfo
.length
) {
5388 uint8_t *temp
= malloc(size
);
5392 DERReturn drtn
= DEREncodeItem(ASN1_CONSTR_SEQUENCE
,
5393 certificate
->_subjectPublicKeyInfo
.length
,
5394 certificate
->_subjectPublicKeyInfo
.data
,
5396 CFDataRef encodedSPKI
= NULL
;
5397 if (drtn
== DR_Success
) {
5398 encodedSPKI
= CFDataCreate(NULL
, temp
, size
);
5404 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA1Digest(SecCertificateRef certificate
) {
5405 CFDataRef encodedSPKI
= SecCertificateCopySPKIEncoded(certificate
);
5406 if (!encodedSPKI
) { return NULL
; }
5407 CFDataRef hash
= SecSHA1DigestCreate(CFGetAllocator(certificate
),
5408 CFDataGetBytePtr(encodedSPKI
),
5409 CFDataGetLength(encodedSPKI
));
5410 CFReleaseNull(encodedSPKI
);
5414 CFDataRef
SecCertificateCopySubjectPublicKeyInfoSHA256Digest(SecCertificateRef certificate
) {
5415 CFDataRef encodedSPKI
= SecCertificateCopySPKIEncoded(certificate
);
5416 if (!encodedSPKI
) { return NULL
; }
5417 CFDataRef hash
= SecSHA256DigestCreate(CFGetAllocator(certificate
),
5418 CFDataGetBytePtr(encodedSPKI
),
5419 CFDataGetLength(encodedSPKI
));
5420 CFReleaseNull(encodedSPKI
);
5424 CFTypeRef
SecCertificateCopyKeychainItem(SecCertificateRef certificate
)
5429 CFRetainSafe(certificate
->_keychain_item
);
5430 return certificate
->_keychain_item
;
5433 CFDataRef
SecCertificateGetAuthorityKeyID(SecCertificateRef certificate
) {
5437 if (!certificate
->_authorityKeyID
&&
5438 certificate
->_authorityKeyIdentifier
.length
) {
5439 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
5440 certificate
->_authorityKeyIdentifier
.data
,
5441 certificate
->_authorityKeyIdentifier
.length
);
5444 return certificate
->_authorityKeyID
;
5447 CFDataRef
SecCertificateGetSubjectKeyID(SecCertificateRef certificate
) {
5451 if (!certificate
->_subjectKeyID
&&
5452 certificate
->_subjectKeyIdentifier
.length
) {
5453 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
5454 certificate
->_subjectKeyIdentifier
.data
,
5455 certificate
->_subjectKeyIdentifier
.length
);
5458 return certificate
->_subjectKeyID
;
5461 CFArrayRef
SecCertificateGetCRLDistributionPoints(SecCertificateRef certificate
) {
5465 return certificate
->_crlDistributionPoints
;
5468 CFArrayRef
SecCertificateGetOCSPResponders(SecCertificateRef certificate
) {
5472 return certificate
->_ocspResponders
;
5475 CFArrayRef
SecCertificateGetCAIssuers(SecCertificateRef certificate
) {
5479 return certificate
->_caIssuers
;
5482 bool SecCertificateHasCriticalSubjectAltName(SecCertificateRef certificate
) {
5486 return certificate
->_subjectAltName
&&
5487 certificate
->_subjectAltName
->critical
;
5490 bool SecCertificateHasSubject(SecCertificateRef certificate
) {
5494 /* Since the _subject field is the content of the subject and not the
5495 whole thing, we can simply check for a 0 length subject here. */
5496 return certificate
->_subject
.length
!= 0;
5499 bool SecCertificateHasUnknownCriticalExtension(SecCertificateRef certificate
) {
5503 return certificate
->_foundUnknownCriticalExtension
;
5506 /* Private API functions. */
5507 void SecCertificateShow(SecCertificateRef certificate
) {
5509 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
5510 fprintf(stderr
, "\n");
5514 CFDictionaryRef
SecCertificateCopyAttributeDictionary(
5515 SecCertificateRef certificate
) {
5516 if (!certificate
|| !(CFGetTypeID(certificate
) == SecCertificateGetTypeID())) {
5519 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
5520 CFNumberRef certificateType
= NULL
;
5521 CFNumberRef certificateEncoding
= NULL
;
5522 CFStringRef label
= NULL
;
5523 CFStringRef alias
= NULL
;
5524 CFDataRef skid
= NULL
;
5525 CFDataRef pubKeyDigest
= NULL
;
5526 CFDataRef certData
= NULL
;
5527 CFDictionaryRef dict
= NULL
;
5531 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
5532 SInt32 ctv
= certificate
->_version
+ 1;
5533 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
5534 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
5535 require_quiet(certificateType
!= NULL
, out
);
5536 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
5537 require_quiet(certificateEncoding
!= NULL
, out
);
5538 certData
= SecCertificateCopyData(certificate
);
5539 require_quiet(certData
!= NULL
, out
);
5540 skid
= SecCertificateGetSubjectKeyID(certificate
);
5541 require_quiet(certificate
->_pubKeyDER
.data
!= NULL
&& certificate
->_pubKeyDER
.length
> 0, out
);
5542 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
5543 certificate
->_pubKeyDER
.length
);
5544 require_quiet(pubKeyDigest
!= NULL
, out
);
5546 /* We still need to figure out how to deal with multi valued attributes. */
5547 alias
= SecCertificateCopyRFC822Names(certificate
);
5548 label
= SecCertificateCopySubjectSummary(certificate
);
5554 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
5555 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
5556 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
5558 DICT_ADDPAIR(kSecAttrLabel
, label
);
5561 DICT_ADDPAIR(kSecAttrAlias
, alias
);
5563 if (isData(certificate
->_normalizedSubject
)) {
5564 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
5566 require_quiet(isData(certificate
->_normalizedIssuer
), out
);
5567 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
5568 require_quiet(isData(certificate
->_serialNumber
), out
);
5569 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
5571 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
5573 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
5574 DICT_ADDPAIR(kSecValueData
, certData
);
5575 dict
= DICT_CREATE(allocator
);
5578 CFReleaseSafe(label
);
5579 CFReleaseSafe(alias
);
5580 CFReleaseSafe(pubKeyDigest
);
5581 CFReleaseSafe(certData
);
5582 CFReleaseSafe(certificateEncoding
);
5583 CFReleaseSafe(certificateType
);
5588 SecCertificateRef
SecCertificateCreateFromAttributeDictionary(
5589 CFDictionaryRef refAttributes
) {
5590 /* @@@ Support having an allocator in refAttributes. */
5591 CFAllocatorRef allocator
= NULL
;
5592 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
5593 return data
? SecCertificateCreateWithData(allocator
, data
) : NULL
;
5597 static bool _SecCertificateIsSelfSigned(SecCertificateRef certificate
) {
5598 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
5599 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
5600 SecKeyRef publicKey
= NULL
;
5601 require(certificate
&& (CFGetTypeID(certificate
) == SecCertificateGetTypeID()), out
);
5602 require(publicKey
= SecCertificateCopyKey(certificate
), out
);
5603 CFDataRef normalizedIssuer
=
5604 SecCertificateGetNormalizedIssuerContent(certificate
);
5605 CFDataRef normalizedSubject
=
5606 SecCertificateGetNormalizedSubjectContent(certificate
);
5607 require_quiet(normalizedIssuer
&& normalizedSubject
&&
5608 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
5610 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(certificate
);
5611 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyID(certificate
);
5612 if (authorityKeyID
) {
5613 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
5616 require_noerr_quiet(SecCertificateIsSignedBy(certificate
, publicKey
), out
);
5618 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
5620 CFReleaseSafe(publicKey
);
5623 return (certificate
->_isSelfSigned
== kSecSelfSignedTrue
);
5626 bool SecCertificateIsCA(SecCertificateRef certificate
) {
5627 bool result
= false;
5628 require(certificate
&& (CFGetTypeID(certificate
) == SecCertificateGetTypeID()), out
);
5629 if (SecCertificateVersion(certificate
) >= 3) {
5630 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraints(certificate
);
5631 result
= (basicConstraints
&& basicConstraints
->isCA
);
5634 result
= _SecCertificateIsSelfSigned(certificate
);
5640 bool SecCertificateIsSelfSignedCA(SecCertificateRef certificate
) {
5641 return (_SecCertificateIsSelfSigned(certificate
) && SecCertificateIsCA(certificate
));
5644 OSStatus
SecCertificateIsSelfSigned(SecCertificateRef certificate
, Boolean
*isSelfSigned
) {
5645 if (!certificate
|| (CFGetTypeID(certificate
) != SecCertificateGetTypeID())) {
5646 return errSecInvalidCertificate
;
5648 if (!isSelfSigned
) {
5651 *isSelfSigned
= _SecCertificateIsSelfSigned(certificate
);
5652 return errSecSuccess
;
5655 SecKeyUsage
SecCertificateGetKeyUsage(SecCertificateRef certificate
) {
5657 return kSecKeyUsageUnspecified
;
5659 return certificate
->_keyUsage
;
5662 CFArrayRef
SecCertificateCopyExtendedKeyUsage(SecCertificateRef certificate
)
5664 CFMutableArrayRef extended_key_usage_oids
=
5665 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
5666 require_quiet(certificate
&& extended_key_usage_oids
, out
);
5668 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5669 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5670 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
5671 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
5674 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
5675 require_noerr_quiet(drtn
, out
);
5676 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
5677 DERDecodedInfo currDecoded
;
5679 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
5680 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
5681 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
5682 currDecoded
.content
.data
, currDecoded
.content
.length
);
5683 require_quiet(oid
, out
);
5684 CFArrayAppendValue(extended_key_usage_oids
, oid
);
5687 require_quiet(drtn
== DR_EndOfSequence
, out
);
5688 return extended_key_usage_oids
;
5692 CFReleaseSafe(extended_key_usage_oids
);
5696 CFArrayRef
SecCertificateCopySignedCertificateTimestamps(SecCertificateRef certificate
)
5698 require_quiet(certificate
, out
);
5701 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5702 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5703 if (extn
->extnID
.length
== oidGoogleEmbeddedSignedCertificateTimestamp
.length
&&
5704 !memcmp(extn
->extnID
.data
, oidGoogleEmbeddedSignedCertificateTimestamp
.data
, extn
->extnID
.length
)) {
5705 /* Got the SCT oid */
5706 DERDecodedInfo sctList
;
5707 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &sctList
);
5708 require_noerr_quiet(drtn
, out
);
5709 require_quiet(sctList
.tag
== ASN1_OCTET_STRING
, out
);
5710 return SecCreateSignedCertificateTimestampsArrayFromSerializedSCTList(sctList
.content
.data
, sctList
.content
.length
);
5718 static bool matches_expected(DERItem der
, CFTypeRef expected
) {
5719 if (der
.length
> 1) {
5720 DERDecodedInfo decoded
;
5721 DERDecodeItem(&der
, &decoded
);
5722 switch (decoded
.tag
) {
5725 return decoded
.content
.length
== 0 && expected
== NULL
;
5729 case ASN1_IA5_STRING
:
5730 case ASN1_UTF8_STRING
: {
5731 if (isString(expected
)) {
5732 CFStringRef expectedString
= (CFStringRef
) expected
;
5733 CFStringRef itemString
= CFStringCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFStringEncodingUTF8
, false, kCFAllocatorNull
);
5735 bool result
= (kCFCompareEqualTo
== CFStringCompare(expectedString
, itemString
, 0));
5736 CFReleaseNull(itemString
);
5742 case ASN1_OCTET_STRING
: {
5743 if (isData(expected
)) {
5744 CFDataRef expectedData
= (CFDataRef
) expected
;
5745 CFDataRef itemData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, decoded
.content
.data
, decoded
.content
.length
, kCFAllocatorNull
);
5747 bool result
= CFEqual(expectedData
, itemData
);
5748 CFReleaseNull(itemData
);
5754 case ASN1_INTEGER
: {
5755 SInt32 expected_value
= 0;
5756 if (isString(expected
))
5758 CFStringRef aStr
= (CFStringRef
)expected
;
5759 expected_value
= CFStringGetIntValue(aStr
);
5761 else if (isNumber(expected
))
5763 CFNumberGetValue(expected
, kCFNumberSInt32Type
, &expected_value
);
5766 uint32_t num_value
= 0;
5767 if (!DERParseInteger(&decoded
.content
, &num_value
))
5769 return ((uint32_t)expected_value
== num_value
);
5782 static bool cert_contains_marker_extension_value(SecCertificateRef certificate
, CFDataRef oid
, CFTypeRef expectedValue
)
5785 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
5786 size_t oid_len
= CFDataGetLength(oid
);
5788 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5789 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5790 if (extn
->extnID
.length
== oid_len
5791 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
5793 return matches_expected(extn
->extnValue
, expectedValue
);
5799 static bool cert_contains_marker_extension(SecCertificateRef certificate
, CFTypeRef oid
)
5801 return cert_contains_marker_extension_value(certificate
, oid
, NULL
);
5804 struct search_context
{
5806 SecCertificateRef certificate
;
5809 static bool GetDecimalValueOfString(CFStringRef string
, uint32_t* value
)
5811 CFCharacterSetRef nonDecimalDigit
= CFCharacterSetCreateInvertedSet(NULL
, CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
));
5812 bool result
= false;
5814 if ( CFStringGetLength(string
) > 0
5815 && !CFStringFindCharacterFromSet(string
, nonDecimalDigit
, CFRangeMake(0, CFStringGetLength(string
)), kCFCompareForcedOrdering
, NULL
))
5818 *value
= CFStringGetIntValue(string
);
5822 CFReleaseNull(nonDecimalDigit
);
5827 bool SecCertificateIsOidString(CFStringRef oid
)
5829 if (!oid
) return false;
5830 if (2 >= CFStringGetLength(oid
)) return false;
5833 /* oid string only has the allowed characters */
5834 CFCharacterSetRef decimalOid
= CFCharacterSetCreateWithCharactersInString(NULL
, CFSTR("0123456789."));
5835 CFCharacterSetRef nonDecimalOid
= CFCharacterSetCreateInvertedSet(NULL
, decimalOid
);
5836 if (CFStringFindCharacterFromSet(oid
, nonDecimalOid
, CFRangeMake(0, CFStringGetLength(oid
)), kCFCompareForcedOrdering
, NULL
)) {
5840 /* first arc is allowed */
5841 UniChar firstArc
[2];
5842 CFRange firstTwo
= {0, 2};
5843 CFStringGetCharacters(oid
, firstTwo
, firstArc
);
5844 if (firstArc
[1] != '.' ||
5845 (firstArc
[0] != '0' && firstArc
[0] != '1' && firstArc
[0] != '2')) {
5849 CFReleaseNull(decimalOid
);
5850 CFReleaseNull(nonDecimalOid
);
5855 CFDataRef
SecCertificateCreateOidDataFromString(CFAllocatorRef allocator
, CFStringRef string
)
5857 CFMutableDataRef currentResult
= NULL
;
5858 CFDataRef encodedResult
= NULL
;
5860 CFArrayRef parts
= NULL
;
5863 if (!string
|| !SecCertificateIsOidString(string
))
5866 parts
= CFStringCreateArrayBySeparatingStrings(NULL
, string
, CFSTR("."));
5871 count
= CFArrayGetCount(parts
);
5875 // assume no more than 5 bytes needed to represent any part of the oid,
5876 // since we limit parts to 32-bit values,
5877 // but the first two parts only need 1 byte
5878 currentResult
= CFDataCreateMutable(allocator
, 1+(count
-2)*5);
5884 part
= CFArrayGetValueAtIndex(parts
, 0);
5886 if (!GetDecimalValueOfString(part
, &x
) || x
> 6)
5893 part
= CFArrayGetValueAtIndex(parts
, 1);
5895 if (!GetDecimalValueOfString(part
, &x
) || x
> 39)
5901 CFDataAppendBytes(currentResult
, &firstByte
, 1);
5903 for (CFIndex i
= 2; i
< count
&& GetDecimalValueOfString(CFArrayGetValueAtIndex(parts
, i
), &x
); ++i
) {
5904 uint8_t b
[5] = {0, 0, 0, 0, 0};
5906 b
[3] = 0x80 | ((x
>> 7) & 0x7F);
5907 b
[2] = 0x80 | ((x
>> 14) & 0x7F);
5908 b
[1] = 0x80 | ((x
>> 21) & 0x7F);
5909 b
[0] = 0x80 | ((x
>> 28) & 0x7F);
5911 // Skip the unused extension bytes.
5912 size_t skipBytes
= 0;
5913 while (b
[skipBytes
] == 0x80)
5916 CFDataAppendBytes(currentResult
, b
+ skipBytes
, sizeof(b
) - skipBytes
);
5919 encodedResult
= currentResult
;
5920 currentResult
= NULL
;
5923 CFReleaseNull(parts
);
5924 CFReleaseNull(currentResult
);
5926 return encodedResult
;
5929 static void check_for_marker(const void *key
, const void *value
, void *context
)
5931 struct search_context
* search_ctx
= (struct search_context
*) context
;
5932 CFStringRef key_string
= (CFStringRef
) key
;
5933 CFTypeRef value_ref
= (CFTypeRef
) value
;
5935 // If we could have short circuted the iteration
5936 // we would have, but the best we can do
5937 // is not waste time comparing once a match
5939 if (search_ctx
->found
)
5942 if (CFGetTypeID(key_string
) != CFStringGetTypeID())
5945 CFDataRef key_data
= SecCertificateCreateOidDataFromString(NULL
, key_string
);
5947 if (NULL
== key_data
)
5950 if (cert_contains_marker_extension_value(search_ctx
->certificate
, key_data
, value_ref
))
5951 search_ctx
->found
= true;
5953 CFReleaseNull(key_data
);
5957 // CFType Ref is either:
5959 // CFData - OID to match with no data permitted
5960 // CFString - decimal OID to match
5961 // CFDictionary - OID -> Value table for expected values Single Object or Array
5962 // CFArray - Array of the above.
5964 // This returns true if any of the requirements are met.
5965 bool SecCertificateHasMarkerExtension(SecCertificateRef certificate
, CFTypeRef oids
)
5967 if (CFGetTypeID(oids
) == CFArrayGetTypeID()) {
5968 CFIndex ix
, length
= CFArrayGetCount(oids
);
5969 for (ix
= 0; ix
< length
; ix
++)
5970 if (SecCertificateHasMarkerExtension(certificate
, CFArrayGetValueAtIndex((CFArrayRef
)oids
, ix
)))
5972 } else if (CFGetTypeID(oids
) == CFDictionaryGetTypeID()) {
5973 struct search_context context
= { .found
= false, .certificate
= certificate
};
5974 CFDictionaryApplyFunction((CFDictionaryRef
) oids
, &check_for_marker
, &context
);
5975 return context
.found
;
5976 } else if (CFGetTypeID(oids
) == CFDataGetTypeID()) {
5977 return cert_contains_marker_extension(certificate
, oids
);
5978 } else if (CFGetTypeID(oids
) == CFStringGetTypeID()) {
5979 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, oids
);
5980 if (dataOid
== NULL
) return false;
5981 bool result
= cert_contains_marker_extension(certificate
, dataOid
);
5982 CFReleaseNull(dataOid
);
5988 static DERItem
*cert_extension_value_for_marker(SecCertificateRef certificate
, CFDataRef oid
) {
5990 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
5991 size_t oid_len
= CFDataGetLength(oid
);
5993 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
5994 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
5995 if (extn
->extnID
.length
== oid_len
5996 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
5998 return (DERItem
*)&extn
->extnValue
;
6005 // CFType Ref is either:
6007 // CFData - OID to match with no data permitted
6008 // CFString - decimal OID to match
6010 DERItem
*SecCertificateGetExtensionValue(SecCertificateRef certificate
, CFTypeRef oid
) {
6011 if (!certificate
|| !oid
) {
6015 if(CFGetTypeID(oid
) == CFDataGetTypeID()) {
6016 return cert_extension_value_for_marker(certificate
, oid
);
6017 } else if (CFGetTypeID(oid
) == CFStringGetTypeID()) {
6018 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, oid
);
6019 if (dataOid
== NULL
) return NULL
;
6020 DERItem
*result
= cert_extension_value_for_marker(certificate
, dataOid
);
6021 CFReleaseNull(dataOid
);
6028 CFDataRef
SecCertificateCopyExtensionValue(SecCertificateRef certificate
, CFTypeRef extensionOID
, bool *isCritical
) {
6029 if (!certificate
|| !extensionOID
) {
6033 CFDataRef oid
= NULL
, extensionValue
= NULL
;
6034 if (CFGetTypeID(extensionOID
) == CFDataGetTypeID()) {
6035 oid
= CFRetainSafe(extensionOID
);
6036 } else if (CFGetTypeID(extensionOID
) == CFStringGetTypeID()) {
6037 oid
= SecCertificateCreateOidDataFromString(NULL
, extensionOID
);
6044 const uint8_t *oid_data
= CFDataGetBytePtr(oid
);
6045 size_t oid_len
= CFDataGetLength(oid
);
6047 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
6048 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
6049 if (extn
->extnID
.length
== oid_len
6050 && !memcmp(extn
->extnID
.data
, oid_data
, extn
->extnID
.length
))
6053 *isCritical
= extn
->critical
;
6055 extensionValue
= CFDataCreate(NULL
, extn
->extnValue
.data
, extn
->extnValue
.length
);
6061 return extensionValue
;
6064 CFDataRef
SecCertificateCopyiAPAuthCapabilities(SecCertificateRef certificate
) {
6068 CFDataRef extensionData
= NULL
;
6069 DERItem
*extensionValue
= NULL
;
6070 extensionValue
= SecCertificateGetExtensionValue(certificate
,
6071 CFSTR("1.2.840.113635.100.6.36"));
6072 require_quiet(extensionValue
, out
);
6073 /* The extension is a octet string containing the DER-encoded 32-byte octet string */
6074 require_quiet(extensionValue
->length
== 34, out
);
6075 DERDecodedInfo decodedValue
;
6076 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
6077 if (decodedValue
.tag
== ASN1_OCTET_STRING
) {
6078 require_quiet(decodedValue
.content
.length
== 32, out
);
6079 extensionData
= CFDataCreate(NULL
, decodedValue
.content
.data
,
6080 decodedValue
.content
.length
);
6082 require_quiet(extensionValue
->data
[33] == 0x00 &&
6083 extensionValue
->data
[32] == 0x00, out
);
6084 extensionData
= CFDataCreate(NULL
, extensionValue
->data
, 32);
6087 return extensionData
;
6091 /* From iapd IAPAuthenticationTypes.h */
6092 typedef struct IapCertSerialNumber
6094 uint8_t xservID
; // Xserver ID
6095 uint8_t hsmID
; // Hardware security module ID (generated cert)
6096 uint8_t delimiter01
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6097 uint8_t dateYear
; // Date year cert was issued
6098 uint8_t dateMonth
; // Date month cert was issued
6099 uint8_t dateDay
; // Date day cert was issued
6100 uint8_t delimiter02
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6101 uint8_t devClass
; // iAP device class (maps to lingo permissions)
6102 uint8_t delimiter03
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6103 uint8_t batchNumHi
; // Batch number high byte (15:08)
6104 uint8_t batchNumLo
; // Batch number low byte (07:00)
6105 uint8_t delimiter04
; // Field delimiter (IAP_CERT_FIELD_DELIMITER)
6106 uint8_t serialNumHi
; // Serial number high byte (23:16)
6107 uint8_t serialNumMid
; // Serial number middle byte (15:08)
6108 uint8_t serialNumLo
; // Serial number low byte (07:00)
6110 } IapCertSerialNumber_t
, *pIapCertSerialNumber_t
;
6113 #define IAP_CERT_FIELD_DELIMITER 0xAA // "Apple_Accessory" delimiter
6114 SeciAuthVersion
SecCertificateGetiAuthVersion(SecCertificateRef certificate
) {
6116 return kSeciAuthInvalid
;
6118 if (NULL
!= SecCertificateGetExtensionValue(certificate
,
6119 CFSTR("1.2.840.113635.100.6.36"))) {
6120 /* v3 Capabilities Extension */
6121 return kSeciAuthVersion3
;
6123 if (NULL
!= SecCertificateGetExtensionValue(certificate
,
6124 CFSTR("1.2.840.113635.100.6.59.1"))) {
6125 /* SW Auth General Capabilities Extension */
6126 return kSeciAuthVersionSW
;
6128 DERItem serialNumber
= certificate
->_serialNum
;
6129 require_quiet(serialNumber
.data
, out
);
6130 require_quiet(serialNumber
.length
== 15, out
);
6131 require_quiet(serialNumber
.data
[2] == IAP_CERT_FIELD_DELIMITER
&&
6132 serialNumber
.data
[6] == IAP_CERT_FIELD_DELIMITER
&&
6133 serialNumber
.data
[8] == IAP_CERT_FIELD_DELIMITER
&&
6134 serialNumber
.data
[11] == IAP_CERT_FIELD_DELIMITER
, out
);
6135 return kSeciAuthVersion2
;
6137 return kSeciAuthInvalid
;
6140 static CFStringRef
SecCertificateiAPSWAuthCapabilitiesTypeToOID(SeciAPSWAuthCapabilitiesType type
) {
6141 CFStringRef extensionOID
= NULL
;
6142 /* Get the oid for the type */
6143 if (type
== kSeciAPSWAuthGeneralCapabilities
) {
6144 extensionOID
= CFSTR("1.2.840.113635.100.6.59.1");
6145 } else if (type
== kSeciAPSWAuthAirPlayCapabilities
) {
6146 extensionOID
= CFSTR("1.2.840.113635.100.6.59.2");
6147 } else if (type
== kSeciAPSWAuthHomeKitCapabilities
) {
6148 extensionOID
= CFSTR("1.2.840.113635.100.6.59.3");
6150 return extensionOID
;
6153 CFDataRef
SecCertificateCopyiAPSWAuthCapabilities(SecCertificateRef certificate
, SeciAPSWAuthCapabilitiesType type
) {
6157 CFDataRef extensionData
= NULL
;
6158 DERItem
*extensionValue
= NULL
;
6159 CFStringRef extensionOID
= SecCertificateiAPSWAuthCapabilitiesTypeToOID(type
);
6160 require_quiet(extensionOID
, out
);
6161 extensionValue
= SecCertificateGetExtensionValue(certificate
, extensionOID
);
6162 require_quiet(extensionValue
, out
);
6163 /* The extension is a octet string containing the DER-encoded variable-length octet string */
6164 DERDecodedInfo decodedValue
;
6165 require_noerr_quiet(DERDecodeItem(extensionValue
, &decodedValue
), out
);
6166 if (decodedValue
.tag
== ASN1_OCTET_STRING
) {
6167 extensionData
= CFDataCreate(NULL
, decodedValue
.content
.data
,
6168 decodedValue
.content
.length
);
6171 return extensionData
;
6174 SecCertificateRef
SecCertificateCreateWithPEM(CFAllocatorRef allocator
,
6175 CFDataRef pem_certificate
)
6177 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
6178 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
6179 uint8_t *base64_data
= NULL
;
6180 SecCertificateRef cert
= NULL
;
6181 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
6182 //const size_t length = CFDataGetLength(pem_certificate);
6183 char *begin
= strstr((const char *)data
, begin_cert
);
6184 char *end
= strstr((const char *)data
, end_cert
);
6187 begin
+= sizeof(begin_cert
) - 1;
6188 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
6189 if (base64_length
&& (base64_length
< (size_t)CFDataGetLength(pem_certificate
))) {
6190 require_quiet(base64_data
= calloc(1, base64_length
), out
);
6191 require_action_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
, free(base64_data
));
6192 cert
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, base64_data
, base64_length
);
6201 // -- MARK -- XPC encoding/decoding
6204 bool SecCertificateAppendToXPCArray(SecCertificateRef certificate
, xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
6206 return true; // NOOP
6208 size_t length
= SecCertificateGetLength(certificate
);
6209 const uint8_t *bytes
= SecCertificateGetBytePtr(certificate
);
6210 #if SECTRUST_VERBOSE_DEBUG
6211 secerror("cert=0x%lX length=%d bytes=0x%lX", (uintptr_t)certificate
, (int)length
, (uintptr_t)bytes
);
6213 if (!length
|| !bytes
) {
6214 return SecError(errSecParam
, error
, CFSTR("failed to der encode certificate"));
6216 xpc_array_set_data(xpc_certificates
, XPC_ARRAY_APPEND
, bytes
, length
);
6220 SecCertificateRef
SecCertificateCreateWithXPCArrayAtIndex(xpc_object_t xpc_certificates
, size_t index
, CFErrorRef
*error
) {
6221 SecCertificateRef certificate
= NULL
;
6223 const uint8_t *bytes
= xpc_array_get_data(xpc_certificates
, index
, &length
);
6225 certificate
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, bytes
, length
);
6228 SecError(errSecParam
, error
, CFSTR("certificates[%zu] failed to decode"), index
);
6233 xpc_object_t
SecCertificateArrayCopyXPCArray(CFArrayRef certificates
, CFErrorRef
*error
) {
6234 xpc_object_t xpc_certificates
;
6235 require_action_quiet(xpc_certificates
= xpc_array_create(NULL
, 0), exit
,
6236 SecError(errSecAllocate
, error
, CFSTR("failed to create xpc_array")));
6237 CFIndex ix
, count
= CFArrayGetCount(certificates
);
6238 for (ix
= 0; ix
< count
; ++ix
) {
6239 SecCertificateRef certificate
= (SecCertificateRef
) CFArrayGetValueAtIndex(certificates
, ix
);
6240 #if SECTRUST_VERBOSE_DEBUG
6241 CFIndex length
= SecCertificateGetLength(certificate
);
6242 const UInt8
*bytes
= SecCertificateGetBytePtr(certificate
);
6243 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
);
6245 if (!SecCertificateAppendToXPCArray(certificate
, xpc_certificates
, error
)) {
6246 xpc_release(xpc_certificates
);
6247 xpc_certificates
= NULL
;
6253 return xpc_certificates
;
6256 CFArrayRef
SecCertificateXPCArrayCopyArray(xpc_object_t xpc_certificates
, CFErrorRef
*error
) {
6257 CFMutableArrayRef certificates
= NULL
;
6258 require_action_quiet(xpc_get_type(xpc_certificates
) == XPC_TYPE_ARRAY
, exit
,
6259 SecError(errSecParam
, error
, CFSTR("certificates xpc value is not an array")));
6260 size_t count
= xpc_array_get_count(xpc_certificates
);
6261 require_action_quiet(certificates
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
), exit
,
6262 SecError(errSecAllocate
, error
, CFSTR("failed to create CFArray of capacity %zu"), count
));
6265 for (ix
= 0; ix
< count
; ++ix
) {
6266 SecCertificateRef cert
= SecCertificateCreateWithXPCArrayAtIndex(xpc_certificates
, ix
, error
);
6268 CFRelease(certificates
);
6271 CFArraySetValueAtIndex(certificates
, ix
, cert
);
6276 return certificates
;
6279 #define do_if_registered(sdp, ...) if (gTrustd && gTrustd->sdp) { return gTrustd->sdp(__VA_ARGS__); }
6282 static CFArrayRef
CopyEscrowCertificates(SecCertificateEscrowRootType escrowRootType
, CFErrorRef
* error
)
6284 __block CFArrayRef result
= NULL
;
6286 do_if_registered(ota_CopyEscrowCertificates
, escrowRootType
, error
);
6288 securityd_send_sync_and_do(kSecXPCOpOTAGetEscrowCertificates
, error
,
6289 ^bool(xpc_object_t message
, CFErrorRef
*error
)
6291 xpc_dictionary_set_uint64(message
, "escrowType", (uint64_t)escrowRootType
);
6294 ^bool(xpc_object_t response
, CFErrorRef
*error
)
6296 xpc_object_t xpc_array
= xpc_dictionary_get_value(response
, kSecXPCKeyResult
);
6298 if (response
&& (NULL
!= xpc_array
)) {
6299 result
= (CFArrayRef
)_CFXPCCreateCFObjectFromXPCObject(xpc_array
);
6302 return SecError(errSecInternal
, error
, CFSTR("Did not get the Escrow certificates"));
6304 return result
!= NULL
;
6309 CFArrayRef
SecCertificateCopyEscrowRoots(SecCertificateEscrowRootType escrowRootType
)
6311 CFArrayRef result
= NULL
;
6313 CFDataRef certData
= NULL
;
6316 if (kSecCertificateBaselineEscrowRoot
== escrowRootType
||
6317 kSecCertificateBaselinePCSEscrowRoot
== escrowRootType
||
6318 kSecCertificateBaselineEscrowBackupRoot
== escrowRootType
||
6319 kSecCertificateBaselineEscrowEnrollmentRoot
== escrowRootType
)
6321 // The request is for the base line certificates.
6322 // Use the hard coded data to generate the return array.
6323 struct RootRecord
** pEscrowRoots
;
6324 switch (escrowRootType
) {
6325 case kSecCertificateBaselineEscrowRoot
:
6326 numRoots
= kNumberOfBaseLineEscrowRoots
;
6327 pEscrowRoots
= kBaseLineEscrowRoots
;
6329 case kSecCertificateBaselinePCSEscrowRoot
:
6330 numRoots
= kNumberOfBaseLinePCSEscrowRoots
;
6331 pEscrowRoots
= kBaseLinePCSEscrowRoots
;
6333 case kSecCertificateBaselineEscrowBackupRoot
:
6334 numRoots
= kNumberOfBaseLineEscrowBackupRoots
;
6335 pEscrowRoots
= kBaseLineEscrowBackupRoots
;
6337 case kSecCertificateBaselineEscrowEnrollmentRoot
:
6339 numRoots
= kNumberOfBaseLineEscrowEnrollmentRoots
;
6340 pEscrowRoots
= kBaseLineEscrowEnrollmentRoots
;
6344 // Get the hard coded set of roots
6345 SecCertificateRef baseLineCerts
[numRoots
];
6346 struct RootRecord
* pRootRecord
= NULL
;
6348 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6349 pRootRecord
= pEscrowRoots
[iCnt
];
6350 if (NULL
!= pRootRecord
&& pRootRecord
->_length
> 0 && NULL
!= pRootRecord
->_bytes
) {
6351 certData
= CFDataCreate(kCFAllocatorDefault
, pRootRecord
->_bytes
, pRootRecord
->_length
);
6352 if (NULL
!= certData
) {
6353 baseLineCerts
[iCnt
] = SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
6354 CFRelease(certData
);
6358 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)baseLineCerts
, numRoots
, &kCFTypeArrayCallBacks
);
6359 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6360 if (NULL
!= baseLineCerts
[iCnt
]) {
6361 CFRelease(baseLineCerts
[iCnt
]);
6366 // The request is for the current certificates.
6367 CFErrorRef error
= NULL
;
6368 CFArrayRef cert_datas
= CopyEscrowCertificates(escrowRootType
, &error
);
6369 if (NULL
!= error
|| NULL
== cert_datas
) {
6370 if (NULL
!= error
) {
6373 if (NULL
!= cert_datas
) {
6374 CFRelease(cert_datas
);
6379 numRoots
= (int)(CFArrayGetCount(cert_datas
));
6381 SecCertificateRef assetCerts
[numRoots
];
6382 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6383 certData
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, iCnt
);
6384 if (NULL
!= certData
) {
6385 SecCertificateRef aCertRef
= SecCertificateCreateWithData(kCFAllocatorDefault
, certData
);
6386 assetCerts
[iCnt
] = aCertRef
;
6389 assetCerts
[iCnt
] = NULL
;
6394 result
= CFArrayCreate(kCFAllocatorDefault
, (const void **)assetCerts
, numRoots
, &kCFTypeArrayCallBacks
);
6395 for (iCnt
= 0; iCnt
< numRoots
; iCnt
++) {
6396 if (NULL
!= assetCerts
[iCnt
]) {
6397 CFRelease(assetCerts
[iCnt
]);
6401 CFReleaseSafe(cert_datas
);
6406 SEC_CONST_DECL (kSecSignatureDigestAlgorithmUnknown
, "SignatureDigestUnknown");
6407 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD2
, "SignatureDigestMD2");
6408 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD4
, "SignatureDigestMD4");
6409 SEC_CONST_DECL (kSecSignatureDigestAlgorithmMD5
, "SignatureDigestMD5");
6410 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA1
, "SignatureDigestSHA1");
6411 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA224
, "SignatureDigestSHA224");
6412 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA256
, "SignatureDigestSHA256");
6413 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA384
, "SignatureDigestSHA284");
6414 SEC_CONST_DECL (kSecSignatureDigestAlgorithmSHA512
, "SignatureDigestSHA512");
6416 SecSignatureHashAlgorithm
SecCertificateGetSignatureHashAlgorithm(SecCertificateRef certificate
)
6418 SecSignatureHashAlgorithm result
= kSecSignatureHashAlgorithmUnknown
;
6419 DERAlgorithmId
*algId
= (certificate
) ? &certificate
->_tbsSigAlg
: NULL
;
6420 const DERItem
*algOid
= (algId
) ? &algId
->oid
: NULL
;
6422 if (!algOid
->data
|| !algOid
->length
) {
6425 /* classify the signature algorithm OID into one of our known types */
6426 if (DEROidCompare(algOid
, &oidSha512Ecdsa
) ||
6427 DEROidCompare(algOid
, &oidSha512Rsa
) ||
6428 DEROidCompare(algOid
, &oidSha512
)) {
6429 result
= kSecSignatureHashAlgorithmSHA512
;
6432 if (DEROidCompare(algOid
, &oidSha384Ecdsa
) ||
6433 DEROidCompare(algOid
, &oidSha384Rsa
) ||
6434 DEROidCompare(algOid
, &oidSha384
)) {
6435 result
= kSecSignatureHashAlgorithmSHA384
;
6438 if (DEROidCompare(algOid
, &oidSha256Ecdsa
) ||
6439 DEROidCompare(algOid
, &oidSha256Rsa
) ||
6440 DEROidCompare(algOid
, &oidSha256
)) {
6441 result
= kSecSignatureHashAlgorithmSHA256
;
6444 if (DEROidCompare(algOid
, &oidSha224Ecdsa
) ||
6445 DEROidCompare(algOid
, &oidSha224Rsa
) ||
6446 DEROidCompare(algOid
, &oidSha224
)) {
6447 result
= kSecSignatureHashAlgorithmSHA224
;
6450 if (DEROidCompare(algOid
, &oidSha1Ecdsa
) ||
6451 DEROidCompare(algOid
, &oidSha1Rsa
) ||
6452 DEROidCompare(algOid
, &oidSha1Dsa
) ||
6453 DEROidCompare(algOid
, &oidSha1DsaOIW
) ||
6454 DEROidCompare(algOid
, &oidSha1DsaCommonOIW
) ||
6455 DEROidCompare(algOid
, &oidSha1RsaOIW
) ||
6456 DEROidCompare(algOid
, &oidSha1Fee
) ||
6457 DEROidCompare(algOid
, &oidSha1
)) {
6458 result
= kSecSignatureHashAlgorithmSHA1
;
6461 if (DEROidCompare(algOid
, &oidMd5Rsa
) ||
6462 DEROidCompare(algOid
, &oidMd5Fee
) ||
6463 DEROidCompare(algOid
, &oidMd5
)) {
6464 result
= kSecSignatureHashAlgorithmMD5
;
6467 if (DEROidCompare(algOid
, &oidMd4Rsa
) ||
6468 DEROidCompare(algOid
, &oidMd4
)) {
6469 result
= kSecSignatureHashAlgorithmMD4
;
6472 if (DEROidCompare(algOid
, &oidMd2Rsa
) ||
6473 DEROidCompare(algOid
, &oidMd2
)) {
6474 result
= kSecSignatureHashAlgorithmMD2
;
6483 CFArrayRef
SecCertificateCopyiPhoneDeviceCAChain(void) {
6484 CFMutableArrayRef result
= NULL
;
6485 SecCertificateRef iPhoneDeviceCA
= NULL
, iPhoneCA
= NULL
, appleRoot
= NULL
;
6487 require_quiet(iPhoneDeviceCA
= SecCertificateCreateWithBytes(NULL
, _AppleiPhoneDeviceCA
, sizeof(_AppleiPhoneDeviceCA
)),
6489 require_quiet(iPhoneCA
= SecCertificateCreateWithBytes(NULL
, _AppleiPhoneCA
, sizeof(_AppleiPhoneCA
)),
6491 require_quiet(appleRoot
= SecCertificateCreateWithBytes(NULL
, _AppleRootCA
, sizeof(_AppleRootCA
)),
6494 require_quiet(result
= CFArrayCreateMutable(NULL
, 3, &kCFTypeArrayCallBacks
), errOut
);
6495 CFArrayAppendValue(result
, iPhoneDeviceCA
);
6496 CFArrayAppendValue(result
, iPhoneCA
);
6497 CFArrayAppendValue(result
, appleRoot
);
6500 CFReleaseNull(iPhoneDeviceCA
);
6501 CFReleaseNull(iPhoneCA
);
6502 CFReleaseNull(appleRoot
);