]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/CKKSCurrentKeyPointer.m
Security-59754.41.1.tar.gz
[apple/security.git] / keychain / ckks / CKKSCurrentKeyPointer.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 "CKKSCurrentKeyPointer.h"
25
26 #if OCTAGON
27
28 #import "keychain/categories/NSError+UsefulConstructors.h"
29
30 @implementation CKKSCurrentKeyPointer
31
32 - (instancetype)initForClass:(CKKSKeyClass*)keyclass
33 currentKeyUUID:(NSString*)currentKeyUUID
34 zoneID:(CKRecordZoneID*)zoneID
35 encodedCKRecord: (NSData*) encodedrecord
36 {
37 if(self = [super initWithCKRecordType: SecCKRecordCurrentKeyType encodedCKRecord:encodedrecord zoneID:zoneID]) {
38 _keyclass = keyclass;
39 _currentKeyUUID = currentKeyUUID;
40
41 if(self.currentKeyUUID == nil) {
42 ckkserror_global("currentkey", "created a CKKSCurrentKey with a nil currentKeyUUID. Why?");
43 }
44 }
45 return self;
46 }
47
48 - (NSString*)description {
49 return [NSString stringWithFormat:@"<CKKSCurrentKeyPointer(%@) %@: %@>", self.zoneID.zoneName, self.keyclass, self.currentKeyUUID];
50 }
51
52 - (instancetype)copyWithZone:(NSZone*)zone {
53 CKKSCurrentKeyPointer* copy = [super copyWithZone:zone];
54 copy.keyclass = [self.keyclass copyWithZone:zone];
55 copy.currentKeyUUID = [self.currentKeyUUID copyWithZone:zone];
56 return copy;
57 }
58 - (BOOL)isEqual: (id) object {
59 if(![object isKindOfClass:[CKKSCurrentKeyPointer class]]) {
60 return NO;
61 }
62
63 CKKSCurrentKeyPointer* obj = (CKKSCurrentKeyPointer*) object;
64
65 return ([self.zoneID isEqual: obj.zoneID] &&
66 ((self.currentKeyUUID == nil && obj.currentKeyUUID == nil) || [self.currentKeyUUID isEqual: obj.currentKeyUUID]) &&
67 ((self.keyclass == nil && obj.keyclass == nil) || [self.keyclass isEqual:obj.keyclass]) &&
68 YES) ? YES : NO;
69 }
70
71 #pragma mark - CKKSCKRecordHolder methods
72
73 - (NSString*) CKRecordName {
74 return self.keyclass;
75 }
76
77 - (CKRecord*) updateCKRecord: (CKRecord*) record zoneID: (CKRecordZoneID*) zoneID {
78 if(![record.recordType isEqualToString: SecCKRecordCurrentKeyType]) {
79 @throw [NSException
80 exceptionWithName:@"WrongCKRecordTypeException"
81 reason:[NSString stringWithFormat: @"CKRecordType (%@) was not %@", record.recordType, SecCKRecordCurrentKeyType]
82 userInfo:nil];
83 }
84
85 // The record name should already match keyclass...
86 if(![record.recordID.recordName isEqualToString: self.keyclass]) {
87 @throw [NSException
88 exceptionWithName:@"WrongCKRecordNameException"
89 reason:[NSString stringWithFormat: @"CKRecord name (%@) was not %@", record.recordID.recordName, self.keyclass]
90 userInfo:nil];
91 }
92
93 // Set the parent reference
94 record[SecCKRecordParentKeyRefKey] = [[CKReference alloc] initWithRecordID: [[CKRecordID alloc] initWithRecordName: self.currentKeyUUID zoneID: zoneID] action: CKReferenceActionNone];
95 return record;
96 }
97
98 - (bool) matchesCKRecord: (CKRecord*) record {
99 if(![record.recordType isEqualToString: SecCKRecordCurrentKeyType]) {
100 return false;
101 }
102
103 if(![record.recordID.recordName isEqualToString: self.keyclass]) {
104 return false;
105 }
106
107 if(![[record[SecCKRecordParentKeyRefKey] recordID].recordName isEqualToString: self.currentKeyUUID]) {
108 return false;
109 }
110
111 return true;
112 }
113
114 - (void) setFromCKRecord: (CKRecord*) record {
115 if(![record.recordType isEqualToString: SecCKRecordCurrentKeyType]) {
116 @throw [NSException
117 exceptionWithName:@"WrongCKRecordTypeException"
118 reason:[NSString stringWithFormat: @"CKRecordType (%@) was not %@", record.recordType, SecCKRecordCurrentKeyType]
119 userInfo:nil];
120 }
121
122 [self setStoredCKRecord:record];
123
124 // TODO: verify this is a real keyclass
125 self.keyclass = (CKKSKeyClass*) record.recordID.recordName;
126 self.currentKeyUUID = [record[SecCKRecordParentKeyRefKey] recordID].recordName;
127
128 if(self.currentKeyUUID == nil) {
129 ckkserror_global("currentkey", "No current key UUID in record! How/why? %@", record);
130 }
131 }
132
133 #pragma mark - Load from database
134
135 + (instancetype) fromDatabase: (CKKSKeyClass*) keyclass zoneID:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
136 return [self fromDatabaseWhere: @{@"keyclass": keyclass, @"ckzone":zoneID.zoneName} error: error];
137 }
138
139 + (instancetype) tryFromDatabase: (CKKSKeyClass*) keyclass zoneID:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
140 return [self tryFromDatabaseWhere: @{@"keyclass": keyclass, @"ckzone":zoneID.zoneName} error: error];
141 }
142
143 + (instancetype) forKeyClass: (CKKSKeyClass*) keyclass withKeyUUID: (NSString*) keyUUID zoneID:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
144 NSError* localerror = nil;
145 CKKSCurrentKeyPointer* current = [self tryFromDatabase: keyclass zoneID:zoneID error: &localerror];
146 if(localerror) {
147 if(error) {
148 *error = localerror;
149 }
150 return nil;
151 }
152
153 if(current) {
154 current.currentKeyUUID = keyUUID;
155 return current;
156 }
157
158 return [[CKKSCurrentKeyPointer alloc] initForClass: keyclass currentKeyUUID: keyUUID zoneID:zoneID encodedCKRecord:nil];
159 }
160
161 + (NSArray<CKKSCurrentKeyPointer*>*)all:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
162 return [self allWhere:@{@"ckzone":zoneID.zoneName} error:error];
163 }
164
165 + (bool) deleteAll:(CKRecordZoneID*) zoneID error: (NSError * __autoreleasing *) error {
166 bool ok = [CKKSSQLDatabaseObject deleteFromTable:[self sqlTable] where: @{@"ckzone":zoneID.zoneName} connection:nil error: error];
167
168 if(ok) {
169 secdebug("ckksitem", "Deleted all %@", self);
170 } else {
171 secdebug("ckksitem", "Couldn't delete all %@: %@", self, error ? *error : @"unknown");
172 }
173 return ok;
174 }
175
176 #pragma mark - CKKSSQLDatabaseObject methods
177
178 + (NSString*) sqlTable {
179 return @"currentkeys";
180 }
181
182 + (NSArray<NSString*>*) sqlColumns {
183 return @[@"keyclass", @"currentKeyUUID", @"ckzone", @"ckrecord"];
184 }
185
186 - (NSDictionary<NSString*,NSString*>*) whereClauseToFindSelf {
187 return @{@"keyclass": self.keyclass, @"ckzone":self.zoneID.zoneName};
188 }
189
190 - (NSDictionary<NSString*,NSString*>*) sqlValues {
191 return @{@"keyclass": self.keyclass,
192 @"currentKeyUUID": CKKSNilToNSNull(self.currentKeyUUID),
193 @"ckzone": CKKSNilToNSNull(self.zoneID.zoneName),
194 @"ckrecord": CKKSNilToNSNull([self.encodedCKRecord base64EncodedStringWithOptions:0]),
195 };
196 }
197
198 + (instancetype)fromDatabaseRow:(NSDictionary<NSString*, CKKSSQLResult*>*)row {
199 return [[CKKSCurrentKeyPointer alloc] initForClass:(CKKSKeyClass*)row[@"keyclass"].asString
200 currentKeyUUID:row[@"currentKeyUUID"].asString
201 zoneID:[[CKRecordZoneID alloc] initWithZoneName:row[@"ckzone"].asString ownerName:CKCurrentUserDefaultName]
202 encodedCKRecord:row[@"ckrecord"].asBase64DecodedData];
203 }
204
205 @end
206
207 @implementation CKKSCurrentKeySet
208 -(instancetype)initForZoneName:(NSString*)zoneName {
209 if((self = [super init])) {
210 _viewName = zoneName;
211 }
212
213 return self;
214 }
215
216 + (CKKSCurrentKeySet*)loadForZone:(CKRecordZoneID*)zoneID
217 {
218 CKKSCurrentKeySet* set = [[CKKSCurrentKeySet alloc] initForZoneName:zoneID.zoneName];
219 NSError* error = nil;
220
221 set.currentTLKPointer = [CKKSCurrentKeyPointer tryFromDatabase: SecCKKSKeyClassTLK zoneID:zoneID error:&error];
222 set.currentClassAPointer = [CKKSCurrentKeyPointer tryFromDatabase: SecCKKSKeyClassA zoneID:zoneID error:&error];
223 set.currentClassCPointer = [CKKSCurrentKeyPointer tryFromDatabase: SecCKKSKeyClassC zoneID:zoneID error:&error];
224
225 set.tlk = set.currentTLKPointer.currentKeyUUID ? [CKKSKey tryFromDatabase:set.currentTLKPointer.currentKeyUUID zoneID:zoneID error:&error] : nil;
226 set.classA = set.currentClassAPointer.currentKeyUUID ? [CKKSKey tryFromDatabase:set.currentClassAPointer.currentKeyUUID zoneID:zoneID error:&error] : nil;
227 set.classC = set.currentClassCPointer.currentKeyUUID ? [CKKSKey tryFromDatabase:set.currentClassCPointer.currentKeyUUID zoneID:zoneID error:&error] : nil;
228
229 set.tlkShares = [CKKSTLKShareRecord allForUUID:set.currentTLKPointer.currentKeyUUID zoneID:zoneID error:&error];
230 set.pendingTLKShares = nil;
231
232 set.proposed = NO;
233
234 set.error = error;
235
236 return set;
237 }
238
239 -(NSString*)description {
240 if(self.error) {
241 return [NSString stringWithFormat:@"<CKKSCurrentKeySet(%@): %@:%@ %@:%@ %@:%@ new:%d %@>",
242 self.viewName,
243 self.currentTLKPointer.currentKeyUUID, self.tlk,
244 self.currentClassAPointer.currentKeyUUID, self.classA,
245 self.currentClassCPointer.currentKeyUUID, self.classC,
246 self.proposed,
247 self.error];
248
249 } else {
250 return [NSString stringWithFormat:@"<CKKSCurrentKeySet(%@): %@:%@ %@:%@ %@:%@ new:%d>",
251 self.viewName,
252 self.currentTLKPointer.currentKeyUUID, self.tlk,
253 self.currentClassAPointer.currentKeyUUID, self.classA,
254 self.currentClassCPointer.currentKeyUUID, self.classC,
255 self.proposed];
256 }
257 }
258 - (instancetype)copyWithZone:(NSZone*)zone {
259 CKKSCurrentKeySet* copy = [[[self class] alloc] init];
260 copy.currentTLKPointer = [self.currentTLKPointer copyWithZone:zone];
261 copy.currentClassAPointer = [self.currentClassAPointer copyWithZone:zone];
262 copy.currentClassCPointer = [self.currentClassCPointer copyWithZone:zone];
263 copy.tlk = [self.tlk copyWithZone:zone];
264 copy.classA = [self.classA copyWithZone:zone];
265 copy.classC = [self.classC copyWithZone:zone];
266 copy.proposed = self.proposed;
267
268 copy.error = [self.error copyWithZone:zone];
269 return copy;
270 }
271
272 - (CKKSKeychainBackedKeySet* _Nullable)asKeychainBackedSet:(NSError**)error
273 {
274 if(!self.tlk.keycore ||
275 !self.classA.keycore ||
276 !self.classC.keycore) {
277 if(error) {
278 *error = [NSError errorWithDomain:CKKSErrorDomain
279 code:CKKSKeysMissing
280 description:@"unable to make keychain backed set; key is missing"];
281 }
282 return nil;
283 }
284
285 return [[CKKSKeychainBackedKeySet alloc] initWithTLK:self.tlk.keycore
286 classA:self.classA.keycore
287 classC:self.classC.keycore
288 newUpload:self.proposed];
289 }
290 @end
291
292 #endif // OCTAGON