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) {
232 - (void)updateOctagonKeySetWithAccount:(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-sos", "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 CFDataRef signingPublicKey = CFBridgingRetain(currentSelfPeer.publicSigningKey.keyData);
243 __block CFDataRef encryptionPublicKey = CFBridgingRetain(currentSelfPeer.publicEncryptionKey.keyData);
244 __block SecKeyRef octagonSigningPubSecKey = CFRetainSafe(currentSelfPeer.publicSigningKey.secKey);
245 __block SecKeyRef octagonEncryptionPubSecKey = CFRetainSafe(currentSelfPeer.publicEncryptionKey.secKey);
247 SFAnalyticsActivityTracker *tracker = [[[CKKSAnalytics class] logger] startLogSystemMetricsForActivityNamed:OctagonSOSAdapterUpdateKeys];
249 /* WARNING! Please be very very careful passing keys to this routine. Note the slightly different variations of keys*/
250 SOSCCPerformUpdateOfAllOctagonKeys(signingFullKey, encryptionFullKey,
251 signingPublicKey, encryptionPublicKey,
252 octagonSigningPubSecKey, octagonEncryptionPubSecKey,
253 ^(CFErrorRef error) {
254 [tracker stopWithEvent: OctagonSOSAdapterUpdateKeys result:(__bridge NSError * _Nullable)(error)];
256 secerror("octagon-sos: failed to update Octagon keys in SOS:%@", error);
258 secnotice("octagon-sos", "successfully updated Octagon keys in SOS!");
260 CFRelease(signingFullKey);
261 CFRelease(encryptionFullKey);
262 CFRelease(signingPublicKey);
263 CFRelease(encryptionPublicKey);
264 CFRelease(octagonSigningPubSecKey);
265 CFRelease(octagonEncryptionPubSecKey);
269 - (void)registerForPeerChangeUpdates:(nonnull id<CKKSPeerUpdateListener>)listener {
270 [self.peerChangeListeners registerListener:listener];
273 - (void)sendSelfPeerChangedUpdate {
274 [self.peerChangeListeners iterateListeners: ^(id<CKKSPeerUpdateListener> listener) {
275 [listener selfPeerChanged: self];
279 - (void)sendTrustedPeerSetChangedUpdate {
280 [self.peerChangeListeners iterateListeners: ^(id<CKKSPeerUpdateListener> listener) {
281 [listener trustedPeerSetChanged: self];
285 - (nonnull CKKSPeerProviderState *)currentState {
286 __block CKKSPeerProviderState* result = nil;
288 [SOSAccount performOnQuietAccountQueue: ^{
289 NSError* selfPeersError = nil;
290 CKKSSelves* currentSelfPeers = [self fetchSelfPeers:&selfPeersError];
292 NSError* trustedPeersError = nil;
293 NSSet<id<CKKSRemotePeerProtocol>>* currentTrustedPeers = [self fetchTrustedPeers:&trustedPeersError];
295 result = [[CKKSPeerProviderState alloc] initWithPeerProviderID:self.providerID
296 essential:self.essential
297 selfPeers:currentSelfPeers
298 selfPeersError:selfPeersError
299 trustedPeers:currentTrustedPeers
300 trustedPeersError:trustedPeersError];
307 @implementation OTSOSMissingAdapter
308 @synthesize sosEnabled;
309 @synthesize providerID = _providerID;
310 @synthesize essential = _essential;
312 - (instancetype)init {
313 if((self = [super init])) {
314 self.sosEnabled = false;
315 _providerID = @"[OTSOSMissingAdapter]";
317 // This adapter is never going to return anything, so you probably shouldn't ever consider it Must Succeed
323 - (SOSCCStatus)circleStatus:(NSError**)error
325 return kSOSCCNotInCircle;
328 - (id<CKKSSelfPeer> _Nullable)currentSOSSelf:(NSError**)error
331 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
332 code:errSecUnimplemented
333 description:@"SOS unsupported on this platform"];
338 - (NSSet<id<CKKSRemotePeerProtocol>>* _Nullable)fetchTrustedPeers:(NSError**)error
341 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
342 code:errSecUnimplemented
343 description:@"SOS unsupported on this platform"];
348 - (void)updateOctagonKeySetWithAccount:(nonnull id<CKKSSelfPeer>)currentSelfPeer error:(NSError *__autoreleasing _Nullable * _Nullable)error {
350 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
351 code:errSecUnimplemented
352 description:@"SOS unsupported on this platform"];
356 - (CKKSSelves * _Nullable)fetchSelfPeers:(NSError * _Nullable __autoreleasing * _Nullable)error
359 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
360 code:errSecUnimplemented
361 description:@"SOS unsupported on this platform"];
366 - (void)registerForPeerChangeUpdates:(nonnull id<CKKSPeerUpdateListener>)listener
371 - (void)sendSelfPeerChangedUpdate
376 - (void)sendTrustedPeerSetChangedUpdate
381 - (nonnull CKKSPeerProviderState *)currentState {
382 NSError* unimplementedError = [NSError errorWithDomain:NSOSStatusErrorDomain
383 code:errSecUnimplemented
384 description:@"SOS unsupported on this platform"];
385 return [[CKKSPeerProviderState alloc] initWithPeerProviderID:self.providerID
386 essential:self.essential
388 selfPeersError:unimplementedError
390 trustedPeersError:unimplementedError];
395 @implementation OTSOSAdapterHelpers
397 + (NSArray<NSData*>*)peerPublicSigningKeySPKIs:(NSSet<id<CKKSPeer>>* _Nullable)peerSet
399 NSMutableArray<NSData*>* publicSigningSPKIs = [NSMutableArray array];
401 for(id<CKKSPeer> peer in peerSet) {
402 NSData* spki = [peer.publicSigningKey encodeSubjectPublicKeyInfo];
404 secerror("octagon-sos: Can't create SPKI for peer: %@", peer);
406 secerror("octagon-sos: Created SPKI for peer: %@", peer);
407 [publicSigningSPKIs addObject:spki];
410 return publicSigningSPKIs;
413 + (NSArray<NSData*>* _Nullable)peerPublicSigningKeySPKIsForCircle:(id<OTSOSAdapter>)sosAdapter error:(NSError**)error
415 NSError* peerError = nil;
416 SOSCCStatus sosStatus = [sosAdapter circleStatus:&peerError];
418 if(sosStatus != kSOSCCInCircle || peerError) {
419 secerror("octagon-sos: Not in circle; not preapproving keys: %@ (%@)", SOSAccountGetSOSCCStatusString(sosStatus), peerError);
425 // We're in-circle: preapprove these peers
426 NSError* peerError = nil;
428 NSSet<id<CKKSRemotePeerProtocol>>* peerSet = [sosAdapter fetchTrustedPeers:&peerError];
430 if(!peerSet || peerError) {
431 secerror("octagon-sos: Can't fetch trusted peer SPKIs: %@", peerError);
438 return [self peerPublicSigningKeySPKIs:peerSet];