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