]> git.saurik.com Git - apple/security.git/blob - KeychainCircle/KCJoiningAcceptSession.m
Security-58286.51.6.tar.gz
[apple/security.git] / KeychainCircle / KCJoiningAcceptSession.m
1 //
2 // KCJoiningAcceptSession.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/KCJoiningMessages.h>
14
15 #import <KeychainCircle/NSError+KCCreationHelpers.h>
16
17 #include <corecrypto/ccder.h>
18 #include <corecrypto/ccrng.h>
19 #include <corecrypto/ccsha2.h>
20 #include <corecrypto/ccdh_gp.h>
21 #include <utilities/debugging.h>
22 #include <CommonCrypto/CommonRandomSPI.h>
23
24 typedef enum {
25 kExpectingA,
26 kExpectingM,
27 kExpectingPeerInfo,
28 kAcceptDone
29 } KCJoiningAcceptSessionState;
30
31 @interface KCJoiningAcceptSession ()
32 @property (readonly) uint64_t dsid;
33 @property (readonly) NSObject<KCJoiningAcceptSecretDelegate>* secretDelegate;
34 @property (readonly) NSObject<KCJoiningAcceptCircleDelegate>* circleDelegate;
35 @property (readonly) KCSRPServerContext* context;
36 @property (readonly) KCAESGCMDuplexSession* session;
37 @property (readonly) KCJoiningAcceptSessionState state;
38 @property (readwrite) NSData* startMessage;
39 @property (readwrite) NSString *piggy_uuid;
40 @property (readwrite) PiggyBackProtocolVersion piggy_version;
41 @end
42
43 @implementation KCJoiningAcceptSession
44
45 + (nullable instancetype) sessionWithInitialMessage: (NSData*) message
46 secretDelegate: (NSObject<KCJoiningAcceptSecretDelegate>*) secretDelegate
47 circleDelegate: (NSObject<KCJoiningAcceptCircleDelegate>*) circleDelegate
48 dsid: (uint64_t) dsid
49 error: (NSError**) error {
50
51 int cc_error = 0;
52 struct ccrng_state * rng = ccrng(&cc_error);
53
54 if (rng == nil) {
55 CoreCryptoError(cc_error, error, @"RNG fetch failed");
56 return nil;
57 }
58
59 return [[KCJoiningAcceptSession alloc] initWithSecretDelegate: secretDelegate
60 circleDelegate: circleDelegate
61 dsid: dsid
62 rng: rng
63 error: error];
64 }
65
66 - (bool) setupSession: (NSError**) error {
67 NSData* key = [self->_context getKey];
68
69 if (key == nil) {
70 KCJoiningErrorCreate(kInternalError, error, @"No session key available");
71 return nil;
72 }
73
74 self->_session = [KCAESGCMDuplexSession sessionAsReceiver:key context:self.dsid];
75
76 return self.session != nil;
77 }
78
79 - (nullable instancetype) initWithSecretDelegate: (NSObject<KCJoiningAcceptSecretDelegate>*) secretDelegate
80 circleDelegate: (NSObject<KCJoiningAcceptCircleDelegate>*) circleDelegate
81 dsid: (uint64_t) dsid
82 rng: (struct ccrng_state *)rng
83 error: (NSError**) error {
84 self = [super init];
85
86 NSString* name = [NSString stringWithFormat: @"%llu", dsid];
87
88 self->_context = [[KCSRPServerContext alloc] initWithUser: name
89 password: [secretDelegate secret]
90 digestInfo: ccsha256_di()
91 group: ccsrp_gp_rfc5054_3072()
92 randomSource: rng];
93 self->_secretDelegate = secretDelegate;
94 self->_circleDelegate = circleDelegate;
95 self->_state = kExpectingA;
96 self->_dsid = dsid;
97 self->_piggy_uuid = nil;
98 self->_piggy_version = kPiggyV1;
99
100 return self;
101 }
102
103 - (NSString*) stateString {
104 switch (self.state) {
105 case kExpectingA: return @"→A";
106 case kExpectingM: return @"→M";
107 case kExpectingPeerInfo: return @"→PeerInfo";
108 case kAcceptDone: return @"done";
109 default: return [NSString stringWithFormat:@"%d", self.state];
110 }
111 }
112
113 - (NSString *)description {
114 return [NSString stringWithFormat: @"<KCJoiningAcceptSession@%p %lld %@ %@>", self, self.dsid, [self stateString], self.context];
115 }
116
117 - (NSData*) copyChallengeMessage: (NSError**) error {
118 NSData* challenge = [self.context copyChallengeFor: self.startMessage error: error];
119 if (challenge == nil) return nil;
120
121 NSData* srpMessage = [NSData dataWithEncodedSequenceData:self.context.salt data:challenge error:error];
122
123 if (![self setupSession:error]) return nil;
124
125 return srpMessage;
126 }
127
128 - (NSData*) processInitialMessage: (NSData*) initialMessage error: (NSError**) error {
129 uint64_t version = 0;
130 NSString *uuid = nil;
131
132 self.startMessage = extractStartFromInitialMessage(initialMessage, &version, &uuid, error);
133 self.piggy_uuid = uuid;
134 self.piggy_version = (PiggyBackProtocolVersion)version;
135
136 if (self.startMessage == nil) return nil;
137
138 NSData* srpMessage = [self copyChallengeMessage: error];
139 if (srpMessage == nil) return nil;
140
141 self->_state = kExpectingM;
142 return [[KCJoiningMessage messageWithType:kChallenge
143 data:srpMessage
144 error:error] der];
145 }
146
147 - (NSData*) processResponse: (KCJoiningMessage*) message error:(NSError**) error {
148 if ([message type] != kResponse) {
149 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected response!");
150 return nil;
151 }
152
153 // We handle failure, don't capture the error.
154 NSData* confirmation = [self.context copyConfirmationFor:message.firstData error:NULL];
155 if (!confirmation) {
156 // Find out what kind of error we should send.
157 NSData* errorData = nil;
158
159 switch ([self.secretDelegate verificationFailed: error]) {
160 case kKCRetryError:
161 // We fill in an error if they didn't, but if they did this wont bother.
162 KCJoiningErrorCreate(kInternalError, error, @"Delegate returned error without filling in error: %@", self.secretDelegate);
163 return nil;
164 case kKCRetryWithSameChallenge:
165 errorData = [NSData data];
166 break;
167 case kKCRetryWithNewChallenge:
168 if ([self.context resetWithPassword:[self.secretDelegate secret] error:error]) {
169 errorData = [self copyChallengeMessage: error];
170 }
171 break;
172 }
173 if (errorData == nil) return nil;
174
175 return [[KCJoiningMessage messageWithType:kError
176 data:errorData
177 error:error] der];
178 }
179
180 NSData* encoded = [NSData dataWithEncodedString:[self.secretDelegate accountCode] error:error];
181 if (encoded == nil)
182 return nil;
183
184 NSData* encrypted = [self.session encrypt:encoded error:error];
185 if (encrypted == nil) return nil;
186
187 self->_state = kExpectingPeerInfo;
188
189 return [[KCJoiningMessage messageWithType:kVerification
190 data:confirmation
191 payload:encrypted
192 error:error] der];
193 }
194
195 - (NSData*) processApplication: (KCJoiningMessage*) message error:(NSError**) error {
196 if ([message type] != kPeerInfo) {
197 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected peerInfo!");
198 return nil;
199 }
200
201 NSData* decryptedPayload = [self.session decryptAndVerify:message.firstData error:error];
202 if (decryptedPayload == nil) return nil;
203
204 CFErrorRef cfError = NULL;
205 SOSPeerInfoRef ref = SOSPeerInfoCreateFromData(NULL, &cfError, (__bridge CFDataRef) decryptedPayload);
206 if (ref == NULL) {
207 if (error) *error = (__bridge_transfer NSError*) cfError;
208 cfError = NULL;
209 return nil;
210 }
211
212 NSData* joinData = [self.circleDelegate circleJoinDataFor:ref error:error];
213 if(ref) {
214 CFRelease(ref);
215 ref = NULL;
216 }
217
218 if (joinData == nil) return nil;
219
220 if(self->_piggy_version == kPiggyV1){
221 //grab iCloud Identities, TLKs
222 secnotice("acceptor", "piggy version is 1");
223 NSError *localV1Error = nil;
224 NSData* initialSyncData = [self.circleDelegate circleGetInitialSyncViews:&localV1Error];
225 if(localV1Error){
226 secnotice("piggy", "PB v1 threw an error: %@", localV1Error);
227 }
228
229 NSMutableData* growPacket = [[NSMutableData alloc] initWithData:joinData];
230 [growPacket appendData:initialSyncData];
231 joinData = growPacket;
232
233 }
234
235 NSData* encryptedOutgoing = [self.session encrypt:joinData error:error];
236 if (encryptedOutgoing == nil) return nil;
237
238 self->_state = kAcceptDone;
239
240 return [[KCJoiningMessage messageWithType:kCircleBlob
241 data:encryptedOutgoing
242 error:error] der];
243 }
244
245
246 - (nullable NSData*) processMessage: (NSData*) incomingMessage error: (NSError**) error {
247 NSData* result = nil;
248
249 KCJoiningMessage *message = (self.state != kExpectingA) ? [KCJoiningMessage messageWithDER:incomingMessage error:error] : nil;
250
251 switch(self.state) {
252 case kExpectingA:
253 return [self processInitialMessage:incomingMessage error: error];
254 case kExpectingM:
255 if (message == nil) return nil;
256 return [self processResponse:message error: error];
257 break;
258 case kExpectingPeerInfo:
259 if (message == nil) return nil;
260 return [self processApplication:message error: error];
261 break;
262 case kAcceptDone:
263 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Unexpected message while done");
264 break;
265 }
266 return result;
267 }
268
269 - (bool) isDone {
270 return self.state == kAcceptDone;
271 }
272
273 @end