]> git.saurik.com Git - apple/security.git/blob - KeychainCircle/KCJoiningRequestSecretSession.m
Security-59754.41.1.tar.gz
[apple/security.git] / KeychainCircle / KCJoiningRequestSecretSession.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 #import <Security/SecureObjectSync/SOSTypes.h>
22 #include <utilities/debugging.h>
23
24 #if OCTAGON
25 #import <Security/OTConstants.h>
26 #import "keychain/ot/OTControl.h"
27 #import "keychain/ot/OTControlProtocol.h"
28 #import "keychain/ot/OctagonControlServer.h"
29 #import "keychain/ot/OTJoiningConfiguration.h"
30 #import "KeychainCircle/KCJoiningRequestSession+Internal.h"
31
32 #import "keychain/ot/proto/generated_source/OTApplicantToSponsorRound2M1.h"
33 #import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound2M2.h"
34 #import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound1M2.h"
35 #import "keychain/ot/proto/generated_source/OTGlobalEnums.h"
36 #import "keychain/ot/proto/generated_source/OTSupportSOSMessage.h"
37 #import "keychain/ot/proto/generated_source/OTSupportOctagonMessage.h"
38 #import "keychain/ot/proto/generated_source/OTPairingMessage.h"
39 #endif
40 #import <KeychainCircle/NSError+KCCreationHelpers.h>
41
42 typedef enum {
43 kExpectingB,
44 kExpectingHAMK,
45 kRequestSecretDone
46 } KCJoiningRequestSecretSessionState;
47
48 #if OCTAGON
49 static bool KCJoiningOctagonPiggybackingDefault = false;
50 bool KCSetJoiningOctagonPiggybackingEnabled(bool value)
51 {
52 KCJoiningOctagonPiggybackingDefault = value;
53 return value;
54 }
55
56 // defaults write com.apple.security.octagon enable -bool YES
57 bool KCJoiningOctagonPiggybackingEnabled() {
58 bool result = KCJoiningOctagonPiggybackingDefault ? KCJoiningOctagonPiggybackingDefault : OctagonIsEnabled();
59 secnotice("octagon", "Octagon Piggybacking is %@ ", result ? @"on" : @"off");
60 return result;
61 }
62 #endif
63
64
65 @interface KCJoiningRequestSecretSession ()
66 @property (weak) id<KCJoiningRequestSecretDelegate> secretDelegate;
67 @property (readonly) KCSRPClientContext* context;
68 @property (readonly) uint64_t dsid;
69 @property (readonly) KCJoiningRequestSecretSessionState state;
70 @property (readwrite) NSString* piggy_uuid;
71 @property (readwrite) uint64_t piggy_version;
72 @property (readwrite) uint64_t epoch;
73 @property (readwrite) NSData* challenge;
74 @property (readwrite) NSData* salt;
75 @property (readwrite) NSString* sessionUUID;
76
77 #if OCTAGON
78 @property (nonatomic, strong) OTControl *otControl;
79 #endif
80 @property (nonatomic, strong) NSMutableDictionary *defaults;
81 @end
82
83 @implementation KCJoiningRequestSecretSession : NSObject
84
85
86 - (nullable NSData*) createUUID
87 {
88 NSUUID *uuid = [NSUUID UUID];
89 uuid_t uuidBytes;
90
91 self.piggy_uuid = [uuid UUIDString];
92 [uuid getUUIDBytes:uuidBytes];
93 NSData *uuidData = [NSData dataWithBytes:uuidBytes length:sizeof(uuid_t)];
94 return uuidData;
95 }
96
97 - (nullable NSData*) initialMessage: (NSError**) error {
98 NSData* start = [self->_context copyStart: error];
99 if (start == nil) return nil;
100
101 NSMutableData* initialMessage = NULL;
102 secnotice("joining", "joining: KCJoiningRequestSecretSession initialMessage called");
103
104 if(self.piggy_version == kPiggyV2){
105 #if OCTAGON
106 if(KCJoiningOctagonPiggybackingEnabled()){
107 NSData* uuidData = [self createUUID];
108
109 NSString* version = @"o";
110 NSData* octagonVersion = [version dataUsingEncoding:kCFStringEncodingUTF8];
111
112 initialMessage = [NSMutableData dataWithLength: sizeof_initialmessage_version2(start, kPiggyV1, uuidData, octagonVersion)];
113
114 if (NULL == encode_initialmessage_version2(start, uuidData, octagonVersion, error, initialMessage.mutableBytes, initialMessage.mutableBytes + initialMessage.length)){
115 secerror("failed to create version 2 message");
116 return nil;
117 }
118 }
119 #endif
120 }
121 else if(self.piggy_version == kPiggyV1){
122 NSData* uuidData = [self createUUID];
123 initialMessage = [NSMutableData dataWithLength: sizeof_initialmessage_version1(start, kPiggyV1, uuidData)];
124
125 if (NULL == encode_initialmessage_version1(start, uuidData, kPiggyV1, error, initialMessage.mutableBytes, initialMessage.mutableBytes + initialMessage.length)){
126 secerror("failed to create version 1 message: %@", *error);
127 return nil;
128 }
129 }
130 else{
131 initialMessage = [NSMutableData dataWithLength: sizeof_initialmessage(start)];
132 if (NULL == encode_initialmessage(start, error, initialMessage.mutableBytes, initialMessage.mutableBytes + initialMessage.length)){
133 return nil;
134 }
135 }
136
137 return initialMessage;
138 }
139
140 - (bool) isDone {
141 return self->_state == kRequestSecretDone;
142 }
143
144 - (bool) setupSession: (NSError**) error {
145 NSData* key = [self->_context getKey];
146
147 if (key == nil) {
148 KCJoiningErrorCreate(kInternalError, error, @"No session key available");
149 return nil;
150 }
151
152 self->_session = [KCAESGCMDuplexSession sessionAsSender:key context:self.dsid];
153 self.session.pairingUUID = self.sessionUUID;
154 self.session.piggybackingVersion = self.piggy_version;
155
156 return self.session != nil;
157 }
158
159 - (nullable NSData*) copyResponseForChallenge:(NSData*) challenge
160 salt:(NSData*) salt
161 secret: (NSString*) password
162 error: (NSError**) error {
163
164 secnotice("joining", "joining: KCJoiningRequestSecretSession copyResponseForChallenge called");
165 NSData* response = [self->_context copyResposeToChallenge:challenge
166 password:password
167 salt:salt
168 error:error];
169
170 if (!response) {
171 // @@@ return error to other side???
172 return nil;
173 } else {
174 if (![self setupSession: error]) return nil;
175
176 self.challenge = challenge;
177 self.salt = salt;
178
179 self->_state = kExpectingHAMK;
180 return [[KCJoiningMessage messageWithType:kResponse
181 data:response
182 error:error] der];
183 }
184 }
185
186
187 - (nullable NSData*) copyResponseForSecret: (NSString*) password
188 error: (NSError**) error {
189 return [self copyResponseForChallenge:self.challenge salt:self.salt secret:password error:error];
190 }
191
192 - (nullable NSData*) handleChallengeData: (NSData*) challengeData
193 secret: (NSString*) password
194 error: (NSError**) error {
195 secnotice("joining", "joining: KCJoiningRequestSecretSession handleChallengeData called");
196 NSData* challenge = nil;
197 NSData* salt = nil;
198
199 if (![challengeData decodeSequenceData:&salt data:&challenge error:error]) return nil;
200
201 return [self copyResponseForChallenge:challenge salt:salt secret:password error:error];
202
203 }
204
205 - (nullable NSData*) handleChallenge: (KCJoiningMessage*) message
206 secret: (NSString*) password
207 error: (NSError**)error {
208 secnotice("joining", "joining: KCJoiningRequestSecretSession handleChallenge called");
209 // Parse the challenge message
210 // Salt and Challenge packet
211 if ([message type] != kChallenge) {
212 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected challenge!");
213 return nil;
214 }
215 #if OCTAGON
216 //handle octagon data if it exists
217 if (KCJoiningOctagonPiggybackingEnabled()){
218 self.piggy_version = [message secondData] ? kPiggyV2 : kPiggyV1;
219
220 // The session may or may not exist at this point. If it doesn't, the version will be set at object creation time.
221 self.session.piggybackingVersion = self.piggy_version;
222
223 if (self.piggy_version == kPiggyV2){
224 OTPairingMessage* pairingMessage = [[OTPairingMessage alloc]initWithData: [message secondData]];
225 if (pairingMessage.hasEpoch) {
226 secnotice("octagon", "received epoch message: %@", [pairingMessage.epoch dictionaryRepresentation]);
227 self.epoch = pairingMessage.epoch.epoch;
228 }
229 else{
230 secerror("octagon: acceptor did not send its epoch. discontinuing octagon protocol. downgrading to verison 1");
231 self.piggy_version = kPiggyV1;
232 }
233 }
234 }else{
235 self.piggy_version = kPiggyV1;
236 }
237 #endif
238 return [self handleChallengeData:[message firstData] secret:password error:error];
239 }
240
241 - (NSData*) handleChallenge: (KCJoiningMessage*) message error: (NSError**)error {
242 return [self handleChallenge:message
243 secret:[self.secretDelegate secret]
244 error:error];
245
246 }
247
248 - (NSData*) handleVerification: (KCJoiningMessage*) message error: (NSError**) error {
249 secnotice("joining", "joining: KCJoiningRequestSecretSession handleVerification called");
250 id<KCJoiningRequestSecretDelegate> secretDelegate = self.secretDelegate;
251
252 if ([message type] == kError) {
253 bool newCode = [[message firstData] length] == 0;
254 NSString* nextSecret = [secretDelegate verificationFailed: newCode];
255
256 if (nextSecret) {
257 if (newCode) {
258 return [self copyResponseForSecret:nextSecret error:error];
259 } else {
260 return [self handleChallengeData:[message firstData] secret:nextSecret error:error];
261 }
262 } else {
263 return nil;
264 }
265 }
266
267 if ([message type] != kVerification) {
268 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected verification!");
269 return nil;
270 }
271
272 if (![self.context verifyConfirmation:[message firstData] error:error]) {
273 // Sender thought we had it right, but he can't prove he has it right!
274 KCJoiningErrorCreate(kInternalError, error, @"Got verification but acceptor doesn't have matching secret: %@", self);
275 secnotice("request-session", "Verification failed: %@", self);
276 return nil;
277 }
278
279 {
280 NSData* payload = [self.session decryptAndVerify:[message secondData] error:error];
281 if (payload == nil) return nil;
282
283 NSString* accountCode = [NSString decodeFromDER:payload error:error];
284 if (accountCode == nil) return nil;
285
286 if (![secretDelegate processAccountCode:accountCode error:error]) return nil;
287 }
288
289 self->_state = kRequestSecretDone;
290
291 return [NSData data];
292 }
293
294 - (NSData*) processMessage: (NSData*) incomingMessage error: (NSError**) error {
295 secnotice("joining", "joining: KCJoiningRequestSecretSession processMessage called");
296 NSData* result = nil;
297 KCJoiningMessage* message = [KCJoiningMessage messageWithDER: incomingMessage error: error];
298 if (message == nil) return nil;
299
300 switch(self->_state) {
301 case kExpectingB:
302 return [self handleChallenge:message error: error];
303 break;
304 case kExpectingHAMK:
305 return [self handleVerification:message error:error];
306 break;
307 case kRequestSecretDone:
308 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Done, no messages expected.");
309 break;
310 }
311
312 return result;
313 }
314
315 + (nullable instancetype)sessionWithSecretDelegate: (NSObject<KCJoiningRequestSecretDelegate>*) secretDelegate
316 dsid: (uint64_t)dsid
317 error: (NSError**) error {
318 return [[KCJoiningRequestSecretSession alloc] initWithSecretDelegate:secretDelegate
319 dsid:dsid
320 error:error];
321 }
322
323 - (nullable instancetype)initWithSecretDelegate: (NSObject<KCJoiningRequestSecretDelegate>*) secretDelegate
324 dsid: (uint64_t)dsid
325 error: (NSError**)error {
326 int cc_error = 0;
327 struct ccrng_state * rng = ccrng(&cc_error);
328
329 if (rng == nil) {
330 CoreCryptoError(cc_error, error, @"RNG fetch failed");
331 return nil;
332 }
333
334 return [self initWithSecretDelegate: secretDelegate
335 dsid: dsid
336 rng: rng
337 error: error];
338 }
339
340 - (nullable instancetype)initWithSecretDelegate: (NSObject<KCJoiningRequestSecretDelegate>*) secretDelegate
341 dsid: (uint64_t)dsid
342 rng: (struct ccrng_state *)rng
343 error: (NSError**)error {
344 secnotice("joining", "joining: initWithSecretDelegate called");
345 if ((self = [super init])) {
346 self->_secretDelegate = secretDelegate;
347 self->_state = kExpectingB;
348 self->_dsid = dsid;
349 self->_defaults = [NSMutableDictionary dictionary];
350
351 #if OCTAGON
352 self->_piggy_version = KCJoiningOctagonPiggybackingEnabled() ? kPiggyV2 : kPiggyV1;
353 self->_otControl = [OTControl controlObject:true error:error];
354
355 _sessionUUID = [[NSUUID UUID] UUIDString];
356 #else
357 self->_piggy_version = kPiggyV1;
358 #endif
359
360 secnotice("joining", "joining: initWithSecretDelegate called, uuid=%@", self.sessionUUID);
361
362 NSString* name = [NSString stringWithFormat: @"%llu", dsid];
363
364 self->_context = [[KCSRPClientContext alloc] initWithUser: name
365 digestInfo: ccsha256_di()
366 group: ccsrp_gp_rfc5054_3072()
367 randomSource: rng];
368 }
369 return self;
370 }
371
372 - (NSString*) stateString {
373 switch (self.state) {
374 case kExpectingB: return @"→B";
375 case kExpectingHAMK: return @"→HAMK";
376 case kRequestSecretDone: return @"SecretDone";
377 default: return [NSString stringWithFormat:@"%d", self.state];
378 }
379 }
380
381 - (NSString *)description {
382 return [NSString stringWithFormat: @"<KCJoiningAcceptSession@%p %lld %@ %@>", self, self.dsid, [self stateString], self.context];
383 }
384 #if OCTAGON
385 /* for test */
386 -(void)setControlObject:(OTControl*)control
387 {
388 self.otControl = control;
389 }
390 #endif
391
392 @end