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