]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/tests/CKKSLoggerTests.m
9c0afc62a687cf7a8a83873db89a0ef326cfcd40
[apple/security.git] / keychain / ckks / tests / CKKSLoggerTests.m
1 /*
2 * Copyright (c) 2017 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #import "CKKSTests.h"
25 #import "keychain/ckks/CKKSAnalyticsLogger.h"
26 #import <Security/SFSQLite.h>
27 #import <Foundation/Foundation.h>
28 #import <XCTest/XCTest.h>
29
30 static NSString* tablePath = nil;
31
32 @interface SQLiteTests : XCTestCase
33 @end
34
35 @implementation SQLiteTests
36
37 + (void)setUp
38 {
39 [super setUp];
40
41 tablePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"test_table.db"];
42 }
43
44 - (void)setUp
45 {
46 [super setUp];
47
48 [[NSFileManager defaultManager] removeItemAtPath:tablePath error:nil];
49 }
50
51 - (void)tearDown
52 {
53 [[NSFileManager defaultManager] removeItemAtPath:tablePath error:nil];
54
55 [super tearDown];
56 }
57
58 - (void)testCreateLoggingDatabase
59 {
60 NSString* schema = @"CREATE table test (test_column INTEGER);";
61 SFSQLite* sqlTable = [[SFSQLite alloc] initWithPath:tablePath schema:schema];
62 NSError* error = nil;
63 XCTAssertTrue([sqlTable openWithError:&error], @"failed to open sql database");
64 XCTAssertNil(error, "encountered error opening database: %@", error);
65 XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:tablePath]);
66 [sqlTable close];
67 }
68
69 - (void)testInsertAndDelete
70 {
71 NSString* schema = @"CREATE table test (test_column INTEGER);";
72 SFSQLite* sqlTable = [[SFSQLite alloc] initWithPath:tablePath schema:schema];
73 NSError* error = nil;
74 XCTAssertTrue([sqlTable openWithError:&error], @"failed to open sql database");
75 XCTAssertNil(error, "encountered error opening database: %@", error);
76
77 [sqlTable insertOrReplaceInto:@"test" values:@{@"test_column" : @(1)}];
78 [sqlTable insertOrReplaceInto:@"test" values:@{@"test_column" : @(2)}];
79 [sqlTable insertOrReplaceInto:@"test" values:@{@"test_column" : @(3)}];
80 XCTAssertTrue([[sqlTable selectAllFrom:@"test" where:nil bindings:nil] count] == 3);
81
82 [sqlTable deleteFrom:@"test" where:@"test_column = ?" bindings:@[@3]];
83 XCTAssertTrue([[sqlTable selectAllFrom:@"test" where:nil bindings:nil] count] == 2);
84
85 [sqlTable executeSQL:@"delete from test"];
86 XCTAssertTrue([[sqlTable selectAllFrom:@"test" where:nil bindings:nil] count] == 0);
87 }
88
89 - (void)testDontCrashWhenThereAreNoWritePermissions
90 {
91 NSString* schema = @"CREATE table test (test_column INTEGER);";
92 SFSQLite* sqlTable = [[SFSQLite alloc] initWithPath:tablePath schema:schema];
93
94 NSError* error = nil;
95 XCTAssertNoThrow([sqlTable openWithError:&error], @"opening database threw an exception");
96 XCTAssertNil(error, "encountered error opening database: %@", error);
97 XCTAssertNoThrow([sqlTable close], @"closing database threw an exception");
98
99 NSDictionary* originalAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:tablePath error:&error];
100 XCTAssertNil(error, @"encountered error getting database file attributes: %@", error);
101
102 [[NSFileManager defaultManager] setAttributes:@{NSFilePosixPermissions : @(400), NSFileImmutable : @(YES)} ofItemAtPath:tablePath error:&error];
103 XCTAssertNil(error, @"encountered error setting database file attributes: %@", error);
104 XCTAssertNoThrow([sqlTable openWithError:&error]);
105 XCTAssertNotNil(error, @"failed to generate error when opening file without permissions");
106 error = nil;
107
108 [[NSFileManager defaultManager] setAttributes:originalAttributes ofItemAtPath:tablePath error:&error];
109 XCTAssertNil(error, @"encountered error setting database file attributes back to original attributes: %@", error);
110 }
111
112 - (void)testDontCrashFromInternalErrors
113 {
114 NSString* schema = @"CREATE table test (test_column INTEGER);";
115 SFSQLite* sqlTable = [[SFSQLite alloc] initWithPath:tablePath schema:schema];
116
117 NSError* error = nil;
118 XCTAssertTrue([sqlTable openWithError:&error], @"failed to open database");
119 XCTAssertNil(error, "encountered error opening database: %@", error);
120
121 // delete the database to create havoc
122 [[NSFileManager defaultManager] removeItemAtPath:tablePath error:nil];
123
124 XCTAssertNoThrow([sqlTable insertOrReplaceInto:@"test" values:@{@"test_column" : @(1)}], @"inserting into deleted table threw an exception");
125 }
126
127 @end
128
129 @interface CKKSAnalyticsLoggerTests : CloudKitKeychainSyncingTestsBase
130 @end
131
132 @implementation CKKSAnalyticsLoggerTests
133
134 - (void)testLoggingJSONGenerated
135 {
136 [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
137
138 // We expect a single record to be uploaded.
139 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID];
140
141 [self startCKKSSubsystem];
142
143 [self addGenericPassword: @"data" account: @"account-delete-me"];
144
145 OCMVerifyAllWithDelay(self.mockDatabase, 8);
146
147 NSError* error = nil;
148 NSData* json = [[CKKSAnalyticsLogger logger] getLoggingJSON:false error:&error];
149 XCTAssertNotNil(json, @"failed to generate logging json");
150 XCTAssertNil(error, @"encourntered error getting logging json: %@", error);
151
152 NSDictionary* dictionary = [NSJSONSerialization JSONObjectWithData:json options:0 error:&error];
153 XCTAssertNotNil(dictionary, @"failed to generate dictionary from json data");
154 XCTAssertNil(error, @"encountered error deserializing json: %@", error);
155 XCTAssertTrue([dictionary isKindOfClass:[NSDictionary class]], @"did not get the class we expected from json deserialization");
156
157 XCTAssertNotNil(dictionary[@"postTime"], @"Failed to get posttime");
158
159 NSArray *events = dictionary[@"events"];
160 XCTAssertNotNil(events, @"Failed to get events");
161 XCTAssert([events isKindOfClass:[NSArray class]], @"did not get the class we expected for events");
162
163
164 for (NSDictionary *event in events) {
165 XCTAssert([event isKindOfClass:[NSDictionary class]], @"did not get the class we expected for events");
166 XCTAssertNotNil(event[@"build"], @"Failed to get build in event");
167 XCTAssertNotNil(event[@"product"], @"Failed to get product in event");
168 XCTAssertNotNil(event[@"topic"], @"Failed to get topic in event");
169
170 NSString *eventtype = event[@"eventType"];
171 XCTAssertNotNil(eventtype, @"Failed to get eventType in eventtype");
172 XCTAssert([eventtype isKindOfClass:[NSString class]], @"did not get the class we expected for events");
173 if ([eventtype isEqualToString:@"ckksHealthSummary"]) {
174 XCTAssertNotNil(event[@"ckdeviceID"], @"Failed to get deviceID in event");
175 }
176 }
177 }
178
179 - (void)testSplunkDefaultTopicNameExists
180 {
181 CKKSAnalyticsLogger* logger = [CKKSAnalyticsLogger logger];
182 dispatch_sync(logger.splunkLoggingQueue, ^{
183 XCTAssertNotNil(logger.splunkTopicName);
184 });
185 }
186
187 - (void)testSplunkDefaultBagURLExists
188 {
189 CKKSAnalyticsLogger* logger = [CKKSAnalyticsLogger logger];
190 dispatch_sync(logger.splunkLoggingQueue, ^{
191 XCTAssertNotNil(logger.splunkBagURL);
192 });
193 }
194
195 // <rdar://problem/32983193> test_KeychainCKKS | CKKSTests failed: "Failed subtests: -[CloudKitKeychainSyncingTests testSplunkUploadURLExists]" [j71ap][CoreOSTigris15Z240][bats-e-27-204-1]
196 #if 0
197 - (void)testSplunkUploadURLExists
198 {
199 CKKSAnalyticsLogger* logger = [CKKSAnalyticsLogger logger];
200 dispatch_sync(logger.splunkLoggingQueue, ^{
201 logger.ignoreServerDisablingMessages = YES;
202 XCTAssertNotNil(logger.splunkUploadURL);
203 });
204 }
205 #endif
206
207 - (void)testLastSuccessfulSyncDate
208 {
209 [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
210 [self startCKKSSubsystem];
211 CKRecord* ckr = [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-F9C5-481E-98AC-5A507ACB2D85"];
212 [self.keychainZone addToZone: ckr];
213
214 // Trigger a notification (with hilariously fake data)
215 [self.keychainView notifyZoneChange:nil];
216
217 [[[self.keychainView waitForFetchAndIncomingQueueProcessing] completionHandlerDidRunCondition] wait:4 * NSEC_PER_SEC];
218
219 NSDate* syncDate = [[CKKSAnalyticsLogger logger] dateOfLastSuccessForEvent:CKKSEventProcessIncomingQueueClassC inView:self.keychainView];
220 XCTAssertNotNil(syncDate, "Failed to get a last successful sync date");
221 NSDate* nowDate = [NSDate dateWithTimeIntervalSinceNow:0];
222 NSTimeInterval timeIntervalSinceSyncDate = [nowDate timeIntervalSinceDate:syncDate];
223 XCTAssertTrue(timeIntervalSinceSyncDate >= 0.0 && timeIntervalSinceSyncDate <= 15.0, "Last sync date does not look like a reasonable one");
224 }
225
226 - (void)testExtraValuesToUploadToServer
227 {
228 [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
229 [self startCKKSSubsystem];
230 CKRecord* ckr = [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-F9C5-481E-98AC-5A507ACB2D85"];
231 [self.keychainZone addToZone: ckr];
232
233 // Trigger a notification (with hilariously fake data)
234 [self.keychainView notifyZoneChange:nil];
235
236 [[[self.keychainView waitForFetchAndIncomingQueueProcessing] completionHandlerDidRunCondition] wait:4 * NSEC_PER_SEC];
237
238 NSDictionary* extraValues = [[CKKSAnalyticsLogger logger] extraValuesToUploadToServer];
239 XCTAssertTrue([extraValues[@"inCircle"] boolValue]);
240 XCTAssertTrue([extraValues[@"keychain-TLKs"] boolValue]);
241 XCTAssertTrue([extraValues[@"keychain-inSyncA"] boolValue]);
242 XCTAssertTrue([extraValues[@"keychain-inSyncC"] boolValue]);
243 XCTAssertTrue([extraValues[@"keychain-IQNOE"] boolValue]);
244 XCTAssertTrue([extraValues[@"keychain-OQNOE"] boolValue]);
245 XCTAssertTrue([extraValues[@"keychain-inSync"] boolValue]);
246 }
247
248 - (void)testNilEventDoesNotCrashTheSystem
249 {
250 CKKSAnalyticsLogger* logger = [CKKSAnalyticsLogger logger];
251 [logger logSuccessForEventNamed:nil];
252
253 NSData* json = nil;
254 NSError* error = nil;
255 XCTAssertNoThrow(json = [logger getLoggingJSON:false error:&error]);
256 XCTAssertNotNil(json, @"Failed to get JSON after logging nil event");
257 XCTAssertNil(error, @"Got error when grabbing JSON after logging nil event: %@", error);
258 }
259
260 - (void)testRaceToCreateLoggers
261 {
262 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
263 for (NSInteger i = 0; i < 5; i++) {
264 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
265 CKKSAnalyticsLogger* logger = [CKKSAnalyticsLogger logger];
266 [logger logSuccessForEvent:(CKKSAnalyticsFailableEvent*)@"test_event" inView:self.keychainView];
267 dispatch_semaphore_signal(semaphore);
268 });
269 }
270
271 for (NSInteger i = 0; i < 5; i++) {
272 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
273 }
274 }
275
276 - (void)testSysdiagnoseDump
277 {
278 [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
279 [self startCKKSSubsystem];
280 CKRecord* ckr = [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-F9C5-481E-98AC-5A507ACB2D85"];
281 [self.keychainZone addToZone: ckr];
282
283 // Trigger a notification (with hilariously fake data)
284 [self.keychainView notifyZoneChange:nil];
285
286 [self.keychainView waitForFetchAndIncomingQueueProcessing];
287
288 NSError* error = nil;
289 NSString* sysdiagnose = [[CKKSAnalyticsLogger logger] getSysdiagnoseDumpWithError:&error];
290 XCTAssertNil(error, @"encountered an error grabbing CKKS analytics sysdiagnose: %@", error);
291 XCTAssertTrue(sysdiagnose.length > 0, @"failed to get a sysdiagnose from CKKS analytics");
292 }
293
294 @end