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>
16 #import <compression.h>
17 #if TARGET_OS_EMBEDDED
18 #import <MobileGestalt.h>
21 #import "SecADWrapper.h"
23 typedef void(^KCPairingInternalCompletion)(BOOL complete, NSDictionary *outdict, NSError *error);
24 typedef void(^KCNextState)(NSDictionary *indict, KCPairingInternalCompletion complete);
26 NSString *kKCPairingChannelErrorDomain = @"com.apple.security.kcparingchannel";
28 @implementation KCPairingChannelContext
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;
39 @property (strong) KCNextState nextState;
43 @implementation KCPairingChannel
45 + (instancetype)pairingChannelInitiator:(KCPairingChannelContext *)peerVersionContext
47 return [[KCPairingChannel alloc] initAsInitiator:true version:peerVersionContext];
50 + (instancetype)pairingChannelAcceptor:(KCPairingChannelContext *)peerVersionContext
52 return [[KCPairingChannel alloc] initAsInitiator:false version:peerVersionContext];
55 - (instancetype)initAsInitiator:(bool)initator version:(KCPairingChannelContext *)peerVersionContext
57 if (![KCPairingChannel isSupportedPlatform])
60 if (self = [super init]) {
61 __weak typeof(self) weakSelf = self;
63 _peerVersionContext = peerVersionContext;
65 _nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
66 [weakSelf initatorFirstPacket:nsdata complete:kscomplete];
69 _nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
70 [weakSelf acceptorFirstPacket:nsdata complete:kscomplete];
73 _needInitialSync = true;
78 + (bool)isSupportedPlatform
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);
88 CFReleaseNull(deviceClass);
92 - (void)oneStepTooMany:(NSDictionary * __unused)indata complete:(KCPairingInternalCompletion)complete
94 secerror("pairingchannel: one step too many");
95 complete(false, NULL, [NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorTooManySteps userInfo:NULL]);
98 - (void)setNextStateError:(NSError *)error complete:(KCPairingInternalCompletion)complete
100 __weak typeof(self) weakSelf = self;
101 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
102 [weakSelf oneStepTooMany:nsdata complete:kscomplete];
106 secerror("pairingchannel: failed pairing with: %@", error);
107 complete(false, NULL, error);
111 //MARK: - Compression
113 const compression_algorithm pairingCompression = COMPRESSION_LZFSE;
114 #define EXTRA_SIZE 100
116 - (NSData *)compressData:(NSData *)data
118 NSMutableData *scratch = [NSMutableData dataWithLength:compression_encode_scratch_buffer_size(pairingCompression)];
120 NSUInteger outLength = [data length];
121 if (outLength > NSUIntegerMax - EXTRA_SIZE)
123 outLength += EXTRA_SIZE;
125 NSMutableData *o = [NSMutableData dataWithLength:outLength];
126 size_t result = compression_encode_buffer([o mutableBytes], [o length], [data bytes], [data length], [scratch mutableBytes], pairingCompression);
130 [o setLength:result];
135 - (NSData *)decompressData:(NSData *)data
137 NSMutableData *scratch = [NSMutableData dataWithLength:compression_decode_scratch_buffer_size(pairingCompression)];
139 size_t outLength = [data length];
141 NSMutableData *o = NULL;
145 if (__builtin_umull_overflow(outLength, 2, &size))
148 o = [NSMutableData dataWithLength:outLength];
150 result = compression_decode_buffer([o mutableBytes], outLength, [data bytes], [data length], [scratch mutableBytes], pairingCompression);
153 } while(result == outLength);
155 [o setLength:result];
164 - (void)initatorFirstPacket:(NSDictionary * __unused)indata complete:(KCPairingInternalCompletion)complete
166 secnotice("pairing", "initator packet 1");
168 if (![self ensureControlChannel]) {
169 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorNoControlChannel userInfo:NULL] complete:complete];
173 __weak typeof(self) weakSelf = self;
174 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
175 [weakSelf initatorSecondPacket:nsdata complete:kscomplete];
177 complete(false, @{ @"d" : @YES }, NULL);
180 - (void)initatorSecondPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
182 secnotice("pairing", "initator packet 2");
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];
192 secnotice("pairing", "acceptor will send send initial credentials");
193 self.acceptorWillSendInitialSyncCredentials = true;
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];
201 secnotice("pairing", "failed stash credentials: %@", error);
202 complete(true, NULL, error);
205 [self initatorCompleteSecondPacket:complete];
209 - (void)initatorCompleteSecondPacket:(KCPairingInternalCompletion)complete
211 __weak typeof(self) weakSelf = self;
212 secnotice("pairing", "initator complete second packet 2");
214 [self setNextStateError:NULL complete:NULL];
216 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
217 complete(true, NULL, error);
218 }] myPeerInfo:^(NSData *application, NSError *error) {
220 complete(false, @{ @"p" : application }, error);
222 weakSelf.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
223 [weakSelf initatorThirdPacket:nsdata complete:kscomplete];
227 secnotice("pairing", "failed getting application: %@", error);
228 complete(true, @{}, error);
233 - (void)initatorThirdPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
235 __weak typeof(self) weakSelf = self;
236 secnotice("pairing", "initator packet 3");
238 [self setNextStateError:NULL complete:NULL];
240 NSData *circleBlob = indata[@"b"];
242 if (circleBlob == NULL) {
243 complete(true, NULL, NULL);
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);
254 if (strongSelf->_acceptorWillSendInitialSyncCredentials) {
255 strongSelf.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
256 [weakSelf initatorFourthPacket:nsdata complete:kscomplete];
259 complete(false, @{}, NULL);
261 complete(true, NULL, NULL);
266 - (void)initatorFourthPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
268 secnotice("pairing", "initator packet 4");
270 [self setNextStateError:NULL complete:NULL];
272 NSArray *items = indata[@"d"];
273 if (![items isKindOfClass:[NSArray class]]) {
274 secnotice("pairing", "initator no items to import");
275 complete(true, NULL, NULL);
279 secnotice("pairing", "importing %lu items", (unsigned long)[items count]);
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);
286 self->_needInitialSync = false;
287 complete(true, NULL, NULL);
295 - (void)acceptorFirstPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
297 __weak typeof(self) weakSelf = self;
299 secnotice("pairing", "acceptor packet 1");
301 [self setNextStateError:NULL complete:NULL];
303 if (![self ensureControlChannel]) {
304 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorNoControlChannel userInfo:NULL] complete:complete];
309 secnotice("pairing", "acceptor initialSyncCredentials requested");
310 self.acceptorWillSendInitialSyncCredentials = true;
313 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
314 complete(true, NULL, error);
315 }] validatedStashedAccountCredential:^(NSData *credential, NSError *error) {
317 secnotice("pairing", "acceptor doesn't have a stashed credential: %@", error);
318 [self setNextStateError:error complete:complete];
322 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
323 [weakSelf acceptorSecondPacket:nsdata complete:kscomplete];
326 NSMutableDictionary *reply = [@{
330 if (self.acceptorWillSendInitialSyncCredentials) {
334 secnotice("pairing", "acceptor reply to packet 1");
335 complete(false, reply, NULL);
339 - (void)acceptorSecondPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
341 __weak typeof(self) weakSelf = self;
343 [self setNextStateError:NULL complete:NULL];
345 secnotice("pairing", "acceptor packet 2");
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];
353 secnotice("pairing", "acceptor pairing complete (will send: %s): %@",
354 self.acceptorWillSendInitialSyncCredentials ? "YES" : "NO",
359 if (self.acceptorWillSendInitialSyncCredentials) {
360 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
361 [weakSelf acceptorThirdPacket:nsdata complete:kscomplete];
364 complete(false, reply, NULL);
366 complete(true, reply, NULL);
369 complete(true, reply, error);
371 secnotice("pairing", "acceptor reply to packet 2");
375 - (void)acceptorThirdPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
377 secnotice("pairing", "acceptor packet 3");
379 const uint32_t initialSyncCredentialsFlags =
380 SOSControlInitialSyncFlagTLK|
381 SOSControlInitialSyncFlagPCS|
382 SOSControlInitialSyncFlagBluetoothMigration;
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];
389 secnotice("pairing", "acceptor initialSyncCredentials complete: items %u: %@", (unsigned)[items count], error2);
393 secnotice("pairing", "acceptor reply to packet 3");
394 complete(true, reply, NULL);
401 - (bool)ensureControlChannel
406 NSXPCInterface *interface = [NSXPCInterface interfaceWithProtocol:@protocol(SOSControlProtocol)];
408 self.connection = [[NSXPCConnection alloc] initWithMachServiceName:@(kSecuritydSOSServiceName) options:0];
409 if (self.connection == NULL)
412 self.connection.remoteObjectInterface = interface;
414 [self.connection resume];
419 - (void)validateStart:(void(^)(bool result, NSError *error))complete
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);
428 dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
429 complete(true, NULL);
434 - (void)exchangePacket:(NSData *)inputCompressedData complete:(KCPairingChannelCompletion)complete
436 NSDictionary *indict = NULL;
438 if (inputCompressedData) {
440 NSData *data = [self decompressData:inputCompressedData];
442 secnotice("pairing", "failed to decompress");
443 complete(true, NULL, NULL);
446 NSError *error = NULL;
447 indict = [NSPropertyListSerialization propertyListWithData:data
448 options:(NSPropertyListReadOptions)kCFPropertyListSupportedFormatBinary_v1_0
451 if (indict == NULL) {
452 secnotice("pairing", "failed to deserialize");
453 complete(true, NULL, error);
457 self.nextState(indict, ^(BOOL completed, NSDictionary *outdict, NSError *error) {
458 NSData *outdata = NULL, *compressedData = NULL;
460 NSError *error2 = NULL;
461 outdata = [NSPropertyListSerialization dataWithPropertyList:outdict format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error2];
462 if (outdata == NULL && error)
465 compressedData = [self compressData:outdata];
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]);
474 complete(completed, compressedData, error);
478 - (NSData *)exchangePacket:(NSData *)data complete:(bool *)complete error:(NSError * __autoreleasing *)error
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) {
485 *complete = cComplete;
487 processingError = cError;
488 dispatch_semaphore_signal(sema);
490 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
492 *error = processingError;
496 - (void)setXPCConnectionObject:(NSXPCConnection *)connection
498 self.connection = connection;