2 * Copyright (c) 2016 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 /* INSTRUCTIONS FOR ADDING NEW SUBTESTS:
26 * 1. Add the certificates, as DER-encoded files with the 'cer' extension, to OSX/shared_regressions/si-20-sectrust-policies-data/
27 * 2. Add a new dictionary to the test plist (OSX/shared_regressions/si-20-sectrust-policies-data/PinningPolicyTrustTest.plist).
28 * This dictionary must include: (see constants below)
35 * It is strongly recommended that all test dictionaries include the Anchors and VerifyDate keys.
36 * Addtional optional keys are defined below.
39 /* INSTRUCTIONS FOR DEBUGGING SUBTESTS:
40 * Add a debugging.plist to OSX/shared_regressions/si-20-sectrust-policies-data/ containing only those subtest dictionaries
41 * you want to debug. Git will ignore this file, so you don't accidentally commit it.
44 #include "shared_regressions.h"
46 #include <AssertMacros.h>
47 #import <Foundation/Foundation.h>
49 #include <utilities/SecInternalReleasePriv.h>
50 #include <utilities/SecCFRelease.h>
51 #include <Security/SecCertificate.h>
52 #include <Security/SecCertificatePriv.h>
53 #include <Security/SecPolicyPriv.h>
54 #include <Security/SecTrust.h>
56 /* Key Constants for Test Dictionaries */
57 const NSString *kSecTrustTestMajorTestName = @"MajorTestName"; /* Required; value: string */
58 const NSString *kSecTrustTestMinorTestName = @"MinorTestName"; /* Required; value: string */
59 const NSString *kSecTrustTestPolicies = @"Policies"; /* Required; value: dictionary or array of dictionaries */
60 const NSString *kSecTrustTestLeaf = @"Leaf"; /* Required; value: string */
61 const NSString *kSecTrustTestIntermediates = @"Intermediates"; /* Required; value: string or array of strings */
62 const NSString *kSecTrustTestAnchors = @"Anchors"; /* Recommended; value: string or array of strings */
63 const NSString *kSecTrustTestVerifyDate = @"VerifyDate"; /* Recommended; value: date */
64 const NSString *kSecTrustTestExpectedResult = @"ExpectedResult"; /* Required; value: number */
65 const NSString *kSecTrustTestChainLength = @"ChainLength"; /* Optional; value: number */
66 const NSString *kSecTrustTestEnableTestCerts= @"EnableTestCertificates"; /* Optiona; value: string */
68 /* Key Constants for Policies Dictionaries */
69 const NSString *kSecTrustTestPolicyOID = @"PolicyIdentifier"; /* Required; value: string */
70 const NSString *kSecTrustTestPolicyProperties = @"Properties"; /* Optional; value: dictionary, see Policy Value Constants, SecPolicy.h */
72 const NSString *kSecTrustTestPinningPolicyResources = @"si-20-sectrust-policies-data";
74 @interface TestObject : NSObject
75 @property (readonly) NSMutableArray *certificates;
76 @property (readonly) NSMutableArray *policies;
77 @property (readonly) NSMutableArray *anchors;
78 @property (readonly) NSString *fullTestName;
80 - (id)initWithMajorTestName:(NSString *)majorTestName minorTestName:(NSString *)minorTestName;
81 - (bool)addLeafToCertificates:(NSString *)leafName;
82 - (bool)addCertsToArray:(id)pathsObj outputArray:(NSMutableArray *)outArray;
83 - (bool)addIntermediatesToCertificates:(id)intermediatesObj;
84 - (bool)addPolicies:(id)policiesObj;
85 - (bool)addAnchors:(id)anchorsObj;
88 @implementation TestObject
95 - (id)initWithMajorTestName:(NSString *)majorTestName minorTestName:(NSString *)minorTestName {
96 if ((self = [super init])) {
97 _fullTestName = [[majorTestName stringByAppendingString:@"-"] stringByAppendingString:minorTestName];
102 - (bool)addLeafToCertificates:(NSString *)leafName {
103 SecCertificateRef cert;
104 NSString *path = nil;
105 require_action_quiet(leafName, errOut,
106 fail("%@: failed to get leaf for test", _fullTestName));
108 path = [[NSBundle mainBundle]
109 pathForResource:leafName
111 inDirectory:(NSString *)kSecTrustTestPinningPolicyResources];
112 require_action_quiet(path, errOut, fail("%@: failed to get path for leaf", _fullTestName));
113 cert = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
114 require_action_quiet(cert, errOut,
115 fail("%@: failed to create leaf certificate from path %@",
116 _fullTestName, path));
117 _certificates = [[NSMutableArray alloc] initWithObjects:(__bridge id)cert, nil];
119 require_action_quiet(_certificates, errOut,
120 fail("%@: failed to initialize certificates array",
128 - (bool)addCertsToArray:(id)pathsObj outputArray:(NSMutableArray *)outArray {
129 __block SecCertificateRef cert = NULL;
130 __block NSString* path = nil;
131 require_action_quiet(pathsObj, errOut,
132 fail("%@: failed to get certificate paths for test", _fullTestName));
134 if ([pathsObj isKindOfClass:[NSString class]]) {
135 /* Only one cert path */
136 path = [[NSBundle mainBundle]
137 pathForResource:pathsObj
139 inDirectory:(NSString *)kSecTrustTestPinningPolicyResources];
140 require_action_quiet(path, errOut, fail("%@: failed to get path for cert",
142 cert = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
143 require_action_quiet(cert, errOut,
144 fail("%@: failed to create certificate from path %@",
145 _fullTestName, path));
146 [outArray addObject:(__bridge id)cert];
150 else if ([pathsObj isKindOfClass:[NSArray class]]) {
151 /* Test has more than one intermediate */
152 [(NSArray *)pathsObj enumerateObjectsUsingBlock:^(NSString *resource, NSUInteger idx, BOOL *stop) {
153 path = [[NSBundle mainBundle]
154 pathForResource:resource
156 inDirectory:(NSString *)kSecTrustTestPinningPolicyResources];
157 require_action_quiet(path, blockOut,
158 fail("%@: failed to get path for cert %ld",
159 _fullTestName, (unsigned long)idx));
160 cert = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
161 require_action_quiet(cert, blockOut,
162 fail("%@: failed to create certificate %ld from path %@",
163 _fullTestName, (unsigned long) idx, path));
164 [outArray addObject:(__bridge id)cert];
176 fail("%@: unexpected type for intermediates or anchors value", _fullTestName);
188 - (bool)addIntermediatesToCertificates:(id)intermediatesObj {
189 require_action_quiet(intermediatesObj, errOut,
190 fail("%@: failed to get intermediates for test", _fullTestName));
192 require_action_quiet([self addCertsToArray:intermediatesObj outputArray:_certificates], errOut,
193 fail("%@: failed to add intermediates to certificates array", _fullTestName));
195 if ([intermediatesObj isKindOfClass:[NSString class]]) {
196 require_action_quiet([_certificates count] == 2, errOut,
197 fail("%@: failed to add all intermediates", _fullTestName));
198 } else if ([intermediatesObj isKindOfClass:[NSArray class]]) {
199 require_action_quiet([_certificates count] == [(NSArray *)intermediatesObj count] + 1, errOut,
200 fail("%@: failed to add all intermediates", _fullTestName));
209 - (bool)addPolicies:(id)policiesObj {
210 __block SecPolicyRef policy = NULL;
211 require_action_quiet(policiesObj, errOut,
212 fail("%@: failed to get policies for test", _fullTestName));
214 _policies = [[NSMutableArray alloc] init];
215 require_action_quiet(_policies, errOut,
216 fail("%@: failed to initialize policies array", _fullTestName));
217 if ([policiesObj isKindOfClass:[NSDictionary class]]) {
218 /* Test has only one policy */
219 NSString *policyIdentifier = [(NSDictionary *)policiesObj objectForKey:kSecTrustTestPolicyOID];
220 NSDictionary *policyProperties = [(NSDictionary *)policiesObj objectForKey:kSecTrustTestPolicyProperties];
221 require_action_quiet(policyIdentifier, errOut, fail("%@: failed to get policy OID", _fullTestName));
223 policy = SecPolicyCreateWithProperties((__bridge CFStringRef)policyIdentifier,
224 (__bridge CFDictionaryRef)policyProperties);
225 require_action_quiet(policy, errOut,
226 fail("%@: failed to create properties for policy OID %@",
227 _fullTestName, policyIdentifier));
228 [_policies addObject:(__bridge id)policy];
229 CFReleaseNull(policy);
232 else if ([policiesObj isKindOfClass:[NSArray class]]) {
233 /* Test more than one intermediate */
234 [(NSArray *)policiesObj enumerateObjectsUsingBlock:^(NSDictionary *policyDict, NSUInteger idx, BOOL *stop) {
235 NSString *policyIdentifier = [(NSDictionary *)policyDict objectForKey:kSecTrustTestPolicyOID];
236 NSDictionary *policyProperties = [(NSDictionary *)policyDict objectForKey:kSecTrustTestPolicyProperties];
237 require_action_quiet(policyIdentifier, blockOut, fail("%@: failed to get policy OID", _fullTestName));
239 policy = SecPolicyCreateWithProperties((__bridge CFStringRef)policyIdentifier,
240 (__bridge CFDictionaryRef)policyProperties);
241 require_action_quiet(policy, blockOut,
242 fail("%@: failed to create properties for policy OID %@",
243 _fullTestName, policyIdentifier));
244 [_policies addObject:(__bridge id)policy];
246 CFReleaseNull(policy);
250 CFReleaseNull(policy);
254 require_action_quiet([(NSArray *)policiesObj count] == [_policies count], errOut,
255 fail("%@: failed to add all policies", _fullTestName));
259 fail("%@: unexpected type for %@ value", _fullTestName, kSecTrustTestPolicies);
266 CFReleaseNull(policy);
270 - (bool)addAnchors:(id)anchorsObj {
271 require_action_quiet(anchorsObj, errOut,
272 fail("%@: failed to get anchors for test", _fullTestName));
274 _anchors = [[NSMutableArray alloc] init];
275 require_action_quiet(_anchors, errOut,
276 fail("%@: failed to initialize anchors array", _fullTestName));
277 require_action_quiet([self addCertsToArray:anchorsObj outputArray:_anchors], errOut,
278 fail("%@: failed to add anchors to anchors array", _fullTestName));
280 if ([anchorsObj isKindOfClass:[NSString class]]) {
281 require_action_quiet([_anchors count] == 1, errOut,
282 fail("%@: failed to add all anchors", _fullTestName));
283 } else if ([anchorsObj isKindOfClass:[NSArray class]]) {
284 require_action_quiet([_anchors count] == [(NSArray *)anchorsObj count], errOut,
285 fail("%@: failed to add all anchors", _fullTestName));
296 void (^runTestForObject)(id, NSUInteger, BOOL *) =
297 ^(NSDictionary *testDict, NSUInteger idx, BOOL *stop) {
298 NSString *majorTestName = nil, *minorTestName = nil;
299 TestObject *test = nil;
300 SecTrustRef trust = NULL;
301 SecTrustResultType trustResult = kSecTrustResultInvalid;
302 NSDate *verifyDate = nil;
303 NSNumber *expectedResult = nil, *chainLen = nil;
305 bool enableTestCertificates = (bool)[testDict objectForKey:kSecTrustTestEnableTestCerts];
307 /* Test name, for documentation purposes */
308 majorTestName = [testDict objectForKey:kSecTrustTestMajorTestName];
309 minorTestName = [testDict objectForKey:kSecTrustTestMinorTestName];
310 require_action_quiet(majorTestName && minorTestName, testOut,
311 fail("Failed to create test names for test %lu",(unsigned long)idx));
312 test = [[TestObject alloc] initWithMajorTestName:majorTestName minorTestName:minorTestName];
313 require_action_quiet((test), testOut, fail("%@-%@: failed to create test object", majorTestName, minorTestName));
315 /* Populate the certificates array */
316 require_quiet([test addLeafToCertificates:[testDict objectForKey:kSecTrustTestLeaf]], testOut);
317 require_quiet([test addIntermediatesToCertificates:[testDict objectForKey:kSecTrustTestIntermediates]], testOut);
319 /* Optionally: enable test certificates for the policy */
320 if (enableTestCertificates) {
321 /* Note: Some of the policies use defaults writes with the "com.apple.Security" domain;
322 * others use "com.apple.security". Set both since we don't know which one this is. */
323 CFPreferencesSetAppValue((__bridge CFStringRef)[testDict objectForKey:kSecTrustTestEnableTestCerts],
324 kCFBooleanTrue, CFSTR("com.apple.Security"));
325 CFPreferencesSetAppValue((__bridge CFStringRef)[testDict objectForKey:kSecTrustTestEnableTestCerts],
326 kCFBooleanTrue, CFSTR("com.apple.security"));
329 /* Create the policies */
330 require_quiet([test addPolicies:[testDict objectForKey:kSecTrustTestPolicies]], testOut);
332 /* Create the trust object */
333 require_noerr_action_quiet(SecTrustCreateWithCertificates((__bridge CFArrayRef)test.certificates,
334 (__bridge CFArrayRef)test.policies,
337 fail("%@: failed to create trust ref", test.fullTestName));
339 /* Optionally set anchors in trust object */
340 if ([testDict objectForKey:kSecTrustTestAnchors]) {
341 require_quiet([test addAnchors:[testDict objectForKey:kSecTrustTestAnchors]], testOut);
342 require_noerr_action_quiet(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)test.anchors),
344 fail("%@: failed to add anchors to trust ref", test.fullTestName));
347 /* Set optional date in trust object */
348 verifyDate = [testDict objectForKey:kSecTrustTestVerifyDate];
350 require_noerr_action_quiet(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)verifyDate), testOut,
351 fail("%@: failed to set verify date, %@, in trust ref", test.fullTestName,
356 require_noerr_action_quiet(SecTrustEvaluate(trust, &trustResult), testOut,
357 fail("%@: failed to evaluate trust", test.fullTestName));
360 require_action_quiet(expectedResult = [testDict objectForKey:kSecTrustTestExpectedResult],
361 testOut, fail("%@: failed to get expected result for test", test.fullTestName));
363 /* If we enabled test certificates on a non-internal device, expect a failure instead of succees. */
364 if (enableTestCertificates && !SecIsInternalRelease() && ([expectedResult unsignedIntValue] == 4)) {
366 "%@: actual trust result %u did not match expected trust result %u",
367 test.fullTestName, trustResult, 5);
369 ok(trustResult == [expectedResult unsignedIntValue],
370 "%@: actual trust result %u did not match expected trust result %u",
371 test.fullTestName, trustResult, [expectedResult unsignedIntValue]);
373 require_quiet(trustResult == [expectedResult unsignedIntValue], testOut);
375 require_quiet(chainLen = [testDict objectForKey:kSecTrustTestChainLength], testOut);
376 require_action_quiet(SecTrustGetCertificateCount(trust) == [chainLen longValue], testOut,
377 fail("%@: actual chain length %ld did not match expected chain length %ld",
378 test.fullTestName, SecTrustGetCertificateCount(trust), [chainLen longValue]));
381 // Unset preferences to prevent contamination
382 if (enableTestCertificates) {
383 CFPreferencesSetAppValue((__bridge CFStringRef)[testDict objectForKey:kSecTrustTestEnableTestCerts],
384 kCFBooleanFalse, CFSTR("com.apple.security"));
385 CFPreferencesSetAppValue((__bridge CFStringRef)[testDict objectForKey:kSecTrustTestEnableTestCerts],
386 kCFBooleanFalse, CFSTR("com.apple.Security"));
388 CFReleaseNull(trust);
391 static void tests(void)
393 NSURL *testPlist = nil;
394 NSArray *testsArray = nil;
396 testPlist = [[NSBundle mainBundle] URLForResource:@"debugging" withExtension:@"plist"
397 subdirectory:(NSString *)kSecTrustTestPinningPolicyResources ];
399 testPlist = [[NSBundle mainBundle] URLForResource:nil withExtension:@"plist"
400 subdirectory:(NSString *)kSecTrustTestPinningPolicyResources ];
402 require_action_quiet(testPlist, exit,
403 fail("Failed to get tests plist from %@", kSecTrustTestPinningPolicyResources));
405 testsArray = [NSArray arrayWithContentsOfURL: testPlist];
406 require_action_quiet(testsArray, exit,
407 fail("Failed to create array from plist"));
409 plan_tests((int)[testsArray count]);
411 [testsArray enumerateObjectsUsingBlock:runTestForObject];
417 int si_20_sectrust_policies(int argc, char *const *argv)