]> git.saurik.com Git - apple/security.git/blob - KeychainCircle/PairingChannel.m
Security-59306.11.20.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(OctagonIsEnabled() && self.sessionSupportsOctagon && !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 } else if (OctagonIsEnabled() && self.testFailOctagon) {
283 complete(true, nil, NULL);
284 return;
285 } else {
286 __weak typeof(self) weakSelf = self;
287 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
288 [weakSelf initiatorSecondPacket:nsdata complete:kscomplete];
289 };
290 complete(false, @{ @"d" : @YES }, NULL);
291 }
292 }
293
294 - (void)initiatorSecondPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
295 {
296 secnotice("pairing", "initiator packet 2");
297
298 NSData *octagonData = indata[@"o"];
299
300 if(octagonData == nil) {
301 secnotice("pairing", "acceptor didn't send a octagon packet, so skipping all octagon flows");
302 self.sessionSupportsOctagon = false;
303 }
304
305 NSData *credential = indata[@"c"];
306 if (![credential isKindOfClass:[NSData class]]) {
307 if(!OctagonIsEnabled() && OctagonPlatformSupportsSOS()) {
308 secnotice("pairing", "no credential");
309 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorAccountCredentialMissing userInfo:NULL] complete:complete];
310 return;
311 }
312 }
313
314 if (OctagonPlatformSupportsSOS() && indata[@"d"]) {
315 secnotice("pairing", "acceptor will send send initial credentials");
316 self.acceptorWillSendInitialSyncCredentials = true;
317 }
318 if(OctagonPlatformSupportsSOS()) {
319 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
320 complete(true, NULL, error);
321 }] stashAccountCredential:credential complete:^(bool success, NSError *error) {
322 [self setNextStateError:NULL complete:NULL];
323 if (!success || self.testFailSOS) {
324 secnotice("pairing", "failed stash credentials: %@", error);
325 if(!OctagonIsEnabled() || !self.sessionSupportsOctagon){
326 complete(true, NULL, error);
327 return;
328 } else {
329 [self initiatorCompleteSecondPacketOctagon:indata application:nil complete:complete];
330 }
331 } else{
332 [self initiatorCompleteSecondPacketWithSOS:indata complete:complete];
333 }
334 }];
335 } else if(OctagonIsEnabled() && self.sessionSupportsOctagon) {
336 [self initiatorCompleteSecondPacketOctagon:indata application:nil complete:complete];
337 return;
338 }
339 }
340
341
342 - (void)initiatorCompleteSecondPacketWithSOS:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
343 {
344 secnotice("pairing", "initiator complete second packet 2");
345
346 __weak typeof(self) weakSelf = self;
347
348 [self setNextStateError:NULL complete:NULL];
349
350 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
351 complete(true, NULL, error);
352 }] myPeerInfo:^(NSData *application, NSError *error) {
353 if (application && !self.testFailSOS) {
354 if(OctagonIsEnabled() && self.sessionSupportsOctagon) {
355 [self initiatorCompleteSecondPacketOctagon:indata application:application complete:complete];
356 } else{
357 complete(false, @{ @"p" : application }, error);
358 weakSelf.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
359 [weakSelf initiatorThirdPacket:nsdata complete:kscomplete];
360 };
361 }
362 } else {
363 if(OctagonIsEnabled() && self.sessionSupportsOctagon){
364 [self initiatorCompleteSecondPacketOctagon:indata application:application complete:complete];
365 } else {
366 secnotice("pairing", "failed getting application: %@", error);
367 complete(true, @{}, error);
368 }
369 }
370 }];
371 }
372
373 - (void)initiatorCompleteSecondPacketOctagon:(NSDictionary*)indata application:(NSData*)application complete:(KCPairingInternalCompletion)complete
374 {
375 secnotice("pairing", "initiator complete second packet 2 with octagon");
376
377 __weak typeof(self) weakSelf = self;
378 NSData *octagonData = indata[@"o"];
379
380 if(![octagonData isKindOfClass:[NSData class]]) {
381 secnotice(pairingScope, "initiatorCompleteSecondPacketOctagon octagonData missing or wrong class");
382 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorOctagonMessageMissing userInfo:NULL] complete:complete];
383 return;
384 }
385
386 //handle epoch and create identity message
387 [self.otControl rpcPrepareIdentityAsApplicantWithConfiguration:self.joiningConfiguration
388 reply:^(NSString *peerID,
389 NSData *permanentInfo,
390 NSData *permanentInfoSig,
391 NSData *stableInfo,
392 NSData *stableInfoSig,
393 NSError *error) {
394 if (error || self.testFailOctagon) {
395 secerror("ot-pairing: failed to create %d message: %@", self.counter, error);
396 complete(true, nil, error);
397 return;
398 } else {
399 OTPairingMessage *octagonMessage = [[OTPairingMessage alloc]init];
400 OTApplicantToSponsorRound2M1 *prepare = [[OTApplicantToSponsorRound2M1 alloc] init];
401 prepare.peerID = peerID;
402 prepare.permanentInfo = permanentInfo;
403 prepare.permanentInfoSig = permanentInfoSig;
404 prepare.stableInfo = stableInfo;
405 prepare.stableInfoSig = stableInfoSig;
406 octagonMessage.prepare = prepare;
407 if(application){
408 secnotice(pairingScope, "initiatorCompleteSecondPacketOctagon returning octagon and sos data");
409 complete(false, @{ @"p" : application, @"o" : octagonMessage.data }, nil);
410 } else {
411 secnotice(pairingScope, "initiatorCompleteSecondPacketOctagon returning octagon data");
412 complete(false, @{ @"o" : octagonMessage.data }, nil);
413 }
414 weakSelf.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
415 [weakSelf initiatorThirdPacket:nsdata complete:kscomplete];
416 };
417 }
418 }];
419 }
420
421 - (void)initiatorThirdPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
422 {
423 __weak typeof(self) weakSelf = self;
424 secnotice("pairing", "initiator packet 3");
425
426 [self setNextStateError:NULL complete:NULL];
427
428 NSData *circleBlob = indata[@"b"];
429
430 if (circleBlob == NULL) {
431 if(!OctagonIsEnabled() && OctagonPlatformSupportsSOS()) {
432 secnotice("pairing", "no sos circle");
433 complete(true, NULL, NULL);
434 return;
435 }
436 } else if(OctagonPlatformSupportsSOS()) {
437 if(![circleBlob isKindOfClass:[NSData class]]) {
438 complete(true, NULL, [NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorTypeConfusion userInfo:NULL]);
439 return;
440 }
441
442 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
443 complete(true, NULL, error);
444 }] joinCircleWithBlob:circleBlob version:kPiggyV1 complete:^(bool success, NSError *error){
445 if(error || self.testFailSOS) {
446 if(OctagonIsEnabled() && self.sessionSupportsOctagon) {
447 secnotice("pairing", "failed to join circle with blob, continuing to handle octagon protocol");
448 } else {
449 secnotice("pairing", "failed to join circle with blob");
450 complete(true, NULL, NULL);
451 }
452 } else{
453 if(OctagonIsEnabled() && self.sessionSupportsOctagon) {
454 secnotice("pairing","initiator circle join complete");
455 } else {
456 //kick off SOS ugprade
457 if(OctagonIsEnabled() && !self.sessionSupportsOctagon) {
458 [self attemptSosUpgrade];
459 }
460 typeof(self) strongSelf = weakSelf;
461 secnotice("pairing", "initiator circle join complete, more data: %s: %@",
462 strongSelf->_acceptorWillSendInitialSyncCredentials ? "yes" : "no", error);
463
464 if (strongSelf->_acceptorWillSendInitialSyncCredentials) {
465 strongSelf.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
466 [weakSelf initiatorFourthPacket:nsdata complete:kscomplete];
467 };
468
469 complete(false, @{}, NULL);
470 } else {
471 complete(true, NULL, NULL);
472 }
473 }
474 }
475 }];
476 }
477 if(OctagonIsEnabled() && self.sessionSupportsOctagon){
478 NSData *octagonData = indata[@"o"];
479 if(![octagonData isKindOfClass:[NSData class]]) {
480 secnotice(pairingScope, "initiatorThirdPacket octagonData missing or wrong class");
481 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorOctagonMessageMissing userInfo:NULL] complete:complete];
482 return;
483 }
484 OTPairingMessage *pairingMessage = [[OTPairingMessage alloc] initWithData:octagonData];
485 if(!pairingMessage.hasVoucher){
486 secnotice(pairingScope, "initiatorThirdPacket pairingMessage has no voucher");
487 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorOctagonMessageMissing userInfo:NULL] complete:complete];
488 return;
489 }
490 OTSponsorToApplicantRound2M2 *voucher = pairingMessage.voucher;
491
492 //handle voucher and join octagon
493 [self.otControl rpcJoinWithConfiguration:self.joiningConfiguration vouchData:voucher.voucher vouchSig:voucher.voucherSignature preapprovedKeys:voucher.preapprovedKeys reply:^(NSError *error) {
494 if (error || self.testFailOctagon) {
495 secerror("ot-pairing: failed to create %d message: %@", self.counter, error);
496 complete(true, NULL, error);
497 return;
498 }else{
499 secnotice(pairingScope, "initiatorThirdPacket successfully joined Octagon");
500 typeof(self) strongSelf = weakSelf;
501 if(OctagonPlatformSupportsSOS() && strongSelf->_acceptorWillSendInitialSyncCredentials == true) {
502 strongSelf.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
503 [weakSelf initiatorFourthPacket:nsdata complete:kscomplete];
504 };
505 complete(false, @{}, nil);
506
507 } else {
508 complete(true, nil, nil);
509
510 }
511 }
512 }];
513 }
514 }
515
516 - (void)initiatorFourthPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
517 {
518 secnotice("pairing", "initiator packet 4");
519
520 [self setNextStateError:NULL complete:NULL];
521
522 NSArray *items = indata[@"d"];
523 if (![items isKindOfClass:[NSArray class]]) {
524 secnotice("pairing", "initiator no items to import");
525 complete(true, NULL, NULL);
526 return;
527 }
528
529 secnotice("pairing", "importing %lu items", (unsigned long)[items count]);
530
531 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
532 complete(true, NULL, error);
533 }] importInitialSyncCredentials:items complete:^(bool success, NSError *error) {
534 secnotice("pairing", "initiator importInitialSyncCredentials: %s: %@", success ? "yes" : "no", error);
535 if (success)
536 self->_needInitialSync = false;
537 complete(true, nil, nil);
538 }];
539 }
540
541
542
543 //MARK: - Acceptor
544
545 - (void)acceptorFirstPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
546 {
547 __weak typeof(self) weakSelf = self;
548
549 secnotice("pairing", "acceptor packet 1");
550 [self setNextStateError:NULL complete:NULL];
551
552 if (self.sessionSupportsSOS && ![self ensureControlChannel]) {
553 secnotice("pairing", "unable to establish a channel to sos control");
554 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorNoControlChannel userInfo:NULL] complete:complete];
555 return;
556 }
557
558 if (self.sessionSupportsSOS && indata[@"d"]) {
559 secnotice("pairing", "acceptor initialSyncCredentials requested");
560 self.acceptorWillSendInitialSyncCredentials = true;
561 }
562
563 if (indata[@"o"] == nil) {
564 secnotice("pairing", "initiator didn't send a octagon packet, so skipping all octagon flows");
565 self.sessionSupportsOctagon = false;
566 }
567 // XXX Before we go here we should check if we are trusted or not, if we are not, there is no point proposing octagon
568
569 NSMutableDictionary *reply = [NSMutableDictionary dictionary];
570
571 if(self.sessionSupportsSOS) {
572 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
573 complete(true, NULL, error);
574 }] validatedStashedAccountCredential:^(NSData *credential, NSError *error) {
575 secnotice("pairing", "acceptor validatedStashedAccountCredential: %@ (%@)", credential != NULL ? @"yes" : @"no", error);
576 if(credential){
577 reply[@"c"] = credential;
578
579 if (self.acceptorWillSendInitialSyncCredentials) {
580 reply[@"d"] = @YES;
581 };
582 if(self.sessionSupportsOctagon) {
583 [self acceptorFirstOctagonPacket:indata reply:reply complete:complete];
584 } else {
585 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
586 [weakSelf acceptorSecondPacket:nsdata complete:kscomplete];
587 };
588 secnotice("pairing", "acceptor reply to packet 1");
589 complete(false, reply, NULL);
590 }
591 }
592 else if ((!credential && !self.sessionSupportsOctagon) || self.testFailSOS) {
593 secnotice("pairing", "acceptor doesn't have a stashed credential: %@", error);
594 NSError *noStashCredentail = [NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorNoStashedCredential userInfo:NULL];
595 [self setNextStateError:noStashCredentail complete:complete];
596 return;
597 }
598 else if(self.sessionSupportsOctagon) {
599 [self acceptorFirstOctagonPacket:indata reply:reply complete:complete];
600 }
601 }];
602 } else if(self.sessionSupportsOctagon){
603 [self acceptorFirstOctagonPacket:indata reply:reply complete:complete];
604 } else {
605 secnotice("pairing", "acceptor neither of octagon nor SOS");
606 NSError *notSupported = [NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorNoTrustAvailable userInfo:NULL];
607 [self setNextStateError:notSupported complete:complete];
608 }
609 }
610 - (void)acceptorFirstOctagonPacket:(NSDictionary *)indata reply:(NSMutableDictionary*)reply complete:(KCPairingInternalCompletion)complete
611 {
612 __weak typeof(self) weakSelf = self;
613 NSDictionary *octagonData = indata[@"o"];
614
615 if(![octagonData isKindOfClass:[NSDictionary class]]) {
616 secnotice(pairingScope, "acceptorFirstOctagonPacket octagon data missing");
617 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorOctagonMessageMissing userInfo:NULL] complete:complete];
618 return;
619 }
620
621 NSDictionary *unpackedMessage = octagonData;
622
623 if(![unpackedMessage[@"v"] isEqualToString:@"O"]){
624 secnotice(pairingScope, "acceptorFirstOctagonPacket 'v' contents wrong");
625 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorOctagonMessageMissing userInfo:NULL] complete:complete];
626 return;
627 }
628
629 //handle epoch request and fetch epoch
630 [self.otControl rpcEpochWithConfiguration:self.joiningConfiguration reply:^(uint64_t epoch, NSError * _Nullable error) {
631 secnotice("pairing", "acceptor rpcEpochWithConfiguration: %ld (%@)", (long)epoch, error);
632 if(error || self.testFailOctagon){
633 secerror("error acceptor handling packet %d", self.counter);
634 complete(true, nil, error);
635 }else{
636 secnotice(pairingScope, "acceptor handled packet %d", self.counter);
637 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
638 [weakSelf acceptorSecondPacket:nsdata complete:kscomplete];
639 };
640 OTPairingMessage *response = [[OTPairingMessage alloc] init];
641 response.epoch = [[OTSponsorToApplicantRound1M2 alloc] init];
642 response.epoch.epoch = epoch;
643 reply[@"o"] = response.data;
644
645 secnotice("pairing", "acceptor reply to packet 1");
646 complete(false, reply, error);
647 }
648 }];
649 }
650
651 - (void)acceptorSecondPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
652 {
653 __weak typeof(self) weakSelf = self;
654
655 [self setNextStateError:NULL complete:NULL];
656
657 secnotice("pairing", "acceptor packet 2");
658 __block NSMutableDictionary *reply = [NSMutableDictionary dictionary];
659
660 NSData *peerJoinBlob = indata[@"p"];
661
662 if(self.sessionSupportsSOS && [peerJoinBlob isKindOfClass:[NSData class]]) {
663 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
664 complete(true, NULL, error);
665 }] circleJoiningBlob:peerJoinBlob complete:^(NSData *blob, NSError *error){
666
667 if (blob) {
668 secnotice("pairing", "acceptor pairing complete (will send: %s): %@",
669 self.acceptorWillSendInitialSyncCredentials ? "YES" : "NO",
670 error);
671
672 reply[@"b"] = blob;
673 }
674 if(self.sessionSupportsOctagon) {
675 [self acceptorSecondOctagonPacket:indata reply:reply complete:complete];
676 } else {
677 secnotice("pairing", "posting kSOSCCCircleOctagonKeysChangedNotification");
678 notify_post(kSOSCCCircleOctagonKeysChangedNotification);
679 if (self.acceptorWillSendInitialSyncCredentials) {
680 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
681 [weakSelf acceptorThirdPacket:nsdata complete:kscomplete];
682 };
683
684 complete(false, reply, NULL);
685 } else {
686 complete(true, reply, NULL);
687 }
688 secnotice("pairing", "acceptor reply to packet 2");
689 }
690 }];
691 } else if(self.sessionSupportsOctagon){
692 [self acceptorSecondOctagonPacket:indata reply:reply complete:complete];
693 }
694 }
695
696 - (void)acceptorSecondOctagonPacket:(NSDictionary*)indata reply:(NSMutableDictionary*)reply complete:(KCPairingInternalCompletion)complete
697 {
698 __weak typeof(self) weakSelf = self;
699 NSData *octagonData = indata[@"o"];
700
701 if(![octagonData isKindOfClass:[NSData class]]) {
702 secnotice(pairingScope, "acceptorSecondOctagonPacket octagon data missing");
703 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorOctagonMessageMissing userInfo:NULL] complete:complete];
704 return;
705 }
706
707 OTPairingMessage *pairingMessage = [[OTPairingMessage alloc] initWithData:octagonData];
708 if(!pairingMessage.hasPrepare){
709 secerror("ot-pairing: acceptorSecondOctagonPacket: no octagon message");
710 [self setNextStateError:[NSError errorWithDomain:kKCPairingChannelErrorDomain code:KCPairingErrorOctagonMessageMissing userInfo:NULL] complete:complete];
711 return;
712 }
713 OTApplicantToSponsorRound2M1 *prepare = pairingMessage.prepare;
714
715 //handle identity and fetch voucher
716 [self.otControl rpcVoucherWithConfiguration:self.joiningConfiguration
717 peerID:prepare.peerID
718 permanentInfo:prepare.permanentInfo
719 permanentInfoSig:prepare.permanentInfoSig
720 stableInfo:prepare.stableInfo
721 stableInfoSig:prepare.stableInfoSig
722 reply:^(NSData *voucher,
723 NSData *voucherSig,
724 NSError *error) {
725 if(error || self.testFailOctagon){
726 secerror("error acceptor handling octagon packet %d", self.counter);
727 complete(true, nil, error);
728 return;
729 } else {
730 bool finished = true;
731
732 secnotice(pairingScope, "acceptor handled octagon packet %d", self.counter);
733 if (OctagonPlatformSupportsSOS() && self.acceptorWillSendInitialSyncCredentials) {
734 self.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
735 [weakSelf acceptorThirdPacket:nsdata complete:kscomplete];
736 };
737 finished = false;
738 }
739 OTPairingMessage *response = [[OTPairingMessage alloc] init];
740 response.voucher = [[OTSponsorToApplicantRound2M2 alloc] init];
741 response.voucher.voucher = voucher;
742 response.voucher.voucherSignature = voucherSig;
743 reply[@"o"] = response.data;
744
745 secnotice("pairing", "acceptor reply to packet 2");
746 complete(finished ? true : false, reply, error);
747 }
748 }];
749 }
750
751 - (void)acceptorThirdPacket:(NSDictionary *)indata complete:(KCPairingInternalCompletion)complete
752 {
753 secnotice("pairing", "acceptor packet 3");
754
755 const uint32_t initialSyncCredentialsFlags =
756 SOSControlInitialSyncFlagTLK|
757 SOSControlInitialSyncFlagPCS|
758 SOSControlInitialSyncFlagBluetoothMigration;
759
760 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
761 complete(true, NULL, error);
762 }] initialSyncCredentials:initialSyncCredentialsFlags complete:^(NSArray *items, NSError *error2) {
763 NSMutableDictionary *reply = [NSMutableDictionary dictionary];
764
765 secnotice("pairing", "acceptor initialSyncCredentials complete: items %u: %@", (unsigned)[items count], error2);
766 if (items) {
767 reply[@"d"] = items;
768 }
769 secnotice("pairing", "acceptor reply to packet 3");
770 complete(true, reply, NULL);
771 }];
772 }
773
774
775 //MARK: - Helper
776
777 - (bool)ensureControlChannel
778 {
779 if (self.connection)
780 return true;
781
782 NSXPCInterface *interface = [NSXPCInterface interfaceWithProtocol:@protocol(SOSControlProtocol)];
783
784 self.connection = [[NSXPCConnection alloc] initWithMachServiceName:@(kSecuritydSOSServiceName) options:0];
785 if (self.connection == NULL){
786 return false;
787 }
788 self.connection.remoteObjectInterface = interface;
789
790 [self.connection resume];
791
792 return true;
793 }
794
795 - (void)validateStart:(void(^)(bool result, NSError *error))complete
796 {
797 if (!self.initiator) {
798 [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
799 complete(false, error);
800 }] stashedCredentialPublicKey:^(NSData *publicKey, NSError *error) {
801 complete(publicKey != NULL, error);
802 }];
803 } else {
804 dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
805 complete(true, NULL);
806 });
807 }
808 }
809
810 - (void)exchangePacket:(NSData *)inputCompressedData complete:(KCPairingChannelCompletion)complete
811 {
812 NSDictionary *indict = NULL;
813 secnotice("pairing", "Exchange packet: %u", self.counter);
814 self.counter++;
815
816 if (inputCompressedData) {
817
818 NSData *data = [self decompressData:inputCompressedData];
819 if (data == NULL) {
820 secnotice("pairing", "failed to decompress");
821 complete(true, NULL, NULL);
822 }
823
824 NSError *error = NULL;
825 indict = [NSPropertyListSerialization propertyListWithData:data
826 options:(NSPropertyListReadOptions)kCFPropertyListSupportedFormatBinary_v1_0
827 format:NULL
828 error:&error];
829 if (indict == NULL) {
830 secnotice("pairing", "failed to deserialize");
831 complete(true, NULL, error);
832 return;
833 }
834 }
835 self.nextState(indict, ^(BOOL completed, NSDictionary *outdict, NSError *error) {
836 NSData *outdata = NULL, *compressedData = NULL;
837 if (outdict) {
838 NSError *error2 = NULL;
839 outdata = [NSPropertyListSerialization dataWithPropertyList:outdict format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error2];
840 if (outdata == NULL && error)
841 error = error2;
842 if (outdata)
843 compressedData = [self compressData:outdata];
844
845 if (compressedData) {
846 NSString *key = [NSString stringWithFormat:@"com.apple.ckks.pairing.packet-size.%s.%u",
847 self->_initiator ? "initiator" : "acceptor", self->_counter];
848 SecADClientPushValueForDistributionKey((__bridge CFStringRef)key, [compressedData length]);
849 secnotice("pairing", "pairing packet size %lu", (unsigned long)[compressedData length]);
850 }
851 }
852 secnotice("pairing", "Exchange packet complete data: %@: %@", outdata ? @"YES" : @"NO", error);
853 complete(completed, compressedData, error);
854 });
855 }
856
857 - (NSData *)exchangePacket:(NSData *)data complete:(bool *)complete error:(NSError * __autoreleasing *)error
858 {
859 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
860 __block NSData *rData = NULL;
861 __block NSError* processingError;
862 [self exchangePacket:data complete:^(BOOL cComplete, NSData *cData, NSError *cError) {
863 *complete = cComplete;
864 rData = cData;
865 processingError = cError;
866 dispatch_semaphore_signal(sema);
867 }];
868 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
869 if (error)
870 *error = processingError;
871 return rData;
872 }
873
874 - (void)setXPCConnectionObject:(NSXPCConnection *)connection
875 {
876 self.connection = connection;
877 }
878
879 - (void)setControlObject:(OTControl *)control
880 {
881 self.otControl = control;
882 }
883
884 - (void)setConfiguration:(OTJoiningConfiguration *)config
885 {
886 self.joiningConfiguration = config;
887 }
888
889 - (void)setSOSMessageFailForTesting:(BOOL)value
890 {
891 self.testFailSOS = value;
892 }
893 - (void)setOctagonMessageFailForTesting:(BOOL)value
894 {
895 self.testFailOctagon = value;
896 }
897
898 - (void)setSessionSupportsOctagonForTesting:(bool)value
899 {
900 self.sessionSupportsOctagon = value;
901 }
902
903 @end