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
52struct __OpaqueSOSCoder {
53    CFStringRef peer_id;
54    SecOTRSessionRef sessRef;
55    bool waitingForDataPacket;
56    CFDataRef pendingResponse;
57};
58
59static 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
87static size_t der_sizeof_bool(bool value) {
88    return ccder_sizeof(CCDER_BOOLEAN, 1);
89}
90
91static 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
97static 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
114static 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
137static size_t der_sizeof_optional_data(CFDataRef data) {
138    return data ? der_sizeof_data(data, NULL) : 0;
139}
140
141static 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
147static 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
166static 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
182CFDataRef 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
201SOSCoderRef 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
249fail:
250    SOSCoderDispose(p);
251    CFReleaseSafe(otr_data);
252    return NULL;
253}
254
255
256SOSCoderRef 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
300errOut:
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
312void 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
323void SOSCoderReset(SOSCoderRef coder)
324{
325    SecOTRSessionReset(coder->sessRef);
326    coder->waitingForDataPacket = false;
327    CFReleaseNull(coder->pendingResponse);
328}
329
330CFDataRef SOSCoderCopyPendingResponse(SOSCoderRef coder)
331{
332    return CFRetainSafe(coder->pendingResponse);
333}
334
335void SOSCoderConsumeResponse(SOSCoderRef coder)
336{
337    CFReleaseNull(coder->pendingResponse);
338}
339
340static 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.
349SOSCoderStatus
350SOSCoderStart(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
366negotiatingOut:
367    result = kSOSCoderNegotiating;
368coderFailure:
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
382SOSCoderStatus
383SOSCoderResendDH(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;
391exit:
392    CFReleaseNull(startPacket);
393    return result;
394}
395
396
397static SOSCoderStatus nullCoder(CFDataRef from, CFMutableDataRef *to) {
398    *to = CFDataCreateMutableCopy(NULL, CFDataGetLength(from), from);
399    return kSOSCoderDataReturned;
400}
401
402SOSCoderStatus 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
512SOSCoderStatus 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
538errOut:
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
550bool SOSCoderCanWrap(SOSCoderRef coder) {
551    return coder->sessRef && SecOTRSGetIsReadyForMessages(coder->sessRef) && !coder->waitingForDataPacket;
552}
553