]> git.saurik.com Git - apple/security.git/blob - sec/SOSCircle/SecureObjectSync/SOSCoder.c
581d1f2f765d94f3fcc31b498215bf8bfcfc1415
[apple/security.git] / sec / SOSCircle / SecureObjectSync / SOSCoder.c
1 //
2 // SOSCoder.c
3 // sec
4 //
5 // Created by Richard Murphy on 2/6/13.
6 //
7 //
8 #include <stdlib.h>
9
10 #include <CoreFoundation/CFBase.h>
11 #include <CoreFoundation/CFError.h>
12
13 #include <Security/SecBasePriv.h>
14 #include <Security/SecOTR.h>
15 #include <Security/SecOTRSession.h>
16 #include <SecureObjectSync/SOSInternal.h>
17 #include <SecureObjectSync/SOSFullPeerInfo.h>
18 #include <SecureObjectSync/SOSPeerInfo.h>
19 #include <SecureObjectSync/SOSPeer.h>
20 #include <SecureObjectSync/SOSCoder.h>
21
22 #include <utilities/SecCFRelease.h>
23 #include <utilities/SecCFWrappers.h>
24 #include <utilities/SecIOFormat.h>
25 #include <utilities/SecCFError.h>
26 #include <utilities/debugging.h>
27
28 #include <utilities/der_plist.h>
29 #include <utilities/der_plist_internal.h>
30
31 #include <corecrypto/ccder.h>
32 #include <utilities/iCloudKeychainTrace.h>
33
34 #include "AssertMacros.h"
35
36 struct __OpaqueSOSCoder {
37 CFStringRef peer_id;
38 SecOTRSessionRef sessRef;
39 bool waitingForDataPacket;
40 };
41
42 static const char *SOSPeerCoderString(SOSPeerCoderStatus coderStatus) {
43 switch (coderStatus) {
44 case kSOSPeerCoderDataReturned: return "DataReturned";
45 case kSOSPeerCoderNegotiating: return "Negotiating";
46 case kSOSPeerCoderNegotiationCompleted: return "NegotiationCompleted";
47 case kSOSPeerCoderFailure: return "Failure";
48 case kSOSPeerCoderStaleEvent: return "StaleEvent";
49 default: return "StatusUnknown";
50 }
51 }
52
53 static size_t der_sizeof_bool(bool value) {
54 return ccder_sizeof(CCDER_BOOLEAN, 1);
55 }
56
57 static uint8_t* der_encode_bool(bool value, const uint8_t *der, uint8_t *der_end) {
58 uint8_t valueByte = value;
59 return ccder_encode_tl(CCDER_BOOLEAN, 1, der,
60 ccder_encode_body(1, &valueByte, der, der_end));
61 }
62
63 static const uint8_t* der_decode_bool(bool *value, const uint8_t *der, const uint8_t *der_end) {
64 size_t payload_size = 0;
65
66 der = ccder_decode_tl(CCDER_BOOLEAN, &payload_size, der, der_end);
67
68 if (payload_size != 1) {
69 der = NULL;
70 }
71
72 if (der != NULL) {
73 *value = (*der != 0);
74 der++;
75 }
76
77 return der;
78 }
79
80 static CFMutableDataRef sessSerialized(SOSCoderRef coder, CFErrorRef *error) {
81 CFMutableDataRef otr_state = NULL;
82
83 if(!coder || !coder->sessRef) {
84 SOSCreateErrorWithFormat(kSOSErrorUnexpectedType, NULL, error, 0, CFSTR("No session reference."));
85 return NULL;
86 }
87
88 if ((otr_state = CFDataCreateMutable(NULL, 0)) == NULL) {
89 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure, NULL, error, 0, CFSTR("Mutable Data allocation failed."));
90 return NULL;
91 }
92
93 if (errSecSuccess != SecOTRSAppendSerialization(coder->sessRef, otr_state)) {
94 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, 0, CFSTR("Append Serialization failed."));
95 CFReleaseSafe(otr_state);
96 return NULL;
97 }
98
99 return otr_state;
100
101 }
102
103 static size_t SOSCoderGetDEREncodedSize(SOSCoderRef coder, CFErrorRef *error) {
104 size_t encoded_size = 0;
105 CFMutableDataRef otr_state = sessSerialized(coder, error);
106
107 if (otr_state) {
108 size_t data_size = der_sizeof_data(otr_state, error);
109 size_t waiting_size = der_sizeof_bool(coder->waitingForDataPacket);
110
111 if ((data_size != 0) && (waiting_size != 0))
112 {
113 encoded_size = ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, data_size + waiting_size);
114 }
115 CFReleaseSafe(otr_state);
116 }
117 return encoded_size;
118 }
119
120 static uint8_t* SOSCoderEncodeToDER(SOSCoderRef coder, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) {
121 if(!der_end) return NULL;
122 uint8_t* result = NULL;
123 CFMutableDataRef otr_state = sessSerialized(coder, error);
124
125 if(otr_state) {
126 result = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
127 der_encode_data(otr_state, error, der,
128 der_encode_bool(coder->waitingForDataPacket, der, der_end)));
129 CFReleaseSafe(otr_state);
130 }
131 return result;
132 }
133
134
135 CFDataRef SOSCoderCopyDER(SOSCoderRef coder, CFErrorRef* error) {
136 CFMutableDataRef encoded = NULL;
137 size_t encoded_size = SOSCoderGetDEREncodedSize(coder, error);
138
139 if (encoded_size > 0) {
140 encoded = CFDataCreateMutable(NULL, encoded_size);
141 if (encoded) {
142 CFDataSetLength(encoded, encoded_size);
143 uint8_t * der = CFDataGetMutableBytePtr(encoded);
144 uint8_t * der_end = der + encoded_size;
145 if (!SOSCoderEncodeToDER(coder, error, der, der_end)) {
146 CFReleaseNull(encoded);
147 encoded = NULL;
148 }
149 }
150 }
151 return encoded;
152 }
153
154 SOSCoderRef SOSCoderCreateFromData(CFDataRef exportedData, CFErrorRef *error) {
155
156 SOSCoderRef p = calloc(1, sizeof(struct __OpaqueSOSCoder));
157
158 const uint8_t *der = CFDataGetBytePtr(exportedData);
159 const uint8_t *der_end = der + CFDataGetLength(exportedData);
160
161 CFDataRef otr_data = NULL;
162
163 ccder_tag tag;
164 require(ccder_decode_tag(&tag, der, der_end),fail);
165
166 switch (tag) {
167 case CCDER_OCTET_STRING:
168 {
169 der = der_decode_data(kCFAllocatorDefault, 0, &otr_data, error, der, der_end);
170 p->waitingForDataPacket = false;
171 }
172 break;
173
174 case CCDER_CONSTRUCTED_SEQUENCE:
175 {
176 const uint8_t *sequence_end = NULL;
177 der = ccder_decode_sequence_tl(&sequence_end, der, der_end);
178
179 require_action_quiet(sequence_end == der_end, fail, SecCFDERCreateError(kSOSErrorDecodeFailure, CFSTR("Extra data in SOS coder"), NULL, error));
180
181 der = der_decode_data(kCFAllocatorDefault, 0, &otr_data, error, der, der_end);
182 der = der_decode_bool(&p->waitingForDataPacket, der, sequence_end);
183 }
184 break;
185
186 default:
187 SecCFDERCreateError(kSOSErrorDecodeFailure, CFSTR("Unsupported SOS Coder DER"), NULL, error);
188 goto fail;
189 }
190
191 require(der, fail);
192
193 p->sessRef = SecOTRSessionCreateFromData(NULL, otr_data);
194 require(p->sessRef, fail);
195
196 CFReleaseSafe(otr_data);
197 return p;
198
199 fail:
200 SOSCoderDispose(p);
201 CFReleaseSafe(otr_data);
202 return NULL;
203 }
204
205
206 SOSCoderRef SOSCoderCreate(SOSPeerInfoRef peerInfo, SOSFullPeerInfoRef myPeerInfo, CFErrorRef *error) {
207 CFAllocatorRef allocator = CFGetAllocator(peerInfo);
208
209 SOSCoderRef p = calloc(1, sizeof(struct __OpaqueSOSCoder));
210
211 SecOTRFullIdentityRef myRef = NULL;
212 SecOTRPublicIdentityRef peerRef = NULL;
213 SecKeyRef privateKey = NULL;
214 SecKeyRef publicKey = NULL;
215
216 if (myPeerInfo && peerInfo) {
217 privateKey = SOSFullPeerInfoCopyDeviceKey(myPeerInfo, error);
218 require_quiet(privateKey, errOut);
219
220 myRef = SecOTRFullIdentityCreateFromSecKeyRef(allocator, privateKey, error);
221 require_quiet(myRef, errOut);
222
223 CFReleaseNull(privateKey);
224
225 publicKey = SOSPeerInfoCopyPubKey(peerInfo);
226
227 peerRef = SecOTRPublicIdentityCreateFromSecKeyRef(allocator, publicKey, error);
228 require_quiet(peerRef, errOut);
229
230 p->sessRef = SecOTRSessionCreateFromID(allocator, myRef, peerRef);
231
232 require(p->sessRef, errOut);
233
234 p->waitingForDataPacket = false;
235
236 CFReleaseNull(publicKey);
237 CFReleaseNull(privateKey);
238 CFReleaseNull(myRef);
239 CFReleaseNull(peerRef);
240 } else {
241 secnotice("coder", "NULL Coder requested, no transport security");
242 }
243
244 return p;
245
246 errOut:
247 secerror("Coder create failed: %@\n", *error);
248 CFReleaseNull(myRef);
249 CFReleaseNull(peerRef);
250 CFReleaseNull(publicKey);
251 CFReleaseNull(privateKey);
252
253 free(p);
254 return NULL;
255 }
256
257 void SOSCoderDispose(SOSCoderRef coder)
258 {
259 CFReleaseNull(coder->sessRef);
260
261 free(coder);
262 }
263
264 void SOSCoderReset(SOSCoderRef coder)
265 {
266 SecOTRSessionReset(coder->sessRef);
267 coder->waitingForDataPacket = false;
268 }
269
270 static bool SOSOTRSAppendStartPacket(SecOTRSessionRef session, CFMutableDataRef appendPacket, CFErrorRef *error) {
271 OSStatus otrStatus = SecOTRSAppendStartPacket(session, appendPacket);
272 if (otrStatus != errSecSuccess) {
273 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("append start packet returned: %" PRIdOSStatus), otrStatus);
274 }
275 return otrStatus == errSecSuccess;
276 }
277
278 // Start OTR negotiation if we haven't already done so.
279 SOSPeerCoderStatus
280 SOSCoderStart(SOSCoderRef coder, SOSPeerSendBlock sendBlock, CFStringRef clientId, CFErrorRef *error) {
281 CFMutableStringRef action = CFStringCreateMutable(kCFAllocatorDefault, 0);
282 CFStringRef beginState = NULL;
283 SOSPeerCoderStatus result = kSOSPeerCoderFailure;
284 CFMutableDataRef startPacket = NULL;
285
286 require_action_quiet(coder->sessRef, coderFailure, CFStringAppend(action, CFSTR("*** no otr session ***")));
287 beginState = CFCopyDescription(coder->sessRef);
288 require_action_quiet(!coder->waitingForDataPacket, negotiatingOut, CFStringAppend(action, CFSTR("waiting for peer to send first data packet")));
289 require_action_quiet(!SecOTRSGetIsReadyForMessages(coder->sessRef), coderFailure, CFStringAppend(action, CFSTR("otr session ready"));
290 result = kSOSPeerCoderDataReturned);
291 require_action_quiet(SecOTRSGetIsIdle(coder->sessRef), negotiatingOut, CFStringAppend(action, CFSTR("otr negotiating already")));
292 require_action_quiet(startPacket = CFDataCreateMutable(kCFAllocatorDefault, 0), coderFailure, SOSCreateError(kSOSErrorAllocationFailure, CFSTR("alloc failed"), NULL, error));
293 require_quiet(SOSOTRSAppendStartPacket(coder->sessRef, startPacket, error), coderFailure);
294 require_quiet(sendBlock(startPacket, error), coderFailure);
295
296 negotiatingOut:
297 result = kSOSPeerCoderNegotiating;
298 coderFailure:
299 // Uber state log
300 if (result == kSOSPeerCoderFailure && error && *error)
301 CFStringAppendFormat(action, NULL, CFSTR(" %@"), *error);
302 secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId, beginState,
303 SecOTRPacketTypeString(startPacket), action, coder->sessRef, SOSPeerCoderString(result));
304 CFReleaseNull(startPacket);
305 CFReleaseSafe(beginState);
306 CFRelease(action);
307
308 return result;
309
310 }
311
312 SOSPeerCoderStatus
313 SOSCoderResendDH(SOSCoderRef coder, SOSPeerSendBlock sendBlock, CFErrorRef *error) {
314 if(coder->sessRef == NULL) return kSOSPeerCoderDataReturned;
315
316 CFMutableDataRef startPacket = CFDataCreateMutable(kCFAllocatorDefault, 0);
317 if (SecOTRSAppendRestartPacket(coder->sessRef, startPacket)) {
318 return kSOSPeerCoderFailure;
319 }
320
321 secnotice("coder", "Resending OTR Start %@", startPacket);
322 SOSPeerCoderStatus result = sendBlock(startPacket, error) ? kSOSPeerCoderNegotiating : kSOSPeerCoderFailure;
323 CFReleaseNull(startPacket);
324 return result;
325 }
326
327
328 static SOSPeerCoderStatus nullCoder(CFDataRef from, CFMutableDataRef *to) {
329 *to = CFDataCreateMutableCopy(NULL, CFDataGetLength(from), from);
330 return kSOSPeerCoderDataReturned;
331 }
332
333 SOSPeerCoderStatus SOSCoderUnwrap(SOSCoderRef coder, SOSPeerSendBlock send_block,
334 CFDataRef codedMessage, CFMutableDataRef *message,
335 CFStringRef clientId,
336 CFErrorRef *error) {
337 if(codedMessage == NULL) return kSOSPeerCoderDataReturned;
338 if(coder->sessRef == NULL) return nullCoder(codedMessage, message);
339 CFMutableStringRef action = CFStringCreateMutable(kCFAllocatorDefault, 0);
340 /* This should be the "normal" case. We just use OTR to unwrap the received message. */
341 SOSPeerCoderStatus result = kSOSPeerCoderFailure;
342
343 CFStringRef beginState = CFCopyDescription(coder->sessRef);
344 enum SecOTRSMessageKind kind = SecOTRSGetMessageKind(coder->sessRef, codedMessage);
345
346 switch (kind) {
347 case kOTRNegotiationPacket: {
348 if(send_block) {
349 /* If we're in here we haven't completed negotiating a session. Use SecOTRSProcessPacket() to go through
350 the negotiation steps and immediately send a reply back if necessary using the sendBlock. This
351 assumes the sendBlock is still available.
352 */
353 CFMutableDataRef response = CFDataCreateMutable(kCFAllocatorDefault, 0);
354 OSStatus ppstatus = errSecSuccess;
355 if (response) {
356 switch (ppstatus = SecOTRSProcessPacket(coder->sessRef, codedMessage, response)) {
357 case errSecSuccess:
358 if (CFDataGetLength(response) > 1) {
359 CFStringAppendFormat(action, NULL, CFSTR("Sending OTR Response %s"), SecOTRPacketTypeString(response));
360 if (send_block(response, error)) {
361 result = kSOSPeerCoderNegotiating;
362 if (SecOTRSGetIsReadyForMessages(coder->sessRef)) {
363 CFStringAppend(action, CFSTR(" begin waiting for data packet"));
364 coder->waitingForDataPacket = true;
365 }
366 } else {
367 secerror("%@ Coder send Error %@", clientId, (CFTypeRef)error);
368 result = kSOSPeerCoderFailure;
369 }
370 } else if(!SecOTRSGetIsReadyForMessages(coder->sessRef)) {
371 CFStringAppend(action, CFSTR("stuck?"));
372 result = kSOSPeerCoderNegotiating;
373 } else {
374 CFStringAppend(action, CFSTR("completed negotiation"));
375 result = kSOSPeerCoderNegotiationCompleted;
376 coder->waitingForDataPacket = false;
377 }
378 break;
379 case errSecDecode:
380 CFStringAppend(action, CFSTR("resending dh"));
381 result = SOSCoderResendDH(coder, send_block, error);
382 break;
383 default:
384 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ Cannot negotiate session (%ld)"), clientId, (long)ppstatus);
385 result = kSOSPeerCoderFailure;
386 break;
387 };
388 } else {
389 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ Cannot allocate CFData"), clientId);
390 result = kSOSPeerCoderFailure;
391 }
392
393 CFReleaseNull(response);
394 } else {
395 secerror("%@ Can't send, no send_block!!", clientId);
396 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ Cannot negotiate session"), clientId);
397 result = kSOSPeerCoderFailure;
398 }
399
400 break;
401 }
402
403 case kOTRDataPacket:
404 if(!SecOTRSGetIsReadyForMessages(coder->sessRef)) {
405 CFStringAppend(action, CFSTR("not ready, resending DH packet"));
406 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncFailed, 1);
407 CFStringAppend(action, CFSTR("not ready for data; resending dh"));
408 result = SOSCoderResendDH(coder, send_block, error);
409 } else {
410 if (coder->waitingForDataPacket) {
411 CFStringAppend(action, CFSTR("got data packet we were waiting for "));
412 coder->waitingForDataPacket = false;
413 }
414 CFMutableDataRef exposed = CFDataCreateMutable(0, 0);
415 OSStatus otrResult = SecOTRSVerifyAndExposeMessage(coder->sessRef, codedMessage, exposed);
416 CFStringAppend(action, CFSTR("verify and expose message"));
417 if (otrResult) {
418 if (otrResult == errSecOTRTooOld) {
419 CFStringAppend(action, CFSTR(" too old"));
420 result = kSOSPeerCoderStaleEvent;
421 } else {
422 SecError(otrResult, error, CFSTR("%@ Cannot expose message: %" PRIdOSStatus), clientId, otrResult);
423 secerror("%@ Decode OTR Protected Packet: %@", clientId, error ? *error : NULL);
424 result = kSOSPeerCoderFailure;
425 }
426 } else {
427 CFStringAppend(action, CFSTR("decoded OTR protected packet"));
428 *message = exposed;
429 exposed = NULL;
430 result = kSOSPeerCoderDataReturned;
431 }
432 CFReleaseNull(exposed);
433 }
434 break;
435
436 default:
437 secerror("%@ Unknown packet type: %@", clientId, codedMessage);
438 SOSCreateError(kSOSErrorDecodeFailure, CFSTR("Unknown packet type"), (error != NULL) ? *error : NULL, error);
439 result = kSOSPeerCoderFailure;
440 break;
441 };
442
443 // Uber state log
444 if (result == kSOSPeerCoderFailure && error && *error)
445 CFStringAppendFormat(action, NULL, CFSTR(" %@"), *error);
446 secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId, beginState,
447 SecOTRPacketTypeString(codedMessage), action, coder->sessRef, SOSPeerCoderString(result));
448 CFReleaseSafe(beginState);
449 CFRelease(action);
450
451 return result;
452 }
453
454
455 SOSPeerCoderStatus SOSCoderWrap(SOSCoderRef coder, CFDataRef message, CFMutableDataRef *codedMessage, CFStringRef clientId, CFErrorRef *error) {
456 CFMutableStringRef action = CFStringCreateMutable(kCFAllocatorDefault, 0);
457 SOSPeerCoderStatus result = kSOSPeerCoderDataReturned;
458 CFStringRef beginState = NULL;
459 CFMutableDataRef encoded = NULL;
460 OSStatus otrStatus = 0;
461
462 require_action_quiet(coder->sessRef, errOut,
463 CFStringAppend(action, CFSTR("*** using null coder ***"));
464 result = nullCoder(message, codedMessage));
465 beginState = CFCopyDescription(coder->sessRef);
466 require_action_quiet(SecOTRSGetIsReadyForMessages(coder->sessRef), errOut,
467 CFStringAppend(action, CFSTR("not ready"));
468 result = kSOSPeerCoderNegotiating);
469 require_action_quiet(!coder->waitingForDataPacket, errOut,
470 CFStringAppend(action, CFSTR("waiting for peer to send data packet first"));
471 result = kSOSPeerCoderNegotiating);
472 require_action_quiet(encoded = CFDataCreateMutable(kCFAllocatorDefault, 0), errOut,
473 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure, NULL, error, NULL, CFSTR("%@ alloc failed"), clientId);
474 result = kSOSPeerCoderFailure);
475 require_noerr_action_quiet(otrStatus = SecOTRSSignAndProtectMessage(coder->sessRef, message, encoded), errOut,
476 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ cannot protect message: %" PRIdOSStatus), clientId, otrStatus);
477 CFReleaseNull(encoded);
478 result = kSOSPeerCoderFailure);
479 *codedMessage = encoded;
480
481 errOut:
482 // Uber state log
483 if (result == kSOSPeerCoderFailure && error && *error)
484 CFStringAppendFormat(action, NULL, CFSTR(" %@"), *error);
485 secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId, beginState,
486 SecOTRPacketTypeString(encoded), action, coder->sessRef, SOSPeerCoderString(result));
487 CFReleaseSafe(beginState);
488 CFRelease(action);
489
490 return result;
491 }
492
493 bool SOSCoderCanWrap(SOSCoderRef coder) {
494 return coder->sessRef && SecOTRSGetIsReadyForMessages(coder->sessRef) && !coder->waitingForDataPacket;
495 }