]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSCoder.c
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSCoder.c
1 /*
2 * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <stdlib.h>
25
26 #include <CoreFoundation/CFBase.h>
27 #include <CoreFoundation/CFError.h>
28
29 #include <Security/SecBasePriv.h>
30 #include <Security/SecOTR.h>
31 #include <Security/SecOTRSession.h>
32 #include <Security/SecureObjectSync/SOSInternal.h>
33 #include <Security/SecureObjectSync/SOSFullPeerInfo.h>
34 #include <Security/SecureObjectSync/SOSPeerInfo.h>
35 #include <Security/SecureObjectSync/SOSPeer.h>
36 #include <Security/SecureObjectSync/SOSCoder.h>
37
38 #include <utilities/SecCFRelease.h>
39 #include <utilities/SecCFWrappers.h>
40 #include <utilities/SecIOFormat.h>
41 #include <utilities/SecCFError.h>
42 #include <utilities/SecCoreCrypto.h>
43 #include <utilities/debugging.h>
44
45 #include <utilities/der_plist.h>
46 #include <utilities/der_plist_internal.h>
47
48 #include <corecrypto/ccder.h>
49 #include <utilities/iCloudKeychainTrace.h>
50
51 #include "AssertMacros.h"
52
53 struct __OpaqueSOSCoder {
54 CFRuntimeBase _base;
55
56 CFStringRef peer_id;
57 SecOTRSessionRef sessRef;
58 bool waitingForDataPacket;
59 CFDataRef pendingResponse;
60
61 CFDataRef hashOfLastReceived;
62 bool lastReceivedWasOld;
63 };
64
65 #define lastReceived_di ccsha1_di
66
67 CFGiblisWithCompareFor(SOSCoder)
68
69 static CFStringRef SOSCoderCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
70 SOSCoderRef coder = (SOSCoderRef)cf;
71 if(coder){
72 CFStringRef desc = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<Coder %@ %@ %@ %s%s>"),
73 coder->peer_id,
74 coder->sessRef,
75 coder->hashOfLastReceived,
76 coder->waitingForDataPacket ? "W" : "w",
77 coder->lastReceivedWasOld ? "O" : "o"
78 );
79 return desc;
80 }
81 else
82 return CFSTR("NULL");
83 }
84
85 static Boolean SOSCoderCompare(CFTypeRef cfA, CFTypeRef cfB) {
86 SOSCoderRef coderA = (SOSCoderRef)cfA, coderB = (SOSCoderRef)cfB;
87 // Use mainly to see if peerB is actually this device (peerA)
88 return CFStringCompare(coderA->peer_id, coderB->peer_id, 0) == kCFCompareEqualTo;
89 }
90
91
92 static const char *SOSCoderString(SOSCoderStatus coderStatus) {
93 switch (coderStatus) {
94 case kSOSCoderDataReturned: return "DataReturned";
95 case kSOSCoderNegotiating: return "Negotiating";
96 case kSOSCoderNegotiationCompleted: return "NegotiationCompleted";
97 case kSOSCoderFailure: return "Failure";
98 case kSOSCoderStaleEvent: return "StaleEvent";
99 case kSOSCoderTooNew: return "TooNew";
100 default: return "StatusUnknown";
101 }
102 }
103
104 CFStringRef SOSCoderGetID(SOSCoderRef coder) {
105 return coder->peer_id;
106 }
107
108 /*
109 static void logRawCoderMessage(const uint8_t* der, uint8_t* der_end, bool encoding)
110 {
111 #ifndef NDEBUG
112 CFStringRef hexMessage = NULL;
113 if (der && der_end) {
114 CFIndex length = der_end - der;
115 CFDataRef message = CFDataCreate(kCFAllocatorDefault, der, length);
116 hexMessage = CFDataCopyHexString(message);
117 secnoticeq("coder", "%s RAW [%ld] %@", encoding ? "encode" : "decode", length, hexMessage);
118 CFReleaseSafe(message);
119 }
120 CFReleaseSafe(hexMessage);
121 #endif
122 }
123 */
124
125 static CFMutableDataRef sessSerialized(SOSCoderRef coder, CFErrorRef *error) {
126 CFMutableDataRef otr_state = NULL;
127
128 if(!coder || !coder->sessRef) {
129 SOSCreateErrorWithFormat(kSOSErrorUnexpectedType, NULL, error, 0, CFSTR("No session reference."));
130 return NULL;
131 }
132
133 if ((otr_state = CFDataCreateMutable(NULL, 0)) == NULL) {
134 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure, NULL, error, 0, CFSTR("Mutable Data allocation failed."));
135 return NULL;
136 }
137
138 if (errSecSuccess != SecOTRSAppendSerialization(coder->sessRef, otr_state)) {
139 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, 0, CFSTR("Append Serialization failed."));
140 CFReleaseSafe(otr_state);
141 return NULL;
142 }
143
144 return otr_state;
145
146 }
147
148 static size_t der_sizeof_optional_data(CFDataRef data) {
149 return data ? der_sizeof_data(data, NULL) : 0;
150 }
151
152 static uint8_t* der_encode_optional_data(CFDataRef data, CFErrorRef *error, const uint8_t* der, uint8_t* der_end) {
153 return data ? der_encode_data(data, error, der, der_end) : der_end;
154 }
155
156
157
158 static size_t SOSCoderGetDEREncodedSize(SOSCoderRef coder, CFErrorRef *error) {
159 size_t encoded_size = 0;
160 CFMutableDataRef otr_state = sessSerialized(coder, error);
161
162 if (otr_state) {
163 size_t data_size = der_sizeof_data(otr_state, error);
164 size_t waiting_size = ccder_sizeof_bool(coder->waitingForDataPacket, error);
165 size_t pending_size = der_sizeof_optional_data(coder->pendingResponse);
166
167 if ((data_size != 0) && (waiting_size != 0))
168 {
169 encoded_size = ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, data_size + waiting_size + pending_size);
170 }
171 CFReleaseSafe(otr_state);
172 }
173 return encoded_size;
174 }
175
176
177 static uint8_t* SOSCoderEncodeToDER(SOSCoderRef coder, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) {
178 if(!der_end) return NULL;
179 uint8_t* result = NULL;
180 CFMutableDataRef otr_state = sessSerialized(coder, error);
181
182 if(otr_state) {
183 result = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
184 der_encode_data(otr_state, error, der,
185 ccder_encode_bool(coder->waitingForDataPacket, der,
186 der_encode_optional_data(coder->pendingResponse, error, der, der_end))));
187 CFReleaseSafe(otr_state);
188 }
189 return result;
190 }
191
192
193 CFDataRef SOSCoderCopyDER(SOSCoderRef coder, CFErrorRef* error) {
194 CFMutableDataRef encoded = NULL;
195 size_t encoded_size = SOSCoderGetDEREncodedSize(coder, error);
196
197 if (encoded_size > 0) {
198 encoded = CFDataCreateMutable(NULL, encoded_size);
199 if (encoded) {
200 CFDataSetLength(encoded, encoded_size);
201 uint8_t * der = CFDataGetMutableBytePtr(encoded);
202 uint8_t * der_end = der + encoded_size;
203 if (!SOSCoderEncodeToDER(coder, error, der, der_end)) {
204 CFReleaseNull(encoded);
205 encoded = NULL;
206 }
207 }
208 }
209 return encoded;
210 }
211
212 static SOSCoderRef SOSCoderCreate_internal() {
213 SOSCoderRef p = CFTypeAllocate(SOSCoder, struct __OpaqueSOSCoder, kCFAllocatorDefault);
214
215 p->peer_id = NULL;
216 p->sessRef = NULL;
217 p->pendingResponse = NULL;
218 p->waitingForDataPacket = false;
219
220 p->hashOfLastReceived = NULL;
221 p->lastReceivedWasOld = false;
222
223 return p;
224
225 }
226
227 // 0 - Type not understood
228 // 1 - OCTET_STRING, just stored the data for OTR
229 // 2 - SEQUENCE with no version value
230 // 3 - SEQUENCE with version value we pull out of the CCDER_INTEGER
231
232 typedef enum coderExportFormatVersion {
233 kNotUnderstood = 0,
234 kCoderAsOTRDataOnly = 1,
235 kCoderAsSequence = 2,
236 kCoderAsVersionedSequence = 3,
237
238 kCurrentCoderExportVersion = kCoderAsVersionedSequence
239 } CoderExportFormatVersion;
240
241 static uint64_t SOSCoderGetExportedVersion(const uint8_t *der, const uint8_t *der_end) {
242 ccder_tag tag;
243 uint64_t result = kNotUnderstood;
244 require(ccder_decode_tag(&tag, der, der_end),xit);
245 switch (tag) {
246 case CCDER_OCTET_STRING: // TODO: this code is safe to delete?
247 result = kCoderAsOTRDataOnly;
248 break;
249
250 case CCDER_CONSTRUCTED_SEQUENCE:
251 {
252 const uint8_t *sequence_end = NULL;
253 der = ccder_decode_sequence_tl(&sequence_end, der, der_end);
254 ccder_tag firstSequenceTag;
255 require(ccder_decode_tag(&firstSequenceTag, der, der_end),xit);
256
257 switch (firstSequenceTag) {
258 case CCDER_OCTET_STRING:
259 result = kCoderAsSequence;
260 break;
261 case CCDER_INTEGER:
262 der = ccder_decode_uint64(NULL, der, sequence_end);
263 if (der == NULL) {
264 result = kNotUnderstood;
265 } else {
266 result = kCoderAsVersionedSequence;
267 }
268 break;
269 }
270 }
271 }
272 xit:
273 return result;
274
275 }
276
277 SOSCoderRef SOSCoderCreateFromData(CFDataRef exportedData, CFErrorRef *error) {
278 // TODO: fill in errors for all failure cases
279 //require_action_quiet(coder, xit, SOSCreateError(kSOSErrorSendFailure, CFSTR("No coder for peer"), NULL, error));
280
281 SOSCoderRef p = SOSCoderCreate_internal();
282
283 const uint8_t *der = CFDataGetBytePtr(exportedData);
284 const uint8_t *der_end = der + CFDataGetLength(exportedData);
285
286 CFDataRef otr_data = NULL;
287
288 switch (SOSCoderGetExportedVersion(der, der_end)) {
289 case kCoderAsOTRDataOnly:
290 der = der_decode_data(kCFAllocatorDefault, 0, &otr_data, error, der, der_end);
291 p->waitingForDataPacket = false;
292 break;
293
294 case kCoderAsSequence:
295 {
296 const uint8_t *sequence_end = NULL;
297 der = ccder_decode_sequence_tl(&sequence_end, der, der_end);
298
299 require_action_quiet(sequence_end == der_end, fail, SecCFDERCreateError(kSOSErrorDecodeFailure, CFSTR("Extra data in SOS coder"), NULL, error));
300
301 der = der_decode_data(kCFAllocatorDefault, 0, &otr_data, error, der, sequence_end);
302 der = ccder_decode_bool(&p->waitingForDataPacket, der, sequence_end);
303 if (der != sequence_end) { // optionally a pending response
304 der = der_decode_data(kCFAllocatorDefault, 0, &p->pendingResponse, error, der, sequence_end);
305 }
306 }
307 break;
308
309 case kCoderAsVersionedSequence:
310 {
311 const uint8_t *sequence_end = NULL;
312 der = ccder_decode_sequence_tl(&sequence_end, der, der_end);
313
314 require_action_quiet(sequence_end == der_end, fail, SecCFDERCreateError(kSOSErrorDecodeFailure, CFSTR("Extra data in SOS coder"), NULL, error));
315
316 uint64_t version;
317 der = ccder_decode_uint64(&version, der, sequence_end);
318 if (version != kCoderAsVersionedSequence) {
319 SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("Unsupported Sequence Version: %lld"), version);
320 goto fail;
321 }
322
323 der = der_decode_data(kCFAllocatorDefault, 0, &otr_data, error, der, sequence_end);
324 der = ccder_decode_bool(&p->waitingForDataPacket, der, sequence_end);
325 der = ccder_decode_bool(&p->lastReceivedWasOld, der, sequence_end);
326 der = der_decode_data(kCFAllocatorDefault, 0, &p->hashOfLastReceived, error, der, sequence_end);
327 if (der != sequence_end) { // optionally a pending response
328 der = der_decode_data(kCFAllocatorDefault, 0, &p->pendingResponse, error, der, sequence_end);
329 }
330 }
331 break;
332
333 default:
334 SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("Unsupported SOS Coder DER"));
335 goto fail;
336 }
337
338 require(der, fail);
339
340 p->sessRef = SecOTRSessionCreateFromData(NULL, otr_data);
341 require(p->sessRef, fail);
342
343 if (p->hashOfLastReceived == NULL)
344 p->hashOfLastReceived = CFDataCreateMutableWithScratch(kCFAllocatorDefault, lastReceived_di()->output_size);
345
346 CFReleaseSafe(otr_data);
347 return p;
348
349 fail:
350 CFReleaseNull(p);
351 CFReleaseSafe(otr_data);
352 return NULL;
353 }
354
355
356 SOSCoderRef SOSCoderCreate(SOSPeerInfoRef peerInfo, SOSFullPeerInfoRef myPeerInfo, CFBooleanRef useCompact, CFErrorRef *error) {
357 CFAllocatorRef allocator = CFGetAllocator(peerInfo);
358
359 SOSCoderRef coder = SOSCoderCreate_internal();
360
361 CFErrorRef localError = NULL;
362
363 SecOTRFullIdentityRef myRef = NULL;
364 SecOTRPublicIdentityRef peerRef = NULL;
365 SecKeyRef privateKey = NULL;
366 SecKeyRef publicKey = NULL;
367
368 if (myPeerInfo && peerInfo) {
369 privateKey = SOSFullPeerInfoCopyDeviceKey(myPeerInfo, &localError);
370 require_quiet(privateKey, errOut);
371
372 myRef = SecOTRFullIdentityCreateFromSecKeyRef(allocator, privateKey, &localError);
373 require_quiet(myRef, errOut);
374
375 CFReleaseNull(privateKey);
376
377 publicKey = SOSPeerInfoCopyPubKey(peerInfo, &localError);
378 require(publicKey, errOut);
379
380 peerRef = SecOTRPublicIdentityCreateFromSecKeyRef(allocator, publicKey, &localError);
381 require_quiet(peerRef, errOut);
382
383 if(useCompact == kCFBooleanTrue)
384 coder->sessRef = SecOTRSessionCreateFromIDAndFlags(allocator, myRef, peerRef, kSecOTRUseAppleCustomMessageFormat);
385
386 else
387 coder->sessRef = SecOTRSessionCreateFromID(allocator, myRef, peerRef);
388
389 require(coder->sessRef, errOut);
390
391 coder->waitingForDataPacket = false;
392 coder->pendingResponse = NULL;
393
394 CFReleaseNull(publicKey);
395 CFReleaseNull(privateKey);
396 CFReleaseNull(myRef);
397 CFReleaseNull(peerRef);
398 } else {
399 secnotice("coder", "NULL Coder requested, no transport security");
400 }
401
402 coder->hashOfLastReceived = CFDataCreateMutableWithScratch(kCFAllocatorDefault, lastReceived_di()->output_size);
403 coder->lastReceivedWasOld = false;
404
405 SOSCoderStart(coder, NULL);
406
407 return coder;
408
409 errOut:
410 secerror("Coder create failed: %@\n", localError ? localError : (CFTypeRef)CFSTR("No local error in SOSCoderCreate"));
411 secerror("Coder create failed: %@\n", error ? *error : (CFTypeRef)CFSTR("WTF NULL?"));
412 CFReleaseNull(myRef);
413 CFReleaseNull(peerRef);
414 CFReleaseNull(publicKey);
415 CFReleaseNull(privateKey);
416
417 CFReleaseNull(coder);
418 return NULL;
419 }
420
421 static void SOSCoderDestroy(CFTypeRef cf)
422 {
423 SOSCoderRef coder = (SOSCoderRef) cf;
424 if (coder) {
425 CFReleaseNull(coder->sessRef);
426 CFReleaseNull(coder->pendingResponse);
427 CFReleaseNull(coder->peer_id);
428 CFReleaseNull(coder->hashOfLastReceived);
429 }
430 }
431
432 void SOSCoderReset(SOSCoderRef coder)
433 {
434 SecOTRSessionReset(coder->sessRef);
435 coder->waitingForDataPacket = false;
436 CFReleaseNull(coder->pendingResponse);
437
438 coder->lastReceivedWasOld = false;
439 CFReleaseNull(coder->hashOfLastReceived);
440 coder->hashOfLastReceived = CFDataCreateMutableWithScratch(kCFAllocatorDefault, lastReceived_di()->output_size);
441 }
442
443 bool SOSCoderIsFor(SOSCoderRef coder, SOSPeerInfoRef peerInfo, SOSFullPeerInfoRef myPeerInfo) {
444 SecKeyRef theirPublicKey = NULL;
445 SecKeyRef myPublicKey = NULL;
446 bool isForThisPair = false;
447 CFErrorRef localError = NULL;
448
449 myPublicKey = SOSPeerInfoCopyPubKey(SOSFullPeerInfoGetPeerInfo(myPeerInfo), &localError);
450 require(myPublicKey, errOut);
451
452 theirPublicKey = SOSPeerInfoCopyPubKey(peerInfo, &localError);
453 require(theirPublicKey, errOut);
454
455 isForThisPair = SecOTRSIsForKeys(coder->sessRef, myPublicKey, theirPublicKey);
456
457 errOut:
458 if (localError) {
459 secerror("SOSCoderIsFor failed: %@\n", localError ? localError : (CFTypeRef)CFSTR("No local error in SOSCoderCreate"));
460 }
461
462 CFReleaseNull(myPublicKey);
463 CFReleaseNull(theirPublicKey);
464 CFReleaseNull(localError);
465 return isForThisPair;
466 }
467
468 CFDataRef SOSCoderCopyPendingResponse(SOSCoderRef coder)
469 {
470 return coder->pendingResponse ? CFDataCreateCopy(kCFAllocatorDefault, coder->pendingResponse) : NULL;
471 }
472
473 void SOSCoderConsumeResponse(SOSCoderRef coder)
474 {
475 CFReleaseNull(coder->pendingResponse);
476 }
477
478 static bool SOSOTRSAppendStartPacket(SecOTRSessionRef session, CFMutableDataRef appendPacket, CFErrorRef *error) {
479 OSStatus otrStatus = SecOTRSAppendStartPacket(session, appendPacket);
480 if (otrStatus != errSecSuccess) {
481 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("append start packet returned: %" PRIdOSStatus), otrStatus);
482 }
483 return otrStatus == errSecSuccess;
484 }
485
486 // Start OTR negotiation if we haven't already done so.
487 SOSCoderStatus
488 SOSCoderStart(SOSCoderRef coder, CFErrorRef *error) {
489 CFMutableStringRef action = CFStringCreateMutable(kCFAllocatorDefault, 0);
490 CFStringRef beginState = NULL;
491 SOSCoderStatus result = kSOSCoderFailure;
492 CFMutableDataRef startPacket = NULL;
493
494 require_action_quiet(coder->sessRef, coderFailure, CFStringAppend(action, CFSTR("*** no otr session ***")));
495 beginState = CFCopyDescription(coder->sessRef);
496 require_action_quiet(!coder->waitingForDataPacket, negotiatingOut, CFStringAppend(action, CFSTR("waiting for peer to send first data packet")));
497 require_action_quiet(!SecOTRSGetIsReadyForMessages(coder->sessRef), coderFailure, CFStringAppend(action, CFSTR("otr session ready"));
498 result = kSOSCoderDataReturned);
499 require_action_quiet(SecOTRSGetIsIdle(coder->sessRef), negotiatingOut, CFStringAppend(action, CFSTR("otr negotiating already")));
500 require_action_quiet(startPacket = CFDataCreateMutable(kCFAllocatorDefault, 0), coderFailure, SOSCreateError(kSOSErrorAllocationFailure, CFSTR("alloc failed"), NULL, error));
501 require_quiet(SOSOTRSAppendStartPacket(coder->sessRef, startPacket, error), coderFailure);
502 CFRetainAssign(coder->pendingResponse, startPacket);
503
504 negotiatingOut:
505 result = kSOSCoderNegotiating;
506 coderFailure:
507 // Uber state log
508 if (result == kSOSCoderFailure && error && *error)
509 CFStringAppendFormat(action, NULL, CFSTR(" %@"), *error);
510 secinfo("coder", "%@ %s %@ %@ returned %s", beginState,
511 SecOTRPacketTypeString(startPacket), action, coder->sessRef, SOSCoderString(result));
512 CFReleaseNull(startPacket);
513 CFReleaseSafe(beginState);
514 CFRelease(action);
515
516 return result;
517
518 }
519
520 SOSCoderStatus
521 SOSCoderResendDH(SOSCoderRef coder, CFErrorRef *error) {
522 if(coder->sessRef == NULL) return kSOSCoderDataReturned;
523 CFMutableDataRef startPacket = CFDataCreateMutable(kCFAllocatorDefault, 0);
524 SOSCoderStatus result = kSOSCoderFailure;
525 require_noerr_quiet(SecOTRSAppendRestartPacket(coder->sessRef, startPacket), exit);
526 secnotice("coder", "Resending OTR Start %@", startPacket);
527 CFRetainAssign(coder->pendingResponse, startPacket);
528 result = kSOSCoderNegotiating;
529 exit:
530 CFReleaseNull(startPacket);
531 return result;
532 }
533
534
535 static SOSCoderStatus nullCoder(CFDataRef from, CFMutableDataRef *to) {
536 *to = CFDataCreateMutableCopy(NULL, CFDataGetLength(from), from);
537 return kSOSCoderDataReturned;
538 }
539
540 SOSCoderStatus SOSCoderUnwrap(SOSCoderRef coder, CFDataRef codedMessage, CFMutableDataRef *message,
541 CFStringRef clientId, CFErrorRef *error) {
542 if(codedMessage == NULL) return kSOSCoderDataReturned;
543 if(coder->sessRef == NULL) return nullCoder(codedMessage, message);
544 CFMutableStringRef action = CFStringCreateMutable(kCFAllocatorDefault, 0);
545 /* This should be the "normal" case. We just use OTR to unwrap the received message. */
546 SOSCoderStatus result = kSOSCoderFailure;
547
548 CFStringRef beginState = CFCopyDescription(coder->sessRef);
549 enum SecOTRSMessageKind kind = SecOTRSGetMessageKind(coder->sessRef, codedMessage);
550
551
552 switch (kind) {
553 case kOTRNegotiationPacket: {
554 /* If we're in here we haven't completed negotiating a session. Use SecOTRSProcessPacket() to go through
555 the negotiation steps and immediately send a reply back if necessary using the sendBlock. This
556 assumes the sendBlock is still available.
557 */
558 CFMutableDataRef response = CFDataCreateMutable(kCFAllocatorDefault, 0);
559 OSStatus ppstatus = errSecSuccess;
560 if (response) {
561 switch (ppstatus = SecOTRSProcessPacket(coder->sessRef, codedMessage, response)) {
562 case errSecSuccess:
563 if (CFDataGetLength(response) > 1) {
564 CFStringAppendFormat(action, NULL, CFSTR("Sending OTR Response %s"), SecOTRPacketTypeString(response));
565 CFRetainAssign(coder->pendingResponse, response);
566 result = kSOSCoderNegotiating;
567 if (SecOTRSGetIsReadyForMessages(coder->sessRef)) {
568 CFStringAppend(action, CFSTR(" begin waiting for data packet"));
569 coder->waitingForDataPacket = true;
570 }
571 } else if(!SecOTRSGetIsReadyForMessages(coder->sessRef)) {
572 CFStringAppend(action, CFSTR("stuck?"));
573 result = kSOSCoderNegotiating;
574 } else {
575 CFStringAppend(action, CFSTR("completed negotiation"));
576 result = kSOSCoderNegotiationCompleted;
577 coder->waitingForDataPacket = false;
578 }
579 break;
580 case errSecDecode:
581 CFStringAppend(action, CFSTR("resending dh"));
582 result = SOSCoderResendDH(coder, error);
583 break;
584 default:
585 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ Cannot negotiate session (%ld)"), clientId, (long)ppstatus);
586 result = kSOSCoderFailure;
587 break;
588 };
589 } else {
590 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ Cannot allocate CFData"), clientId);
591 result = kSOSCoderFailure;
592 }
593
594 CFReleaseNull(response);
595
596 break;
597 }
598
599 case kOTRDataPacket:
600 {
601 CFDataRef previousMessageHash = coder->hashOfLastReceived;
602 coder->hashOfLastReceived = CFDataCreateWithHash(kCFAllocatorDefault, lastReceived_di(), CFDataGetBytePtr(codedMessage), CFDataGetLength(codedMessage));
603 bool lastWasOld = coder->lastReceivedWasOld;
604 coder->lastReceivedWasOld = false;
605
606 if(!SecOTRSGetIsReadyForMessages(coder->sessRef)) {
607 CFStringAppend(action, CFSTR("not ready for data; resending DH packet"));
608 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncFailed, 1);
609 result = SOSCoderResendDH(coder, error);
610 } else {
611 if (coder->waitingForDataPacket) {
612 CFStringAppend(action, CFSTR("got data packet we were waiting for "));
613 coder->waitingForDataPacket = false;
614 }
615 CFMutableDataRef exposed = CFDataCreateMutable(0, 0);
616 OSStatus otrResult = SecOTRSVerifyAndExposeMessage(coder->sessRef, codedMessage, exposed);
617 CFStringAppend(action, CFSTR("verify and expose message"));
618 switch(otrResult) {
619 case errSecSuccess:
620 CFStringAppend(action, CFSTR("decoded OTR protected packet"));
621 CFTransferRetained(*message, exposed);
622 result = kSOSCoderDataReturned;
623 break;
624 case errSecOTRTooOld:
625 if (CFEqualSafe(previousMessageHash, coder->hashOfLastReceived)) {
626 CFStringAppend(action, CFSTR(" repeated"));
627 result = kSOSCoderStaleEvent;
628 } else {
629 coder->lastReceivedWasOld = true;
630 if (lastWasOld) {
631 CFStringAppend(action, CFSTR(" too old, repeated renegotiating"));
632 // Fail so we will renegotiate
633 result = kSOSCoderFailure;
634 } else {
635 CFStringAppend(action, CFSTR(" too old, forcing message"));
636 // Force message send.
637 result = kSOSCoderForceMessage;
638 }
639 }
640 break;
641 case errSecOTRIDTooNew:
642 CFStringAppend(action, CFSTR(" too new"));
643 result = kSOSCoderTooNew;
644 break;
645 default:
646 SecError(otrResult, error, CFSTR("%@ Cannot expose message: %" PRIdOSStatus), clientId, otrResult);
647 secerror("%@ Decode OTR Protected Packet: %@", clientId, error ? *error : NULL);
648 result = kSOSCoderFailure;
649 break;
650 }
651
652 CFReleaseNull(exposed);
653 }
654 CFReleaseNull(previousMessageHash);
655 }
656 break;
657
658 default:
659 secerror("%@ Unknown packet type: %@", clientId, codedMessage);
660 SOSCreateError(kSOSErrorDecodeFailure, CFSTR("Unknown packet type"), (error != NULL) ? *error : NULL, error);
661 result = kSOSCoderFailure;
662 break;
663 };
664
665 // Uber state log
666 if (result == kSOSCoderFailure && error && *error)
667 CFStringAppendFormat(action, NULL, CFSTR(" %@"), *error);
668 secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId, beginState,
669 SecOTRPacketTypeString(codedMessage), action, coder->sessRef, SOSCoderString(result));
670 CFReleaseSafe(beginState);
671 CFRelease(action);
672
673 return result;
674 }
675
676
677 SOSCoderStatus SOSCoderWrap(SOSCoderRef coder, CFDataRef message, CFMutableDataRef *codedMessage, CFStringRef clientId, CFErrorRef *error) {
678 CFMutableStringRef action = CFStringCreateMutable(kCFAllocatorDefault, 0);
679 SOSCoderStatus result = kSOSCoderDataReturned;
680 CFStringRef beginState = NULL;
681 CFMutableDataRef encoded = NULL;
682 OSStatus otrStatus = 0;
683
684 require_action_quiet(coder->sessRef, errOut,
685 CFStringAppend(action, CFSTR("*** using null coder ***"));
686 result = nullCoder(message, codedMessage));
687 beginState = CFCopyDescription(coder->sessRef);
688 require_action_quiet(SecOTRSGetIsReadyForMessages(coder->sessRef), errOut,
689 CFStringAppend(action, CFSTR("not ready"));
690 result = kSOSCoderNegotiating);
691 require_action_quiet(!coder->waitingForDataPacket, errOut,
692 CFStringAppend(action, CFSTR("waiting for peer to send data packet first"));
693 result = kSOSCoderNegotiating);
694 require_action_quiet(encoded = CFDataCreateMutable(kCFAllocatorDefault, 0), errOut,
695 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure, NULL, error, NULL, CFSTR("%@ alloc failed"), clientId);
696 result = kSOSCoderFailure);
697 require_noerr_action_quiet(otrStatus = SecOTRSSignAndProtectMessage(coder->sessRef, message, encoded), errOut,
698 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ cannot protect message: %" PRIdOSStatus), clientId, otrStatus);
699 CFReleaseNull(encoded);
700 result = kSOSCoderFailure);
701 *codedMessage = encoded;
702
703 errOut:
704 // Uber state log
705 if (result == kSOSCoderFailure && error && *error)
706 CFStringAppendFormat(action, NULL, CFSTR(" %@"), *error);
707 secinfo("coder", "%@ %@ %s %@ %@ returned %s", clientId, beginState,
708 SecOTRPacketTypeString(encoded), action, coder->sessRef, SOSCoderString(result));
709 CFReleaseSafe(beginState);
710 CFRelease(action);
711
712 return result;
713 }
714
715 bool SOSCoderCanWrap(SOSCoderRef coder) {
716 return coder->sessRef && SecOTRSGetIsReadyForMessages(coder->sessRef) && !coder->waitingForDataPacket;
717 }