2 * Copyright (c) 2016 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@
25 #import <Foundation/Foundation.h>
26 #include <dispatch/dispatch.h>
28 #import "keychain/analytics/CKKSLaunchSequence.h"
29 #import "keychain/ckks/OctagonAPSReceiver.h"
30 #import "keychain/ckks/CKKSLockStateTracker.h"
31 #import "keychain/ckks/CKKSReachabilityTracker.h"
32 #import "keychain/ckks/CloudKitDependencies.h"
34 #include <securityd/SecDbItem.h>
35 #include <utilities/SecDb.h>
37 #import "keychain/ckks/CKKS.h"
38 #import "keychain/ckks/CKKSFetchAllRecordZoneChangesOperation.h"
39 #import "keychain/ckks/CKKSGroupOperation.h"
40 #import "keychain/ckks/CKKSIncomingQueueOperation.h"
41 #import "keychain/ckks/CKKSNearFutureScheduler.h"
42 #import "keychain/ckks/CKKSNewTLKOperation.h"
43 #import "keychain/ckks/CKKSNotifier.h"
44 #import "keychain/ckks/CKKSOutgoingQueueOperation.h"
45 #import "keychain/ckks/CKKSPeer.h"
46 #import "keychain/ckks/CKKSProcessReceivedKeysOperation.h"
47 #import "keychain/ckks/CKKSReencryptOutgoingItemsOperation.h"
48 #import "keychain/ckks/CKKSScanLocalItemsOperation.h"
49 #import "keychain/ckks/CKKSTLKShareRecord.h"
50 #import "keychain/ckks/CKKSUpdateDeviceStateOperation.h"
51 #import "keychain/ckks/CKKSZone.h"
52 #import "keychain/ckks/CKKSZoneModifier.h"
53 #import "keychain/ckks/CKKSZoneChangeFetcher.h"
54 #import "keychain/ckks/CKKSSynchronizeOperation.h"
55 #import "keychain/ckks/CKKSLocalSynchronizeOperation.h"
56 #import "keychain/ckks/CKKSProvideKeySetOperation.h"
60 NS_ASSUME_NONNULL_BEGIN
64 @
class CKKSSynchronizeOperation
;
65 @
class CKKSRateLimiter
;
67 @
class CKKSEgoManifest
;
68 @
class CKKSOutgoingQueueEntry
;
69 @
class CKKSZoneChangeFetcher
;
70 @
class CKKSCurrentKeySet
;
72 @interface CKKSPeerProviderState
: NSObject
73 @property NSString
* peerProviderID
;
75 // The peer provider believes trust in this state is essential. Any subsystem using
76 // a peer provider state should fail and pause if this is YES and there are trust errors.
77 @property BOOL essential
;
79 @
property (nonatomic
, readonly
, nullable
) CKKSSelves
* currentSelfPeers
;
80 @
property (nonatomic
, readonly
, nullable
) NSError
* currentSelfPeersError
;
81 @
property (nonatomic
, readonly
, nullable
) NSSet
<id
<CKKSRemotePeerProtocol
>>* currentTrustedPeers
;
82 @
property (nonatomic
, readonly
, nullable
) NSSet
<NSString
*>* currentTrustedPeerIDs
;
83 @
property (nonatomic
, readonly
, nullable
) NSError
* currentTrustedPeersError
;
85 - (instancetype
)initWithPeerProviderID
:(NSString
*)providerID
86 essential
:(BOOL
)essential
87 selfPeers
:(CKKSSelves
* _Nullable
)selfPeers
88 selfPeersError
:(NSError
* _Nullable
)selfPeersError
89 trustedPeers
:(NSSet
<id
<CKKSPeer
>>* _Nullable
)currentTrustedPeers
90 trustedPeersError
:(NSError
* _Nullable
)trustedPeersError
;
92 + (CKKSPeerProviderState
*)noPeersState
:(id
<CKKSPeerProvider
>)provider
;
95 @interface CKKSKeychainView
: CKKSZone
<CKKSZoneUpdateReceiver
,
96 CKKSChangeFetcherClient
,
97 CKKSPeerUpdateListener
>
99 CKKSZoneKeyState
* _keyHierarchyState
;
102 @property CKKSCondition
* loggedIn
;
103 @property CKKSCondition
* loggedOut
;
104 @property CKKSCondition
* accountStateKnown
;
106 @property CKKSAccountStatus trustStatus
;
107 @
property (nullable
) CKKSResultOperation
* trustDependency
;
109 @
property (nullable
) CKKSLaunchSequence
*launch
;
111 @property CKKSLockStateTracker
* lockStateTracker
;
113 @property CKKSZoneKeyState
* keyHierarchyState
;
114 @
property (nullable
) NSError
* keyHierarchyError
;
115 @
property (nullable
) CKOperationGroup
* keyHierarchyOperationGroup
;
116 @
property (nullable
) NSOperation
* keyStateMachineOperation
;
118 // If the key hierarchy isn't coming together, it might be because we're out of sync with cloudkit.
119 // Use this to track if we've completed a full refetch, so fix-up operations can be done.
120 @property
bool keyStateMachineRefetched
;
121 @
property (nullable
) CKKSEgoManifest
* egoManifest
;
122 @
property (nullable
) CKKSManifest
* latestManifest
;
123 @
property (nullable
) CKKSResultOperation
* keyStateReadyDependency
;
125 // Wait for the key state to become 'nontransient': no pending operation is expected to advance it (at least until intervention)
126 @
property (nullable
) CKKSResultOperation
* keyStateNonTransientDependency
;
128 // True if we believe there's any items in the keychain which haven't been brought up in CKKS yet
129 @property
bool droppedItems
;
131 @
property (readonly
) NSString
* lastActiveTLKUUID
;
133 // Full of condition variables, if you'd like to try to wait until the key hierarchy is in some state
134 @property NSMutableDictionary
<CKKSZoneKeyState
*, CKKSCondition
*>* keyHierarchyConditions
;
136 @property CKKSZoneChangeFetcher
* zoneChangeFetcher
;
138 @
property (weak
) CKKSNearFutureScheduler
* savedTLKNotifier
;
140 @
property (nullable
) CKKSNearFutureScheduler
* suggestTLKUpload
;
143 /* Used for debugging: just what happened last time we ran this? */
144 @property CKKSIncomingQueueOperation
* lastIncomingQueueOperation
;
145 @property CKKSNewTLKOperation
* lastNewTLKOperation
;
146 @property CKKSOutgoingQueueOperation
* lastOutgoingQueueOperation
;
147 @property CKKSProcessReceivedKeysOperation
* lastProcessReceivedKeysOperation
;
148 @property CKKSReencryptOutgoingItemsOperation
* lastReencryptOutgoingItemsOperation
;
149 @property CKKSScanLocalItemsOperation
* lastScanLocalItemsOperation
;
150 @property CKKSSynchronizeOperation
* lastSynchronizeOperation
;
151 @property CKKSResultOperation
* lastFixupOperation
;
153 /* Used for testing: pause operation types by adding operations here */
154 @property NSOperation
* holdReencryptOutgoingItemsOperation
;
155 @property NSOperation
* holdOutgoingQueueOperation
;
156 @property NSOperation
* holdIncomingQueueOperation
;
157 @property NSOperation
* holdLocalSynchronizeOperation
;
158 @property CKKSResultOperation
* holdFixupOperation
;
160 /* Trigger this to tell the whole machine that this view has changed */
161 @property CKKSNearFutureScheduler
* notifyViewChangedScheduler
;
163 /* Trigger this to tell the whole machine that this view is more ready then before */
164 @property CKKSNearFutureScheduler
* notifyViewReadyScheduler
;
166 /* trigger this to request key state machine poking */
167 @property CKKSNearFutureScheduler
* pokeKeyStateMachineScheduler
;
169 // The current list of peer providers. If empty, CKKS will consider itself untrusted, and halt operation
170 @
property (readonly
) NSArray
<id
<CKKSPeerProvider
>>* currentPeerProviders
;
172 // These are available when you're in a dispatchSyncWithAccountKeys call, but at no other time
173 // These must be pre-fetched before you get on the CKKS queue, otherwise we end up with CKKS<->SQLite<->SOSAccountQueue deadlocks
175 // They will be in a parallel array with currentPeerProviders above
176 @
property (readonly
) NSArray
<CKKSPeerProviderState
*>* currentTrustStates
;
178 - (instancetype
)initWithContainer
:(CKContainer
*)container
179 zoneName
:(NSString
*)zoneName
180 accountTracker
:(CKKSAccountStateTracker
*)accountTracker
181 lockStateTracker
:(CKKSLockStateTracker
*)lockStateTracker
182 reachabilityTracker
:(CKKSReachabilityTracker
*)reachabilityTracker
183 changeFetcher
:(CKKSZoneChangeFetcher
*)fetcher
184 zoneModifier
:(CKKSZoneModifier
*)zoneModifier
185 savedTLKNotifier
:(CKKSNearFutureScheduler
*)savedTLKNotifier
186 cloudKitClassDependencies
:(CKKSCloudKitClassDependencies
*)cloudKitClassDependencies
;
188 /* Trust state management */
189 - (void)beginTrustedOperation
:(NSArray
<id
<CKKSPeerProvider
>>*)peerProviders
190 suggestTLKUpload
:(CKKSNearFutureScheduler
*)suggestTLKUpload
;
191 - (void)endTrustedOperation
;
193 /* Synchronous operations */
195 - (void)handleKeychainEventDbConnection
:(SecDbConnectionRef
)dbconn
196 added
:(SecDbItemRef _Nullable
)added
197 deleted
:(SecDbItemRef _Nullable
)deleted
198 rateLimiter
:(CKKSRateLimiter
*)rateLimiter
199 syncCallback
:(SecBoolNSErrorCallback
)syncCallback
;
201 - (void)setCurrentItemForAccessGroup
:(NSData
*)newItemPersistentRef
202 hash
:(NSData
*)newItemSHA1
203 accessGroup
:(NSString
*)accessGroup
204 identifier
:(NSString
*)identifier
205 replacing
:(NSData
* _Nullable
)oldCurrentItemPersistentRef
206 hash
:(NSData
* _Nullable
)oldItemSHA1
207 complete
:(void (^)(NSError
* operror
))complete
;
209 - (void)getCurrentItemForAccessGroup
:(NSString
*)accessGroup
210 identifier
:(NSString
*)identifier
211 fetchCloudValue
:(bool)fetchCloudValue
212 complete
:(void (^)(NSString
* uuid
, NSError
* operror
))complete
;
214 - (bool)outgoingQueueEmpty
:(NSError
* __autoreleasing
*)error
;
216 - (CKKSResultOperation
<CKKSKeySetProviderOperationProtocol
>*)findKeySet
;
217 - (void)receiveTLKUploadRecords
:(NSArray
<CKRecord
*>*)records
;
219 - (CKKSResultOperation
*)waitForFetchAndIncomingQueueProcessing
;
220 - (void)waitForKeyHierarchyReadiness
;
221 - (void)cancelAllOperations
;
223 - (CKKSKey
* _Nullable
)keyForItem
:(SecDbItemRef
)item error
:(NSError
* __autoreleasing
*)error
;
225 - (bool)_onqueueWithAccountKeysCheckTLK
:(CKKSKey
*)proposedTLK error
:(NSError
* __autoreleasing
*)error
;
227 - (BOOL
)otherDevicesReportHavingTLKs
:(CKKSCurrentKeySet
*)keyset
;
229 - (NSSet
<NSString
*>*)_onqueuePriorityOutgoingQueueUUIDs
;
231 /* Asynchronous kickoffs */
233 - (CKKSOutgoingQueueOperation
*)processOutgoingQueue
:(CKOperationGroup
* _Nullable
)ckoperationGroup
;
234 - (CKKSOutgoingQueueOperation
*)processOutgoingQueueAfter
:(CKKSResultOperation
* _Nullable
)after
235 ckoperationGroup
:(CKOperationGroup
* _Nullable
)ckoperationGroup
;
236 - (CKKSOutgoingQueueOperation
*)processOutgoingQueueAfter
:(CKKSResultOperation
*)after
237 requiredDelay
:(uint64_t)requiredDelay
238 ckoperationGroup
:(CKOperationGroup
*)ckoperationGroup
;
240 - (CKKSIncomingQueueOperation
*)processIncomingQueue
:(bool)failOnClassA
;
241 - (CKKSIncomingQueueOperation
*)processIncomingQueue
:(bool)failOnClassA after
:(CKKSResultOperation
* _Nullable
)after
;
243 - (CKKSScanLocalItemsOperation
*)scanLocalItems
:(NSString
*)name
;
245 // Schedules a process queueoperation to happen after the next device unlock. This may be Immediately, if the device is unlocked.
246 - (void)processIncomingQueueAfterNextUnlock
;
248 // This operation will complete directly after the next ProcessIncomingQueue, and should supply that IQO's result. Used mainly for testing; otherwise you'd just kick off a IQO directly.
249 - (CKKSResultOperation
*)resultsOfNextProcessIncomingQueueOperation
;
251 // Schedules an operation to update this device's state record in CloudKit
252 // If rateLimit is true, the operation will abort if it's updated the record in the past 3 days
253 - (CKKSUpdateDeviceStateOperation
*)updateDeviceState
:(bool)rateLimit
254 waitForKeyHierarchyInitialization
:(uint64_t)timeout
255 ckoperationGroup
:(CKOperationGroup
* _Nullable
)ckoperationGroup
;
257 - (CKKSSynchronizeOperation
*)resyncWithCloud
;
258 - (CKKSLocalSynchronizeOperation
*)resyncLocal
;
260 - (CKKSResultOperation
*)fetchAndProcessCKChanges
:(CKKSFetchBecause
*)because
;
262 - (CKKSResultOperation
*)resetLocalData
;
263 - (CKKSResultOperation
*)resetCloudKitZone
:(CKOperationGroup
*)operationGroup
;
265 // Call this to tell the key state machine that you think some new data has arrived that it is interested in
266 - (void)keyStateMachineRequestProcess
;
268 // For our serial queue to work with how handleKeychainEventDbConnection is called from the main thread,
269 // every block on our queue must have a SecDBConnectionRef available to it before it begins on the queue.
270 // Use these helper methods to make sure those exist.
271 - (void)dispatchSync
:(bool (^)(void))block
;
272 - (void)dispatchSyncWithAccountKeys
:(bool (^)(void))block
;
274 /* Synchronous operations which must be called from inside a dispatchAsyncWithAccountKeys or dispatchSync block */
276 // Call this to request the key hierarchy state machine to fetch new updates
277 - (void)_onqueueKeyStateMachineRequestFetch
;
279 // Call this to request the key hierarchy state machine to reprocess
280 - (void)_onqueueKeyStateMachineRequestProcess
;
282 // Call this from a key hierarchy operation to move the state machine, and record the results of the last move.
283 - (void)_onqueueAdvanceKeyStateMachineToState
:(CKKSZoneKeyState
* _Nullable
)state withError
:(NSError
* _Nullable
)error
;
285 // Since we might have people interested in the state transitions of objects, please do those transitions via these methods
286 - (bool)_onqueueChangeOutgoingQueueEntry
:(CKKSOutgoingQueueEntry
*)oqe
287 toState
:(NSString
*)state
288 error
:(NSError
* __autoreleasing
*)error
;
289 - (bool)_onqueueErrorOutgoingQueueEntry
:(CKKSOutgoingQueueEntry
*)oqe
290 itemError
:(NSError
*)itemError
291 error
:(NSError
* __autoreleasing
*)error
;
293 // Call this if you've done a write and received an error. It'll pull out any new records returned as CKErrorServerRecordChanged and pretend we received them in a fetch
295 // Note that you need to tell this function the records you wanted to save, so it can determine which record failed from its CKRecordID.
296 // I don't know why CKRecordIDs don't have record types, either.
297 - (bool)_onqueueCKWriteFailed
:(NSError
*)ckerror attemptedRecordsChanged
:(NSDictionary
<CKRecordID
*, CKRecord
*>*)savedRecords
;
299 - (bool)_onqueueCKRecordChanged
:(CKRecord
*)record resync
:(bool)resync
;
300 - (bool)_onqueueCKRecordDeleted
:(CKRecordID
*)recordID recordType
:(NSString
*)recordType resync
:(bool)resync
;
302 // For this key, who doesn't yet have a CKKSTLKShare for it, shared to their current Octagon keys?
303 // Note that we really want a record sharing the TLK to ourselves, so this function might return
304 // a non-empty set even if all peers have the TLK: it wants us to make a record for ourself.
305 // If you pass in a non-empty set in afterUploading, those records will be included in the calculation.
306 - (NSSet
<id
<CKKSPeer
>>*)_onqueueFindPeers
:(CKKSPeerProviderState
*)trustState
307 missingShare
:(CKKSKey
*)key
308 afterUploading
:(NSSet
<CKKSTLKShareRecord
*>* _Nullable
)newShares
309 error
:(NSError
* __autoreleasing
*)error
;
311 - (BOOL
)_onqueueAreNewSharesSufficient
:(NSSet
<CKKSTLKShareRecord
*>*)newShares
312 currentTLK
:(CKKSKey
*)key
313 error
:(NSError
* __autoreleasing
*)error
;
315 // For this key, share it to all trusted peers who don't have it yet
316 - (NSSet
<CKKSTLKShareRecord
*>* _Nullable
)_onqueueCreateMissingKeyShares
:(CKKSKey
*)key error
:(NSError
* __autoreleasing
*)error
;
318 - (bool)_onqueueUpdateLatestManifestWithError
:(NSError
**)error
;
320 - (CKKSDeviceStateEntry
* _Nullable
)_onqueueCurrentDeviceStateEntry
:(NSError
* __autoreleasing
*)error
;
322 // Please don't use these unless you're an Operation in this package
323 @property NSHashTable
<CKKSIncomingQueueOperation
*>* incomingQueueOperations
;
324 @property NSHashTable
<CKKSOutgoingQueueOperation
*>* outgoingQueueOperations
;
325 @property CKKSScanLocalItemsOperation
* initialScanOperation
;
327 // Returns the current state of this view, fastStatus is the same, but as name promise, no expensive calculations
328 - (NSDictionary
<NSString
*, NSString
*>*)status
;
329 - (NSDictionary
<NSString
*, NSString
*>*)fastStatus
;
332 NS_ASSUME_NONNULL_END
334 #import <Foundation/Foundation.h>
335 @interface CKKSKeychainView
: NSObject
337 NSString
* _containerName
;