]> git.saurik.com Git - apple/security.git/blob - keychain/ot/OTSOSAdapter.m
Security-59306.11.20.tar.gz
[apple/security.git] / keychain / ot / OTSOSAdapter.m
1 #if OCTAGON
2 #import <dispatch/dispatch.h>
3 #import <notify.h>
4
5 #import "keychain/ot/OTSOSAdapter.h"
6
7 #import "keychain/SecureObjectSync/SOSCloudCircleInternal.h"
8 #include "keychain/SecureObjectSync/SOSViews.h"
9
10 #import "OSX/sec/securityd/SOSCloudCircleServer.h"
11 #import "OSX/utilities/SecCFWrappers.h"
12
13 #import "keychain/categories/NSError+UsefulConstructors.h"
14
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"
20
21 #import "keychain/SecureObjectSync/SOSAccountTrustClassic.h"
22 #import "keychain/ot/OTDefines.h"
23 #import "keychain/ot/OTConstants.h"
24 #import "keychain/ckks/CKKSAnalytics.h"
25
26 @interface OTSOSActualAdapter ()
27 @property CKKSListenerCollection* peerChangeListeners;
28 @end
29
30 @implementation OTSOSActualAdapter
31 @synthesize sosEnabled;
32 @synthesize essential = _essential;
33 @synthesize providerID = _providerID;
34
35 + (NSSet<NSString*>*)sosCKKSViewList
36 {
37 static NSSet<NSString*>* list = nil;
38 static dispatch_once_t onceToken;
39 dispatch_once(&onceToken, ^{
40 list = CFBridgingRelease(SOSViewCopyViewSet(kViewSetCKKS));
41 });
42 return list;
43 }
44
45 - (instancetype)initAsEssential:(BOOL)essential {
46 if((self = [super init])) {
47 self.sosEnabled = true;
48
49 _essential = essential;
50
51 _providerID = @"[OTSOSActualAdapter]";
52 _peerChangeListeners = [[CKKSListenerCollection alloc] initWithName:@"ckks-sos"];
53
54 __typeof(self) weakSelf = self;
55 // If this is a live server, register with notify
56 if(!SecCKKSTestsEnabled()) {
57 int token = 0;
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];
62 });
63 }
64 }
65 return self;
66 }
67
68 - (NSString*)description
69 {
70 return [NSString stringWithFormat:@"<OTSOSActualAdapter e:%d>", self.essential];
71 }
72
73 - (SOSCCStatus)circleStatus:(NSError**)error
74 {
75 CFErrorRef cferror = nil;
76 SOSCCStatus status = SOSCCThisDeviceIsInCircle(&cferror);
77 if(error && cferror) {
78 *error = CFBridgingRelease(cferror);
79 } else {
80 CFReleaseNull(cferror);
81 }
82 return status;
83 }
84
85 - (id<CKKSSelfPeer> _Nullable)currentSOSSelf:(NSError**)error
86 {
87 __block SFECKeyPair* signingPrivateKey = nil;
88 __block SFECKeyPair* encryptionPrivateKey = nil;
89
90 __block NSError* localerror = nil;
91
92 CFErrorRef cferror = nil;
93
94 SOSCCStatus circleStatus = [self circleStatus:&localerror];
95 if(circleStatus != kSOSCCInCircle) {
96 if(!localerror) {
97 localerror = [NSError errorWithDomain:CKKSErrorDomain
98 code:CKKSNoPeersAvailable
99 description:@"Not in SOS circle, but no error returned"];
100 }
101 secerror("octagon-sos: Not in circle : %d %@", circleStatus, localerror);
102 if(error) {
103 *error = localerror;
104 }
105 return nil;
106 }
107
108 SOSPeerInfoRef egoPeerInfo = SOSCCCopyMyPeerInfo(&cferror);
109 NSString* egoPeerID = egoPeerInfo ? (NSString*)CFBridgingRelease(CFRetainSafe(SOSPeerInfoGetPeerID(egoPeerInfo))) : nil;
110 CFReleaseNull(egoPeerInfo);
111
112 if(!egoPeerID || cferror) {
113 localerror = CFBridgingRelease(cferror);
114 if(!localerror) {
115 localerror = [NSError errorWithDomain:CKKSErrorDomain
116 code:CKKSNoPeersAvailable
117 description:@"No SOS peer info available, but no error returned"];
118 }
119
120 secerror("octagon-sos: Error fetching self peer : %@", cferror);
121 if(error) {
122 *error = localerror;
123 }
124 return nil;
125 }
126
127 SOSCCPerformWithAllOctagonKeys(^(SecKeyRef octagonEncryptionKey, SecKeyRef octagonSigningKey, CFErrorRef cferror) {
128 if(cferror) {
129 localerror = (__bridge NSError*)cferror;
130 return;
131 }
132 if (!cferror && octagonEncryptionKey && octagonSigningKey) {
133 signingPrivateKey = [[SFECKeyPair alloc] initWithSecKey:octagonSigningKey];
134 encryptionPrivateKey = [[SFECKeyPair alloc] initWithSecKey:octagonEncryptionKey];
135 } else {
136 localerror = [NSError errorWithDomain:CKKSErrorDomain
137 code:CKKSNoPeersAvailable
138 description:@"Not all SOS peer keys available, but no error returned"];
139 }
140 });
141
142 if(localerror) {
143 if(![[CKKSLockStateTracker globalTracker] isLockedError:localerror]) {
144 secerror("octagon-sos: Error fetching self encryption keys: %@", localerror);
145 }
146 if(error) {
147 *error = localerror;
148 }
149 return nil;
150 }
151
152 CKKSSOSSelfPeer* selfPeer = [[CKKSSOSSelfPeer alloc] initWithSOSPeerID:egoPeerID
153 encryptionKey:encryptionPrivateKey
154 signingKey:signingPrivateKey
155 viewList:[OTSOSActualAdapter sosCKKSViewList]];
156 return selfPeer;
157 }
158
159 - (CKKSSelves * _Nullable)fetchSelfPeers:(NSError *__autoreleasing _Nullable * _Nullable)error {
160 id<CKKSSelfPeer> peer = [self currentSOSSelf:error];
161 if(!peer) {
162 return nil;
163 }
164
165 return [[CKKSSelves alloc] initWithCurrent:peer allSelves:nil];
166 }
167
168 - (NSSet<id<CKKSRemotePeerProtocol>>* _Nullable)fetchTrustedPeers:(NSError**)error
169 {
170 __block NSMutableSet<id<CKKSRemotePeerProtocol>>* peerSet = [NSMutableSet set];
171
172 __block NSError* localError = nil;
173
174 SOSCCPerformWithTrustedPeers(^(CFSetRef sosPeerInfoRefs, CFErrorRef cfTrustedPeersError) {
175 if(cfTrustedPeersError) {
176 secerror("octagon-sos: Error fetching trusted peers: %@", cfTrustedPeersError);
177 if(localError) {
178 localError = (__bridge NSError*)cfTrustedPeersError;
179 }
180 }
181
182 CFSetForEach(sosPeerInfoRefs, ^(const void* voidPeer) {
183 CFErrorRef cfPeerError = NULL;
184 SOSPeerInfoRef sosPeerInfoRef = (SOSPeerInfoRef)voidPeer;
185
186 if(!sosPeerInfoRef) {
187 return;
188 }
189
190 CFStringRef cfpeerID = SOSPeerInfoGetPeerID(sosPeerInfoRef);
191 SecKeyRef cfOctagonSigningKey = NULL, cfOctagonEncryptionKey = NULL;
192
193 cfOctagonSigningKey = SOSPeerInfoCopyOctagonSigningPublicKey(sosPeerInfoRef, &cfPeerError);
194 if (cfOctagonSigningKey) {
195 cfOctagonEncryptionKey = SOSPeerInfoCopyOctagonEncryptionPublicKey(sosPeerInfoRef, &cfPeerError);
196 }
197
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)))
202 {
203 secerror("octagon-sos: error fetching octagon keys for peer: %@ %@", sosPeerInfoRef, cfPeerError);
204 } else {
205 secinfo("octagon-sos", "Peer(%@) doesn't have Octagon keys, but this is expected: %@", cfpeerID, cfPeerError);
206 }
207 }
208
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;
212
213 CKKSSOSPeer* peer = [[CKKSSOSPeer alloc] initWithSOSPeerID:(__bridge NSString*)cfpeerID
214 encryptionPublicKey:encryptionPublicKey
215 signingPublicKey:signingPublicKey
216 viewList:[OTSOSActualAdapter sosCKKSViewList]];
217 [peerSet addObject:peer];
218
219 CFReleaseNull(cfOctagonSigningKey);
220 CFReleaseNull(cfOctagonEncryptionKey);
221 CFReleaseNull(cfPeerError);
222 });
223 });
224
225 if(error && localError) {
226 *error = localError;
227 }
228
229 return peerSet;
230 }
231
232 - (void)updateOctagonKeySetWithAccount:(id<CKKSSelfPeer>)currentSelfPeer error:(NSError**)error {
233
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");
237 return;
238 }
239
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);
246
247 SFAnalyticsActivityTracker *tracker = [[[CKKSAnalytics class] logger] startLogSystemMetricsForActivityNamed:OctagonSOSAdapterUpdateKeys];
248
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)];
255 if(error){
256 secerror("octagon-sos: failed to update Octagon keys in SOS:%@", error);
257 }else {
258 secnotice("octagon-sos", "successfully updated Octagon keys in SOS!");
259 }
260 CFRelease(signingFullKey);
261 CFRelease(encryptionFullKey);
262 CFRelease(signingPublicKey);
263 CFRelease(encryptionPublicKey);
264 CFRelease(octagonSigningPubSecKey);
265 CFRelease(octagonEncryptionPubSecKey);
266 });
267 }
268
269 - (void)registerForPeerChangeUpdates:(nonnull id<CKKSPeerUpdateListener>)listener {
270 [self.peerChangeListeners registerListener:listener];
271 }
272
273 - (void)sendSelfPeerChangedUpdate {
274 [self.peerChangeListeners iterateListeners: ^(id<CKKSPeerUpdateListener> listener) {
275 [listener selfPeerChanged: self];
276 }];
277 }
278
279 - (void)sendTrustedPeerSetChangedUpdate {
280 [self.peerChangeListeners iterateListeners: ^(id<CKKSPeerUpdateListener> listener) {
281 [listener trustedPeerSetChanged: self];
282 }];
283 }
284
285 + (NSArray<NSData*>*)peerPublicSigningKeySPKIs:(NSSet<id<CKKSPeer>>* _Nullable)peerSet
286 {
287 NSMutableArray<NSData*>* publicSigningSPKIs = [NSMutableArray array];
288
289 for(id<CKKSPeer> peer in peerSet) {
290 NSData* spki = [peer.publicSigningKey encodeSubjectPublicKeyInfo];
291 if(!spki) {
292 secerror("octagon-sos: Can't create SPKI for peer: %@", peer);
293 } else {
294 secerror("octagon-sos: Created SPKI for peer: %@", peer);
295 [publicSigningSPKIs addObject:spki];
296 }
297 }
298 return publicSigningSPKIs;
299 }
300 @end
301
302 @implementation OTSOSMissingAdapter
303 @synthesize sosEnabled;
304 @synthesize providerID = _providerID;
305 @synthesize essential = _essential;
306
307 - (instancetype)init {
308 if((self = [super init])) {
309 self.sosEnabled = false;
310 _providerID = @"[OTSOSMissingAdapter]";
311
312 // This adapter is never going to return anything, so you probably shouldn't ever consider it Must Succeed
313 _essential = NO;
314 }
315 return self;
316 }
317
318 - (SOSCCStatus)circleStatus:(NSError**)error
319 {
320 return kSOSCCNotInCircle;
321 }
322
323 - (id<CKKSSelfPeer> _Nullable)currentSOSSelf:(NSError**)error
324 {
325 if(error) {
326 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
327 code:errSecUnimplemented
328 description:@"SOS unsupported on this platform"];
329 }
330 return nil;
331 }
332
333 - (NSSet<id<CKKSRemotePeerProtocol>>* _Nullable)fetchTrustedPeers:(NSError**)error
334 {
335 if(error) {
336 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
337 code:errSecUnimplemented
338 description:@"SOS unsupported on this platform"];
339 }
340 return nil;
341 }
342
343 - (void)updateOctagonKeySetWithAccount:(nonnull id<CKKSSelfPeer>)currentSelfPeer error:(NSError *__autoreleasing _Nullable * _Nullable)error {
344 if(error) {
345 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
346 code:errSecUnimplemented
347 description:@"SOS unsupported on this platform"];
348 }
349 }
350
351 - (CKKSSelves * _Nullable)fetchSelfPeers:(NSError * _Nullable __autoreleasing * _Nullable)error
352 {
353 if(error) {
354 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
355 code:errSecUnimplemented
356 description:@"SOS unsupported on this platform"];
357 }
358 return nil;
359 }
360
361 - (void)registerForPeerChangeUpdates:(nonnull id<CKKSPeerUpdateListener>)listener
362 {
363 // no op
364 }
365
366 - (void)sendSelfPeerChangedUpdate
367 {
368 // no op
369 }
370
371 - (void)sendTrustedPeerSetChangedUpdate
372 {
373 // no op
374 }
375
376
377 @end
378 #endif // OCTAGON