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 <utilities/SecIOFormat.h>
36 #include <CoreFoundation/CFRuntime.h>
37 #include <CoreFoundation/CFSet.h>
38 #include <CoreFoundation/CFString.h>
39 #include <CoreFoundation/CFNumber.h>
40 #include <CoreFoundation/CFArray.h>
41 #include <CoreFoundation/CFPropertyList.h>
42 #include <AssertMacros.h>
47 #include <Security/SecBase.h>
48 #include "SecRSAKey.h"
49 #include <libDER/oids.h>
50 #include <utilities/debugging.h>
51 #include <Security/SecInternal.h>
52 #include <AssertMacros.h>
53 #include <utilities/SecCFError.h>
56 // MARK: SecCertificatePath
57 /********************************************************
58 ************* SecCertificatePath object ****************
59 ********************************************************/
60 struct SecCertificatePath
{
64 /* Index of next parent source to search for parents. */
65 CFIndex nextParentSource
;
67 /* Index of last certificate in chain who's signature has been verified.
68 0 means nothing has been checked. 1 means the leaf has been verified
69 against it's issuer, etc. */
70 CFIndex lastVerifiedSigner
;
72 /* Index of first self issued certificate in the chain. -1 mean there is
73 none. 0 means the leaf is self signed. */
76 /* True iff cert at index selfIssued does in fact self verify. */
79 /* True if the root of this path is a trusted anchor.
80 FIXME get rid of this since it's a property of the evaluation, not a
81 static feature of a certificate path? */
83 SecCertificateRef certificates
[];
86 /* CFRuntime regsitration data. */
87 static pthread_once_t kSecCertificatePathRegisterClass
= PTHREAD_ONCE_INIT
;
88 static CFTypeID kSecCertificatePathTypeID
= _kCFRuntimeNotATypeID
;
90 static void SecCertificatePathDestroy(CFTypeRef cf
) {
91 SecCertificatePathRef certificatePath
= (SecCertificatePathRef
) cf
;
93 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
94 CFRelease(certificatePath
->certificates
[ix
]);
98 static Boolean
SecCertificatePathEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
99 SecCertificatePathRef cp1
= (SecCertificatePathRef
) cf1
;
100 SecCertificatePathRef cp2
= (SecCertificatePathRef
) cf2
;
101 if (cp1
->count
!= cp2
->count
)
104 for (ix
= 0; ix
< cp1
->count
; ++ix
) {
105 if (!CFEqual(cp1
->certificates
[ix
], cp2
->certificates
[ix
]))
112 static CFHashCode
SecCertificatePathHash(CFTypeRef cf
) {
113 SecCertificatePathRef certificatePath
= (SecCertificatePathRef
) cf
;
114 CFHashCode hashCode
= 0;
115 // hashCode = 31 * SecCertificatePathGetTypeID();
117 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
118 hashCode
+= CFHash(certificatePath
->certificates
[ix
]);
123 static CFStringRef
SecCertificateCopyPathDescription(CFTypeRef cf
) {
124 SecCertificatePathRef certificatePath
= (SecCertificatePathRef
) cf
;
125 CFMutableStringRef desc
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
126 CFStringRef typeStr
= CFCopyTypeIDDescription(CFGetTypeID(cf
));
127 CFStringAppendFormat(desc
, NULL
,
128 CFSTR("<%@ lvs: %" PRIdCFIndex
" certs: "), typeStr
,
129 certificatePath
->lastVerifiedSigner
);
132 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
134 CFStringAppend(desc
, CFSTR(", "));
136 CFStringRef str
= CFCopyDescription(certificatePath
->certificates
[ix
]);
137 CFStringAppend(desc
, str
);
140 CFStringAppend(desc
, CFSTR(" >"));
145 static void SecCertificatePathRegisterClass(void) {
146 static const CFRuntimeClass kSecCertificatePathClass
= {
148 "SecCertificatePath", /* class name */
151 SecCertificatePathDestroy
, /* dealloc */
152 SecCertificatePathEqual
, /* equal */
153 SecCertificatePathHash
, /* hash */
154 NULL
, /* copyFormattingDesc */
155 SecCertificateCopyPathDescription
/* copyDebugDesc */
158 kSecCertificatePathTypeID
=
159 _CFRuntimeRegisterClass(&kSecCertificatePathClass
);
162 /* SecCertificatePath API functions. */
163 CFTypeID
SecCertificatePathGetTypeID(void) {
164 pthread_once(&kSecCertificatePathRegisterClass
,
165 SecCertificatePathRegisterClass
);
166 return kSecCertificatePathTypeID
;
169 /* Create a new certificate path from an old one. */
170 SecCertificatePathRef
SecCertificatePathCreate(SecCertificatePathRef path
,
171 SecCertificateRef certificate
) {
172 CFAllocatorRef allocator
= kCFAllocatorDefault
;
175 CFIndex selfIssued
, lastVerifiedSigner
;
178 count
= path
->count
+ 1;
179 lastVerifiedSigner
= path
->lastVerifiedSigner
;
180 selfIssued
= path
->selfIssued
;
181 isSelfSigned
= path
->isSelfSigned
;
184 lastVerifiedSigner
= 0;
186 isSelfSigned
= false;
189 CFIndex size
= sizeof(struct SecCertificatePath
) +
190 count
* sizeof(SecCertificateRef
);
191 SecCertificatePathRef result
=
192 (SecCertificatePathRef
)_CFRuntimeCreateInstance(allocator
,
193 SecCertificatePathGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
197 result
->count
= count
;
198 result
->nextParentSource
= 0;
199 result
->lastVerifiedSigner
= lastVerifiedSigner
;
200 result
->selfIssued
= selfIssued
;
201 result
->isSelfSigned
= isSelfSigned
;
202 result
->isAnchored
= false;
204 for (ix
= 0; ix
< count
- 1; ++ix
) {
205 result
->certificates
[ix
] = path
->certificates
[ix
];
206 CFRetain(result
->certificates
[ix
]);
208 result
->certificates
[count
- 1] = certificate
;
209 CFRetainSafe(certificate
);
214 /* Create a new certificate path from an xpc_array of data. */
215 SecCertificatePathRef
SecCertificatePathCreateWithXPCArray(xpc_object_t xpc_path
, CFErrorRef
*error
) {
216 SecCertificatePathRef result
= NULL
;
217 require_action_quiet(xpc_path
, exit
, SecError(errSecParam
, error
, CFSTR("xpc_path is NULL")));
218 require_action_quiet(xpc_get_type(xpc_path
) == XPC_TYPE_ARRAY
, exit
, SecError(errSecDecode
, error
, CFSTR("xpc_path value is not an array")));
220 require_action_quiet(count
= xpc_array_get_count(xpc_path
), exit
, SecError(errSecDecode
, error
, CFSTR("xpc_path array count == 0")));
221 size_t size
= sizeof(struct SecCertificatePath
) + count
* sizeof(SecCertificateRef
);
222 require_action_quiet(result
= (SecCertificatePathRef
)_CFRuntimeCreateInstance(kCFAllocatorDefault
, SecCertificatePathGetTypeID(), size
- sizeof(CFRuntimeBase
), 0), exit
, SecError(errSecDecode
, error
, CFSTR("_CFRuntimeCreateInstance returned NULL")));
224 result
->count
= count
;
225 result
->nextParentSource
= 0;
226 result
->lastVerifiedSigner
= count
;
227 result
->selfIssued
= -1;
228 result
->isSelfSigned
= false;
229 result
->isAnchored
= false;
231 for (ix
= 0; ix
< count
; ++ix
) {
232 SecCertificateRef certificate
= SecCertificateCreateWithXPCArrayAtIndex(xpc_path
, ix
, error
);
234 result
->certificates
[ix
] = certificate
;
236 result
->count
= ix
; // total allocated
237 CFReleaseNull(result
);
246 SecCertificatePathRef
SecCertificatePathCopyFromParent(
247 SecCertificatePathRef path
, CFIndex skipCount
) {
248 CFAllocatorRef allocator
= kCFAllocatorDefault
;
250 CFIndex selfIssued
, lastVerifiedSigner
;
253 /* Ensure we are at least returning a path of length 1. */
254 if (skipCount
< 0 || path
->count
< 1 + skipCount
)
257 count
= path
->count
- skipCount
;
258 lastVerifiedSigner
= path
->lastVerifiedSigner
> skipCount
259 ? path
->lastVerifiedSigner
- skipCount
: 0;
260 selfIssued
= path
->selfIssued
>= skipCount
261 ? path
->selfIssued
- skipCount
: -1;
262 isSelfSigned
= path
->selfIssued
>= 0 ? path
->isSelfSigned
: false;
264 CFIndex size
= sizeof(struct SecCertificatePath
) +
265 count
* sizeof(SecCertificateRef
);
266 SecCertificatePathRef result
=
267 (SecCertificatePathRef
)_CFRuntimeCreateInstance(allocator
,
268 SecCertificatePathGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
272 result
->count
= count
;
273 result
->nextParentSource
= 0;
274 result
->lastVerifiedSigner
= lastVerifiedSigner
;
275 result
->selfIssued
= selfIssued
;
276 result
->isSelfSigned
= isSelfSigned
;
277 result
->isAnchored
= path
->isAnchored
;
279 for (ix
= 0; ix
< count
; ++ix
) {
280 result
->certificates
[ix
] = path
->certificates
[ix
+ skipCount
];
281 CFRetain(result
->certificates
[ix
]);
287 SecCertificatePathRef
SecCertificatePathCopyAddingLeaf(SecCertificatePathRef path
,
288 SecCertificateRef leaf
) {
289 CFAllocatorRef allocator
= kCFAllocatorDefault
;
291 CFIndex selfIssued
, lastVerifiedSigner
;
294 /* First make sure the new leaf is signed by path's current leaf. */
295 SecKeyRef issuerKey
= SecCertificatePathCopyPublicKeyAtIndex(path
, 0);
298 OSStatus status
= SecCertificateIsSignedBy(leaf
, issuerKey
);
299 CFRelease(issuerKey
);
303 count
= path
->count
+ 1;
304 lastVerifiedSigner
= path
->lastVerifiedSigner
+ 1;
305 selfIssued
= path
->selfIssued
;
306 isSelfSigned
= path
->isSelfSigned
;
308 CFIndex size
= sizeof(struct SecCertificatePath
) +
309 count
* sizeof(SecCertificateRef
);
310 SecCertificatePathRef result
=
311 (SecCertificatePathRef
)_CFRuntimeCreateInstance(allocator
,
312 SecCertificatePathGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
316 result
->count
= count
;
317 result
->nextParentSource
= 0;
318 result
->lastVerifiedSigner
= lastVerifiedSigner
;
319 result
->selfIssued
= selfIssued
;
320 result
->isSelfSigned
= isSelfSigned
;
321 result
->isAnchored
= path
->isAnchored
;
323 for (ix
= 1; ix
< count
; ++ix
) {
324 result
->certificates
[ix
] = path
->certificates
[ix
- 1];
325 CFRetain(result
->certificates
[ix
]);
327 result
->certificates
[0] = leaf
;
333 /* Create an array of CFDataRefs from a certificate path. */
334 xpc_object_t
SecCertificatePathCopyXPCArray(SecCertificatePathRef path
, CFErrorRef
*error
) {
335 xpc_object_t xpc_chain
= NULL
;
336 size_t ix
, count
= path
->count
;
337 require_action_quiet(xpc_chain
= xpc_array_create(NULL
, 0), exit
, SecError(errSecParam
, error
, CFSTR("xpc_array_create failed")));
338 for (ix
= 0; ix
< count
; ++ix
) {
339 SecCertificateRef cert
= SecCertificatePathGetCertificateAtIndex(path
, ix
);
340 if (!SecCertificateAppendToXPCArray(cert
, xpc_chain
, error
)) {
341 xpc_release(xpc_chain
);
350 /* Record the fact that we found our own root cert as our parent
352 void SecCertificatePathSetSelfIssued(
353 SecCertificatePathRef certificatePath
) {
354 if (certificatePath
->selfIssued
>= 0) {
355 secdebug("trust", "%@ is already issued at %" PRIdCFIndex
, certificatePath
,
356 certificatePath
->selfIssued
);
359 secdebug("trust", "%@ is self issued", certificatePath
);
360 certificatePath
->selfIssued
= certificatePath
->count
- 1;
363 void SecCertificatePathSetIsAnchored(
364 SecCertificatePathRef certificatePath
) {
365 secdebug("trust", "%@ is anchored", certificatePath
);
366 certificatePath
->isAnchored
= true;
369 /* Return the index of the first non anchor certificate in the chain that is
370 self signed counting from the leaf up. Return -1 if there is none. */
371 CFIndex
SecCertificatePathSelfSignedIndex(
372 SecCertificatePathRef certificatePath
) {
373 if (certificatePath
->isSelfSigned
)
374 return certificatePath
->selfIssued
;
378 Boolean
SecCertificatePathIsAnchored(
379 SecCertificatePathRef certificatePath
) {
380 return certificatePath
->isAnchored
;
383 void SecCertificatePathSetNextSourceIndex(
384 SecCertificatePathRef certificatePath
, CFIndex sourceIndex
) {
385 certificatePath
->nextParentSource
= sourceIndex
;
388 CFIndex
SecCertificatePathGetNextSourceIndex(
389 SecCertificatePathRef certificatePath
) {
390 return certificatePath
->nextParentSource
;
393 CFIndex
SecCertificatePathGetCount(
394 SecCertificatePathRef certificatePath
) {
395 check(certificatePath
);
396 return certificatePath
? certificatePath
->count
: 0;
399 SecCertificateRef
SecCertificatePathGetCertificateAtIndex(
400 SecCertificatePathRef certificatePath
, CFIndex ix
) {
401 check(certificatePath
);
402 check(ix
>= 0 && ix
< certificatePath
->count
);
403 return certificatePath
->certificates
[ix
];
406 CFIndex
SecCertificatePathGetIndexOfCertificate(SecCertificatePathRef path
,
407 SecCertificateRef certificate
) {
408 CFIndex ix
, count
= path
->count
;
409 for (ix
= 0; ix
< count
; ++ix
) {
410 if (CFEqual(path
->certificates
[ix
], certificate
))
417 /* Return the leaf certificate for certificatePath. */
418 SecCertificateRef
SecCertificatePathGetLeaf(
419 SecCertificatePathRef certificatePath
) {
420 return SecCertificatePathGetCertificateAtIndex(certificatePath
, 0);
424 /* Return the root certificate for certificatePath. Note that root is just
425 the top of the path as far as it is constructed. It may or may not be
426 trusted or self signed. */
427 SecCertificateRef
SecCertificatePathGetRoot(
428 SecCertificatePathRef certificatePath
) {
429 return SecCertificatePathGetCertificateAtIndex(certificatePath
,
430 SecCertificatePathGetCount(certificatePath
) - 1);
433 SecKeyRef
SecCertificatePathCopyPublicKeyAtIndex(
434 SecCertificatePathRef certificatePath
, CFIndex ix
) {
435 SecCertificateRef certificate
=
436 SecCertificatePathGetCertificateAtIndex(certificatePath
, ix
);
437 const DERAlgorithmId
*algId
=
438 SecCertificateGetPublicKeyAlgorithm(certificate
);
439 const DERItem
*params
= NULL
;
440 if (algId
->params
.length
!= 0) {
441 params
= &algId
->params
;
443 CFIndex count
= certificatePath
->count
;
444 for (++ix
; ix
< count
; ++ix
) {
445 certificate
= certificatePath
->certificates
[ix
];
446 const DERAlgorithmId
*chain_algId
=
447 SecCertificateGetPublicKeyAlgorithm(certificate
);
448 if (!DEROidCompare(&algId
->oid
, &chain_algId
->oid
)) {
449 /* Algorithm oids differ, params stay NULL. */
452 if (chain_algId
->params
.length
!= 0) {
453 params
= &chain_algId
->params
;
458 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
459 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
460 SecAsn1Item params1
= {
461 .Data
= params
? params
->data
: NULL
,
462 .Length
= params
? params
->length
: 0
464 SecAsn1Item keyData1
= {
465 .Data
= keyData
? keyData
->data
: NULL
,
466 .Length
= keyData
? keyData
->length
: 0
468 return SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
472 SecPathVerifyStatus
SecCertificatePathVerify(
473 SecCertificatePathRef certificatePath
) {
474 check(certificatePath
);
475 if (!certificatePath
)
476 return kSecPathVerifyFailed
;
478 certificatePath
->lastVerifiedSigner
< certificatePath
->count
- 1;
479 ++certificatePath
->lastVerifiedSigner
) {
480 SecKeyRef issuerKey
=
481 SecCertificatePathCopyPublicKeyAtIndex(certificatePath
,
482 certificatePath
->lastVerifiedSigner
+ 1);
484 return kSecPathVerifiesUnknown
;
485 OSStatus status
= SecCertificateIsSignedBy(
486 certificatePath
->certificates
[certificatePath
->lastVerifiedSigner
],
488 CFRelease(issuerKey
);
490 return kSecPathVerifyFailed
;
494 if (certificatePath
->selfIssued
>= 0 && !certificatePath
->isSelfSigned
) {
495 SecKeyRef issuerKey
=
496 SecCertificatePathCopyPublicKeyAtIndex(certificatePath
,
497 certificatePath
->selfIssued
);
499 certificatePath
->selfIssued
= -1;
501 OSStatus status
= SecCertificateIsSignedBy(
502 certificatePath
->certificates
[certificatePath
->selfIssued
],
504 CFRelease(issuerKey
);
506 certificatePath
->isSelfSigned
= true;
508 certificatePath
->selfIssued
= -1;
513 return kSecPathVerifySuccess
;
516 /* Return a score for this certificate chain. */
517 CFIndex
SecCertificatePathScore(
518 SecCertificatePathRef certificatePath
, CFAbsoluteTime verifyTime
) {
520 if (certificatePath
->isAnchored
) {
521 /* Anchored paths for the win! */
525 /* Score points for each certificate in the chain. */
526 score
+= 10 * certificatePath
->count
;
528 if (certificatePath
->isSelfSigned
) {
529 /* If there is a self signed certificate at the end ofthe chain we
530 count it as an extra certificate. If there is one in the middle
531 of the chain we count it for half. */
532 if (certificatePath
->selfIssued
== certificatePath
->count
- 1)
538 /* Paths that don't verify score terribly. */
539 if (certificatePath
->lastVerifiedSigner
!= certificatePath
->count
- 1) {
540 secdebug("trust", "lvs: %" PRIdCFIndex
" count: %" PRIdCFIndex
,
541 certificatePath
->lastVerifiedSigner
, certificatePath
->count
);
545 /* Subtract 1 point for each not valid certificate, make sure we
546 subtract less than the amount we add per certificate, since
547 regardless of temporal validity we still prefer longer chains
548 to shorter ones. This distinction is just to ensure that when
549 everything else is equal we prefer the chain with the most
550 certificates that are valid at the given verifyTime. */
552 for (ix
= 0; ix
< certificatePath
->count
- 1; ++ix
) {
553 if (!SecCertificateIsValid(certificatePath
->certificates
[ix
],