]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/tests/CKKSSQLTests.m
Security-59306.101.1.tar.gz
[apple/security.git] / keychain / ckks / tests / CKKSSQLTests.m
1 /*
2 * Copyright (c) 2016 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 #if OCTAGON
25
26 #import <XCTest/XCTest.h>
27 #import <Security/Security.h>
28 #import <Security/SecItemPriv.h>
29 #import "CloudKitMockXCTest.h"
30
31 #import "keychain/ckks/CKKS.h"
32 #import "keychain/ckks/CKKSKey.h"
33 #import "keychain/ckks/CKKSOutgoingQueueEntry.h"
34 #import "keychain/ckks/CKKSZoneStateEntry.h"
35 #import "keychain/ckks/CKKSDeviceStateEntry.h"
36 #import "keychain/ckks/CKKSRateLimiter.h"
37
38 #include "keychain/securityd/SecItemServer.h"
39
40 @interface CloudKitKeychainSQLTests : CloudKitMockXCTest
41 @end
42
43 @implementation CloudKitKeychainSQLTests
44
45 + (void)setUp {
46 [super setUp];
47 }
48
49 - (void)setUp {
50 SecCKKSDisable();
51 [super setUp];
52 }
53
54 - (void)tearDown {
55 [super tearDown];
56 SecCKKSResetSyncing();
57 }
58
59 - (void)addTestZoneEntries {
60 CKKSOutgoingQueueEntry* one = [[CKKSOutgoingQueueEntry alloc] initWithCKKSItem:
61 [[CKKSItem alloc] initWithUUID:[[NSUUID UUID] UUIDString]
62 parentKeyUUID:[[NSUUID UUID] UUIDString]
63 zoneID:self.testZoneID
64 encItem:[@"nonsense" dataUsingEncoding:NSUTF8StringEncoding]
65 wrappedkey:[[CKKSWrappedAESSIVKey alloc]initWithBase64: @"KFfL58XtugiYNoD859EjG0StfrYd6eakm0CQrgX7iO+DEo4kio3WbEeA1kctCU0GaeTGsRFpbdy4oo6jXhVu7cZqB0svhUPGq55aGnszUjI="]
66 generationCount:0
67 encver:0]
68 action:SecCKKSActionAdd
69 state:SecCKKSStateError
70 waitUntil:nil
71 accessGroup:@"nope"];
72
73
74 CKKSOutgoingQueueEntry* two = [[CKKSOutgoingQueueEntry alloc] initWithCKKSItem:
75 [[CKKSItem alloc] initWithUUID:[[NSUUID UUID] UUIDString]
76 parentKeyUUID:[[NSUUID UUID] UUIDString]
77 zoneID:self.testZoneID
78 encItem:[@"nonsense" dataUsingEncoding:NSUTF8StringEncoding]
79 wrappedkey:[[CKKSWrappedAESSIVKey alloc]initWithBase64: @"KFfL58XtugiYNoD859EjG0StfrYd6eakm0CQrgX7iO+DEo4kio3WbEeA1kctCU0GaeTGsRFpbdy4oo6jXhVu7cZqB0svhUPGq55aGnszUjI="]
80 generationCount:0
81 encver:0]
82 action:SecCKKSActionAdd
83 state:SecCKKSStateNew
84 waitUntil:nil
85 accessGroup:@"nope"];
86
87 CKKSOutgoingQueueEntry* three = [[CKKSOutgoingQueueEntry alloc] initWithCKKSItem:
88 [[CKKSItem alloc] initWithUUID:[[NSUUID UUID] UUIDString]
89 parentKeyUUID:[[NSUUID UUID] UUIDString]
90 zoneID:self.testZoneID
91 encItem:[@"nonsense" dataUsingEncoding:NSUTF8StringEncoding]
92 wrappedkey:[[CKKSWrappedAESSIVKey alloc]initWithBase64: @"KFfL58XtugiYNoD859EjG0StfrYd6eakm0CQrgX7iO+DEo4kio3WbEeA1kctCU0GaeTGsRFpbdy4oo6jXhVu7cZqB0svhUPGq55aGnszUjI="]
93 generationCount:0
94 encver:0]
95 action:SecCKKSActionModify
96 state:SecCKKSStateError
97 waitUntil:nil
98 accessGroup:@"nope"];
99
100 NSError* error = nil;
101 [one saveToDatabase:&error];
102 [two saveToDatabase: &error];
103 [three saveToDatabase: &error];
104 XCTAssertNil(error, "no error saving ZoneStateEntries to database");
105 }
106
107 - (void)testCKKSOutgoingQueueEntry {
108 NSString* testUUID = @"157A3171-0677-451B-9EAE-0DDC4D4315B0";
109 NSUUID* testKeyUUID = [[NSUUID alloc] init];
110
111 NSError * nserror;
112 __block CFErrorRef error = NULL;
113
114 CKKSOutgoingQueueEntry* shouldFail = [CKKSOutgoingQueueEntry fromDatabase:testUUID state:SecCKKSStateInFlight zoneID:self.testZoneID error: &nserror];
115 XCTAssertNil(shouldFail, "Can't find a nonexisting object");
116 XCTAssertNotNil(nserror, "NSError exists when things break");
117
118 __weak __typeof(self) weakSelf = self;
119 kc_with_dbt(true, &error, ^bool (SecDbConnectionRef dbconn) {
120 __strong __typeof(weakSelf) strongSelf = weakSelf;
121 XCTAssertNotNil(strongSelf, "called while self still exists");
122
123 NSString * sql = @"insert INTO outgoingqueue (UUID, parentKeyUUID, ckzone, action, state, accessgroup, gencount, encitem, wrappedkey, encver) VALUES (?,?,?,?,?,?,?,?,?,?);";
124 SecDbPrepare(dbconn, (__bridge CFStringRef) sql, &error, ^void (sqlite3_stmt *stmt) {
125 SecDbBindText(stmt, 1, [testUUID UTF8String], strlen([testUUID UTF8String]), NULL, &error);
126 SecDbBindText(stmt, 2, [[testKeyUUID UUIDString] UTF8String], strlen([[testKeyUUID UUIDString] UTF8String]), NULL, &error);
127 SecDbBindObject(stmt, 3, (__bridge CFStringRef) weakSelf.testZoneID.zoneName, &error);
128 SecDbBindText(stmt, 4, "newitem", strlen("newitem"), NULL, &error);
129 SecDbBindText(stmt, 5, "unprocessed", strlen("unprocessed"), NULL, &error);
130 SecDbBindText(stmt, 6, "com.apple.access", strlen("com.apple.access"), NULL, &error);
131 SecDbBindText(stmt, 7, "0", strlen("0"), NULL, &error);
132 SecDbBindText(stmt, 8, "bm9uc2Vuc2UK", strlen("bm9uc2Vuc2UK"), NULL, &error);
133 SecDbBindObject(stmt, 9, CFSTR("KFfL58XtugiYNoD859EjG0StfrYd6eakm0CQrgX7iO+DEo4kio3WbEeA1kctCU0GaeTGsRFpbdy4oo6jXhVu7cZqB0svhUPGq55aGnszUjI="), &error);
134 SecDbBindText(stmt, 10, "0", strlen("0"), NULL, &error);
135
136 SecDbStep(dbconn, stmt, &error, ^(bool *stop) {
137 // don't do anything, I guess?
138 });
139
140 XCTAssertNil((__bridge NSError*)error, @"no error occurred while adding row to database");
141
142 CFReleaseNull(error);
143 });
144 XCTAssertNil((__bridge NSError*)error, @"no error occurred preparing sql");
145
146 CFReleaseNull(error);
147 return true;
148 });
149
150 // Create another oqe with different values
151 CKKSItem* baseitem = [[CKKSItem alloc] initWithUUID: [[NSUUID UUID] UUIDString]
152 parentKeyUUID:[[NSUUID UUID] UUIDString]
153 zoneID:self.testZoneID
154 encItem:[@"nonsense" dataUsingEncoding:NSUTF8StringEncoding]
155 wrappedkey:[[CKKSWrappedAESSIVKey alloc]initWithBase64: @"KFfL58XtugiYNoD859EjG0StfrYd6eakm0CQrgX7iO+DEo4kio3WbEeA1kctCU0GaeTGsRFpbdy4oo6jXhVu7cZqB0svhUPGq55aGnszUjI="]
156 generationCount:0
157 encver:0];
158 CKKSOutgoingQueueEntry* other = [[CKKSOutgoingQueueEntry alloc] initWithCKKSItem:baseitem
159 action:SecCKKSActionAdd
160 state:SecCKKSStateError
161 waitUntil:[NSDate date]
162 accessGroup:@"nope"];
163 [other saveToDatabase:&nserror];
164 XCTAssertNil(nserror, "no error occurred saving to database");
165
166 CKKSOutgoingQueueEntry * oqe = [CKKSOutgoingQueueEntry fromDatabase:testUUID state:@"unprocessed" zoneID:self.testZoneID error: &nserror];
167 XCTAssertNil(nserror, "no error occurred creating from database");
168
169 XCTAssertNotNil(oqe, "load outgoing queue entry from database");
170 XCTAssertEqualObjects(oqe.state, @"unprocessed", "state matches what was in the DB");
171
172 oqe.item.parentKeyUUID = @"not a parent key either";
173 oqe.action = @"null";
174 oqe.state = @"savedtocloud";
175 oqe.accessgroup = @"com.evil.access";
176 oqe.item.generationCount = (NSInteger) 1;
177 oqe.item.base64encitem = @"bW9yZW5vbnNlbnNlCg==";
178 oqe.item.encver = 1;
179
180 XCTAssertTrue([oqe saveToDatabase: &nserror], "saving to database");
181
182 CKKSOutgoingQueueEntry * oqe2 = [CKKSOutgoingQueueEntry fromDatabase:testUUID state:@"savedtocloud" zoneID:self.testZoneID error: &nserror];
183 XCTAssertNil(nserror, "no error occurred");
184
185 XCTAssertEqualObjects(oqe2.item.parentKeyUUID, @"not a parent key either", @"parent key uuid persisted through db save and load");
186 XCTAssertEqualObjects(oqe2.item.zoneID , self.testZoneID , @"zone id persisted through db save and load");
187 XCTAssertEqualObjects(oqe2.action , @"null" , @"action persisted through db save and load");
188 XCTAssertEqualObjects(oqe2.state , @"savedtocloud" , @"state persisted through db save and load");
189 XCTAssertEqual( oqe2.waitUntil , nil , @"no date when none given");
190 XCTAssertEqualObjects(oqe2.accessgroup , @"com.evil.access" , @"accessgroup persisted through db save and load");
191 XCTAssertEqual( oqe2.item.generationCount, (NSUInteger) 1 , @"generationCount persisted through db save and load");
192 XCTAssertEqualObjects(oqe2.item.base64encitem, @"bW9yZW5vbnNlbnNlCg==" , @"encitem persisted through db save and load");
193 XCTAssertEqual( oqe2.item.encver, 1 , @"encver persisted through db save and load");
194 XCTAssertEqualObjects([oqe2.item.wrappedkey base64WrappedKey], @"KFfL58XtugiYNoD859EjG0StfrYd6eakm0CQrgX7iO+DEo4kio3WbEeA1kctCU0GaeTGsRFpbdy4oo6jXhVu7cZqB0svhUPGq55aGnszUjI=",
195 @"wrapped key persisted through db save and load");
196
197 // Test 'all' methods
198 NSArray<CKKSOutgoingQueueEntry*>* oqes = [CKKSOutgoingQueueEntry all:&nserror];
199 XCTAssertNil(nserror, "no error occurred");
200 XCTAssertNotNil(oqes, "receive oqes from database");
201 XCTAssert([oqes count] == 2, "received 2 oqes from all");
202
203 NSArray<CKKSOutgoingQueueEntry*>* oqeswhere = [CKKSOutgoingQueueEntry allWhere: @{@"state": @"savedtocloud"} error:&nserror];
204 XCTAssertNil(nserror, "no error occurred");
205 XCTAssertNotNil(oqeswhere, "receive oqes from database");
206 XCTAssert([oqeswhere count] == 1, "received 1 oqe from allWhere");
207
208 // Test row deletion
209 nserror = nil;
210 [oqe2 deleteFromDatabase:&nserror];
211 XCTAssertNil(nserror, "No NSError exists when deleting existing item");
212 oqe2 = [CKKSOutgoingQueueEntry fromDatabase:testUUID state:@"savedtocloud" zoneID:self.testZoneID error: &nserror];
213 XCTAssertNil(oqe2, "Can't find a nonexisting object");
214 XCTAssertNotNil(nserror, "NSError exists when things break");
215
216 // Test loading other
217 nserror = nil;
218 CKKSOutgoingQueueEntry* other2 = [CKKSOutgoingQueueEntry fromDatabase: other.item.uuid state:SecCKKSStateError zoneID:self.testZoneID error:&nserror];
219 XCTAssertNil(nserror, "No error loading other2 from database");
220 XCTAssertNotNil(other2, "Able to re-load other.");
221 XCTAssertEqualObjects(other, other2, "loaded object is equal to object");
222 }
223
224 -(void)testCKKSZoneStateEntrySQL {
225 CKKSZoneStateEntry* zse = [[CKKSZoneStateEntry alloc] initWithCKZone:@"sqltest"
226 zoneCreated:true
227 zoneSubscribed:true
228 changeToken:[@"nonsense" dataUsingEncoding:NSUTF8StringEncoding]
229 moreRecordsInCloudKit:YES
230 lastFetch:[NSDate date]
231 lastFixup:CKKSCurrentFixupNumber
232 encodedRateLimiter:nil];
233 zse.rateLimiter = [[CKKSRateLimiter alloc] init];
234
235 CKKSZoneStateEntry* zseClone = [[CKKSZoneStateEntry alloc] initWithCKZone:@"sqltest"
236 zoneCreated:true
237 zoneSubscribed:true
238 changeToken:[@"nonsense" dataUsingEncoding:NSUTF8StringEncoding]
239 moreRecordsInCloudKit:YES
240 lastFetch:zse.lastFetchTime
241 lastFixup:CKKSCurrentFixupNumber
242 encodedRateLimiter:zse.encodedRateLimiter];
243
244 CKKSZoneStateEntry* zseDifferent = [[CKKSZoneStateEntry alloc] initWithCKZone:@"sqltest"
245 zoneCreated:true
246 zoneSubscribed:true
247 changeToken:[@"allnonsense" dataUsingEncoding:NSUTF8StringEncoding]
248 moreRecordsInCloudKit:NO
249 lastFetch:zse.lastFetchTime
250 lastFixup:CKKSCurrentFixupNumber
251 encodedRateLimiter:zse.encodedRateLimiter];
252 XCTAssertEqualObjects(zse, zseClone, "CKKSZoneStateEntry isEqual of equal objects seems sane");
253 XCTAssertNotEqualObjects(zse, zseDifferent, "CKKSZoneStateEntry isEqual of nonequal objects seems sane");
254
255 NSError* error = nil;
256 CKKSZoneStateEntry* loaded = [CKKSZoneStateEntry tryFromDatabase: @"sqltest" error:&error];
257 XCTAssertNil(error, "No error trying to load nonexistent record");
258 XCTAssertNil(loaded, "No record saved in database");
259
260 [zse saveToDatabase: &error];
261 XCTAssertNil(error, "no error saving CKKSZoneStateEntry to database");
262
263 loaded = [CKKSZoneStateEntry tryFromDatabase: @"sqltest" error:&error];
264 XCTAssertNil(error, "No error trying to load saved record");
265 XCTAssertNotNil(loaded, "CKKSZoneStateEntry came back out of database");
266
267 XCTAssertEqualObjects(zse.ckzone, loaded.ckzone, "ckzone persisted through db save and load");
268 XCTAssertEqual (zse.ckzonecreated, loaded.ckzonecreated, "ckzonecreated persisted through db save and load");
269 XCTAssertEqual (zse.ckzonesubscribed, loaded.ckzonesubscribed, "ckzonesubscribed persisted through db save and load");
270 XCTAssertEqualObjects(zse.encodedChangeToken, loaded.encodedChangeToken, "encodedChangeToken persisted through db save and load");
271
272 secnotice("ckkstests", "zse.lastFetchTime: %@", zse.lastFetchTime);
273 secnotice("ckkstests", "loaded.lastFetchTime: %@", loaded.lastFetchTime);
274
275 secnotice("ckkstests", "equal?: %d", [zse.lastFetchTime isEqualToDate:loaded.lastFetchTime]);
276 secnotice("ckkstests", "equal to seconds?: %d", [[NSCalendar currentCalendar] isDate:zse.lastFetchTime equalToDate: loaded.lastFetchTime toUnitGranularity:NSCalendarUnitSecond]);
277
278 // We only compare to the minute level, as that's enough to test the save+load.
279 XCTAssert([[NSCalendar currentCalendar] isDate:zse.lastFetchTime equalToDate: loaded.lastFetchTime toUnitGranularity:NSCalendarUnitMinute],
280 "lastFetchTime persisted through db save and load");
281 }
282
283 -(void)testRoundtripCKKSDeviceStateEntry {
284 // Very simple test: can these objects roundtrip through the db?
285 NSString* testUUID = @"157A3171-0677-451B-9EAE-0DDC4D4315B0";
286 CKKSDeviceStateEntry* cdse = [[CKKSDeviceStateEntry alloc] initForDevice:testUUID
287 osVersion:@"faux-version"
288 lastUnlockTime:nil
289 octagonPeerID:@"peerID"
290 octagonStatus:nil
291 circlePeerID:@"asdf"
292 circleStatus:kSOSCCInCircle
293 keyState:SecCKKSZoneKeyStateReady
294 currentTLKUUID:@"tlk"
295 currentClassAUUID:@"classA"
296 currentClassCUUID:@"classC"
297 zoneID:self.testZoneID
298 encodedCKRecord:nil];
299 XCTAssertNotNil(cdse, "Constructor works");
300 NSError* saveError = nil;
301 [cdse saveToDatabase:&saveError];
302 XCTAssertNil(saveError, "No error saving cdse to database");
303
304 NSError* loadError = nil;
305 CKKSDeviceStateEntry* loadedCDSE = [CKKSDeviceStateEntry fromDatabase:testUUID zoneID:self.testZoneID error:&loadError];
306 XCTAssertNil(loadError, "No error loading CDSE");
307 XCTAssertNotNil(loadedCDSE, "Received a CDSE back");
308
309 XCTAssertEqualObjects(cdse, loadedCDSE, "Roundtripping CKKSDeviceStateEntry ends up with equivalent objects");
310 }
311
312 // disabled, as CKKS syncing is disabled in this class.
313 // To re-enable, need to add flags CKKS syncing to perform queue actions but not automatically start queue processing operations
314 -(void)disabledtestItemAddCreatesCKKSOutgoingQueueEntry {
315 CFMutableDictionaryRef attrs;
316 CFDataRef data;
317
318 NSError* error;
319
320 NSArray* oqes = [CKKSOutgoingQueueEntry all: &error];
321 XCTAssertEqual([oqes count], 0ul, @"Nothing in outgoing queue");
322 XCTAssertNil(error, @"No error loading queue");
323
324 attrs = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
325 CFDictionarySetValue( attrs, kSecClass, kSecClassGenericPassword );
326 CFDictionarySetValue( attrs, kSecAttrAccessible, kSecAttrAccessibleAlwaysPrivate );
327 CFDictionarySetValue( attrs, kSecAttrLabel, CFSTR( "TestLabel" ) );
328 CFDictionarySetValue( attrs, kSecAttrDescription, CFSTR( "TestDescription" ) );
329 CFDictionarySetValue( attrs, kSecAttrAccount, CFSTR( "TestAccount" ) );
330 CFDictionarySetValue( attrs, kSecAttrService, CFSTR( "TestService" ) );
331 CFDictionarySetValue( attrs, kSecAttrAccessGroup, CFSTR("com.apple.lakitu"));
332 data = CFDataCreate( NULL, (const uint8_t *) "important data", strlen("important data"));
333 CFDictionarySetValue( attrs, kSecValueData, data );
334 CFRelease( data );
335
336 XCTAssertEqual(SecItemAdd(attrs, NULL), errSecSuccess, @"Adding item works flawlessly");
337
338 oqes = [CKKSOutgoingQueueEntry all: &error];
339 XCTAssertEqual([oqes count], 1ul, @"Single entry in outgoing queue after adding item");
340 XCTAssertNil(error, @"No error loading queue");
341
342 CFDictionarySetValue( attrs, kSecAttrLabel, CFSTR( "TestLabel2" ) );
343 CFDictionarySetValue( attrs, kSecAttrAccount, CFSTR( "TestAccount2" ) );
344 CFDictionarySetValue( attrs, kSecAttrService, CFSTR( "TestService2" ) );
345 XCTAssertEqual(SecItemAdd(attrs, NULL), errSecSuccess);
346 CFRelease( attrs );
347
348 oqes = [CKKSOutgoingQueueEntry all: &error];
349 XCTAssertEqual([oqes count], 2ul, @"Two entries in outgoing queue after adding item");
350 XCTAssertNil(error, @"No error loading queue");
351 }
352
353 - (void)testCKKSKey {
354 CKKSKey* key = nil;
355 NSString* testUUID = @"157A3171-0677-451B-9EAE-0DDC4D4315B0";
356 NSString* testParentUUID = @"f5e7f20f-0885-48f9-b75d-9f0cfd2171b6";
357
358 NSData* testCKRecord = [@"nonsense" dataUsingEncoding:NSUTF8StringEncoding];
359
360 CKKSWrappedAESSIVKey* wrappedkey = [[CKKSWrappedAESSIVKey alloc] initWithBase64:@"KFfL58XtugiYNoD859EjG0StfrYd6eakm0CQrgX7iO+DEo4kio3WbEeA1kctCU0GaeTGsRFpbdy4oo6jXhVu7cZqB0svhUPGq55aGnszUjI="];
361
362 NSError* error = nil;
363
364 key = [CKKSKey fromDatabase:testUUID zoneID:self.testZoneID error:&error];
365 XCTAssertNil(key, "key does not exist yet");
366 XCTAssertNotNil(error, "error exists when things go wrong");
367 error = nil;
368
369 key = [[CKKSKey alloc] initWithWrappedAESKey: wrappedkey
370 uuid: testUUID
371 parentKeyUUID:testParentUUID
372 keyclass:SecCKKSKeyClassA
373 state: SecCKKSProcessedStateLocal
374 zoneID:self.testZoneID
375 encodedCKRecord:testCKRecord
376 currentkey:true];
377 XCTAssertNotNil(key, "could create key");
378
379 [key saveToDatabase: &error];
380 XCTAssertNil(error, "could save key to database");
381 error = nil;
382
383 CKKSKey* key2 = [CKKSKey fromDatabase:testUUID zoneID:self.testZoneID error:&error];
384 XCTAssertNil(error, "no error exists when loading key");
385 XCTAssertNotNil(key2, "key was fetched properly");
386
387 XCTAssertEqualObjects(key.uuid, key2.uuid, "key uuids match");
388 XCTAssertEqualObjects(key.parentKeyUUID, key2.parentKeyUUID, "parent key uuids match");
389 XCTAssertEqualObjects(key.state, key2.state, "key states match");
390 XCTAssertEqualObjects(key.encodedCKRecord, key2.encodedCKRecord, "encodedCKRecord match");
391 XCTAssertEqualObjects(key.wrappedkey, key2.wrappedkey, "wrapped keys match");
392 XCTAssertEqual(key.currentkey, key2.currentkey, "currentkey match");
393 }
394
395 - (void)testWhere {
396 NSError* error = nil;
397
398 NSData* testCKRecord = [@"nonsense" dataUsingEncoding:NSUTF8StringEncoding];
399
400 CKKSKey* tlk = [[CKKSKey alloc] initSelfWrappedWithAESKey: [[CKKSAESSIVKey alloc] initWithBase64: @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="]
401 uuid:@"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"
402 keyclass:SecCKKSKeyClassTLK
403 state: SecCKKSProcessedStateLocal
404 zoneID:self.testZoneID
405 encodedCKRecord: testCKRecord
406 currentkey: true];
407 XCTAssertTrue([tlk saveToDatabase: &error], "TLK saved to database");
408 XCTAssertNil(error, "no error saving TLK to database");
409
410 CKKSKey* wrappedKey = [[CKKSKey alloc] initWrappedBy: tlk
411 AESKey:[CKKSAESSIVKey randomKey:&error]
412 uuid:@"157A3171-0677-451B-9EAE-0DDC4D4315B0"
413 keyclass:SecCKKSKeyClassC
414 state: SecCKKSProcessedStateLocal
415 zoneID:self.testZoneID
416 encodedCKRecord:testCKRecord
417 currentkey:true];
418 XCTAssertTrue([wrappedKey saveToDatabase: &error], "key saved to database");
419 XCTAssertNil(error, "no error saving key to database");
420
421 NSString* secondUUID = @"8b2aeb7f-0000-0000-0000-70d5c728ebf7";
422 CKKSKey* secondtlk = [[CKKSKey alloc] initSelfWrappedWithAESKey:[[CKKSAESSIVKey alloc] initWithBase64: @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="]
423 uuid:secondUUID
424 keyclass:SecCKKSKeyClassTLK
425 state:SecCKKSProcessedStateLocal
426 zoneID:self.testZoneID
427 encodedCKRecord:testCKRecord
428 currentkey:true];
429 XCTAssertTrue([secondtlk saveToDatabase: &error], "Second TLK saved to database");
430 XCTAssertNil(error, "no error saving TLK to database");
431
432 NSArray<CKKSKey*>* tlks = [CKKSKey allWhere: @{@"UUID": @"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"} error: &error];
433 XCTAssertNotNil(tlks, "Returned some array from allWhere");
434 XCTAssertNil(error, "no error back from allWhere");
435 XCTAssertEqual([tlks count], 1ul, "Received one row (and expected one row)");
436
437 NSArray<CKKSKey*>* selfWrapped = [CKKSKey allWhere: @{@"parentKeyUUID": [CKKSSQLWhereColumn op:CKKSSQLWhereComparatorEquals column:CKKSSQLWhereColumnNameUUID]} error: &error];
438 XCTAssertNotNil(selfWrapped, "Returned some array from allWhere");
439 XCTAssertNil(error, "no error back from allWhere");
440 XCTAssertEqual([selfWrapped count], 2ul, "Should have recievied two rows");
441
442 // Try using CKKSSQLWhereColumn alongside normal binds
443 NSArray<CKKSKey*>* selfWrapped2 = [CKKSKey allWhere: @{@"parentKeyUUID": [CKKSSQLWhereColumn op:CKKSSQLWhereComparatorEquals column:CKKSSQLWhereColumnNameUUID],
444 @"uuid": @"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"}
445 error: &error];
446 XCTAssertNotNil(selfWrapped2, "Returned some array from allWhere");
447 XCTAssertNil(error, "no error back from allWhere");
448 XCTAssertEqual([selfWrapped2 count], 1ul, "Received one row (and expected one row)");
449 XCTAssertEqualObjects([selfWrapped2[0] uuid], @"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7", "Should received first TLK UUID");
450
451 NSArray<CKKSKey*>* selfWrapped3 = [CKKSKey allWhere: @{@"parentKeyUUID": [CKKSSQLWhereColumn op:CKKSSQLWhereComparatorEquals column:CKKSSQLWhereColumnNameUUID],
452 @"uuid": [CKKSSQLWhereValue op:CKKSSQLWhereComparatorNotEquals value:@"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"]}
453 error: &error];
454 XCTAssertNotNil(selfWrapped3, "Returned some array from allWhere");
455 XCTAssertNil(error, "no error back from allWhere");
456 XCTAssertEqual([selfWrapped3 count], 1ul, "Should have received one rows");
457 XCTAssertEqualObjects([selfWrapped3[0] uuid], secondUUID, "Should received second TLK UUID");
458 }
459
460 - (void)testGroupBy {
461 [self addTestZoneEntries];
462 NSError* error = nil;
463
464 __block NSMutableDictionary<NSString*, NSString*>* results = [[NSMutableDictionary alloc] init];
465 NSDictionary* expectedResults = nil;
466
467 [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
468 where: nil
469 columns: @[@"action", @"count(rowid)"]
470 groupBy: @[@"action"]
471 orderBy:nil
472 limit: -1
473 processRow: ^(NSDictionary<NSString*, CKKSSQLResult*>* row) {
474 results[row[@"action"].asString] = row[@"count(rowid)"].asString;
475 }
476 error: &error];
477
478 XCTAssertNil(error, "no error doing group by query");
479 expectedResults = @{
480 SecCKKSActionAdd: @"2",
481 SecCKKSActionModify: @"1"
482 };
483 XCTAssertEqualObjects(results, expectedResults, "Recieved correct group by result");
484
485 // Now test with a where clause:
486 results = [[NSMutableDictionary alloc] init];
487 [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
488 where: @{@"state": SecCKKSStateError}
489 columns: @[@"action", @"count(rowid)"]
490 groupBy: @[@"action"]
491 orderBy:nil
492 limit: -1
493 processRow: ^(NSDictionary<NSString*, CKKSSQLResult*>* row) {
494 results[row[@"action"].asString] = row[@"count(rowid)"].asString;
495 }
496 error: &error];
497
498 XCTAssertNil(error, "no error doing where+group by query");
499 expectedResults = @{
500 SecCKKSActionAdd: @"1",
501 SecCKKSActionModify: @"1"
502 };
503 XCTAssertEqualObjects(results, expectedResults, "Recieved correct where+group by result");
504 }
505
506 - (void)testOrderBy {
507 [self addTestZoneEntries];
508 NSError* error = nil;
509
510 __block NSMutableArray<NSDictionary<NSString*, CKKSSQLResult*>*>* rows = [[NSMutableArray alloc] init];
511
512 [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
513 where: nil
514 columns: @[@"action", @"uuid"]
515 groupBy:nil
516 orderBy:@[@"uuid"]
517 limit:-1
518 processRow:^(NSDictionary<NSString*, CKKSSQLResult*>* row) {
519 [rows addObject:row];
520 }
521 error: &error];
522
523 XCTAssertNil(error, "no error doing order by query");
524 XCTAssertEqual(rows.count, 3u, "got three items");
525
526 XCTAssertEqual([rows[0][@"uuid"].asString compare: rows[1][@"uuid"].asString], NSOrderedAscending, "first order is fine");
527 XCTAssertEqual([rows[1][@"uuid"].asString compare: rows[2][@"uuid"].asString], NSOrderedAscending, "second order is fine");
528
529 // Check that order-by + limit works to page
530 __block NSString* lastUUID = nil;
531 __block NSString* uuid = nil;
532 uint64_t count = 0;
533
534 while(count == 0 || uuid != nil) {
535 uuid = nil;
536 [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
537 where: lastUUID ? @{@"UUID": [CKKSSQLWhereValue op:CKKSSQLWhereComparatorGreaterThan value:lastUUID]} : nil
538 columns: @[@"action", @"UUID"]
539 groupBy:nil
540 orderBy:@[@"uuid"]
541 limit:1
542 processRow: ^(NSDictionary<NSString*, CKKSSQLResult*>* row) {
543 XCTAssertNil(uuid, "Only one row returned");
544 uuid = row[@"UUID"].asString;
545 }
546 error: &error];
547 XCTAssertNil(error, "No error doing SQL");
548 if(uuid && lastUUID) {
549 XCTAssertEqual([lastUUID compare:uuid], NSOrderedAscending, "uuids returning in right order");
550 }
551 lastUUID = uuid;
552 count += 1;
553 }
554 XCTAssertEqual(count, 4u, "Received 3 objects (and 1 nil)");
555 }
556
557 - (void)testLimit {
558 [self addTestZoneEntries];
559 NSError* error = nil;
560
561 __block NSMutableDictionary<NSString*, NSString*>* results = [[NSMutableDictionary alloc] init];
562
563 [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
564 where: nil
565 columns: @[@"uuid", @"action"]
566 groupBy: nil
567 orderBy:nil
568 limit: -1
569 processRow: ^(NSDictionary<NSString*, CKKSSQLResult*>* row) {
570 results[row[@"uuid"].asString] = row[@"action"].asString;
571 }
572 error: &error];
573
574 XCTAssertNil(error, "no error doing vanilla query");
575 XCTAssertEqual(results.count, 3u, "Received three elements in normal query");
576 results = [[NSMutableDictionary alloc] init];
577
578 [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
579 where: nil
580 columns: @[@"uuid", @"action"]
581 groupBy: nil
582 orderBy:nil
583 limit: 1
584 processRow: ^(NSDictionary<NSString*, CKKSSQLResult*>* row) {
585 results[row[@"uuid"].asString] = row[@"action"].asString;
586 }
587 error: &error];
588
589 XCTAssertNil(error, "no error doing limit query");
590 XCTAssertEqual(results.count, 1u, "Received one element in limited query");
591 results = [[NSMutableDictionary alloc] init];
592
593 // Now test with a where clause:
594 results = [[NSMutableDictionary alloc] init];
595 [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
596 where: @{@"state": SecCKKSStateError}
597 columns: @[@"uuid", @"action"]
598 groupBy: nil
599 orderBy:nil
600 limit: 3
601 processRow: ^(NSDictionary<NSString*, CKKSSQLResult*>* row) {
602 results[row[@"uuid"].asString] = row[@"action"].asString;
603 }
604 error: &error];
605
606 XCTAssertNil(error, "no error doing limit+where query");
607 XCTAssertEqual(results.count, 2u, "Received two elements in where+limited query");
608 results = [[NSMutableDictionary alloc] init];
609
610 results = [[NSMutableDictionary alloc] init];
611 [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
612 where: @{@"state": SecCKKSStateError}
613 columns: @[@"uuid", @"action"]
614 groupBy: nil
615 orderBy:nil
616 limit: 1
617 processRow: ^(NSDictionary<NSString*, CKKSSQLResult*>* row) {
618 results[row[@"uuid"].asString] = row[@"action"].asString;
619 }
620 error: &error];
621
622 XCTAssertNil(error, "no error doing limit+where query");
623 XCTAssertEqual(results.count, 1u, "Received one element in where+limited query");
624 results = [[NSMutableDictionary alloc] init];
625 }
626
627 - (void)testQuoting {
628 XCTAssertEqualObjects([CKKSSQLDatabaseObject quotedString:@"hej"], @"hej", "no quote");
629 XCTAssertEqualObjects([CKKSSQLDatabaseObject quotedString:@"hej'"], @"hej''", "single quote");
630 XCTAssertEqualObjects([CKKSSQLDatabaseObject quotedString:@"'hej'"], @"''hej''", "two single quote");
631 XCTAssertEqualObjects([CKKSSQLDatabaseObject quotedString:@"hej\""], @"hej\"", "double quote");
632 XCTAssertEqualObjects([CKKSSQLDatabaseObject quotedString:@"\"hej\""], @"\"hej\"", "double quote");
633 XCTAssertEqualObjects([CKKSSQLDatabaseObject quotedString:@"'\"hej\""], @"''\"hej\"", "double quote");
634 }
635
636
637 @end
638
639 #endif