]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/CKKSItem.m
Security-58286.41.2.tar.gz
[apple/security.git] / keychain / ckks / CKKSItem.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 #include <AssertMacros.h>
27
28 #import <Foundation/Foundation.h>
29 #import "CKKSItem.h"
30 #import "CKKSSIV.h"
31
32 #include <utilities/SecDb.h>
33 #include <securityd/SecDbItem.h>
34 #include <securityd/SecItemSchema.h>
35
36 #include <sys/sysctl.h>
37 #import <CloudKit/CloudKit.h>
38 #import <CloudKit/CloudKit_Private.h>
39
40 @implementation CKKSItem
41
42 - (instancetype) initWithCKRecord: (CKRecord*) record {
43 if(self = [super initWithCKRecord: record]) {
44 }
45 return self;
46 }
47
48 - (instancetype) initCopyingCKKSItem: (CKKSItem*) item {
49 if(self = [super initWithCKRecordType: item.ckRecordType encodedCKRecord:item.encodedCKRecord zoneID:item.zoneID]) {
50 _uuid = item.uuid;
51 _parentKeyUUID = item.parentKeyUUID;
52 _generationCount = item.generationCount;
53 _encitem = item.encitem;
54 _wrappedkey = item.wrappedkey;
55 _encver = item.encver;
56
57 _plaintextPCSServiceIdentifier = item.plaintextPCSServiceIdentifier;
58 _plaintextPCSPublicKey = item.plaintextPCSPublicKey;
59 _plaintextPCSPublicIdentity = item.plaintextPCSPublicIdentity;
60 }
61 return self;
62 }
63
64 - (instancetype) initWithUUID: (NSString*) uuid
65 parentKeyUUID: (NSString*) parentKeyUUID
66 zoneID: (CKRecordZoneID*) zoneID
67 {
68 return [self initWithUUID:uuid
69 parentKeyUUID:parentKeyUUID
70 zoneID:zoneID
71 encodedCKRecord:nil
72 encItem:nil
73 wrappedkey:nil
74 generationCount:0
75 encver:CKKSItemEncryptionVersionNone];
76 }
77
78 - (instancetype) initWithUUID: (NSString*) uuid
79 parentKeyUUID: (NSString*) parentKeyUUID
80 zoneID: (CKRecordZoneID*) zoneID
81 encItem: (NSData*) encitem
82 wrappedkey: (CKKSWrappedAESSIVKey*) wrappedkey
83 generationCount: (NSUInteger) genCount
84 encver: (NSUInteger) encver
85 {
86 return [self initWithUUID:uuid
87 parentKeyUUID:parentKeyUUID
88 zoneID:zoneID
89 encodedCKRecord:nil
90 encItem:encitem
91 wrappedkey:wrappedkey
92 generationCount:genCount
93 encver:encver];
94 }
95
96 - (instancetype) initWithUUID: (NSString*) uuid
97 parentKeyUUID: (NSString*) parentKeyUUID
98 zoneID: (CKRecordZoneID*)zoneID
99 encodedCKRecord: (NSData*) encodedrecord
100 encItem: (NSData*) encitem
101 wrappedkey: (CKKSWrappedAESSIVKey*) wrappedkey
102 generationCount: (NSUInteger) genCount
103 encver: (NSUInteger) encver
104 {
105 return [self initWithUUID:uuid
106 parentKeyUUID:parentKeyUUID
107 zoneID:zoneID
108 encodedCKRecord:encodedrecord
109 encItem:encitem
110 wrappedkey:wrappedkey
111 generationCount:genCount
112 encver:encver
113 plaintextPCSServiceIdentifier:nil
114 plaintextPCSPublicKey:nil
115 plaintextPCSPublicIdentity:nil];
116 }
117
118 - (instancetype) initWithUUID: (NSString*) uuid
119 parentKeyUUID: (NSString*) parentKeyUUID
120 zoneID: (CKRecordZoneID*)zoneID
121 encodedCKRecord: (NSData*) encodedrecord
122 encItem: (NSData*) encitem
123 wrappedkey: (CKKSWrappedAESSIVKey*) wrappedkey
124 generationCount: (NSUInteger) genCount
125 encver: (NSUInteger) encver
126 plaintextPCSServiceIdentifier: (NSNumber*) pcsServiceIdentifier
127 plaintextPCSPublicKey: (NSData*) pcsPublicKey
128 plaintextPCSPublicIdentity: (NSData*) pcsPublicIdentity
129 {
130 if(self = [super initWithCKRecordType: SecCKRecordItemType encodedCKRecord:encodedrecord zoneID:zoneID]) {
131 _uuid = uuid;
132 _parentKeyUUID = parentKeyUUID;
133 _generationCount = genCount;
134 self.encitem = encitem;
135 _wrappedkey = wrappedkey;
136 _encver = encver;
137
138 _plaintextPCSServiceIdentifier = pcsServiceIdentifier;
139 _plaintextPCSPublicKey = pcsPublicKey;
140 _plaintextPCSPublicIdentity = pcsPublicIdentity;
141 }
142
143 return self;
144 }
145
146 - (BOOL)isEqual: (id) object {
147 if(![object isKindOfClass:[CKKSItem class]]) {
148 return NO;
149 }
150
151 CKKSItem* obj = (CKKSItem*) object;
152
153 return ([self.uuid isEqual: obj.uuid] &&
154 [self.parentKeyUUID isEqual: obj.parentKeyUUID] &&
155 [self.zoneID isEqual: obj.zoneID] &&
156 ((self.encitem == nil && obj.encitem == nil) || ([self.encitem isEqual: obj.encitem])) &&
157 [self.wrappedkey isEqual: obj.wrappedkey] &&
158 self.generationCount == obj.generationCount &&
159 self.encver == obj.encver &&
160 true) ? YES : NO;
161 }
162
163 #pragma mark - CKRecord handling
164
165 - (NSString*) CKRecordName {
166 return self.uuid;
167 }
168
169 - (void) setFromCKRecord: (CKRecord*) record {
170 if(![record.recordType isEqual: SecCKRecordItemType]) {
171 @throw [NSException
172 exceptionWithName:@"WrongCKRecordTypeException"
173 reason:[NSString stringWithFormat: @"CKRecordType (%@) was not %@", record.recordType, SecCKRecordItemType]
174 userInfo:nil];
175 }
176
177 [self setStoredCKRecord:record];
178
179 _uuid = [[record recordID] recordName];
180 self.parentKeyUUID = [record[SecCKRecordParentKeyRefKey] recordID].recordName;
181 self.encitem = record[SecCKRecordDataKey];
182 self.wrappedkey = [[CKKSWrappedAESSIVKey alloc] initWithBase64: record[SecCKRecordWrappedKeyKey]];
183 self.generationCount = [record[SecCKRecordGenerationCountKey] unsignedIntegerValue];
184 self.encver = [record[SecCKRecordEncryptionVersionKey] unsignedIntegerValue];
185
186 self.plaintextPCSServiceIdentifier = record[SecCKRecordPCSServiceIdentifier];
187 self.plaintextPCSPublicKey = record[SecCKRecordPCSPublicKey];
188 self.plaintextPCSPublicIdentity = record[SecCKRecordPCSPublicIdentity];
189 }
190
191 + (void)setOSVersionInRecord: (CKRecord*) record {
192 #ifdef PLATFORM
193 // Use complicated macro magic to get the string value passed in as preprocessor define PLATFORM.
194 #define PLATFORM_VALUE(f) #f
195 #define PLATFORM_OBJCSTR(f) @PLATFORM_VALUE(f)
196 NSString* platform = (PLATFORM_OBJCSTR(PLATFORM));
197 #undef PLATFORM_OBJCSTR
198 #undef PLATFORM_VALUE
199 #else
200 NSString* platform = "unknown";
201 #warning No PLATFORM defined; why?
202 #endif
203
204 NSString* osversion = nil;
205
206 // If we can get the build information from sysctl, use it.
207 char release[256];
208 size_t releasesize = sizeof(release);
209 bool haveSysctlInfo = true;
210 haveSysctlInfo &= (0 == sysctlbyname("kern.osrelease", release, &releasesize, NULL, 0));
211
212 char version[256];
213 size_t versionsize = sizeof(version);
214 haveSysctlInfo &= (0 == sysctlbyname("kern.osversion", version, &versionsize, NULL, 0));
215
216 if(haveSysctlInfo) {
217 // Null-terminate for extra safety
218 release[sizeof(release)-1] = '\0';
219 version[sizeof(version)-1] = '\0';
220 osversion = [NSString stringWithFormat:@"%s (%s)", release, version];
221 }
222
223 if(!osversion) {
224 // Otherwise, use the not-really-supported fallback.
225 osversion = [[NSProcessInfo processInfo] operatingSystemVersionString];
226
227 // subtly improve osversion (but it's okay if that does nothing)
228 osversion = [osversion stringByReplacingOccurrencesOfString:@"Version" withString:@""];
229 }
230
231 record[SecCKRecordHostOSVersionKey] = [NSString stringWithFormat:@"%@ %@", platform, osversion];
232 }
233
234 - (CKRecord*) updateCKRecord: (CKRecord*) record zoneID: (CKRecordZoneID*) zoneID {
235 if(![record.recordType isEqual: SecCKRecordItemType]) {
236 @throw [NSException
237 exceptionWithName:@"WrongCKRecordTypeException"
238 reason:[NSString stringWithFormat: @"CKRecordType (%@) was not %@", record.recordType, SecCKRecordItemType]
239 userInfo:nil];
240 }
241
242 // Items must have a wrapping key.
243 record[SecCKRecordParentKeyRefKey] = [[CKReference alloc] initWithRecordID: [[CKRecordID alloc] initWithRecordName: self.parentKeyUUID zoneID: zoneID] action: CKReferenceActionValidate];
244
245 [CKKSItem setOSVersionInRecord: record];
246
247 record[SecCKRecordDataKey] = self.encitem;
248 record[SecCKRecordWrappedKeyKey] = [self.wrappedkey base64WrappedKey];
249 record[SecCKRecordGenerationCountKey] = [NSNumber numberWithInteger:self.generationCount];
250 // TODO: if the record's generation count is already higher than ours, that's a problem.
251 record[SecCKRecordEncryptionVersionKey] = [NSNumber numberWithInteger:self.encver];
252
253 // Add unencrypted fields
254 record[SecCKRecordPCSServiceIdentifier] = self.plaintextPCSServiceIdentifier;
255 record[SecCKRecordPCSPublicKey] = self.plaintextPCSPublicKey;
256 record[SecCKRecordPCSPublicIdentity] = self.plaintextPCSPublicIdentity;
257
258 return record;
259 }
260
261
262 - (bool) matchesCKRecord: (CKRecord*) record {
263 if(![record.recordType isEqual: SecCKRecordItemType]) {
264 return false;
265 }
266
267 // We only really care about the data, the wrapped key, the generation count, and the parent key.
268 // Note that since all of those things are included as authenticated data into the AES-SIV ciphertext, we could just
269 // compare that. However, check 'em all.
270 if(![record.recordID.recordName isEqualToString: self.uuid]) {
271 secinfo("ckksitem", "UUID does not match");
272 return false;
273 }
274
275 if(![[record[SecCKRecordParentKeyRefKey] recordID].recordName isEqualToString: self.parentKeyUUID]) {
276 secinfo("ckksitem", "wrapping key reference does not match");
277 return false;
278 }
279
280 if(![record[SecCKRecordGenerationCountKey] isEqual: [NSNumber numberWithInteger:self.generationCount]]) {
281 secinfo("ckksitem", "SecCKRecordGenerationCountKey does not match");
282 return false;
283 }
284
285 if(![record[SecCKRecordWrappedKeyKey] isEqual: [self.wrappedkey base64WrappedKey]]) {
286 secinfo("ckksitem", "SecCKRecordWrappedKeyKey does not match");
287 return false;
288 }
289
290 if(![record[SecCKRecordDataKey] isEqual: self.encitem]) {
291 secinfo("ckksitem", "SecCKRecordDataKey does not match");
292 return false;
293 }
294
295 // Compare plaintext records, too
296 // Why is obj-c nullable equality so difficult?
297 if(!((record[SecCKRecordPCSServiceIdentifier] == nil && self.plaintextPCSServiceIdentifier == nil) ||
298 [record[SecCKRecordPCSServiceIdentifier] isEqual: self.plaintextPCSServiceIdentifier])) {
299 secinfo("ckksitem", "SecCKRecordPCSServiceIdentifier does not match");
300 return false;
301 }
302
303 if(!((record[SecCKRecordPCSPublicKey] == nil && self.plaintextPCSPublicKey == nil) ||
304 [record[SecCKRecordPCSPublicKey] isEqual: self.plaintextPCSPublicKey])) {
305 secinfo("ckksitem", "SecCKRecordPCSPublicKey does not match");
306 return false;
307 }
308
309 if(!((record[SecCKRecordPCSPublicIdentity] == nil && self.plaintextPCSPublicIdentity == nil) ||
310 [record[SecCKRecordPCSPublicIdentity] isEqual: self.plaintextPCSPublicIdentity])) {
311 secinfo("ckksitem", "SecCKRecordPCSPublicIdentity does not match");
312 return false;
313 }
314
315 return true;
316 }
317
318 // Generates the list of 'authenticated data' to go along with this item, and optionally adds in unknown, future fields received from CloudKit
319 - (NSDictionary<NSString*, NSData*>*)makeAuthenticatedDataDictionaryUpdatingCKKSItem:(CKKSItem*) olditem encryptionVersion:(SecCKKSItemEncryptionVersion)encversion {
320 switch(encversion) {
321 case CKKSItemEncryptionVersion1:
322 return [self makeAuthenticatedDataDictionaryUpdatingCKKSItemEncVer1];
323 case CKKSItemEncryptionVersion2:
324 return [self makeAuthenticatedDataDictionaryUpdatingCKKSItemEncVer2:olditem];
325 default:
326 @throw [NSException
327 exceptionWithName:@"WrongEncryptionVersionException"
328 reason:[NSString stringWithFormat: @"%d is not a known encryption version", (int)encversion]
329 userInfo:nil];
330 }
331 }
332
333 - (NSDictionary<NSString*, NSData*>*)makeAuthenticatedDataDictionaryUpdatingCKKSItemEncVer1 {
334 NSMutableDictionary<NSString*, NSData*>* authenticatedData = [[NSMutableDictionary alloc] init];
335
336 authenticatedData[@"UUID"] = [self.uuid dataUsingEncoding: NSUTF8StringEncoding];
337 authenticatedData[SecCKRecordWrappedKeyKey] = [self.parentKeyUUID dataUsingEncoding: NSUTF8StringEncoding];
338
339 uint64_t genCount64 = OSSwapHostToLittleConstInt64(self.generationCount);
340 authenticatedData[SecCKRecordGenerationCountKey] = [NSData dataWithBytes:&genCount64 length:sizeof(genCount64)];
341
342 uint64_t encver = OSSwapHostToLittleConstInt64((uint64_t)self.encver);
343 authenticatedData[SecCKRecordEncryptionVersionKey] = [NSData dataWithBytes:&encver length:sizeof(encver)];
344
345 // In v1, don't authenticate the plaintext PCS fields
346 authenticatedData[SecCKRecordPCSServiceIdentifier] = nil;
347 authenticatedData[SecCKRecordPCSPublicKey] = nil;
348 authenticatedData[SecCKRecordPCSPublicIdentity] = nil;
349
350 return authenticatedData;
351 }
352
353 - (NSDictionary<NSString*, NSData*>*)makeAuthenticatedDataDictionaryUpdatingCKKSItemEncVer2:(CKKSItem*) olditem {
354 NSMutableDictionary<NSString*, NSData*>* authenticatedData = [[NSMutableDictionary alloc] init];
355
356 authenticatedData[@"UUID"] = [self.uuid dataUsingEncoding: NSUTF8StringEncoding];
357 authenticatedData[SecCKRecordWrappedKeyKey] = [self.parentKeyUUID dataUsingEncoding: NSUTF8StringEncoding];
358
359 uint64_t genCount64 = OSSwapHostToLittleConstInt64(self.generationCount);
360 authenticatedData[SecCKRecordGenerationCountKey] = [NSData dataWithBytes:&genCount64 length:sizeof(genCount64)];
361
362 uint64_t encver = OSSwapHostToLittleConstInt64((uint64_t)self.encver);
363 authenticatedData[SecCKRecordEncryptionVersionKey] = [NSData dataWithBytes:&encver length:sizeof(encver)];
364
365 // v2 authenticates the PCS fields too
366 if(self.plaintextPCSServiceIdentifier) {
367 uint64_t pcsServiceIdentifier = OSSwapHostToLittleConstInt64([self.plaintextPCSServiceIdentifier unsignedLongValue]);
368 authenticatedData[SecCKRecordPCSServiceIdentifier] = [NSData dataWithBytes:&pcsServiceIdentifier length:sizeof(pcsServiceIdentifier)];
369 }
370 authenticatedData[SecCKRecordPCSPublicKey] = self.plaintextPCSPublicKey;
371 authenticatedData[SecCKRecordPCSPublicIdentity] = self.plaintextPCSPublicIdentity;
372
373 // Iterate through the fields in the old CKKSItem. If we don't recognize any of them, add them to the authenticated data.
374 if(olditem) {
375 CKRecord* record = olditem.storedCKRecord;
376 if(record) {
377 for(NSString* key in record.allKeys) {
378 if([key isEqualToString:@"UUID"] ||
379 [key isEqualToString:SecCKRecordHostOSVersionKey] ||
380 [key isEqualToString:SecCKRecordDataKey] ||
381 [key isEqualToString:SecCKRecordWrappedKeyKey] ||
382 [key isEqualToString:SecCKRecordGenerationCountKey] ||
383 [key isEqualToString:SecCKRecordEncryptionVersionKey] ||
384 [key isEqualToString:SecCKRecordPCSServiceIdentifier] ||
385 [key isEqualToString:SecCKRecordPCSPublicKey] ||
386 [key isEqualToString:SecCKRecordPCSPublicIdentity]) {
387 // This version of CKKS knows about this data field. Ignore them with prejudice.
388 continue;
389 }
390
391 if([key hasPrefix:@"server_"]) {
392 // Ignore all fields prefixed by "server_"
393 continue;
394 }
395
396 id obj = record[key];
397
398 // Skip CKReferences, NSArray, CLLocation, and CKAsset.
399 if([obj isKindOfClass: [NSString class]]) {
400 // Add an NSString.
401 authenticatedData[key] = [obj dataUsingEncoding: NSUTF8StringEncoding];
402 } else if([obj isKindOfClass: [NSData class]]) {
403 // Add an NSData
404 authenticatedData[key] = [obj copy];
405 } else if([obj isKindOfClass:[NSDate class]]) {
406 // Add an NSDate
407 NSISO8601DateFormatter *formatter = [[NSISO8601DateFormatter alloc] init];
408 NSString* str = [formatter stringForObjectValue: obj];
409
410 authenticatedData[key] = [str dataUsingEncoding: NSUTF8StringEncoding];
411 } else if([obj isKindOfClass: [NSNumber class]]) {
412 // Add an NSNumber
413 uint64_t n64 = OSSwapHostToLittleConstInt64([obj unsignedLongLongValue]);
414 authenticatedData[key] = [NSData dataWithBytes:&n64 length:sizeof(n64)];
415 }
416 }
417
418 }
419 }
420
421 // TODO: add unauth'ed field name here
422
423 return authenticatedData;
424 }
425
426 #pragma mark - Utility
427
428 - (NSString*)description {
429 return [NSString stringWithFormat: @"<%@: %@>", NSStringFromClass([self class]), self.uuid];
430 }
431
432 - (NSString*)debugDescription {
433 return [NSString stringWithFormat: @"<%@: %@ %p>", NSStringFromClass([self class]), self.uuid, self];
434 }
435
436 - (instancetype)copyWithZone:(NSZone *)zone {
437 CKKSItem *itemCopy = [super copyWithZone:zone];
438 itemCopy->_uuid = _uuid;
439 itemCopy->_parentKeyUUID = _parentKeyUUID;
440 itemCopy->_encitem = _encitem;
441 itemCopy->_wrappedkey = _wrappedkey;
442 itemCopy->_generationCount = _generationCount;
443 itemCopy->_encver = _encver;
444 return itemCopy;
445 }
446
447 #pragma mark - Getters/Setters
448
449 - (NSString*) base64Item {
450 return [self.encitem base64EncodedStringWithOptions:0];
451 }
452
453 - (void) setBase64Item: (NSString*) base64Item {
454 _encitem = [[NSData alloc] initWithBase64EncodedString: base64Item options:0];
455 }
456
457 #pragma mark - CKKSSQLDatabaseObject helpers
458
459 // Note that CKKSItems are not intended to be saved directly, and so CKKSItem does not implement sqlTable.
460 // You must subclass CKKSItem to have this work correctly, although you can call back up into this class to use these if you like.
461
462 + (NSArray<NSString*>*)sqlColumns {
463 return @[@"UUID", @"parentKeyUUID", @"ckzone", @"encitem", @"wrappedkey", @"gencount", @"encver", @"ckrecord",
464 @"pcss", @"pcsk", @"pcsi"];
465 }
466
467 - (NSDictionary<NSString*,NSString*>*)whereClauseToFindSelf {
468 return @{@"UUID": self.uuid, @"ckzone":self.zoneID.zoneName};
469 }
470
471 - (NSDictionary<NSString*,NSString*>*)sqlValues {
472 return @{@"UUID": self.uuid,
473 @"parentKeyUUID": self.parentKeyUUID,
474 @"ckzone": CKKSNilToNSNull(self.zoneID.zoneName),
475 @"encitem": self.base64encitem,
476 @"wrappedkey": [self.wrappedkey base64WrappedKey],
477 @"gencount": [NSNumber numberWithInteger:self.generationCount],
478 @"encver": [NSNumber numberWithInteger:self.encver],
479 @"ckrecord": CKKSNilToNSNull([self.encodedCKRecord base64EncodedStringWithOptions:0]),
480 @"pcss": CKKSNilToNSNull(self.plaintextPCSServiceIdentifier),
481 @"pcsk": CKKSNilToNSNull([self.plaintextPCSPublicKey base64EncodedStringWithOptions:0]),
482 @"pcsi": CKKSNilToNSNull([self.plaintextPCSPublicIdentity base64EncodedStringWithOptions:0])};
483 }
484
485 + (instancetype)fromDatabaseRow: (NSDictionary*) row {
486 return [[CKKSItem alloc] initWithUUID:row[@"UUID"]
487 parentKeyUUID:row[@"parentKeyUUID"]
488 zoneID:[[CKRecordZoneID alloc] initWithZoneName: row[@"ckzone"] ownerName:CKCurrentUserDefaultName]
489 encodedCKRecord:CKKSUnbase64NullableString(row[@"ckrecord"])
490 encItem:CKKSUnbase64NullableString(row[@"encitem"])
491 wrappedkey:CKKSIsNull(row[@"wrappedkey"]) ? nil : [[CKKSWrappedAESSIVKey alloc] initWithBase64: row[@"wrappedkey"]]
492 generationCount:[row[@"gencount"] integerValue]
493 encver:[row[@"encver"] integerValue]
494 plaintextPCSServiceIdentifier:CKKSIsNull(row[@"pcss"]) ? nil : [NSNumber numberWithInteger: [row[@"pcss"] integerValue]]
495 plaintextPCSPublicKey:CKKSUnbase64NullableString(row[@"pcsk"])
496 plaintextPCSPublicIdentity:CKKSUnbase64NullableString(row[@"pcsi"])
497 ];
498 }
499
500 @end
501
502 #pragma mark - CK-Aware Database Helpers
503
504 @implementation CKKSSQLDatabaseObject (CKKSZoneExtras)
505
506 + (NSArray<NSString*>*)allUUIDs:(CKRecordZoneID*)zoneID error:(NSError * __autoreleasing *)error {
507 __block NSMutableArray* uuids = [[NSMutableArray alloc] init];
508
509 [CKKSSQLDatabaseObject queryDatabaseTable: [self sqlTable]
510 where:@{@"ckzone": CKKSNilToNSNull(zoneID.zoneName)}
511 columns: @[@"UUID"]
512 groupBy: nil
513 orderBy:nil
514 limit: -1
515 processRow: ^(NSDictionary* row) {
516 [uuids addObject: row[@"UUID"]];
517 }
518 error: error];
519 return uuids;
520 }
521
522 + (NSArray*) all:(CKRecordZoneID*) zoneID error: (NSError * __autoreleasing *) error {
523 return [self allWhere: @{@"ckzone": CKKSNilToNSNull(zoneID.zoneName)} error:error];
524 }
525
526 + (bool) deleteAll:(CKRecordZoneID*) zoneID error: (NSError * __autoreleasing *) error {
527 bool ok = [CKKSSQLDatabaseObject deleteFromTable:[self sqlTable] where: @{@"ckzone":CKKSNilToNSNull(zoneID.zoneName)} connection:nil error: error];
528
529 if(ok) {
530 secdebug("ckksitem", "Deleted all %@", self);
531 } else {
532 secdebug("ckksitem", "Couldn't delete all %@: %@", self, error ? *error : @"unknown");
533 }
534 return ok;
535 }
536
537 @end
538
539 #endif