2 * Copyright (c) 2006-2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecCertificate.c - CoreFoundation based certificate object
29 #include "SecCertificateInternalP.h"
31 #include <CommonCrypto/CommonDigest.h>
32 #include <CoreFoundation/CFRuntime.h>
33 #include <CoreFoundation/CFString.h>
34 #include <CoreFoundation/CFBundle.h>
35 #include <CoreFoundation/CFDictionary.h>
36 #include <CoreFoundation/CFNumber.h>
37 #include <CoreFoundation/CFTimeZone.h>
40 #include <AssertMacros.h>
41 #include <libDER/libDER.h>
42 #include <libDER/DER_CertCrl.h>
43 #include <libDER/DER_Encode.h>
44 #include <libDER/DER_Keys.h>
45 #include <libDER/asn1Types.h>
46 #include <libDER/oidsPriv.h>
48 #include "SecBasePriv.h"
50 #include "SecRSAKeyP.h"
51 #include "SecFrameworkP.h"
53 #include "SecItemPriv.h"
56 #include <libkern/OSByteOrder.h>
58 #include "SecInternal.h"
59 #include "SecBase64P.h"
61 #include <security_utilities/debugging.h>
63 typedef struct SecCertificateExtension
{
67 } SecCertificateExtension
;
70 typedef struct KnownExtension
{
76 kSecSelfSignedUnknown
= 0,
82 struct __SecCertificate
{
85 DERItem _der
; /* Entire certificate in DER form. */
86 DERItem _tbs
; /* To Be Signed cert DER bytes. */
87 DERAlgorithmId _sigAlg
; /* Top level signature algorithm. */
88 DERItem _signature
; /* The content of the sig bit string. */
91 DERItem _serialNum
; /* Integer. */
92 DERAlgorithmId _tbsSigAlg
; /* sig alg MUST be same as _sigAlg. */
93 DERItem _issuer
; /* Sequence of RDN. */
94 CFAbsoluteTime _notBefore
;
95 CFAbsoluteTime _notAfter
;
96 DERItem _subject
; /* Sequence of RDN. */
97 DERAlgorithmId _algId
; /* oid and params of _pubKeyDER. */
98 DERItem _pubKeyDER
; /* contents of bit string */
99 DERItem _issuerUniqueID
; /* bit string, optional */
100 DERItem _subjectUniqueID
; /* bit string, optional */
103 /* Known extensions if the certificate contains them,
104 extnValue.length will be > 0. */
105 KnownExtension _authorityKeyID
;
107 /* This extension is used to uniquely identify a certificate from among
108 several that have the same subject name. If the extension is not
109 present, its value is calculated by performing a SHA-1 hash of the
110 certificate's DER encoded subjectPublicKeyInfo, as recommended by
112 KnownExtension _subjectKeyID
;
113 KnownExtension _keyUsage
;
114 KnownExtension _extendedKeyUsage
;
115 KnownExtension _basicConstraints
;
116 KnownExtension _netscapeCertType
;
117 KnownExtension _subjectAltName
;
118 KnownExtension _qualCertStatements
;
121 bool _foundUnknownCriticalExtension
;
123 /* Well known certificate extensions. */
124 SecCEBasicConstraints _basicConstraints
;
125 SecCEPolicyConstraints _policyConstraints
;
126 CFDictionaryRef _policyMappings
;
127 SecCECertificatePolicies _certificatePolicies
;
129 /* If InhibitAnyPolicy extension is not present or invalid UINT32_MAX,
130 value of the SkipCerts field of the InhibitAnyPolicy extension
132 uint32_t _inhibitAnyPolicySkipCerts
;
134 /* If KeyUsage extension is not present this is 0, otherwise it's
135 the value of the extension. */
136 SecKeyUsage _keyUsage
;
138 /* OCTECTS of SubjectKeyIdentifier extensions KeyIdentifier.
139 Length = 0 if not present. */
140 DERItem _subjectKeyIdentifier
;
142 /* OCTECTS of AuthorityKeyIdentifier extensions KeyIdentifier.
143 Length = 0 if not present. */
144 DERItem _authorityKeyIdentifier
;
145 /* AuthorityKeyIdentifier extension _authorityKeyIdentifierIssuer and
146 _authorityKeyIdentifierSerialNumber have non zero length if present.
147 Both are either present or absent together. */
148 DERItem _authorityKeyIdentifierIssuer
;
149 DERItem _authorityKeyIdentifierSerialNumber
;
151 /* Subject alt name extension, if present. Not malloced, it's just a
152 pointer to an element in the _extensions array. */
153 const SecCertificateExtension
*_subjectAltName
;
155 /* Parsed extension values. */
157 /* Array of CFURLRefs containing the URI values of crlDistributionPoints. */
158 CFMutableArrayRef _crlDistributionPoints
;
160 /* Array of CFURLRefs containing the URI values of accessLocations of each
161 id-ad-ocsp AccessDescription in the Authority Information Access
163 CFMutableArrayRef _ocspResponders
;
165 /* Array of CFURLRefs containing the URI values of accessLocations of each
166 id-ad-caIssuers AccessDescription in the Authority Information Access
168 CFMutableArrayRef _caIssuers
;
170 /* All other (non known) extensions. The _extensions array is malloced. */
171 CFIndex _extensionCount
;
172 SecCertificateExtension
*_extensions
;
174 /* Optional cached fields. */
177 CFArrayRef _properties
;
178 CFDataRef _serialNumber
;
179 CFDataRef _normalizedIssuer
;
180 CFDataRef _normalizedSubject
;
181 CFDataRef _authorityKeyID
;
182 CFDataRef _subjectKeyID
;
184 CFDataRef _sha1Digest
;
185 uint8_t _isSelfSigned
;
189 /* Public Constants for property list keys. */
190 const CFStringRef kSecPropertyKeyType
= CFSTR("type");
191 const CFStringRef kSecPropertyKeyLabel
= CFSTR("label");
192 const CFStringRef kSecPropertyKeyLocalizedLabel
= CFSTR("localized label");
193 const CFStringRef kSecPropertyKeyValue
= CFSTR("value");
195 /* Public Constants for property list values. */
196 const CFStringRef kSecPropertyTypeWarning
= CFSTR("warning");
197 const CFStringRef kSecPropertyTypeError
= CFSTR("error");
198 const CFStringRef kSecPropertyTypeSuccess
= CFSTR("success");
199 const CFStringRef kSecPropertyTypeTitle
= CFSTR("title");
200 const CFStringRef kSecPropertyTypeSection
= CFSTR("section");
201 const CFStringRef kSecPropertyTypeData
= CFSTR("data");
202 const CFStringRef kSecPropertyTypeString
= CFSTR("string");
203 const CFStringRef kSecPropertyTypeURL
= CFSTR("url");
204 const CFStringRef kSecPropertyTypeDate
= CFSTR("date");
206 /* Extension parsing routine. */
207 typedef void (*SecCertificateExtensionParser
)(SecCertificateRefP certificate
,
208 const SecCertificateExtension
*extn
);
210 /* CFRuntime regsitration data. */
211 static pthread_once_t kSecCertificateRegisterClass
= PTHREAD_ONCE_INIT
;
212 static CFTypeID kSecCertificateTypeID
= _kCFRuntimeNotATypeID
;
214 /* Mapping from extension OIDs (as a DERItem *) to
215 SecCertificateExtensionParser extension parsing routines. */
216 static CFDictionaryRef gExtensionParsers
;
218 /* Forward declartions of static functions. */
219 static CFStringRef
SecCertificateCopyDescription(CFTypeRef cf
);
220 static void SecCertificateDestroy(CFTypeRef cf
);
221 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
222 CFAbsoluteTime
*absTime
);
224 /* Static functions. */
225 static CFStringRef
SecCertificateCopyDescription(CFTypeRef cf
) {
226 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
227 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
228 CFSTR("<cert(%p) s: %@ i: %@>"), certificate
,
229 SecCertificateCopySubjectSummaryP(certificate
),
230 SecCertificateCopyIssuerSummaryP(certificate
));
233 static void SecCertificateDestroy(CFTypeRef cf
) {
234 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
235 if (certificate
->_certificatePolicies
.policies
) {
236 free(certificate
->_certificatePolicies
.policies
);
237 certificate
->_certificatePolicies
.policies
= NULL
;
239 CFReleaseNull(certificate
->_policyMappings
);
240 CFReleaseNull(certificate
->_crlDistributionPoints
);
241 CFReleaseNull(certificate
->_ocspResponders
);
242 CFReleaseNull(certificate
->_caIssuers
);
243 if (certificate
->_extensions
) {
244 free(certificate
->_extensions
);
245 certificate
->_extensions
= NULL
;
247 CFReleaseNull(certificate
->_pubKey
);
248 CFReleaseNull(certificate
->_der_data
);
249 CFReleaseNull(certificate
->_properties
);
250 CFReleaseNull(certificate
->_serialNumber
);
251 CFReleaseNull(certificate
->_normalizedIssuer
);
252 CFReleaseNull(certificate
->_normalizedSubject
);
253 CFReleaseNull(certificate
->_authorityKeyID
);
254 CFReleaseNull(certificate
->_subjectKeyID
);
255 CFReleaseNull(certificate
->_sha1Digest
);
258 static Boolean
SecCertificateEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
259 SecCertificateRefP cert1
= (SecCertificateRefP
)cf1
;
260 SecCertificateRefP cert2
= (SecCertificateRefP
)cf2
;
263 if (!cert2
|| cert1
->_der
.length
!= cert2
->_der
.length
)
265 return !memcmp(cert1
->_der
.data
, cert2
->_der
.data
, cert1
->_der
.length
);
268 /* Hash of the certificate is der length + signature length + last 4 bytes
270 static CFHashCode
SecCertificateHash(CFTypeRef cf
) {
271 SecCertificateRefP certificate
= (SecCertificateRefP
)cf
;
272 DERSize der_length
= certificate
->_der
.length
;
273 DERSize sig_length
= certificate
->_signature
.length
;
274 DERSize ix
= (sig_length
> 4) ? sig_length
- 4 : 0;
275 CFHashCode hashCode
= 0;
276 for (; ix
< sig_length
; ++ix
)
277 hashCode
= (hashCode
<< 8) + certificate
->_signature
.data
[ix
];
279 return (hashCode
+ der_length
+ sig_length
);
284 /************************************************************************/
285 /************************* General Name Parsing *************************/
286 /************************************************************************/
288 typedef OSStatus (*parseGeneralNameCallback
)(void *context
,
289 SecCEGeneralNameType type
, const DERItem
*value
);
293 GeneralName ::= CHOICE {
294 otherName [0] OtherName,
295 rfc822Name [1] IA5String,
296 dNSName [2] IA5String,
297 x400Address [3] ORAddress,
298 directoryName [4] Name,
299 ediPartyName [5] EDIPartyName,
300 uniformResourceIdentifier [6] IA5String,
301 iPAddress [7] OCTET STRING,
302 registeredID [8] OBJECT IDENTIFIER}
304 OtherName ::= SEQUENCE {
305 type-id OBJECT IDENTIFIER,
306 value [0] EXPLICIT ANY DEFINED BY type-id }
308 EDIPartyName ::= SEQUENCE {
309 nameAssigner [0] DirectoryString OPTIONAL,
310 partyName [1] DirectoryString }
312 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
313 const DERItem
*generalNameContent
,
314 void *context
, parseGeneralNameCallback callback
) {
316 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
317 return callback(context
, GNT_OtherName
, generalNameContent
);
318 case ASN1_CONTEXT_SPECIFIC
| 1:
319 return callback(context
, GNT_RFC822Name
, generalNameContent
);
320 case ASN1_CONTEXT_SPECIFIC
| 2:
321 return callback(context
, GNT_DNSName
, generalNameContent
);
322 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
323 return callback(context
, GNT_X400Address
, generalNameContent
);
324 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
325 return callback(context
, GNT_DirectoryName
, generalNameContent
);
326 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
327 return callback(context
, GNT_EdiPartyName
, generalNameContent
);
328 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
330 /* Technically I don't think this is valid, but there are certs out
331 in the wild that use a constructed IA5String. In particular the
332 VeriSign Time Stamping Authority CA.cer does this. */
333 DERDecodedInfo uriContent
;
334 require_noerr(DERDecodeItem(generalNameContent
, &uriContent
), badDER
);
335 require(uriContent
.tag
== ASN1_IA5_STRING
, badDER
);
336 return callback(context
, GNT_URI
, &uriContent
.content
);
338 case ASN1_CONTEXT_SPECIFIC
| 6:
339 return callback(context
, GNT_URI
, generalNameContent
);
340 case ASN1_CONTEXT_SPECIFIC
| 7:
341 return callback(context
, GNT_IPAddress
, generalNameContent
);
342 case ASN1_CONTEXT_SPECIFIC
| 8:
343 return callback(context
, GNT_RegisteredID
, generalNameContent
);
348 return errSecInvalidCertificate
;
351 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
352 void *context
, parseGeneralNameCallback callback
) {
354 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
355 require_noerr_quiet(drtn
, badDER
);
356 DERDecodedInfo generalNameContent
;
357 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
359 OSStatus status
= parseGeneralNameContentProperty(
360 generalNameContent
.tag
, &generalNameContent
.content
, context
,
365 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
366 return errSecSuccess
;
369 return errSecInvalidCertificate
;
372 static OSStatus
parseGeneralNames(const DERItem
*generalNames
, void *context
,
373 parseGeneralNameCallback callback
) {
374 DERDecodedInfo generalNamesContent
;
375 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
376 require_noerr_quiet(drtn
, badDER
);
377 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
378 return parseGeneralNamesContent(&generalNamesContent
.content
, context
,
381 return errSecInvalidCertificate
;
387 GeneralName ::= CHOICE {
388 otherName [0] OtherName,
389 rfc822Name [1] IA5String,
390 dNSName [2] IA5String,
391 x400Address [3] ORAddress,
392 directoryName [4] Name,
393 ediPartyName [5] EDIPartyName,
394 uniformResourceIdentifier [6] IA5String,
395 iPAddress [7] OCTET STRING,
396 registeredID [8] OBJECT IDENTIFIER}
398 EDIPartyName ::= SEQUENCE {
399 nameAssigner [0] DirectoryString OPTIONAL,
400 partyName [1] DirectoryString }
402 static OSStatus
parseGeneralNameContentProperty(DERTag tag
,
403 const DERItem
*generalNameContent
, SecCEGeneralName
*generalName
) {
405 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
406 generalName
->nameType
= GNT_OtherName
;
407 generalName
->berEncoded
= true;
408 generalName
->name
= *generalNameContent
;
410 case ASN1_CONTEXT_SPECIFIC
| 1:
412 generalName
->nameType
= GNT_RFC822Name
;
413 generalName
->berEncoded
= false;
414 generalName
->name
= *generalNameContent
;
416 case ASN1_CONTEXT_SPECIFIC
| 2:
418 generalName
->nameType
= GNT_DNSName
;
419 generalName
->berEncoded
= false;
420 generalName
->name
= *generalNameContent
;
422 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
423 generalName
->nameType
= GNT_X400Address
;
424 generalName
->berEncoded
= true;
425 generalName
->name
= *generalNameContent
;
427 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
428 generalName
->nameType
= GNT_DirectoryName
;
429 generalName
->berEncoded
= true;
430 generalName
->name
= *generalNameContent
;
432 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
433 generalName
->nameType
= GNT_EdiPartyName
;
434 generalName
->berEncoded
= true;
435 generalName
->name
= *generalNameContent
;
437 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
439 /* Technically I don't think this is valid, but there are certs out
440 in the wild that use a constructed IA5String. In particular the
441 VeriSign Time Stamping Authority CA.cer does this. */
442 DERDecodedInfo decoded
;
443 require_noerr(DERDecodeItem(generalNameContent
, &decoded
), badDER
);
444 require(decoded
.tag
== ASN1_IA5_STRING
, badDER
);
445 generalName
->nameType
= GNT_URI
;
446 generalName
->berEncoded
= false;
447 generalName
->name
= decoded
.content
;
450 case ASN1_CONTEXT_SPECIFIC
| 6:
451 generalName
->nameType
= GNT_URI
;
452 generalName
->berEncoded
= false;
453 generalName
->name
= *generalNameContent
;
455 case ASN1_CONTEXT_SPECIFIC
| 7:
456 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
457 8 octects, addr/mask for ipv6 it's 32. */
458 generalName
->nameType
= GNT_IPAddress
;
459 generalName
->berEncoded
= false;
460 generalName
->name
= *generalNameContent
;
462 case ASN1_CONTEXT_SPECIFIC
| 8:
463 /* name is the content of an OID. */
464 generalName
->nameType
= GNT_RegisteredID
;
465 generalName
->berEncoded
= false;
466 generalName
->name
= *generalNameContent
;
472 return errSecSuccess
;
474 return errSecInvalidCertificate
;
478 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
480 static OSStatus
parseGeneralNamesContent(const DERItem
*generalNamesContent
,
481 CFIndex
*count
, SecCEGeneralName
**name
) {
482 SecCEGeneralName
*generalNames
= NULL
;
484 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
485 require_noerr_quiet(drtn
, badDER
);
486 DERDecodedInfo generalNameContent
;
487 CFIndex generalNamesCount
= 0;
488 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
492 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
494 require(generalNames
= calloc(generalNamesCount
, sizeof(SecCEGeneralName
)),
496 DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
498 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
500 if (!parseGeneralNameContentProperty(generalNameContent
.tag
,
501 &generalNameContent
.content
, &generalNames
[ix
])) {
506 *count
= generalNamesCount
;
507 *name
= generalNames
;
508 return errSecSuccess
;
513 return errSecInvalidCertificate
;
516 static OSStatus
parseGeneralNames(const DERItem
*generalNames
,
517 CFIndex
*count
, SecCEGeneralName
**name
) {
518 DERDecodedInfo generalNamesContent
;
519 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
520 require_noerr_quiet(drtn
, badDER
);
521 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
523 parseGeneralNamesContent(&generalNamesContent
.content
, count
, name
);
524 return errSecSuccess
;
526 return errSecInvalidCertificate
;
530 /************************************************************************/
531 /************************** X.509 Name Parsing **************************/
532 /************************************************************************/
534 typedef OSStatus (*parseX501NameCallback
)(void *context
, const DERItem
*type
,
535 const DERItem
*value
, CFIndex rdnIX
);
537 static OSStatus
parseRDNContent(const DERItem
*rdnSetContent
, void *context
,
538 parseX501NameCallback callback
) {
540 DERReturn drtn
= DERDecodeSeqContentInit(rdnSetContent
, &rdn
);
541 require_noerr_quiet(drtn
, badDER
);
542 DERDecodedInfo atvContent
;
544 while ((drtn
= DERDecodeSeqNext(&rdn
, &atvContent
)) == DR_Success
) {
545 require_quiet(atvContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
546 DERAttributeTypeAndValue atv
;
547 drtn
= DERParseSequenceContent(&atvContent
.content
,
548 DERNumAttributeTypeAndValueItemSpecs
,
549 DERAttributeTypeAndValueItemSpecs
,
551 require_noerr_quiet(drtn
, badDER
);
552 require_quiet(atv
.type
.length
!= 0, badDER
);
553 OSStatus status
= callback(context
, &atv
.type
, &atv
.value
, rdnIX
++);
557 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
559 return errSecSuccess
;
561 return errSecInvalidCertificate
;
564 static OSStatus
parseX501NameContent(const DERItem
*x501NameContent
, void *context
,
565 parseX501NameCallback callback
) {
567 DERReturn drtn
= DERDecodeSeqContentInit(x501NameContent
, &derSeq
);
568 require_noerr_quiet(drtn
, badDER
);
569 DERDecodedInfo currDecoded
;
570 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
571 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SET
, badDER
);
572 OSStatus status
= parseRDNContent(&currDecoded
.content
, context
,
577 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
579 return errSecSuccess
;
582 return errSecInvalidCertificate
;
585 static OSStatus
parseX501Name(const DERItem
*x501Name
, void *context
,
586 parseX501NameCallback callback
) {
587 DERDecodedInfo x501NameContent
;
588 if (DERDecodeItem(x501Name
, &x501NameContent
) ||
589 x501NameContent
.tag
!= ASN1_CONSTR_SEQUENCE
) {
590 return errSecInvalidCertificate
;
592 return parseX501NameContent(&x501NameContent
.content
, context
,
597 /************************************************************************/
598 /********************** Extension Parsing Routines **********************/
599 /************************************************************************/
601 static void SecCEPSubjectKeyIdentifier(SecCertificateRefP certificate
,
602 const SecCertificateExtension
*extn
) {
603 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
604 DERDecodedInfo keyIdentifier
;
605 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &keyIdentifier
);
606 require_noerr_quiet(drtn
, badDER
);
607 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
608 certificate
->_subjectKeyIdentifier
= keyIdentifier
.content
;
612 secinfo("cert", "Invalid SubjectKeyIdentifier Extension");
615 static void SecCEPKeyUsage(SecCertificateRefP certificate
,
616 const SecCertificateExtension
*extn
) {
617 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
618 SecKeyUsage keyUsage
= extn
->critical
? kSecKeyUsageCritical
: 0;
619 DERDecodedInfo bitStringContent
;
620 DERReturn drtn
= DERDecodeItem(&extn
->extnValue
, &bitStringContent
);
621 require_noerr_quiet(drtn
, badDER
);
622 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
623 DERSize len
= bitStringContent
.content
.length
- 1;
624 require_quiet(len
== 1 || len
== 2, badDER
);
625 DERByte numUnusedBits
= bitStringContent
.content
.data
[0];
626 require_quiet(numUnusedBits
< 8, badDER
);
627 /* Flip the bits in the bit string so the first bit in the lsb. */
628 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
629 uint_fast16_t value
= bitStringContent
.content
.data
[1];
632 value
= (value
<< 8) + bitStringContent
.content
.data
[2];
638 for (ix
= 0; ix
< bits
; ++ix
) {
644 certificate
->_keyUsage
= keyUsage
;
647 certificate
->_keyUsage
= kSecKeyUsageUnspecified
;
650 static void SecCEPPrivateKeyUsagePeriod(SecCertificateRefP certificate
,
651 const SecCertificateExtension
*extn
) {
652 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
655 static void SecCEPSubjectAltName(SecCertificateRefP certificate
,
656 const SecCertificateExtension
*extn
) {
657 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
658 certificate
->_subjectAltName
= extn
;
661 static void SecCEPIssuerAltName(SecCertificateRefP certificate
,
662 const SecCertificateExtension
*extn
) {
663 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
666 static void SecCEPBasicConstraints(SecCertificateRefP certificate
,
667 const SecCertificateExtension
*extn
) {
668 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
669 DERBasicConstraints basicConstraints
;
670 require_noerr_quiet(DERParseSequence(&extn
->extnValue
,
671 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
672 &basicConstraints
, sizeof(basicConstraints
)), badDER
);
673 require_noerr_quiet(DERParseBooleanWithDefault(&basicConstraints
.cA
, false,
674 &certificate
->_basicConstraints
.isCA
), badDER
);
675 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
676 require_noerr_quiet(DERParseInteger(
677 &basicConstraints
.pathLenConstraint
,
678 &certificate
->_basicConstraints
.pathLenConstraint
), badDER
);
679 certificate
->_basicConstraints
.pathLenConstraintPresent
= true;
681 certificate
->_basicConstraints
.present
= true;
682 certificate
->_basicConstraints
.critical
= extn
->critical
;
685 certificate
->_basicConstraints
.present
= false;
686 secinfo("cert", "Invalid BasicConstraints Extension");
689 static void SecCEPCrlDistributionPoints(SecCertificateRefP certificate
,
690 const SecCertificateExtension
*extn
) {
691 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
695 certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
697 PolicyInformation ::= SEQUENCE {
698 policyIdentifier CertPolicyId,
699 policyQualifiers SEQUENCE SIZE (1..MAX) OF
700 PolicyQualifierInfo OPTIONAL }
702 CertPolicyId ::= OBJECT IDENTIFIER
704 PolicyQualifierInfo ::= SEQUENCE {
705 policyQualifierId PolicyQualifierId,
706 qualifier ANY DEFINED BY policyQualifierId }
708 /* maximum number of policies of 8192 seems more than adequate */
709 #define MAX_CERTIFICATE_POLICIES 8192
710 static void SecCEPCertificatePolicies(SecCertificateRefP certificate
,
711 const SecCertificateExtension
*extn
) {
712 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
715 SecCEPolicyInformation
*policies
= NULL
;
716 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
717 require_noerr_quiet(drtn
, badDER
);
718 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
719 DERDecodedInfo piContent
;
720 DERSize policy_count
= 0;
721 while ((policy_count
< MAX_CERTIFICATE_POLICIES
) &&
722 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
723 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
726 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
727 policies
= (SecCEPolicyInformation
*)malloc(sizeof(SecCEPolicyInformation
)
729 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &piSeq
);
730 require_noerr_quiet(drtn
, badDER
);
731 DERSize policy_ix
= 0;
732 while ((policy_ix
< (policy_count
> 0 ? policy_count
: 1)) &&
733 (drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
734 DERPolicyInformation pi
;
735 drtn
= DERParseSequenceContent(&piContent
.content
,
736 DERNumPolicyInformationItemSpecs
,
737 DERPolicyInformationItemSpecs
,
739 require_noerr_quiet(drtn
, badDER
);
740 policies
[policy_ix
].policyIdentifier
= pi
.policyIdentifier
;
741 policies
[policy_ix
++].policyQualifiers
= pi
.policyQualifiers
;
743 certificate
->_certificatePolicies
.present
= true;
744 certificate
->_certificatePolicies
.critical
= extn
->critical
;
745 certificate
->_certificatePolicies
.numPolicies
= (uint32_t)policy_count
;
746 certificate
->_certificatePolicies
.policies
= policies
;
751 certificate
->_certificatePolicies
.present
= false;
752 secinfo("cert", "Invalid CertificatePolicies Extension");
756 id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
758 PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
759 issuerDomainPolicy CertPolicyId,
760 subjectDomainPolicy CertPolicyId }
763 static void SecCEPPolicyMappings(SecCertificateRefP certificate
,
764 const SecCertificateExtension
*extn
) {
765 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
768 SecCEPolicyMapping
*mappings
= NULL
;
769 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
770 require_noerr_quiet(drtn
, badDER
);
771 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
772 DERDecodedInfo pmContent
;
773 DERSize mapping_count
= 0;
774 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
775 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
778 mappings
= (SecCEPolicyMapping
*)malloc(sizeof(SecCEPolicyMapping
)
780 drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
781 require_noerr_quiet(drtn
, badDER
);
782 DERSize mapping_ix
= 0;
783 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
785 drtn
= DERParseSequenceContent(&pmContent
.content
,
786 DERNumPolicyMappingItemSpecs
,
787 DERPolicyMappingItemSpecs
,
789 require_noerr_quiet(drtn
, badDER
);
790 mappings
[mapping_ix
].issuerDomainPolicy
= pm
.issuerDomainPolicy
;
791 mappings
[mapping_ix
++].subjectDomainPolicy
= pm
.subjectDomainPolicy
;
793 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
794 certificate
->_policyMappings
.present
= true;
795 certificate
->_policyMappings
.critical
= extn
->critical
;
796 certificate
->_policyMappings
.numMappings
= mapping_count
;
797 certificate
->_policyMappings
.mappings
= mappings
;
802 CFReleaseSafe(mappings
);
803 certificate
->_policyMappings
.present
= false;
804 secinfo("cert", "Invalid CertificatePolicies Extension");
807 static void SecCEPPolicyMappings(SecCertificateRefP certificate
,
808 const SecCertificateExtension
*extn
) {
809 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
812 CFMutableDictionaryRef mappings
= NULL
;
813 CFDataRef idp
= NULL
, sdp
= NULL
;
814 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &pmSeq
);
815 require_noerr_quiet(drtn
, badDER
);
816 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
817 DERDecodedInfo pmContent
;
818 require_quiet(mappings
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
819 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
),
821 while ((drtn
= DERDecodeSeqNext(&pmSeq
, &pmContent
)) == DR_Success
) {
822 require_quiet(pmContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
824 drtn
= DERParseSequenceContent(&pmContent
.content
,
825 DERNumPolicyMappingItemSpecs
,
826 DERPolicyMappingItemSpecs
,
828 require_noerr_quiet(drtn
, badDER
);
830 require_quiet(idp
= CFDataCreate(kCFAllocatorDefault
,
831 pm
.issuerDomainPolicy
.data
, pm
.issuerDomainPolicy
.length
), badDER
);
832 require_quiet(sdp
= CFDataCreate(kCFAllocatorDefault
,
833 pm
.subjectDomainPolicy
.data
, pm
.subjectDomainPolicy
.length
), badDER
);
834 CFMutableArrayRef sdps
=
835 (CFMutableArrayRef
)CFDictionaryGetValue(mappings
, idp
);
837 CFArrayAppendValue(sdps
, sdp
);
839 require_quiet(sdps
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
840 &kCFTypeArrayCallBacks
), badDER
);
841 CFDictionarySetValue(mappings
, idp
, sdps
);
847 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
848 certificate
->_policyMappings
= mappings
;
853 CFReleaseSafe(mappings
);
854 certificate
->_policyMappings
= NULL
;
855 secinfo("cert", "Invalid CertificatePolicies Extension");
860 AuthorityKeyIdentifier ::= SEQUENCE {
861 keyIdentifier [0] KeyIdentifier OPTIONAL,
862 authorityCertIssuer [1] GeneralNames OPTIONAL,
863 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
864 -- authorityCertIssuer and authorityCertSerialNumber MUST both
865 -- be present or both be absent
867 KeyIdentifier ::= OCTET STRING
869 static void SecCEPAuthorityKeyIdentifier(SecCertificateRefP certificate
,
870 const SecCertificateExtension
*extn
) {
871 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
872 DERAuthorityKeyIdentifier akid
;
874 drtn
= DERParseSequence(&extn
->extnValue
,
875 DERNumAuthorityKeyIdentifierItemSpecs
,
876 DERAuthorityKeyIdentifierItemSpecs
,
877 &akid
, sizeof(akid
));
878 require_noerr_quiet(drtn
, badDER
);
879 if (akid
.keyIdentifier
.length
) {
880 certificate
->_authorityKeyIdentifier
= akid
.keyIdentifier
;
882 if (akid
.authorityCertIssuer
.length
||
883 akid
.authorityCertSerialNumber
.length
) {
884 require_quiet(akid
.authorityCertIssuer
.length
&&
885 akid
.authorityCertSerialNumber
.length
, badDER
);
886 /* Perhaps put in a subsection called Authority Certificate Issuer. */
887 certificate
->_authorityKeyIdentifierIssuer
= akid
.authorityCertIssuer
;
888 certificate
->_authorityKeyIdentifierSerialNumber
= akid
.authorityCertSerialNumber
;
893 secinfo("cert", "Invalid AuthorityKeyIdentifier Extension");
896 static void SecCEPPolicyConstraints(SecCertificateRefP certificate
,
897 const SecCertificateExtension
*extn
) {
898 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
899 DERPolicyConstraints pc
;
901 drtn
= DERParseSequence(&extn
->extnValue
,
902 DERNumPolicyConstraintsItemSpecs
,
903 DERPolicyConstraintsItemSpecs
,
905 require_noerr_quiet(drtn
, badDER
);
906 if (pc
.requireExplicitPolicy
.length
) {
907 require_noerr_quiet(DERParseInteger(
908 &pc
.requireExplicitPolicy
,
909 &certificate
->_policyConstraints
.requireExplicitPolicy
), badDER
);
910 certificate
->_policyConstraints
.requireExplicitPolicyPresent
= true;
912 if (pc
.inhibitPolicyMapping
.length
) {
913 require_noerr_quiet(DERParseInteger(
914 &pc
.inhibitPolicyMapping
,
915 &certificate
->_policyConstraints
.inhibitPolicyMapping
), badDER
);
916 certificate
->_policyConstraints
.inhibitPolicyMappingPresent
= true;
919 certificate
->_policyConstraints
.present
= true;
920 certificate
->_policyConstraints
.critical
= extn
->critical
;
924 certificate
->_policyConstraints
.present
= false;
925 secinfo("cert", "Invalid PolicyConstraints Extension");
928 static void SecCEPExtendedKeyUsage(SecCertificateRefP certificate
,
929 const SecCertificateExtension
*extn
) {
930 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
934 InhibitAnyPolicy ::= SkipCerts
936 SkipCerts ::= INTEGER (0..MAX)
938 static void SecCEPInhibitAnyPolicy(SecCertificateRefP certificate
,
939 const SecCertificateExtension
*extn
) {
940 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
941 require_noerr_quiet(DERParseInteger(
943 &certificate
->_inhibitAnyPolicySkipCerts
), badDER
);
946 certificate
->_inhibitAnyPolicySkipCerts
= UINT32_MAX
;
947 secinfo("cert", "Invalid InhibitAnyPolicy Extension");
951 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
953 AuthorityInfoAccessSyntax ::=
954 SEQUENCE SIZE (1..MAX) OF AccessDescription
956 AccessDescription ::= SEQUENCE {
957 accessMethod OBJECT IDENTIFIER,
958 accessLocation GeneralName }
960 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
962 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
964 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
966 static void SecCEPAuthorityInfoAccess(SecCertificateRefP certificate
,
967 const SecCertificateExtension
*extn
) {
968 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
971 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &adSeq
);
972 require_noerr_quiet(drtn
, badDER
);
973 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
974 DERDecodedInfo adContent
;
975 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
976 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
977 DERAccessDescription ad
;
978 drtn
= DERParseSequenceContent(&adContent
.content
,
979 DERNumAccessDescriptionItemSpecs
,
980 DERAccessDescriptionItemSpecs
,
982 require_noerr_quiet(drtn
, badDER
);
983 CFMutableArrayRef
*urls
;
984 if (DEROidCompare(&ad
.accessMethod
, &oidAdOCSP
))
985 urls
= &certificate
->_ocspResponders
;
986 else if (DEROidCompare(&ad
.accessMethod
, &oidAdCAIssuer
))
987 urls
= &certificate
->_caIssuers
;
991 DERDecodedInfo generalNameContent
;
992 drtn
= DERDecodeItem(&ad
.accessLocation
, &generalNameContent
);
993 require_noerr_quiet(drtn
, badDER
);
994 switch (generalNameContent
.tag
) {
996 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
997 /* Technically I don't think this is valid, but there are certs out
998 in the wild that use a constructed IA5String. In particular the
999 VeriSign Time Stamping Authority CA.cer does this. */
1001 case ASN1_CONTEXT_SPECIFIC
| 6:
1003 CFURLRef url
= CFURLCreateWithBytes(kCFAllocatorDefault
,
1004 generalNameContent
.content
.data
, generalNameContent
.content
.length
,
1005 kCFStringEncodingASCII
, NULL
);
1008 *urls
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1009 CFArrayAppendValue(*urls
, url
);
1015 secinfo("cert", "bad general name for id-ad-ocsp AccessDescription t: 0x%02llx v: %.*s",
1016 generalNameContent
.tag
, (int)generalNameContent
.content
.length
, generalNameContent
.content
.data
);
1020 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1023 secinfo("cert", "failed to parse Authority Information Access extension");
1026 static void SecCEPSubjectInfoAccess(SecCertificateRefP certificate
,
1027 const SecCertificateExtension
*extn
) {
1028 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
1031 static void SecCEPNetscapeCertType(SecCertificateRefP certificate
,
1032 const SecCertificateExtension
*extn
) {
1033 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
1036 static void SecCEPEntrustVersInfo(SecCertificateRefP certificate
,
1037 const SecCertificateExtension
*extn
) {
1038 secinfo("cert", "critical: %s", extn
->critical
? "yes" : "no");
1041 /* Dictionary key callback for comparing to DERItems. */
1042 static Boolean
SecDERItemEqual(const void *value1
, const void *value2
) {
1043 return DEROidCompare((const DERItem
*)value1
, (const DERItem
*)value2
);
1046 /* Dictionary key callback calculating the hash of a DERItem. */
1047 static CFHashCode
SecDERItemHash(const void *value
) {
1048 const DERItem
*derItem
= (const DERItem
*)value
;
1049 CFHashCode hash
= derItem
->length
;
1050 DERSize ix
= derItem
->length
> 8 ? derItem
->length
- 8 : 0;
1051 for (; ix
< derItem
->length
; ++ix
) {
1052 hash
= (hash
<< 9) + (hash
>> 23) + derItem
->data
[ix
];
1058 /* Dictionary key callbacks using the above 2 functions. */
1059 static const CFDictionaryKeyCallBacks SecDERItemKeyCallBacks
= {
1063 NULL
, /* copyDescription */
1064 SecDERItemEqual
, /* equal */
1065 SecDERItemHash
/* hash */
1068 static void SecCertificateRegisterClass(void) {
1069 static const CFRuntimeClass kSecCertificateClass
= {
1071 "SecCertificate", /* class name */
1074 SecCertificateDestroy
, /* dealloc */
1075 SecCertificateEqual
, /* equal */
1076 SecCertificateHash
, /* hash */
1077 NULL
, /* copyFormattingDesc */
1078 SecCertificateCopyDescription
/* copyDebugDesc */
1081 kSecCertificateTypeID
= _CFRuntimeRegisterClass(&kSecCertificateClass
);
1083 /* Build a dictionary that maps from extension OIDs to callback functions
1084 which can parse the extension of the type given. */
1085 static const void *extnOIDs
[] = {
1086 &oidSubjectKeyIdentifier
,
1088 &oidPrivateKeyUsagePeriod
,
1091 &oidBasicConstraints
,
1092 &oidCrlDistributionPoints
,
1093 &oidCertificatePolicies
,
1095 &oidAuthorityKeyIdentifier
,
1096 &oidPolicyConstraints
,
1097 &oidExtendedKeyUsage
,
1098 &oidInhibitAnyPolicy
,
1099 &oidAuthorityInfoAccess
,
1100 &oidSubjectInfoAccess
,
1101 &oidNetscapeCertType
,
1104 static const void *extnParsers
[] = {
1105 SecCEPSubjectKeyIdentifier
,
1107 SecCEPPrivateKeyUsagePeriod
,
1108 SecCEPSubjectAltName
,
1109 SecCEPIssuerAltName
,
1110 SecCEPBasicConstraints
,
1111 SecCEPCrlDistributionPoints
,
1112 SecCEPCertificatePolicies
,
1113 SecCEPPolicyMappings
,
1114 SecCEPAuthorityKeyIdentifier
,
1115 SecCEPPolicyConstraints
,
1116 SecCEPExtendedKeyUsage
,
1117 SecCEPInhibitAnyPolicy
,
1118 SecCEPAuthorityInfoAccess
,
1119 SecCEPSubjectInfoAccess
,
1120 SecCEPNetscapeCertType
,
1121 SecCEPEntrustVersInfo
1123 gExtensionParsers
= CFDictionaryCreate(kCFAllocatorDefault
, extnOIDs
,
1124 extnParsers
, sizeof(extnOIDs
) / sizeof(*extnOIDs
),
1125 &SecDERItemKeyCallBacks
, NULL
);
1128 /* Given the contents of an X.501 Name return the contents of a normalized
1130 static CFDataRef
createNormalizedX501Name(CFAllocatorRef allocator
,
1131 const DERItem
*x501name
) {
1132 CFMutableDataRef result
= CFDataCreateMutable(allocator
, x501name
->length
);
1133 CFIndex length
= x501name
->length
;
1134 CFDataSetLength(result
, length
);
1135 UInt8
*base
= CFDataGetMutableBytePtr(result
);
1138 DERReturn drtn
= DERDecodeSeqContentInit(x501name
, &rdnSeq
);
1140 require_noerr_quiet(drtn
, badDER
);
1143 /* Always points to last rdn tag. */
1144 const DERByte
*rdnTag
= rdnSeq
.nextItem
;
1145 /* Offset relative to base of current rdn set tag. */
1146 CFIndex rdnTagLocation
= 0;
1147 while ((drtn
= DERDecodeSeqNext(&rdnSeq
, &rdn
)) == DR_Success
) {
1148 require_quiet(rdn
.tag
== ASN1_CONSTR_SET
, badDER
);
1149 /* We don't allow empty RDNs. */
1150 require_quiet(rdn
.content
.length
!= 0, badDER
);
1151 /* Length of the tag and length of the current rdn. */
1152 CFIndex rdnTLLength
= rdn
.content
.data
- rdnTag
;
1153 CFIndex rdnContentLength
= rdn
.content
.length
;
1154 /* Copy the tag and length of the RDN. */
1155 memcpy(base
+ rdnTagLocation
, rdnTag
, rdnTLLength
);
1158 drtn
= DERDecodeSeqContentInit(&rdn
.content
, &atvSeq
);
1160 /* Always points to tag of current atv sequence. */
1161 const DERByte
*atvTag
= atvSeq
.nextItem
;
1162 /* Offset relative to base of current atv sequence tag. */
1163 CFIndex atvTagLocation
= rdnTagLocation
+ rdnTLLength
;
1164 while ((drtn
= DERDecodeSeqNext(&atvSeq
, &atv
)) == DR_Success
) {
1165 require_quiet(atv
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
1166 /* Length of the tag and length of the current atv. */
1167 CFIndex atvTLLength
= atv
.content
.data
- atvTag
;
1168 CFIndex atvContentLength
= atv
.content
.length
;
1169 /* Copy the tag and length of the atv and the atv itself. */
1170 memcpy(base
+ atvTagLocation
, atvTag
,
1171 atvTLLength
+ atv
.content
.length
);
1173 /* Now decode the atv sequence. */
1174 DERAttributeTypeAndValue atvPair
;
1175 drtn
= DERParseSequenceContent(&atv
.content
,
1176 DERNumAttributeTypeAndValueItemSpecs
,
1177 DERAttributeTypeAndValueItemSpecs
,
1178 &atvPair
, sizeof(atvPair
));
1179 require_noerr_quiet(drtn
, badDER
);
1180 require_quiet(atvPair
.type
.length
!= 0, badDER
);
1181 DERDecodedInfo value
;
1182 drtn
= DERDecodeItem(&atvPair
.value
, &value
);
1183 require_noerr_quiet(drtn
, badDER
);
1185 /* (c) attribute values in PrintableString are not case sensitive
1186 (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and
1188 (d) attribute values in PrintableString are compared after
1189 removing leading and trailing white space and converting internal
1190 substrings of one or more consecutive white space characters to a
1192 if (value
.tag
== ASN1_PRINTABLE_STRING
) {
1193 /* Offset relative to base of current value tag. */
1194 CFIndex valueTagLocation
= atvTagLocation
+ atvPair
.value
.data
- atvTag
;
1195 CFIndex valueTLLength
= value
.content
.data
- atvPair
.value
.data
;
1196 CFIndex valueContentLength
= value
.content
.length
;
1198 /* Now copy all the bytes, but convert to upper case while
1199 doing so and convert multiple whitespace chars into a
1201 bool lastWasBlank
= false;
1202 CFIndex valueLocation
= valueTagLocation
+ valueTLLength
;
1203 CFIndex valueCurrentLocation
= valueLocation
;
1205 for (ix
= 0; ix
< valueContentLength
; ++ix
) {
1206 UInt8 ch
= value
.content
.data
[ix
];
1211 /* Don't insert a space for first character
1213 if (valueCurrentLocation
> valueLocation
) {
1214 base
[valueCurrentLocation
++] = ' ';
1216 lastWasBlank
= true;
1219 lastWasBlank
= false;
1220 if ('a' <= ch
&& ch
<= 'z') {
1221 base
[valueCurrentLocation
++] = ch
+ 'A' - 'a';
1223 base
[valueCurrentLocation
++] = ch
;
1227 /* Finally if lastWasBlank remove the trailing space. */
1228 if (lastWasBlank
&& valueCurrentLocation
> valueLocation
) {
1229 valueCurrentLocation
--;
1231 /* Adjust content length to normalized length. */
1232 valueContentLength
= valueCurrentLocation
- valueLocation
;
1234 /* Number of bytes by which the length should be shorted. */
1235 CFIndex lengthDiff
= value
.content
.length
- valueContentLength
;
1236 if (lengthDiff
== 0) {
1237 /* Easy case no need to adjust lengths. */
1239 /* Hard work we need to go back and fix up length fields
1241 1) The value itself.
1242 2) The ATV Sequence containing type/value
1243 3) The RDN Set containing one or more atv pairs.
1247 /* Step 1 fix up length of value. */
1248 /* Length of value tag and length minus the tag. */
1249 DERSize newValueTLLength
= valueTLLength
- 1;
1250 drtn
= DEREncodeLength(valueContentLength
,
1251 base
+ valueTagLocation
+ 1, &newValueTLLength
);
1252 /* Add the length of the tag back in. */
1254 CFIndex valueLLDiff
= valueTLLength
- newValueTLLength
;
1256 /* The size of the length field changed, let's slide
1257 the value back by valueLLDiff bytes. */
1258 memmove(base
+ valueTagLocation
+ newValueTLLength
,
1259 base
+ valueTagLocation
+ valueTLLength
,
1260 valueContentLength
);
1261 /* The length diff for the enclosing object. */
1262 lengthDiff
+= valueLLDiff
;
1265 /* Step 2 fix up length of the enclosing ATV Sequence. */
1266 atvContentLength
-= lengthDiff
;
1267 DERSize newATVTLLength
= atvTLLength
- 1;
1268 drtn
= DEREncodeLength(atvContentLength
,
1269 base
+ atvTagLocation
+ 1, &newATVTLLength
);
1270 /* Add the length of the tag back in. */
1272 CFIndex atvLLDiff
= atvTLLength
- newATVTLLength
;
1274 /* The size of the length field changed, let's slide
1275 the value back by valueLLDiff bytes. */
1276 memmove(base
+ atvTagLocation
+ newATVTLLength
,
1277 base
+ atvTagLocation
+ atvTLLength
,
1279 /* The length diff for the enclosing object. */
1280 lengthDiff
+= atvLLDiff
;
1281 atvTLLength
= newATVTLLength
;
1284 /* Step 3 fix up length of enclosing RDN Set. */
1285 rdnContentLength
-= lengthDiff
;
1286 DERSize newRDNTLLength
= rdnTLLength
- 1;
1287 drtn
= DEREncodeLength(rdnContentLength
,
1288 base
+ rdnTagLocation
+ 1, &newRDNTLLength
);
1289 /* Add the length of the tag back in. */
1291 CFIndex rdnLLDiff
= rdnTLLength
- newRDNTLLength
;
1293 /* The size of the length field changed, let's slide
1294 the value back by valueLLDiff bytes. */
1295 memmove(base
+ rdnTagLocation
+ newRDNTLLength
,
1296 base
+ rdnTagLocation
+ rdnTLLength
,
1298 /* The length diff for the enclosing object. */
1299 lengthDiff
+= rdnLLDiff
;
1300 rdnTLLength
= newRDNTLLength
;
1302 /* Adjust the locations that might have changed due to
1304 atvTagLocation
-= rdnLLDiff
;
1308 atvTagLocation
+= atvTLLength
+ atvContentLength
;
1309 atvTag
= atvSeq
.nextItem
;
1311 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1312 rdnTagLocation
+= rdnTLLength
+ rdnContentLength
;
1313 rdnTag
= rdnSeq
.nextItem
;
1315 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
1316 /* Truncate the result to the proper length. */
1317 CFDataSetLength(result
, rdnTagLocation
);
1326 /* AUDIT[securityd]:
1327 certificate->_der is a caller provided data of any length (might be 0).
1329 Top level certificate decode.
1331 static bool SecCertificateParse(SecCertificateRefP certificate
)
1336 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
1338 /* top level decode */
1339 DERSignedCertCrl signedCert
;
1340 drtn
= DERParseSequence(&certificate
->_der
, DERNumSignedCertCrlItemSpecs
,
1341 DERSignedCertCrlItemSpecs
, &signedCert
,
1342 sizeof(signedCert
));
1343 require_noerr_quiet(drtn
, badCert
);
1344 /* Store tbs since we need to digest it for verification later on. */
1345 certificate
->_tbs
= signedCert
.tbs
;
1347 /* decode the TBSCert - it was saved in full DER form */
1349 drtn
= DERParseSequence(&signedCert
.tbs
,
1350 DERNumTBSCertItemSpecs
, DERTBSCertItemSpecs
,
1351 &tbsCert
, sizeof(tbsCert
));
1352 require_noerr_quiet(drtn
, badCert
);
1354 /* sequence we're given: decode the signedCerts Signature Algorithm. */
1355 /* This MUST be the same as the certificate->_tbsSigAlg with the exception
1356 of the params field. */
1357 drtn
= DERParseSequenceContent(&signedCert
.sigAlg
,
1358 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1359 &certificate
->_sigAlg
, sizeof(certificate
->_sigAlg
));
1360 require_noerr_quiet(drtn
, badCert
);
1362 /* The contents of signedCert.sig is a bit string whose contents
1363 are the signature itself. */
1364 DERByte numUnusedBits
;
1365 drtn
= DERParseBitString(&signedCert
.sig
,
1366 &certificate
->_signature
, &numUnusedBits
);
1367 require_noerr_quiet(drtn
, badCert
);
1369 /* Now decode the tbsCert. */
1371 /* First we turn the optional version into an int. */
1372 if (tbsCert
.version
.length
) {
1373 DERDecodedInfo decoded
;
1374 drtn
= DERDecodeItem(&tbsCert
.version
, &decoded
);
1375 require_noerr_quiet(drtn
, badCert
);
1376 require_quiet(decoded
.tag
== ASN1_INTEGER
, badCert
);
1377 require_quiet(decoded
.content
.length
== 1, badCert
);
1378 certificate
->_version
= decoded
.content
.data
[0];
1379 require_quiet(certificate
->_version
> 0, badCert
);
1380 require_quiet(certificate
->_version
< 3, badCert
);
1382 certificate
->_version
= 0;
1385 /* The serial number is in the tbsCert.serialNum - it was saved in
1386 INTEGER form without the tag and length. */
1387 certificate
->_serialNum
= tbsCert
.serialNum
;
1388 certificate
->_serialNumber
= CFDataCreate(allocator
,
1389 tbsCert
.serialNum
.data
, tbsCert
.serialNum
.length
);
1391 /* sequence we're given: decode the tbsCerts TBS Signature Algorithm. */
1392 drtn
= DERParseSequenceContent(&tbsCert
.tbsSigAlg
,
1393 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1394 &certificate
->_tbsSigAlg
, sizeof(certificate
->_tbsSigAlg
));
1395 require_noerr_quiet(drtn
, badCert
);
1397 /* The issuer is in the tbsCert.issuer - it's a sequence without the tag
1398 and length fields. */
1399 certificate
->_issuer
= tbsCert
.issuer
;
1400 certificate
->_normalizedIssuer
= createNormalizedX501Name(allocator
,
1403 /* sequence we're given: decode the tbsCerts Validity sequence. */
1404 DERValidity validity
;
1405 drtn
= DERParseSequenceContent(&tbsCert
.validity
,
1406 DERNumValidityItemSpecs
, DERValidityItemSpecs
,
1407 &validity
, sizeof(validity
));
1408 require_noerr_quiet(drtn
, badCert
);
1409 require_quiet(derDateGetAbsoluteTime(&validity
.notBefore
,
1410 &certificate
->_notBefore
), badCert
);
1411 require_quiet(derDateGetAbsoluteTime(&validity
.notAfter
,
1412 &certificate
->_notAfter
), badCert
);
1414 /* The subject is in the tbsCert.subject - it's a sequence without the tag
1415 and length fields. */
1416 certificate
->_subject
= tbsCert
.subject
;
1417 certificate
->_normalizedSubject
= createNormalizedX501Name(allocator
,
1420 /* sequence we're given: encoded DERSubjPubKeyInfo - it was saved in full DER form */
1421 DERSubjPubKeyInfo pubKeyInfo
;
1422 drtn
= DERParseSequence(&tbsCert
.subjectPubKey
,
1423 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
1424 &pubKeyInfo
, sizeof(pubKeyInfo
));
1425 require_noerr_quiet(drtn
, badCert
);
1427 /* sequence we're given: decode the pubKeyInfos DERAlgorithmId */
1428 drtn
= DERParseSequenceContent(&pubKeyInfo
.algId
,
1429 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
1430 &certificate
->_algId
, sizeof(certificate
->_algId
));
1431 require_noerr_quiet(drtn
, badCert
);
1433 /* Now we can figure out the key's algorithm id and params based on
1434 certificate->_algId.oid. */
1436 /* The contents of pubKeyInfo.pubKey is a bit string whose contents
1437 are a PKCS1 format RSA key. */
1438 drtn
= DERParseBitString(&pubKeyInfo
.pubKey
,
1439 &certificate
->_pubKeyDER
, &numUnusedBits
);
1440 require_noerr_quiet(drtn
, badCert
);
1442 /* The contents of tbsCert.issuerID is a bit string. */
1443 certificate
->_issuerUniqueID
= tbsCert
.issuerID
;
1445 /* The contents of tbsCert.subjectID is a bit string. */
1446 certificate
->_subjectUniqueID
= tbsCert
.subjectID
;
1449 if (tbsCert
.extensions
.length
) {
1450 CFIndex extensionCount
= 0;
1453 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
,
1455 require_noerr_quiet(drtn
, badCert
);
1456 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1457 DERDecodedInfo currDecoded
;
1458 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
1460 /* ! = MUST recognize ? = SHOULD recognize
1463 KnownExtension _subjectKeyID
; /* ?SubjectKeyIdentifier id-ce 14 */
1464 KnownExtension _keyUsage
; /* !KeyUsage id-ce 15 */
1465 KnownExtension _subjectAltName
; /* !SubjectAltName id-ce 17 */
1466 KnownExtension _basicConstraints
; /* !BasicConstraints id-ce 19 */
1467 KnownExtension _authorityKeyID
; /* ?AuthorityKeyIdentifier id-ce 35 */
1468 KnownExtension _extKeyUsage
; /* !ExtKeyUsage id-ce 37 */
1469 KnownExtension _netscapeCertType
; /* 2.16.840.1.113730.1.1 netscape 1 1 */
1470 KnownExtension _qualCertStatements
; /* QCStatements id-pe 3 */
1472 KnownExtension _issuerAltName
; /* IssuerAltName id-ce 18 */
1473 KnownExtension _nameConstraints
; /* !NameConstraints id-ce 30 */
1474 KnownExtension _cRLDistributionPoints
; /* CRLDistributionPoints id-ce 31 */
1475 KnownExtension _certificatePolicies
; /* !CertificatePolicies id-ce 32 */
1476 KnownExtension _policyMappings
; /* ?PolicyMappings id-ce 33 */
1477 KnownExtension _policyConstraints
; /* !PolicyConstraints id-ce 36 */
1478 KnownExtension _freshestCRL
; /* FreshestCRL id-ce 46 */
1479 KnownExtension _inhibitAnyPolicy
; /* !InhibitAnyPolicy id-ce 54 */
1481 KnownExtension _authorityInfoAccess
; /* AuthorityInfoAccess id-pe 1 */
1482 KnownExtension _subjectInfoAccess
; /* SubjectInfoAccess id-pe 11 */
1487 require_quiet(drtn
== DR_EndOfSequence
, badCert
);
1489 /* Put some upper limit on the number of extentions allowed. */
1490 require_quiet(extensionCount
< 10000, badCert
);
1491 certificate
->_extensionCount
= extensionCount
;
1492 certificate
->_extensions
=
1493 malloc(sizeof(SecCertificateExtension
) * extensionCount
);
1496 drtn
= DERDecodeSeqInit(&tbsCert
.extensions
, &tag
, &derSeq
);
1497 require_noerr_quiet(drtn
, badCert
);
1498 for (ix
= 0; ix
< extensionCount
; ++ix
) {
1499 drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
);
1500 require_quiet(drtn
== DR_Success
||
1501 (ix
== extensionCount
- 1 && drtn
== DR_EndOfSequence
), badCert
);
1502 require_quiet(currDecoded
.tag
== ASN1_CONSTR_SEQUENCE
, badCert
);
1504 drtn
= DERParseSequenceContent(&currDecoded
.content
,
1505 DERNumExtensionItemSpecs
, DERExtensionItemSpecs
,
1506 &extn
, sizeof(extn
));
1507 require_noerr_quiet(drtn
, badCert
);
1508 /* Copy stuff into certificate->extensions[ix]. */
1509 certificate
->_extensions
[ix
].extnID
= extn
.extnID
;
1510 require_noerr_quiet(drtn
= DERParseBooleanWithDefault(&extn
.critical
, false,
1511 &certificate
->_extensions
[ix
].critical
), badCert
);
1512 certificate
->_extensions
[ix
].extnValue
= extn
.extnValue
;
1514 SecCertificateExtensionParser parser
=
1515 (SecCertificateExtensionParser
)CFDictionaryGetValue(
1516 gExtensionParsers
, &certificate
->_extensions
[ix
].extnID
);
1518 /* Invoke the parser. */
1519 parser(certificate
, &certificate
->_extensions
[ix
]);
1520 } else if (certificate
->_extensions
[ix
].critical
) {
1521 secinfo("cert", "Found unknown critical extension");
1522 certificate
->_foundUnknownCriticalExtension
= true;
1524 secinfo("cert", "Found unknown non critical extension");
1536 /* Public API functions. */
1537 CFTypeID
SecCertificateGetTypeIDP(void) {
1538 pthread_once(&kSecCertificateRegisterClass
, SecCertificateRegisterClass
);
1539 return kSecCertificateTypeID
;
1542 SecCertificateRefP
SecCertificateCreateWithBytesP(CFAllocatorRef allocator
,
1543 const UInt8
*der_bytes
, CFIndex der_length
) {
1546 CFIndex size
= sizeof(struct __SecCertificate
) + der_length
;
1547 SecCertificateRefP result
= (SecCertificateRefP
)_CFRuntimeCreateInstance(
1548 allocator
, SecCertificateGetTypeIDP(), size
- sizeof(CFRuntimeBase
), 0);
1550 memset((char*)result
+ sizeof(result
->_base
), 0,
1551 sizeof(*result
) - sizeof(result
->_base
));
1552 result
->_der
.data
= ((DERByte
*)result
+ sizeof(*result
));
1553 result
->_der
.length
= der_length
;
1554 memcpy(result
->_der
.data
, der_bytes
, der_length
);
1555 if (!SecCertificateParse(result
)) {
1563 /* @@@ Placeholder until <rdar://problem/5701851> iap submits a binary is fixed. */
1564 SecCertificateRefP
SecCertificateCreate(CFAllocatorRef allocator
,
1565 const UInt8
*der_bytes
, CFIndex der_length
);
1567 SecCertificateRefP
SecCertificateCreate(CFAllocatorRef allocator
,
1568 const UInt8
*der_bytes
, CFIndex der_length
) {
1569 return SecCertificateCreateWithBytesP(allocator
, der_bytes
, der_length
);
1571 /* @@@ End of placeholder. */
1573 /* AUDIT[securityd](done):
1574 der_certificate is a caller provided data of any length (might be 0), only
1575 its cf type has been checked.
1577 SecCertificateRefP
SecCertificateCreateWithDataP(CFAllocatorRef allocator
,
1578 CFDataRef der_certificate
) {
1579 check(der_certificate
);
1580 CFIndex size
= sizeof(struct __SecCertificate
);
1581 SecCertificateRefP result
= (SecCertificateRefP
)_CFRuntimeCreateInstance(
1582 allocator
, SecCertificateGetTypeIDP(), size
- sizeof(CFRuntimeBase
), 0);
1584 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
1585 result
->_der_data
= CFDataCreateCopy(allocator
, der_certificate
);
1586 result
->_der
.data
= (DERByte
*)CFDataGetBytePtr(result
->_der_data
);
1587 result
->_der
.length
= CFDataGetLength(result
->_der_data
);
1588 if (!SecCertificateParse(result
)) {
1596 CFDataRef
SecCertificateCopyDataP(SecCertificateRefP certificate
) {
1599 if (certificate
->_der_data
) {
1600 CFRetain(certificate
->_der_data
);
1601 result
= certificate
->_der_data
;
1603 result
= CFDataCreate(CFGetAllocator(certificate
),
1604 certificate
->_der
.data
, certificate
->_der
.length
);
1606 /* FIXME: If we wish to cache result we need to lock the certificate.
1607 Also this create 2 copies of the certificate data which is somewhat
1610 certificate
->_der_data
= result
;
1617 CFIndex
SecCertificateGetLengthP(SecCertificateRefP certificate
) {
1618 return certificate
->_der
.length
;
1621 const UInt8
*SecCertificateGetBytePtrP(SecCertificateRefP certificate
) {
1622 return certificate
->_der
.data
;
1625 /* From rfc3280 - Appendix B. ASN.1 Notes
1627 Object Identifiers (OIDs) are used throughout this specification to
1628 identify certificate policies, public key and signature algorithms,
1629 certificate extensions, etc. There is no maximum size for OIDs.
1630 This specification mandates support for OIDs which have arc elements
1631 with values that are less than 2^28, that is, they MUST be between 0
1632 and 268,435,455, inclusive. This allows each arc element to be
1633 represented within a single 32 bit word. Implementations MUST also
1634 support OIDs where the length of the dotted decimal (see [RFC 2252],
1635 section 4.1) string representation can be up to 100 bytes
1636 (inclusive). Implementations MUST be able to handle OIDs with up to
1637 20 elements (inclusive). CAs SHOULD NOT issue certificates which
1638 contain OIDs that exceed these requirements. Likewise, CRL issuers
1639 SHOULD NOT issue CRLs which contain OIDs that exceed these
1643 /* Oids longer than this are considered invalid. */
1644 #define MAX_OID_SIZE 32
1646 static CFStringRef
SecDERItemCopyOIDDecimalRepresentation(CFAllocatorRef allocator
,
1647 const DERItem
*oid
) {
1649 if (oid
->length
== 0) {
1650 return SecFrameworkCopyLocalizedString(CFSTR("<NULL>"),
1651 CFSTR("SecCertificate"));
1653 if (oid
->length
> MAX_OID_SIZE
) {
1654 return SecFrameworkCopyLocalizedString(CFSTR("Oid too long"),
1655 CFSTR("SecCertificate"));
1658 CFMutableStringRef result
= CFStringCreateMutable(allocator
, 0);
1660 // The first two levels are encoded into one byte, since the root level
1661 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
1662 // y may be > 39, so we have to add special-case handling for this.
1663 uint32_t x
= oid
->data
[0] / 40;
1664 uint32_t y
= oid
->data
[0] % 40;
1667 // Handle special case for large y if x = 2
1671 CFStringAppendFormat(result
, NULL
, CFSTR("%u.%u"), x
, y
);
1674 for (x
= 1; x
< oid
->length
; ++x
)
1676 value
= (value
<< 7) | (oid
->data
[x
] & 0x7F);
1677 /* @@@ value may not span more than 4 bytes. */
1678 /* A max number of 20 values is allowed. */
1679 if (!(oid
->data
[x
] & 0x80))
1681 CFStringAppendFormat(result
, NULL
, CFSTR(".%lu"), (unsigned long)value
);
1688 static CFStringRef
copyLocalizedOidDescription(CFAllocatorRef allocator
,
1689 const DERItem
*oid
) {
1690 if (oid
->length
== 0) {
1691 return SecFrameworkCopyLocalizedString(CFSTR("<NULL>"),
1692 CFSTR("SecCertificate"));
1695 /* Build the key we use to lookup the localized OID description. */
1696 CFMutableStringRef oidKey
= CFStringCreateMutable(allocator
,
1697 oid
->length
* 3 + 5);
1698 CFStringAppendFormat(oidKey
, NULL
, CFSTR("06 %02lX"), (unsigned long)oid
->length
);
1700 for (ix
= 0; ix
< oid
->length
; ++ix
)
1701 CFStringAppendFormat(oidKey
, NULL
, CFSTR(" %02X"), oid
->data
[ix
]);
1703 CFStringRef name
= SecFrameworkCopyLocalizedString(oidKey
, CFSTR("OID"));
1704 if (CFEqual(oidKey
, name
)) {
1706 name
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1713 /* Return the ipAddress as a dotted quad for ipv4 or as 8 colon separated
1714 4 digit hex strings for ipv6. Return NULL if the passed in IP doesn't
1715 have a length of exactly 4 or 16 octects. */
1716 static CFStringRef
copyIPAddressContentDescription(CFAllocatorRef allocator
,
1717 const DERItem
*ip
) {
1718 /* @@@ This is the IP Address as an OCTECT STRING. For IPv4 it's
1719 4 octects addr, or 8 octects, addr/mask for ipv6 it's
1720 16 octects addr, or 32 octects addr/mask. */
1721 CFStringRef value
= NULL
;
1722 if (ip
->length
== 4) {
1723 value
= CFStringCreateWithFormat(allocator
, NULL
,
1724 CFSTR("%u.%u.%u.%u"),
1725 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3]);
1726 } else if (ip
->length
== 16) {
1727 value
= CFStringCreateWithFormat(allocator
, NULL
,
1728 CFSTR("%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
1729 "%02x%02x:%02x%02x:%02x%02x:%02x%02x"),
1730 ip
->data
[0], ip
->data
[1], ip
->data
[2], ip
->data
[3],
1731 ip
->data
[4], ip
->data
[5], ip
->data
[6], ip
->data
[7],
1732 ip
->data
[8], ip
->data
[9], ip
->data
[10], ip
->data
[11],
1733 ip
->data
[12], ip
->data
[13], ip
->data
[14], ip
->data
[15]);
1740 static CFStringRef
copyFullOidDescription(CFAllocatorRef allocator
,
1741 const DERItem
*oid
) {
1742 CFStringRef decimal
= SecDERItemCopyOIDDecimalRepresentation(allocator
, oid
);
1743 CFStringRef name
= copyLocalizedOidDescription(allocator
, oid
);
1744 CFStringRef oid_string
= CFStringCreateWithFormat(allocator
, NULL
,
1745 CFSTR("%@ (%@)"), name
, decimal
);
1752 void appendPropertyP(CFMutableArrayRef properties
,
1753 CFStringRef propertyType
, CFStringRef label
, CFTypeRef value
) {
1754 CFDictionaryRef property
;
1756 CFStringRef localizedLabel
= SecFrameworkCopyLocalizedString(label
,
1757 CFSTR("SecCertificate"));
1758 const void *all_keys
[4];
1759 all_keys
[0] = kSecPropertyKeyType
;
1760 all_keys
[1] = kSecPropertyKeyLabel
;
1761 all_keys
[2] = kSecPropertyKeyLocalizedLabel
;
1762 all_keys
[3] = kSecPropertyKeyValue
;
1763 const void *property_values
[] = {
1769 property
= CFDictionaryCreate(CFGetAllocator(properties
),
1770 all_keys
, property_values
, value
? 4 : 3,
1771 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1772 CFRelease(localizedLabel
);
1774 const void *nolabel_keys
[2];
1775 nolabel_keys
[0] = kSecPropertyKeyType
;
1776 nolabel_keys
[1] = kSecPropertyKeyValue
;
1777 const void *property_values
[] = {
1781 property
= CFDictionaryCreate(CFGetAllocator(properties
),
1782 nolabel_keys
, property_values
, 2,
1783 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1786 CFArrayAppendValue(properties
, property
);
1787 CFRelease(property
);
1791 #define UTC_TIME_NOSEC_ZULU_LEN 11
1793 #define UTC_TIME_ZULU_LEN 13
1794 /* YYMMDDhhmmssThhmm */
1795 #define UTC_TIME_LOCALIZED_LEN 17
1796 /* YYYYMMDDhhmmssZ */
1797 #define GENERALIZED_TIME_ZULU_LEN 15
1798 /* YYYYMMDDhhmmssThhmm */
1799 #define GENERALIZED_TIME_LOCALIZED_LEN 19
1801 /* Parse 2 digits at (*p)[0] and (*p)[1] and return the result. Also
1803 static inline SInt32
parseDecimalPair(const DERByte
**p
) {
1804 const DERByte
*cp
= *p
;
1806 return 10 * (cp
[0] - '0') + cp
[1] - '0';
1809 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
1810 true if the date was valid and properly decoded, also return the result in
1811 absTime. Return false otherwise. */
1812 static CFAbsoluteTime
SecAbsoluteTimeFromDateContent(DERTag tag
, const uint8_t *bytes
,
1818 bool isUtcLength
= false;
1819 bool isLocalized
= false;
1820 bool noSeconds
= false;
1822 case UTC_TIME_NOSEC_ZULU_LEN
: /* YYMMDDhhmmZ */
1826 case UTC_TIME_ZULU_LEN
: /* YYMMDDhhmmssZ */
1829 case GENERALIZED_TIME_ZULU_LEN
: /* YYYYMMDDhhmmssZ */
1831 case UTC_TIME_LOCALIZED_LEN
: /* YYMMDDhhmmssThhmm (where T=[+,-]) */
1834 case GENERALIZED_TIME_LOCALIZED_LEN
:/* YYYYMMDDhhmmssThhmm (where T=[+,-]) */
1837 default: /* unknown format */
1841 /* Make sure the der tag fits the thing inside it. */
1842 if (tag
== ASN1_UTC_TIME
) {
1845 } else if (tag
== ASN1_GENERALIZED_TIME
) {
1852 const DERByte
*cp
= bytes
;
1853 /* Check that all characters are digits, except if localized the timezone
1854 indicator or if not localized the 'Z' at the end. */
1856 for (ix
= 0; ix
< length
; ++ix
) {
1857 if (!(isdigit(cp
[ix
]))) {
1858 if ((isLocalized
&& ix
== length
- 5 &&
1859 (cp
[ix
] == '+' || cp
[ix
] == '-')) ||
1860 (!isLocalized
&& ix
== length
- 1 && cp
[ix
] == 'Z')) {
1867 /* Initialize the fields in a gregorian date struct. */
1868 CFGregorianDate gdate
;
1870 SInt32 year
= parseDecimalPair(&cp
);
1872 /* 0 <= year < 50 : assume century 21 */
1873 gdate
.year
= 2000 + year
;
1874 } else if (year
< 70) {
1875 /* 50 <= year < 70 : illegal per PKIX */
1878 /* 70 < year <= 99 : assume century 20 */
1879 gdate
.year
= 1900 + year
;
1882 gdate
.year
= 100 * parseDecimalPair(&cp
) + parseDecimalPair(&cp
);
1884 gdate
.month
= parseDecimalPair(&cp
);
1885 gdate
.day
= parseDecimalPair(&cp
);
1886 gdate
.hour
= parseDecimalPair(&cp
);
1887 gdate
.minute
= parseDecimalPair(&cp
);
1891 gdate
.second
= parseDecimalPair(&cp
);
1894 CFTimeInterval timeZoneOffset
= 0;
1896 /* ZONE INDICATOR */
1897 SInt32 multiplier
= *cp
++ == '+' ? 60 : -60;
1898 timeZoneOffset
= multiplier
*
1899 (parseDecimalPair(&cp
) + 60 * parseDecimalPair(&cp
));
1904 secinfo("dateparse",
1905 "date %.*s year: %04d-%02d-%02d %02d:%02d:%02.f %+05.f",
1906 (int)length
, bytes
, (int)gdate
.year
, gdate
.month
,
1907 gdate
.day
, gdate
.hour
, gdate
.minute
, gdate
.second
,
1908 timeZoneOffset
/ 60);
1910 if (!CFGregorianDateIsValid(gdate
, kCFGregorianAllUnits
))
1912 CFTimeZoneRef timeZone
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
,
1916 CFAbsoluteTime absTime
= CFGregorianDateGetAbsoluteTime(gdate
, timeZone
);
1917 CFRelease(timeZone
);
1921 static bool derDateContentGetAbsoluteTime(DERTag tag
, const DERItem
*date
,
1922 CFAbsoluteTime
*pabsTime
) {
1923 CFAbsoluteTime absTime
= SecAbsoluteTimeFromDateContent(tag
, date
->data
,
1925 if (absTime
== NULL_TIME
)
1928 *pabsTime
= absTime
;
1932 /* Decode a choice of UTCTime or GeneralizedTime to a CFAbsoluteTime. Return
1933 true if the date was valid and properly decoded, also return the result in
1934 absTime. Return false otherwise. */
1935 static bool derDateGetAbsoluteTime(const DERItem
*dateChoice
,
1936 CFAbsoluteTime
*absTime
) {
1939 if (dateChoice
->length
== 0)
1942 DERDecodedInfo decoded
;
1943 if (DERDecodeItem(dateChoice
, &decoded
))
1946 return derDateContentGetAbsoluteTime(decoded
.tag
, &decoded
.content
,
1950 static void appendDataProperty(CFMutableArrayRef properties
,
1951 CFStringRef label
, const DERItem
*der_data
) {
1952 CFDataRef data
= CFDataCreate(CFGetAllocator(properties
),
1953 der_data
->data
, der_data
->length
);
1954 appendPropertyP(properties
, kSecPropertyTypeData
, label
, data
);
1958 static void appendUnparsedProperty(CFMutableArrayRef properties
,
1959 CFStringRef label
, const DERItem
*der_data
) {
1960 CFStringRef newLabel
= CFStringCreateWithFormat(CFGetAllocator(properties
),
1961 NULL
, CFSTR("Unparsed %@"), label
);
1962 appendDataProperty(properties
, newLabel
, der_data
);
1963 CFRelease(newLabel
);
1966 static void appendInvalidProperty(CFMutableArrayRef properties
,
1967 CFStringRef label
, const DERItem
*der_data
) {
1968 CFStringRef newLabel
= CFStringCreateWithFormat(CFGetAllocator(properties
),
1969 NULL
, CFSTR("Invalid %@"), label
);
1970 appendDataProperty(properties
, newLabel
, der_data
);
1971 CFRelease(newLabel
);
1974 static void appendDateContentProperty(CFMutableArrayRef properties
,
1975 CFStringRef label
, DERTag tag
, const DERItem
*dateContent
) {
1976 CFAbsoluteTime absTime
;
1977 if (!derDateContentGetAbsoluteTime(tag
, dateContent
, &absTime
)) {
1978 /* Date decode failure insert hex bytes instead. */
1979 return appendInvalidProperty(properties
, label
, dateContent
);
1981 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
1982 appendPropertyP(properties
, kSecPropertyTypeDate
, label
, date
);
1986 static void appendDateProperty(CFMutableArrayRef properties
,
1987 CFStringRef label
, CFAbsoluteTime absTime
) {
1988 CFDateRef date
= CFDateCreate(CFGetAllocator(properties
), absTime
);
1989 appendPropertyP(properties
, kSecPropertyTypeDate
, label
, date
);
1993 static void appendIPAddressContentProperty(CFMutableArrayRef properties
,
1994 CFStringRef label
, const DERItem
*ip
) {
1996 copyIPAddressContentDescription(CFGetAllocator(properties
), ip
);
1998 appendPropertyP(properties
, kSecPropertyTypeString
, label
, value
);
2001 appendUnparsedProperty(properties
, label
, ip
);
2005 static void appendURLContentProperty(CFMutableArrayRef properties
,
2006 CFStringRef label
, const DERItem
*urlContent
) {
2007 CFURLRef url
= CFURLCreateWithBytes(CFGetAllocator(properties
),
2008 urlContent
->data
, urlContent
->length
, kCFStringEncodingASCII
, NULL
);
2010 appendPropertyP(properties
, kSecPropertyTypeURL
, label
, url
);
2013 appendInvalidProperty(properties
, label
, urlContent
);
2017 static void appendURLProperty(CFMutableArrayRef properties
,
2018 CFStringRef label
, const DERItem
*url
) {
2019 DERDecodedInfo decoded
;
2022 drtn
= DERDecodeItem(url
, &decoded
);
2023 if (drtn
|| decoded
.tag
!= ASN1_IA5_STRING
) {
2024 appendInvalidProperty(properties
, label
, url
);
2026 appendURLContentProperty(properties
, label
, &decoded
.content
);
2030 static void appendOIDProperty(CFMutableArrayRef properties
,
2031 CFStringRef label
, const DERItem
*oid
) {
2032 CFStringRef oid_string
= copyLocalizedOidDescription(CFGetAllocator(properties
),
2034 appendPropertyP(properties
, kSecPropertyTypeString
, label
, oid_string
);
2035 CFRelease(oid_string
);
2038 static void appendAlgorithmProperty(CFMutableArrayRef properties
,
2039 CFStringRef label
, const DERAlgorithmId
*algorithm
) {
2040 CFMutableArrayRef alg_props
=
2041 CFArrayCreateMutable(CFGetAllocator(properties
), 0,
2042 &kCFTypeArrayCallBacks
);
2043 appendOIDProperty(alg_props
, CFSTR("Algorithm"), &algorithm
->oid
);
2044 if (algorithm
->params
.length
) {
2045 if (algorithm
->params
.length
== 2 &&
2046 algorithm
->params
.data
[0] == ASN1_NULL
&&
2047 algorithm
->params
.data
[1] == 0) {
2048 /* @@@ Localize <NULL> or perhaps skip it? */
2049 appendPropertyP(alg_props
, kSecPropertyTypeString
,
2050 CFSTR("Parameters"), CFSTR("none"));
2052 appendUnparsedProperty(alg_props
, CFSTR("Parameters"),
2053 &algorithm
->params
);
2056 appendPropertyP(properties
, kSecPropertyTypeSection
, label
, alg_props
);
2057 CFRelease(alg_props
);
2060 static CFStringRef
copyHexDescription(CFAllocatorRef allocator
,
2061 const DERItem
*blob
) {
2062 CFIndex ix
, length
= blob
->length
/* < 24 ? blob->length : 24 */;
2063 CFMutableStringRef string
= CFStringCreateMutable(allocator
,
2064 blob
->length
* 3 - 1);
2065 for (ix
= 0; ix
< length
; ++ix
)
2067 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), blob
->data
[ix
]);
2069 CFStringAppendFormat(string
, NULL
, CFSTR(" %02X"), blob
->data
[ix
]);
2074 static CFStringRef
copyBlobString(CFAllocatorRef allocator
,
2075 CFStringRef blobType
, CFStringRef quanta
, const DERItem
*blob
) {
2076 CFStringRef blobFormat
= SecFrameworkCopyLocalizedString(
2077 CFSTR("%@; %d %@; data = %@"), CFSTR("SecCertificate")
2078 /*, "format string for encoded field data (e.g. Sequence; 128 bytes; "
2079 "data = 00 00 ...)" */);
2080 CFStringRef hex
= copyHexDescription(allocator
, blob
);
2081 CFStringRef result
= CFStringCreateWithFormat(allocator
, NULL
,
2082 blobFormat
, blobType
, blob
->length
, quanta
, hex
);
2084 CFRelease(blobFormat
);
2089 static CFStringRef
copyContentString(CFAllocatorRef allocator
,
2090 const DERItem
*string
, CFStringEncoding encoding
,
2091 bool printableOnly
) {
2092 /* Strip potential bogus trailing zero from printable strings. */
2093 DERSize length
= string
->length
;
2094 if (length
&& string
->data
[length
- 1] == 0) {
2095 /* Don't mess with the length of UTF16 strings though. */
2096 if (encoding
!= kCFStringEncodingUTF16
)
2099 /* A zero length string isn't considered printable. */
2100 if (!length
&& printableOnly
)
2103 /* Passing true for the 5th paramater to CFStringCreateWithBytes() makes
2104 it treat kCFStringEncodingUTF16 as big endian by default, whereas
2105 passing false makes it treat it as native endian by default. */
2106 CFStringRef result
= CFStringCreateWithBytes(allocator
, string
->data
,
2107 length
, encoding
, encoding
== kCFStringEncodingUTF16
);
2111 return printableOnly
? NULL
: copyHexDescription(allocator
, string
);
2114 /* From rfc3280 - Appendix B. ASN.1 Notes
2116 CAs MUST force the serialNumber to be a non-negative integer, that
2117 is, the sign bit in the DER encoding of the INTEGER value MUST be
2118 zero - this can be done by adding a leading (leftmost) `00'H octet if
2119 necessary. This removes a potential ambiguity in mapping between a
2120 string of octets and an integer value.
2122 As noted in section 4.1.2.2, serial numbers can be expected to
2123 contain long integers. Certificate users MUST be able to handle
2124 serialNumber values up to 20 octets in length. Conformant CAs MUST
2125 NOT use serialNumber values longer than 20 octets.
2128 /* Return the given numeric data as a string: decimal up to 64 bits,
2130 static CFStringRef
copyIntegerContentDescription(CFAllocatorRef allocator
,
2131 const DERItem
*integer
) {
2133 CFIndex ix
, length
= integer
->length
;
2135 if (length
== 0 || length
> 8)
2136 return copyHexDescription(allocator
, integer
);
2138 for(ix
= 0; ix
< length
; ++ix
) {
2140 value
+= integer
->data
[ix
];
2143 return CFStringCreateWithFormat(allocator
, NULL
, CFSTR("%llu"), value
);
2146 static CFStringRef
copyDERThingContentDescription(CFAllocatorRef allocator
,
2147 DERTag tag
, const DERItem
*derThing
, bool printableOnly
) {
2151 return printableOnly
? NULL
: copyIntegerContentDescription(allocator
, derThing
);
2152 case ASN1_PRINTABLE_STRING
:
2153 case ASN1_IA5_STRING
:
2154 return copyContentString(allocator
, derThing
, kCFStringEncodingASCII
, printableOnly
);
2155 case ASN1_UTF8_STRING
:
2156 case ASN1_GENERAL_STRING
:
2157 case ASN1_UNIVERSAL_STRING
:
2158 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF8
, printableOnly
);
2159 case ASN1_T61_STRING
: // 20, also BER_TAG_TELETEX_STRING
2160 case ASN1_VIDEOTEX_STRING
: // 21
2161 case ASN1_VISIBLE_STRING
: // 26
2162 return copyContentString(allocator
, derThing
, kCFStringEncodingISOLatin1
, printableOnly
);
2163 case ASN1_BMP_STRING
: // 30
2164 return copyContentString(allocator
, derThing
, kCFStringEncodingUTF16
, printableOnly
);
2165 case ASN1_OCTET_STRING
:
2166 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Byte string"), CFSTR("bytes"),
2168 //return copyBlobString(BYTE_STRING_STR, BYTES_STR, derThing);
2169 case ASN1_BIT_STRING
:
2170 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Bit string"), CFSTR("bits"),
2172 case (DERByte
)ASN1_CONSTR_SEQUENCE
:
2173 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Sequence"), CFSTR("bytes"),
2175 case (DERByte
)ASN1_CONSTR_SET
:
2176 return printableOnly
? NULL
: copyBlobString(allocator
, CFSTR("Set"), CFSTR("bytes"),
2178 case ASN1_OBJECT_ID
:
2179 return printableOnly
? NULL
: copyLocalizedOidDescription(allocator
, derThing
);
2182 /* "format string for undisplayed field data with a given DER tag" */
2183 return printableOnly
? NULL
: CFStringCreateWithFormat(allocator
, NULL
,
2184 CFSTR("not displayed (tag = %llu; length %d)"),
2185 tag
, (int)derThing
->length
);
2189 static CFStringRef
copyDERThingDescription(CFAllocatorRef allocator
,
2190 const DERItem
*derThing
, bool printableOnly
) {
2191 DERDecodedInfo decoded
;
2194 drtn
= DERDecodeItem(derThing
, &decoded
);
2196 return printableOnly
? NULL
: copyHexDescription(allocator
, derThing
);
2198 return copyDERThingContentDescription(allocator
, decoded
.tag
,
2199 &decoded
.content
, false);
2203 static void appendDERThingProperty(CFMutableArrayRef properties
,
2204 CFStringRef label
, const DERItem
*derThing
) {
2205 CFStringRef value
= copyDERThingDescription(CFGetAllocator(properties
),
2207 appendPropertyP(properties
, kSecPropertyTypeString
, label
, value
);
2208 CFReleaseSafe(value
);
2211 static OSStatus
appendRDNProperty(void *context
, const DERItem
*rdnType
,
2212 const DERItem
*rdnValue
, CFIndex rdnIX
) {
2213 CFMutableArrayRef properties
= (CFMutableArrayRef
)context
;
2215 /* If there is more than one value pair we create a subsection for the
2216 second pair, and append things to the subsection for subsequent
2218 CFIndex lastIX
= CFArrayGetCount(properties
) - 1;
2219 CFTypeRef lastValue
= CFArrayGetValueAtIndex(properties
, lastIX
);
2221 /* Since this is the second rdn pair for a given rdn, we setup a
2222 new subsection for this rdn. We remove the first property
2223 from the properties array and make it the first element in the
2224 subsection instead. */
2225 CFMutableArrayRef rdn_props
= CFArrayCreateMutable(
2226 CFGetAllocator(properties
), 0, &kCFTypeArrayCallBacks
);
2227 CFArrayAppendValue(rdn_props
, lastValue
);
2228 CFArrayRemoveValueAtIndex(properties
, lastIX
);
2229 appendPropertyP(properties
, kSecPropertyTypeSection
, NULL
, rdn_props
);
2230 properties
= rdn_props
;
2232 /* Since this is the third or later rdn pair we have already
2233 created a subsection in the top level properties array. Instead
2234 of appending to that directly we append to the array inside the
2236 properties
= (CFMutableArrayRef
)CFDictionaryGetValue(
2237 (CFDictionaryRef
)lastValue
, kSecPropertyKeyValue
);
2241 /* Finally we append the new rdn value to the property array. */
2242 CFStringRef label
= copyLocalizedOidDescription(CFGetAllocator(properties
),
2245 appendDERThingProperty(properties
, label
, rdnValue
);
2247 return errSecSuccess
;
2249 return errSecInvalidCertificate
;
2253 static CFArrayRef
createPropertiesForRDNContent(CFAllocatorRef allocator
,
2254 const DERItem
*rdnSetContent
) {
2255 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2256 &kCFTypeArrayCallBacks
);
2257 OSStatus status
= parseRDNContent(rdnSetContent
, properties
,
2260 CFArrayRemoveAllValues(properties
);
2261 appendInvalidProperty(properties
, CFSTR("RDN"), rdnSetContent
);
2268 From rfc3739 - 3.1.2. Subject
2270 When parsing the subject here are some tips for a short name of the cert.
2271 Choice I: commonName
2272 Choice II: givenName
2273 Choice III: pseudonym
2275 The commonName attribute value SHALL, when present, contain a name
2276 of the subject. This MAY be in the subject's preferred
2277 presentation format, or a format preferred by the CA, or some
2278 other format. Pseudonyms, nicknames, and names with spelling
2279 other than defined by the registered name MAY be used. To
2280 understand the nature of the name presented in commonName,
2281 complying applications MAY have to examine present values of the
2282 givenName and surname attributes, or the pseudonym attribute.
2285 static CFArrayRef
createPropertiesForX501NameContent(CFAllocatorRef allocator
,
2286 const DERItem
*x501NameContent
) {
2287 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2288 &kCFTypeArrayCallBacks
);
2289 OSStatus status
= parseX501NameContent(x501NameContent
, properties
,
2292 CFArrayRemoveAllValues(properties
);
2293 appendInvalidProperty(properties
, CFSTR("X.501 Name"), x501NameContent
);
2299 static CFArrayRef
createPropertiesForX501Name(CFAllocatorRef allocator
,
2300 const DERItem
*x501Name
) {
2301 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
2302 &kCFTypeArrayCallBacks
);
2303 OSStatus status
= parseX501Name(x501Name
, properties
, appendRDNProperty
);
2305 CFArrayRemoveAllValues(properties
);
2306 appendInvalidProperty(properties
, CFSTR("X.501 Name"), x501Name
);
2312 static void appendIntegerProperty(CFMutableArrayRef properties
,
2313 CFStringRef label
, const DERItem
*integer
) {
2314 CFStringRef string
= copyIntegerContentDescription(
2315 CFGetAllocator(properties
), integer
);
2316 appendPropertyP(properties
, kSecPropertyTypeString
, label
, string
);
2320 static void appendBoolProperty(CFMutableArrayRef properties
,
2321 CFStringRef label
, bool boolean
) {
2322 appendPropertyP(properties
, kSecPropertyTypeString
,
2323 label
, boolean
? CFSTR("Yes") : CFSTR("No"));
2326 static void appendBooleanProperty(CFMutableArrayRef properties
,
2327 CFStringRef label
, const DERItem
*boolean
, bool defaultValue
) {
2329 DERReturn drtn
= DERParseBooleanWithDefault(boolean
, defaultValue
, &result
);
2331 /* Couldn't parse boolean; dump the raw unparsed data as hex. */
2332 appendInvalidProperty(properties
, label
, boolean
);
2334 appendBoolProperty(properties
, label
, result
);
2338 static void appendBitStringContentNames(CFMutableArrayRef properties
,
2339 CFStringRef label
, const DERItem
*bitStringContent
,
2340 const CFStringRef
*names
, CFIndex namesCount
) {
2341 DERSize len
= bitStringContent
->length
- 1;
2342 require_quiet(len
== 1 || len
== 2, badDER
);
2343 DERByte numUnusedBits
= bitStringContent
->data
[0];
2344 require_quiet(numUnusedBits
< 8, badDER
);
2345 uint_fast16_t bits
= 8 * len
- numUnusedBits
;
2346 require_quiet(bits
<= (uint_fast16_t)namesCount
, badDER
);
2347 uint_fast16_t value
= bitStringContent
->data
[1];
2350 value
= (value
<< 8) + bitStringContent
->data
[2];
2356 bool didOne
= false;
2357 CFMutableStringRef string
=
2358 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2359 for (ix
= 0; ix
< bits
; ++ix
) {
2362 CFStringAppend(string
, CFSTR(", "));
2366 CFStringAppend(string
, names
[ix
]);
2370 appendPropertyP(properties
, kSecPropertyTypeString
, label
, string
);
2374 appendInvalidProperty(properties
, label
, bitStringContent
);
2377 static void appendBitStringNames(CFMutableArrayRef properties
,
2378 CFStringRef label
, const DERItem
*bitString
,
2379 const CFStringRef
*names
, CFIndex namesCount
) {
2380 DERDecodedInfo bitStringContent
;
2381 DERReturn drtn
= DERDecodeItem(bitString
, &bitStringContent
);
2382 require_noerr_quiet(drtn
, badDER
);
2383 require_quiet(bitStringContent
.tag
== ASN1_BIT_STRING
, badDER
);
2384 appendBitStringContentNames(properties
, label
, &bitStringContent
.content
,
2388 appendInvalidProperty(properties
, label
, bitString
);
2392 typedef uint16_t SecKeyUsage
;
2394 #define kSecKeyUsageDigitalSignature 0x8000
2395 #define kSecKeyUsageNonRepudiation 0x4000
2396 #define kSecKeyUsageKeyEncipherment 0x2000
2397 #define kSecKeyUsageDataEncipherment 0x1000
2398 #define kSecKeyUsageKeyAgreement 0x0800
2399 #define kSecKeyUsageKeyCertSign 0x0400
2400 #define kSecKeyUsageCRLSign 0x0200
2401 #define kSecKeyUsageEncipherOnly 0x0100
2402 #define kSecKeyUsageDecipherOnly 0x0080
2405 KeyUsage ::= BIT STRING {
2406 digitalSignature (0),
2408 keyEncipherment (2),
2409 dataEncipherment (3),
2416 static void appendKeyUsage(CFMutableArrayRef properties
,
2417 const DERItem
*extnValue
) {
2418 if ((extnValue
->length
!= 4 && extnValue
->length
!= 5) ||
2419 extnValue
->data
[0] != ASN1_BIT_STRING
||
2420 extnValue
->data
[1] < 2 || extnValue
->data
[1] > 3 ||
2421 extnValue
->data
[2] > 7) {
2422 appendInvalidProperty(properties
, CFSTR("KeyUsage Extension"),
2425 CFMutableStringRef string
=
2426 CFStringCreateMutable(CFGetAllocator(properties
), 0);
2427 SecKeyUsage usage
= (extnValue
->data
[3] << 8);
2428 if (extnValue
->length
== 5)
2429 usage
+= extnValue
->data
[4];
2430 secinfo("keyusage", "keyusage: %04X", usage
);
2431 static const CFStringRef usageNames
[] = {
2432 CFSTR("Digital Signature"),
2433 CFSTR("Non-Repudiation"),
2434 CFSTR("Key Encipherment"),
2435 CFSTR("Data Encipherment"),
2436 CFSTR("Key Agreement"),
2442 bool didOne
= false;
2443 SecKeyUsage mask
= kSecKeyUsageDigitalSignature
;
2444 CFIndex ix
, bits
= (extnValue
->data
[1] - 1) * 8 - extnValue
->data
[2];
2445 for (ix
= 0; ix
< bits
; ++ix
) {
2448 CFStringAppend(string
, CFSTR(", "));
2452 /* @@@ Localize usageNames[ix]. */
2453 CFStringAppend(string
, usageNames
[ix
]);
2457 appendPropertyP(properties
, kSecPropertyTypeString
, CFSTR("Usage"),
2463 static void appendKeyUsage(CFMutableArrayRef properties
,
2464 const DERItem
*extnValue
) {
2465 static const CFStringRef usageNames
[] = {
2466 CFSTR("Digital Signature"),
2467 CFSTR("Non-Repudiation"),
2468 CFSTR("Key Encipherment"),
2469 CFSTR("Data Encipherment"),
2470 CFSTR("Key Agreement"),
2473 CFSTR("Encipher Only"),
2474 CFSTR("Decipher Only")
2476 appendBitStringNames(properties
, CFSTR("Usage"), extnValue
,
2477 usageNames
, sizeof(usageNames
) / sizeof(*usageNames
));
2481 static void appendPrivateKeyUsagePeriod(CFMutableArrayRef properties
,
2482 const DERItem
*extnValue
) {
2483 DERPrivateKeyUsagePeriod pkup
;
2484 DERReturn drtn
= DERParseSequence(extnValue
,
2485 DERNumPrivateKeyUsagePeriodItemSpecs
, DERPrivateKeyUsagePeriodItemSpecs
,
2486 &pkup
, sizeof(pkup
));
2487 require_noerr_quiet(drtn
, badDER
);
2488 if (pkup
.notBefore
.length
) {
2489 appendDateContentProperty(properties
, CFSTR("Not Valid Before"),
2490 ASN1_GENERALIZED_TIME
, &pkup
.notBefore
);
2492 if (pkup
.notAfter
.length
) {
2493 appendDateContentProperty(properties
, CFSTR("Not Valid After"),
2494 ASN1_GENERALIZED_TIME
, &pkup
.notAfter
);
2498 appendInvalidProperty(properties
, CFSTR("Private Key Usage Period"),
2502 static void appendStringContentProperty(CFMutableArrayRef properties
,
2503 CFStringRef label
, const DERItem
*stringContent
,
2504 CFStringEncoding encoding
) {
2505 CFStringRef string
= CFStringCreateWithBytes(CFGetAllocator(properties
),
2506 stringContent
->data
, stringContent
->length
, encoding
, FALSE
);
2508 appendPropertyP(properties
, kSecPropertyTypeString
, label
, string
);
2511 appendInvalidProperty(properties
, label
, stringContent
);
2516 OtherName ::= SEQUENCE {
2517 type-id OBJECT IDENTIFIER,
2518 value [0] EXPLICIT ANY DEFINED BY type-id }
2520 static void appendOtherNameContentProperty(CFMutableArrayRef properties
,
2521 const DERItem
*otherNameContent
) {
2523 DERReturn drtn
= DERParseSequenceContent(otherNameContent
,
2524 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
2526 require_noerr_quiet(drtn
, badDER
);
2527 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2528 CFStringRef oid_string
= copyLocalizedOidDescription(allocator
,
2529 &on
.typeIdentifier
);
2530 CFStringRef value_string
= copyDERThingDescription(allocator
, &on
.value
, false);
2532 appendPropertyP(properties
, kSecPropertyTypeString
, oid_string
,
2535 appendUnparsedProperty(properties
, oid_string
, &on
.value
);
2539 appendInvalidProperty(properties
, CFSTR("Other Name"), otherNameContent
);
2543 GeneralName ::= CHOICE {
2544 otherName [0] OtherName,
2545 rfc822Name [1] IA5String,
2546 dNSName [2] IA5String,
2547 x400Address [3] ORAddress,
2548 directoryName [4] Name,
2549 ediPartyName [5] EDIPartyName,
2550 uniformResourceIdentifier [6] IA5String,
2551 iPAddress [7] OCTET STRING,
2552 registeredID [8] OBJECT IDENTIFIER}
2554 EDIPartyName ::= SEQUENCE {
2555 nameAssigner [0] DirectoryString OPTIONAL,
2556 partyName [1] DirectoryString }
2558 static bool appendGeneralNameContentProperty(CFMutableArrayRef properties
,
2559 DERTag tag
, const DERItem
*generalName
) {
2561 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0:
2562 appendOtherNameContentProperty(properties
, generalName
);
2564 case ASN1_CONTEXT_SPECIFIC
| 1:
2566 appendStringContentProperty(properties
, CFSTR("Email Address"),
2567 generalName
, kCFStringEncodingASCII
);
2569 case ASN1_CONTEXT_SPECIFIC
| 2:
2571 appendStringContentProperty(properties
, CFSTR("DNS Name"), generalName
,
2572 kCFStringEncodingASCII
);
2574 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 3:
2575 appendUnparsedProperty(properties
, CFSTR("X.400 Address"),
2578 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 4:
2580 CFArrayRef directory_plist
=
2581 createPropertiesForX501Name(CFGetAllocator(properties
),
2583 appendPropertyP(properties
, kSecPropertyTypeSection
,
2584 CFSTR("Directory Name"), directory_plist
);
2585 CFRelease(directory_plist
);
2588 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 5:
2589 appendUnparsedProperty(properties
, CFSTR("EDI Party Name"),
2592 case ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 6:
2593 /* Technically I don't think this is valid, but there are certs out
2594 in the wild that use a constructed IA5String. In particular the
2595 VeriSign Time Stamping Authority CA.cer does this. */
2596 appendURLProperty(properties
, CFSTR("URI"), generalName
);
2598 case ASN1_CONTEXT_SPECIFIC
| 6:
2599 appendURLContentProperty(properties
, CFSTR("URI"), generalName
);
2601 case ASN1_CONTEXT_SPECIFIC
| 7:
2602 appendIPAddressContentProperty(properties
, CFSTR("IP Address"),
2605 case ASN1_CONTEXT_SPECIFIC
| 8:
2606 appendOIDProperty(properties
, CFSTR("Registered ID"), generalName
);
2616 static void appendGeneralNameProperty(CFMutableArrayRef properties
,
2617 const DERItem
*generalName
) {
2618 DERDecodedInfo generalNameContent
;
2619 DERReturn drtn
= DERDecodeItem(generalName
, &generalNameContent
);
2620 require_noerr_quiet(drtn
, badDER
);
2621 if (appendGeneralNameContentProperty(properties
, generalNameContent
.tag
,
2622 &generalNameContent
.content
))
2625 appendInvalidProperty(properties
, CFSTR("General Name"), generalName
);
2630 GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
2632 static void appendGeneralNamesContent(CFMutableArrayRef properties
,
2633 const DERItem
*generalNamesContent
) {
2635 DERReturn drtn
= DERDecodeSeqContentInit(generalNamesContent
, &gnSeq
);
2636 require_noerr_quiet(drtn
, badDER
);
2637 DERDecodedInfo generalNameContent
;
2638 while ((drtn
= DERDecodeSeqNext(&gnSeq
, &generalNameContent
)) ==
2640 if (!appendGeneralNameContentProperty(properties
,
2641 generalNameContent
.tag
, &generalNameContent
.content
)) {
2645 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2648 appendInvalidProperty(properties
, CFSTR("General Names"),
2649 generalNamesContent
);
2652 static void appendGeneralNames(CFMutableArrayRef properties
,
2653 const DERItem
*generalNames
) {
2654 DERDecodedInfo generalNamesContent
;
2655 DERReturn drtn
= DERDecodeItem(generalNames
, &generalNamesContent
);
2656 require_noerr_quiet(drtn
, badDER
);
2657 require_quiet(generalNamesContent
.tag
== ASN1_CONSTR_SEQUENCE
,
2659 appendGeneralNamesContent(properties
, &generalNamesContent
.content
);
2662 appendInvalidProperty(properties
, CFSTR("General Names"), generalNames
);
2666 BasicConstraints ::= SEQUENCE {
2667 cA BOOLEAN DEFAULT FALSE,
2668 pathLenConstraint INTEGER (0..MAX) OPTIONAL }
2670 static void appendBasicConstraints(CFMutableArrayRef properties
,
2671 const DERItem
*extnValue
) {
2672 DERBasicConstraints basicConstraints
;
2673 DERReturn drtn
= DERParseSequence(extnValue
,
2674 DERNumBasicConstraintsItemSpecs
, DERBasicConstraintsItemSpecs
,
2675 &basicConstraints
, sizeof(basicConstraints
));
2676 require_noerr_quiet(drtn
, badDER
);
2678 appendBooleanProperty(properties
, CFSTR("Certificate Authority"),
2679 &basicConstraints
.cA
, false);
2681 if (basicConstraints
.pathLenConstraint
.length
!= 0) {
2682 appendIntegerProperty(properties
, CFSTR("Path Length Constraint"),
2683 &basicConstraints
.pathLenConstraint
);
2687 appendInvalidProperty(properties
, CFSTR("Basic Constraints"), extnValue
);
2691 CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
2693 DistributionPoint ::= SEQUENCE {
2694 distributionPoint [0] DistributionPointName OPTIONAL,
2695 reasons [1] ReasonFlags OPTIONAL,
2696 cRLIssuer [2] GeneralNames OPTIONAL }
2698 DistributionPointName ::= CHOICE {
2699 fullName [0] GeneralNames,
2700 nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
2702 ReasonFlags ::= BIT STRING {
2706 affiliationChanged (3),
2708 cessationOfOperation (5),
2709 certificateHold (6),
2710 privilegeWithdrawn (7),
2713 static void appendCrlDistributionPoints(CFMutableArrayRef properties
,
2714 const DERItem
*extnValue
) {
2715 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2718 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &dpSeq
);
2719 require_noerr_quiet(drtn
, badDER
);
2720 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2721 DERDecodedInfo dpSeqContent
;
2722 while ((drtn
= DERDecodeSeqNext(&dpSeq
, &dpSeqContent
)) == DR_Success
) {
2723 require_quiet(dpSeqContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2724 DERDistributionPoint dp
;
2725 drtn
= DERParseSequenceContent(&dpSeqContent
.content
,
2726 DERNumDistributionPointItemSpecs
,
2727 DERDistributionPointItemSpecs
,
2729 require_noerr_quiet(drtn
, badDER
);
2730 if (dp
.distributionPoint
.length
) {
2731 DERDecodedInfo distributionPointName
;
2732 drtn
= DERDecodeItem(&dp
.distributionPoint
, &distributionPointName
);
2733 require_noerr_quiet(drtn
, badDER
);
2734 if (distributionPointName
.tag
==
2735 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 0)) {
2737 appendGeneralNamesContent(properties
,
2738 &distributionPointName
.content
);
2739 } else if (distributionPointName
.tag
==
2740 (ASN1_CONTEXT_SPECIFIC
| ASN1_CONSTRUCTED
| 1)) {
2741 CFArrayRef rdn_props
= createPropertiesForRDNContent(allocator
,
2743 appendPropertyP(properties
, kSecPropertyTypeSection
,
2744 CFSTR("Name Relative To CRL Issuer"), rdn_props
);
2745 CFRelease(rdn_props
);
2750 if (dp
.reasons
.length
) {
2751 static const CFStringRef reasonNames
[] = {
2753 CFSTR("Key Compromise"),
2754 CFSTR("CA Compromise"),
2755 CFSTR("Affiliation Changed"),
2756 CFSTR("Superseded"),
2757 CFSTR("Cessation Of Operation"),
2758 CFSTR("Certificate Hold"),
2759 CFSTR("Priviledge Withdrawn"),
2760 CFSTR("AA Compromise")
2762 appendBitStringContentNames(properties
, CFSTR("Reasons"),
2764 reasonNames
, sizeof(reasonNames
) / sizeof(*reasonNames
));
2766 if (dp
.cRLIssuer
.length
) {
2767 CFMutableArrayRef crlIssuer
= CFArrayCreateMutable(allocator
, 0,
2768 &kCFTypeArrayCallBacks
);
2769 appendPropertyP(properties
, kSecPropertyTypeSection
,
2770 CFSTR("CRL Issuer"), crlIssuer
);
2771 CFRelease(crlIssuer
);
2772 appendGeneralNames(crlIssuer
, &dp
.cRLIssuer
);
2775 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2778 appendInvalidProperty(properties
, CFSTR("Crl Distribution Points"),
2782 /* Decode a sequence of integers into a comma separated list of ints. */
2783 static void appendIntegerSequenceContent(CFMutableArrayRef properties
,
2784 CFStringRef label
, const DERItem
*intSequenceContent
) {
2785 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2787 DERReturn drtn
= DERDecodeSeqContentInit(intSequenceContent
, &intSeq
);
2788 require_noerr_quiet(drtn
, badDER
);
2789 DERDecodedInfo intContent
;
2790 CFMutableStringRef value
= NULL
;
2791 while ((drtn
= DERDecodeSeqNext(&intSeq
, &intContent
))
2793 require_quiet(intContent
.tag
== ASN1_INTEGER
, badDER
);
2794 CFStringRef intDesc
= copyIntegerContentDescription(
2795 allocator
, &intContent
.content
);
2797 CFStringAppendFormat(value
, NULL
, CFSTR(", %@"), intDesc
);
2799 value
= CFStringCreateMutableCopy(allocator
, 0, intDesc
);
2803 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2805 appendPropertyP(properties
, kSecPropertyTypeString
,
2806 CFSTR("Notice Numbers"), value
);
2810 /* DROPTHOUGH if !value. */
2812 appendInvalidProperty(properties
, label
, intSequenceContent
);
2815 static void appendCertificatePolicies(CFMutableArrayRef properties
,
2816 const DERItem
*extnValue
) {
2817 CFAllocatorRef allocator
= CFGetAllocator(properties
);
2820 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &piSeq
);
2821 require_noerr_quiet(drtn
, badDER
);
2822 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2823 DERDecodedInfo piContent
;
2825 while ((drtn
= DERDecodeSeqNext(&piSeq
, &piContent
)) == DR_Success
) {
2826 require_quiet(piContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2827 DERPolicyInformation pi
;
2828 drtn
= DERParseSequenceContent(&piContent
.content
,
2829 DERNumPolicyInformationItemSpecs
,
2830 DERPolicyInformationItemSpecs
,
2832 require_noerr_quiet(drtn
, badDER
);
2833 CFStringRef piLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2834 CFSTR("Policy Identifier #%d"), pin
++);
2835 appendOIDProperty(properties
, piLabel
, &pi
.policyIdentifier
);
2837 if (pi
.policyQualifiers
.length
== 0)
2841 drtn
= DERDecodeSeqContentInit(&pi
.policyQualifiers
, &pqSeq
);
2842 require_noerr_quiet(drtn
, badDER
);
2843 DERDecodedInfo pqContent
;
2845 while ((drtn
= DERDecodeSeqNext(&pqSeq
, &pqContent
)) == DR_Success
) {
2846 DERPolicyQualifierInfo pqi
;
2847 drtn
= DERParseSequenceContent(&pqContent
.content
,
2848 DERNumPolicyQualifierInfoItemSpecs
,
2849 DERPolicyQualifierInfoItemSpecs
,
2851 require_noerr_quiet(drtn
, badDER
);
2852 DERDecodedInfo qualifierContent
;
2853 drtn
= DERDecodeItem(&pqi
.qualifier
, &qualifierContent
);
2854 require_noerr_quiet(drtn
, badDER
);
2855 CFStringRef pqLabel
= CFStringCreateWithFormat(allocator
, NULL
,
2856 CFSTR("Policy Qualifier #%d"), pqn
++);
2857 appendOIDProperty(properties
, pqLabel
, &pqi
.policyQualifierID
);
2859 if (DEROidCompare(&oidQtCps
, &pqi
.policyQualifierID
)) {
2860 require_quiet(qualifierContent
.tag
== ASN1_IA5_STRING
, badDER
);
2861 appendURLContentProperty(properties
,
2863 &qualifierContent
.content
);
2864 } else if (DEROidCompare(&oidQtUNotice
, &pqi
.policyQualifierID
)) {
2865 require_quiet(qualifierContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
2867 drtn
= DERParseSequenceContent(&qualifierContent
.content
,
2868 DERNumUserNoticeItemSpecs
,
2869 DERUserNoticeItemSpecs
,
2871 require_noerr_quiet(drtn
, badDER
);
2872 if (un
.noticeRef
.length
) {
2873 DERNoticeReference nr
;
2874 drtn
= DERParseSequenceContent(&un
.noticeRef
,
2875 DERNumNoticeReferenceItemSpecs
,
2876 DERNoticeReferenceItemSpecs
,
2878 require_noerr_quiet(drtn
, badDER
);
2879 appendDERThingProperty(properties
,
2880 CFSTR("Organization"),
2882 appendIntegerSequenceContent(properties
,
2883 CFSTR("Notice Numbers"), &nr
.noticeNumbers
);
2885 if (un
.explicitText
.length
) {
2886 appendDERThingProperty(properties
, CFSTR("Explicit Text"),
2890 appendUnparsedProperty(properties
, CFSTR("Qualifier"),
2894 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2896 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
2899 appendInvalidProperty(properties
, CFSTR("Certificate Policies"),
2903 static void appendSubjectKeyIdentifier(CFMutableArrayRef properties
,
2904 const DERItem
*extnValue
) {
2906 DERDecodedInfo keyIdentifier
;
2907 drtn
= DERDecodeItem(extnValue
, &keyIdentifier
);
2908 require_noerr_quiet(drtn
, badDER
);
2909 require_quiet(keyIdentifier
.tag
== ASN1_OCTET_STRING
, badDER
);
2910 appendDataProperty(properties
, CFSTR("Key Identifier"),
2911 &keyIdentifier
.content
);
2915 appendInvalidProperty(properties
, CFSTR("Invalid Subject Key Identifier"),
2920 AuthorityKeyIdentifier ::= SEQUENCE {
2921 keyIdentifier [0] KeyIdentifier OPTIONAL,
2922 authorityCertIssuer [1] GeneralNames OPTIONAL,
2923 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
2924 -- authorityCertIssuer and authorityCertSerialNumber MUST both
2925 -- be present or both be absent
2927 KeyIdentifier ::= OCTET STRING
2929 static void appendAuthorityKeyIdentifier(CFMutableArrayRef properties
,
2930 const DERItem
*extnValue
) {
2931 DERAuthorityKeyIdentifier akid
;
2933 drtn
= DERParseSequence(extnValue
,
2934 DERNumAuthorityKeyIdentifierItemSpecs
,
2935 DERAuthorityKeyIdentifierItemSpecs
,
2936 &akid
, sizeof(akid
));
2937 require_noerr_quiet(drtn
, badDER
);
2938 if (akid
.keyIdentifier
.length
) {
2939 appendDataProperty(properties
, CFSTR("Key Identifier"),
2940 &akid
.keyIdentifier
);
2942 if (akid
.authorityCertIssuer
.length
||
2943 akid
.authorityCertSerialNumber
.length
) {
2944 require_quiet(akid
.authorityCertIssuer
.length
&&
2945 akid
.authorityCertSerialNumber
.length
, badDER
);
2946 /* Perhaps put in a subsection called Authority Certificate Issuer. */
2947 appendGeneralNamesContent(properties
,
2948 &akid
.authorityCertIssuer
);
2949 appendIntegerProperty(properties
,
2950 CFSTR("Authority Certificate Serial Number"),
2951 &akid
.authorityCertSerialNumber
);
2956 appendInvalidProperty(properties
, CFSTR("Authority Key Identifier"),
2961 PolicyConstraints ::= SEQUENCE {
2962 requireExplicitPolicy [0] SkipCerts OPTIONAL,
2963 inhibitPolicyMapping [1] SkipCerts OPTIONAL }
2965 SkipCerts ::= INTEGER (0..MAX)
2967 static void appendPolicyConstraints(CFMutableArrayRef properties
,
2968 const DERItem
*extnValue
) {
2969 DERPolicyConstraints pc
;
2971 drtn
= DERParseSequence(extnValue
,
2972 DERNumPolicyConstraintsItemSpecs
,
2973 DERPolicyConstraintsItemSpecs
,
2975 require_noerr_quiet(drtn
, badDER
);
2976 if (pc
.requireExplicitPolicy
.length
) {
2977 appendIntegerProperty(properties
,
2978 CFSTR("Require Explicit Policy"), &pc
.requireExplicitPolicy
);
2980 if (pc
.inhibitPolicyMapping
.length
) {
2981 appendIntegerProperty(properties
,
2982 CFSTR("Inhibit Policy Mapping"), &pc
.inhibitPolicyMapping
);
2988 appendInvalidProperty(properties
, CFSTR("Policy Constraints"), extnValue
);
2992 extendedKeyUsage EXTENSION ::= {
2993 SYNTAX SEQUENCE SIZE (1..MAX) OF KeyPurposeId
2994 IDENTIFIED BY id-ce-extKeyUsage }
2996 KeyPurposeId ::= OBJECT IDENTIFIER
2998 static void appendExtendedKeyUsage(CFMutableArrayRef properties
,
2999 const DERItem
*extnValue
) {
3002 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &derSeq
);
3003 require_noerr_quiet(drtn
, badDER
);
3004 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3005 DERDecodedInfo currDecoded
;
3006 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3007 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, badDER
);
3008 appendOIDProperty(properties
, CFSTR("Purpose"),
3009 &currDecoded
.content
);
3011 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3014 appendInvalidProperty(properties
, CFSTR("Extended Key Usage"), extnValue
);
3018 id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
3020 AuthorityInfoAccessSyntax ::=
3021 SEQUENCE SIZE (1..MAX) OF AccessDescription
3023 AccessDescription ::= SEQUENCE {
3024 accessMethod OBJECT IDENTIFIER,
3025 accessLocation GeneralName }
3027 id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
3029 id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
3031 id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
3033 static void appendInfoAccess(CFMutableArrayRef properties
,
3034 const DERItem
*extnValue
) {
3037 DERReturn drtn
= DERDecodeSeqInit(extnValue
, &tag
, &adSeq
);
3038 require_noerr_quiet(drtn
, badDER
);
3039 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3040 DERDecodedInfo adContent
;
3041 while ((drtn
= DERDecodeSeqNext(&adSeq
, &adContent
)) == DR_Success
) {
3042 require_quiet(adContent
.tag
== ASN1_CONSTR_SEQUENCE
, badDER
);
3043 DERAccessDescription ad
;
3044 drtn
= DERParseSequenceContent(&adContent
.content
,
3045 DERNumAccessDescriptionItemSpecs
,
3046 DERAccessDescriptionItemSpecs
,
3048 require_noerr_quiet(drtn
, badDER
);
3049 appendOIDProperty(properties
, CFSTR("Access Method"),
3051 //CFSTR("Access Location");
3052 appendGeneralNameProperty(properties
, &ad
.accessLocation
);
3054 require_quiet(drtn
== DR_EndOfSequence
, badDER
);
3057 appendInvalidProperty(properties
, CFSTR("Authority Information Access"),
3061 static void appendNetscapeCertType(CFMutableArrayRef properties
,
3062 const DERItem
*extnValue
) {
3063 static const CFStringRef certTypes
[] = {
3064 CFSTR("SSL client"),
3065 CFSTR("SSL server"),
3067 CFSTR("Object Signing"),
3071 CFSTR("Object Signing CA")
3073 appendBitStringNames(properties
, CFSTR("Usage"), extnValue
,
3074 certTypes
, sizeof(certTypes
) / sizeof(*certTypes
));
3078 static void appendEntrustVersInfo(CFMutableArrayRef properties
,
3079 const DERItem
*extnValue
) {
3083 * The list of Qualified Cert Statement statementIds we understand, even though
3084 * we don't actually do anything with them; if these are found in a Qualified
3085 * Cert Statement that's critical, we can truthfully say "yes we understand this".
3087 static const CSSM_OID_PTR knownQualifiedCertStatements
[] =
3089 /* id-qcs := { id-pkix 11 } */
3090 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V1
, /* id-qcs 1 */
3091 (const CSSM_OID_PTR
)&CSSMOID_OID_QCS_SYNTAX_V2
, /* id-qcs 2 */
3092 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_COMPLIANCE
,
3093 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_LIMIT_VALUE
,
3094 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_RETENTION
,
3095 (const CSSM_OID_PTR
)&CSSMOID_ETSI_QCS_QC_SSCD
3097 #define NUM_KNOWN_QUAL_CERT_STATEMENTS (sizeof(knownQualifiedCertStatements) / sizeof(CSSM_OID_PTR))
3099 static void appendQCCertStatements(CFMutableArrayRef properties
,
3100 const DERItem
*extnValue
) {
3105 static bool appendPrintableDERSequenceP(CFMutableArrayRef properties
,
3106 CFStringRef label
, const DERItem
*sequence
) {
3109 DERReturn drtn
= DERDecodeSeqInit(sequence
, &tag
, &derSeq
);
3110 require_noerr_quiet(drtn
, badSequence
);
3111 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, badSequence
);
3112 DERDecodedInfo currDecoded
;
3113 bool appendedSomething
= false;
3114 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
3115 switch (currDecoded
.tag
)
3118 case ASN1_SEQUENCE
: // 16
3119 case ASN1_SET
: // 17
3120 // skip constructed object lengths
3123 case ASN1_UTF8_STRING
: // 12
3124 case ASN1_NUMERIC_STRING
: // 18
3125 case ASN1_PRINTABLE_STRING
: // 19
3126 case ASN1_T61_STRING
: // 20, also ASN1_TELETEX_STRING
3127 case ASN1_VIDEOTEX_STRING
: // 21
3128 case ASN1_IA5_STRING
: // 22
3129 case ASN1_GRAPHIC_STRING
: // 25
3130 case ASN1_VISIBLE_STRING
: // 26, also ASN1_ISO646_STRING
3131 case ASN1_GENERAL_STRING
: // 27
3132 case ASN1_UNIVERSAL_STRING
: // 28
3134 CFStringRef string
=
3135 copyDERThingContentDescription(CFGetAllocator(properties
),
3136 currDecoded
.tag
, &currDecoded
.content
, false);
3137 //CFStringRef cleanString = copyStringRemovingPercentEscapes(string);
3139 appendPropertyP(properties
, kSecPropertyTypeString
, label
,
3142 appendedSomething
= true;
3149 require_quiet(drtn
== DR_EndOfSequence
, badSequence
);
3150 return appendedSomething
;
3155 static void appendExtension(CFMutableArrayRef parent
,
3156 const SecCertificateExtension
*extn
) {
3157 CFAllocatorRef allocator
= CFGetAllocator(parent
);
3158 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3159 &kCFTypeArrayCallBacks
);
3161 *extnID
= &extn
->extnID
,
3162 *extnValue
= &extn
->extnValue
;
3164 appendBoolProperty(properties
, CFSTR("Critical"), extn
->critical
);
3167 bool handled
= true;
3168 /* Extensions that we know how to handle ourselves... */
3169 if (extnID
->length
== oidSubjectKeyIdentifier
.length
&&
3170 !memcmp(extnID
->data
, oidSubjectKeyIdentifier
.data
, extnID
->length
- 1))
3172 switch (extnID
->data
[extnID
->length
- 1]) {
3173 case 14: /* SubjectKeyIdentifier id-ce 14 */
3174 appendSubjectKeyIdentifier(properties
, extnValue
);
3176 case 15: /* KeyUsage id-ce 15 */
3177 appendKeyUsage(properties
, extnValue
);
3179 case 16: /* PrivateKeyUsagePeriod id-ce 16 */
3180 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3182 case 17: /* SubjectAltName id-ce 17 */
3183 case 18: /* IssuerAltName id-ce 18 */
3184 appendGeneralNames(properties
, extnValue
);
3186 case 19: /* BasicConstraints id-ce 19 */
3187 appendBasicConstraints(properties
, extnValue
);
3189 case 30: /* NameConstraints id-ce 30 */
3192 case 31: /* CRLDistributionPoints id-ce 31 */
3193 appendCrlDistributionPoints(properties
, extnValue
);
3195 case 32: /* CertificatePolicies id-ce 32 */
3196 appendCertificatePolicies(properties
, extnValue
);
3198 case 33: /* PolicyMappings id-ce 33 */
3201 case 35: /* AuthorityKeyIdentifier id-ce 35 */
3202 appendAuthorityKeyIdentifier(properties
, extnValue
);
3204 case 36: /* PolicyConstraints id-ce 36 */
3205 appendPolicyConstraints(properties
, extnValue
);
3207 case 37: /* ExtKeyUsage id-ce 37 */
3208 appendExtendedKeyUsage(properties
, extnValue
);
3210 case 46: /* FreshestCRL id-ce 46 */
3213 case 54: /* InhibitAnyPolicy id-ce 54 */
3220 } else if (extnID
->length
== oidAuthorityInfoAccess
.length
&&
3221 !memcmp(extnID
->data
, oidAuthorityInfoAccess
.data
, extnID
->length
- 1))
3223 switch (extnID
->data
[extnID
->length
- 1]) {
3224 case 1: /* AuthorityInfoAccess id-pe 1 */
3225 appendInfoAccess(properties
, extnValue
);
3227 case 3: /* QCStatements id-pe 3 */
3230 case 11: /* SubjectInfoAccess id-pe 11 */
3231 appendInfoAccess(properties
, extnValue
);
3237 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3238 /* 2.16.840.1.113730.1.1 netscape 1 1 */
3239 appendNetscapeCertType(properties
, extnValue
);
3245 /* Try to parse and display printable string(s). */
3246 if (appendPrintableDERSequenceP(properties
, CFSTR("Data"), extnValue
)) {
3247 /* Nothing to do here appendPrintableDERSequenceP did the work. */
3249 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3250 appendUnparsedProperty(properties
, CFSTR("Data"), extnValue
);
3254 /* Extensions that we know how to handle ourselves... */
3255 if (DEROidCompare(extnID
, &oidSubjectKeyIdentifier
)) {
3256 appendSubjectKeyIdentifier(properties
, extnValue
);
3257 } else if (DEROidCompare(extnID
, &oidKeyUsage
)) {
3258 appendKeyUsage(properties
, extnValue
);
3259 } else if (DEROidCompare(extnID
, &oidPrivateKeyUsagePeriod
)) {
3260 appendPrivateKeyUsagePeriod(properties
, extnValue
);
3261 } else if (DEROidCompare(extnID
, &oidSubjectAltName
)) {
3262 appendGeneralNames(properties
, extnValue
);
3263 } else if (DEROidCompare(extnID
, &oidIssuerAltName
)) {
3264 appendGeneralNames(properties
, extnValue
);
3265 } else if (DEROidCompare(extnID
, &oidBasicConstraints
)) {
3266 appendBasicConstraints(properties
, extnValue
);
3267 } else if (DEROidCompare(extnID
, &oidCrlDistributionPoints
)) {
3268 appendCrlDistributionPoints(properties
, extnValue
);
3269 } else if (DEROidCompare(extnID
, &oidCertificatePolicies
)) {
3270 appendCertificatePolicies(properties
, extnValue
);
3271 } else if (DEROidCompare(extnID
, &oidAuthorityKeyIdentifier
)) {
3272 appendAuthorityKeyIdentifier(properties
, extnValue
);
3273 } else if (DEROidCompare(extnID
, &oidPolicyConstraints
)) {
3274 appendPolicyConstraints(properties
, extnValue
);
3275 } else if (DEROidCompare(extnID
, &oidExtendedKeyUsage
)) {
3276 appendExtendedKeyUsage(properties
, extnValue
);
3277 } else if (DEROidCompare(extnID
, &oidAuthorityInfoAccess
)) {
3278 appendInfoAccess(properties
, extnValue
);
3279 } else if (DEROidCompare(extnID
, &oidSubjectInfoAccess
)) {
3280 appendInfoAccess(properties
, extnValue
);
3281 } else if (DEROidCompare(extnID
, &oidNetscapeCertType
)) {
3282 appendNetscapeCertType(properties
, extnValue
);
3284 } else if (DEROidCompare(extnID
, &oidEntrustVersInfo
)) {
3285 appendEntrustVersInfo(properties
, extnValue
);
3288 /* Try to parse and display printable string(s). */
3289 if (appendPrintableDERSequenceP(properties
, CFSTR("Data"), extnValue
)) {
3290 /* Nothing to do here appendPrintableDERSequenceP did the work. */
3292 /* Couldn't parse extension; dump the raw unparsed data as hex. */
3293 appendUnparsedProperty(properties
, CFSTR("Data"), extnValue
);
3296 CFStringRef oid_string
= copyLocalizedOidDescription(allocator
, extnID
);
3297 appendPropertyP(parent
, kSecPropertyTypeSection
, oid_string
, properties
);
3298 CFRelease(oid_string
);
3299 CFRelease(properties
);
3302 /* Different types of summary types from least desired to most desired. */
3305 kSummaryTypePrintable
,
3306 kSummaryTypeOrganizationName
,
3307 kSummaryTypeOrganizationalUnitName
,
3308 kSummaryTypeCommonName
,
3312 enum SummaryType type
;
3313 CFStringRef summary
;
3314 CFStringRef description
;
3317 static OSStatus
obtainSummaryFromX501Name(void *context
,
3318 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
3319 struct Summary
*summary
= (struct Summary
*)context
;
3320 enum SummaryType stype
= kSummaryTypeNone
;
3321 CFStringRef string
= NULL
;
3322 if (DEROidCompare(type
, &oidCommonName
)) {
3323 /* We skip Common Names that have generic values. */
3324 const char tfm
[] = "Thawte Freemail Member";
3325 if ((value
->length
== sizeof(tfm
) + 1) &&
3326 !memcmp(value
->data
+ 2, tfm
, sizeof(tfm
) - 1)) {
3327 return errSecSuccess
;
3329 stype
= kSummaryTypeCommonName
;
3330 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
3331 stype
= kSummaryTypeOrganizationalUnitName
;
3332 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
3333 stype
= kSummaryTypeOrganizationName
;
3334 } else if (DEROidCompare(type
, &oidDescription
)) {
3335 if (!summary
->description
) {
3336 summary
->description
= string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3339 stype
= kSummaryTypePrintable
;
3341 stype
= kSummaryTypePrintable
;
3344 /* Use the first field we encounter of the highest priority type. */
3345 if (summary
->type
< stype
) {
3347 string
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
3351 CFReleaseSafe(summary
->summary
);
3352 summary
->summary
= string
;
3353 summary
->type
= stype
;
3356 CFReleaseSafe(string
);
3359 return errSecSuccess
;
3362 CFStringRef
SecCertificateCopySubjectSummaryP(SecCertificateRefP certificate
) {
3363 struct Summary summary
= {};
3364 parseX501NameContent(&certificate
->_subject
, &summary
, obtainSummaryFromX501Name
);
3365 /* If we found a description and a common name we change the summary to
3366 CommonName (Description). */
3367 if (summary
.description
) {
3368 if (summary
.type
== kSummaryTypeCommonName
) {
3369 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
3370 CFSTR("%@ (%@)"), summary
.summary
, summary
.description
);
3371 CFRelease(summary
.summary
);
3372 summary
.summary
= newSummary
;
3374 CFRelease(summary
.description
);
3377 if (!summary
.summary
) {
3378 /* If we didn't find a suitable printable string in the subject at all, we try
3379 the first email address in the certificate instead. */
3380 CFArrayRef names
= SecCertificateCopyRFC822NamesP(certificate
);
3382 /* If we didn't find any email addresses in the certificate, we try finding
3383 a DNS name instead. */
3384 names
= SecCertificateCopyDNSNamesP(certificate
);
3387 summary
.summary
= CFArrayGetValueAtIndex(names
, 0);
3388 CFRetain(summary
.summary
);
3393 return summary
.summary
;
3396 CFStringRef
SecCertificateCopyIssuerSummaryP(SecCertificateRefP certificate
) {
3397 struct Summary summary
= {};
3398 parseX501NameContent(&certificate
->_issuer
, &summary
, obtainSummaryFromX501Name
);
3399 /* If we found a description and a common name we change the summary to
3400 CommonName (Description). */
3401 if (summary
.description
) {
3402 if (summary
.type
== kSummaryTypeCommonName
) {
3403 CFStringRef newSummary
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
3404 CFSTR("%@ (%@)"), summary
.summary
, summary
.description
);
3405 CFRelease(summary
.summary
);
3406 summary
.summary
= newSummary
;
3408 CFRelease(summary
.description
);
3411 return summary
.summary
;
3414 /* Return the earliest date on which all certificates in this chain are still
3416 static CFAbsoluteTime
SecCertificateGetChainsLastValidity(
3417 SecCertificateRefP certificate
) {
3418 CFAbsoluteTime earliest
= certificate
->_notAfter
;
3420 while (certificate
->_parent
) {
3421 certificate
= certificate
->_parent
;
3422 if (earliest
> certificate
->_notAfter
)
3423 earliest
= certificate
->_notAfter
;
3430 /* Return the latest date on which all certificates in this chain will be
3432 static CFAbsoluteTime
SecCertificateGetChainsFirstValidity(
3433 SecCertificateRefP certificate
) {
3434 CFAbsoluteTime latest
= certificate
->_notBefore
;
3436 while (certificate
->_parent
) {
3437 certificate
= certificate
->_parent
;
3438 if (latest
< certificate
->_notBefore
)
3439 latest
= certificate
->_notBefore
;
3446 bool SecCertificateIsValidP(SecCertificateRefP certificate
,
3447 CFAbsoluteTime verifyTime
) {
3449 return certificate
->_notBefore
<= verifyTime
&&
3450 verifyTime
<= certificate
->_notAfter
;
3453 CFIndex
SecCertificateVersionP(SecCertificateRefP certificate
) {
3454 return certificate
->_version
+ 1;
3457 CFAbsoluteTime
SecCertificateNotValidBeforeP(SecCertificateRefP certificate
) {
3458 return certificate
->_notBefore
;
3461 CFAbsoluteTime
SecCertificateNotValidAfterP(SecCertificateRefP certificate
) {
3462 return certificate
->_notAfter
;
3465 CFMutableArrayRef
SecCertificateCopySummaryPropertiesP(
3466 SecCertificateRefP certificate
, CFAbsoluteTime verifyTime
) {
3467 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3468 CFMutableArrayRef summary
= CFArrayCreateMutable(allocator
, 0,
3469 &kCFTypeArrayCallBacks
);
3471 /* First we put the subject summary name. */
3472 CFStringRef ssummary
= SecCertificateCopySubjectSummaryP(certificate
);
3474 appendPropertyP(summary
, kSecPropertyTypeTitle
,
3476 CFRelease(ssummary
);
3479 CFStringRef isummary
= CFSTR("Issuer Summary");
3480 appendPropertyP(summary
, kSecPropertyTypeString
,
3481 CFSTR("Issued By"), isummary
);
3482 CFRelease(isummary
);
3485 /* Let see if this certificate is currently valid. */
3487 CFAbsoluteTime when
;
3488 CFStringRef message
;
3490 if (verifyTime
> certificate
->_notAfter
) {
3491 label
= CFSTR("Expired");
3492 when
= certificate
->_notAfter
;
3493 ptype
= kSecPropertyTypeError
;
3494 message
= CFSTR("This certificate has expired");
3495 } else if (certificate
->_notBefore
> verifyTime
) {
3496 label
= CFSTR("Valid from");
3497 when
= certificate
->_notBefore
;
3498 ptype
= kSecPropertyTypeError
;
3499 message
= CFSTR("This certificate is not yet valid");
3501 CFAbsoluteTime last
= SecCertificateGetChainsLastValidity(certificate
);
3502 CFAbsoluteTime first
= SecCertificateGetChainsFirstValidity(certificate
);
3503 if (verifyTime
> last
) {
3504 label
= CFSTR("Expired");
3506 ptype
= kSecPropertyTypeError
;
3507 message
= CFSTR("This certificate has an issuer that has expired");
3508 } else if (verifyTime
< first
) {
3509 label
= CFSTR("Valid from");
3511 ptype
= kSecPropertyTypeError
;
3512 message
= CFSTR("This certificate has an issuer that is not yet valid");
3514 label
= CFSTR("Expires");
3515 when
= certificate
->_notAfter
;
3516 ptype
= kSecPropertyTypeSuccess
;
3517 message
= CFSTR("This certificate is valid");
3521 appendDateProperty(summary
, label
, when
);
3522 appendPropertyP(summary
, ptype
, NULL
, message
);
3527 CFArrayRef
SecCertificateCopyPropertiesP(SecCertificateRefP certificate
) {
3528 if (!certificate
->_properties
) {
3529 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
3530 CFMutableArrayRef properties
= CFArrayCreateMutable(allocator
, 0,
3531 &kCFTypeArrayCallBacks
);
3533 /* First we put the Subject Name in the property list. */
3534 CFArrayRef subject_plist
= createPropertiesForX501NameContent(allocator
,
3535 &certificate
->_subject
);
3536 appendPropertyP(properties
, kSecPropertyTypeSection
,
3537 CFSTR("Subject Name"), subject_plist
);
3538 CFRelease(subject_plist
);
3541 /* Put Normalized subject in for testing. */
3542 if (certificate
->_normalizedSubject
) {
3543 DERItem nsubject
= {
3544 (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
),
3545 CFDataGetLength(certificate
->_normalizedSubject
)
3547 CFArrayRef nsubject_plist
= createPropertiesForX501NameContent(allocator
,
3549 appendPropertyP(properties
, kSecPropertyTypeSection
,
3550 CFSTR("Normalized Subject Name"), nsubject_plist
);
3551 CFRelease(nsubject_plist
);
3555 /* Next we put the Issuer Name in the property list. */
3556 CFArrayRef issuer_plist
= createPropertiesForX501NameContent(allocator
,
3557 &certificate
->_issuer
);
3558 appendPropertyP(properties
, kSecPropertyTypeSection
,
3559 CFSTR("Issuer Name"), issuer_plist
);
3560 CFRelease(issuer_plist
);
3563 /* Certificate version/type. */
3564 bool isRoot
= false;
3565 CFStringRef typeString
= CFStringCreateWithFormat(allocator
, NULL
,
3566 CFSTR("X.509 version %d %scertificate"),
3567 certificate
->_version
+ 1, isRoot
? "root " : "");
3568 appendPropertyP(properties
, kSecPropertyTypeString
,
3569 CFSTR("Certificate Type"), typeString
);
3570 CFRelease(typeString
);
3574 CFStringRef versionString
= CFStringCreateWithFormat(allocator
,
3575 NULL
, CFSTR("%d"), certificate
->_version
+ 1);
3576 appendPropertyP(properties
, kSecPropertyTypeString
,
3577 CFSTR("Version"), versionString
);
3578 CFRelease(versionString
);
3581 if (certificate
->_serialNum
.length
) {
3582 appendIntegerProperty(properties
, CFSTR("Serial Number"),
3583 &certificate
->_serialNum
);
3586 /* Signature algorithm. */
3588 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
3589 &certificate
->_sigAlg
);
3591 appendAlgorithmProperty(properties
, CFSTR("Signature Algorithm"),
3592 &certificate
->_tbsSigAlg
);
3595 /* Validity dates. */
3596 appendDateProperty(properties
, CFSTR("Not Valid Before"),
3597 certificate
->_notBefore
);
3598 appendDateProperty(properties
, CFSTR("Not Valid After"),
3599 certificate
->_notAfter
);
3601 if (certificate
->_subjectUniqueID
.length
) {
3602 appendDataProperty(properties
, CFSTR("Subject Unique ID"),
3603 &certificate
->_subjectUniqueID
);
3605 if (certificate
->_issuerUniqueID
.length
) {
3606 appendDataProperty(properties
, CFSTR("Issuer Unique ID"),
3607 &certificate
->_issuerUniqueID
);
3610 /* Public key algorithm. */
3611 appendAlgorithmProperty(properties
, CFSTR("Public Key Algorithm"),
3612 &certificate
->_algId
);
3614 /* Consider breaking down an RSA public key into modulus and
3616 appendDataProperty(properties
, CFSTR("Public Key Data"),
3617 &certificate
->_pubKeyDER
);
3619 /* @@@ Key Usage. */
3621 appendDataProperty(properties
, CFSTR("Signature"),
3622 &certificate
->_signature
);
3625 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
3626 appendExtension(properties
, &certificate
->_extensions
[ix
]);
3629 /* @@@ Key Fingerprints. */
3631 certificate
->_properties
= properties
;
3634 CFRetain(certificate
->_properties
);
3635 return certificate
->_properties
;
3638 CFDataRef
SecCertificateCopySerialNumberP(
3639 SecCertificateRefP certificate
) {
3640 if (certificate
->_serialNumber
) {
3641 CFRetain(certificate
->_serialNumber
);
3643 return certificate
->_serialNumber
;
3647 * Accessor for normalized issuer content
3649 CFDataRef
SecCertificateGetNormalizedIssuerContentP(
3650 SecCertificateRefP certificate
) {
3651 return certificate
->_normalizedIssuer
;
3655 * Accessor for normalized subject content
3657 CFDataRef
SecCertificateGetNormalizedSubjectContentP(
3658 SecCertificateRefP certificate
) {
3659 return certificate
->_normalizedSubject
;
3663 * Returns DER-encoded normalized issuer sequence
3664 * for use with SecItemCopyMatching; caller must release
3666 CFDataRef
SecCertificateCopyNormalizedIssuerSequenceP(
3667 SecCertificateRefP certificate
) {
3668 if (!certificate
|| !certificate
->_normalizedIssuer
) {
3672 tmpdi
.data
= (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedIssuer
);
3673 tmpdi
.length
= CFDataGetLength(certificate
->_normalizedIssuer
);
3675 return SecDERItemCopySequenceP(&tmpdi
);
3679 * Returns DER-encoded normalized subject sequence
3680 * for use with SecItemCopyMatching; caller must release
3682 CFDataRef
SecCertificateCopyNormalizedSubjectSequenceP(
3683 SecCertificateRefP certificate
) {
3684 if (!certificate
|| !certificate
->_normalizedSubject
) {
3688 tmpdi
.data
= (DERByte
*)CFDataGetBytePtr(certificate
->_normalizedSubject
);
3689 tmpdi
.length
= CFDataGetLength(certificate
->_normalizedSubject
);
3691 return SecDERItemCopySequenceP(&tmpdi
);
3694 /* Verify that certificate was signed by issuerKey. */
3695 OSStatus
SecCertificateIsSignedByP(SecCertificateRefP certificate
,
3696 SecKeyRefP issuerKey
) {
3697 /* Setup algId in SecAsn1AlgId format. */
3699 algId
.algorithm
.Length
= certificate
->_tbsSigAlg
.oid
.length
;
3700 algId
.algorithm
.Data
= certificate
->_tbsSigAlg
.oid
.data
;
3701 algId
.parameters
.Length
= certificate
->_tbsSigAlg
.params
.length
;
3702 algId
.parameters
.Data
= certificate
->_tbsSigAlg
.params
.data
;
3705 OSStatus status
= SecKeyDigestAndVerify(issuerKey
, &algId
,
3706 certificate
->_tbs
.data
, certificate
->_tbs
.length
,
3707 certificate
->_signature
.data
, certificate
->_signature
.length
);
3709 secinfo("verify", "signature verify failed: %d", status
);
3710 return errSecNotSigner
;
3714 return errSecSuccess
;
3718 static OSStatus
SecCertificateIsIssuedBy(SecCertificateRefP certificate
,
3719 SecCertificateRefP issuer
, bool signatureCheckOnly
) {
3720 if (!signatureCheckOnly
) {
3721 /* It turns out we don't actually need to use normalized subject and
3722 issuer according to rfc2459. */
3724 /* If present we should check issuerID against the issuer subjectID. */
3726 /* If we have an AuthorityKeyIdentifier extension that has a keyIdentifier
3727 then we should look for a SubjectKeyIdentifier in the issuer
3729 If we have a authorityCertSerialNumber we can use that for chaining.
3730 If we have a authorityCertIssuer we can use that? (or not) */
3732 /* Verify that this cert was issued by issuer. Do so by chaining
3733 either issuerID to subjectID or normalized issuer to normalized
3735 CFDataRef normalizedIssuer
=
3736 SecCertificateGetNormalizedIssuerContentP(certificate
);
3737 CFDataRef normalizedIssuerSubject
=
3738 SecCertificateGetNormalizedSubjectContentP(issuer
);
3739 if (normalizedIssuer
&& normalizedIssuerSubject
&&
3740 !CFEqual(normalizedIssuer
, normalizedIssuerSubject
))
3741 return errSecIssuerMismatch
;
3744 /* Next verify that this cert was signed by issuer. */
3745 SecKeyRef issuerKey
= SecCertificateGetPublicKey(issuer
);
3747 /* Get the encodedDigestInfo from the digest of the subject's TBSCert */
3748 /* FIXME: We sould cache this (or at least the digest) until we find
3749 a suitable issuer. */
3750 uint8_t signedData
[DER_SHA1_DIGEST_INFO_LEN
];
3751 CFIndex signedDataLength
;
3752 CertVerifyReturn crtn
;
3753 if (DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidSha1Rsa
)) {
3754 signedDataLength
= DER_SHA1_DIGEST_INFO_LEN
;
3755 crtn
= sha1DigestInfo(&certificate
->_tbs
, signedData
);
3756 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd5Rsa
)) {
3757 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3758 crtn
= mdDigestInfo(WD_MD5
, &certificate
->_tbs
, signedData
);
3759 } else if(DEROidCompare(&certificate
->_tbsSigAlg
.oid
, &oidMd2Rsa
)) {
3760 signedDataLength
= DER_MD_DIGEST_INFO_LEN
;
3761 crtn
= mdDigestInfo(WD_MD2
, &certificate
->_tbs
, signedData
);
3763 secinfo("verify", "unsupported algorithm");
3764 return errSecUnsupportedAlgorithm
;
3767 secinfo("verify", "*DigestInfo returned: %d", crtn
);
3768 /* FIXME: Do proper error code translation. */
3769 return errSecUnsupportedAlgorithm
;
3772 OSStatus status
= SecKeyRawVerify(issuerKey
, kSecPaddingPKCS1
,
3773 signedData
, signedDataLength
,
3774 certificate
->_signature
.data
, certificate
->_signature
.length
);
3776 secinfo("verify", "signature verify failed: %d", status
);
3777 return errSecNotSigner
;
3780 return errSecSuccess
;
3783 static OSStatus
_SecCertificateSetParent(SecCertificateRefP certificate
,
3784 SecCertificateRefP issuer
, bool signatureCheckOnly
) {
3786 if (certificate
->_parent
) {
3787 /* Setting a certificates issuer twice is only allowed if the new
3788 issuer is equal to the current one. */
3789 return issuer
&& CFEqual(certificate
->_parent
, issuer
);
3793 OSStatus status
= SecCertificateIsIssuedBy(certificate
, issuer
,
3794 signatureCheckOnly
);
3796 OSStatus status
= errSecSuccess
;
3799 if (CFEqual(certificate
, issuer
)) {
3800 /* We don't retain ourselves cause that would be bad mojo,
3801 however we do record that we are properly self signed. */
3802 certificate
->_isSelfSigned
= kSecSelfSignedTrue
;
3803 secinfo("cert", "set self as parent");
3804 return errSecSuccess
;
3808 certificate
->_parent
= issuer
;
3809 certificate
->_isSelfSigned
= kSecSelfSignedFalse
;
3815 static bool SecCertificateIsSelfSignedP(SecCertificateRefP certificate
) {
3816 if (certificate
->_isSelfSigned
== kSecSelfSignedUnknown
) {
3817 certificate
->_isSelfSigned
=
3818 (SecCertificateIsIssuedBy(certificate
, certificate
, false) ?
3819 kSecSelfSignedTrue
: kSecSelfSignedFalse
);
3822 return certificate
->_isSelfSigned
== kSecSelfSignedTrue
;
3825 /* Return true iff we were able to set our own parent from one of the
3826 certificates in other_certificates, return false otherwise. If
3827 signatureCheckOnly is true, we can skip the subject == issuer or
3828 authorityKeyIdentifier tests. */
3829 static bool SecCertificateSetParentFrom(SecCertificateRefP certificate
,
3830 CFArrayRef other_certificates
, bool signatureCheckOnly
) {
3831 CFIndex count
= CFArrayGetCount(other_certificates
);
3833 for (ix
= 0; ix
< count
; ++ix
) {
3834 SecCertificateRefP candidate
= (SecCertificateRefP
)
3835 CFArrayGetValueAtIndex(other_certificates
, ix
);
3836 if (_SecCertificateSetParent(certificate
, candidate
,
3837 signatureCheckOnly
))
3843 /* Lookup the parent of certificate in the keychain and set it. */
3844 static bool SecCertificateFindParent(SecCertificateRefP certificate
) {
3845 /* FIXME: Search for things other than just subject of our issuer if we
3846 have a subjectID or authorityKeyIdentifier. */
3847 CFDataRef normalizedIssuer
=
3848 SecCertificateGetNormalizedIssuerContentP(certificate
);
3849 const void *keys
[] = {
3856 kSecClassCertificate
,
3861 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4,
3862 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3864 OSStatus status
= SecItemCopyMatching(query
, &results
);
3867 secinfo("cert", "SecCertificateFindParent: SecItemCopyMatching: %d",
3871 CFArrayRef certs
= (CFArrayRef
)results
;
3872 /* Since we already know the certificates we are providing as candidates
3873 have been checked for subject matching, we can ask
3874 SecCertificateSetParentFrom to skip everything except the signature
3876 bool result
= SecCertificateSetParentFrom(certificate
, certs
, true);
3881 OSStatus
SecCertificateCompleteChainP(SecCertificateRefP certificate
,
3882 CFArrayRef other_certificates
) {
3884 if (certificate
->_parent
== NULL
) {
3885 if (SecCertificateIsSelfSignedP(certificate
))
3886 return errSecSuccess
;
3887 if (!other_certificates
||
3888 !SecCertificateSetParentFrom(certificate
, other_certificates
,\
3890 if (!SecCertificateFindParent(certificate
))
3891 return errSecIssuerNotFound
;
3894 certificate
= certificate
->_parent
;
3899 static OSStatus
appendIPAddressesFromGeneralNames(void *context
,
3900 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
3901 CFMutableArrayRef ipAddresses
= (CFMutableArrayRef
)context
;
3902 if (gnType
== GNT_IPAddress
) {
3903 CFStringRef string
= copyIPAddressContentDescription(
3904 kCFAllocatorDefault
, generalName
);
3906 CFArrayAppendValue(ipAddresses
, string
);
3909 return errSecInvalidCertificate
;
3912 return errSecSuccess
;
3915 CFArrayRef
SecCertificateCopyIPAddressesP(SecCertificateRefP certificate
) {
3916 /* These can only exist in the subject alt name. */
3917 if (!certificate
->_subjectAltName
)
3920 CFMutableArrayRef ipAddresses
= CFArrayCreateMutable(kCFAllocatorDefault
,
3921 0, &kCFTypeArrayCallBacks
);
3922 OSStatus status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
3923 ipAddresses
, appendIPAddressesFromGeneralNames
);
3924 if (status
|| CFArrayGetCount(ipAddresses
) == 0) {
3925 CFRelease(ipAddresses
);
3931 static OSStatus
appendDNSNamesFromGeneralNames(void *context
, SecCEGeneralNameType gnType
,
3932 const DERItem
*generalName
) {
3933 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
3934 if (gnType
== GNT_DNSName
) {
3935 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
3936 generalName
->data
, generalName
->length
,
3937 kCFStringEncodingUTF8
, FALSE
);
3939 CFArrayAppendValue(dnsNames
, string
);
3942 return errSecInvalidCertificate
;
3945 return errSecSuccess
;
3948 /* Return true if the passed in string matches the
3949 Preferred name syntax from sections 2.3.1. in RFC 1035.
3950 With the added check that we disallow empty dns names.
3951 Also in order to support wildcard DNSNames we allow for the '*'
3952 character anywhere in a dns component where we currently allow
3955 <domain> ::= <subdomain> | " "
3957 <subdomain> ::= <label> | <subdomain> "." <label>
3959 <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
3961 <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
3963 <let-dig-hyp> ::= <let-dig> | "-"
3965 <let-dig> ::= <letter> | <digit>
3967 <letter> ::= any one of the 52 alphabetic characters A through Z in
3968 upper case and a through z in lower case
3970 <digit> ::= any one of the ten digits 0 through 9
3972 static bool isDNSName(CFStringRef string
) {
3973 CFStringInlineBuffer buf
;
3974 CFIndex ix
, labelLength
= 0, length
= CFStringGetLength(string
);
3975 /* From RFC 1035 2.3.4. Size limits:
3976 labels 63 octets or less
3977 names 255 octets or less */
3978 require_quiet(length
<= 255, notDNS
);
3979 CFRange range
= { 0, length
};
3980 CFStringInitInlineBuffer(string
, &buf
, range
);
3984 kDNSStateAfterAlpha
,
3985 kDNSStateAfterDigit
,
3987 } state
= kDNSStateInital
;
3989 bool nonAlpha
= false;
3990 for (ix
= 0; ix
< length
; ++ix
) {
3991 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, ix
);
3994 require_quiet(labelLength
<= 64 &&
3995 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
3997 state
= kDNSStateAfterDot
;
4000 } else if (('A' <= ch
&& ch
<= 'Z') || ('a' <= ch
&& ch
<= 'z') ||
4002 state
= kDNSStateAfterAlpha
;
4003 } else if ('0' <= ch
&& ch
<= '9') {
4005 /* The requirement for labels to start with a letter was
4006 dropped so we don't check this anymore. */
4007 require_quiet(state
== kDNSStateAfterAlpha
||
4008 state
== kDNSStateAfterDigit
||
4009 state
== kDNSStateAfterDash
, notDNS
);
4011 state
= kDNSStateAfterDigit
;
4013 } else if (ch
== '-') {
4014 require_quiet(state
== kDNSStateAfterAlpha
||
4015 state
== kDNSStateAfterDigit
||
4016 state
== kDNSStateAfterDash
, notDNS
);
4017 state
= kDNSStateAfterDash
;
4024 /* We don't allow a dns name to end in a dot, and we require the
4025 final name component to only have alphanumeric chars. */
4026 require_quiet(!nonAlpha
&& labelLength
<= 63 &&
4027 (state
== kDNSStateAfterAlpha
|| state
== kDNSStateAfterDigit
),
4035 static OSStatus
appendDNSNamesFromX501Name(void *context
, const DERItem
*type
,
4036 const DERItem
*value
, CFIndex rdnIX
) {
4037 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4038 if (DEROidCompare(type
, &oidCommonName
)) {
4039 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4042 if (isDNSName(string
)) {
4043 /* We found a common name that is formatted like a valid
4045 CFArrayAppendValue(dnsNames
, string
);
4049 return errSecInvalidCertificate
;
4052 return errSecSuccess
;
4055 /* Not everything returned by this function is going to be a proper DNS name,
4056 we also return the certificates common name entries from the subject,
4057 assuming they look like dns names as specified in RFC 1035. */
4058 CFArrayRef
SecCertificateCopyDNSNamesP(SecCertificateRefP certificate
) {
4059 /* These can exist in the subject alt name or in the subject. */
4060 CFMutableArrayRef dnsNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4061 0, &kCFTypeArrayCallBacks
);
4062 OSStatus status
= errSecSuccess
;
4063 if (certificate
->_subjectAltName
) {
4064 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4065 dnsNames
, appendDNSNamesFromGeneralNames
);
4067 /* RFC 2818 section 3.1. Server Identity
4069 If a subjectAltName extension of type dNSName is present, that MUST
4070 be used as the identity. Otherwise, the (most specific) Common Name
4071 field in the Subject field of the certificate MUST be used. Although
4072 the use of the Common Name is existing practice, it is deprecated and
4073 Certification Authorities are encouraged to use the dNSName instead.
4076 This implies that if we found 1 or more DNSNames in the
4077 subjectAltName, we should not use the Common Name of the subject as
4080 if (!status
&& CFArrayGetCount(dnsNames
) == 0) {
4081 status
= parseX501NameContent(&certificate
->_subject
, dnsNames
,
4082 appendDNSNamesFromX501Name
);
4084 if (status
|| CFArrayGetCount(dnsNames
) == 0) {
4085 CFRelease(dnsNames
);
4091 static OSStatus
appendRFC822NamesFromGeneralNames(void *context
,
4092 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4093 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4094 if (gnType
== GNT_RFC822Name
) {
4095 CFStringRef string
= CFStringCreateWithBytes(kCFAllocatorDefault
,
4096 generalName
->data
, generalName
->length
,
4097 kCFStringEncodingASCII
, FALSE
);
4099 CFArrayAppendValue(dnsNames
, string
);
4102 return errSecInvalidCertificate
;
4105 return errSecSuccess
;
4108 static OSStatus
appendRFC822NamesFromX501Name(void *context
, const DERItem
*type
,
4109 const DERItem
*value
, CFIndex rdnIX
) {
4110 CFMutableArrayRef dnsNames
= (CFMutableArrayRef
)context
;
4111 if (DEROidCompare(type
, &oidEmailAddress
)) {
4112 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4115 CFArrayAppendValue(dnsNames
, string
);
4118 return errSecInvalidCertificate
;
4121 return errSecSuccess
;
4124 CFArrayRef
SecCertificateCopyRFC822NamesP(SecCertificateRefP certificate
) {
4125 /* These can exist in the subject alt name or in the subject. */
4126 CFMutableArrayRef rfc822Names
= CFArrayCreateMutable(kCFAllocatorDefault
,
4127 0, &kCFTypeArrayCallBacks
);
4128 OSStatus status
= errSecSuccess
;
4129 if (certificate
->_subjectAltName
) {
4130 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4131 rfc822Names
, appendRFC822NamesFromGeneralNames
);
4134 status
= parseX501NameContent(&certificate
->_subject
, rfc822Names
,
4135 appendRFC822NamesFromX501Name
);
4137 if (status
|| CFArrayGetCount(rfc822Names
) == 0) {
4138 CFRelease(rfc822Names
);
4144 static OSStatus
appendCommonNamesFromX501Name(void *context
,
4145 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4146 CFMutableArrayRef commonNames
= (CFMutableArrayRef
)context
;
4147 if (DEROidCompare(type
, &oidCommonName
)) {
4148 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4151 CFArrayAppendValue(commonNames
, string
);
4154 return errSecInvalidCertificate
;
4157 return errSecSuccess
;
4160 CFArrayRef
SecCertificateCopyCommonNamesP(SecCertificateRefP certificate
) {
4161 CFMutableArrayRef commonNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4162 0, &kCFTypeArrayCallBacks
);
4164 status
= parseX501NameContent(&certificate
->_subject
, commonNames
,
4165 appendCommonNamesFromX501Name
);
4166 if (status
|| CFArrayGetCount(commonNames
) == 0) {
4167 CFRelease(commonNames
);
4173 static OSStatus
appendOrganizationFromX501Name(void *context
,
4174 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4175 CFMutableArrayRef organization
= (CFMutableArrayRef
)context
;
4176 if (DEROidCompare(type
, &oidOrganizationName
)) {
4177 CFStringRef string
= copyDERThingDescription(kCFAllocatorDefault
,
4180 CFArrayAppendValue(organization
, string
);
4183 return errSecInvalidCertificate
;
4186 return errSecSuccess
;
4189 CFArrayRef
SecCertificateCopyOrganizationP(SecCertificateRefP certificate
) {
4190 CFMutableArrayRef organization
= CFArrayCreateMutable(kCFAllocatorDefault
,
4191 0, &kCFTypeArrayCallBacks
);
4193 status
= parseX501NameContent(&certificate
->_subject
, organization
,
4194 appendOrganizationFromX501Name
);
4195 if (status
|| CFArrayGetCount(organization
) == 0) {
4196 CFRelease(organization
);
4197 organization
= NULL
;
4199 return organization
;
4202 const SecCEBasicConstraints
*
4203 SecCertificateGetBasicConstraintsP(SecCertificateRefP certificate
) {
4204 if (certificate
->_basicConstraints
.present
)
4205 return &certificate
->_basicConstraints
;
4210 const SecCEPolicyConstraints
*
4211 SecCertificateGetPolicyConstraintsP(SecCertificateRefP certificate
) {
4212 if (certificate
->_policyConstraints
.present
)
4213 return &certificate
->_policyConstraints
;
4219 SecCertificateGetPolicyMappingsP(SecCertificateRefP certificate
) {
4220 return certificate
->_policyMappings
;
4223 const SecCECertificatePolicies
*
4224 SecCertificateGetCertificatePoliciesP(SecCertificateRefP certificate
) {
4225 if (certificate
->_certificatePolicies
.present
)
4226 return &certificate
->_certificatePolicies
;
4232 SecCertificateGetInhibitAnyPolicySkipCertsP(SecCertificateRefP certificate
) {
4233 return certificate
->_inhibitAnyPolicySkipCerts
;
4236 static OSStatus
appendNTPrincipalNamesFromGeneralNames(void *context
,
4237 SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
4238 CFMutableArrayRef ntPrincipalNames
= (CFMutableArrayRef
)context
;
4239 if (gnType
== GNT_OtherName
) {
4241 DERReturn drtn
= DERParseSequenceContent(generalName
,
4242 DERNumOtherNameItemSpecs
, DEROtherNameItemSpecs
,
4244 require_noerr_quiet(drtn
, badDER
);
4245 if (DEROidCompare(&on
.typeIdentifier
, &oidMSNTPrincipalName
)) {
4247 require_quiet(string
= copyDERThingDescription(kCFAllocatorDefault
,
4248 &on
.value
, true), badDER
);
4249 CFArrayAppendValue(ntPrincipalNames
, string
);
4253 return errSecSuccess
;
4256 return errSecInvalidCertificate
;
4260 CFArrayRef
SecCertificateCopyNTPrincipalNamesP(SecCertificateRefP certificate
) {
4261 CFMutableArrayRef ntPrincipalNames
= CFArrayCreateMutable(kCFAllocatorDefault
,
4262 0, &kCFTypeArrayCallBacks
);
4263 OSStatus status
= errSecSuccess
;
4264 if (certificate
->_subjectAltName
) {
4265 status
= parseGeneralNames(&certificate
->_subjectAltName
->extnValue
,
4266 ntPrincipalNames
, appendNTPrincipalNamesFromGeneralNames
);
4268 if (status
|| CFArrayGetCount(ntPrincipalNames
) == 0) {
4269 CFRelease(ntPrincipalNames
);
4270 ntPrincipalNames
= NULL
;
4272 return ntPrincipalNames
;
4275 static OSStatus
appendToRFC2253String(void *context
,
4276 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4277 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4281 ST stateOrProvinceName
4283 OU organizationalUnitName
4285 STREET streetAddress
4289 /* Prepend a + if this is not the first RDN in an RDN set.
4290 Otherwise prepend a , if this is not the first RDN. */
4292 CFStringAppend(string
, CFSTR("+"));
4293 else if (CFStringGetLength(string
)) {
4294 CFStringAppend(string
, CFSTR(","));
4297 CFStringRef label
, oid
= NULL
;
4298 /* @@@ Consider changing this to a dictionary lookup keyed by the
4299 decimal representation. */
4300 #if 0 // represent all labels as oids
4301 if (DEROidCompare(type
, &oidCommonName
)) {
4302 label
= CFSTR("CN");
4303 } else if (DEROidCompare(type
, &oidLocalityName
)) {
4305 } else if (DEROidCompare(type
, &oidStateOrProvinceName
)) {
4306 label
= CFSTR("ST");
4307 } else if (DEROidCompare(type
, &oidOrganizationName
)) {
4309 } else if (DEROidCompare(type
, &oidOrganizationalUnitName
)) {
4310 label
= CFSTR("OU");
4311 } else if (DEROidCompare(type
, &oidCountryName
)) {
4314 } else if (DEROidCompare(type
, &oidStreetAddress
)) {
4315 label
= CFSTR("STREET");
4316 } else if (DEROidCompare(type
, &oidDomainComponent
)) {
4317 label
= CFSTR("DC");
4318 } else if (DEROidCompare(type
, &oidUserID
)) {
4319 label
= CFSTR("UID");
4324 label
= oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, type
);
4327 CFStringAppend(string
, label
);
4328 CFStringAppend(string
, CFSTR("="));
4329 CFStringRef raw
= NULL
;
4331 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4334 /* Append raw to string while escaping:
4335 a space or "#" character occurring at the beginning of the string
4336 a space character occurring at the end of the string
4337 one of the characters ",", "+", """, "\", "<", ">" or ";"
4339 CFStringInlineBuffer buffer
;
4340 CFIndex ix
, length
= CFStringGetLength(raw
);
4341 CFRange range
= { 0, length
};
4342 CFStringInitInlineBuffer(raw
, &buffer
, range
);
4343 for (ix
= 0; ix
< length
; ++ix
) {
4344 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buffer
, ix
);
4346 CFStringAppendFormat(string
, NULL
, CFSTR("\\%02X"), ch
);
4347 } else if (ch
== ',' || ch
== '+' || ch
== '"' || ch
== '\\' ||
4348 ch
== '<' || ch
== '>' || ch
== ';' ||
4349 (ch
== ' ' && (ix
== 0 || ix
== length
- 1)) ||
4350 (ch
== '#' && ix
== 0)) {
4351 UniChar chars
[] = { '\\', ch
};
4352 CFStringAppendCharacters(string
, chars
, 2);
4354 CFStringAppendCharacters(string
, &ch
, 1);
4359 /* Append the value in hex. */
4360 CFStringAppend(string
, CFSTR("#"));
4362 for (ix
= 0; ix
< value
->length
; ++ix
)
4363 CFStringAppendFormat(string
, NULL
, CFSTR("%02X"), value
->data
[ix
]);
4368 return errSecSuccess
;
4371 CFStringRef
SecCertificateCopySubjectStringP(SecCertificateRefP certificate
) {
4372 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4373 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
, appendToRFC2253String
);
4374 if (status
|| CFStringGetLength(string
) == 0) {
4381 static OSStatus
appendToCompanyNameString(void *context
,
4382 const DERItem
*type
, const DERItem
*value
, CFIndex rdnIX
) {
4383 CFMutableStringRef string
= (CFMutableStringRef
)context
;
4384 if (CFStringGetLength(string
) != 0)
4385 return errSecSuccess
;
4387 if (!DEROidCompare(type
, &oidOrganizationName
))
4388 return errSecSuccess
;
4391 raw
= copyDERThingDescription(kCFAllocatorDefault
, value
, true);
4393 return errSecSuccess
;
4394 CFStringAppend(string
, raw
);
4397 return errSecSuccess
;
4400 CFStringRef
SecCertificateCopyCompanyNameP(SecCertificateRefP certificate
) {
4401 CFMutableStringRef string
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
4402 OSStatus status
= parseX501NameContent(&certificate
->_subject
, string
,
4403 appendToCompanyNameString
);
4404 if (status
|| CFStringGetLength(string
) == 0) {
4411 CFDataRef
SecDERItemCopySequenceP(DERItem
*content
) {
4412 DERSize seq_len_length
= DERLengthOfLength(content
->length
);
4413 size_t sequence_length
= 1 + seq_len_length
+ content
->length
;
4414 CFMutableDataRef sequence
= CFDataCreateMutable(kCFAllocatorDefault
,
4416 CFDataSetLength(sequence
, sequence_length
);
4417 uint8_t *sequence_ptr
= CFDataGetMutableBytePtr(sequence
);
4418 *sequence_ptr
++ = ONE_BYTE_ASN1_CONSTR_SEQUENCE
;
4419 require_noerr_quiet(DEREncodeLength(content
->length
,
4420 sequence_ptr
, &seq_len_length
), out
);
4421 sequence_ptr
+= seq_len_length
;
4422 memcpy(sequence_ptr
, content
->data
, content
->length
);
4425 CFReleaseSafe(sequence
);
4429 CFDataRef
SecCertificateCopyIssuerSequenceP(
4430 SecCertificateRefP certificate
) {
4431 return SecDERItemCopySequenceP(&certificate
->_issuer
);
4434 CFDataRef
SecCertificateCopySubjectSequenceP(
4435 SecCertificateRefP certificate
) {
4436 return SecDERItemCopySequenceP(&certificate
->_subject
);
4439 const DERAlgorithmId
*SecCertificateGetPublicKeyAlgorithmP(
4440 SecCertificateRefP certificate
) {
4441 return &certificate
->_algId
;
4444 const DERItem
*SecCertificateGetPublicKeyDataP(SecCertificateRefP certificate
) {
4445 return &certificate
->_pubKeyDER
;
4448 SecKeyRefP
SecCertificateCopyPublicKeyP(SecCertificateRefP certificate
) {
4449 SecKeyRefP publicKey
= NULL
;
4451 const DERAlgorithmId
*algId
=
4452 SecCertificateGetPublicKeyAlgorithmP(certificate
);
4453 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
4454 if (DEROidCompare(&algId
->oid
, &oidRsa
)) {
4455 publicKey
= SecKeyCreateRSAPublicKey(kCFAllocatorDefault
,
4456 keyData
->data
, keyData
->length
, kSecKeyEncodingPkcs1
);
4458 secinfo("cert", "Unsupported algorithm oid");
4465 CFDataRef
SecCertificateGetSHA1DigestP(SecCertificateRefP certificate
) {
4466 if (!certificate
->_sha1Digest
) {
4467 certificate
->_sha1Digest
=
4468 SecSHA1DigestCreate(CFGetAllocator(certificate
),
4469 certificate
->_der
.data
, certificate
->_der
.length
);
4472 return certificate
->_sha1Digest
;
4475 CFDataRef
SecCertificateCopyIssuerSHA1DigestP(SecCertificateRefP certificate
) {
4476 CFDataRef digest
= NULL
;
4477 CFDataRef issuer
= SecCertificateCopyIssuerSequenceP(certificate
);
4479 digest
= SecSHA1DigestCreate(kCFAllocatorDefault
,
4480 CFDataGetBytePtr(issuer
), CFDataGetLength(issuer
));
4486 CFDataRef
SecCertificateCopyPublicKeySHA1DigestP(SecCertificateRefP certificate
) {
4487 return SecSHA1DigestCreate(CFGetAllocator(certificate
),
4488 certificate
->_pubKeyDER
.data
, certificate
->_pubKeyDER
.length
);
4491 /* note: this function is exported with a non-P-suffix name.
4492 * since it doesn't accept or return a SecCertificateRefP type, this is OK for now.
4494 CFDataRef
SecCertificateCopyPublicKeySHA1DigestFromCertificateData(CFAllocatorRef allocator
,
4495 CFDataRef der_certificate
)
4497 CFDataRef result
= NULL
;
4498 SecCertificateRefP iosCertRef
= SecCertificateCreateWithDataP(allocator
, der_certificate
);
4499 if (NULL
== iosCertRef
)
4504 result
= SecCertificateCopyPublicKeySHA1DigestP(iosCertRef
);
4505 CFRelease(iosCertRef
);
4509 CFDataRef
SecCertificateGetAuthorityKeyIDP(SecCertificateRefP certificate
) {
4510 if (!certificate
->_authorityKeyID
&&
4511 certificate
->_authorityKeyIdentifier
.length
) {
4512 certificate
->_authorityKeyID
= CFDataCreate(kCFAllocatorDefault
,
4513 certificate
->_authorityKeyIdentifier
.data
,
4514 certificate
->_authorityKeyIdentifier
.length
);
4517 return certificate
->_authorityKeyID
;
4520 CFDataRef
SecCertificateGetSubjectKeyIDP(SecCertificateRefP certificate
) {
4521 if (!certificate
->_subjectKeyID
&&
4522 certificate
->_subjectKeyIdentifier
.length
) {
4523 certificate
->_subjectKeyID
= CFDataCreate(kCFAllocatorDefault
,
4524 certificate
->_subjectKeyIdentifier
.data
,
4525 certificate
->_subjectKeyIdentifier
.length
);
4528 return certificate
->_subjectKeyID
;
4531 CFArrayRef
SecCertificateGetCRLDistributionPointsP(SecCertificateRefP certificate
) {
4532 return certificate
->_crlDistributionPoints
;
4535 CFArrayRef
SecCertificateGetOCSPRespondersP(SecCertificateRefP certificate
) {
4536 return certificate
->_ocspResponders
;
4539 CFArrayRef
SecCertificateGetCAIssuersP(SecCertificateRefP certificate
) {
4540 return certificate
->_caIssuers
;
4543 bool SecCertificateHasCriticalSubjectAltNameP(SecCertificateRefP certificate
) {
4544 return certificate
->_subjectAltName
&&
4545 certificate
->_subjectAltName
->critical
;
4548 bool SecCertificateHasSubjectP(SecCertificateRefP certificate
) {
4549 /* Since the _subject field is the content of the subject and not the
4550 whole thing, we can simply check for a 0 length subject here. */
4551 return certificate
->_subject
.length
!= 0;
4554 bool SecCertificateHasUnknownCriticalExtensionP(SecCertificateRefP certificate
) {
4555 return certificate
->_foundUnknownCriticalExtension
;
4558 /* Private API functions. */
4559 void SecCertificateShowP(SecCertificateRefP certificate
) {
4561 fprintf(stderr
, "SecCertificate instance %p:\n", certificate
);
4562 fprintf(stderr
, "\n");
4565 CFDictionaryRef
SecCertificateCopyAttributeDictionaryP(
4566 SecCertificateRefP certificate
) {
4567 CFAllocatorRef allocator
= CFGetAllocator(certificate
);
4568 CFNumberRef certificateType
, certificateEncoding
;
4569 CFStringRef label
, alias
;
4570 CFDataRef skid
, pubKeyDigest
, certData
;
4571 CFDictionaryRef dict
= NULL
;
4575 /* CSSM_CERT_X_509v1, CSSM_CERT_X_509v2 or CSSM_CERT_X_509v3 */
4576 SInt32 ctv
= certificate
->_version
+ 1;
4577 SInt32 cev
= 3; /* CSSM_CERT_ENCODING_DER */
4578 certificateType
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &ctv
);
4579 certificateEncoding
= CFNumberCreate(allocator
, kCFNumberSInt32Type
, &cev
);
4580 certData
= SecCertificateCopyDataP(certificate
);
4581 skid
= SecCertificateGetSubjectKeyIDP(certificate
);
4582 pubKeyDigest
= SecSHA1DigestCreate(allocator
, certificate
->_pubKeyDER
.data
,
4583 certificate
->_pubKeyDER
.length
);
4585 /* We still need to figure out how to deal with multi valued attributes. */
4586 alias
= SecCertificateCopyRFC822NamesP(certificate
);
4587 label
= SecCertificateCopySubjectSummary(certificate
);
4593 DICT_ADDPAIR(kSecClass
, kSecClassCertificate
);
4594 DICT_ADDPAIR(kSecAttrCertificateType
, certificateType
);
4595 DICT_ADDPAIR(kSecAttrCertificateEncoding
, certificateEncoding
);
4597 DICT_ADDPAIR(kSecAttrLabel
, label
);
4599 DICT_ADDPAIR(kSecAttrAlias
, alias
);
4600 DICT_ADDPAIR(kSecAttrSubject
, certificate
->_normalizedSubject
);
4601 DICT_ADDPAIR(kSecAttrIssuer
, certificate
->_normalizedIssuer
);
4602 DICT_ADDPAIR(kSecAttrSerialNumber
, certificate
->_serialNumber
);
4604 DICT_ADDPAIR(kSecAttrSubjectKeyID
, skid
);
4605 DICT_ADDPAIR(kSecAttrPublicKeyHash
, pubKeyDigest
);
4606 DICT_ADDPAIR(kSecValueData
, certData
);
4607 dict
= DICT_CREATE(allocator
);
4609 CFReleaseSafe(label
);
4610 CFReleaseSafe(pubKeyDigest
);
4611 CFReleaseSafe(certData
);
4612 CFReleaseSafe(certificateEncoding
);
4613 CFReleaseSafe(certificateType
);
4618 SecCertificateRefP
SecCertificateCreateFromAttributeDictionaryP(
4619 CFDictionaryRef refAttributes
) {
4620 /* @@@ Support having an allocator in refAttributes. */
4621 CFAllocatorRef allocator
= NULL
;
4622 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
4623 return SecCertificateCreateWithDataP(allocator
, data
);
4626 bool SecCertificateIsSelfSignedCAP(SecCertificateRefP certificate
) {
4627 bool result
= false;
4628 SecKeyRefP publicKey
;
4629 require(publicKey
= SecCertificateCopyPublicKeyP(certificate
), out
);
4630 CFDataRef normalizedIssuer
=
4631 SecCertificateGetNormalizedIssuerContentP(certificate
);
4632 CFDataRef normalizedSubject
=
4633 SecCertificateGetNormalizedSubjectContentP(certificate
);
4634 require_quiet(normalizedIssuer
&& normalizedSubject
&&
4635 CFEqual(normalizedIssuer
, normalizedSubject
), out
);
4637 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyIDP(certificate
);
4638 CFDataRef subjectKeyID
= SecCertificateGetSubjectKeyIDP(certificate
);
4639 if (authorityKeyID
) {
4640 require_quiet(subjectKeyID
&& CFEqual(subjectKeyID
, authorityKeyID
), out
);
4643 if (SecCertificateVersionP(certificate
) >= 3) {
4644 const SecCEBasicConstraints
*basicConstraints
= SecCertificateGetBasicConstraintsP(certificate
);
4645 require_quiet(basicConstraints
&& basicConstraints
->isCA
, out
);
4646 require_noerr_quiet(SecCertificateIsSignedByP(certificate
, publicKey
), out
);
4651 CFReleaseSafe(publicKey
);
4655 SecKeyUsage
SecCertificateGetKeyUsageP(SecCertificateRefP certificate
) {
4656 return certificate
->_keyUsage
;
4659 CFArrayRef
SecCertificateCopyExtendedKeyUsageP(SecCertificateRefP certificate
)
4661 CFMutableArrayRef extended_key_usage_oids
=
4662 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
4663 require_quiet(extended_key_usage_oids
, out
);
4665 for (ix
= 0; ix
< certificate
->_extensionCount
; ++ix
) {
4666 const SecCertificateExtension
*extn
= &certificate
->_extensions
[ix
];
4667 if (extn
->extnID
.length
== oidExtendedKeyUsage
.length
&&
4668 !memcmp(extn
->extnID
.data
, oidExtendedKeyUsage
.data
, extn
->extnID
.length
)) {
4671 DERReturn drtn
= DERDecodeSeqInit(&extn
->extnValue
, &tag
, &derSeq
);
4672 require_noerr_quiet(drtn
, out
);
4673 require_quiet(tag
== ASN1_CONSTR_SEQUENCE
, out
);
4674 DERDecodedInfo currDecoded
;
4676 while ((drtn
= DERDecodeSeqNext(&derSeq
, &currDecoded
)) == DR_Success
) {
4677 require_quiet(currDecoded
.tag
== ASN1_OBJECT_ID
, out
);
4678 CFDataRef oid
= CFDataCreate(kCFAllocatorDefault
,
4679 currDecoded
.content
.data
, currDecoded
.content
.length
);
4681 CFArrayAppendValue(extended_key_usage_oids
, oid
);
4685 require_quiet(drtn
== DR_EndOfSequence
, out
);
4686 return extended_key_usage_oids
;
4690 CFReleaseSafe(extended_key_usage_oids
);
4694 SecCertificateRefP
SecCertificateCreateWithPEMP(CFAllocatorRef allocator
,
4695 CFDataRef pem_certificate
)
4697 static const char begin_cert
[] = "-----BEGIN CERTIFICATE-----\n";
4698 static const char end_cert
[] = "-----END CERTIFICATE-----\n";
4699 uint8_t *base64_data
= NULL
;
4700 SecCertificateRefP cert
= NULL
;
4701 const unsigned char *data
= CFDataGetBytePtr(pem_certificate
);
4702 //const size_t length = CFDataGetLength(pem_certificate);
4703 char *begin
= strstr((const char *)data
, begin_cert
);
4704 char *end
= strstr((const char *)data
, end_cert
);
4707 begin
+= sizeof(begin_cert
) - 1;
4708 size_t base64_length
= SecBase64Decode(begin
, end
- begin
, NULL
, 0);
4709 if (base64_length
) {
4710 require_quiet(base64_data
= calloc(1, base64_length
), out
);
4711 require_quiet(base64_length
= SecBase64Decode(begin
, end
- begin
, base64_data
, base64_length
), out
);
4712 cert
= SecCertificateCreateWithBytesP(kCFAllocatorDefault
, base64_data
, base64_length
);
4719 static void convertCertificateToCFData(const void *value
, void *context
) {
4720 CFMutableArrayRef result
= (CFMutableArrayRef
)context
;
4721 SecCertificateRefP certificate
= (SecCertificateRefP
)value
;
4722 CFDataRef data
= SecCertificateCopyDataP(certificate
);
4723 CFArrayAppendValue(result
, data
);
4727 /* Return an array of CFDataRefs from an array of SecCertificateRefPs. */
4728 CFArrayRef
SecCertificateArrayCopyDataArrayP(CFArrayRef certificates
) {
4729 CFIndex count
= CFArrayGetCount(certificates
);
4730 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
);
4731 CFRange all_certs
= { 0, count
};
4732 CFArrayApplyFunction(certificates
, all_certs
, convertCertificateToCFData
, result
);
4736 /* AUDIT[securityd](done):
4737 value (ok) is an element in a caller provided array.
4739 static void convertCFDataToCertificate(const void *value
, void *context
) {
4740 CFMutableArrayRef result
= (CFMutableArrayRef
)context
;
4741 CFDataRef data
= (CFDataRef
)value
;
4742 if (data
&& CFGetTypeID(data
) == CFDataGetTypeID()) {
4743 SecCertificateRefP certificate
= SecCertificateCreateWithDataP(kCFAllocatorDefault
, data
);
4745 CFArrayAppendValue(result
, certificate
);
4746 CFRelease(certificate
);
4751 /* AUDIT[securityd](done):
4752 certificates (ok) is a caller provided array, only its cf type has
4755 CFArrayRef
SecCertificateDataArrayCopyArrayP(CFArrayRef certificates
) {
4756 CFIndex count
= CFArrayGetCount(certificates
);
4757 CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, count
, &kCFTypeArrayCallBacks
);
4758 CFRange all_certs
= { 0, count
};
4759 CFArrayApplyFunction(certificates
, all_certs
, convertCFDataToCertificate
, result
);