2 * Copyright (c) 2007-2010 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 * SecCertificatePath.c - CoreFoundation based certificate path object
28 #include "SecCertificatePath.h"
30 #include <Security/SecTrust.h>
31 #include <Security/SecTrustStore.h>
32 #include <Security/SecItem.h>
33 #include <Security/SecCertificateInternal.h>
34 #include <Security/SecFramework.h>
35 #include <CoreFoundation/CFRuntime.h>
36 #include <CoreFoundation/CFSet.h>
37 #include <CoreFoundation/CFString.h>
38 #include <CoreFoundation/CFNumber.h>
39 #include <CoreFoundation/CFArray.h>
40 #include <CoreFoundation/CFPropertyList.h>
41 #include <AssertMacros.h>
46 #include <MacErrors.h>
47 #include "SecRSAKey.h"
48 #include <libDER/oids.h>
49 #include <security_utilities/debugging.h>
50 #include <Security/SecInternal.h>
53 #pragma mark SecCertificatePath
54 /********************************************************
55 ************* SecCertificatePath object ****************
56 ********************************************************/
57 struct SecCertificatePath
{
61 /* Index of next parent source to search for parents. */
62 CFIndex nextParentSource
;
64 /* Index of last certificate in chain who's signature has been verified.
65 0 means nothing has been checked. 1 means the leaf has been verified
66 against it's issuer, etc. */
67 CFIndex lastVerifiedSigner
;
69 /* Index of first self issued certificate in the chain. -1 mean there is
70 none. 0 means the leaf is self signed. */
73 /* True iff cert at index selfIssued does in fact self verify. */
76 /* True if the root of this path is a trusted anchor.
77 FIXME get rid of this since it's a property of the evaluation, not a
78 static feature of a certificate path? */
80 SecCertificateRef certificates
[];
83 /* CFRuntime regsitration data. */
84 static pthread_once_t kSecCertificatePathRegisterClass
= PTHREAD_ONCE_INIT
;
85 static CFTypeID kSecCertificatePathTypeID
= _kCFRuntimeNotATypeID
;
87 static void SecCertificatePathDestroy(CFTypeRef cf
) {
88 SecCertificatePathRef certificatePath
= (SecCertificatePathRef
) cf
;
90 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
91 CFRelease(certificatePath
->certificates
[ix
]);
95 static Boolean
SecCertificatePathEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
96 SecCertificatePathRef cp1
= (SecCertificatePathRef
) cf1
;
97 SecCertificatePathRef cp2
= (SecCertificatePathRef
) cf2
;
98 if (cp1
->count
!= cp2
->count
)
101 for (ix
= 0; ix
< cp1
->count
; ++ix
) {
102 if (!CFEqual(cp1
->certificates
[ix
], cp2
->certificates
[ix
]))
109 static CFHashCode
SecCertificatePathHash(CFTypeRef cf
) {
110 SecCertificatePathRef certificatePath
= (SecCertificatePathRef
) cf
;
111 CFHashCode hashCode
= 0;
112 // hashCode = 31 * SecCertificatePathGetTypeID();
114 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
115 hashCode
+= CFHash(certificatePath
->certificates
[ix
]);
120 static CFStringRef
SecCertificatePathDescribe(CFTypeRef cf
) {
121 SecCertificatePathRef certificatePath
= (SecCertificatePathRef
) cf
;
122 CFMutableStringRef desc
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
123 CFStringRef typeStr
= CFCopyTypeIDDescription(CFGetTypeID(cf
));
124 CFStringAppendFormat(desc
, NULL
,
125 CFSTR("<%@ lvs: %d certs: "), typeStr
,
126 certificatePath
->lastVerifiedSigner
);
129 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
131 CFStringAppend(desc
, CFSTR(", "));
133 CFStringRef str
= CFCopyDescription(certificatePath
->certificates
[ix
]);
134 CFStringAppend(desc
, str
);
137 CFStringAppend(desc
, CFSTR(" >"));
142 static void SecCertificatePathRegisterClass(void) {
143 static const CFRuntimeClass kSecCertificatePathClass
= {
145 "SecCertificatePath", /* class name */
148 SecCertificatePathDestroy
, /* dealloc */
149 SecCertificatePathEqual
, /* equal */
150 SecCertificatePathHash
, /* hash */
151 NULL
, /* copyFormattingDesc */
152 SecCertificatePathDescribe
/* copyDebugDesc */
155 kSecCertificatePathTypeID
=
156 _CFRuntimeRegisterClass(&kSecCertificatePathClass
);
159 /* SecCertificatePath API functions. */
160 CFTypeID
SecCertificatePathGetTypeID(void) {
161 pthread_once(&kSecCertificatePathRegisterClass
,
162 SecCertificatePathRegisterClass
);
163 return kSecCertificatePathTypeID
;
166 /* Create a new certificate path from an old one. */
167 SecCertificatePathRef
SecCertificatePathCreate(SecCertificatePathRef path
,
168 SecCertificateRef certificate
) {
169 CFAllocatorRef allocator
= kCFAllocatorDefault
;
172 CFIndex selfIssued
, lastVerifiedSigner
;
175 count
= path
->count
+ 1;
176 lastVerifiedSigner
= path
->lastVerifiedSigner
;
177 selfIssued
= path
->selfIssued
;
178 isSelfSigned
= path
->isSelfSigned
;
181 lastVerifiedSigner
= 0;
183 isSelfSigned
= false;
186 CFIndex size
= sizeof(struct SecCertificatePath
) +
187 count
* sizeof(SecCertificateRef
);
188 SecCertificatePathRef result
=
189 (SecCertificatePathRef
)_CFRuntimeCreateInstance(allocator
,
190 SecCertificatePathGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
194 result
->count
= count
;
195 result
->nextParentSource
= 0;
196 result
->lastVerifiedSigner
= lastVerifiedSigner
;
197 result
->selfIssued
= selfIssued
;
198 result
->isSelfSigned
= isSelfSigned
;
199 result
->isAnchored
= false;
201 for (ix
= 0; ix
< count
- 1; ++ix
) {
202 result
->certificates
[ix
] = path
->certificates
[ix
];
203 CFRetain(result
->certificates
[ix
]);
205 result
->certificates
[count
- 1] = certificate
;
206 CFRetain(certificate
);
211 /* Create a new certificate path from an array of CFDataRefs. */
212 SecCertificatePathRef
SecCertificatePathCreateWithArray(CFArrayRef certificates
) {
213 CFIndex count
= CFArrayGetCount(certificates
);
214 CFIndex size
= sizeof(struct SecCertificatePath
) +
215 count
* sizeof(SecCertificateRef
);
216 SecCertificatePathRef result
=
217 (SecCertificatePathRef
)_CFRuntimeCreateInstance(kCFAllocatorDefault
,
218 SecCertificatePathGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
222 result
->count
= count
;
223 result
->nextParentSource
= 0;
224 result
->lastVerifiedSigner
= count
;
225 result
->selfIssued
= -1;
226 result
->isSelfSigned
= false;
227 result
->isAnchored
= false;
229 for (ix
= 0; ix
< count
; ++ix
) {
230 CFDataRef data
= CFArrayGetValueAtIndex(certificates
, ix
);
231 SecCertificateRef certificate
= SecCertificateCreateWithData(kCFAllocatorDefault
, data
);
232 result
->certificates
[ix
] = certificate
;
238 SecCertificatePathRef
SecCertificatePathCopyFromParent(
239 SecCertificatePathRef path
, CFIndex skipCount
) {
240 CFAllocatorRef allocator
= kCFAllocatorDefault
;
242 CFIndex selfIssued
, lastVerifiedSigner
;
245 /* Ensure we are at least returning a path of length 1. */
246 if (skipCount
< 0 || path
->count
< 1 + skipCount
)
249 count
= path
->count
- skipCount
;
250 lastVerifiedSigner
= path
->lastVerifiedSigner
> skipCount
251 ? path
->lastVerifiedSigner
- skipCount
: 0;
252 selfIssued
= path
->selfIssued
>= skipCount
253 ? path
->selfIssued
- skipCount
: -1;
254 isSelfSigned
= path
->selfIssued
>= 0 ? path
->isSelfSigned
: false;
256 CFIndex size
= sizeof(struct SecCertificatePath
) +
257 count
* sizeof(SecCertificateRef
);
258 SecCertificatePathRef result
=
259 (SecCertificatePathRef
)_CFRuntimeCreateInstance(allocator
,
260 SecCertificatePathGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
264 result
->count
= count
;
265 result
->nextParentSource
= 0;
266 result
->lastVerifiedSigner
= lastVerifiedSigner
;
267 result
->selfIssued
= selfIssued
;
268 result
->isSelfSigned
= isSelfSigned
;
269 result
->isAnchored
= path
->isAnchored
;
271 for (ix
= 0; ix
< count
; ++ix
) {
272 result
->certificates
[ix
] = path
->certificates
[ix
+ skipCount
];
273 CFRetain(result
->certificates
[ix
]);
279 SecCertificatePathRef
SecCertificatePathCopyAddingLeaf(SecCertificatePathRef path
,
280 SecCertificateRef leaf
) {
281 CFAllocatorRef allocator
= kCFAllocatorDefault
;
283 CFIndex selfIssued
, lastVerifiedSigner
;
286 /* First make sure the new leaf is signed by path's current leaf. */
287 SecKeyRef issuerKey
= SecCertificatePathCopyPublicKeyAtIndex(path
, 0);
290 OSStatus status
= SecCertificateIsSignedBy(leaf
, issuerKey
);
291 CFRelease(issuerKey
);
295 count
= path
->count
+ 1;
296 lastVerifiedSigner
= path
->lastVerifiedSigner
+ 1;
297 selfIssued
= path
->selfIssued
;
298 isSelfSigned
= path
->isSelfSigned
;
300 CFIndex size
= sizeof(struct SecCertificatePath
) +
301 count
* sizeof(SecCertificateRef
);
302 SecCertificatePathRef result
=
303 (SecCertificatePathRef
)_CFRuntimeCreateInstance(allocator
,
304 SecCertificatePathGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
308 result
->count
= count
;
309 result
->nextParentSource
= 0;
310 result
->lastVerifiedSigner
= lastVerifiedSigner
;
311 result
->selfIssued
= selfIssued
;
312 result
->isSelfSigned
= isSelfSigned
;
313 result
->isAnchored
= path
->isAnchored
;
315 for (ix
= 1; ix
< count
; ++ix
) {
316 result
->certificates
[ix
] = path
->certificates
[ix
- 1];
317 CFRetain(result
->certificates
[ix
]);
319 result
->certificates
[0] = leaf
;
325 /* Create an array of CFDataRefs from a certificate path. */
326 CFArrayRef
SecCertificatePathCopyArray(SecCertificatePathRef path
) {
327 CFIndex ix
, count
= path
->count
;
328 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, count
,
329 &kCFTypeArrayCallBacks
);
330 for (ix
= 0; ix
< count
; ++ix
) {
331 CFDataRef data
= SecCertificateCopyData(path
->certificates
[ix
]);
332 CFArrayAppendValue(result
, data
);
338 /* Record the fact that we found our own root cert as our parent
340 void SecCertificatePathSetSelfIssued(
341 SecCertificatePathRef certificatePath
) {
342 if (certificatePath
->selfIssued
>= 0) {
343 secdebug("trust", "%@ is already issued at %d", certificatePath
,
344 certificatePath
->selfIssued
);
347 secdebug("trust", "%@ is self issued", certificatePath
);
348 certificatePath
->selfIssued
= certificatePath
->count
- 1;
351 void SecCertificatePathSetIsAnchored(
352 SecCertificatePathRef certificatePath
) {
353 secdebug("trust", "%@ is anchored", certificatePath
);
354 certificatePath
->isAnchored
= true;
357 /* Return the index of the first non anchor certificate in the chain that is
358 self signed counting from the leaf up. Return -1 if there is none. */
359 CFIndex
SecCertificatePathSelfSignedIndex(
360 SecCertificatePathRef certificatePath
) {
361 if (certificatePath
->isSelfSigned
)
362 return certificatePath
->selfIssued
;
366 Boolean
SecCertificatePathIsAnchored(
367 SecCertificatePathRef certificatePath
) {
368 return certificatePath
->isAnchored
;
371 void SecCertificatePathSetNextSourceIndex(
372 SecCertificatePathRef certificatePath
, CFIndex sourceIndex
) {
373 certificatePath
->nextParentSource
= sourceIndex
;
376 CFIndex
SecCertificatePathGetNextSourceIndex(
377 SecCertificatePathRef certificatePath
) {
378 return certificatePath
->nextParentSource
;
381 CFIndex
SecCertificatePathGetCount(
382 SecCertificatePathRef certificatePath
) {
383 check(certificatePath
);
384 return certificatePath
->count
;
387 SecCertificateRef
SecCertificatePathGetCertificateAtIndex(
388 SecCertificatePathRef certificatePath
, CFIndex ix
) {
389 check(certificatePath
);
390 check(ix
>= 0 && ix
< certificatePath
->count
);
391 return certificatePath
->certificates
[ix
];
394 CFIndex
SecCertificatePathGetIndexOfCertificate(SecCertificatePathRef path
,
395 SecCertificateRef certificate
) {
396 CFIndex ix
, count
= path
->count
;
397 for (ix
= 0; ix
< count
; ++ix
) {
398 if (CFEqual(path
->certificates
[ix
], certificate
))
405 /* Return the leaf certificate for certificatePath. */
406 SecCertificateRef
SecCertificatePathGetLeaf(
407 SecCertificatePathRef certificatePath
) {
408 return SecCertificatePathGetCertificateAtIndex(certificatePath
, 0);
412 /* Return the root certificate for certificatePath. Note that root is just
413 the top of the path as far as it is constructed. It may or may not be
414 trusted or self signed. */
415 SecCertificateRef
SecCertificatePathGetRoot(
416 SecCertificatePathRef certificatePath
) {
417 return SecCertificatePathGetCertificateAtIndex(certificatePath
,
418 SecCertificatePathGetCount(certificatePath
) - 1);
421 SecKeyRef
SecCertificatePathCopyPublicKeyAtIndex(
422 SecCertificatePathRef certificatePath
, CFIndex ix
) {
423 SecCertificateRef certificate
=
424 SecCertificatePathGetCertificateAtIndex(certificatePath
, ix
);
425 const DERAlgorithmId
*algId
=
426 SecCertificateGetPublicKeyAlgorithm(certificate
);
427 const DERItem
*params
= NULL
;
428 if (algId
->params
.length
!= 0) {
429 params
= &algId
->params
;
431 CFIndex count
= certificatePath
->count
;
432 for (++ix
; ix
< count
; ++ix
) {
433 certificate
= certificatePath
->certificates
[ix
];
434 const DERAlgorithmId
*chain_algId
=
435 SecCertificateGetPublicKeyAlgorithm(certificate
);
436 if (!DEROidCompare(&algId
->oid
, &chain_algId
->oid
)) {
437 /* Algorithm oids differ, params stay NULL. */
440 if (chain_algId
->params
.length
!= 0) {
441 params
= &chain_algId
->params
;
446 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
447 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
448 SecAsn1Item params1
= {
449 .Data
= params
? params
->data
: NULL
,
450 .Length
= params
? params
->length
: 0
452 SecAsn1Item keyData1
= {
453 .Data
= keyData
? keyData
->data
: NULL
,
454 .Length
= keyData
? keyData
->length
: 0
456 return SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
460 SecPathVerifyStatus
SecCertificatePathVerify(
461 SecCertificatePathRef certificatePath
) {
462 check(certificatePath
);
464 certificatePath
->lastVerifiedSigner
< certificatePath
->count
- 1;
465 ++certificatePath
->lastVerifiedSigner
) {
466 SecKeyRef issuerKey
=
467 SecCertificatePathCopyPublicKeyAtIndex(certificatePath
,
468 certificatePath
->lastVerifiedSigner
+ 1);
470 return kSecPathVerifiesUnknown
;
471 OSStatus status
= SecCertificateIsSignedBy(
472 certificatePath
->certificates
[certificatePath
->lastVerifiedSigner
],
474 CFRelease(issuerKey
);
476 return kSecPathVerifyFailed
;
480 if (certificatePath
->selfIssued
>= 0 && !certificatePath
->isSelfSigned
) {
481 SecKeyRef issuerKey
=
482 SecCertificatePathCopyPublicKeyAtIndex(certificatePath
,
483 certificatePath
->selfIssued
);
485 certificatePath
->selfIssued
= -1;
487 OSStatus status
= SecCertificateIsSignedBy(
488 certificatePath
->certificates
[certificatePath
->selfIssued
],
490 CFRelease(issuerKey
);
492 certificatePath
->isSelfSigned
= true;
494 certificatePath
->selfIssued
= -1;
499 return kSecPathVerifySuccess
;
502 /* Return a score for this certificate chain. */
503 CFIndex
SecCertificatePathScore(
504 SecCertificatePathRef certificatePath
, CFAbsoluteTime verifyTime
) {
506 if (certificatePath
->isAnchored
) {
507 /* Anchored paths for the win! */
511 /* Score points for each certificate in the chain. */
512 score
+= 10 * certificatePath
->count
;
514 if (certificatePath
->isSelfSigned
) {
515 /* If there is a self signed certificate at the end ofthe chain we
516 count it as an extra certificate. If there is one in the middle
517 of the chain we count it for half. */
518 if (certificatePath
->selfIssued
== certificatePath
->count
- 1)
524 /* Paths that don't verify score terribly. */
525 if (certificatePath
->lastVerifiedSigner
!= certificatePath
->count
- 1) {
526 secdebug("trust", "lvs: %d count: %d",
527 certificatePath
->lastVerifiedSigner
, certificatePath
->count
);
531 /* Subtract 1 point for each not valid certificate, make sure we
532 subtract less than the amount we add per certificate, since
533 regardless of temporal validity we still prefer longer chains
534 to shorter ones. This distinction is just to ensure that when
535 everything else is equal we prefer the chain with the most
536 certificates that are valid at the given verifyTime. */
538 for (ix
= 0; ix
< certificatePath
->count
- 1; ++ix
) {
539 if (!SecCertificateIsValid(certificatePath
->certificates
[ix
],