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