]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/CKKS.m
Security-59306.101.1.tar.gz
[apple/security.git] / keychain / ckks / CKKS.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 #include <dispatch/dispatch.h>
25 #import <Foundation/Foundation.h>
26 #if OCTAGON
27 #import <CloudKit/CloudKit.h>
28 #endif
29
30 #include <utilities/debugging.h>
31 #include "keychain/securityd/SecItemServer.h"
32 #include <Security/SecItemPriv.h>
33
34 #import <Foundation/Foundation.h>
35 #import "keychain/ckks/CKKS.h"
36 #import "keychain/ckks/CKKSKeychainView.h"
37 #import "keychain/ckks/CKKSViewManager.h"
38 #import "keychain/ckks/CKKSKey.h"
39
40 #import "keychain/ot/OTManager.h"
41
42 NSDictionary<CKKSZoneKeyState*, NSNumber*>* CKKSZoneKeyStateMap(void) {
43 static NSDictionary<CKKSZoneKeyState*, NSNumber*>* map = nil;
44 static dispatch_once_t onceToken;
45 dispatch_once(&onceToken, ^{
46 map = @{
47 SecCKKSZoneKeyStateReady: @0U,
48 SecCKKSZoneKeyStateError: @1U,
49 SecCKKSZoneKeyStateCancelled: @2U,
50
51 SecCKKSZoneKeyStateInitializing: @3U,
52 SecCKKSZoneKeyStateInitialized: @4U,
53 SecCKKSZoneKeyStateFetchComplete: @5U,
54 SecCKKSZoneKeyStateWaitForTLK: @6U,
55 SecCKKSZoneKeyStateWaitForUnlock: @7U,
56 SecCKKSZoneKeyStateUnhealthy: @8U,
57 SecCKKSZoneKeyStateBadCurrentPointers: @9U,
58 SecCKKSZoneKeyStateNewTLKsFailed: @10U,
59 SecCKKSZoneKeyStateNeedFullRefetch: @11U,
60 SecCKKSZoneKeyStateHealTLKShares: @12U,
61 SecCKKSZoneKeyStateHealTLKSharesFailed:@13U,
62 SecCKKSZoneKeyStateWaitForFixupOperation:@14U,
63 SecCKKSZoneKeyStateReadyPendingUnlock: @15U,
64 SecCKKSZoneKeyStateFetch: @16U,
65 SecCKKSZoneKeyStateResettingZone: @17U,
66 SecCKKSZoneKeyStateResettingLocalData: @18U,
67 SecCKKSZoneKeyStateLoggedOut: @19U,
68 SecCKKSZoneKeyStateZoneCreationFailed: @20U,
69 SecCKKSZoneKeyStateWaitForTrust: @21U,
70 SecCKKSZoneKeyStateWaitForTLKUpload: @22U,
71 SecCKKSZoneKeyStateWaitForTLKCreation: @23U,
72 SecCKKSZoneKeyStateProcess: @24U,
73 };
74 });
75 return map;
76 }
77
78 NSDictionary<NSNumber*, CKKSZoneKeyState*>* CKKSZoneKeyStateInverseMap(void) {
79 static NSDictionary<NSNumber*, CKKSZoneKeyState*>* backwardMap = nil;
80 static dispatch_once_t onceToken;
81 dispatch_once(&onceToken, ^{
82 NSDictionary<CKKSZoneKeyState*, NSNumber*>* forwardMap = CKKSZoneKeyStateMap();
83 backwardMap = [NSDictionary dictionaryWithObjects:[forwardMap allKeys] forKeys:[forwardMap allValues]];
84 });
85 return backwardMap;
86 }
87
88 NSNumber* CKKSZoneKeyToNumber(CKKSZoneKeyState* state) {
89 if(!state) {
90 return CKKSZoneKeyStateMap()[SecCKKSZoneKeyStateError];
91 }
92 NSNumber* result = CKKSZoneKeyStateMap()[state];
93 if(result) {
94 return result;
95 }
96 return CKKSZoneKeyStateMap()[SecCKKSZoneKeyStateError];
97 }
98 CKKSZoneKeyState* CKKSZoneKeyRecover(NSNumber* stateNumber) {
99 if(!stateNumber) {
100 return SecCKKSZoneKeyStateError;
101 }
102 CKKSZoneKeyState* result = CKKSZoneKeyStateInverseMap()[stateNumber];
103 if(result) {
104 return result;
105 }
106 return SecCKKSZoneKeyStateError;
107 }
108
109 bool CKKSKeyStateTransient(CKKSZoneKeyState* state) {
110 // Easier to compare against a blacklist of end states
111 bool nontransient = [state isEqualToString:SecCKKSZoneKeyStateReady] ||
112 [state isEqualToString:SecCKKSZoneKeyStateReadyPendingUnlock] ||
113 [state isEqualToString:SecCKKSZoneKeyStateWaitForTrust] ||
114 [state isEqualToString:SecCKKSZoneKeyStateWaitForTLK] ||
115 [state isEqualToString:SecCKKSZoneKeyStateWaitForTLKCreation] ||
116 [state isEqualToString:SecCKKSZoneKeyStateWaitForTLKUpload] ||
117 [state isEqualToString:SecCKKSZoneKeyStateWaitForUnlock] ||
118 [state isEqualToString:SecCKKSZoneKeyStateError] ||
119 [state isEqualToString:SecCKKSZoneKeyStateCancelled];
120 return !nontransient;
121 }
122
123 #if OCTAGON
124 // If you want CKKS to run in your daemon/tests, you must call SecCKKSEnable before bringing up the keychain db
125 static bool enableCKKS = false;
126 static bool testCKKS = false;
127
128 bool SecCKKSIsEnabled(void) {
129 if([CKDatabase class] == nil) {
130 // CloudKit is not linked. We cannot bring CKKS up; disable it with prejudice.
131 secerror("CKKS: CloudKit.framework appears to not be linked. Cannot enable CKKS (on pain of crash).");
132 return false;
133 }
134
135 return enableCKKS;
136 }
137
138 bool SecCKKSEnable() {
139 enableCKKS = true;
140 return enableCKKS;
141 }
142
143 bool SecCKKSDisable() {
144 enableCKKS = false;
145 return enableCKKS;
146 }
147
148 bool SecCKKSResetSyncing(void) {
149 // The function name is a bit of a lie, but it does the thing.
150 [OTManager resetManager:true to:nil];
151 return SecCKKSIsEnabled();
152 }
153
154 bool SecCKKSTestsEnabled(void) {
155 return testCKKS;
156 }
157
158 bool SecCKKSTestsEnable(void) {
159 if([CKDatabase class] == nil) {
160 // CloudKit is not linked. We cannot bring CKKS up; disable it with prejudice.
161 secerror("CKKS: CloudKit.framework appears to not be linked. Cannot enable CKKS testing.");
162 testCKKS = false;
163 return false;
164 }
165
166 testCKKS = true;
167 return testCKKS;
168 }
169
170 bool SecCKKSTestsDisable(void) {
171 testCKKS = false;
172 return testCKKS;
173 }
174
175 // Feature flags to twiddle behavior
176 static bool CKKSSyncManifests = false;
177 bool SecCKKSSyncManifests(void) {
178 return CKKSSyncManifests;
179 }
180 bool SecCKKSEnableSyncManifests() {
181 CKKSSyncManifests = true;
182 return CKKSSyncManifests;
183 }
184 bool SecCKKSSetSyncManifests(bool value) {
185 CKKSSyncManifests = value;
186 return CKKSSyncManifests;
187 }
188
189 static bool CKKSEnforceManifests = false;
190 bool SecCKKSEnforceManifests(void) {
191 return CKKSEnforceManifests;
192 }
193 bool SecCKKSEnableEnforceManifests() {
194 CKKSEnforceManifests = true;
195 return CKKSEnforceManifests;
196 }
197 bool SecCKKSSetEnforceManifests(bool value) {
198 CKKSEnforceManifests = value;
199 return CKKSEnforceManifests;
200 }
201
202 // defaults write com.apple.security.ckks reduce-rate-limiting YES
203 static bool CKKSReduceRateLimiting = false;
204 bool SecCKKSReduceRateLimiting(void) {
205 static dispatch_once_t onceToken;
206 dispatch_once(&onceToken, ^{
207 // Use the default value as above, or apply the preferences value if it exists
208 NSUserDefaults* defaults = [[NSUserDefaults alloc] initWithSuiteName:SecCKKSUserDefaultsSuite];
209 NSString* key = @"reduce-rate-limiting";
210 [defaults registerDefaults: @{key: CKKSReduceRateLimiting ? @YES : @NO}];
211
212 CKKSReduceRateLimiting = !![defaults boolForKey:@"reduce-rate-limiting"];
213 secnotice("ckks", "reduce-rate-limiting is %@", CKKSReduceRateLimiting ? @"on" : @"off");
214 });
215
216 return CKKSReduceRateLimiting;
217 }
218
219 bool SecCKKSSetReduceRateLimiting(bool value) {
220 (void) SecCKKSReduceRateLimiting(); // Call this once to read the defaults write
221 CKKSReduceRateLimiting = value;
222 secnotice("ckks", "reduce-rate-limiting is now %@", CKKSReduceRateLimiting ? @"on" : @"off");
223 return CKKSReduceRateLimiting;
224 }
225
226 // Here's a mechanism for CKKS feature flags with default values from NSUserDefaults:
227 /*static bool CKKSShareTLKs = true;
228 bool SecCKKSShareTLKs(void) {
229 return true;
230 static dispatch_once_t onceToken;
231 dispatch_once(&onceToken, ^{
232 // Use the default value as above, or apply the preferences value if it exists
233 NSUserDefaults* defaults = [[NSUserDefaults alloc] initWithSuiteName:SecCKKSUserDefaultsSuite];
234 [defaults registerDefaults: @{@"tlksharing": CKKSShareTLKs ? @YES : @NO}];
235
236 CKKSShareTLKs = !![defaults boolForKey:@"tlksharing"];
237 secnotice("ckksshare", "TLK sharing is %@", CKKSShareTLKs ? @"on" : @"off");
238 });
239
240 return CKKSShareTLKs;
241 }*/
242
243 // Feature flags to twiddle behavior for tests
244 static bool CKKSDisableAutomaticUUID = false;
245 bool SecCKKSTestDisableAutomaticUUID(void) {
246 #if DEBUG
247 return CKKSDisableAutomaticUUID;
248 #else
249 return false;
250 #endif
251 }
252 void SecCKKSTestSetDisableAutomaticUUID(bool set) {
253 CKKSDisableAutomaticUUID = set;
254 }
255
256 static bool CKKSDisableSOS = false;
257 bool SecCKKSTestDisableSOS(void) {
258 #if DEBUG
259 return CKKSDisableSOS;
260 #else
261 return false;
262 #endif
263 }
264 void SecCKKSTestSetDisableSOS(bool set) {
265 CKKSDisableSOS = set;
266 }
267
268
269 static bool CKKSDisableKeyNotifications = false;
270 bool SecCKKSTestDisableKeyNotifications(void) {
271 #if DEBUG
272 return CKKSDisableKeyNotifications;
273 #else
274 return false;
275 #endif
276 }
277 void SecCKKSTestSetDisableKeyNotifications(bool set) {
278 CKKSDisableKeyNotifications = set;
279 }
280
281 void SecCKKSTestResetFlags(void) {
282 SecCKKSTestSetDisableAutomaticUUID(false);
283 SecCKKSTestSetDisableSOS(false);
284 SecCKKSTestSetDisableKeyNotifications(false);
285 }
286
287 #else /* NO OCTAGON */
288
289 bool SecCKKSIsEnabled(void) {
290 secerror("CKKS was disabled at compile time.");
291 return false;
292 }
293
294 bool SecCKKSEnable() {
295 return false;
296 }
297
298 bool SecCKKSDisable() {
299 return false;
300 }
301
302 bool SecCKKSResetSyncing(void) {
303 return SecCKKSIsEnabled();
304 }
305
306 #endif /* OCTAGON */
307
308
309
310 void SecCKKSInitialize(SecDbRef db) {
311 #if OCTAGON
312 @autoreleasepool {
313 CKKSViewManager* manager = [CKKSViewManager manager];
314 [manager createViews];
315 [manager setupAnalytics];
316
317 SecDbAddNotifyPhaseBlock(db, ^(SecDbConnectionRef dbconn, SecDbTransactionPhase phase, SecDbTransactionSource source, CFArrayRef changes) {
318 SecCKKSNotifyBlock(dbconn, phase, source, changes);
319 });
320
321 [manager.completedSecCKKSInitialize fulfill];
322
323 if(!SecCKKSTestsEnabled()) {
324 static dispatch_once_t onceToken;
325 dispatch_once(&onceToken, ^{
326 [OctagonAPSReceiver receiverForEnvironment:APSEnvironmentProduction namedDelegatePort:SecCKKSAPSNamedPort apsConnectionClass:[APSConnection class]];
327 });
328 }
329 }
330 #endif
331 }
332
333 void SecCKKSNotifyBlock(SecDbConnectionRef dbconn, SecDbTransactionPhase phase, SecDbTransactionSource source, CFArrayRef changes) {
334 #if OCTAGON
335 if(phase == kSecDbTransactionDidRollback) {
336 return;
337 }
338
339 // Ignore our own changes, otherwise we'd infinite-loop.
340 if(source == kSecDbCKKSTransaction) {
341 secinfo("ckks", "Ignoring kSecDbCKKSTransaction notification");
342 return;
343 }
344
345 CFArrayForEach(changes, ^(CFTypeRef r) {
346 SecDbItemRef deleted = NULL;
347 SecDbItemRef added = NULL;
348
349 SecDbEventTranslateComponents(r, (CFTypeRef*) &deleted, (CFTypeRef*) &added);
350
351 if(!added && !deleted) {
352 secerror("CKKS: SecDbEvent gave us garbage: %@", r);
353 return;
354 }
355
356 [[CKKSViewManager manager] handleKeychainEventDbConnection: dbconn source:source added: added deleted: deleted];
357 });
358 #endif
359 }
360
361 void SecCKKS24hrNotification() {
362 #if OCTAGON
363 @autoreleasepool {
364 [[CKKSViewManager manager] xpc24HrNotification];
365 }
366 #endif
367 }
368
369 void CKKSRegisterSyncStatusCallback(CFStringRef cfuuid, SecBoolCFErrorCallback cfcallback) {
370 #if OCTAGON
371 // Keep plumbing, but transition to NS.
372 SecBoolNSErrorCallback nscallback = ^(bool result, NSError* err) {
373 cfcallback(result, (__bridge CFErrorRef) err);
374 };
375
376 [[CKKSViewManager manager] registerSyncStatusCallback: (__bridge NSString*) cfuuid callback:nscallback];
377 #endif
378 }
379
380 void SecCKKSPerformLocalResync() {
381 #if OCTAGON
382 if(SecCKKSIsEnabled()) {
383 secnotice("ckks", "Local keychain was reset; performing local resync");
384 [[CKKSViewManager manager] rpcResyncLocal:nil reply:^(NSError *result) {
385 if(result) {
386 secnotice("ckks", "Local keychain reset resync finished with an error: %@", result);
387 } else {
388 secnotice("ckks", "Local keychain reset resync finished successfully");
389 }
390 }];
391 }
392 #endif
393 }