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