1/* 2 * Copyright (c) 2011-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 25#include "SecOTRSession.h" 26 27#include "SecOTRMath.h" 28#include "SecOTRIdentityPriv.h" 29#include "SecOTRSessionPriv.h" 30#include "SecOTRPackets.h" 31#include "SecOTRPacketData.h" 32#include "SecOTRDHKey.h" 33 34#include <utilities/SecCFWrappers.h> 35 36#include <CoreFoundation/CFRuntime.h> 37#include <CoreFoundation/CFString.h> 38 39#include <Security/SecBase.h> 40#include <Security/SecRandom.h> 41 42#include <AssertMacros.h> 43 44#include <corecrypto/cchmac.h> 45#include <corecrypto/ccsha2.h> 46 47#include <string.h> 48 49static void SecOTRInitMyDHKeys(SecOTRSessionRef session) 50{ 51 CFReleaseNull(session->_myKey); 52 session->_myKey = SecOTRFullDHKCreate(kCFAllocatorDefault); 53 CFReleaseNull(session->_myNextKey); 54 session->_myNextKey = SecOTRFullDHKCreate(kCFAllocatorDefault); 55 session->_keyID = 1; 56 57 bzero(session->_keyCache, sizeof(session->_keyCache)); 58} 59 60OSStatus SecOTRSAppendStartPacket(SecOTRSessionRef session, CFMutableDataRef appendPacket) 61{ 62 __block OSStatus result = errSecSuccess; 63 64 dispatch_sync(session->_queue, ^{ 65 session->_state = kAwaitingDHKey; 66 67 // Generate r and x and calculate gx: 68 SecOTRInitMyDHKeys(session); 69 70 CFMutableDataRef destinationMessage = NULL; 71 if (session->_textOutput) { 72 destinationMessage = CFDataCreateMutable(kCFAllocatorDefault, 0); 73 } else { 74 destinationMessage = CFRetainSafe(appendPacket); 75 } 76 77 78 result = SecRandomCopyBytes(kSecRandomDefault, sizeof(session->_r), session->_r); 79 if (result == errSecSuccess) { 80 SecOTRAppendDHMessage(session, destinationMessage); 81 if (session->_textOutput) { 82 SecOTRPrepareOutgoingBytes(destinationMessage, appendPacket); 83 } 84 } 85 CFReleaseSafe(destinationMessage); 86 }); 87 88 return result; 89} 90 91OSStatus SecOTRSAppendRestartPacket(SecOTRSessionRef session, CFMutableDataRef appendPacket) 92{ 93 __block OSStatus result = errSecSuccess; 94 95 dispatch_sync(session->_queue, ^{ 96 if (!session->_myKey) { 97 secerror("_myKey is NULL, avoiding crash"); 98 result = errSecDecode; 99 return; 100 } 101 CFMutableDataRef destinationMessage; 102 if (session->_textOutput) { 103 destinationMessage = CFDataCreateMutable(kCFAllocatorDefault, 0); 104 } else { 105 destinationMessage = CFRetainSafe(appendPacket); 106 } 107 108 session->_state = kAwaitingDHKey; 109 CFReleaseNull(session->_receivedDHMessage); 110 CFReleaseNull(session->_receivedDHKeyMessage); 111 112 SecOTRAppendDHMessage(session, destinationMessage); 113 if (session->_textOutput) { 114 SecOTRPrepareOutgoingBytes(destinationMessage, appendPacket); 115 } 116 CFReleaseSafe(destinationMessage); 117 }); 118 119 return result; 120} 121 122static const uint8_t* FindGXHash(CFDataRef dhPacket) 123{ 124 const uint8_t* messageBytes = CFDataGetBytePtr(dhPacket); 125 size_t remainingBytes = (size_t)CFDataGetLength(dhPacket); 126 127 OTRMessageType messageType; 128 129 require_noerr(ReadHeader(&messageBytes, &remainingBytes, &messageType), fail); 130 require(messageType == kDHMessage, fail); 131 132 uint32_t egxiLength = 0; 133 require_noerr(ReadLong(&messageBytes, &remainingBytes, & egxiLength), fail); 134 require(egxiLength <= remainingBytes, fail); 135 messageBytes += egxiLength; 136 remainingBytes -= egxiLength; 137 138 uint32_t dataLength = 0; 139 require_noerr(ReadLong(&messageBytes, &remainingBytes, &dataLength), fail); 140 require(dataLength <= remainingBytes, fail); 141 require(dataLength == CCSHA256_OUTPUT_SIZE, fail); 142 143 return messageBytes; 144 145fail: 146 return NULL; 147} 148 149static bool SecOTRMyGXHashIsBigger(SecOTRSessionRef session, CFDataRef dhCommitMessage) 150{ 151 bool mineIsBigger = false; 152 153 CFMutableDataRef myDHCommitMessage = CFDataCreateMutable(kCFAllocatorDefault, 0); 154 155 SecOTRAppendDHMessage(session, myDHCommitMessage); 156 157 const uint8_t* myHash = FindGXHash(myDHCommitMessage); 158 const uint8_t* theirHash = FindGXHash(dhCommitMessage); 159 160 require(myHash, fail); 161 require(theirHash, fail); 162 163 mineIsBigger = 0 < memcmp(myHash, theirHash, CCSHA256_OUTPUT_SIZE); 164 165fail: 166 CFReleaseNull(myDHCommitMessage); 167 return mineIsBigger; 168} 169 170static OSStatus SecOTRSProcessDHMessage(SecOTRSessionRef session, 171 CFDataRef incomingPacket, 172 CFMutableDataRef negotiationResponse) 173{ 174 OSStatus result = errSecParam; 175 176 switch (session->_state) { 177 case kAwaitingDHKey: 178 // Compare hash values. 179 if (SecOTRMyGXHashIsBigger(session, incomingPacket)) { 180 // If we're bigger we resend to force them to deal. 181 CFReleaseNull(session->_receivedDHMessage); 182 SecOTRAppendDHMessage(session, negotiationResponse); 183 result = errSecSuccess; 184 break; 185 } // Else intentionally fall through to idle 186 case kAwaitingSignature: 187 case kIdle: 188 case kDone: 189 // Generate a new X and GX.. 190 SecOTRInitMyDHKeys(session); 191 // If we were already waiting on reveal, then just send the packet again 192 case kAwaitingRevealSignature: 193 SecOTRAppendDHKeyMessage(session, negotiationResponse); 194 195 // Keep the packet for use later. 196 CFReleaseNull(session->_receivedDHMessage); 197 session->_receivedDHMessage = CFDataCreateCopy(kCFAllocatorDefault, incomingPacket); 198 199 session->_state = kAwaitingRevealSignature; 200 result = errSecSuccess; 201 break; 202 default: 203 result = errSecInteractionNotAllowed; 204 break; 205 } 206 207 return result; 208} 209 210static OSStatus SecOTRSetupTheirKeyFrom(SecOTRSessionRef session, const uint8_t**data, size_t*size) 211{ 212 SecOTRPublicDHKeyRef tempKey = SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault, data, size); 213 require(tempKey != NULL, fail); 214 session->_theirKey = tempKey; 215 session->_theirKeyID = 1; 216 217 return errSecSuccess; 218 219fail: 220 return errSecDecode; 221} 222 223static OSStatus SecOTRSExtractTheirPublicDHKey(SecOTRSessionRef session, CFDataRef dhPacket) 224{ 225 OSStatus result = errSecParam; 226 227 const uint8_t *messageBytes = CFDataGetBytePtr(dhPacket); 228 size_t messageSize = (size_t)CFDataGetLength(dhPacket); 229 OTRMessageType messageType = kDHMessage; // Suppress warning. 230 231 ReadHeader(&messageBytes, &messageSize, &messageType); 232 require(messageType == kDHKeyMessage, exit); 233 234 result = SecOTRSetupTheirKeyFrom(session, &messageBytes, &messageSize); 235 236exit: 237 return result; 238} 239 240 241static OSStatus SecOTRSProcessDHKeyMessage(SecOTRSessionRef session, 242 CFDataRef incomingPacket, 243 CFMutableDataRef negotiationResponse) 244{ 245 OSStatus result = errSecUnimplemented; 246 247 result = SecOTRSExtractTheirPublicDHKey(session, incomingPacket); 248 require_noerr(result, exit); 249 250 switch (session->_state) { 251 case kAwaitingDHKey: 252 CFReleaseNull(session->_receivedDHKeyMessage); 253 SecOTRAppendRevealSignatureMessage(session, negotiationResponse); 254 session->_state = kAwaitingSignature; 255 session->_receivedDHKeyMessage = CFDataCreateCopy(kCFAllocatorDefault, incomingPacket); 256 result = errSecSuccess; 257 break; 258 case kAwaitingSignature: 259 if (CFEqualSafe(incomingPacket, session->_receivedDHKeyMessage)) 260 SecOTRAppendRevealSignatureMessage(session, negotiationResponse); 261 result = errSecSuccess; 262 break; 263 case kIdle: 264 case kDone: 265 case kAwaitingRevealSignature: 266 result = errSecSuccess; 267 break; 268 default: 269 result = errSecInteractionNotAllowed; 270 break; 271 } 272 273exit: 274 return result; 275} 276 277 278static OSStatus SecOTRSExtractR(SecOTRSessionRef session, 279 const uint8_t **messageBytes, 280 size_t *messageSize) 281{ 282 OSStatus result = errSecDecode; 283 284 OTRMessageType messageType = kDHMessage; // Suppress warning 285 286 ReadHeader(messageBytes, messageSize, &messageType); 287 require(messageType == kRevealSignatureMessage, exit); 288 289 { 290 uint32_t rSize = 0; 291 ReadLong(messageBytes, messageSize, &rSize); 292 require(rSize == kOTRAuthKeyBytes, exit); 293 } 294 295 memcpy(session->_r, *messageBytes, kOTRAuthKeyBytes); 296 297 *messageBytes += kOTRAuthKeyBytes; 298 *messageSize -= kOTRAuthKeyBytes; 299 300 result = errSecSuccess; 301exit: 302 return result; 303} 304 305static OSStatus FindEncGYInDHPacket(SecOTRSessionRef session, 306 const uint8_t **dhMessageBytesPtr, 307 size_t *messageSizePtr, 308 size_t* encGYBufferSize) 309{ 310 OSStatus result = errSecParam; 311 require_action(*encGYBufferSize >= kExponentiationBytes + 4, exit, result = errSecParam); 312 313 OTRMessageType messageType; 314 result = ReadHeader(dhMessageBytesPtr, messageSizePtr, &messageType); 315 require_noerr(result, exit); 316 require_action(messageType == kDHMessage, exit, result = errSecDecode); 317 318 uint32_t readEncSize; 319 result = ReadLong(dhMessageBytesPtr, messageSizePtr, &readEncSize); 320 require_noerr(result, exit); 321 322 *encGYBufferSize = readEncSize; 323exit: 324 // Don't bother erasing the public gy decrypted, it's public after all. 325 return result; 326 327} 328 329static OSStatus SecOTRSExtractRAndTheirDHKey(SecOTRSessionRef session, 330 const uint8_t **messageBytes, 331 size_t *messageSize) 332{ 333 OSStatus result = errSecDecode; 334 335 require(session->_receivedDHMessage != NULL, exit); 336 result = SecOTRSExtractR(session, messageBytes, messageSize); 337 require_noerr(result, exit); 338 339 uint8_t gxiDecrypted[kExponentiationBytes + 4]; 340 const uint8_t *gxiDecryptedBuffer = gxiDecrypted; 341 342 const uint8_t* dhMessageBytes = CFDataGetBytePtr(session->_receivedDHMessage); 343 size_t dhMessageSize = (size_t)CFDataGetLength(session->_receivedDHMessage); 344 345 size_t encGYSize = sizeof(gxiDecrypted); 346 result = FindEncGYInDHPacket(session, &dhMessageBytes, &dhMessageSize, &encGYSize); 347 require_noerr(result, exit); 348 require_action(encGYSize <= kExponentiationBytes + 4, exit, result = errSecDecode); 349 350 AES_CTR_IV0_Transform(sizeof(session->_r), session->_r, encGYSize, dhMessageBytes, gxiDecrypted); 351 352 result = SecOTRSetupTheirKeyFrom(session, &gxiDecryptedBuffer, &encGYSize); 353 354exit: 355 // Don't bother erasing the public gy decrypted, it's public after all. 356 return result; 357} 358 359static OSStatus SecVerifySignatureAndMac(SecOTRSessionRef session, 360 bool usePrimes, 361 const uint8_t **signatureAndMacBytes, 362 size_t *signatureAndMacSize) 363{ 364 OSStatus result = errSecDecode; 365 366 uint8_t m1[kOTRAuthMACKeyBytes]; 367 uint8_t m2[kOTRAuthMACKeyBytes]; 368 uint8_t c[kOTRAuthKeyBytes]; 369 370 { 371 cc_unit s[kExponentiationUnits]; 372 373 SecPDHKeyGenerateS(session->_myKey, session->_theirKey, s); 374 // Derive M1, M2 and C, either prime or normal versions. 375 DeriveOTR256BitsFromS(usePrimes ? kM1Prime : kM1, 376 kExponentiationUnits, s, sizeof(m1), m1); 377 DeriveOTR256BitsFromS(usePrimes ? kM2Prime : kM2, 378 kExponentiationUnits, s, sizeof(m2), m2); 379 DeriveOTR128BitPairFromS(kCs, 380 kExponentiationUnits, s, 381 sizeof(c),usePrimes ? NULL : c, 382 sizeof(c), usePrimes ? c : NULL); 383 bzero(s, sizeof(s)); 384 } 385 386 cchmac_di_decl(ccsha256_di(), mBContext); 387 388 cchmac_init(ccsha256_di(), mBContext, sizeof(m1), m1); 389 390 { 391 CFMutableDataRef toHash = CFDataCreateMutable(kCFAllocatorDefault, 0); 392 393 SecPDHKAppendSerialization(session->_theirKey, toHash); 394 SecFDHKAppendPublicSerialization(session->_myKey, toHash); 395 396 cchmac_update(ccsha256_di(), mBContext, (size_t)CFDataGetLength(toHash), CFDataGetBytePtr(toHash)); 397 398 CFReleaseNull(toHash); 399 } 400 401 const uint8_t* encSigDataBlobStart = *signatureAndMacBytes; 402 403 uint32_t xbSize = 0; 404 result = ReadLong(signatureAndMacBytes, signatureAndMacSize, &xbSize); 405 require_noerr(result, exit); 406 require_action(xbSize > 4, exit, result = errSecDecode); 407 require_action(xbSize <= *signatureAndMacSize, exit, result = errSecDecode); 408 409 uint8_t signatureMac[CCSHA256_OUTPUT_SIZE]; 410 cchmac(ccsha256_di(), sizeof(m2), m2, xbSize + 4, encSigDataBlobStart, signatureMac); 411 412 require(xbSize + kSHA256HMAC160Bytes <= *signatureAndMacSize, exit); 413 const uint8_t *macStart = *signatureAndMacBytes + xbSize; 414 415 // check the outer hmac 416 require_action(0 == memcmp(macStart, signatureMac, kSHA256HMAC160Bytes), exit, result = errSecDecode); 417 418 419 { 420 uint8_t xb[xbSize]; 421 // Decrypt and copy the signature block 422 AES_CTR_IV0_Transform(sizeof(c), c, xbSize, *signatureAndMacBytes, xb); 423 424 const uint8_t* signaturePacket = xb; 425 size_t signaturePacketSize = xbSize; 426 427 uint16_t pubKeyType; 428 result = ReadShort(&signaturePacket, &signaturePacketSize, &pubKeyType); 429 require_noerr(result, exit); 430 require_action(pubKeyType == 0xF000, exit, result = errSecUnimplemented); 431 432 uint32_t pubKeySize; 433 result = ReadLong(&signaturePacket, &signaturePacketSize, &pubKeySize); 434 require_noerr(result, exit); 435 require_action(pubKeySize <= signaturePacketSize, exit, result = errSecDecode); 436 require(((CFIndex)pubKeySize) >= 0, exit); 437 438 // Add the signature and keyid to the hash. 439 // PUBKEY of our type is 2 bytes of type, 2 bytes of size and size bytes. 440 // Key ID is 4 bytes. 441 cchmac_update(ccsha256_di(), mBContext, 2 + 4 + pubKeySize + 4, xb); 442 443 uint8_t mb[CCSHA256_OUTPUT_SIZE]; 444 cchmac_final(ccsha256_di(), mBContext, mb); 445 446 // Make reference to the deflated key 447 require_action(SecOTRPIEqualToBytes(session->_them, signaturePacket, (CFIndex)pubKeySize), exit, result = errSecAuthFailed); 448 449 signaturePacket += pubKeySize; 450 signaturePacketSize -= pubKeySize; 451 452 result = ReadLong(&signaturePacket, &signaturePacketSize, &session->_theirKeyID); 453 require_noerr(result, exit); 454 455 uint32_t sigSize; 456 result = ReadLong(&signaturePacket, &signaturePacketSize, &sigSize); 457 require_noerr(result, exit); 458 require_action(sigSize <= signaturePacketSize, exit, result = errSecDecode); 459 460 bool bresult = SecOTRPIVerifySignature(session->_them, mb, sizeof(mb), signaturePacket, sigSize, NULL); 461 result = bresult ? errSecSuccess : errSecDecode; 462 require_noerr(result, exit); 463 464 } 465 466exit: 467 bzero(m1, sizeof(m1)); 468 bzero(m2, sizeof(m2)); 469 bzero(c, sizeof(c)); 470 471 return result; 472} 473 474static OSStatus SecOTRSProcessRevealSignatureMessage(SecOTRSessionRef session, 475 CFDataRef incomingPacket, 476 CFMutableDataRef negotiationResponse) 477{ 478 OSStatus result = errSecParam; 479 480 require_action_quiet(session->_state == kAwaitingRevealSignature, exit, result = errSecSuccess); 481 482 const uint8_t *messageBytes = CFDataGetBytePtr(incomingPacket); 483 size_t messageSize = (size_t)CFDataGetLength(incomingPacket); 484 485 result = SecOTRSExtractRAndTheirDHKey(session, &messageBytes, &messageSize); 486 require_noerr(result, exit); 487 488 result = SecVerifySignatureAndMac(session, false, &messageBytes, &messageSize); 489 require_noerr(result, exit); 490 491 SecOTRAppendSignatureMessage(session, negotiationResponse); 492 493 session->_state = kDone; 494 result = errSecSuccess; 495exit: 496 return result; 497} 498 499static OSStatus SecOTRSProcessSignatureMessage(SecOTRSessionRef session, 500 CFDataRef incomingPacket, 501 CFMutableDataRef negotiationResponse) 502{ 503 OSStatus result = errSecParam; 504 505 require_action_quiet(session->_state == kAwaitingSignature, exit, result = errSecSuccess); 506 507 const uint8_t *messageBytes = CFDataGetBytePtr(incomingPacket); 508 size_t messageSize = (size_t)CFDataGetLength(incomingPacket); 509 510 OTRMessageType messageType; 511 result = ReadHeader(&messageBytes, &messageSize, &messageType); 512 require_noerr(result, exit); 513 require_action(messageType == kSignatureMessage, exit, result = errSecDecode); 514 515 result = SecVerifySignatureAndMac(session, true, &messageBytes, &messageSize); 516 require_noerr(result, exit); 517 518 CFReleaseNull(session->_receivedDHKeyMessage); 519 session->_state = kDone; 520 521 result = errSecSuccess; 522exit: 523 return result; 524} 525 526OSStatus SecOTRSProcessPacket(SecOTRSessionRef session, 527 CFDataRef incomingPacket, 528 CFMutableDataRef negotiationResponse) 529{ 530 __block OSStatus result = errSecParam; 531 532 require(CFDataGetLength(incomingPacket) > 0, fail); 533 dispatch_sync(session->_queue, ^{ 534 CFDataRef decodedBytes = SecOTRCopyIncomingBytes(incomingPacket); 535 536 const uint8_t* bytes = CFDataGetBytePtr(decodedBytes); 537 size_t size = CFDataGetLength(decodedBytes); 538 539 OTRMessageType packetType = kInvalidMessage; 540 if (ReadHeader(&bytes, &size, &packetType)) 541 packetType = kInvalidMessage; 542 543 CFMutableDataRef destinationMessage; 544 if (session->_textOutput) { 545 destinationMessage = CFDataCreateMutable(kCFAllocatorDefault, 0); 546 } else { 547 destinationMessage = CFRetainSafe(negotiationResponse); 548 } 549 550 switch (packetType) { 551 case kDHMessage: 552 result = SecOTRSProcessDHMessage(session, decodedBytes, destinationMessage); 553 break; 554 case kDHKeyMessage: 555 result = SecOTRSProcessDHKeyMessage(session, decodedBytes, destinationMessage); 556 break; 557 case kRevealSignatureMessage: 558 result = SecOTRSProcessRevealSignatureMessage(session, decodedBytes, destinationMessage); 559 break; 560 case kSignatureMessage: 561 result = SecOTRSProcessSignatureMessage(session, decodedBytes, destinationMessage); 562 break; 563 default: 564 result = errSecDecode; 565 break; 566 }; 567 568 if (result != errSecSuccess) { 569 secnotice("session", "Error %d processing packet type %d, session state %d, keyid %d, myKey %p, myNextKey %p, theirKeyId %d, theirKey %p, theirPreviousKey %p, bytes %@", (int)result, packetType, session->_state, session->_keyID, session->_myKey, session->_myNextKey, session->_theirKeyID, session->_theirKey, session->_theirPreviousKey, decodedBytes); 570 } 571 572 if (session->_textOutput) { 573 SecOTRPrepareOutgoingBytes(destinationMessage, negotiationResponse); 574 } 575 CFReleaseSafe(destinationMessage); 576 CFReleaseSafe(decodedBytes); 577 }); 578 579fail: 580 return result; 581} 582