]> git.saurik.com Git - apple/security.git/blob - KeychainCircle/KCJoiningRequestCircleSession.m
Security-59754.41.1.tar.gz
[apple/security.git] / KeychainCircle / KCJoiningRequestCircleSession.m
1 //
2 // KCJoiningRequestCircleSession.m
3 // Security
4 //
5
6 #import <Foundation/Foundation.h>
7
8 #import <KeychainCircle/KCJoiningSession.h>
9
10 #import <KeychainCircle/KCError.h>
11 #import <KeychainCircle/KCDer.h>
12 #import <KeychainCircle/KCSRPContext.h>
13
14 #import <KeychainCircle/KCJoiningMessages.h>
15
16 #include <utilities/debugging.h>
17 #include "KCInitialMessageData.h"
18
19 #if OCTAGON
20 #import <Security/OTConstants.h>
21 #import "keychain/ot/OTControl.h"
22 #import "keychain/ot/OTControlProtocol.h"
23 #import "keychain/ot/OctagonControlServer.h"
24 #import "keychain/ot/OTJoiningConfiguration.h"
25 #import "KeychainCircle/KCJoiningRequestSession+Internal.h"
26 #import "keychain/ot/proto/generated_source/OTApplicantToSponsorRound2M1.h"
27 #import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound2M2.h"
28 #import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound1M2.h"
29 #import "keychain/ot/proto/generated_source/OTGlobalEnums.h"
30 #import "keychain/ot/proto/generated_source/OTSupportSOSMessage.h"
31 #import "keychain/ot/proto/generated_source/OTSupportOctagonMessage.h"
32 #import "keychain/ot/proto/generated_source/OTPairingMessage.h"
33 #endif
34 #import <KeychainCircle/NSError+KCCreationHelpers.h>
35
36 typedef enum {
37 kExpectingCircleBlob,
38 kRequestCircleDone
39 } KCJoiningRequestCircleSessionState;
40
41 @interface KCJoiningRequestCircleSession ()
42 @property (readonly) NSObject<KCJoiningRequestCircleDelegate>* circleDelegate;
43 @property (readonly) KCAESGCMDuplexSession* session;
44 @property (readwrite) KCJoiningRequestCircleSessionState state;
45 @property (nonatomic) uint64_t piggy_version;
46 #if OCTAGON
47 @property (nonatomic, strong) OTControl *otControl;
48 @property (nonatomic, strong) OTJoiningConfiguration* joiningConfiguration;
49 #endif
50 @end
51
52 @implementation KCJoiningRequestCircleSession
53
54 #if OCTAGON
55 - (void)setControlObject:(OTControl *)control{
56 self.otControl = control;
57 }
58 - (void)setContextIDOnJoiningConfiguration:(NSString*)contextID
59 {
60 self.joiningConfiguration.contextID = contextID;
61 }
62 - (KCAESGCMDuplexSession*)accessSession
63 {
64 return self.session;
65 }
66
67 #endif
68
69 - (nullable NSData*) encryptedPeerInfo: (NSError**) error {
70 // Get our peer info and send it along:
71 if (self->_session == nil) {
72 KCJoiningErrorCreate(kInternalError, error, @"Attempt to encrypt with no session");
73 return nil;
74 }
75
76 SOSPeerInfoRef us = [self.circleDelegate copyPeerInfoError:error];
77 if (us == NULL) return nil;
78 CFErrorRef cfError = NULL;
79 NSData* piEncoded = (__bridge_transfer NSData*) SOSPeerInfoCopyEncodedData(us, NULL, &cfError);
80 if(us) {
81 CFRelease(us);
82 us = NULL;
83 }
84
85 if (piEncoded == nil) {
86 if (error != nil) {
87 *error = (__bridge_transfer NSError*) cfError;
88 }
89 return nil;
90 }
91
92 return [self->_session encrypt:piEncoded error:error];
93 }
94
95 - (nullable NSData*) encryptedInitialMessage:(NSData*)prepareMessage error:(NSError**) error {
96
97 if (self->_session == nil) {
98 KCJoiningErrorCreate(kInternalError, error, @"Attempt to encrypt with no session");
99 return nil;
100 }
101
102 KCInitialMessageData *initialMessage = [[KCInitialMessageData alloc] init];
103 [initialMessage setPrepareMessage:prepareMessage];
104
105 return [self->_session encrypt:initialMessage.data error:error];
106 }
107
108
109 - (nullable NSData*) initialMessage: (NSError**) error {
110 secnotice("joining", "joining: KCJoiningRequestCircleSession initialMessage called");
111
112 #if OCTAGON
113 if(KCJoiningOctagonPiggybackingEnabled() && self.piggy_version == kPiggyV2){
114 __block NSData* next = nil;
115 __block NSError* localError = nil;
116
117 if(!self.joiningConfiguration.epoch) {
118 secerror("octagon: expected epoch! returning from piggybacking.");
119 return nil;
120 }
121
122 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
123 //giving securityd the epoch, expecting identity message
124 [self.otControl rpcPrepareIdentityAsApplicantWithConfiguration:self.joiningConfiguration
125 reply:^(NSString *peerID,
126 NSData *permanentInfo,
127 NSData *permanentInfoSig,
128 NSData *stableInfo,
129 NSData *stableInfoSig,
130 NSError *err) {
131 if(err){
132 secerror("octagon: error preparing identity: %@", err);
133 localError = err;
134 } else{
135 OTPairingMessage *pairingMessage = [[OTPairingMessage alloc]init];
136 pairingMessage.supportsSOS = [[OTSupportSOSMessage alloc] init];
137 pairingMessage.supportsOctagon = [[OTSupportOctagonMessage alloc] init];
138 OTApplicantToSponsorRound2M1 *prepareMessage = [[OTApplicantToSponsorRound2M1 alloc]init];
139 prepareMessage.peerID = peerID;
140 prepareMessage.permanentInfo = permanentInfo;
141 prepareMessage.permanentInfoSig = permanentInfoSig;
142 prepareMessage.stableInfo = stableInfo;
143 prepareMessage.stableInfoSig = stableInfoSig;
144
145 pairingMessage.prepare = prepareMessage;
146
147 pairingMessage.supportsSOS.supported = OctagonPlatformSupportsSOS() ? OTSupportType_supported : OTSupportType_not_supported;
148 pairingMessage.supportsOctagon.supported = OTSupportType_supported;
149
150 next = pairingMessage.data;
151 }
152 dispatch_semaphore_signal(sema);
153 }];
154
155 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 30)) != 0) {
156 secerror("octagon: timed out preparing identity");
157 return nil;
158 }
159 if(error){
160 *error = localError;
161 }
162
163 NSData* encryptedPi = nil;
164 if (OctagonPlatformSupportsSOS()) {
165 secnotice("joining", "doing SOS encryptedPeerInfo");
166 encryptedPi = [self encryptedPeerInfo:error];
167 if (encryptedPi == nil) return nil;
168 } else {
169 secnotice("joining", "no platform support for encryptedPeerInfo");
170 }
171
172 self->_state = kExpectingCircleBlob;
173 NSData *encryptedInitialMessage = [self encryptedInitialMessage:next error:error];
174
175 return [[KCJoiningMessage messageWithType:kPeerInfo
176 data:encryptedInitialMessage
177 payload:encryptedPi
178 error:error] der];
179 }
180 #endif
181
182 NSData* encryptedPi = [self encryptedPeerInfo:error];
183 if (encryptedPi == nil) return nil;
184
185 self->_state = kExpectingCircleBlob;
186
187 return [[KCJoiningMessage messageWithType:kPeerInfo
188 data:encryptedPi
189 error:error] der];
190
191 }
192
193 - (void) waitForOctagonUpgrade
194 {
195 #if OCTAGON
196 [self.otControl waitForOctagonUpgrade:self.joiningConfiguration.containerName context:self.joiningConfiguration.contextID reply:^(NSError *error) {
197 if(error){
198 secerror("pairing: failed to upgrade initiator into Octagon: %@", error);
199 }
200 }];
201 #endif
202 }
203
204 - (BOOL)shouldJoinSOS:(KCJoiningMessage*)message pairingMessage:(OTPairingMessage*)pairingMessage
205 {
206
207 BOOL shouldJoin = YES;
208
209 if (OctagonPlatformSupportsSOS() == NO) {
210 secnotice("joining", "platform does not support SOS");
211 shouldJoin = NO;
212 } else if (message.secondData == nil) {
213 secnotice("joining", "message does not contain SOS data");
214 shouldJoin = NO;
215 } else if (pairingMessage.hasSupportsSOS && pairingMessage.supportsSOS.supported == OTSupportType_not_supported) {
216 secnotice("joining", "acceptor explicitly does not support SOS");
217 shouldJoin = NO;
218 }
219
220 return shouldJoin;
221 }
222
223 - (NSData*) handleCircleBlob: (KCJoiningMessage*) message error: (NSError**) error {
224 secnotice("joining", "joining: KCJoiningRequestCircleSession handleCircleBlob called");
225 if ([message type] != kCircleBlob) {
226 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected CircleBlob!");
227 return nil;
228 }
229 #if OCTAGON
230 if(self.piggy_version == kPiggyV2 && KCJoiningOctagonPiggybackingEnabled() && message.firstData != nil){
231 __block NSData* nextMessage = nil;
232 __block NSError* localError = nil;
233 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
234
235 OTPairingMessage* pairingMessage = [[OTPairingMessage alloc]initWithData:message.firstData];
236 if(!pairingMessage.hasVoucher) {
237 secerror("octagon: expected voucher! returning from piggybacking.");
238 return nil;
239 }
240
241 OTSponsorToApplicantRound2M2 *voucher = pairingMessage.voucher;
242
243 //handle voucher message then join octagon
244 [self.otControl rpcJoinWithConfiguration:self.joiningConfiguration
245 vouchData:voucher.voucher
246 vouchSig:voucher.voucherSignature
247 reply:^(NSError * _Nullable err) {
248 if(err){
249 secerror("octagon: error joining octagon: %@", err);
250 localError = err;
251 }else{
252 secnotice("octagon", "successfully joined octagon");
253 }
254 dispatch_semaphore_signal(sema);
255 }];
256
257 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 30)) != 0) {
258
259 secerror("octagon: timed out joining octagon");
260 return nil;
261 }
262
263 if ([self shouldJoinSOS:message pairingMessage:pairingMessage]) {
264 secnotice("joining", "doing SOS processCircleJoinData");
265 //note we are stuffing SOS into the payload "secondData"
266 NSData* circleBlob = [self.session decryptAndVerify:message.secondData error:error];
267 if (circleBlob == nil) {
268 secnotice("joining", "decryptAndVerify failed: %@", error && *error ? *error : nil);
269 return nil;
270 }
271 if (![self.circleDelegate processCircleJoinData: circleBlob version:kPiggyV1 error:error]){
272 secerror("joining: processCircleJoinData failed %@", error && *error ? *error : nil);
273 return nil;
274 }
275 }
276
277 self->_state = kRequestCircleDone;
278
279 NSData* final = nil;
280 if(nextMessage == nil){
281 final = [NSData data];
282 }
283 self->_state = kRequestCircleDone;
284 return final;
285 }
286 #endif
287 NSData* circleBlob = [self.session decryptAndVerify:message.firstData error:error];
288 if (circleBlob == nil) return nil;
289
290 if (![self.circleDelegate processCircleJoinData: circleBlob version:kPiggyV1 error:error]) {
291 return nil;
292 } else {
293 secnotice("joining", "joined the SOS circle!");
294 #if OCTAGON
295 if(OctagonIsEnabled()) {
296 secnotice("joining", "kicking off SOS Upgrade into Octagon!");
297 [self waitForOctagonUpgrade];
298 }
299 #endif
300 }
301 self->_state = kRequestCircleDone;
302
303 return [NSData data]; // Success, an empty message.
304 }
305
306 - (NSData*) processMessage: (NSData*) incomingMessage error: (NSError**) error {
307 secnotice("joining", "joining: KCJoiningRequestCircleSession processMessage called");
308 NSData* result = nil;
309 KCJoiningMessage* message = [KCJoiningMessage messageWithDER: incomingMessage error: error];
310 if (message == nil) return nil;
311
312 switch(self.state) {
313 case kExpectingCircleBlob:
314 return [self handleCircleBlob:message error:error];
315 case kRequestCircleDone:
316 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Done, no messages expected.");
317 break;
318 }
319
320 return result;
321 }
322
323 - (bool) isDone {
324 return self.state = kRequestCircleDone;
325 }
326
327 + (instancetype) sessionWithCircleDelegate: (NSObject<KCJoiningRequestCircleDelegate>*) circleDelegate
328 session: (KCAESGCMDuplexSession*) session
329 error: (NSError**) error {
330 return [[KCJoiningRequestCircleSession alloc] initWithCircleDelegate:circleDelegate
331 session:session
332 error:error];
333 }
334
335 - (instancetype) initWithCircleDelegate: (NSObject<KCJoiningRequestCircleDelegate>*) circleDelegate
336 session: (KCAESGCMDuplexSession*) session
337 error: (NSError**) error {
338 return [self initWithCircleDelegate:circleDelegate
339 session:session
340 otcontrol:[OTControl controlObject:true error:error]
341 error:error];
342 }
343
344 - (instancetype)initWithCircleDelegate:(NSObject<KCJoiningRequestCircleDelegate>*)circleDelegate
345 session:(KCAESGCMDuplexSession*) session
346 otcontrol:(OTControl*)otcontrol
347 error:(NSError**) error
348 {
349 secnotice("joining", "joining: KCJoiningRequestCircleSession initWithCircleDelegate called, uuid=%@", session.pairingUUID);
350 if ((self = [super init])) {
351 self->_circleDelegate = circleDelegate;
352 self->_session = session;
353 self.state = kExpectingCircleBlob;
354 #if OCTAGON
355 self->_otControl = otcontrol;
356 self->_joiningConfiguration = [[OTJoiningConfiguration alloc]initWithProtocolType:@"OctagonPiggybacking"
357 uniqueDeviceID:@"requester-id"
358 uniqueClientID:@"requester-id"
359 pairingUUID:session.pairingUUID
360 containerName:nil
361 contextID:OTDefaultContext
362 epoch:session.epoch
363 isInitiator:true];
364
365 self->_piggy_version = session.piggybackingVersion;
366 #else
367 self->_piggy_version = kPiggyV1;
368 #endif
369 }
370 return self;
371 }
372
373 @end
374