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