]> git.saurik.com Git - apple/security.git/blob - KeychainCircle/PairingChannel.m
Security-59306.101.1.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 #import "keychain/ot/OTManager.h"
16 #import "keychain/ot/OctagonControlServer.h"
17 #import "keychain/ot/OTControl.h"
18 #import "keychain/ot/OctagonControlServer.h"
19 #import "keychain/ot/OTJoiningConfiguration.h"
20 #import "keychain/ot/proto/generated_source/OTPairingMessage.h"
21 #import "keychain/ot/proto/generated_source/OTApplicantToSponsorRound2M1.h"
22 #import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound2M2.h"
23 #import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound1M2.h"
24 #import "keychain/ot/proto/generated_source/OTPairingMessage.h"
25 #include <notify.h>
26
27 #import <compression.h>
28 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
29 #import <MobileGestalt.h>
30 #endif
31
32 #import "utilities/SecADWrapper.h"
33
34 KCPairingIntent_Type KCPairingIntent_Type_None = @"none";
35 KCPairingIntent_Type KCPairingIntent_Type_SilentRepair = @"repair";
36 KCPairingIntent_Type KCPairingIntent_Type_UserDriven = @"userdriven";
37
38 typedef void(^KCPairingInternalCompletion)(BOOL complete, NSDictionary *outdict, NSError *error);
39 typedef void(^KCNextState)(NSDictionary *indict, KCPairingInternalCompletion complete);
40
41 NSString *kKCPairingChannelErrorDomain = @"com.apple.security.kcparingchannel";
42
43 const char* pairingScope = "ot-pairing";
44
45 typedef void(^OTPairingInternalCompletion)(BOOL complete, NSData * _Nullable outData, NSError * _Nullable error);
46 typedef void(^OTNextState)(NSData *inData, OTPairingInternalCompletion complete);
47
48 @implementation KCPairingChannelContext
49
50 + (BOOL)supportsSecureCoding {
51 return YES;
52 }
53
54 - (BOOL)isEqual:(id)object
55 {
56 KCPairingChannelContext *other = (KCPairingChannelContext *)object;
57
58 return [other isMemberOfClass:[self class]]
59 && ((!self->_model && !other->_model) || [self->_model isEqual:other->_model])
60 && ((!self->_modelVersion && !other->_modelVersion) || [self->_modelVersion isEqual:other->_modelVersion])
61 && ((!self->_modelClass && !other->_modelClass) || [self->_modelClass isEqual:other->_modelClass])
62 && ((!self->_osVersion && !other->_osVersion) || [self->_osVersion isEqual:other->_osVersion])
63 && ((!self->_uniqueDeviceID && !other->_uniqueDeviceID) || [self->_uniqueDeviceID isEqual:other->_uniqueDeviceID])
64 && ((!self->_uniqueClientID && !other->_uniqueClientID) || [self->_uniqueClientID isEqual:other->_uniqueClientID])
65 && ((!self->_intent && !other->_intent) || [self->_intent isEqual:other->_intent])
66 ;
67 }
68
69 - (void)encodeWithCoder:(NSCoder *)coder {
70 [coder encodeObject:_model forKey:@"model"];
71 [coder encodeObject:_modelVersion forKey:@"modelVersion"];
72 [coder encodeObject:_modelClass forKey:@"modelClass"];
73 [coder encodeObject:_osVersion forKey:@"osVersion"];
74 [coder encodeObject:_uniqueDeviceID forKey:@"uniqueDeviceID"];
75 [coder encodeObject:_uniqueClientID forKey:@"uniqueClientID"];
76 [coder encodeObject:_intent forKey:@"intent"];
77 }
78
79 - (nullable instancetype)initWithCoder:(NSCoder *)decoder
80 {
81 self = [super init];
82 if (self) {
83 _model = [decoder decodeObjectOfClass:[NSString class] forKey:@"model"];
84 _modelVersion = [decoder decodeObjectOfClass:[NSString class] forKey:@"modelVersion"];
85 _modelClass = [decoder decodeObjectOfClass:[NSString class] forKey:@"modelClass"];
86 _osVersion = [decoder decodeObjectOfClass:[NSString class] forKey:@"osVersion"];
87 _uniqueDeviceID = [decoder decodeObjectOfClass:[NSString class] forKey:@"uniqueDeviceID"];
88 _uniqueClientID = [decoder decodeObjectOfClass:[NSString class] forKey:@"uniqueClientID"];
89 _intent = [decoder decodeObjectOfClass:[NSString class] forKey:@"intent"];
90
91 /* validate intent if we have one */
92 if (_intent != NULL &&
93 !([_intent isEqualToString:KCPairingIntent_Type_None] ||
94 [_intent isEqualToString:KCPairingIntent_Type_SilentRepair] ||
95 [_intent isEqualToString:KCPairingIntent_Type_UserDriven]))
96 {
97 return nil;
98 }
99 }
100 return self;
101 }
102
103 @end
104
105
106 @interface KCPairingChannel ()
107 @property (assign) KCPairingChannelContext *peerVersionContext;
108 @property (assign) bool initiator;
109 @property (assign) unsigned counter;
110 @property (assign) bool acceptorWillSendInitialSyncCredentials;
111 @property (assign) uint32_t acceptorInitialSyncCredentialsFlags;
112 @property (strong) NSXPCConnection *connection;
113 @property (strong) OTControl *otControl;
114 @property (strong) NSString* contextID;
115 @property (strong) OTNextState nextOctagonState;
116 @property (strong) KCNextState nextState;
117 @property (nonatomic, strong) OTJoiningConfiguration* joiningConfiguration;
118 @property (nonatomic) bool testFailSOS;
119 @property (nonatomic) bool testFailOctagon;
120
121 @property (assign) bool sessionSupportsSOS;
122 @property (assign) bool sessionSupportsOctagon;
123 @end
124
125
126 @implementation KCPairingChannel
127
128 + (instancetype)pairingChannelInitiator:(KCPairingChannelContext *)peerVersionContext
129 {
130 return [[KCPairingChannel alloc] initAsInitiator:true version:peerVersionContext];
131 }
132
133 + (instancetype)pairingChannelAcceptor:(KCPairingChannelContext *)peerVersionContext
134 {
135 return [[KCPairingChannel alloc] initAsInitiator:false version:peerVersionContext];
136 }
137
138 - (instancetype)initAsInitiator:(bool)initiator version:(KCPairingChannelContext *)peerVersionContext
139 {
140 if (![KCPairingChannel isSupportedPlatform]) {
141 secerror("platform not supported for pairing");
142 return NULL;
143 }
144
145 if (self = [super init]) {
146 __weak typeof(self) weakSelf = self;
147 _initiator = initiator;
148 _peerVersionContext = peerVersionContext;
149 if (_initiator) {
150 _nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
151 [weakSelf initiatorFirstPacket:nsdata complete:kscomplete];
152 };
153 } else {
154 _nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
155 [weakSelf acceptorFirstPacket:nsdata complete:kscomplete];
156 };
157 }
158 _needInitialSync = true;
159 _testFailSOS = false;
160 _contextID = OTDefaultContext;
161
162 /* only apply to acceptor right now */
163 _sessionSupportsSOS = OctagonPlatformSupportsSOS();
164 _sessionSupportsOctagon = OctagonIsEnabled();
165 NSError *localError = nil;
166 _otControl = [OTControl controlObject:true error:&localError];
167 if(localError){
168 secerror("could not stand up otcontrol connection");
169 }
170 _joiningConfiguration = [[OTJoiningConfiguration alloc]initWithProtocolType:OTProtocolPairing
171 uniqueDeviceID:peerVersionContext.uniqueDeviceID
172 uniqueClientID:peerVersionContext.uniqueClientID
173 pairingUUID:[[NSUUID UUID] UUIDString]
174 containerName:nil
175 contextID:OTDefaultContext
176 epoch:0
177 isInitiator:initiator];
178 }
179 return self;
180 }
181
182 + (bool)isSupportedPlatform
183 {
184 return true;
185 }
186
187 - (void)oneStepTooMany:(NSDictionary * __unused)indata complete:(KCPairingInternalCompletion)complete
188 {
189 secerror("pairingchannel: one step too many");
190 complete(false, NULL, [NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorTooManySteps userInfo:NULL]);
191 }
192
193 - (void)setNextStateError:(NSError *)error complete:(KCPairingInternalCompletion)complete
194 {
195 __weak typeof(self) weakSelf = self;
196 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
197 [weakSelf oneStepTooMany:nsdata complete:kscomplete];
198 };
199 if (complete) {
200 if (error)
201 secerror("pairingchannel: failed pairing with: %@", error);
202 complete(false, NULL, error);
203 }
204 }
205
206 //MARK: - Compression
207
208 const compression_algorithm pairingCompression = COMPRESSION_LZFSE;
209 #define EXTRA_SIZE 100
210
211 + (NSData *)pairingChannelCompressData:(NSData *)data
212 {
213 NSMutableData *scratch = [NSMutableData dataWithLength:compression_encode_scratch_buffer_size(pairingCompression)];
214
215 NSUInteger outLength = [data length];
216 if (outLength > NSUIntegerMax - EXTRA_SIZE)
217 return nil;
218 outLength += EXTRA_SIZE;
219
220 NSMutableData *o = [NSMutableData dataWithLength:outLength];
221 size_t result = compression_encode_buffer([o mutableBytes], [o length], [data bytes], [data length], [scratch mutableBytes], pairingCompression);
222 if (result == 0)
223 return nil;
224
225 [o setLength:result];
226
227 return o;
228 }
229
230 + (NSData *)pairingChannelDecompressData:(NSData *)data
231 {
232 NSMutableData *scratch = [NSMutableData dataWithLength:compression_decode_scratch_buffer_size(pairingCompression)];
233
234 size_t outLength = [data length];
235 size_t result;
236 NSMutableData *o = NULL;
237
238 do {
239 size_t size;
240 if (__builtin_umull_overflow(outLength, 2, &size))
241 return nil;
242 outLength = size;
243 o = [NSMutableData dataWithLength:outLength];
244
245 result = compression_decode_buffer([o mutableBytes], outLength, [data bytes], [data length], [scratch mutableBytes], pairingCompression);
246 if (result == 0)
247 return nil;
248 } while(result == outLength);
249
250 [o setLength:result];
251
252 return o;
253 }
254
255
256
257 //MARK: - Initiator
258
259 - (void) attemptSosUpgrade
260 {
261 [self.otControl attemptSosUpgrade:nil context:self.contextID reply:^(NSError *error) {
262 if(error){
263 secerror("pairing: failed to upgrade initiator into Octagon: %@", error);
264 }
265 }];
266 }
267
268 - (void)initiatorFirstPacket:(NSDictionary * __unused)indata complete:(KCPairingInternalCompletion)complete
269 {
270 secnotice("pairing", "initiator packet 1");
271
272 if (OctagonPlatformSupportsSOS() && ![self ensureControlChannel]) {
273 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorNoControlChannel userInfo:NULL] complete:complete];
274 return;
275 }
276
277 if(self.sessionSupportsOctagon && self.sessionSupportsSOS && !self.testFailOctagon) {
278 __weak typeof(self) weakSelf = self;
279 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
280 [weakSelf initiatorSecondPacket:nsdata complete:kscomplete];
281 };
282 complete(false, @{ @"d" : @YES, @"o" : @{@"v" : @"O"} }, NULL);
283 return;
284 } else if (self.sessionSupportsOctagon && self.testFailOctagon) {
285 complete(true, nil, NULL);
286 return;
287 } else if (self.sessionSupportsOctagon && !self.sessionSupportsSOS) {
288 __weak typeof(self) weakSelf = self;
289 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
290 [weakSelf initiatorSecondPacket:nsdata complete:kscomplete];
291 };
292 complete(false, @{ @"o" : @{@"v" : @"O"} }, NULL);
293 return;
294 }
295 else {
296 __weak typeof(self) weakSelf = self;
297 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
298 [weakSelf initiatorSecondPacket:nsdata complete:kscomplete];
299 };
300 complete(false, @{ @"d" : @YES }, NULL);
301 }
302 }
303
304 - (void)initiatorSecondPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
305 {
306 secnotice("pairing", "initiator packet 2");
307
308 NSData *octagonData = indata[@"o"];
309
310 if(octagonData == nil) {
311 secnotice("pairing", "acceptor didn't send a octagon packet, so skipping all octagon flows");
312 self.sessionSupportsOctagon = false;
313 }
314
315 NSData *credential = indata[@"c"];
316 if (![credential isKindOfClass:[NSData class]]) {
317 if(!OctagonIsEnabled() && OctagonPlatformSupportsSOS()) {
318 secnotice("pairing", "no credential");
319 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorAccountCredentialMissing userInfo:NULL] complete:complete];
320 return;
321 }
322 }
323
324 if (OctagonPlatformSupportsSOS() && indata[@"d"]) {
325 secnotice("pairing", "acceptor will send send initial credentials");
326 self.acceptorWillSendInitialSyncCredentials = true;
327 }
328 if(OctagonPlatformSupportsSOS()) {
329 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
330 complete(true, NULL, error);
331 }] stashAccountCredential:credential complete:^(bool success, NSError *error) {
332 [self setNextStateError:NULL complete:NULL];
333 if (!success || self.testFailSOS) {
334 secnotice("pairing", "failed stash credentials: %@", error);
335 if(!OctagonIsEnabled() || !self.sessionSupportsOctagon){
336 complete(true, NULL, error);
337 return;
338 } else {
339 [self initiatorCompleteSecondPacketOctagon:indata application:nil complete:complete];
340 }
341 } else{
342 [self initiatorCompleteSecondPacketWithSOS:indata complete:complete];
343 }
344 }];
345 } else if(OctagonIsEnabled() && self.sessionSupportsOctagon) {
346 [self initiatorCompleteSecondPacketOctagon:indata application:nil complete:complete];
347 return;
348 }
349 }
350
351
352 - (void)initiatorCompleteSecondPacketWithSOS:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
353 {
354 secnotice("pairing", "initiator complete second packet 2");
355
356 __weak typeof(self) weakSelf = self;
357
358 [self setNextStateError:NULL complete:NULL];
359
360 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
361 complete(true, NULL, error);
362 }] myPeerInfo:^(NSData *application, NSError *error) {
363 if (application && !self.testFailSOS) {
364 if(OctagonIsEnabled() && self.sessionSupportsOctagon) {
365 [self initiatorCompleteSecondPacketOctagon:indata application:application complete:complete];
366 } else{
367 complete(false, @{ @"p" : application }, error);
368 weakSelf.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
369 [weakSelf initiatorThirdPacket:nsdata complete:kscomplete];
370 };
371 }
372 } else {
373 if(OctagonIsEnabled() && self.sessionSupportsOctagon){
374 [self initiatorCompleteSecondPacketOctagon:indata application:application complete:complete];
375 } else {
376 secnotice("pairing", "failed getting application: %@", error);
377 complete(true, @{}, error);
378 }
379 }
380 }];
381 }
382
383 - (void)initiatorCompleteSecondPacketOctagon:(NSDictionary*)indata application:(NSData*)application complete:(KCPairingInternalCompletion)complete
384 {
385 secnotice("pairing", "initiator complete second packet 2 with octagon");
386
387 __weak typeof(self) weakSelf = self;
388 NSData *octagonData = indata[@"o"];
389
390 if(![octagonData isKindOfClass:[NSData class]]) {
391 secnotice(pairingScope, "initiatorCompleteSecondPacketOctagon octagonData missing or wrong class");
392 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorOctagonMessageMissing userInfo:NULL] complete:complete];
393 return;
394 }
395
396 //handle epoch and create identity message
397 [self.otControl rpcPrepareIdentityAsApplicantWithConfiguration:self.joiningConfiguration
398 reply:^(NSString *peerID,
399 NSData *permanentInfo,
400 NSData *permanentInfoSig,
401 NSData *stableInfo,
402 NSData *stableInfoSig,
403 NSError *error) {
404 if (error || self.testFailOctagon) {
405 secerror("ot-pairing: failed to create %d message: %@", self.counter, error);
406 complete(true, nil, error);
407 return;
408 } else {
409 OTPairingMessage *octagonMessage = [[OTPairingMessage alloc]init];
410 OTApplicantToSponsorRound2M1 *prepare = [[OTApplicantToSponsorRound2M1 alloc] init];
411 prepare.peerID = peerID;
412 prepare.permanentInfo = permanentInfo;
413 prepare.permanentInfoSig = permanentInfoSig;
414 prepare.stableInfo = stableInfo;
415 prepare.stableInfoSig = stableInfoSig;
416 octagonMessage.prepare = prepare;
417 if(application){
418 secnotice(pairingScope, "initiatorCompleteSecondPacketOctagon returning octagon and sos data");
419 complete(false, @{ @"p" : application, @"o" : octagonMessage.data }, nil);
420 } else {
421 secnotice(pairingScope, "initiatorCompleteSecondPacketOctagon returning octagon data");
422 complete(false, @{ @"o" : octagonMessage.data }, nil);
423 }
424 weakSelf.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
425 [weakSelf initiatorThirdPacket:nsdata complete:kscomplete];
426 };
427 }
428 }];
429 }
430
431 - (void)initiatorThirdPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
432 {
433 __weak typeof(self) weakSelf = self;
434 secnotice("pairing", "initiator packet 3");
435
436 [self setNextStateError:NULL complete:NULL];
437
438 NSData *circleBlob = indata[@"b"];
439
440 if (circleBlob == NULL) {
441 if(!OctagonIsEnabled() && OctagonPlatformSupportsSOS()) {
442 secnotice("pairing", "no sos circle");
443 complete(true, NULL, NULL);
444 return;
445 }
446 } else if(OctagonPlatformSupportsSOS()) {
447 if(![circleBlob isKindOfClass:[NSData class]]) {
448 complete(true, NULL, [NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorTypeConfusion userInfo:NULL]);
449 return;
450 }
451
452 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
453 complete(true, NULL, error);
454 }] joinCircleWithBlob:circleBlob version:kPiggyV1 complete:^(bool success, NSError *error){
455 if(error || self.testFailSOS) {
456 if(OctagonIsEnabled() && self.sessionSupportsOctagon) {
457 secnotice("pairing", "failed to join circle with blob, continuing to handle octagon protocol");
458 } else {
459 secnotice("pairing", "failed to join circle with blob");
460 complete(true, NULL, NULL);
461 }
462 } else{
463 if(OctagonIsEnabled() && self.sessionSupportsOctagon) {
464 secnotice("pairing","initiator circle join complete");
465 } else {
466 //kick off SOS ugprade
467 if(OctagonIsEnabled() && !self.sessionSupportsOctagon) {
468 [self attemptSosUpgrade];
469 }
470 typeof(self) strongSelf = weakSelf;
471 secnotice("pairing", "initiator circle join complete, more data: %s: %@",
472 strongSelf->_acceptorWillSendInitialSyncCredentials ? "yes" : "no", error);
473
474 if (strongSelf->_acceptorWillSendInitialSyncCredentials) {
475 strongSelf.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
476 [weakSelf initiatorFourthPacket:nsdata complete:kscomplete];
477 };
478
479 complete(false, @{}, NULL);
480 } else {
481 complete(true, NULL, NULL);
482 }
483 }
484 }
485 }];
486 }
487 if(OctagonIsEnabled() && self.sessionSupportsOctagon){
488 NSData *octagonData = indata[@"o"];
489 if(![octagonData isKindOfClass:[NSData class]]) {
490 secnotice(pairingScope, "initiatorThirdPacket octagonData missing or wrong class");
491 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorOctagonMessageMissing userInfo:NULL] complete:complete];
492 return;
493 }
494 OTPairingMessage *pairingMessage = [[OTPairingMessage alloc] initWithData:octagonData];
495 if(!pairingMessage.hasVoucher){
496 secnotice(pairingScope, "initiatorThirdPacket pairingMessage has no voucher");
497 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorOctagonMessageMissing userInfo:NULL] complete:complete];
498 return;
499 }
500 OTSponsorToApplicantRound2M2 *voucher = pairingMessage.voucher;
501
502 //handle voucher and join octagon
503 [self.otControl rpcJoinWithConfiguration:self.joiningConfiguration
504 vouchData:voucher.voucher
505 vouchSig:voucher.voucherSignature
506 reply:^(NSError *error) {
507 if (error || self.testFailOctagon) {
508 secerror("ot-pairing: failed to create %d message: %@", self.counter, error);
509 complete(true, NULL, error);
510 return;
511 }else{
512 secnotice(pairingScope, "initiatorThirdPacket successfully joined Octagon");
513 typeof(self) strongSelf = weakSelf;
514 if(OctagonPlatformSupportsSOS() && strongSelf->_acceptorWillSendInitialSyncCredentials) {
515 strongSelf.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
516 [weakSelf initiatorFourthPacket:nsdata complete:kscomplete];
517 };
518 complete(false, @{}, nil);
519
520 } else {
521 complete(true, nil, nil);
522
523 }
524 }
525 }];
526 }
527 }
528
529 - (void)initiatorFourthPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
530 {
531 secnotice("pairing", "initiator packet 4");
532
533 [self setNextStateError:NULL complete:NULL];
534
535 NSArray *items = indata[@"d"];
536 if (![items isKindOfClass:[NSArray class]]) {
537 secnotice("pairing", "initiator no items to import");
538 complete(true, NULL, NULL);
539 return;
540 }
541
542 secnotice("pairing", "importing %lu items", (unsigned long)[items count]);
543
544 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
545 complete(true, NULL, error);
546 }] importInitialSyncCredentials:items complete:^(bool success, NSError *error) {
547 secnotice("pairing", "initiator importInitialSyncCredentials: %s: %@", success ? "yes" : "no", error);
548 if (success)
549 self->_needInitialSync = false;
550 complete(true, nil, nil);
551 }];
552 }
553
554
555
556 //MARK: - Acceptor
557
558 - (void)acceptorFirstPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
559 {
560 __weak typeof(self) weakSelf = self;
561
562 secnotice("pairing", "acceptor packet 1");
563 [self setNextStateError:NULL complete:NULL];
564
565 if (self.sessionSupportsSOS && ![self ensureControlChannel]) {
566 secnotice("pairing", "unable to establish a channel to sos control");
567 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorNoControlChannel userInfo:NULL] complete:complete];
568 return;
569 }
570
571 if (self.sessionSupportsSOS && indata[@"d"]) {
572 secnotice("pairing", "acceptor initialSyncCredentials requested");
573 self.acceptorWillSendInitialSyncCredentials = true;
574 self.acceptorInitialSyncCredentialsFlags =
575 SOSControlInitialSyncFlagTLK|
576 SOSControlInitialSyncFlagPCS|
577 SOSControlInitialSyncFlagBluetoothMigration;
578
579 }
580
581 if (indata[@"o"] == nil) {
582 secnotice("pairing", "initiator didn't send a octagon packet, so skipping all octagon flows");
583 self.sessionSupportsOctagon = false;
584 }
585 // XXX Before we go here we should check if we are trusted or not, if we are not, there is no point proposing octagon
586
587 NSMutableDictionary *reply = [NSMutableDictionary dictionary];
588
589 if(self.sessionSupportsSOS) {
590 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
591 complete(true, NULL, error);
592 }] validatedStashedAccountCredential:^(NSData *credential, NSError *error) {
593 secnotice("pairing", "acceptor validatedStashedAccountCredential: %@ (%@)", credential != NULL ? @"yes" : @"no", error);
594 if(credential){
595 reply[@"c"] = credential;
596
597 if (self.acceptorWillSendInitialSyncCredentials) {
598 reply[@"d"] = @YES;
599 };
600 if(self.sessionSupportsOctagon) {
601 [self acceptorFirstOctagonPacket:indata reply:reply complete:complete];
602 } else {
603 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
604 [weakSelf acceptorSecondPacket:nsdata complete:kscomplete];
605 };
606 secnotice("pairing", "acceptor reply to packet 1");
607 complete(false, reply, NULL);
608 }
609 }
610 else if ((!credential && !self.sessionSupportsOctagon) || self.testFailSOS) {
611 secnotice("pairing", "acceptor doesn't have a stashed credential: %@", error);
612 NSError *noStashCredentail = [NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorNoStashedCredential userInfo:NULL];
613 [self setNextStateError:noStashCredentail complete:complete];
614 return;
615 }
616 else if(self.sessionSupportsOctagon) {
617 [self acceptorFirstOctagonPacket:indata reply:reply complete:complete];
618 }
619 }];
620 } else if(self.sessionSupportsOctagon){
621 [self acceptorFirstOctagonPacket:indata reply:reply complete:complete];
622 } else {
623 secnotice("pairing", "acceptor neither of octagon nor SOS");
624 NSError *notSupported = [NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorNoTrustAvailable userInfo:NULL];
625 [self setNextStateError:notSupported complete:complete];
626 }
627 }
628 - (void)acceptorFirstOctagonPacket:(NSDictionary *)indata reply:(NSMutableDictionary*)reply complete:(KCPairingInternalCompletion)complete
629 {
630 __weak typeof(self) weakSelf = self;
631 NSDictionary *octagonData = indata[@"o"];
632
633 if(![octagonData isKindOfClass:[NSDictionary class]]) {
634 secnotice(pairingScope, "acceptorFirstOctagonPacket octagon data missing");
635 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorOctagonMessageMissing userInfo:NULL] complete:complete];
636 return;
637 }
638
639 NSDictionary *unpackedMessage = octagonData;
640
641 if(![unpackedMessage[@"v"] isEqualToString:@"O"]){
642 secnotice(pairingScope, "acceptorFirstOctagonPacket 'v' contents wrong");
643 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorOctagonMessageMissing userInfo:NULL] complete:complete];
644 return;
645 }
646
647 //handle epoch request and fetch epoch
648 [self.otControl rpcEpochWithConfiguration:self.joiningConfiguration reply:^(uint64_t epoch, NSError * _Nullable error) {
649 secnotice("pairing", "acceptor rpcEpochWithConfiguration: %ld (%@)", (long)epoch, error);
650 if(error || self.testFailOctagon){
651 secerror("error acceptor handling packet %d", self.counter);
652 complete(true, nil, error);
653 }else{
654 secnotice(pairingScope, "acceptor handled packet %d", self.counter);
655 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
656 [weakSelf acceptorSecondPacket:nsdata complete:kscomplete];
657 };
658 OTPairingMessage *response = [[OTPairingMessage alloc] init];
659 response.epoch = [[OTSponsorToApplicantRound1M2 alloc] init];
660 response.epoch.epoch = epoch;
661 reply[@"o"] = response.data;
662
663 secnotice("pairing", "acceptor reply to packet 1");
664 complete(false, reply, error);
665 }
666 }];
667 }
668
669 - (void)acceptorSecondPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
670 {
671 __weak typeof(self) weakSelf = self;
672
673 [self setNextStateError:NULL complete:NULL];
674
675 secnotice("pairing", "acceptor packet 2");
676 __block NSMutableDictionary *reply = [NSMutableDictionary dictionary];
677
678 NSData *peerJoinBlob = indata[@"p"];
679
680 if(self.sessionSupportsSOS && [peerJoinBlob isKindOfClass:[NSData class]]) {
681 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
682 complete(true, NULL, error);
683 }] circleJoiningBlob:peerJoinBlob complete:^(NSData *blob, NSError *error){
684
685 if (blob) {
686 secnotice("pairing", "acceptor pairing complete (will send: %s): %@",
687 self.acceptorWillSendInitialSyncCredentials ? "YES" : "NO",
688 error);
689
690 reply[@"b"] = blob;
691 }
692 if(self.sessionSupportsOctagon) {
693 [self acceptorSecondOctagonPacket:indata reply:reply complete:complete];
694 } else {
695 secnotice("pairing", "posting kSOSCCCircleOctagonKeysChangedNotification");
696 notify_post(kSOSCCCircleOctagonKeysChangedNotification);
697 if (self.acceptorWillSendInitialSyncCredentials) {
698 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
699 [weakSelf acceptorThirdPacket:nsdata complete:kscomplete];
700 };
701
702 complete(false, reply, NULL);
703 } else {
704 complete(true, reply, NULL);
705 }
706 secnotice("pairing", "acceptor reply to packet 2");
707 }
708 }];
709 } else if(self.sessionSupportsOctagon){
710 [self acceptorSecondOctagonPacket:indata reply:reply complete:complete];
711 }
712 }
713
714 - (void)acceptorSecondOctagonPacket:(NSDictionary*)indata reply:(NSMutableDictionary*)reply complete:(KCPairingInternalCompletion)complete
715 {
716 __weak typeof(self) weakSelf = self;
717 NSData *octagonData = indata[@"o"];
718
719 if(![octagonData isKindOfClass:[NSData class]]) {
720 secnotice(pairingScope, "acceptorSecondOctagonPacket octagon data missing");
721 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorOctagonMessageMissing userInfo:NULL] complete:complete];
722 return;
723 }
724
725 OTPairingMessage *pairingMessage = [[OTPairingMessage alloc] initWithData:octagonData];
726 if(!pairingMessage.hasPrepare){
727 secerror("ot-pairing: acceptorSecondOctagonPacket: no octagon message");
728 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorOctagonMessageMissing userInfo:NULL] complete:complete];
729 return;
730 }
731 OTApplicantToSponsorRound2M1 *prepare = pairingMessage.prepare;
732
733 //handle identity and fetch voucher
734 [self.otControl rpcVoucherWithConfiguration:self.joiningConfiguration
735 peerID:prepare.peerID
736 permanentInfo:prepare.permanentInfo
737 permanentInfoSig:prepare.permanentInfoSig
738 stableInfo:prepare.stableInfo
739 stableInfoSig:prepare.stableInfoSig
740 reply:^(NSData *voucher,
741 NSData *voucherSig,
742 NSError *error) {
743 if(error || self.testFailOctagon){
744 secerror("error acceptor handling octagon packet %d", self.counter);
745 complete(true, nil, error);
746 return;
747 } else {
748 bool finished = true;
749
750 secnotice(pairingScope, "acceptor handled octagon packet %d", self.counter);
751 if (OctagonPlatformSupportsSOS() && self.acceptorWillSendInitialSyncCredentials) {
752 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
753 [weakSelf acceptorThirdPacket:nsdata complete:kscomplete];
754 };
755 finished = false;
756 }
757 OTPairingMessage *response = [[OTPairingMessage alloc] init];
758 response.voucher = [[OTSponsorToApplicantRound2M2 alloc] init];
759 response.voucher.voucher = voucher;
760 response.voucher.voucherSignature = voucherSig;
761
762 if (self.acceptorWillSendInitialSyncCredentials) {
763 // no need to share TLKs over the pairing channel, that's provided by octagon
764 self.acceptorInitialSyncCredentialsFlags &= ~(SOSControlInitialSyncFlagTLK | SOSControlInitialSyncFlagPCS);
765 }
766
767 reply[@"o"] = response.data;
768
769 secnotice("pairing", "acceptor reply to packet 2");
770 complete(finished ? true : false, reply, error);
771 }
772 }];
773 }
774
775 - (void)acceptorThirdPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
776 {
777 secnotice("pairing", "acceptor packet 3");
778
779 const uint32_t initialSyncCredentialsFlags = self.acceptorInitialSyncCredentialsFlags;
780
781 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
782 complete(true, NULL, error);
783 }] initialSyncCredentials:initialSyncCredentialsFlags complete:^(NSArray *items, NSError *error2) {
784 NSMutableDictionary *reply = [NSMutableDictionary dictionary];
785
786 secnotice("pairing", "acceptor initialSyncCredentials complete: items %u: %@", (unsigned)[items count], error2);
787 if (items) {
788 reply[@"d"] = items;
789 }
790 secnotice("pairing", "acceptor reply to packet 3");
791 complete(true, reply, NULL);
792 }];
793 }
794
795
796 //MARK: - Helper
797
798 - (bool)ensureControlChannel
799 {
800 if (self.connection)
801 return true;
802
803 NSXPCInterface *interface = [NSXPCInterface interfaceWithProtocol:@protocol(SOSControlProtocol)];
804
805 self.connection = [[NSXPCConnection alloc] initWithMachServiceName:@(kSecuritydSOSServiceName) options:0];
806 if (self.connection == NULL){
807 return false;
808 }
809 self.connection.remoteObjectInterface = interface;
810
811 [self.connection resume];
812
813 return true;
814 }
815
816 - (void)validateStart:(void(^)(bool result, NSError *error))complete
817 {
818 if (!self.initiator) {
819 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
820 complete(false, error);
821 }] stashedCredentialPublicKey:^(NSData *publicKey, NSError *error) {
822 complete(publicKey != NULL, error);
823 }];
824 } else {
825 dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
826 complete(true, NULL);
827 });
828 }
829 }
830
831 - (void)exchangePacket:(NSData *)inputCompressedData complete:(KCPairingChannelCompletion)complete
832 {
833 NSDictionary *indict = NULL;
834 secnotice("pairing", "Exchange packet: %u", self.counter);
835 self.counter++;
836
837 if (inputCompressedData) {
838
839 NSData *data = [[self class] pairingChannelDecompressData:inputCompressedData];
840 if (data == NULL) {
841 secnotice("pairing", "failed to decompress");
842 complete(true, NULL, NULL);
843 }
844
845 NSError *error = NULL;
846 indict = [NSPropertyListSerialization propertyListWithData:data
847 options:(NSPropertyListReadOptions)kCFPropertyListSupportedFormatBinary_v1_0
848 format:NULL
849 error:&error];
850 if (indict == NULL) {
851 secnotice("pairing", "failed to deserialize");
852 complete(true, NULL, error);
853 return;
854 }
855 }
856 self.nextState(indict, ^(BOOL completed, NSDictionary *outdict, NSError *error) {
857 NSData *outdata = NULL, *compressedData = NULL;
858 if (outdict) {
859 NSError *error2 = NULL;
860 outdata = [NSPropertyListSerialization dataWithPropertyList:outdict format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error2];
861 if (outdata == NULL && error)
862 error = error2;
863 if (outdata)
864 compressedData = [[self class] pairingChannelCompressData:outdata];
865
866 if (compressedData) {
867 NSString *key = [NSString stringWithFormat:@"com.apple.ckks.pairing.packet-size.%s.%u",
868 self->_initiator ? "initiator" : "acceptor", self->_counter];
869 SecADClientPushValueForDistributionKey((__bridge CFStringRef)key, [compressedData length]);
870 secnotice("pairing", "pairing packet size %lu", (unsigned long)[compressedData length]);
871 }
872 }
873 secnotice("pairing", "Exchange packet complete data: %@: %@", outdata ? @"YES" : @"NO", error);
874 complete(completed, compressedData, error);
875 });
876 }
877
878 - (NSData *)exchangePacket:(NSData *)data complete:(bool *)complete error:(NSError * __autoreleasing *)error
879 {
880 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
881 __block NSData *rData = NULL;
882 __block NSError* processingError;
883 [self exchangePacket:data complete:^(BOOL cComplete, NSData *cData, NSError *cError) {
884 *complete = cComplete;
885 rData = cData;
886 processingError = cError;
887 dispatch_semaphore_signal(sema);
888 }];
889 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
890 if (error)
891 *error = processingError;
892 return rData;
893 }
894
895 - (void)setXPCConnectionObject:(NSXPCConnection *)connection
896 {
897 self.connection = connection;
898 }
899
900 - (void)setControlObject:(OTControl *)control
901 {
902 self.otControl = control;
903 }
904
905 - (void)setConfiguration:(OTJoiningConfiguration *)config
906 {
907 self.joiningConfiguration = config;
908 }
909
910 - (void)setSOSMessageFailForTesting:(BOOL)value
911 {
912 self.testFailSOS = value;
913 }
914 - (void)setOctagonMessageFailForTesting:(BOOL)value
915 {
916 self.testFailOctagon = value;
917 }
918
919 - (void)setSessionSupportsOctagonForTesting:(bool)value
920 {
921 self.sessionSupportsOctagon = value;
922 }
923
924 @end