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