2 // TrustEvaluationTestHelpers.m
7 #include <AssertMacros.h>
8 #import <Foundation/Foundation.h>
9 #import <Security/Security.h>
11 #include <utilities/SecInternalReleasePriv.h>
12 #include <utilities/SecCFRelease.h>
13 #include <Security/SecCertificate.h>
14 #include <Security/SecCertificatePriv.h>
15 #include <Security/SecPolicyPriv.h>
16 #include <Security/SecTrust.h>
17 #include <Security/SecTrustPriv.h>
19 #include <sys/socket.h>
20 #include <sys/types.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
27 #include "TrustEvaluationTestHelpers.h"
29 // Want a class for running trust evaluations
30 // Want a dictionary-driven test with callback
32 @interface TestTrustEvaluation ()
33 @property NSString *directory;
34 @property NSMutableArray *certificates;
35 @property NSMutableArray *policies;
36 @property BOOL enableTestCertificates;
39 @implementation TestTrustEvaluation
40 @synthesize ocspResponses = _ocspResponses;
41 @synthesize presentedSCTs = _presentedSCTs;
42 @synthesize trustedCTLogs = _trustedCTLogs;
45 - (instancetype)initWithCertificates:(NSArray *)certs policies:(NSArray *)policies {
46 if (self = [super init]) {
47 if (errSecSuccess != SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, (__bridge CFArrayRef)policies, &_trust)) {
54 - (void)addAnchor:(SecCertificateRef)certificate
56 CFArrayRef anchors = NULL;
57 SecTrustCopyCustomAnchorCertificates(_trust, &anchors);
59 NSMutableArray *newAnchors = [NSMutableArray array];
61 [newAnchors addObjectsFromArray:CFBridgingRelease(anchors)];
63 [newAnchors addObject:(__bridge id)certificate];
64 (void)SecTrustSetAnchorCertificates(_trust, (__bridge CFArrayRef)newAnchors);
67 - (void)setAnchors:(NSArray *)anchorArray {
68 (void)SecTrustSetAnchorCertificates(_trust, (__bridge CFArrayRef)anchorArray);
71 - (NSArray *)anchors {
72 CFArrayRef anchors = NULL;
73 SecTrustCopyCustomAnchorCertificates(_trust, &anchors);
74 return CFBridgingRelease(anchors);
77 - (void)setOcspResponses:(NSArray *)ocspResponsesArray {
78 if (ocspResponsesArray != _ocspResponses) {
80 _ocspResponses = ocspResponsesArray;
81 (void)SecTrustSetOCSPResponse(_trust, (__bridge CFArrayRef)ocspResponsesArray);
85 - (void)setPresentedSCTs:(NSArray *)presentedSCTsArray {
86 if (presentedSCTsArray != _presentedSCTs) {
88 _presentedSCTs = presentedSCTsArray;
89 (void)SecTrustSetSignedCertificateTimestamps(_trust, (__bridge CFArrayRef)presentedSCTsArray);
93 - (void)setTrustedCTLogs:(NSArray *)trustedCTLogsArray {
94 if (trustedCTLogsArray != _trustedCTLogs) {
96 _trustedCTLogs = trustedCTLogsArray;
97 (void)SecTrustSetTrustedLogs(_trust, (__bridge CFArrayRef)trustedCTLogsArray);
101 - (void)setVerifyDate:(NSDate *)aVerifyDate {
102 if (aVerifyDate != _verifyDate) {
104 _verifyDate = aVerifyDate;
105 (void)SecTrustSetVerifyDate(_trust, (__bridge CFDateRef)aVerifyDate);
109 - (void)setNeedsEvaluation {
110 SecTrustSetNeedsEvaluation(_trust);
113 - (bool)evaluate:(out NSError * _Nullable __autoreleasing *)outError {
114 CFErrorRef localError = nil;
115 _trustResult = kSecTrustResultInvalid;
116 _resultDictionary = nil;
117 bool result = SecTrustEvaluateWithError(_trust, &localError);
118 if (outError && localError) {
119 *outError = CFBridgingRelease(localError);
121 (void)SecTrustGetTrustResult(_trust, &_trustResult);
122 _resultDictionary = CFBridgingRelease(SecTrustCopyResult(_trust));
127 CFReleaseNull(_trust);
131 * MARK: Dictionary-driven trust objects
134 /* INSTRUCTIONS FOR ADDING NEW DICTIONARY-DRIVEN TRUSTS:
135 * 1. Add the certificates, as DER-encoded files with the 'cer' extension to a directory included in the test Resources
136 * (e.g. OSX/shared_regressions/si-20-sectrust-policies-data/)
137 * NOTE: If your cert needs to be named with "(i[Pp]hone|i[Pp]ad|i[Pp]od)", you need to make two copies -- one named properly
138 * and another named such that it doesn't match that regex. Use the regex trick below for TARGET_OS_TV to make sure your test
140 * 2. The input dictionary must include: (see constants below)
147 * It is strongly recommended that all test dictionaries include the Anchors and VerifyDate keys.
148 * Addtional optional keys are defined below.
151 /* Key Constants for Test Dictionaries */
152 const NSString *kSecTrustTestMajorTestName = @"MajorTestName"; /* Required; value: string */
153 const NSString *kSecTrustTestMinorTestName = @"MinorTestName"; /* Required; value: string */
154 const NSString *kSecTrustTestPolicies = @"Policies"; /* Required; value: dictionary or array of dictionaries */
155 const NSString *kSecTrustTestLeaf = @"Leaf"; /* Required; value: string */
156 const NSString *kSecTrustTestIntermediates = @"Intermediates"; /* Optional; value: string or array of strings */
157 const NSString *kSecTrustTestAnchors = @"Anchors"; /* Recommended; value: string or array of strings */
158 const NSString *kSecTrustTestVerifyDate = @"VerifyDate"; /* Recommended; value: date */
159 const NSString *kSecTrustTestExpectedResult = @"ExpectedResult"; /* Required; value: number */
160 const NSString *kSecTrustTestChainLength = @"ChainLength"; /* Optional; value: number */
161 const NSString *kSecTrustTestEnableTestCerts= @"EnableTestCertificates"; /* Optional; value: string */
162 const NSString *kSecTrustTestDisableBridgeOS= @"BridgeOSDisable"; /* Optional; value: boolean */
163 const NSString *kSecTrustTestDirectory = @"CertDirectory"; /* Required; value: string */
165 /* Key Constants for Policies Dictionaries */
166 const NSString *kSecTrustTestPolicyOID = @"PolicyIdentifier"; /* Required; value: string */
167 const NSString *kSecTrustTestPolicyProperties = @"Properties"; /* Optional; value: dictionary, see Policy Value Constants, SecPolicy.h */
169 - (void)setMajorTestName:(NSString *)majorTestName minorTestName:(NSString *)minorTestName {
170 self.fullTestName = [[majorTestName stringByAppendingString:@"-"] stringByAppendingString:minorTestName];
174 /* Mastering removes all files named i[Pp]hone, so dynamically replace any i[Pp]hone's with
175 * iPh0ne. We have two copies in the resources directory. */
176 - (NSString *)replaceiPhoneNamedFiles:(NSString *)filename {
177 NSRegularExpression *regularExpression = [NSRegularExpression regularExpressionWithPattern:@"iphone"
178 options:NSRegularExpressionCaseInsensitive
180 NSString *newfilename = [regularExpression stringByReplacingMatchesInString:filename
182 range:NSMakeRange(0, [filename length])
183 withTemplate:@"iPh0ne"];
188 - (bool)addLeafToCertificates:(NSString *)leafName {
189 SecCertificateRef cert;
190 NSString *path = nil, *filename = nil;
191 require_string(leafName, errOut, "%@: failed to get leaf for test");
193 filename = [self replaceiPhoneNamedFiles:leafName];
198 path = [[NSBundle bundleForClass:[self class]]
199 pathForResource:filename
201 inDirectory:self.directory];
202 require_string(path, errOut, "failed to get path for leaf");
203 cert = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
204 require_string(cert, errOut, "failed to create leaf certificate from path");
205 self.certificates = [[NSMutableArray alloc] initWithObjects:(__bridge id)cert, nil];
207 require_string(self.certificates, errOut, "failed to initialize certificates array");
214 - (bool)addCertsToArray:(id)pathsObj outputArray:(NSMutableArray *)outArray {
215 __block SecCertificateRef cert = NULL;
216 __block NSString* path = nil, *filename = nil;
217 require_string(pathsObj, errOut,
218 "failed to get certificate paths for test");
220 if ([pathsObj isKindOfClass:[NSString class]]) {
221 /* Only one cert path */
223 filename = [self replaceiPhoneNamedFiles:pathsObj];
227 path = [[NSBundle bundleForClass:[self class]]
228 pathForResource:filename
230 inDirectory:self.directory];
231 require_string(path, errOut, "failed to get path for cert");
232 cert = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
233 require_string(cert, errOut, "failed to create certificate from path");
234 [outArray addObject:(__bridge id)cert];
236 } else if ([pathsObj isKindOfClass:[NSArray class]]) {
237 /* Test has more than one intermediate */
238 [(NSArray *)pathsObj enumerateObjectsUsingBlock:^(NSString *resource, NSUInteger idx, BOOL *stop) {
240 filename = [self replaceiPhoneNamedFiles:resource];
244 path = [[NSBundle bundleForClass:[self class]]
245 pathForResource:filename
247 inDirectory:self.directory];
248 require_string(path, blockOut, "failed to get path for cert");
249 cert = SecCertificateCreateWithData(NULL, (CFDataRef)[NSData dataWithContentsOfFile:path]);
250 require_string(cert, blockOut, "failed to create certificate %ld from path %@");
251 [outArray addObject:(__bridge id)cert];
261 require_string(false, errOut, "unexpected type for intermediates or anchors value");
271 - (bool)addIntermediatesToCertificates:(id)intermediatesObj {
272 require_string(intermediatesObj, errOut, "failed to get intermediates for test");
274 require_string([self addCertsToArray:intermediatesObj outputArray:self.certificates], errOut,
275 "failed to add intermediates to certificates array");
277 if ([intermediatesObj isKindOfClass:[NSString class]]) {
278 require_string([self.certificates count] == 2, errOut,
279 "failed to add all intermediates");
280 } else if ([intermediatesObj isKindOfClass:[NSArray class]]) {
281 require_string([self.certificates count] == [(NSArray *)intermediatesObj count] + 1, errOut,
282 "failed to add all intermediates");
291 - (bool)addPolicy:(NSDictionary *)policyDict
293 SecPolicyRef policy = NULL;
294 NSString *policyIdentifier = [(NSDictionary *)policyDict objectForKey:kSecTrustTestPolicyOID];
295 NSDictionary *policyProperties = [(NSDictionary *)policyDict objectForKey:kSecTrustTestPolicyProperties];
296 require_string(policyIdentifier, errOut, "failed to get policy OID");
298 policy = SecPolicyCreateWithProperties((__bridge CFStringRef)policyIdentifier,
299 (__bridge CFDictionaryRef)policyProperties);
300 require_string(policy, errOut, "failed to create properties for policy OID");
301 [self.policies addObject:(__bridge id)policy];
302 CFReleaseNull(policy);
309 - (bool)addPolicies:(id)policiesObj {
310 require_string(policiesObj, errOut,
311 "failed to get policies for test");
313 self.policies = [[NSMutableArray alloc] init];
314 require_string(self.policies, errOut,
315 "failed to initialize policies array");
316 if ([policiesObj isKindOfClass:[NSDictionary class]]) {
317 /* Test has only one policy */
318 require_string([self addPolicy:policiesObj], errOut, "failed to add policy");
319 } else if ([policiesObj isKindOfClass:[NSArray class]]) {
320 /* Test more than one policy */
321 [(NSArray *)policiesObj enumerateObjectsUsingBlock:^(NSDictionary *policyDict, NSUInteger idx, BOOL *stop) {
322 if (![self addPolicy:policyDict]) {
327 require_string([(NSArray *)policiesObj count] == [self.policies count], errOut, "failed to add all policies");
329 require_string(false, errOut, "unexpected type for Policies value");
338 - (bool)setAnchorsFromPlist:(id)anchorsObj {
339 NSMutableArray *anchors = [NSMutableArray array];
340 require_string(anchorsObj, errOut, "failed to get anchors for test");
341 require_string([self addCertsToArray:anchorsObj outputArray:anchors], errOut, "failed to add anchors to anchors array");
343 if ([anchorsObj isKindOfClass:[NSString class]]) {
344 require_string([anchors count] == 1, errOut, "failed to add all anchors");
345 } else if ([anchorsObj isKindOfClass:[NSArray class]]) {
346 require_string([anchors count] == [(NSArray *)anchorsObj count], errOut, "failed to add all anchors");
349 // set the anchors in the SecTrustRef
350 self.anchors = anchors;
357 - (instancetype _Nullable) initWithTrustDictionary:(NSDictionary *)testDict
359 if (!(self = [super init])) {
363 NSString *majorTestName = nil, *minorTestName = nil;
364 SecTrustRef trust = NULL;
367 /* Some of the tests don't work on bridgeOS because there is no Certificates bundle. Skip them. */
368 if([testDict[kSecTrustTestDisableBridgeOS] boolValue]) {
369 self.bridgeOSDisabled = YES;
373 /* Test certificates work by default on internal builds. We still need this to
374 * determine whether to expect failure for production devices. */
375 self.enableTestCertificates = [testDict[kSecTrustTestEnableTestCerts] boolValue];
377 /* Test name, for documentation purposes */
378 majorTestName = testDict[kSecTrustTestMajorTestName];
379 minorTestName = testDict[kSecTrustTestMinorTestName];
380 require_string(majorTestName && minorTestName, errOut, "Failed to create test names for test");
381 [self setMajorTestName:majorTestName minorTestName:minorTestName];
384 fprintf(stderr, "BEGIN trust creation for %s", [self.fullTestName cStringUsingEncoding:NSUTF8StringEncoding]);
388 self.directory = testDict[kSecTrustTestDirectory];
389 require_string(self.directory, errOut, "No directory for test!");
391 /* Populate the certificates array */
392 require_quiet([self addLeafToCertificates:testDict[kSecTrustTestLeaf]], errOut);
394 /* Add optional intermediates to certificates array */
395 if (testDict[kSecTrustTestIntermediates]) {
396 require_quiet([self addIntermediatesToCertificates:testDict[kSecTrustTestIntermediates]], errOut);
399 /* Create the policies */
400 #if !TARGET_OS_BRIDGE
401 require_quiet([self addPolicies:testDict[kSecTrustTestPolicies]], errOut);
402 #else // TARGET_OS_BRIDGE
403 if (![self addPolicies:testDict[kSecTrustTestPolicies]]) {
404 /* Some policies aren't available on bridgeOS (because there is no Certificates bundle on bridgeOS).
405 * If we fail to add the policies for a disabled test, let SecTrustCreate fall back to the Basic policy.
406 * We'll skip the evaluation and other tests by honoring bridgeOSDisabled, but we need to return a
407 * TestTrustEvaluation object so that the test continues. */
408 if (self.bridgeOSDisabled) {
414 #endif // TARGET_OS_BRIDGE
417 /* Create the trust object */
418 require_noerr_string(SecTrustCreateWithCertificates((__bridge CFArrayRef)self.certificates,
419 (__bridge CFArrayRef)self.policies,
422 "failed to create trust ref");
425 /* Optionally set anchors in trust object */
426 if (testDict[kSecTrustTestAnchors]) {
427 require_quiet([self setAnchorsFromPlist:testDict[kSecTrustTestAnchors]], errOut);
430 /* Set optional date in trust object */
431 if (testDict[kSecTrustTestVerifyDate]) {
432 self.verifyDate = testDict[kSecTrustTestVerifyDate];
435 /* Set expected results */
436 self.expectedResult = testDict[kSecTrustTestExpectedResult];
437 self.expectedChainLength = testDict[kSecTrustTestChainLength];
440 fprintf(stderr, "END trust creation for %s", [self.fullTestName cStringUsingEncoding:NSUTF8StringEncoding]);
449 - (bool)evaluateForExpectedResults:(out NSError * _Nullable __autoreleasing *)outError
452 // Artificially skip tests for bridgeOS. To prevent test errors these need to be reported as a pass.
453 if (self.bridgeOSDisabled) {
458 if (!self.expectedResult) {
460 NSString *errorDescription = [NSString stringWithFormat:@"Test %@: no expected results set",
462 *outError = [NSError errorWithDomain:@"TrustTestsError" code:(-1)
463 userInfo:@{ NSLocalizedFailureReasonErrorKey : errorDescription}];
468 SecTrustResultType trustResult = kSecTrustResultInvalid;
469 if (errSecSuccess != SecTrustGetTrustResult(self.trust, &trustResult)) {
471 NSString *errorDescription = [NSString stringWithFormat:@"Test %@: Failed to get trust result",
473 *outError = [NSError errorWithDomain:@"TrustTestsError" code:(-2)
474 userInfo:@{ NSLocalizedFailureReasonErrorKey : errorDescription}];
481 /* If we enabled test certificates on a non-internal device, expect a failure instead of success. */
482 if (self.enableTestCertificates && !SecIsInternalRelease() && ([self.expectedResult unsignedIntValue] == 4)) {
483 if (trustResult == kSecTrustResultRecoverableTrustFailure) {
486 } else if (trustResult == [self.expectedResult unsignedIntValue]) {
492 NSString *errorDescription = [NSString stringWithFormat:@"Test %@: Expected result %@ %s does not match actual result %u %s",
493 self.fullTestName, self.expectedResult,
494 (self.enableTestCertificates ? "for test cert" : ""),
496 SecIsInternalRelease() ? "" : "on prod device"];
497 *outError = [NSError errorWithDomain:@"TrustTestsError" code:(-3)
498 userInfo:@{ NSLocalizedFailureReasonErrorKey : errorDescription}];
503 /* expected chain length is optional, but if we have it, verify */
504 if (self.expectedChainLength && (SecTrustGetCertificateCount(self.trust) != [self.expectedChainLength longValue])) {
506 NSString *errorDescription = [NSString stringWithFormat:@"Test %@: Expected chain length %@ does not match actual chain length %ld",
507 self.fullTestName, self.expectedChainLength, SecTrustGetCertificateCount(self.trust)];
508 *outError = [NSError errorWithDomain:@"TrustTestsError" code:(-4)
509 userInfo:@{ NSLocalizedFailureReasonErrorKey : errorDescription}];
518 int ping_host(char *host_name)
520 struct sockaddr_in pin;
521 struct hostent *nlp_host;
525 int retries = 5; // we try 5 times, then give up
526 char **h_addr_list = NULL;
528 while ((nlp_host=gethostbyname(host_name)) == 0 && retries--) {
529 printf("Resolve Error! (%s) %d\n", host_name, h_errno);
536 bzero(&pin,sizeof(pin));
537 pin.sin_family=AF_INET;
538 pin.sin_addr.s_addr=htonl(INADDR_ANY);
539 h_addr_list = malloc(nlp_host->h_length * sizeof(char *));
540 memcpy(h_addr_list, nlp_host->h_addr_list, nlp_host->h_length * sizeof(char *));
541 memcpy(&addr, h_addr_list[0], sizeof(struct in_addr));
542 pin.sin_addr.s_addr=addr.s_addr;
543 pin.sin_port=htons(port);
545 sd=socket(AF_INET,SOCK_STREAM,0);
547 if (connect(sd,(struct sockaddr*)&pin,sizeof(pin)) == -1) {
548 printf("connect error! (%s) %d\n", host_name, errno);