]> git.saurik.com Git - apple/security.git/blob - KeychainCircle/KCJoiningAcceptSession.m
Security-57740.1.18.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
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 @end
40
41 @implementation KCJoiningAcceptSession
42
43 + (nullable instancetype) sessionWithInitialMessage: (NSData*) message
44 secretDelegate: (NSObject<KCJoiningAcceptSecretDelegate>*) secretDelegate
45 circleDelegate: (NSObject<KCJoiningAcceptCircleDelegate>*) circleDelegate
46 dsid: (uint64_t) dsid
47 error: (NSError**) error {
48
49 int cc_error = 0;
50 struct ccrng_state * rng = ccrng(&cc_error);
51
52 if (rng == nil) {
53 CoreCryptoError(cc_error, error, @"RNG fetch failed");
54 return nil;
55 }
56
57 return [[KCJoiningAcceptSession alloc] initWithSecretDelegate: secretDelegate
58 circleDelegate: circleDelegate
59 dsid: dsid
60 rng: rng
61 error: error];
62 }
63
64 - (bool) setupSession: (NSError**) error {
65 NSData* key = [self->_context getKey];
66
67 if (key == nil) {
68 KCJoiningErrorCreate(kInternalError, error, @"No session key available");
69 return nil;
70 }
71
72 self->_session = [KCAESGCMDuplexSession sessionAsReceiver:key context:self.dsid];
73
74 return self.session != nil;
75 }
76
77 - (nullable instancetype) initWithSecretDelegate: (NSObject<KCJoiningAcceptSecretDelegate>*) secretDelegate
78 circleDelegate: (NSObject<KCJoiningAcceptCircleDelegate>*) circleDelegate
79 dsid: (uint64_t) dsid
80 rng: (struct ccrng_state *)rng
81 error: (NSError**) error {
82 self = [super init];
83
84 NSString* name = [NSString stringWithFormat: @"%llu", dsid];
85
86 self->_context = [[KCSRPServerContext alloc] initWithUser: name
87 password: [secretDelegate secret]
88 digestInfo: ccsha256_di()
89 group: ccsrp_gp_rfc5054_3072()
90 randomSource: rng];
91 self->_secretDelegate = secretDelegate;
92 self->_circleDelegate = circleDelegate;
93 self->_state = kExpectingA;
94 self->_dsid = dsid;
95
96 return self;
97 }
98
99 - (NSString*) stateString {
100 switch (self.state) {
101 case kExpectingA: return @"→A";
102 case kExpectingM: return @"→M";
103 case kExpectingPeerInfo: return @"→PeerInfo";
104 case kAcceptDone: return @"done";
105 default: return [NSString stringWithFormat:@"%d", self.state];
106 }
107 }
108
109 - (NSString *)description {
110 return [NSString stringWithFormat: @"<KCJoiningAcceptSession@%p %lld %@ %@>", self, self.dsid, [self stateString], self.context];
111 }
112
113 - (NSData*) copyChallengeMessage: (NSError**) error {
114 NSData* challenge = [self.context copyChallengeFor: self.startMessage error: error];
115 if (challenge == nil) return nil;
116
117 NSData* srpMessage = [NSData dataWithEncodedSequenceData:self.context.salt data:challenge error:error];
118
119 if (![self setupSession:error]) return nil;
120
121 return srpMessage;
122 }
123
124 - (NSData*) processInitialMessage: (NSData*) initialMessage error: (NSError**) error {
125 self.startMessage = extractStartFromInitialMessage(initialMessage, error);
126 if (self.startMessage == nil) return nil;
127
128 NSData* srpMessage = [self copyChallengeMessage: error];
129 if (srpMessage == nil) return nil;
130
131 self->_state = kExpectingM;
132 return [[KCJoiningMessage messageWithType:kChallenge
133 data:srpMessage
134 error:error] der];
135 }
136
137 - (NSData*) processResponse: (KCJoiningMessage*) message error:(NSError**) error {
138 if ([message type] != kResponse) {
139 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected response!");
140 return nil;
141 }
142
143 // We handle failure, don't capture the error.
144 NSData* confirmation = [self.context copyConfirmationFor:message.firstData error:NULL];
145 if (!confirmation) {
146 // Find out what kind of error we should send.
147 NSData* errorData = nil;
148
149 switch ([self.secretDelegate verificationFailed: error]) {
150 case kKCRetryError:
151 // We fill in an error if they didn't, but if they did this wont bother.
152 KCJoiningErrorCreate(kInternalError, error, @"Delegate returned error without filling in error: %@", self.secretDelegate);
153 return nil;
154 case kKCRetryWithSameChallenge:
155 errorData = [NSData data];
156 break;
157 case kKCRetryWithNewChallenge:
158 if ([self.context resetWithPassword:[self.secretDelegate secret] error:error]) {
159 errorData = [self copyChallengeMessage: error];
160 }
161 break;
162 }
163 if (errorData == nil) return nil;
164
165 return [[KCJoiningMessage messageWithType:kError
166 data:errorData
167 error:error] der];
168 }
169
170 NSData* encoded = [NSData dataWithEncodedString:[self.secretDelegate accountCode] error:error];
171 if (encoded == nil)
172 return nil;
173
174 NSData* encrypted = [self.session encrypt:encoded error:error];
175 if (encrypted == nil) return nil;
176
177 self->_state = kExpectingPeerInfo;
178
179 return [[KCJoiningMessage messageWithType:kVerification
180 data:confirmation
181 payload:encrypted
182 error:error] der];
183 }
184
185
186 - (NSData*) processApplication: (KCJoiningMessage*) message error:(NSError**) error {
187 if ([message type] != kPeerInfo) {
188 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected peerInfo!");
189 return nil;
190 }
191
192 NSData* decryptedPayload = [self.session decryptAndVerify:message.firstData error:error];
193 if (decryptedPayload == nil) return nil;
194
195 CFErrorRef cfError = NULL;
196 SOSPeerInfoRef ref = SOSPeerInfoCreateFromData(NULL, &cfError, (__bridge CFDataRef) decryptedPayload);
197 if (ref == NULL) {
198 if (error) *error = (__bridge_transfer NSError*) cfError;
199 cfError = NULL;
200 return nil;
201 }
202
203 NSData* joinData = [self.circleDelegate circleJoinDataFor:ref error:error];
204 if (joinData == nil) return nil;
205
206 NSData* encryptedOutgoing = [self.session encrypt:joinData error:error];
207 if (encryptedOutgoing == nil) return nil;
208
209 self->_state = kAcceptDone;
210
211 return [[KCJoiningMessage messageWithType:kCircleBlob
212 data:encryptedOutgoing
213 error:error] der];
214 }
215
216
217 - (nullable NSData*) processMessage: (NSData*) incomingMessage error: (NSError**) error {
218 NSData* result = nil;
219
220 KCJoiningMessage *message = (self.state != kExpectingA) ? [KCJoiningMessage messageWithDER:incomingMessage error:error] : nil;
221
222 switch(self.state) {
223 case kExpectingA:
224 return [self processInitialMessage:incomingMessage error: error];
225 case kExpectingM:
226 if (message == nil) return nil;
227 return [self processResponse:message error: error];
228 break;
229 case kExpectingPeerInfo:
230 if (message == nil) return nil;
231 return [self processApplication:message error: error];
232 break;
233 case kAcceptDone:
234 KCJoiningErrorCreate(kUnexpectedMessage, error, @"Unexpected message while done");
235 break;
236 }
237 return result;
238 }
239
240 - (bool) isDone {
241 return self.state == kAcceptDone;
242 }
243
244 @end