]> git.saurik.com Git - apple/security.git/blob - KeychainCircle/KCJoiningAcceptSession.m
Security-59306.101.1.tar.gz
[apple/security.git] / KeychainCircle / KCJoiningAcceptSession.m
1 //
2 // KCJoiningAcceptSession.m
3 // Security
4 //
5 //
6
7 #import <Foundation/Foundation.h>
8
9 #import <KeychainCircle/KCJoiningSession.h>
10
11 #import <KeychainCircle/KCError.h>
12 #import <KeychainCircle/KCDer.h>
13 #import <KeychainCircle/KCJoiningMessages.h>
14
15 #import <KeychainCircle/NSError+KCCreationHelpers.h>
16 #import "KCInitialMessageData.h"
17
18 #include <corecrypto/ccder.h>
19 #include <corecrypto/ccrng.h>
20 #include <corecrypto/ccsha2.h>
21 #include <corecrypto/ccdh_gp.h>
22 #include <utilities/debugging.h>
23 #include <CommonCrypto/CommonRandomSPI.h>
24 #include <notify.h>
25
26 #if OCTAGON
27 #import "keychain/ot/OTControl.h"
28 #import "keychain/ot/OTJoiningConfiguration.h"
29 #import "KeychainCircle/KCJoiningAcceptSession+Internal.h"
30 #import "keychain/ot/proto/generated_source/OTApplicantToSponsorRound2M1.h"
31 #import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound2M2.h"
32 #import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound1M2.h"
33 #import "keychain/ot/proto/generated_source/OTPairingMessage.h"
34 #endif
35
36 typedef enum {
37 kExpectingA,
38 kExpectingM,
39 kExpectingPeerInfo,
40 kAcceptDone
41 } KCJoiningAcceptSessionState;
42
43 @interface KCJoiningAcceptSession ()
44 @property (readonly) uint64_t dsid;
45 @property (weak) id<KCJoiningAcceptSecretDelegate> secretDelegate;
46 @property (weak) id<KCJoiningAcceptCircleDelegate> circleDelegate;
47 @property (readonly) KCSRPServerContext* context;
48 @property (readonly) KCAESGCMDuplexSession* session;
49 @property (readonly) KCJoiningAcceptSessionState state;
50 @property (readwrite) NSData* startMessage;
51 @property (readwrite) NSString *piggy_uuid;
52 @property (readwrite) PiggyBackProtocolVersion piggy_version;
53 @property (readwrite) NSData* octagon;
54 #if OCTAGON
55 @property (nonatomic, strong) OTJoiningConfiguration* joiningConfiguration;
56 @property (nonatomic, strong) OTControl* otControl;
57 #endif
58 @property (nonatomic, strong) NSMutableDictionary *defaults;
59 @end
60
61 @implementation KCJoiningAcceptSession
62
63 + (nullable instancetype) sessionWithInitialMessage: (NSData*) message
64 secretDelegate: (NSObject<KCJoiningAcceptSecretDelegate>*) secretDelegate
65 circleDelegate: (NSObject<KCJoiningAcceptCircleDelegate>*) circleDelegate
66 dsid: (uint64_t) dsid
67 error: (NSError**) error {
68
69 int cc_error = 0;
70 struct ccrng_state * rng = ccrng(&cc_error);
71
72 if (rng == nil) {
73 CoreCryptoError(cc_error, error, @"RNG fetch failed");
74 return nil;
75 }
76
77 return [[KCJoiningAcceptSession alloc] initWithSecretDelegate: secretDelegate
78 circleDelegate: circleDelegate
79 dsid: dsid
80 rng: rng
81 error: error];
82 }
83
84 - (bool) setupSession: (NSError**) error {
85 NSData* key = [self->_context getKey];
86
87 if (key == nil) {
88 KCJoiningErrorCreate(kInternalError, error, @"No session key available");
89 return nil;
90 }
91
92 self->_session = [KCAESGCMDuplexSession sessionAsReceiver:key context:self.dsid];
93 #if OCTAGON
94 self.session.pairingUUID = self.joiningConfiguration.pairingUUID;
95 #endif
96 self.session.piggybackingVersion = self.piggy_version;
97
98 return self.session != nil;
99 }
100
101 - (nullable instancetype) initWithSecretDelegate: (NSObject<KCJoiningAcceptSecretDelegate>*) secretDelegate
102 circleDelegate: (NSObject<KCJoiningAcceptCircleDelegate>*) circleDelegate
103 dsid: (uint64_t) dsid
104 rng: (struct ccrng_state *)rng
105 error: (NSError**) error {
106 self = [super init];
107
108 secnotice("accepting", "initWithSecretDelegate");
109
110 NSString* name = [NSString stringWithFormat: @"%llu", dsid];
111
112 self->_context = [[KCSRPServerContext alloc] initWithUser: name
113 password: [secretDelegate secret]
114 digestInfo: ccsha256_di()
115 group: ccsrp_gp_rfc5054_3072()
116 randomSource: rng];
117 self.secretDelegate = secretDelegate;
118 self.circleDelegate = circleDelegate;
119 self->_state = kExpectingA;
120 self->_dsid = dsid;
121 self->_piggy_uuid = nil;
122 self->_defaults = [NSMutableDictionary dictionary];
123
124 #if OCTAGON
125 self->_otControl = [OTControl controlObject:true error:error];
126 self->_piggy_version = KCJoiningOctagonPiggybackingEnabled()? kPiggyV2 : kPiggyV1;
127 self->_joiningConfiguration = [[OTJoiningConfiguration alloc]initWithProtocolType:@"OctagonPiggybacking"
128 uniqueDeviceID:@"acceptor-deviceid"
129 uniqueClientID:@"requester-deviceid"
130 pairingUUID:[[NSUUID UUID] UUIDString]
131 containerName:nil
132 contextID:OTDefaultContext
133 epoch:0
134 isInitiator:false];
135 #else
136 self->_piggy_version = kPiggyV1;
137 #endif
138
139 return self;
140 }
141
142 - (NSString*) stateString {
143 switch (self.state) {
144 case kExpectingA: return @"→A";
145 case kExpectingM: return @"→M";
146 case kExpectingPeerInfo: return @"→PeerInfo";
147 case kAcceptDone: return @"done";
148 default: return [NSString stringWithFormat:@"%d", self.state];
149 }
150 }
151
152 - (NSString *)description {
153 return [NSString stringWithFormat: @"<KCJoiningAcceptSession: %lld %@ %@ uuid: %@>", self.dsid, [self stateString], self.context, self.piggy_uuid];
154 }
155
156 - (NSData*) copyChallengeMessage: (NSError**) error {
157 NSData* challenge = [self.context copyChallengeFor: self.startMessage error: error];
158 if (challenge == nil) return nil;
159
160 NSData* srpMessage = [NSData dataWithEncodedSequenceData:self.context.salt data:challenge error:error];
161
162 if (![self setupSession:error]) return nil;
163
164 return srpMessage;
165 }
166
167 #if OCTAGON
168 - (BOOL)shouldAcceptOctagonRequests {
169 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
170 __block BOOL result = NO;
171
172 OTOperationConfiguration* configuration = [[OTOperationConfiguration alloc] init];
173 configuration.discretionaryNetwork = TRUE;
174
175 [self.otControl fetchTrustStatus:self.joiningConfiguration.containerName context:self.joiningConfiguration.self.contextID
176 configuration:configuration
177 reply:^(CliqueStatus status,
178 NSString* peerID,
179 NSNumber * _Nullable numberOfPeersInOctagon,
180 BOOL isExcluded, NSError* _Nullable error)
181 {
182 secerror("octagon haveSelfEgo: status %d: %@ %@ %d: %@", (int)status,
183 peerID, numberOfPeersInOctagon, isExcluded, error);
184
185 if (status == CliqueStatusIn) {
186 result = YES;
187 }
188 dispatch_semaphore_signal(sema);
189 }];
190
191 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 30)) != 0) {
192 secerror("octagon: timed out fetching trust status");
193 return NO;
194 }
195 return result;
196 }
197 #endif
198
199 - (NSData*) processInitialMessage: (NSData*) initialMessage error: (NSError**) error {
200 __block uint64_t version = 0;
201 NSString *uuid = nil;
202 NSData *octagon = nil;
203 NSError* localError = nil;
204
205 self.startMessage = extractStartFromInitialMessage(initialMessage, &version, &uuid, &octagon, error);
206 if (self.startMessage == NULL) {
207 return nil;
208 }
209 #if OCTAGON
210 if(version == kPiggyV2 && KCJoiningOctagonPiggybackingEnabled()){
211 /* before we go ahead with octagon, let see if we are an octagon peer */
212
213 if (![self shouldAcceptOctagonRequests]) {
214 secerror("octagon refusing octagon acceptor since we don't have a selfEgo");
215 version = kPiggyV1;
216 } else {
217 self.octagon = octagon;
218 }
219 localError = nil;
220 }
221 #endif
222 self.piggy_uuid = uuid;
223 self.piggy_version = (PiggyBackProtocolVersion)version;
224
225 NSData* srpMessage = [self copyChallengeMessage: error];
226 if (srpMessage == nil) {
227 return nil;
228 }
229
230 self->_state = kExpectingM;
231 #if OCTAGON
232 NSString* piggyVersionMessage = [[NSString alloc]initWithData:self.octagon encoding:NSUTF8StringEncoding];
233 __block NSError *captureError = nil;
234
235 if(version == kPiggyV2 && KCJoiningOctagonPiggybackingEnabled() && piggyVersionMessage && [piggyVersionMessage isEqualToString:@"o"]) {
236 __block NSData* next = nil;
237 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
238
239 //fetch epoch
240 [self.otControl rpcEpochWithConfiguration:self.joiningConfiguration reply:^(uint64_t epoch, NSError * _Nullable epochError) {
241 if(epochError){
242 secerror("error retrieving next message! :%@", epochError);
243 captureError = epochError;
244 }else{
245 OTPairingMessage* responseMessage = [[OTPairingMessage alloc] init];
246 responseMessage.epoch = [[OTSponsorToApplicantRound1M2 alloc] init];
247 responseMessage.epoch.epoch = epoch;
248 next = responseMessage.data;
249 }
250 dispatch_semaphore_signal(sema);
251 }];
252
253 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 30)) != 0) {
254 secerror("octagon: timed out fetching epoch");
255 return nil;
256 }
257 if(error && captureError){
258 *error = captureError;
259 }
260 return [[KCJoiningMessage messageWithType:kChallenge
261 data:srpMessage
262 payload:next
263 error:error] der];
264 }
265 #endif
266 return [[KCJoiningMessage messageWithType:kChallenge
267 data:srpMessage
268 error:error] der];
269 }
270
271 - (NSData*) processResponse: (KCJoiningMessage*) message error:(NSError**) error {
272 if ([message type] != kResponse) {
273 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected response!");
274 return nil;
275 }
276
277 id<KCJoiningAcceptSecretDelegate> secretDelegate = self.secretDelegate;
278
279 // We handle failure, don't capture the error.
280 NSData* confirmation = [self.context copyConfirmationFor:message.firstData error:NULL];
281 if (!confirmation) {
282 // Find out what kind of error we should send.
283 NSData* errorData = nil;
284
285 KCRetryOrNot status = [secretDelegate verificationFailed: error];
286 secerror("processResponse: handle error: %d", (int)status);
287
288 switch (status) {
289 case kKCRetryError:
290 // We fill in an error if they didn't, but if they did this wont bother.
291 KCJoiningErrorCreate(kInternalError, error, @"Delegate returned error without filling in error: %@", secretDelegate);
292 return nil;
293 case kKCRetryWithSameChallenge:
294 errorData = [NSData data];
295 break;
296 case kKCRetryWithNewChallenge:
297 if ([self.context resetWithPassword:[secretDelegate secret] error:error]) {
298 errorData = [self copyChallengeMessage: error];
299 }
300 break;
301 }
302 if (errorData == nil) return nil;
303
304 return [[KCJoiningMessage messageWithType:kError
305 data:errorData
306 error:error] der];
307 }
308
309 NSData* encoded = [NSData dataWithEncodedString:[secretDelegate accountCode] error:error];
310 if (encoded == nil)
311 return nil;
312
313 NSData* encrypted = [self.session encrypt:encoded error:error];
314 if (encrypted == nil) return nil;
315
316 self->_state = kExpectingPeerInfo;
317
318 return [[KCJoiningMessage messageWithType:kVerification
319 data:confirmation
320 payload:encrypted
321 error:error] der];
322 }
323
324 - (NSData*) processSOSApplication: (NSData*) message error:(NSError**) error
325 {
326 NSData* decryptedPayload = [self.session decryptAndVerify:message error:error];
327 if (decryptedPayload == nil) return nil;
328
329 id<KCJoiningAcceptCircleDelegate> circleDelegate = self.circleDelegate;
330
331 CFErrorRef cfError = NULL;
332 SOSPeerInfoRef ref = SOSPeerInfoCreateFromData(NULL, &cfError, (__bridge CFDataRef) decryptedPayload);
333 if (ref == NULL) {
334 if (error) *error = (__bridge_transfer NSError*) cfError;
335 cfError = NULL;
336 return nil;
337 }
338
339 NSData* joinData = [circleDelegate circleJoinDataFor:ref error:error];
340 if(ref) {
341 CFRelease(ref);
342 ref = NULL;
343 }
344
345 if (joinData == nil) return nil;
346
347 SOSInitialSyncFlags flags = 0;
348 switch (self.piggy_version) {
349 case kPiggyV0:
350 break;
351 case kPiggyV1:
352 secnotice("acceptor", "piggy version is 1");
353 flags |= kSOSInitialSyncFlagTLKs | kSOSInitialSyncFlagiCloudIdentity;
354 break;
355 case kPiggyV2:
356 secnotice("acceptor", "piggy version is 2");
357 flags |= kSOSInitialSyncFlagiCloudIdentity;
358 break;
359 }
360
361 if (flags) {
362 //grab iCloud Identities, TLKs
363 NSError *localISVError = nil;
364 NSData* initialSyncData = [circleDelegate circleGetInitialSyncViews:flags error:&localISVError];
365 if(initialSyncData == NULL){
366 secnotice("piggy", "PB threw an error: %@", localISVError);
367 }
368
369 NSMutableData* growPacket = [[NSMutableData alloc] initWithData:joinData];
370 [growPacket appendData:initialSyncData];
371 joinData = growPacket;
372
373 }
374
375 NSData* encryptedOutgoing = [self.session encrypt:joinData error:error];
376 if (encryptedOutgoing == nil) return nil;
377 return encryptedOutgoing;
378 }
379
380 #if OCTAGON
381 - (OTPairingMessage *)createPairingMessageFromJoiningMessage:(KCJoiningMessage *)message error:(NSError**) error
382 {
383 NSData *decryptInitialMessage = [self.session decryptAndVerify:message.firstData error:error];
384 if(!decryptInitialMessage) {
385 secinfo("KeychainCircle", "Failed to decrypt message first data: %@. Trying legacy OTPairingMessage construction.", error ? *error : @"");
386 return [[OTPairingMessage alloc] initWithData:message.firstData];
387 } else {
388 KCInitialMessageData *initialMessage = [[KCInitialMessageData alloc] initWithData:decryptInitialMessage];
389 if(!initialMessage) {
390 secerror("Failed to parse InitialMessageData from decrypted message data");
391 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Failed to parse InitialMessageData from decrypted message data");
392 return nil;
393 }
394
395 if(!initialMessage.hasPrepareMessage) {
396 secerror("InitialMessageData does not contain prepare message");
397 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected prepare message inside InitialMessageData");
398 return nil;
399 }
400
401 return [[OTPairingMessage alloc] initWithData:initialMessage.prepareMessage];
402 }
403 }
404 #endif
405
406 - (NSData*) createTLKRequestResponse: (NSError**) error {
407 NSError* localError = NULL;
408 NSData* initialSync = [self.circleDelegate circleGetInitialSyncViews:kSOSInitialSyncFlagTLKs error:&localError];
409 if (!initialSync) {
410 secnotice("joining", "Failed to get initial sync view: %@", localError);
411 if ( error!=NULL && localError != NULL )
412 *error = localError;
413 return nil;
414 }
415
416 NSData* encryptedOutgoing = [self.session encrypt:initialSync error:&localError];
417 if (!encryptedOutgoing) {
418 secnotice("joining", "TLK request failed to encrypt: %@", localError);
419 if ( error!=NULL && localError != NULL )
420 *error = localError;
421 return nil;
422 }
423 self->_state = kAcceptDone;
424
425 secnotice("joining", "TLKRequest done.");
426
427 return [[KCJoiningMessage messageWithType:kTLKRequest
428 data:encryptedOutgoing
429 error:error] der];
430 }
431
432 - (NSData*) processApplication: (KCJoiningMessage*) message error:(NSError**) error {
433
434 if ([message type] == kTLKRequest) {
435 return [self createTLKRequestResponse: error];
436 }
437
438 if ([message type] != kPeerInfo) {
439 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected peerInfo!");
440 return nil;
441 }
442 #if OCTAGON
443 if(self.piggy_version == kPiggyV2 && KCJoiningOctagonPiggybackingEnabled()){
444 __block NSData* next = nil;
445 __block NSError* localError = nil;
446 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
447
448 OTPairingMessage *pairingMessage = [self createPairingMessageFromJoiningMessage:message error:error];
449 if(!pairingMessage) {
450 secerror("octagon, failed to create pairing message from JoiningMessage");
451 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Failed to create pairing message from JoiningMessage");
452 return nil;
453 }
454
455 if(!pairingMessage.hasPrepare) {
456 secerror("octagon, message does not contain prepare message");
457 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected prepare message!");
458 return nil;
459 }
460 OTApplicantToSponsorRound2M1 *prepareMessage = pairingMessage.prepare;
461
462 //handle identity, fetch voucher
463 [self.otControl rpcVoucherWithConfiguration:self.joiningConfiguration
464 peerID:prepareMessage.peerID
465 permanentInfo:prepareMessage.permanentInfo
466 permanentInfoSig:prepareMessage.permanentInfoSig
467 stableInfo:prepareMessage.stableInfo
468 stableInfoSig:prepareMessage.stableInfoSig reply:^(NSData *voucher,
469 NSData *voucherSig,
470 NSError *err) {
471 if(err){
472 secerror("error producing octagon voucher: %@", err);
473 localError = err;
474 }else{
475 OTPairingMessage *pairingResponse = [[OTPairingMessage alloc] init];
476 pairingResponse.voucher = [[OTSponsorToApplicantRound2M2 alloc] init];
477 pairingResponse.voucher.voucher = voucher;
478 pairingResponse.voucher.voucherSignature = voucherSig;
479 next = pairingResponse.data;
480 }
481 dispatch_semaphore_signal(sema);
482 }];
483
484 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 30)) != 0) {
485 secerror("octagon: timed out producing octagon voucher");
486 return nil;
487 }
488 if (next == NULL) {
489 if(error && localError){
490 *error = localError;
491 }
492 return nil;
493 }
494
495 NSData* encryptedOutgoing = nil;
496 if (OctagonPlatformSupportsSOS() && message.secondData) {
497 secnotice("joining", "doing SOS processSOSApplication");
498 //note we are stuffing SOS into the payload "secondData"
499 encryptedOutgoing = [self processSOSApplication: message.secondData error:error];
500 } else {
501 secnotice("joining", "no platform support processSOSApplication, peer sent data: %s",
502 message.secondData ? "yes" : "no");
503 }
504
505 self->_state = kAcceptDone;
506
507 return [[KCJoiningMessage messageWithType:kCircleBlob
508 data:next
509 payload:encryptedOutgoing
510 error:error] der];
511 }
512 #endif
513 NSData* encryptedOutgoing = [self processSOSApplication: message.firstData error:error];
514
515 self->_state = kAcceptDone;
516
517 secnotice("joining", "posting kSOSCCCircleOctagonKeysChangedNotification");
518 notify_post(kSOSCCCircleOctagonKeysChangedNotification);
519
520 return [[KCJoiningMessage messageWithType:kCircleBlob
521 data:encryptedOutgoing
522 error:error] der];
523 }
524
525
526 - (nullable NSData*) processMessage: (NSData*) incomingMessage error: (NSError**) error {
527 NSData* result = nil;
528
529 secnotice("acceptor", "processMessages: %@", [self description]);
530
531 KCJoiningMessage *message = (self.state != kExpectingA) ? [KCJoiningMessage messageWithDER:incomingMessage error:error] : nil;
532
533 switch(self.state) {
534 case kExpectingA:
535 return [self processInitialMessage:incomingMessage error: error];
536 case kExpectingM:
537 if (message == nil) return nil;
538 return [self processResponse:message error: error];
539 break;
540 case kExpectingPeerInfo:
541 if (message == nil) return nil;
542 return [self processApplication:message error: error];
543 break;
544 case kAcceptDone:
545 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Unexpected message while done");
546 break;
547 }
548 return result;
549 }
550
551 - (bool) isDone {
552 return self.state == kAcceptDone;
553 }
554
555 /* for test*/
556 #if OCTAGON
557 - (void)setControlObject:(OTControl *)control
558 {
559 self.otControl = control;
560 }
561
562 - (void)setConfiguration:(OTJoiningConfiguration *)config
563 {
564 self.joiningConfiguration = config;
565 }
566
567 - (KCAESGCMDuplexSession*)accessSession
568 {
569 return self.session;
570 }
571 #endif
572
573 @end