2 #import <dispatch/dispatch.h>
5 #import "keychain/ot/OTSOSAdapter.h"
7 #import "keychain/SecureObjectSync/SOSCloudCircleInternal.h"
8 #include "keychain/SecureObjectSync/SOSViews.h"
10 #import "keychain/securityd/SOSCloudCircleServer.h"
11 #import "OSX/utilities/SecCFWrappers.h"
13 #import "keychain/categories/NSError+UsefulConstructors.h"
15 #import "keychain/ckks/CKKS.h"
16 #import "keychain/ckks/CKKSLockStateTracker.h"
17 #import "keychain/ckks/CKKSListenerCollection.h"
18 #import "keychain/SecureObjectSync/SOSAccount.h"
19 #import "keychain/SecureObjectSync/SOSAccountPriv.h"
21 #import "keychain/SecureObjectSync/SOSAccountTrustClassic.h"
22 #import "keychain/ot/OTDefines.h"
23 #import "keychain/ot/OTConstants.h"
24 #import "keychain/ckks/CKKSAnalytics.h"
26 @interface OTSOSActualAdapter ()
27 @property CKKSListenerCollection* peerChangeListeners;
30 @implementation OTSOSActualAdapter
31 @synthesize sosEnabled;
32 @synthesize essential = _essential;
33 @synthesize providerID = _providerID;
35 + (NSSet<NSString*>*)sosCKKSViewList
37 static NSSet<NSString*>* list = nil;
38 static dispatch_once_t onceToken;
39 dispatch_once(&onceToken, ^{
40 list = CFBridgingRelease(SOSViewCopyViewSet(kViewSetCKKS));
45 - (instancetype)initAsEssential:(BOOL)essential {
46 if((self = [super init])) {
47 self.sosEnabled = true;
49 _essential = essential;
51 _providerID = @"[OTSOSActualAdapter]";
52 _peerChangeListeners = [[CKKSListenerCollection alloc] initWithName:@"ckks-sos"];
54 __typeof(self) weakSelf = self;
55 // If this is a live server, register with notify
56 if(!SecCKKSTestsEnabled()) {
58 notify_register_dispatch(kSOSCCCircleOctagonKeysChangedNotification, &token, dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^(int t) {
59 // Since SOS doesn't change the self peer, we can reliably just send "trusted peers changed"; it'll be mostly right
60 secnotice("octagon-sos", "Received a notification that the SOS Octagon peer set changed");
61 [weakSelf sendTrustedPeerSetChangedUpdate];
68 - (NSString*)description
70 return [NSString stringWithFormat:@"<OTSOSActualAdapter e:%d>", self.essential];
73 - (SOSCCStatus)circleStatus:(NSError**)error
75 CFErrorRef cferror = nil;
76 SOSCCStatus status = SOSCCThisDeviceIsInCircle(&cferror);
77 if(error && cferror) {
78 *error = CFBridgingRelease(cferror);
80 CFReleaseNull(cferror);
85 - (id<CKKSSelfPeer> _Nullable)currentSOSSelf:(NSError**)error
87 __block SFECKeyPair* signingPrivateKey = nil;
88 __block SFECKeyPair* encryptionPrivateKey = nil;
90 __block NSError* localerror = nil;
92 CFErrorRef cferror = nil;
94 SOSCCStatus circleStatus = [self circleStatus:&localerror];
95 if(circleStatus != kSOSCCInCircle) {
97 localerror = [NSError errorWithDomain:CKKSErrorDomain
98 code:CKKSNoPeersAvailable
99 description:@"Not in SOS circle, but no error returned"];
101 secerror("octagon-sos: Not in circle : %@ %@", SOSAccountGetSOSCCStatusString(circleStatus), localerror);
108 SOSPeerInfoRef egoPeerInfo = SOSCCCopyMyPeerInfo(&cferror);
109 NSString* egoPeerID = egoPeerInfo ? (NSString*)CFBridgingRelease(CFRetainSafe(SOSPeerInfoGetPeerID(egoPeerInfo))) : nil;
110 CFReleaseNull(egoPeerInfo);
112 if(!egoPeerID || cferror) {
113 localerror = CFBridgingRelease(cferror);
115 localerror = [NSError errorWithDomain:CKKSErrorDomain
116 code:CKKSNoPeersAvailable
117 description:@"No SOS peer info available, but no error returned"];
120 secerror("octagon-sos: Error fetching self peer : %@", cferror);
127 SOSCCPerformWithAllOctagonKeys(^(SecKeyRef octagonEncryptionKey, SecKeyRef octagonSigningKey, CFErrorRef cferror) {
129 localerror = (__bridge NSError*)cferror;
132 if (!cferror && octagonEncryptionKey && octagonSigningKey) {
133 signingPrivateKey = [[SFECKeyPair alloc] initWithSecKey:octagonSigningKey];
134 encryptionPrivateKey = [[SFECKeyPair alloc] initWithSecKey:octagonEncryptionKey];
136 localerror = [NSError errorWithDomain:CKKSErrorDomain
137 code:CKKSNoPeersAvailable
138 description:@"Not all SOS peer keys available, but no error returned"];
143 if(![[CKKSLockStateTracker globalTracker] isLockedError:localerror]) {
144 secerror("octagon-sos: Error fetching self encryption keys: %@", localerror);
152 CKKSSOSSelfPeer* selfPeer = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:egoPeerID
153 encryptionKey:encryptionPrivateKey
154 signingKey:signingPrivateKey
155 viewList:[OTSOSActualAdapter sosCKKSViewList]];
159 - (CKKSSelves * _Nullable)fetchSelfPeers:(NSError *__autoreleasing _Nullable * _Nullable)error {
160 id<CKKSSelfPeer> peer = [self currentSOSSelf:error];
165 return [[CKKSSelves alloc] initWithCurrent:peer allSelves:nil];
168 - (NSSet<id<CKKSRemotePeerProtocol>>* _Nullable)fetchTrustedPeers:(NSError**)error
170 __block NSMutableSet<id<CKKSRemotePeerProtocol>>* peerSet = [NSMutableSet set];
172 __block NSError* localError = nil;
174 SOSCCPerformWithTrustedPeers(^(CFSetRef sosPeerInfoRefs, CFErrorRef cfTrustedPeersError) {
175 if(cfTrustedPeersError) {
176 secerror("octagon-sos: Error fetching trusted peers: %@", cfTrustedPeersError);
178 localError = (__bridge NSError*)cfTrustedPeersError;
182 CFSetForEach(sosPeerInfoRefs, ^(const void* voidPeer) {
183 CFErrorRef cfPeerError = NULL;
184 SOSPeerInfoRef sosPeerInfoRef = (SOSPeerInfoRef)voidPeer;
186 if(!sosPeerInfoRef) {
190 CFStringRef cfpeerID = SOSPeerInfoGetPeerID(sosPeerInfoRef);
191 SecKeyRef cfOctagonSigningKey = NULL, cfOctagonEncryptionKey = NULL;
193 cfOctagonSigningKey = SOSPeerInfoCopyOctagonSigningPublicKey(sosPeerInfoRef, &cfPeerError);
194 if (cfOctagonSigningKey) {
195 cfOctagonEncryptionKey = SOSPeerInfoCopyOctagonEncryptionPublicKey(sosPeerInfoRef, &cfPeerError);
198 if(cfOctagonSigningKey == NULL || cfOctagonEncryptionKey == NULL) {
199 // Don't log non-debug for -50; it almost always just means this peer didn't have octagon keys
200 if(cfPeerError == NULL
201 || !(CFEqualSafe(CFErrorGetDomain(cfPeerError), kCFErrorDomainOSStatus) && (CFErrorGetCode(cfPeerError) == errSecParam)))
203 secerror("octagon-sos: error fetching octagon keys for peer: %@ %@", sosPeerInfoRef, cfPeerError);
205 secinfo("octagon-sos", "Peer(%@) doesn't have Octagon keys, but this is expected: %@", cfpeerID, cfPeerError);
209 // Add all peers to the trust set: old-style SOS peers will just have null keys
210 SFECPublicKey* signingPublicKey = cfOctagonSigningKey ? [[SFECPublicKey alloc] initWithSecKey:cfOctagonSigningKey] : nil;
211 SFECPublicKey* encryptionPublicKey = cfOctagonEncryptionKey ? [[SFECPublicKey alloc] initWithSecKey:cfOctagonEncryptionKey] : nil;
213 CKKSSOSPeer* peer = [[CKKSSOSPeer alloc] initWithSOSPeerID:(__bridge NSString*)cfpeerID
214 encryptionPublicKey:encryptionPublicKey
215 signingPublicKey:signingPublicKey
216 viewList:[OTSOSActualAdapter sosCKKSViewList]];
217 [peerSet addObject:peer];
219 CFReleaseNull(cfOctagonSigningKey);
220 CFReleaseNull(cfOctagonEncryptionKey);
221 CFReleaseNull(cfPeerError);
225 if(error && localError) {
233 - (BOOL)preloadOctagonKeySetOnAccount:(id<CKKSSelfPeer>)currentSelfPeer error:(NSError**)error {
234 // in case we don't have the keys, don't try to update them
235 if (currentSelfPeer.publicSigningKey.secKey == NULL || currentSelfPeer.publicEncryptionKey.secKey == NULL) {
236 secnotice("octagon-preload-keys", "no octagon keys available skipping updating SOS record");
240 __block CFDataRef signingFullKey = CFBridgingRetain(currentSelfPeer.signingKey.keyData);
241 __block CFDataRef encryptionFullKey = CFBridgingRetain(currentSelfPeer.encryptionKey.keyData);
242 __block SecKeyRef octagonSigningPubSecKey = CFRetainSafe(currentSelfPeer.publicSigningKey.secKey);
243 __block SecKeyRef octagonEncryptionPubSecKey = CFRetainSafe(currentSelfPeer.publicEncryptionKey.secKey);
244 __block SecKeyRef signingFullKeyRef = CFRetainSafe(currentSelfPeer.signingKey.secKey);
245 __block SecKeyRef encryptionFullKeyRef = CFRetainSafe(currentSelfPeer.encryptionKey.secKey);
247 SFAnalyticsActivityTracker *tracker = [[[CKKSAnalytics class] logger] startLogSystemMetricsForActivityNamed:OctagonSOSAdapterUpdateKeys];
250 __block NSError *localError = nil;
252 /* WARNING! Please be very very careful passing keys to this routine. Note the slightly different variations of keys*/
253 SOSCCPerformPreloadOfAllOctagonKeys(signingFullKey, encryptionFullKey,
254 signingFullKeyRef, encryptionFullKeyRef,
255 octagonSigningPubSecKey, octagonEncryptionPubSecKey,
256 ^(CFErrorRef cferror) {
257 [tracker stopWithEvent: OctagonSOSAdapterUpdateKeys result:(__bridge NSError * _Nullable)(cferror)];
259 secerror("octagon-preload-keys: failed to preload Octagon keys in SOS:%@", cferror);
261 localError = (__bridge NSError *)cferror;
264 secnotice("octagon-preload-keys", "successfully preloaded Octagon keys in SOS!");
266 CFRelease(signingFullKey);
267 CFRelease(encryptionFullKey);
268 CFRelease(octagonSigningPubSecKey);
269 CFRelease(octagonEncryptionPubSecKey);
270 CFRelease(signingFullKeyRef);
271 CFRelease(encryptionFullKeyRef);
280 - (BOOL)updateOctagonKeySetWithAccount:(id<CKKSSelfPeer>)currentSelfPeer error:(NSError**)error {
282 // in case we don't have the keys, don't try to update them
283 if (currentSelfPeer.publicSigningKey.secKey == NULL || currentSelfPeer.publicEncryptionKey.secKey == NULL) {
284 secnotice("octagon-sos", "no octagon keys available skipping updating SOS record");
288 __block CFDataRef signingFullKey = CFBridgingRetain(currentSelfPeer.signingKey.keyData);
289 __block CFDataRef encryptionFullKey = CFBridgingRetain(currentSelfPeer.encryptionKey.keyData);
290 __block CFDataRef signingPublicKey = CFBridgingRetain(currentSelfPeer.publicSigningKey.keyData);
291 __block CFDataRef encryptionPublicKey = CFBridgingRetain(currentSelfPeer.publicEncryptionKey.keyData);
292 __block SecKeyRef octagonSigningPubSecKey = CFRetainSafe(currentSelfPeer.publicSigningKey.secKey);
293 __block SecKeyRef octagonEncryptionPubSecKey = CFRetainSafe(currentSelfPeer.publicEncryptionKey.secKey);
295 SFAnalyticsActivityTracker *tracker = [[[CKKSAnalytics class] logger] startLogSystemMetricsForActivityNamed:OctagonSOSAdapterUpdateKeys];
298 __block NSError *localError = nil;
300 /* WARNING! Please be very very careful passing keys to this routine. Note the slightly different variations of keys*/
301 SOSCCPerformUpdateOfAllOctagonKeys(signingFullKey, encryptionFullKey,
302 signingPublicKey, encryptionPublicKey,
303 octagonSigningPubSecKey, octagonEncryptionPubSecKey,
304 ^(CFErrorRef cferror) {
305 [tracker stopWithEvent: OctagonSOSAdapterUpdateKeys result:(__bridge NSError * _Nullable)(cferror)];
307 secerror("octagon-sos: failed to update Octagon keys in SOS:%@", cferror);
309 localError = (__bridge NSError *)cferror;
312 secnotice("octagon-sos", "successfully updated Octagon keys in SOS!");
314 CFRelease(signingFullKey);
315 CFRelease(encryptionFullKey);
316 CFRelease(signingPublicKey);
317 CFRelease(encryptionPublicKey);
318 CFRelease(octagonSigningPubSecKey);
319 CFRelease(octagonEncryptionPubSecKey);
327 - (BOOL)updateCKKS4AllStatus:(BOOL)status error:(NSError**)error
329 CFErrorRef cferror = nil;
330 bool result = SOSCCSetCKKS4AllStatus(status, &cferror);
332 NSError* localerror = CFBridgingRelease(cferror);
334 if(!result || localerror) {
335 secerror("octagon-sos: failed to update CKKS4All status in SOS: %@", localerror);
337 secnotice("octagon-sos", "successfully updated CKKS4All status in SOS to '%@'", status ? @"supported" : @"not supported");
340 if(error && localerror) {
346 - (void)registerForPeerChangeUpdates:(nonnull id<CKKSPeerUpdateListener>)listener {
347 [self.peerChangeListeners registerListener:listener];
350 - (void)sendSelfPeerChangedUpdate {
351 [self.peerChangeListeners iterateListeners: ^(id<CKKSPeerUpdateListener> listener) {
352 [listener selfPeerChanged: self];
356 - (void)sendTrustedPeerSetChangedUpdate {
357 [self.peerChangeListeners iterateListeners: ^(id<CKKSPeerUpdateListener> listener) {
358 [listener trustedPeerSetChanged: self];
362 - (nonnull CKKSPeerProviderState *)currentState {
363 __block CKKSPeerProviderState* result = nil;
365 [SOSAccount performOnQuietAccountQueue: ^{
366 NSError* selfPeersError = nil;
367 CKKSSelves* currentSelfPeers = [self fetchSelfPeers:&selfPeersError];
369 NSError* trustedPeersError = nil;
370 NSSet<id<CKKSRemotePeerProtocol>>* currentTrustedPeers = [self fetchTrustedPeers:&trustedPeersError];
372 result = [[CKKSPeerProviderState alloc] initWithPeerProviderID:self.providerID
373 essential:self.essential
374 selfPeers:currentSelfPeers
375 selfPeersError:selfPeersError
376 trustedPeers:currentTrustedPeers
377 trustedPeersError:trustedPeersError];
383 - (BOOL)safariViewSyncingEnabled:(NSError**)error
385 CFErrorRef viewCFError = NULL;
386 SOSViewResultCode result = SOSCCView(kSOSViewAutofillPasswords, kSOSCCViewQuery, &viewCFError);
390 *error = CFBridgingRelease(viewCFError);
392 CFReleaseNull(viewCFError);
397 return result == kSOSCCViewMember;
401 @implementation OTSOSMissingAdapter
402 @synthesize sosEnabled;
403 @synthesize providerID = _providerID;
404 @synthesize essential = _essential;
406 - (instancetype)init {
407 if((self = [super init])) {
408 self.sosEnabled = false;
409 _providerID = @"[OTSOSMissingAdapter]";
411 // This adapter is never going to return anything, so you probably shouldn't ever consider it Must Succeed
417 - (SOSCCStatus)circleStatus:(NSError**)error
419 return kSOSCCNotInCircle;
422 - (id<CKKSSelfPeer> _Nullable)currentSOSSelf:(NSError**)error
425 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
426 code:errSecUnimplemented
427 description:@"SOS unsupported on this platform"];
432 - (NSSet<id<CKKSRemotePeerProtocol>>* _Nullable)fetchTrustedPeers:(NSError**)error
435 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
436 code:errSecUnimplemented
437 description:@"SOS unsupported on this platform"];
442 - (BOOL)updateOctagonKeySetWithAccount:(nonnull id<CKKSSelfPeer>)currentSelfPeer error:(NSError *__autoreleasing _Nullable * _Nullable)error {
444 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
445 code:errSecUnimplemented
446 description:@"SOS unsupported on this platform"];
451 - (BOOL)updateCKKS4AllStatus:(BOOL)status error:(NSError**)error
454 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
455 code:errSecUnimplemented
456 description:@"SOS unsupported on this platform"];
461 - (CKKSSelves * _Nullable)fetchSelfPeers:(NSError * _Nullable __autoreleasing * _Nullable)error
464 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
465 code:errSecUnimplemented
466 description:@"SOS unsupported on this platform"];
471 - (void)registerForPeerChangeUpdates:(nonnull id<CKKSPeerUpdateListener>)listener
476 - (void)sendSelfPeerChangedUpdate
481 - (void)sendTrustedPeerSetChangedUpdate
486 - (nonnull CKKSPeerProviderState *)currentState {
487 NSError* unimplementedError = [NSError errorWithDomain:NSOSStatusErrorDomain
488 code:errSecUnimplemented
489 description:@"SOS unsupported on this platform"];
490 return [[CKKSPeerProviderState alloc] initWithPeerProviderID:self.providerID
491 essential:self.essential
493 selfPeersError:unimplementedError
495 trustedPeersError:unimplementedError];
498 - (BOOL)safariViewSyncingEnabled:(NSError**)error
503 - (BOOL)preloadOctagonKeySetOnAccount:(nonnull id<CKKSSelfPeer>)currentSelfPeer error:(NSError *__autoreleasing _Nullable * _Nullable)error {
505 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
506 code:errSecUnimplemented
507 description:@"SOS unsupported on this platform"];
514 @implementation OTSOSAdapterHelpers
516 + (NSArray<NSData*>*)peerPublicSigningKeySPKIs:(NSSet<id<CKKSPeer>>* _Nullable)peerSet
518 NSMutableArray<NSData*>* publicSigningSPKIs = [NSMutableArray array];
520 for(id<CKKSPeer> peer in peerSet) {
521 NSData* spki = [peer.publicSigningKey encodeSubjectPublicKeyInfo];
523 secerror("octagon-sos: Can't create SPKI for peer: %@", peer);
525 secerror("octagon-sos: Created SPKI for peer: %@", peer);
526 [publicSigningSPKIs addObject:spki];
529 return publicSigningSPKIs;
532 + (NSArray<NSData*>* _Nullable)peerPublicSigningKeySPKIsForCircle:(id<OTSOSAdapter>)sosAdapter error:(NSError**)error
534 NSError* peerError = nil;
535 SOSCCStatus sosStatus = [sosAdapter circleStatus:&peerError];
537 if(sosStatus != kSOSCCInCircle || peerError) {
538 secerror("octagon-sos: Not in circle; not preapproving keys: %@ (%@)", SOSAccountGetSOSCCStatusString(sosStatus), peerError);
544 // We're in-circle: preapprove these peers
545 NSError* peerError = nil;
547 NSSet<id<CKKSRemotePeerProtocol>>* peerSet = [sosAdapter fetchTrustedPeers:&peerError];
549 if(!peerSet || peerError) {
550 secerror("octagon-sos: Can't fetch trusted peer SPKIs: %@", peerError);
557 return [self peerPublicSigningKeySPKIs:peerSet];