2 * Copyright (c) 2007-2010,2012-2014 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/oidsPriv.h>
50 #include <utilities/debugging.h>
51 #include <Security/SecInternal.h>
52 #include <AssertMacros.h>
53 #include <utilities/SecCFError.h>
54 #include <utilities/SecCFWrappers.h>
57 // MARK: SecCertificatePath
58 /********************************************************
59 ************* SecCertificatePath object ****************
60 ********************************************************/
61 struct SecCertificatePath
{
65 /* Index of next parent source to search for parents. */
66 CFIndex nextParentSource
;
68 /* Index of last certificate in chain who's signature has been verified.
69 0 means nothing has been checked. 1 means the leaf has been verified
70 against it's issuer, etc. */
71 CFIndex lastVerifiedSigner
;
73 /* Index of first self issued certificate in the chain. -1 mean there is
74 none. 0 means the leaf is self signed. */
77 /* True iff cert at index selfIssued does in fact self verify. */
80 /* True if the root of this path is a trusted anchor.
81 FIXME get rid of this since it's a property of the evaluation, not a
82 static feature of a certificate path? */
84 SecCertificateRef certificates
[];
87 CFGiblisWithHashFor(SecCertificatePath
)
89 static void SecCertificatePathDestroy(CFTypeRef cf
) {
90 SecCertificatePathRef certificatePath
= (SecCertificatePathRef
) cf
;
92 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
93 CFRelease(certificatePath
->certificates
[ix
]);
97 static Boolean
SecCertificatePathCompare(CFTypeRef cf1
, CFTypeRef cf2
) {
98 SecCertificatePathRef cp1
= (SecCertificatePathRef
) cf1
;
99 SecCertificatePathRef cp2
= (SecCertificatePathRef
) cf2
;
100 if (cp1
->count
!= cp2
->count
)
103 for (ix
= 0; ix
< cp1
->count
; ++ix
) {
104 if (!CFEqual(cp1
->certificates
[ix
], cp2
->certificates
[ix
]))
111 static CFHashCode
SecCertificatePathHash(CFTypeRef cf
) {
112 SecCertificatePathRef certificatePath
= (SecCertificatePathRef
) cf
;
113 CFHashCode hashCode
= 0;
114 // hashCode = 31 * SecCertificatePathGetTypeID();
116 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
117 hashCode
+= CFHash(certificatePath
->certificates
[ix
]);
122 static CFStringRef
SecCertificatePathCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
123 SecCertificatePathRef certificatePath
= (SecCertificatePathRef
) cf
;
124 CFMutableStringRef desc
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
125 CFStringRef typeStr
= CFCopyTypeIDDescription(CFGetTypeID(cf
));
126 CFStringAppendFormat(desc
, NULL
,
127 CFSTR("<%@ lvs: %" PRIdCFIndex
" certs: "), typeStr
,
128 certificatePath
->lastVerifiedSigner
);
131 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
133 CFStringAppend(desc
, CFSTR(", "));
135 CFStringRef str
= CFCopyDescription(certificatePath
->certificates
[ix
]);
136 CFStringAppend(desc
, str
);
139 CFStringAppend(desc
, CFSTR(" >"));
144 /* Create a new certificate path from an old one. */
145 SecCertificatePathRef
SecCertificatePathCreate(SecCertificatePathRef path
,
146 SecCertificateRef certificate
) {
147 CFAllocatorRef allocator
= kCFAllocatorDefault
;
150 CFIndex selfIssued
, lastVerifiedSigner
;
153 count
= path
->count
+ 1;
154 lastVerifiedSigner
= path
->lastVerifiedSigner
;
155 selfIssued
= path
->selfIssued
;
156 isSelfSigned
= path
->isSelfSigned
;
159 lastVerifiedSigner
= 0;
161 isSelfSigned
= false;
164 CFIndex size
= sizeof(struct SecCertificatePath
) +
165 count
* sizeof(SecCertificateRef
);
166 SecCertificatePathRef result
=
167 (SecCertificatePathRef
)_CFRuntimeCreateInstance(allocator
,
168 SecCertificatePathGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
172 result
->count
= count
;
173 result
->nextParentSource
= 0;
174 result
->lastVerifiedSigner
= lastVerifiedSigner
;
175 result
->selfIssued
= selfIssued
;
176 result
->isSelfSigned
= isSelfSigned
;
177 result
->isAnchored
= false;
179 for (ix
= 0; ix
< count
- 1; ++ix
) {
180 result
->certificates
[ix
] = path
->certificates
[ix
];
181 CFRetain(result
->certificates
[ix
]);
183 result
->certificates
[count
- 1] = certificate
;
184 CFRetainSafe(certificate
);
189 /* Create a new certificate path from an xpc_array of data. */
190 SecCertificatePathRef
SecCertificatePathCreateWithXPCArray(xpc_object_t xpc_path
, CFErrorRef
*error
) {
191 SecCertificatePathRef result
= NULL
;
192 require_action_quiet(xpc_path
, exit
, SecError(errSecParam
, error
, CFSTR("xpc_path is NULL")));
193 require_action_quiet(xpc_get_type(xpc_path
) == XPC_TYPE_ARRAY
, exit
, SecError(errSecDecode
, error
, CFSTR("xpc_path value is not an array")));
195 require_action_quiet(count
= xpc_array_get_count(xpc_path
), exit
, SecError(errSecDecode
, error
, CFSTR("xpc_path array count == 0")));
196 size_t size
= sizeof(struct SecCertificatePath
) + count
* sizeof(SecCertificateRef
);
197 require_action_quiet(result
= (SecCertificatePathRef
)_CFRuntimeCreateInstance(kCFAllocatorDefault
, SecCertificatePathGetTypeID(), size
- sizeof(CFRuntimeBase
), 0), exit
, SecError(errSecDecode
, error
, CFSTR("_CFRuntimeCreateInstance returned NULL")));
199 result
->count
= count
;
200 result
->nextParentSource
= 0;
201 result
->lastVerifiedSigner
= count
;
202 result
->selfIssued
= -1;
203 result
->isSelfSigned
= false;
204 result
->isAnchored
= false;
206 for (ix
= 0; ix
< count
; ++ix
) {
207 SecCertificateRef certificate
= SecCertificateCreateWithXPCArrayAtIndex(xpc_path
, ix
, error
);
209 result
->certificates
[ix
] = certificate
;
211 result
->count
= ix
; // total allocated
212 CFReleaseNull(result
);
221 SecCertificatePathRef
SecCertificatePathCopyFromParent(
222 SecCertificatePathRef path
, CFIndex skipCount
) {
223 CFAllocatorRef allocator
= kCFAllocatorDefault
;
225 CFIndex selfIssued
, lastVerifiedSigner
;
228 /* Ensure we are at least returning a path of length 1. */
229 if (skipCount
< 0 || path
->count
< 1 + skipCount
)
232 count
= path
->count
- skipCount
;
233 lastVerifiedSigner
= path
->lastVerifiedSigner
> skipCount
234 ? path
->lastVerifiedSigner
- skipCount
: 0;
235 selfIssued
= path
->selfIssued
>= skipCount
236 ? path
->selfIssued
- skipCount
: -1;
237 isSelfSigned
= path
->selfIssued
>= 0 ? path
->isSelfSigned
: false;
239 CFIndex size
= sizeof(struct SecCertificatePath
) +
240 count
* sizeof(SecCertificateRef
);
241 SecCertificatePathRef result
=
242 (SecCertificatePathRef
)_CFRuntimeCreateInstance(allocator
,
243 SecCertificatePathGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
247 result
->count
= count
;
248 result
->nextParentSource
= 0;
249 result
->lastVerifiedSigner
= lastVerifiedSigner
;
250 result
->selfIssued
= selfIssued
;
251 result
->isSelfSigned
= isSelfSigned
;
252 result
->isAnchored
= path
->isAnchored
;
254 for (ix
= 0; ix
< count
; ++ix
) {
255 result
->certificates
[ix
] = path
->certificates
[ix
+ skipCount
];
256 CFRetain(result
->certificates
[ix
]);
262 SecCertificatePathRef
SecCertificatePathCopyAddingLeaf(SecCertificatePathRef path
,
263 SecCertificateRef leaf
) {
264 CFAllocatorRef allocator
= kCFAllocatorDefault
;
266 CFIndex selfIssued
, lastVerifiedSigner
;
269 /* First make sure the new leaf is signed by path's current leaf. */
270 SecKeyRef issuerKey
= SecCertificatePathCopyPublicKeyAtIndex(path
, 0);
273 OSStatus status
= SecCertificateIsSignedBy(leaf
, issuerKey
);
274 CFRelease(issuerKey
);
278 count
= path
->count
+ 1;
279 lastVerifiedSigner
= path
->lastVerifiedSigner
+ 1;
280 selfIssued
= path
->selfIssued
;
281 isSelfSigned
= path
->isSelfSigned
;
283 CFIndex size
= sizeof(struct SecCertificatePath
) +
284 count
* sizeof(SecCertificateRef
);
285 SecCertificatePathRef result
=
286 (SecCertificatePathRef
)_CFRuntimeCreateInstance(allocator
,
287 SecCertificatePathGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
291 result
->count
= count
;
292 result
->nextParentSource
= 0;
293 result
->lastVerifiedSigner
= lastVerifiedSigner
;
294 result
->selfIssued
= selfIssued
;
295 result
->isSelfSigned
= isSelfSigned
;
296 result
->isAnchored
= path
->isAnchored
;
298 for (ix
= 1; ix
< count
; ++ix
) {
299 result
->certificates
[ix
] = path
->certificates
[ix
- 1];
300 CFRetain(result
->certificates
[ix
]);
302 result
->certificates
[0] = leaf
;
308 /* Create an array of CFDataRefs from a certificate path. */
309 xpc_object_t
SecCertificatePathCopyXPCArray(SecCertificatePathRef path
, CFErrorRef
*error
) {
310 xpc_object_t xpc_chain
= NULL
;
311 size_t ix
, count
= path
->count
;
312 require_action_quiet(xpc_chain
= xpc_array_create(NULL
, 0), exit
, SecError(errSecParam
, error
, CFSTR("xpc_array_create failed")));
313 for (ix
= 0; ix
< count
; ++ix
) {
314 SecCertificateRef cert
= SecCertificatePathGetCertificateAtIndex(path
, ix
);
315 if (!SecCertificateAppendToXPCArray(cert
, xpc_chain
, error
)) {
316 xpc_release(xpc_chain
);
325 /* Record the fact that we found our own root cert as our parent
327 void SecCertificatePathSetSelfIssued(
328 SecCertificatePathRef certificatePath
) {
329 if (certificatePath
->selfIssued
>= 0) {
330 secdebug("trust", "%@ is already issued at %" PRIdCFIndex
, certificatePath
,
331 certificatePath
->selfIssued
);
334 secdebug("trust", "%@ is self issued", certificatePath
);
335 certificatePath
->selfIssued
= certificatePath
->count
- 1;
338 void SecCertificatePathSetIsAnchored(
339 SecCertificatePathRef certificatePath
) {
340 secdebug("trust", "%@ is anchored", certificatePath
);
341 certificatePath
->isAnchored
= true;
344 /* Return the index of the first non anchor certificate in the chain that is
345 self signed counting from the leaf up. Return -1 if there is none. */
346 CFIndex
SecCertificatePathSelfSignedIndex(
347 SecCertificatePathRef certificatePath
) {
348 if (certificatePath
->isSelfSigned
)
349 return certificatePath
->selfIssued
;
353 Boolean
SecCertificatePathIsAnchored(
354 SecCertificatePathRef certificatePath
) {
355 return certificatePath
->isAnchored
;
358 void SecCertificatePathSetNextSourceIndex(
359 SecCertificatePathRef certificatePath
, CFIndex sourceIndex
) {
360 certificatePath
->nextParentSource
= sourceIndex
;
363 CFIndex
SecCertificatePathGetNextSourceIndex(
364 SecCertificatePathRef certificatePath
) {
365 return certificatePath
->nextParentSource
;
368 CFIndex
SecCertificatePathGetCount(
369 SecCertificatePathRef certificatePath
) {
370 check(certificatePath
);
371 return certificatePath
? certificatePath
->count
: 0;
374 SecCertificateRef
SecCertificatePathGetCertificateAtIndex(
375 SecCertificatePathRef certificatePath
, CFIndex ix
) {
376 check(certificatePath
&& ix
>= 0 && ix
< certificatePath
->count
);
377 return certificatePath
->certificates
[ix
];
380 CFIndex
SecCertificatePathGetIndexOfCertificate(SecCertificatePathRef path
,
381 SecCertificateRef certificate
) {
382 CFIndex ix
, count
= path
->count
;
383 for (ix
= 0; ix
< count
; ++ix
) {
384 if (CFEqual(path
->certificates
[ix
], certificate
))
391 /* Return the leaf certificate for certificatePath. */
392 SecCertificateRef
SecCertificatePathGetLeaf(
393 SecCertificatePathRef certificatePath
) {
394 return SecCertificatePathGetCertificateAtIndex(certificatePath
, 0);
398 /* Return the root certificate for certificatePath. Note that root is just
399 the top of the path as far as it is constructed. It may or may not be
400 trusted or self signed. */
401 SecCertificateRef
SecCertificatePathGetRoot(
402 SecCertificatePathRef certificatePath
) {
403 return SecCertificatePathGetCertificateAtIndex(certificatePath
,
404 SecCertificatePathGetCount(certificatePath
) - 1);
407 SecKeyRef
SecCertificatePathCopyPublicKeyAtIndex(
408 SecCertificatePathRef certificatePath
, CFIndex ix
) {
409 SecCertificateRef certificate
=
410 SecCertificatePathGetCertificateAtIndex(certificatePath
, ix
);
411 const DERAlgorithmId
*algId
=
412 SecCertificateGetPublicKeyAlgorithm(certificate
);
413 const DERItem
*params
= NULL
;
414 if (algId
->params
.length
!= 0) {
415 params
= &algId
->params
;
417 CFIndex count
= certificatePath
->count
;
418 for (++ix
; ix
< count
; ++ix
) {
419 certificate
= certificatePath
->certificates
[ix
];
420 const DERAlgorithmId
*chain_algId
=
421 SecCertificateGetPublicKeyAlgorithm(certificate
);
422 if (!DEROidCompare(&algId
->oid
, &chain_algId
->oid
)) {
423 /* Algorithm oids differ, params stay NULL. */
426 if (chain_algId
->params
.length
!= 0) {
427 params
= &chain_algId
->params
;
432 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
433 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
434 SecAsn1Item params1
= {
435 .Data
= params
? params
->data
: NULL
,
436 .Length
= params
? params
->length
: 0
438 SecAsn1Item keyData1
= {
439 .Data
= keyData
? keyData
->data
: NULL
,
440 .Length
= keyData
? keyData
->length
: 0
442 return SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
446 SecPathVerifyStatus
SecCertificatePathVerify(
447 SecCertificatePathRef certificatePath
) {
448 check(certificatePath
);
449 if (!certificatePath
)
450 return kSecPathVerifyFailed
;
452 certificatePath
->lastVerifiedSigner
< certificatePath
->count
- 1;
453 ++certificatePath
->lastVerifiedSigner
) {
454 SecKeyRef issuerKey
=
455 SecCertificatePathCopyPublicKeyAtIndex(certificatePath
,
456 certificatePath
->lastVerifiedSigner
+ 1);
458 return kSecPathVerifiesUnknown
;
459 OSStatus status
= SecCertificateIsSignedBy(
460 certificatePath
->certificates
[certificatePath
->lastVerifiedSigner
],
462 CFRelease(issuerKey
);
464 return kSecPathVerifyFailed
;
468 if (certificatePath
->selfIssued
>= 0 && !certificatePath
->isSelfSigned
) {
469 SecKeyRef issuerKey
=
470 SecCertificatePathCopyPublicKeyAtIndex(certificatePath
,
471 certificatePath
->selfIssued
);
473 certificatePath
->selfIssued
= -1;
475 OSStatus status
= SecCertificateIsSignedBy(
476 certificatePath
->certificates
[certificatePath
->selfIssued
],
478 CFRelease(issuerKey
);
480 certificatePath
->isSelfSigned
= true;
482 certificatePath
->selfIssued
= -1;
487 return kSecPathVerifySuccess
;
490 /* Return a score for this certificate chain. */
491 CFIndex
SecCertificatePathScore(
492 SecCertificatePathRef certificatePath
, CFAbsoluteTime verifyTime
) {
494 if (certificatePath
->isAnchored
) {
495 /* Anchored paths for the win! */
499 /* Score points for each certificate in the chain. */
500 score
+= 10 * certificatePath
->count
;
502 if (certificatePath
->isSelfSigned
) {
503 /* If there is a self signed certificate at the end ofthe chain we
504 count it as an extra certificate. If there is one in the middle
505 of the chain we count it for half. */
506 if (certificatePath
->selfIssued
== certificatePath
->count
- 1)
512 /* Paths that don't verify score terribly. */
513 if (certificatePath
->lastVerifiedSigner
!= certificatePath
->count
- 1) {
514 secdebug("trust", "lvs: %" PRIdCFIndex
" count: %" PRIdCFIndex
,
515 certificatePath
->lastVerifiedSigner
, certificatePath
->count
);
519 /* Subtract 1 point for each not valid certificate, make sure we
520 subtract less than the amount we add per certificate, since
521 regardless of temporal validity we still prefer longer chains
522 to shorter ones. This distinction is just to ensure that when
523 everything else is equal we prefer the chain with the most
524 certificates that are valid at the given verifyTime. */
526 for (ix
= 0; ix
< certificatePath
->count
- 1; ++ix
) {
527 if (!SecCertificateIsValid(certificatePath
->certificates
[ix
],