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