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/oids.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
SecCertificatePathCopyDescription(CFTypeRef cf
) {
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
);
377 check(ix
>= 0 && ix
< certificatePath
->count
);
378 return certificatePath
->certificates
[ix
];
381 CFIndex
SecCertificatePathGetIndexOfCertificate(SecCertificatePathRef path
,
382 SecCertificateRef certificate
) {
383 CFIndex ix
, count
= path
->count
;
384 for (ix
= 0; ix
< count
; ++ix
) {
385 if (CFEqual(path
->certificates
[ix
], certificate
))
392 /* Return the leaf certificate for certificatePath. */
393 SecCertificateRef
SecCertificatePathGetLeaf(
394 SecCertificatePathRef certificatePath
) {
395 return SecCertificatePathGetCertificateAtIndex(certificatePath
, 0);
399 /* Return the root certificate for certificatePath. Note that root is just
400 the top of the path as far as it is constructed. It may or may not be
401 trusted or self signed. */
402 SecCertificateRef
SecCertificatePathGetRoot(
403 SecCertificatePathRef certificatePath
) {
404 return SecCertificatePathGetCertificateAtIndex(certificatePath
,
405 SecCertificatePathGetCount(certificatePath
) - 1);
408 SecKeyRef
SecCertificatePathCopyPublicKeyAtIndex(
409 SecCertificatePathRef certificatePath
, CFIndex ix
) {
410 SecCertificateRef certificate
=
411 SecCertificatePathGetCertificateAtIndex(certificatePath
, ix
);
412 const DERAlgorithmId
*algId
=
413 SecCertificateGetPublicKeyAlgorithm(certificate
);
414 const DERItem
*params
= NULL
;
415 if (algId
->params
.length
!= 0) {
416 params
= &algId
->params
;
418 CFIndex count
= certificatePath
->count
;
419 for (++ix
; ix
< count
; ++ix
) {
420 certificate
= certificatePath
->certificates
[ix
];
421 const DERAlgorithmId
*chain_algId
=
422 SecCertificateGetPublicKeyAlgorithm(certificate
);
423 if (!DEROidCompare(&algId
->oid
, &chain_algId
->oid
)) {
424 /* Algorithm oids differ, params stay NULL. */
427 if (chain_algId
->params
.length
!= 0) {
428 params
= &chain_algId
->params
;
433 const DERItem
*keyData
= SecCertificateGetPublicKeyData(certificate
);
434 SecAsn1Oid oid1
= { .Data
= algId
->oid
.data
, .Length
= algId
->oid
.length
};
435 SecAsn1Item params1
= {
436 .Data
= params
? params
->data
: NULL
,
437 .Length
= params
? params
->length
: 0
439 SecAsn1Item keyData1
= {
440 .Data
= keyData
? keyData
->data
: NULL
,
441 .Length
= keyData
? keyData
->length
: 0
443 return SecKeyCreatePublicFromDER(kCFAllocatorDefault
, &oid1
, ¶ms1
,
447 SecPathVerifyStatus
SecCertificatePathVerify(
448 SecCertificatePathRef certificatePath
) {
449 check(certificatePath
);
450 if (!certificatePath
)
451 return kSecPathVerifyFailed
;
453 certificatePath
->lastVerifiedSigner
< certificatePath
->count
- 1;
454 ++certificatePath
->lastVerifiedSigner
) {
455 SecKeyRef issuerKey
=
456 SecCertificatePathCopyPublicKeyAtIndex(certificatePath
,
457 certificatePath
->lastVerifiedSigner
+ 1);
459 return kSecPathVerifiesUnknown
;
460 OSStatus status
= SecCertificateIsSignedBy(
461 certificatePath
->certificates
[certificatePath
->lastVerifiedSigner
],
463 CFRelease(issuerKey
);
465 return kSecPathVerifyFailed
;
469 if (certificatePath
->selfIssued
>= 0 && !certificatePath
->isSelfSigned
) {
470 SecKeyRef issuerKey
=
471 SecCertificatePathCopyPublicKeyAtIndex(certificatePath
,
472 certificatePath
->selfIssued
);
474 certificatePath
->selfIssued
= -1;
476 OSStatus status
= SecCertificateIsSignedBy(
477 certificatePath
->certificates
[certificatePath
->selfIssued
],
479 CFRelease(issuerKey
);
481 certificatePath
->isSelfSigned
= true;
483 certificatePath
->selfIssued
= -1;
488 return kSecPathVerifySuccess
;
491 /* Return a score for this certificate chain. */
492 CFIndex
SecCertificatePathScore(
493 SecCertificatePathRef certificatePath
, CFAbsoluteTime verifyTime
) {
495 if (certificatePath
->isAnchored
) {
496 /* Anchored paths for the win! */
500 /* Score points for each certificate in the chain. */
501 score
+= 10 * certificatePath
->count
;
503 if (certificatePath
->isSelfSigned
) {
504 /* If there is a self signed certificate at the end ofthe chain we
505 count it as an extra certificate. If there is one in the middle
506 of the chain we count it for half. */
507 if (certificatePath
->selfIssued
== certificatePath
->count
- 1)
513 /* Paths that don't verify score terribly. */
514 if (certificatePath
->lastVerifiedSigner
!= certificatePath
->count
- 1) {
515 secdebug("trust", "lvs: %" PRIdCFIndex
" count: %" PRIdCFIndex
,
516 certificatePath
->lastVerifiedSigner
, certificatePath
->count
);
520 /* Subtract 1 point for each not valid certificate, make sure we
521 subtract less than the amount we add per certificate, since
522 regardless of temporal validity we still prefer longer chains
523 to shorter ones. This distinction is just to ensure that when
524 everything else is equal we prefer the chain with the most
525 certificates that are valid at the given verifyTime. */
527 for (ix
= 0; ix
< certificatePath
->count
- 1; ++ix
) {
528 if (!SecCertificateIsValid(certificatePath
->certificates
[ix
],