]> git.saurik.com Git - apple/security.git/blob - KeychainCircle/PairingChannel.m
Security-58286.220.15.tar.gz
[apple/security.git] / KeychainCircle / PairingChannel.m
1 //
2 // Security
3 //
4
5 #import "PairingChannel.h"
6 #import <Foundation/NSXPCConnection_Private.h>
7 #import <CoreFoundation/CoreFoundation.h>
8 #import <CoreFoundation/CFPropertyList_Private.h>
9 #import <Security/Security.h>
10 #import <Security/SecItemPriv.h>
11 #import <Security/SecureObjectSync/SOSTypes.h>
12 #import <utilities/debugging.h>
13 #import <utilities/SecCFWrappers.h>
14 #import <ipc/securityd_client.h>
15
16 #import <compression.h>
17 #if TARGET_OS_EMBEDDED
18 #import <MobileGestalt.h>
19 #endif
20
21 #import "SecADWrapper.h"
22
23 typedef void(^KCPairingInternalCompletion)(BOOL complete, NSDictionary *outdict, NSError *error);
24 typedef void(^KCNextState)(NSDictionary *indict, KCPairingInternalCompletion complete);
25
26 NSString *kKCPairingChannelErrorDomain = @"com.apple.security.kcparingchannel";
27
28 @implementation KCPairingChannelContext
29 @end
30
31
32 @interface KCPairingChannel ()
33 @property (assign) KCPairingChannelContext *peerVersionContext;
34 @property (assign) bool initator;
35 @property (assign) unsigned counter;
36 @property (assign) bool acceptorWillSendInitialSyncCredentials;
37 @property (strong) NSXPCConnection *connection;
38
39 @property (strong) KCNextState nextState;
40 @end
41
42
43 @implementation KCPairingChannel
44
45 + (instancetype)pairingChannelInitiator:(KCPairingChannelContext *)peerVersionContext
46 {
47 return [[KCPairingChannel alloc] initAsInitiator:true version:peerVersionContext];
48 }
49
50 + (instancetype)pairingChannelAcceptor:(KCPairingChannelContext *)peerVersionContext
51 {
52 return [[KCPairingChannel alloc] initAsInitiator:false version:peerVersionContext];
53 }
54
55 - (instancetype)initAsInitiator:(bool)initator version:(KCPairingChannelContext *)peerVersionContext
56 {
57 if (![KCPairingChannel isSupportedPlatform])
58 return NULL;
59
60 if (self = [super init]) {
61 __weak typeof(self) weakSelf = self;
62 _initator = initator;
63 _peerVersionContext = peerVersionContext;
64 if (_initator) {
65 _nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
66 [weakSelf initatorFirstPacket:nsdata complete:kscomplete];
67 };
68 } else {
69 _nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
70 [weakSelf acceptorFirstPacket:nsdata complete:kscomplete];
71 };
72 }
73 _needInitialSync = true;
74 }
75 return self;
76 }
77
78 + (bool)isSupportedPlatform
79 {
80 CFStringRef deviceClass = NULL;
81 #if TARGET_OS_EMBEDDED && !RC_HIDE_HARDWARE_WINTER_2017_IOS
82 deviceClass = MGCopyAnswer(kMGQDeviceClass, NULL);
83 if (deviceClass && CFEqual(deviceClass, kMGDeviceClassAudioAccessory)){
84 CFReleaseNull(deviceClass);
85 return false;
86 }
87 #endif
88 CFReleaseNull(deviceClass);
89 return true;
90 }
91
92 - (void)oneStepTooMany:(NSDictionary * __unused)indata complete:(KCPairingInternalCompletion)complete
93 {
94 secerror("pairingchannel: one step too many");
95 complete(false, NULL, [NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorTooManySteps userInfo:NULL]);
96 }
97
98 - (void)setNextStateError:(NSError *)error complete:(KCPairingInternalCompletion)complete
99 {
100 __weak typeof(self) weakSelf = self;
101 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
102 [weakSelf oneStepTooMany:nsdata complete:kscomplete];
103 };
104 if (complete) {
105 if (error)
106 secerror("pairingchannel: failed pairing with: %@", error);
107 complete(false, NULL, error);
108 }
109 }
110
111 //MARK: - Compression
112
113 const compression_algorithm pairingCompression = COMPRESSION_LZFSE;
114 #define EXTRA_SIZE 100
115
116 - (NSData *)compressData:(NSData *)data
117 {
118 NSMutableData *scratch = [NSMutableData dataWithLength:compression_encode_scratch_buffer_size(pairingCompression)];
119
120 NSUInteger outLength = [data length];
121 if (outLength > NSUIntegerMax - EXTRA_SIZE)
122 return nil;
123 outLength += EXTRA_SIZE;
124
125 NSMutableData *o = [NSMutableData dataWithLength:outLength];
126 size_t result = compression_encode_buffer([o mutableBytes], [o length], [data bytes], [data length], [scratch mutableBytes], pairingCompression);
127 if (result == 0)
128 return nil;
129
130 [o setLength:result];
131
132 return o;
133 }
134
135 - (NSData *)decompressData:(NSData *)data
136 {
137 NSMutableData *scratch = [NSMutableData dataWithLength:compression_decode_scratch_buffer_size(pairingCompression)];
138
139 size_t outLength = [data length];
140 size_t result;
141 NSMutableData *o = NULL;
142
143 do {
144 size_t size;
145 if (__builtin_umull_overflow(outLength, 2, &size))
146 return nil;
147 outLength = size;
148 o = [NSMutableData dataWithLength:outLength];
149
150 result = compression_decode_buffer([o mutableBytes], outLength, [data bytes], [data length], [scratch mutableBytes], pairingCompression);
151 if (result == 0)
152 return nil;
153 } while(result == outLength);
154
155 [o setLength:result];
156
157 return o;
158 }
159
160
161
162 //MARK: - Initiator
163
164 - (void)initatorFirstPacket:(NSDictionary * __unused)indata complete:(KCPairingInternalCompletion)complete
165 {
166 secnotice("pairing", "initator packet 1");
167
168 if (![self ensureControlChannel]) {
169 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorNoControlChannel userInfo:NULL] complete:complete];
170 return;
171 }
172
173 __weak typeof(self) weakSelf = self;
174 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
175 [weakSelf initatorSecondPacket:nsdata complete:kscomplete];
176 };
177 complete(false, @{ @"d" : @YES }, NULL);
178 }
179
180 - (void)initatorSecondPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
181 {
182 secnotice("pairing", "initator packet 2");
183
184 NSData *credential = indata[@"c"];
185 if (credential == NULL) {
186 secnotice("pairing", "no credential");
187 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorAccountCredentialMissing userInfo:NULL] complete:complete];
188 return;
189 }
190
191 if (indata[@"d"]) {
192 secnotice("pairing", "acceptor will send send initial credentials");
193 self.acceptorWillSendInitialSyncCredentials = true;
194 }
195
196 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
197 complete(true, NULL, error);
198 }] stashAccountCredential:credential complete:^(bool success, NSError *error) {
199 [self setNextStateError:NULL complete:NULL];
200 if (!success) {
201 secnotice("pairing", "failed stash credentials: %@", error);
202 complete(true, NULL, error);
203 return;
204 }
205 [self initatorCompleteSecondPacket:complete];
206 }];
207 }
208
209 - (void)initatorCompleteSecondPacket:(KCPairingInternalCompletion)complete
210 {
211 __weak typeof(self) weakSelf = self;
212 secnotice("pairing", "initator complete second packet 2");
213
214 [self setNextStateError:NULL complete:NULL];
215
216 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
217 complete(true, NULL, error);
218 }] myPeerInfo:^(NSData *application, NSError *error) {
219 if (application) {
220 complete(false, @{ @"p" : application }, error);
221
222 weakSelf.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
223 [weakSelf initatorThirdPacket:nsdata complete:kscomplete];
224 };
225
226 } else {
227 secnotice("pairing", "failed getting application: %@", error);
228 complete(true, @{}, error);
229 }
230 }];
231 }
232
233 - (void)initatorThirdPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
234 {
235 __weak typeof(self) weakSelf = self;
236 secnotice("pairing", "initator packet 3");
237
238 [self setNextStateError:NULL complete:NULL];
239
240 NSData *circleBlob = indata[@"b"];
241
242 if (circleBlob == NULL) {
243 complete(true, NULL, NULL);
244 return;
245 }
246
247 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
248 complete(true, NULL, error);
249 }] joinCircleWithBlob:circleBlob version:kPiggyV1 complete:^(bool success, NSError *error){
250 typeof(self) strongSelf = weakSelf;
251 secnotice("pairing", "initator cirle join complete, more data: %s: %@",
252 strongSelf->_acceptorWillSendInitialSyncCredentials ? "yes" : "no", error);
253
254 if (strongSelf->_acceptorWillSendInitialSyncCredentials) {
255 strongSelf.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
256 [weakSelf initatorFourthPacket:nsdata complete:kscomplete];
257 };
258
259 complete(false, @{}, NULL);
260 } else {
261 complete(true, NULL, NULL);
262 }
263 }];
264 }
265
266 - (void)initatorFourthPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
267 {
268 secnotice("pairing", "initator packet 4");
269
270 [self setNextStateError:NULL complete:NULL];
271
272 NSArray *items = indata[@"d"];
273 if (![items isKindOfClass:[NSArray class]]) {
274 secnotice("pairing", "initator no items to import");
275 complete(true, NULL, NULL);
276 return;
277 }
278
279 secnotice("pairing", "importing %lu items", (unsigned long)[items count]);
280
281 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
282 complete(true, NULL, error);
283 }] importInitialSyncCredentials:items complete:^(bool success, NSError *error) {
284 secnotice("pairing", "initator importInitialSyncCredentials: %s: %@", success ? "yes" : "no", error);
285 if (success)
286 self->_needInitialSync = false;
287 complete(true, NULL, NULL);
288 }];
289 }
290
291
292
293 //MARK: - Acceptor
294
295 - (void)acceptorFirstPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
296 {
297 __weak typeof(self) weakSelf = self;
298
299 secnotice("pairing", "acceptor packet 1");
300
301 [self setNextStateError:NULL complete:NULL];
302
303 if (![self ensureControlChannel]) {
304 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorNoControlChannel userInfo:NULL] complete:complete];
305 return;
306 }
307
308 if (indata[@"d"]) {
309 secnotice("pairing", "acceptor initialSyncCredentials requested");
310 self.acceptorWillSendInitialSyncCredentials = true;
311 }
312
313 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
314 complete(true, NULL, error);
315 }] validatedStashedAccountCredential:^(NSData *credential, NSError *error) {
316 if (!credential) {
317 secnotice("pairing", "acceptor doesn't have a stashed credential: %@", error);
318 [self setNextStateError:error complete:complete];
319 return;
320 }
321
322 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
323 [weakSelf acceptorSecondPacket:nsdata complete:kscomplete];
324 };
325
326 NSMutableDictionary *reply = [@{
327 @"c" : credential,
328 } mutableCopy];
329
330 if (self.acceptorWillSendInitialSyncCredentials) {
331 reply[@"d"] = @YES;
332 };
333
334 secnotice("pairing", "acceptor reply to packet 1");
335 complete(false, reply, NULL);
336 }];
337 }
338
339 - (void)acceptorSecondPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
340 {
341 __weak typeof(self) weakSelf = self;
342
343 [self setNextStateError:NULL complete:NULL];
344
345 secnotice("pairing", "acceptor packet 2");
346
347 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
348 complete(true, NULL, error);
349 }] circleJoiningBlob:indata[@"p"] complete:^(NSData *blob, NSError *error){
350 NSMutableDictionary *reply = [NSMutableDictionary dictionary];
351
352 if (blob) {
353 secnotice("pairing", "acceptor pairing complete (will send: %s): %@",
354 self.acceptorWillSendInitialSyncCredentials ? "YES" : "NO",
355 error);
356
357 reply[@"b"] = blob;
358
359 if (self.acceptorWillSendInitialSyncCredentials) {
360 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
361 [weakSelf acceptorThirdPacket:nsdata complete:kscomplete];
362 };
363
364 complete(false, reply, NULL);
365 } else {
366 complete(true, reply, NULL);
367 }
368 } else {
369 complete(true, reply, error);
370 }
371 secnotice("pairing", "acceptor reply to packet 2");
372 }];
373 }
374
375 - (void)acceptorThirdPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
376 {
377 secnotice("pairing", "acceptor packet 3");
378
379 const uint32_t initialSyncCredentialsFlags =
380 SOSControlInitialSyncFlagTLK|
381 SOSControlInitialSyncFlagPCS|
382 SOSControlInitialSyncFlagBluetoothMigration;
383
384 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
385 complete(true, NULL, error);
386 }] initialSyncCredentials:initialSyncCredentialsFlags complete:^(NSArray *items, NSError *error2) {
387 NSMutableDictionary *reply = [NSMutableDictionary dictionary];
388
389 secnotice("pairing", "acceptor initialSyncCredentials complete: items %u: %@", (unsigned)[items count], error2);
390 if (items) {
391 reply[@"d"] = items;
392 }
393 secnotice("pairing", "acceptor reply to packet 3");
394 complete(true, reply, NULL);
395 }];
396 }
397
398
399 //MARK: - Helper
400
401 - (bool)ensureControlChannel
402 {
403 if (self.connection)
404 return true;
405
406 NSXPCInterface *interface = [NSXPCInterface interfaceWithProtocol:@protocol(SOSControlProtocol)];
407
408 self.connection = [[NSXPCConnection alloc] initWithMachServiceName:@(kSecuritydSOSServiceName) options:0];
409 if (self.connection == NULL)
410 return false;
411
412 self.connection.remoteObjectInterface = interface;
413
414 [self.connection resume];
415
416 return true;
417 }
418
419 - (void)validateStart:(void(^)(bool result, NSError *error))complete
420 {
421 if (!self.initator) {
422 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
423 complete(false, error);
424 }] stashedCredentialPublicKey:^(NSData *publicKey, NSError *error) {
425 complete(publicKey != NULL, error);
426 }];
427 } else {
428 dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
429 complete(true, NULL);
430 });
431 }
432 }
433
434 - (void)exchangePacket:(NSData *)inputCompressedData complete:(KCPairingChannelCompletion)complete
435 {
436 NSDictionary *indict = NULL;
437
438 if (inputCompressedData) {
439
440 NSData *data = [self decompressData:inputCompressedData];
441 if (data == NULL) {
442 secnotice("pairing", "failed to decompress");
443 complete(true, NULL, NULL);
444 }
445
446 NSError *error = NULL;
447 indict = [NSPropertyListSerialization propertyListWithData:data
448 options:(NSPropertyListReadOptions)kCFPropertyListSupportedFormatBinary_v1_0
449 format:NULL
450 error:&error];
451 if (indict == NULL) {
452 secnotice("pairing", "failed to deserialize");
453 complete(true, NULL, error);
454 return;
455 }
456 }
457 self.nextState(indict, ^(BOOL completed, NSDictionary *outdict, NSError *error) {
458 NSData *outdata = NULL, *compressedData = NULL;
459 if (outdict) {
460 NSError *error2 = NULL;
461 outdata = [NSPropertyListSerialization dataWithPropertyList:outdict format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error2];
462 if (outdata == NULL && error)
463 error = error2;
464 if (outdata)
465 compressedData = [self compressData:outdata];
466
467 if (compressedData) {
468 NSString *key = [NSString stringWithFormat:@"com.apple.ckks.pairing.packet-size.%s.%u",
469 self->_initator ? "initator" : "acceptor", self->_counter];
470 SecADClientPushValueForDistributionKey((__bridge CFStringRef)key, [compressedData length]);
471 secnotice("pairing", "pairing packet size %lu", (unsigned long)[compressedData length]);
472 }
473 }
474 complete(completed, compressedData, error);
475 });
476 }
477
478 - (NSData *)exchangePacket:(NSData *)data complete:(bool *)complete error:(NSError * __autoreleasing *)error
479 {
480 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
481 __block NSData *rData = NULL;
482 __block NSError* processingError;
483 [self exchangePacket:data complete:^(BOOL cComplete, NSData *cData, NSError *cError) {
484 self.counter++;
485 *complete = cComplete;
486 rData = cData;
487 processingError = cError;
488 dispatch_semaphore_signal(sema);
489 }];
490 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
491 if (error)
492 *error = processingError;
493 return rData;
494 }
495
496 - (void)setXPCConnectionObject:(NSXPCConnection *)connection
497 {
498 self.connection = connection;
499 }
500
501
502 @end