]> git.saurik.com Git - apple/security.git/blob - KeychainCircle/KCJoiningRequestSession.m
Security-58286.70.7.tar.gz
[apple/security.git] / KeychainCircle / KCJoiningRequestSession.m
1 //
2 // KCJoiningSession.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/KCSRPContext.h>
14
15 #import <KeychainCircle/KCJoiningMessages.h>
16
17 #include <corecrypto/ccrng.h>
18 #include <corecrypto/ccsha2.h>
19 #include <corecrypto/ccdh_gp.h>
20 #include <corecrypto/ccder.h>
21 #include <CommonCrypto/CommonRandomSPI.h>
22
23 #include <utilities/debugging.h>
24
25 #import <KeychainCircle/NSError+KCCreationHelpers.h>
26
27 typedef enum {
28 kExpectingB,
29 kExpectingHAMK,
30 kRequestSecretDone
31 } KCJoiningRequestSecretSessionState;
32
33 typedef enum {
34 kExpectingCircleBlob,
35 kRequestCircleDone
36 } KCJoiningRequestCircleSessionState;
37
38
39 @interface KCJoiningRequestSecretSession ()
40 @property (readonly) NSObject<KCJoiningRequestSecretDelegate>* secretDelegate;
41 @property (readonly) KCSRPClientContext* context;
42 @property (readonly) uint64_t dsid;
43 @property (readonly) KCJoiningRequestSecretSessionState state;
44 @property (readwrite) NSString* piggy_uuid;
45 @property (readwrite) uint64_t piggy_version;
46 @property (readwrite) NSData* challenge;
47 @property (readwrite) NSData* salt;
48 @end
49
50 static const uint64_t KCProtocolVersion = kPiggyV1;
51
52 @implementation KCJoiningRequestSecretSession : NSObject
53
54 - (nullable NSData*) initialMessage: (NSError**) error {
55 NSData* start = [self->_context copyStart: error];
56 if (start == nil) return nil;
57
58 NSMutableData* initialMessage = NULL;
59
60
61 if(KCProtocolVersion == kPiggyV1)
62 {
63 NSUUID *uuid = [NSUUID UUID];
64 uuid_t uuidBytes;
65
66 self.piggy_uuid = [uuid UUIDString];
67 [uuid getUUIDBytes:uuidBytes];
68 NSData *uuidData = [NSData dataWithBytes:uuidBytes length:sizeof(uuid_t)];
69
70 initialMessage = [NSMutableData dataWithLength: sizeof_initialmessage_version1(start, KCProtocolVersion, uuidData)];
71
72 if (NULL == encode_initialmessage_version1(start, uuidData, KCProtocolVersion, error, initialMessage.mutableBytes, initialMessage.mutableBytes + initialMessage.length))
73 return nil;
74 }
75 else{
76 initialMessage = [NSMutableData dataWithLength: sizeof_initialmessage(start)];
77 if (NULL == encode_initialmessage(start, error, initialMessage.mutableBytes, initialMessage.mutableBytes + initialMessage.length))
78 return nil;
79 }
80
81 return initialMessage;
82 }
83
84 - (bool) isDone {
85 return self->_state == kRequestSecretDone;
86 }
87
88 - (bool) setupSession: (NSError**) error {
89 NSData* key = [self->_context getKey];
90
91 if (key == nil) {
92 KCJoiningErrorCreate(kInternalError, error, @"No session key available");
93 return nil;
94 }
95
96 self->_session = [KCAESGCMDuplexSession sessionAsSender:key context:self.dsid];
97
98 return self.session != nil;
99 }
100
101 - (nullable NSData*) copyResponseForChallenge:(NSData*) challenge
102 salt:(NSData*) salt
103 secret: (NSString*) password
104 error: (NSError**) error {
105 NSData* response = [self->_context copyResposeToChallenge:challenge
106 password:password
107 salt:salt
108 error:error];
109
110 if (!response) {
111 // @@@ return error to other side???
112 return nil;
113 } else {
114 if (![self setupSession: error]) return nil;
115
116 self.challenge = challenge;
117 self.salt = salt;
118
119 self->_state = kExpectingHAMK;
120 return [[KCJoiningMessage messageWithType:kResponse
121 data:response
122 error:error] der];
123 }
124 }
125
126
127 - (nullable NSData*) copyResponseForSecret: (NSString*) password
128 error: (NSError**) error {
129 return [self copyResponseForChallenge:self.challenge salt:self.salt secret:password error:error];
130 }
131
132 - (nullable NSData*) handleChallengeData: (NSData*) challengeData
133 secret: (NSString*) password
134 error: (NSError**) error {
135 NSData* challenge = nil;
136 NSData* salt = nil;
137
138 if (![challengeData decodeSequenceData:&salt data:&challenge error:error]) return nil;
139
140 return [self copyResponseForChallenge:challenge salt:salt secret:password error:error];
141
142 }
143
144 - (nullable NSData*) handleChallenge: (KCJoiningMessage*) message
145 secret: (NSString*) password
146 error: (NSError**)error {
147 // Parse the challenge message
148 // Salt and Challenge packet
149 if ([message type] != kChallenge) {
150 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected challenge!");
151 return nil;
152 }
153
154 return [self handleChallengeData:[message firstData] secret:password error:error];
155 }
156
157 - (NSData*) handleChallenge: (KCJoiningMessage*) message error: (NSError**)error {
158 return [self handleChallenge:message
159 secret:[self.secretDelegate secret]
160 error:error];
161
162 }
163
164 - (NSData*) handleVerification: (KCJoiningMessage*) message error: (NSError**) error {
165 if ([message type] == kError) {
166 bool newCode = [[message firstData] length] == 0;
167 NSString* nextSecret = [self.secretDelegate verificationFailed: newCode];
168
169 if (nextSecret) {
170 if (newCode) {
171 return [self copyResponseForSecret:nextSecret error:error];
172 } else {
173 return [self handleChallengeData:[message firstData] secret:nextSecret error:error];
174 }
175 } else {
176 return nil;
177 }
178 }
179
180 if ([message type] != kVerification) {
181 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected verification!");
182 return nil;
183 }
184
185 if (![self.context verifyConfirmation:[message firstData] error:error]) {
186 // Sender thought we had it right, but he can't prove he has it right!
187 KCJoiningErrorCreate(kInternalError, error, @"Got verification but acceptor doesn't have matching secret: %@", self);
188 secnotice("request-session", "Verification failed: %@", self);
189 return nil;
190 }
191
192 {
193 NSData* payload = [self.session decryptAndVerify:[message secondData] error:error];
194 if (payload == nil) return nil;
195
196 NSString* accountCode = [NSString decodeFromDER:payload error:error];
197 if (accountCode == nil) return nil;
198
199 if (![self.secretDelegate processAccountCode:accountCode error:error]) return nil;
200 }
201
202 self->_state = kRequestSecretDone;
203
204 return [NSData data];
205 }
206
207
208
209
210 // [self.delegate processCircleJoinData:circleData error:error];
211
212 - (NSData*) processMessage: (NSData*) incomingMessage error: (NSError**) error {
213 NSData* result = nil;
214 KCJoiningMessage* message = [KCJoiningMessage messageWithDER: incomingMessage error: error];
215 if (message == nil) return nil;
216
217 switch(self->_state) {
218 case kExpectingB:
219 return [self handleChallenge:message error: error];
220 break;
221 case kExpectingHAMK:
222 return [self handleVerification:message error:error];
223 break;
224 case kRequestSecretDone:
225 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Done, no messages expected.");
226 break;
227 }
228
229 return result;
230 }
231
232 + (nullable instancetype)sessionWithSecretDelegate: (NSObject<KCJoiningRequestSecretDelegate>*) secretDelegate
233 dsid: (uint64_t)dsid
234 error: (NSError**) error {
235 return [[KCJoiningRequestSecretSession alloc] initWithSecretDelegate:secretDelegate
236 dsid:dsid
237 error:error];
238 }
239
240 - (nullable instancetype)initWithSecretDelegate: (NSObject<KCJoiningRequestSecretDelegate>*) secretDelegate
241 dsid: (uint64_t)dsid
242 error: (NSError**)error {
243 int cc_error = 0;
244 struct ccrng_state * rng = ccrng(&cc_error);
245
246 if (rng == nil) {
247 CoreCryptoError(cc_error, error, @"RNG fetch failed");
248 return nil;
249 }
250
251 return [self initWithSecretDelegate: secretDelegate
252 dsid: dsid
253 rng: rng
254 error: error];
255 }
256
257 - (nullable instancetype)initWithSecretDelegate: (NSObject<KCJoiningRequestSecretDelegate>*) secretDelegate
258 dsid: (uint64_t)dsid
259 rng: (struct ccrng_state *)rng
260 error: (NSError**)error {
261
262 self = [super init];
263
264 self->_secretDelegate = secretDelegate;
265 self->_state = kExpectingB;
266 self->_dsid = dsid;
267
268 NSString* name = [NSString stringWithFormat: @"%llu", dsid];
269
270 self->_context = [[KCSRPClientContext alloc] initWithUser: name
271 digestInfo: ccsha256_di()
272 group: ccsrp_gp_rfc5054_3072()
273 randomSource: rng];
274
275 return self;
276 }
277
278 - (NSString*) stateString {
279 switch (self.state) {
280 case kExpectingB: return @"→B";
281 case kExpectingHAMK: return @"→HAMK";
282 case kRequestSecretDone: return @"SecretDone";
283 default: return [NSString stringWithFormat:@"%d", self.state];
284 }
285 }
286
287 - (NSString *)description {
288 return [NSString stringWithFormat: @"<KCJoiningAcceptSession@%p %lld %@ %@>", self, self.dsid, [self stateString], self.context];
289 }
290
291 @end
292
293 @interface KCJoiningRequestCircleSession ()
294 @property (readonly) NSObject<KCJoiningRequestCircleDelegate>* circleDelegate;
295 @property (readonly) KCAESGCMDuplexSession* session;
296 @property (readwrite) KCJoiningRequestCircleSessionState state;
297 @end
298
299 @implementation KCJoiningRequestCircleSession
300 - (nullable NSData*) encryptedPeerInfo: (NSError**) error {
301 // Get our peer info and send it along:
302 if (self->_session == nil) {
303 KCJoiningErrorCreate(kInternalError, error, @"Attempt to encrypt with no session");
304 return nil;
305 }
306
307 SOSPeerInfoRef us = [self.circleDelegate copyPeerInfoError:error];
308 if (us == NULL) return nil;
309 CFErrorRef cfError = NULL;
310 NSData* piEncoded = (__bridge_transfer NSData*) SOSPeerInfoCopyEncodedData(us, NULL, &cfError);
311 if(us) {
312 CFRelease(us);
313 us = NULL;
314 }
315
316 if (piEncoded == nil) {
317 if (error != nil) {
318 *error = (__bridge_transfer NSError*) cfError;
319 }
320 return nil;
321 }
322
323 return [self->_session encrypt:piEncoded error:error];
324 }
325
326 - (nullable NSData*) initialMessage: (NSError**) error {
327 NSData* encryptedPi = [self encryptedPeerInfo:error];
328 if (encryptedPi == nil) return nil;
329
330 self->_state = kExpectingCircleBlob;
331
332 return [[KCJoiningMessage messageWithType:kPeerInfo
333 data:encryptedPi
334 error:error] der];
335
336 }
337
338 - (NSData*) handleCircleBlob: (KCJoiningMessage*) message error: (NSError**) error {
339 if ([message type] != kCircleBlob) {
340 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected CircleBlob!");
341 return nil;
342 }
343
344 NSData* circleBlob = [self.session decryptAndVerify:message.firstData error:error];
345 if (circleBlob == nil) return nil;
346
347 if (![self.circleDelegate processCircleJoinData: circleBlob version:kPiggyV1 error:error])
348 return nil;
349
350 self->_state = kRequestCircleDone;
351
352 return [NSData data]; // Success, an empty message.
353 }
354
355 - (NSData*) processMessage: (NSData*) incomingMessage error: (NSError**) error {
356 NSData* result = nil;
357 KCJoiningMessage* message = [KCJoiningMessage messageWithDER: incomingMessage error: error];
358 if (message == nil) return nil;
359
360 switch(self.state) {
361 case kExpectingCircleBlob:
362 return [self handleCircleBlob:message error:error];
363 case kRequestCircleDone:
364 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Done, no messages expected.");
365 break;
366 }
367
368 return result;
369 }
370
371 - (bool) isDone {
372 return self.state = kRequestCircleDone;
373 }
374
375 + (instancetype) sessionWithCircleDelegate: (NSObject<KCJoiningRequestCircleDelegate>*) circleDelegate
376 session: (KCAESGCMDuplexSession*) session
377 error: (NSError**) error {
378 return [[KCJoiningRequestCircleSession alloc] initWithCircleDelegate:circleDelegate
379 session:session
380 error:error];
381 }
382
383 - (instancetype) initWithCircleDelegate: (NSObject<KCJoiningRequestCircleDelegate>*) circleDelegate
384 session: (KCAESGCMDuplexSession*) session
385 error: (NSError**) error {
386 self = [super init];
387
388 self->_circleDelegate = circleDelegate;
389 self->_session = session;
390 self.state = kExpectingCircleBlob;
391
392 return self;
393 }
394
395 @end
396