2 * Copyright (c) 2017 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@
24 #import "CKKSCurrentKeyPointer.h"
28 @implementation CKKSCurrentKeyPointer
30 - (instancetype)initForClass:(CKKSKeyClass*)keyclass
31 currentKeyUUID:(NSString*)currentKeyUUID
32 zoneID:(CKRecordZoneID*)zoneID
33 encodedCKRecord: (NSData*) encodedrecord
35 if(self = [super initWithCKRecordType: SecCKRecordCurrentKeyType encodedCKRecord:encodedrecord zoneID:zoneID]) {
37 _currentKeyUUID = currentKeyUUID;
39 if(self.currentKeyUUID == nil) {
40 secerror("ckkscurrentkey: created a CKKSCurrentKey with a nil currentKeyUUID. Why?");
46 - (NSString*)description {
47 return [NSString stringWithFormat:@"<CKKSCurrentKeyPointer(%@) %@: %@>", self.zoneID.zoneName, self.keyclass, self.currentKeyUUID];
50 #pragma mark - CKKSCKRecordHolder methods
52 - (NSString*) CKRecordName {
56 - (CKRecord*) updateCKRecord: (CKRecord*) record zoneID: (CKRecordZoneID*) zoneID {
57 // The record name should already match keyclass...
58 if(![record.recordID.recordName isEqualToString: self.keyclass]) {
60 exceptionWithName:@"WrongCKRecordNameException"
61 reason:[NSString stringWithFormat: @"CKRecord name (%@) was not %@", record.recordID.recordName, self.keyclass]
65 // Set the parent reference
66 record[SecCKRecordParentKeyRefKey] = [[CKReference alloc] initWithRecordID: [[CKRecordID alloc] initWithRecordName: self.currentKeyUUID zoneID: zoneID] action: CKReferenceActionNone];
70 - (bool) matchesCKRecord: (CKRecord*) record {
71 if(![record.recordType isEqualToString: SecCKRecordCurrentKeyType]) {
75 if(![record.recordID.recordName isEqualToString: self.keyclass]) {
79 if(![[record[SecCKRecordParentKeyRefKey] recordID].recordName isEqualToString: self.currentKeyUUID]) {
86 - (void) setFromCKRecord: (CKRecord*) record {
87 if(![record.recordType isEqualToString: SecCKRecordCurrentKeyType]) {
89 exceptionWithName:@"WrongCKRecordTypeException"
90 reason:[NSString stringWithFormat: @"CKRecordType (%@) was not %@", record.recordType, SecCKRecordCurrentKeyType]
94 [self setStoredCKRecord:record];
96 // TODO: verify this is a real keyclass
97 self.keyclass = (CKKSKeyClass*) record.recordID.recordName;
98 self.currentKeyUUID = [record[SecCKRecordParentKeyRefKey] recordID].recordName;
100 if(self.currentKeyUUID == nil) {
101 secerror("ckkscurrentkey: No current key UUID in record! How/why? %@", record);
105 #pragma mark - Load from database
107 + (instancetype) fromDatabase: (CKKSKeyClass*) keyclass zoneID:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
108 return [self fromDatabaseWhere: @{@"keyclass": keyclass, @"ckzone":zoneID.zoneName} error: error];
111 + (instancetype) tryFromDatabase: (CKKSKeyClass*) keyclass zoneID:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
112 return [self tryFromDatabaseWhere: @{@"keyclass": keyclass, @"ckzone":zoneID.zoneName} error: error];
115 + (instancetype) forKeyClass: (CKKSKeyClass*) keyclass withKeyUUID: (NSString*) keyUUID zoneID:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
116 NSError* localerror = nil;
117 CKKSCurrentKeyPointer* current = [self tryFromDatabase: keyclass zoneID:zoneID error: &localerror];
126 current.currentKeyUUID = keyUUID;
130 return [[CKKSCurrentKeyPointer alloc] initForClass: keyclass currentKeyUUID: keyUUID zoneID:zoneID encodedCKRecord:nil];
133 + (NSArray<CKKSCurrentKeyPointer*>*)all:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
134 return [self allWhere:@{@"ckzone":zoneID.zoneName} error:error];
137 + (bool) deleteAll:(CKRecordZoneID*) zoneID error: (NSError * __autoreleasing *) error {
138 bool ok = [CKKSSQLDatabaseObject deleteFromTable:[self sqlTable] where: @{@"ckzone":zoneID.zoneName} connection:nil error: error];
141 secdebug("ckksitem", "Deleted all %@", self);
143 secdebug("ckksitem", "Couldn't delete all %@: %@", self, error ? *error : @"unknown");
148 #pragma mark - CKKSSQLDatabaseObject methods
150 + (NSString*) sqlTable {
151 return @"currentkeys";
154 + (NSArray<NSString*>*) sqlColumns {
155 return @[@"keyclass", @"currentKeyUUID", @"ckzone", @"ckrecord"];
158 - (NSDictionary<NSString*,NSString*>*) whereClauseToFindSelf {
159 return @{@"keyclass": self.keyclass, @"ckzone":self.zoneID.zoneName};
162 - (NSDictionary<NSString*,NSString*>*) sqlValues {
163 return @{@"keyclass": self.keyclass,
164 @"currentKeyUUID": CKKSNilToNSNull(self.currentKeyUUID),
165 @"ckzone": CKKSNilToNSNull(self.zoneID.zoneName),
166 @"ckrecord": CKKSNilToNSNull([self.encodedCKRecord base64EncodedStringWithOptions:0]),
170 + (instancetype) fromDatabaseRow: (NSDictionary*) row {
171 return [[CKKSCurrentKeyPointer alloc] initForClass: row[@"keyclass"]
172 currentKeyUUID: [row[@"currentKeyUUID"] isEqual: [NSNull null]] ? nil : row[@"currentKeyUUID"]
173 zoneID: [[CKRecordZoneID alloc] initWithZoneName: row[@"ckzone"] ownerName:CKCurrentUserDefaultName]
174 encodedCKRecord: [[NSData alloc] initWithBase64EncodedString: row[@"ckrecord"] options:0]];
179 @implementation CKKSCurrentKeySet
180 -(instancetype)init {
181 if((self = [super init])) {
186 -(instancetype)initForZone:(CKRecordZoneID*)zoneID {
187 if((self = [super init])) {
188 NSError* error = nil;
189 _currentTLKPointer = [CKKSCurrentKeyPointer tryFromDatabase: SecCKKSKeyClassTLK zoneID:zoneID error:&error];
190 _currentClassAPointer = [CKKSCurrentKeyPointer tryFromDatabase: SecCKKSKeyClassA zoneID:zoneID error:&error];
191 _currentClassCPointer = [CKKSCurrentKeyPointer tryFromDatabase: SecCKKSKeyClassC zoneID:zoneID error:&error];
193 _tlk = _currentTLKPointer.currentKeyUUID ? [CKKSKey tryFromDatabase:_currentTLKPointer.currentKeyUUID zoneID:zoneID error:&error] : nil;
194 _classA = _currentClassAPointer.currentKeyUUID ? [CKKSKey tryFromDatabase:_currentClassAPointer.currentKeyUUID zoneID:zoneID error:&error] : nil;
195 _classC = _currentClassCPointer.currentKeyUUID ? [CKKSKey tryFromDatabase:_currentClassCPointer.currentKeyUUID zoneID:zoneID error:&error] : nil;
202 -(NSString*)description {
204 return [NSString stringWithFormat:@"<CKKSCurrentKeySet: %@:%@ %@:%@ %@:%@ %@>",
205 self.currentTLKPointer.currentKeyUUID, self.tlk,
206 self.currentClassAPointer.currentKeyUUID, self.classA,
207 self.currentClassCPointer.currentKeyUUID, self.classC,
211 return [NSString stringWithFormat:@"<CKKSCurrentKeySet: %@:%@ %@:%@ %@:%@>",
212 self.currentTLKPointer.currentKeyUUID, self.tlk,
213 self.currentClassAPointer.currentKeyUUID, self.classA,
214 self.currentClassCPointer.currentKeyUUID, self.classC];