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