SOSPeerInfo.m
1 /* 2 * Copyright (c) 2012-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 <AssertMacros.h> 26 #include <TargetConditionals.h> 27 28 #include <Security/SecureObjectSync/SOSPeerInfo.h> 29 #include "keychain/SecureObjectSync/SOSPeerInfoInternal.h" 30 #include "keychain/SecureObjectSync/SOSPeerInfoPriv.h" 31 #include "keychain/SecureObjectSync/SOSPeerInfoV2.h" 32 #include "keychain/SecureObjectSync/SOSCircle.h" 33 #include "keychain/SecureObjectSync/SOSInternal.h" 34 #include <ipc/securityd_client.h> 35 36 #include <CoreFoundation/CFArray.h> 37 #include <dispatch/dispatch.h> 38 39 #include <stdlib.h> 40 41 #include <utilities/SecCFWrappers.h> 42 #include <utilities/SecCFRelease.h> 43 #include <utilities/SecCFError.h> 44 #include <utilities/SecXPCError.h> 45 46 #include <utilities/der_plist.h> 47 #include <utilities/der_plist_internal.h> 48 #include <corecrypto/ccder.h> 49 #include <utilities/der_date.h> 50 51 #include <corecrypto/ccdigest.h> 52 #include <corecrypto/ccsha2.h> 53 54 55 #include <CoreFoundation/CoreFoundation.h> 56 #include <CoreFoundation/CFDate.h> 57 58 #include <xpc/xpc.h> 59 60 #if TARGET_OS_IPHONE 61 #include <MobileGestalt.h> 62 #endif 63 64 #include <Security/SecBase64.h> 65 #include <Security/SecKeyPriv.h> 66 #include <Security/SecOTR.h> 67 #include <Security/SecuritydXPC.h> 68 69 CFGiblisWithHashFor(SOSPeerInfo); 70 71 72 const CFStringRef kPIUserDefinedDeviceNameKey = CFSTR("ComputerName"); 73 const CFStringRef kPIDeviceModelNameKey = CFSTR("ModelName"); 74 const CFStringRef kPIMessageProtocolVersionKey = CFSTR("MessageProtocolVersion"); 75 const CFStringRef kPIOSVersionKey = CFSTR("OSVersion"); 76 77 // Description Dictionary Entries 78 static CFStringRef sPublicKeyKey = CFSTR("PublicSigningKey"); 79 static CFStringRef sOctagonPeerSigningPublicKeyKey = CFSTR("OctagonPublicSigningKey"); 80 static CFStringRef sOctagonPeerEncryptionPublicKeyKey = CFSTR("OctagonPublicEncryptionKey"); 81 const CFStringRef sGestaltKey = CFSTR("DeviceGestalt"); 82 const CFStringRef sVersionKey = CFSTR("ConflictVersion"); 83 static CFStringRef sCloudIdentityKey = CFSTR("CloudIdentity"); 84 static CFStringRef sApplicationDate = CFSTR("ApplicationDate"); 85 static CFStringRef sApplicationUsig = CFSTR("ApplicationUsig"); 86 static CFStringRef sRetirementDate = CFSTR("RetirementDate"); 87 88 // Peerinfo Entries 89 CFStringRef kSOSPeerInfoDescriptionKey = CFSTR("SOSPeerInfoDescription"); 90 CFStringRef kSOSPeerInfoSignatureKey = CFSTR("SOSPeerInfoSignature"); 91 CFStringRef kSOSPeerInfoNameKey = CFSTR("SOSPeerInfoName"); 92 93 //Peer Info V2 Dictionary IDS keys 94 CFStringRef sPreferIDS = CFSTR("PreferIDS"); 95 CFStringRef sPreferIDSFragmentation = CFSTR("PreferIDFragmentation"); 96 CFStringRef sPreferIDSACKModel = CFSTR("PreferIDSAckModel"); 97 CFStringRef sTransportType = CFSTR("TransportType"); 98 CFStringRef sDeviceID = CFSTR("DeviceID"); 99 100 CFStringRef sCKKSForAll = CFSTR("CKKS4A"); 101 102 const CFStringRef peerIDLengthKey = CFSTR("idLength"); 103 104 SOSPeerInfoRef SOSPeerInfoAllocate(CFAllocatorRef allocator) { 105 return CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator); 106 } 107 108 static SecKeyRef _SOSPeerInfoCopyPubKey(SOSPeerInfoRef peer, CFStringRef keyDictionaryKey, CFErrorRef* error) 109 { 110 SecKeyRef result = NULL; 111 112 CFDataRef pubKeyBytes = asData(CFDictionaryGetValue(peer->description, keyDictionaryKey), error); 113 require_quiet(pubKeyBytes, fail); 114 115 CFAllocatorRef allocator = CFGetAllocator(peer); 116 result = SecKeyCreateFromPublicData(allocator, kSecECDSAAlgorithmID, pubKeyBytes); 117 118 require_quiet(SecAllocationError(result, error, CFSTR("Failed to create public key from data %@"), pubKeyBytes), fail); 119 120 fail: 121 return result; 122 } 123 124 SecKeyRef SOSPeerInfoCopyPubKey(SOSPeerInfoRef peer, CFErrorRef* error) { 125 return _SOSPeerInfoCopyPubKey(peer, sPublicKeyKey, error); 126 } 127 128 SecKeyRef SOSPeerInfoCopyOctagonSigningPublicKey(SOSPeerInfoRef peer, CFErrorRef* error) 129 { 130 return _SOSPeerInfoCopyPubKey(peer, sOctagonPeerSigningPublicKeyKey, error); 131 } 132 133 SecKeyRef SOSPeerInfoCopyOctagonEncryptionPublicKey(SOSPeerInfoRef peer, CFErrorRef* error) 134 { 135 return _SOSPeerInfoCopyPubKey(peer, sOctagonPeerEncryptionPublicKeyKey, error); 136 } 137 138 139 CFDataRef SOSPeerInfoGetAutoAcceptInfo(SOSPeerInfoRef peer) { 140 CFDataRef pubKeyBytes = NULL; 141 142 pubKeyBytes = CFDictionaryGetValue(peer->description, sPublicKeyKey); 143 if (!pubKeyBytes || CFGetTypeID(pubKeyBytes) != CFDataGetTypeID()) { 144 pubKeyBytes = NULL; 145 } 146 147 return pubKeyBytes; 148 } 149 150 static bool SOSDescriptionHash(SOSPeerInfoRef peer, const struct ccdigest_info *di, void *hashresult, CFErrorRef *error) { 151 ccdigest_di_decl(di, ctx); 152 ccdigest_init(di, ctx); 153 void *ctx_p = ctx; 154 if(!SOSPeerInfoUpdateDigestWithDescription(peer, di, ctx_p, error)) return false; 155 ccdigest_final(di, ctx, hashresult); 156 return true; 157 } 158 159 160 #define SIGLEN 128 161 static CFDataRef sosCopySignedHash(SecKeyRef privkey, const struct ccdigest_info *di, uint8_t *hbuf) { 162 size_t siglen = SIGLEN; 163 uint8_t sig[siglen]; 164 if(SecKeyRawSign(privkey, kSecPaddingNone, hbuf, di->output_size, sig, &siglen) != 0) { 165 return NULL; 166 } 167 return CFDataCreate(NULL, sig, (CFIndex)siglen); 168 } 169 170 static bool sosVerifyHash(SecKeyRef pubkey, const struct ccdigest_info *di, uint8_t *hbuf, CFDataRef signature) { 171 return SecKeyRawVerify(pubkey, kSecPaddingNone, hbuf, di->output_size, 172 CFDataGetBytePtr(signature), CFDataGetLength(signature)) == errSecSuccess; 173 } 174 175 bool SOSPeerInfoSign(SecKeyRef privKey, SOSPeerInfoRef peer, CFErrorRef *error) { 176 bool status = false; 177 const struct ccdigest_info *di = ccsha256_di(); 178 uint8_t hbuf[di->output_size]; 179 CFDataRef newSignature = NULL; 180 181 require_action_quiet(SOSDescriptionHash(peer, di, hbuf, error), fail, 182 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to hash description for peer"), NULL, error)); 183 184 newSignature = sosCopySignedHash(privKey, di, hbuf); 185 require_action_quiet(newSignature, fail, SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to sign peerinfo for peer"), NULL, error)); 186 187 CFReleaseNull(peer->signature); 188 peer->signature = newSignature; 189 newSignature = NULL; 190 status = true; 191 192 fail: 193 CFReleaseNull(newSignature); 194 return status; 195 } 196 197 // Return true (1) if the signature verifies. 198 bool SOSPeerInfoVerify(SOSPeerInfoRef peer, CFErrorRef *error) { 199 bool result = false; 200 const struct ccdigest_info *di = ccsha256_di(); 201 uint8_t hbuf[di->output_size]; 202 203 SecKeyRef pubKey = SOSPeerInfoCopyPubKey(peer, error); 204 require_quiet(pubKey, error_out); 205 206 require_quiet(SOSDescriptionHash(peer, di, hbuf, error), error_out); 207 208 require_action_quiet(sosVerifyHash(pubKey, di, hbuf, peer->signature), error_out, 209 SOSErrorCreate(kSOSErrorBadSignature, error, NULL, 210 CFSTR("Signature didn't verify for %@"), peer)); 211 result = true; 212 213 error_out: 214 CFReleaseNull(pubKey); 215 return result; 216 } 217 218 void SOSPeerInfoSetVersionNumber(SOSPeerInfoRef pi, int version) { 219 pi->version = version; 220 CFNumberRef versionNumber = CFNumberCreateWithCFIndex(NULL, pi->version); 221 CFDictionarySetValue(pi->description, sVersionKey, versionNumber); 222 CFReleaseNull(versionNumber); 223 } 224 225 static SOSPeerInfoRef SOSPeerInfoCreate_Internal(CFAllocatorRef allocator, 226 CFDictionaryRef gestalt, CFDataRef backup_key, 227 CFStringRef IDSID, CFStringRef transportType, CFBooleanRef preferIDS, 228 CFBooleanRef preferFragmentation, CFBooleanRef preferAckModel, 229 CFSetRef enabledViews, 230 SecKeyRef signingKey, 231 SecKeyRef octagonPeerSigningKey, 232 SecKeyRef octagonPeerEncryptionKey, 233 bool supportsCKKS4All, 234 CFErrorRef* error, 235 void (^ description_modifier)(CFMutableDictionaryRef description)) { 236 SOSPeerInfoRef pi = CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator); 237 pi->gestalt = gestalt; 238 CFRetain(pi->gestalt); 239 240 pi->version = SOSPeerInfoGetPeerProtocolVersion(pi); 241 CFDataRef publicBytes = NULL; 242 CFDataRef octagonPeerSigningPublicBytes = NULL; 243 CFDataRef octagonPeerEncryptionPublicBytes = NULL; 244 CFNumberRef versionNumber = NULL; 245 246 SecKeyRef publicKey = SecKeyCreatePublicFromPrivate(signingKey); 247 if (publicKey == NULL) { 248 SOSCreateError(kSOSErrorBadKey, CFSTR("Unable to get public"), NULL, error); 249 CFReleaseNull(pi); 250 goto exit; 251 } 252 253 OSStatus result = SecKeyCopyPublicBytes(publicKey, &publicBytes); 254 255 if (result != errSecSuccess) { 256 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error); 257 CFReleaseNull(pi); 258 goto exit; 259 } 260 261 262 if (octagonPeerSigningKey) { 263 SecKeyRef octagonPeerSigningPublicKey = SecKeyCreatePublicFromPrivate(octagonPeerSigningKey); 264 if (octagonPeerSigningPublicKey == NULL) { 265 SOSCreateError(kSOSErrorBadKey, CFSTR("Unable to get public key"), NULL, error); 266 CFReleaseNull(pi); 267 goto exit; 268 } 269 270 result = SecKeyCopyPublicBytes(octagonPeerSigningPublicKey, &octagonPeerSigningPublicBytes); 271 CFReleaseNull(octagonPeerSigningPublicKey); 272 if (result != errSecSuccess) { 273 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error); 274 CFReleaseNull(pi); 275 goto exit; 276 } 277 } 278 279 if (octagonPeerEncryptionKey) { 280 SecKeyRef octagonPeerEncryptionPublicKey = SecKeyCreatePublicFromPrivate(octagonPeerEncryptionKey); 281 if (octagonPeerEncryptionPublicKey == NULL) { 282 SOSCreateError(kSOSErrorBadKey, CFSTR("Unable to get public key"), NULL, error); 283 CFReleaseNull(pi); 284 goto exit; 285 } 286 287 result = SecKeyCopyPublicBytes(octagonPeerEncryptionPublicKey, &octagonPeerEncryptionPublicBytes); 288 CFReleaseNull(octagonPeerEncryptionPublicKey); 289 if (result != errSecSuccess) { 290 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error); 291 CFReleaseNull(pi); 292 goto exit; 293 } 294 } 295 296 pi->signature = CFDataCreateMutable(allocator, 0); 297 298 versionNumber = CFNumberCreateWithCFIndex(NULL, pi->version); 299 300 pi->description = CFDictionaryCreateMutableForCFTypesWith(allocator, 301 sVersionKey, versionNumber, 302 sPublicKeyKey, publicBytes, 303 sGestaltKey, pi->gestalt, 304 NULL); 305 if (octagonPeerSigningPublicBytes) { 306 CFDictionarySetValue(pi->description, sOctagonPeerSigningPublicKeyKey, octagonPeerSigningPublicBytes); 307 } 308 if (octagonPeerEncryptionPublicBytes) { 309 CFDictionarySetValue(pi->description, sOctagonPeerEncryptionPublicKeyKey, octagonPeerEncryptionPublicBytes); 310 } 311 312 313 description_modifier(pi->description); 314 315 pi->peerID = SOSCopyIDOfKey(publicKey, error); 316 pi->spid = CFStringCreateTruncatedCopy(pi->peerID, 8); 317 318 pi->verifiedAppKeyID = NULL; 319 pi->verifiedResult = false; 320 321 require_quiet(pi->peerID, exit); 322 323 // ================ V2 Additions Start 324 325 if(!SOSPeerInfoUpdateToV2(pi, error)) { 326 CFReleaseNull(pi); 327 goto exit; 328 } 329 330 // V2DictionarySetValue handles NULL as remove 331 if (backup_key != NULL) SOSPeerInfoV2DictionarySetValue(pi, sBackupKeyKey, backup_key); 332 SOSPeerInfoV2DictionarySetValue(pi, sViewsKey, enabledViews); 333 334 SOSPeerInfoSetSupportsCKKSForAll(pi, supportsCKKS4All); 335 336 // ================ V2 Additions End 337 338 if (!SOSPeerInfoSign(signingKey, pi, error)) { 339 CFReleaseNull(pi); 340 goto exit; 341 } 342 343 exit: 344 CFReleaseNull(versionNumber); 345 CFReleaseNull(publicKey); 346 CFReleaseNull(publicBytes); 347 CFReleaseNull(octagonPeerSigningPublicBytes); 348 CFReleaseNull(octagonPeerEncryptionPublicBytes); 349 return pi; 350 } 351 352 SOSPeerInfoRef SOSPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, CFDataRef backup_key, SecKeyRef signingKey, 353 SecKeyRef octagonPeerSigningKey, 354 SecKeyRef octagonPeerEncryptionKey, 355 bool supportsCKKS4All, 356 CFErrorRef* error) { 357 return SOSPeerInfoCreate_Internal(allocator, gestalt, backup_key, NULL, NULL, NULL, NULL, NULL, NULL, signingKey, octagonPeerSigningKey, octagonPeerEncryptionKey, 358 supportsCKKS4All, 359 error, ^(CFMutableDictionaryRef description) {}); 360 } 361 362 SOSPeerInfoRef SOSPeerInfoCreateWithTransportAndViews(CFAllocatorRef allocator, CFDictionaryRef gestalt, CFDataRef backup_key, 363 CFStringRef IDSID, CFStringRef transportType, CFBooleanRef preferIDS, 364 CFBooleanRef preferFragmentation, CFBooleanRef preferAckModel, CFSetRef enabledViews, 365 SecKeyRef signingKey, 366 SecKeyRef octagonPeerSigningKey, 367 SecKeyRef octagonPeerEncryptionKey, 368 bool supportsCKKS4All, 369 CFErrorRef* error) 370 { 371 return SOSPeerInfoCreate_Internal(allocator, gestalt, backup_key, IDSID, transportType, preferIDS, preferFragmentation, preferAckModel, enabledViews, signingKey, 372 octagonPeerSigningKey, 373 octagonPeerEncryptionKey, 374 supportsCKKS4All, 375 error, ^(CFMutableDictionaryRef description) {}); 376 } 377 378 379 SOSPeerInfoRef SOSPeerInfoCreateCloudIdentity(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) { 380 return SOSPeerInfoCreate_Internal(allocator, gestalt, NULL, NULL, NULL, NULL, NULL, NULL, NULL, signingKey, NULL, NULL, false, error, ^(CFMutableDictionaryRef description) { 381 CFDictionarySetValue(description, sCloudIdentityKey, kCFBooleanTrue); 382 }); 383 384 } 385 386 387 SOSPeerInfoRef SOSPeerInfoCreateCopy(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFErrorRef* error) { 388 if(!toCopy) return NULL; 389 SOSPeerInfoRef pi = CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator); 390 391 pi->description = CFDictionaryCreateMutableCopy(allocator, 0, toCopy->description); 392 pi->signature = CFDataCreateCopy(allocator, toCopy->signature); 393 394 pi->gestalt = CFDictionaryCreateCopy(allocator, toCopy->gestalt); 395 pi->peerID = CFStringCreateCopy(allocator, toCopy->peerID); 396 pi->spid = CFStringCreateCopy(allocator, toCopy->spid); 397 pi->verifiedAppKeyID = NULL; // The peer resulting from this will need to be re-evaluated for an application signature. 398 pi->verifiedResult = false; 399 400 pi->version = toCopy->version; 401 if(!SOSPeerInfoVersionHasV2Data(pi)) SOSPeerInfoExpandV2Data(pi, error); 402 403 return pi; 404 } 405 406 407 bool SOSPeerInfoVersionIsCurrent(SOSPeerInfoRef pi) { 408 return pi->version >= PEERINFO_CURRENT_VERSION; 409 } 410 411 bool SOSPeerInfoVersionHasV2Data(SOSPeerInfoRef pi) { 412 return pi->version >= kSOSPeerV2BaseVersion; 413 } 414 415 SOSPeerInfoRef SOSPeerInfoCreateCurrentCopy(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, 416 CFStringRef IDSID, CFStringRef transportType, CFBooleanRef preferIDS, CFBooleanRef preferFragmentation, CFBooleanRef preferAckModel, CFSetRef enabledViews, 417 SecKeyRef signingKey, CFErrorRef* error) { 418 419 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, toCopy, error); 420 if(!SOSPeerInfoVersionHasV2Data(pi)) SOSPeerInfoUpdateToV2(pi, error); 421 422 if (enabledViews) { 423 SOSPeerInfoV2DictionarySetValue(pi, sViewsKey, enabledViews); 424 } 425 426 if(!SOSPeerInfoSign(signingKey, pi, error)) { 427 CFReleaseNull(pi); 428 } 429 430 return pi; 431 } 432 433 434 SOSPeerInfoRef SOSPeerInfoCopyWithModification(CFAllocatorRef allocator, SOSPeerInfoRef original, 435 SecKeyRef signingKey, CFErrorRef *error, 436 bool (^modification)(SOSPeerInfoRef peerToModify, CFErrorRef *error)) 437 { 438 SOSPeerInfoRef result = NULL; 439 SOSPeerInfoRef copy = SOSPeerInfoCreateCopy(allocator, original, error); 440 441 require_quiet(modification(copy, error), fail); 442 443 require_quiet(SOSPeerInfoSign(signingKey, copy, error), fail); 444 445 CFTransferRetained(result, copy); 446 447 fail: 448 CFReleaseNull(copy); 449 return result; 450 451 } 452 453 SOSPeerInfoRef SOSPeerInfoCopyWithGestaltUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) { 454 return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error, 455 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) { 456 if(!gestalt || !peerToModify) return false; 457 CFRetainAssign(peerToModify->gestalt, gestalt); 458 CFDictionarySetValue(peerToModify->description, sGestaltKey, peerToModify->gestalt); 459 return true; 460 461 }); 462 } 463 464 465 SOSPeerInfoRef SOSPeerInfoCopyWithBackupKeyUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDataRef backupKey, SecKeyRef signingKey, CFErrorRef *error) { 466 return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error, 467 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) { 468 if (backupKey != NULL) { 469 CFStringRef shortHash = SOSCopyIDOfDataBufferWithLength(backupKey, 8, NULL); 470 secnotice("backup", "Setting peerInfo backupKey to %@", shortHash); 471 CFReleaseNull(shortHash); 472 SOSPeerInfoV2DictionarySetValue(peerToModify, sBackupKeyKey, backupKey); 473 } else { 474 secnotice("backup", "Setting peerInfo backupKey to NULL"); 475 SOSPeerInfoV2DictionaryRemoveValue(peerToModify, sBackupKeyKey); 476 } 477 return true; 478 }); 479 } 480 481 SOSPeerInfoRef SOSPeerInfoCopyWithReplacedEscrowRecords(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef escrowRecords, SecKeyRef signingKey, CFErrorRef *error) { 482 return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error, 483 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) { 484 if(escrowRecords != NULL) 485 SOSPeerInfoV2DictionarySetValue(peerToModify, sEscrowRecord, escrowRecords); 486 487 return true; 488 }); 489 } 490 491 CFDataRef SOSPeerInfoCopyBackupKey(SOSPeerInfoRef peer) { 492 return SOSPeerInfoV2DictionaryCopyData(peer, sBackupKeyKey); 493 } 494 495 bool SOSPeerInfoHasBackupKey(SOSPeerInfoRef peer) { 496 CFDataRef bk = SOSPeerInfoCopyBackupKey(peer); 497 bool success = bk != NULL; 498 CFReleaseNull(bk); 499 return success; 500 } 501 502 SOSPeerInfoRef SOSPeerInfoCopyWithViewsChange(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, 503 SOSViewActionCode action, CFStringRef viewname, SOSViewResultCode *retval, 504 SecKeyRef signingKey, CFErrorRef* error) { 505 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, toCopy, error); 506 if(action == kSOSCCViewEnable) { 507 *retval = SOSViewsEnable(pi, viewname, error); 508 require((kSOSCCViewMember == *retval), exit); 509 } else if(action == kSOSCCViewDisable) { 510 *retval = SOSViewsDisable(pi, viewname, error); 511 require((kSOSCCViewNotMember == *retval), exit); 512 } 513 514 require_action_quiet(SOSPeerInfoSign(signingKey, pi, error), exit, *retval = kSOSCCGeneralViewError); 515 return pi; 516 517 exit: 518 CFReleaseNull(pi); 519 return NULL; 520 } 521 522 523 CFStringRef sPingKey = CFSTR("Ping"); 524 525 SOSPeerInfoRef SOSPeerInfoCopyWithPing(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, SecKeyRef signingKey, CFErrorRef* error) { 526 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, toCopy, error); 527 CFDataRef ping = CFDataCreateWithRandomBytes(8); 528 SOSPeerInfoV2DictionarySetValue(pi, sPingKey, ping); 529 SecKeyRef pub_key = SOSPeerInfoCopyPubKey(pi, error); 530 require_quiet(pub_key, exit); 531 pi->peerID = SOSCopyIDOfKey(pub_key, error); 532 pi->spid = CFStringCreateTruncatedCopy(pi->peerID, 8); 533 require_quiet(pi->peerID, exit); 534 require_action_quiet(SOSPeerInfoSign(signingKey, pi, error), exit, CFReleaseNull(pi)); 535 exit: 536 CFReleaseNull(ping); 537 CFReleaseNull(pub_key); 538 return pi; 539 } 540 541 542 SOSViewResultCode SOSPeerInfoViewStatus(SOSPeerInfoRef pi, CFStringRef view, CFErrorRef *error) { 543 return SOSViewsQuery(pi, view, error); 544 } 545 546 static void SOSPeerInfoDestroy(CFTypeRef aObj) { 547 SOSPeerInfoRef pi = (SOSPeerInfoRef) aObj; 548 549 if(!pi) return; 550 CFReleaseNull(pi->description); 551 CFReleaseNull(pi->signature); 552 CFReleaseNull(pi->gestalt); 553 CFReleaseNull(pi->peerID); 554 CFReleaseNull(pi->spid); 555 CFReleaseNull(pi->verifiedAppKeyID); 556 CFReleaseNull(pi->v2Dictionary); 557 pi->verifiedResult = false; 558 } 559 560 static Boolean SOSPeerInfoCompare(CFTypeRef lhs, CFTypeRef rhs) { 561 SOSPeerInfoRef lpeer = (SOSPeerInfoRef) lhs; 562 SOSPeerInfoRef rpeer = (SOSPeerInfoRef) rhs; 563 if(!lpeer || !rpeer) return false; 564 return CFEqualSafe(lpeer->description, rpeer->description) && CFEqualSafe(lpeer->signature, rpeer->signature); 565 } 566 567 568 CFComparisonResult SOSPeerInfoCompareByID(const void *val1, const void *val2, void *context) { 569 // The code below is necessary but not sufficient; not returning a CFComparisonResult 570 // It probably is OK to say that a NULL is < <non-NULL> 571 if (val1 == NULL || val2 == NULL) { 572 ptrdiff_t dv = val1 - val2; 573 return dv < 0 ? kCFCompareLessThan : dv == 0 ? kCFCompareEqualTo : kCFCompareGreaterThan; 574 } 575 576 CFStringRef v1 = SOSPeerInfoGetPeerID((SOSPeerInfoRef) val1); 577 CFStringRef v2 = SOSPeerInfoGetPeerID((SOSPeerInfoRef) val2); 578 if (v1 == NULL || v2 == NULL) { 579 ptrdiff_t dv = (const void *)v1 - (const void *)v2; 580 return dv < 0 ? kCFCompareLessThan : dv == 0 ? kCFCompareEqualTo : kCFCompareGreaterThan; 581 } 582 583 return CFStringCompare(v1, v2, 0); 584 } 585 586 587 CFComparisonResult SOSPeerInfoCompareByApplicationDate(const void *val1, const void *val2, void *context) { 588 // The code below is necessary but not sufficient; not returning a CFComparisonResult 589 // It probably is OK to say that a NULL is < <non-NULL> 590 if (val1 == NULL || val2 == NULL) { 591 ptrdiff_t dv = val1 - val2; 592 return dv < 0 ? kCFCompareLessThan : dv == 0 ? kCFCompareEqualTo : kCFCompareGreaterThan; 593 } 594 595 CFDateRef v1 = SOSPeerInfoGetApplicationDate((SOSPeerInfoRef) val1); 596 CFDateRef v2 = SOSPeerInfoGetApplicationDate((SOSPeerInfoRef) val2); 597 if (v1 == NULL || v2 == NULL) { 598 ptrdiff_t dv = (const void *)v1 - (const void *)v2; 599 CFReleaseNull(v1); 600 CFReleaseNull(v2); 601 return dv < 0 ? kCFCompareLessThan : dv == 0 ? kCFCompareEqualTo : kCFCompareGreaterThan; 602 } 603 604 CFComparisonResult retval = CFDateCompare(v1, v2, 0); 605 CFReleaseNull(v1); 606 CFReleaseNull(v2); 607 return retval; 608 } 609 610 static CFHashCode SOSPeerInfoHash(CFTypeRef cf) { 611 SOSPeerInfoRef peer = (SOSPeerInfoRef) cf; 612 613 return CFHash(peer->description) ^ CFHash(peer->signature); 614 } 615 616 617 static char boolToChars(bool val, char truechar, char falsechar) { 618 return val? truechar: falsechar; 619 } 620 621 static CFStringRef isKnown(CFStringRef ref) { 622 return ref? ref: CFSTR("Unknown "); 623 } 624 625 static CFStringRef copyDescriptionWithFormatOptions(CFTypeRef aObj, CFDictionaryRef formatOptions){ 626 627 SOSPeerInfoRef pi = (SOSPeerInfoRef) aObj; 628 if(!pi) return NULL; 629 630 CFStringRef description = NULL; 631 // Get the format options we care about: 632 bool retired = SOSPeerInfoIsRetirementTicket(pi); 633 bool selfValid = SOSPeerInfoVerify(pi, NULL); 634 bool backingUp = SOSPeerInfoHasBackupKey(pi); 635 bool isKVS = SOSPeerInfoKVSOnly(pi); 636 bool isCKKSForAll = SOSPeerInfoSupportsCKKSForAll(pi); 637 CFStringRef osVersion = CFDictionaryGetValue(pi->gestalt, kPIOSVersionKey); 638 CFStringRef tmp = SOSPeerInfoV2DictionaryCopyString(pi, sDeviceID); 639 CFStringRef deviceID = CFStringCreateTruncatedCopy(tmp, 8); 640 CFReleaseNull(tmp); 641 CFStringRef serialNum = SOSPeerInfoCopySerialNumber(pi); 642 643 // Calculate the truncated length 644 645 CFStringRef objectPrefix = CFStringCreateWithFormat(kCFAllocatorDefault, formatOptions, CFSTR("PI@%p"), pi); 646 647 description = CFStringCreateWithFormat(kCFAllocatorDefault, formatOptions, 648 CFSTR("<%@: [name: %20@] [%c%c%c%c%c%c%c%c] [type: %-20@] [spid: %8@] [os: %10@] [devid: %10@] [serial: %12@]"), 649 objectPrefix, 650 isKnown(SOSPeerInfoGetPeerName(pi)), 651 '-', 652 '-', 653 boolToChars(selfValid, 'S', 's'), 654 boolToChars(retired, 'R', 'r'), 655 boolToChars(backingUp, 'B', 'b'), 656 boolToChars(isKVS, 'K', 'I'), 657 '-', 658 boolToChars(isCKKSForAll, 'C', '_'), 659 isKnown(SOSPeerInfoGetPeerDeviceType(pi)), isKnown(SOSPeerInfoGetSPID(pi)), 660 isKnown(osVersion), isKnown(deviceID), isKnown(serialNum)); 661 662 CFReleaseNull(deviceID); 663 CFReleaseNull(serialNum); 664 CFReleaseNull(objectPrefix); 665 666 return description; 667 } 668 669 static CFStringRef SOSPeerInfoCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) { 670 671 CFStringRef description = NULL; 672 673 description = copyDescriptionWithFormatOptions(aObj, formatOptions); 674 675 return description; 676 } 677 678 void SOSPeerInfoLogState(char *category, SOSPeerInfoRef pi, SecKeyRef pubKey, CFStringRef myPID, char sigchr) { 679 if(!pi) return; 680 bool appValid = SOSPeerInfoApplicationVerify(pi, pubKey, NULL); 681 bool retired = SOSPeerInfoIsRetirementTicket(pi); 682 // We won't inflate invalid peerInfos. Mark this true to keep scanning utilities from breaking. 683 bool selfValid = true; 684 bool backingUp = SOSPeerInfoHasBackupKey(pi); 685 bool isMe = CFEqualSafe(SOSPeerInfoGetPeerID(pi), myPID) == true; 686 bool isKVS = SOSPeerInfoKVSOnly(pi); 687 bool isCKKSForAll = SOSPeerInfoSupportsCKKSForAll(pi); 688 CFStringRef osVersion = CFDictionaryGetValue(pi->gestalt, kPIOSVersionKey); 689 CFStringRef tmp = SOSPeerInfoV2DictionaryCopyString(pi, sDeviceID); 690 CFStringRef deviceID = CFStringCreateTruncatedCopy(tmp, 8); 691 CFReleaseNull(tmp); 692 CFStringRef serialNum = SOSPeerInfoCopySerialNumber(pi); 693 694 secnotice(category, "PI: [name: %-20@] [%c%c%c%c%c%c%c%c] [type: %-20@] [spid: %8@] [os: %10@] [devid: %10@] [serial: %12@]", isKnown(SOSPeerInfoGetPeerName(pi)), 695 boolToChars(isMe, 'M', 'm'), 696 boolToChars(appValid, 'A', 'a'), 697 boolToChars(selfValid, 'S', 's'), 698 boolToChars(retired, 'R', 'r'), 699 boolToChars(backingUp, 'B', 'b'), 700 boolToChars(isKVS, 'K', 'I'), 701 boolToChars(isCKKSForAll, 'C', '_'), 702 sigchr, 703 isKnown(SOSPeerInfoGetPeerDeviceType(pi)), isKnown(SOSPeerInfoGetSPID(pi)), 704 isKnown(osVersion), isKnown(deviceID), isKnown(serialNum)); 705 706 CFReleaseNull(deviceID); 707 CFReleaseNull(serialNum); 708 } 709 710 CFDictionaryRef SOSPeerInfoCopyPeerGestalt(SOSPeerInfoRef pi) { 711 CFRetain(pi->gestalt); 712 return pi->gestalt; 713 } 714 715 CFDictionaryRef SOSPeerGetGestalt(SOSPeerInfoRef pi){ 716 return pi->gestalt; 717 } 718 719 CFStringRef SOSPeerInfoGetPeerName(SOSPeerInfoRef peer) { 720 return SOSPeerInfoLookupGestaltValue(peer, kPIUserDefinedDeviceNameKey); 721 } 722 723 CFStringRef SOSPeerInfoGetPeerDeviceType(SOSPeerInfoRef peer) { 724 return SOSPeerInfoLookupGestaltValue(peer, kPIDeviceModelNameKey); 725 } 726 727 CFIndex SOSPeerInfoGetPeerProtocolVersion(SOSPeerInfoRef peer) { 728 CFIndex version = PEERINFO_CURRENT_VERSION; 729 CFTypeRef val = SOSPeerInfoLookupGestaltValue(peer, kPIMessageProtocolVersionKey); 730 if (val && CFGetTypeID(val) == CFNumberGetTypeID()) 731 CFNumberGetValue(val, kCFNumberCFIndexType, &version); 732 return version; 733 } 734 735 CFTypeRef SOSPeerInfoLookupGestaltValue(SOSPeerInfoRef pi, CFStringRef key) { 736 return CFDictionaryGetValue(pi->gestalt, key); 737 } 738 739 CFStringRef SOSPeerInfoGetPeerID(SOSPeerInfoRef pi) { 740 return pi ? pi->peerID : NULL; 741 } 742 743 CFStringRef SOSPeerInfoGetSPID(SOSPeerInfoRef pi) { 744 return pi ? pi->spid : NULL; 745 } 746 747 bool SOSPeerInfoPeerIDEqual(SOSPeerInfoRef pi, CFStringRef myPeerID) { 748 return CFEqualSafe(myPeerID, SOSPeerInfoGetPeerID(pi)); 749 } 750 751 CFIndex SOSPeerInfoGetVersion(SOSPeerInfoRef pi) { 752 return pi->version; 753 } 754 755 bool SOSPeerInfoUpdateDigestWithPublicKeyBytes(SOSPeerInfoRef peer, const struct ccdigest_info *di, 756 ccdigest_ctx_t ctx, CFErrorRef *error) { 757 CFDataRef pubKeyBytes = CFDictionaryGetValue(peer->description, sPublicKeyKey); 758 759 if(!pubKeyBytes) { 760 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Digest failed – no public key")); 761 return false; 762 } 763 764 ccdigest_update(di, ctx, CFDataGetLength(pubKeyBytes), CFDataGetBytePtr(pubKeyBytes)); 765 766 return true; 767 } 768 769 bool SOSPeerInfoUpdateDigestWithDescription(SOSPeerInfoRef peer, const struct ccdigest_info *di, 770 ccdigest_ctx_t ctx, CFErrorRef *error) { 771 if(SOSPeerInfoVersionHasV2Data(peer)) SOSPeerInfoPackV2Data(peer); 772 size_t description_size = der_sizeof_plist(peer->description, error); 773 if (description_size == 0) { 774 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Description length failed")); 775 return false; 776 } 777 778 uint8_t * data = malloc(description_size); 779 if (data == NULL) { 780 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Description alloc failed")); 781 return false; 782 } 783 uint8_t *data_end = data + description_size; 784 uint8_t *encoded = der_encode_plist(peer->description, error, data, data_end); 785 786 if(!encoded) { 787 free(data); 788 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Description encode failed")); 789 return false; 790 } 791 792 ccdigest_update(di, ctx, description_size, data); 793 794 free(data); 795 796 return true; 797 } 798 799 800 static CFDataRef sosCreateDate() { 801 CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); 802 size_t bufsiz = der_sizeof_date(now, NULL); 803 uint8_t buf[bufsiz]; 804 der_encode_date(now, NULL, buf, buf+bufsiz); 805 CFReleaseNull(now); 806 return CFDataCreate(NULL, buf, bufsiz); 807 } 808 809 static CFDateRef sosCreateCFDate(CFDataRef sosdate) { 810 CFDateRef date; 811 der_decode_date(NULL, &date, NULL, CFDataGetBytePtr(sosdate), 812 CFDataGetBytePtr(sosdate) + CFDataGetLength(sosdate)); 813 return date; 814 } 815 816 static bool sospeer_application_hash(SOSPeerInfoRef pi, const struct ccdigest_info *di, uint8_t *hbuf) { 817 CFDataRef appdate = asData(CFDictionaryGetValue(pi->description, sApplicationDate), NULL); 818 if(!appdate) return false; 819 ccdigest_di_decl(di, ctx); 820 ccdigest_init(di, ctx); 821 ccdigest_update(di, ctx, CFDataGetLength(appdate), CFDataGetBytePtr(appdate)); 822 if (!SOSPeerInfoUpdateDigestWithPublicKeyBytes(pi, di, ctx, NULL)) return false; 823 ccdigest_final(di, ctx, hbuf); 824 return true; 825 } 826 827 SOSPeerInfoRef SOSPeerInfoCopyAsApplication(SOSPeerInfoRef original, SecKeyRef userkey, SecKeyRef peerkey, CFErrorRef *error) { 828 SOSPeerInfoRef result = NULL; 829 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, original, error); 830 831 const struct ccdigest_info *di = ccsha256_di(); 832 uint8_t hbuf[di->output_size]; 833 CFDataRef usersig = NULL; 834 835 CFDataRef creationDate = sosCreateDate(); 836 CFDictionarySetValue(pi->description, sApplicationDate, creationDate); 837 CFReleaseNull(creationDate); 838 839 // Create User Application Signature 840 require_action_quiet(sospeer_application_hash(pi, di, hbuf), fail, 841 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to create hash for peer applicant"), NULL, error)); 842 843 usersig = sosCopySignedHash(userkey, di, hbuf); 844 require_action_quiet(usersig, fail, 845 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to sign public key hash for peer"), NULL, error)); 846 847 CFDictionarySetValue(pi->description, sApplicationUsig, usersig); 848 pi->verifiedAppKeyID = SOSCopyIDOfKey(userkey, error); 849 if(pi->verifiedAppKeyID == NULL) { 850 secnotice("PICache", "failed to get userKeyID"); 851 } else { 852 pi->verifiedResult = true; 853 } 854 require_quiet(SOSPeerInfoSign(peerkey, pi, error), fail); 855 856 result = pi; 857 pi = NULL; 858 859 fail: 860 CFReleaseNull(usersig); 861 CFReleaseNull(pi); 862 return result; 863 } 864 865 bool SOSPeerInfoApplicationVerify(SOSPeerInfoRef pi, SecKeyRef userkey, CFErrorRef *error) { 866 const struct ccdigest_info *di = ccsha256_di(); 867 uint8_t hbuf[di->output_size]; 868 bool result = false; 869 CFStringRef userKeyID = NULL; 870 871 // If we've already succeeded signature check with this key move on. 872 require_action_quiet(userkey, exit, SOSErrorCreate(kSOSErrorNoKey, error, NULL, CFSTR("Can't validate PeerInfos with no userKey"))); 873 userKeyID = SOSCopyIDOfKey(userkey, error); 874 require_action_quiet(!CFEqualSafe(userKeyID, pi->verifiedAppKeyID), exit, result = pi->verifiedResult); 875 876 // verifiedAppKeyID was NULL or not the key we're looking for - clear it. 877 CFReleaseNull(pi->verifiedAppKeyID); 878 pi->verifiedResult = false; 879 CFDataRef usig = CFDictionaryGetValue(pi->description, sApplicationUsig); 880 require_action_quiet(usig, exit, 881 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Peer is not an applicant"), NULL, error)); 882 // Verify User Application Signature 883 require_action_quiet(sospeer_application_hash(pi, di, hbuf), exit, 884 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to create hash for peer applicant"), NULL, error)); 885 require_action_quiet(sosVerifyHash(userkey, di, hbuf, usig), exit, 886 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("user signature of public key hash fails to verify"), NULL, error)); 887 // Remember the userkey we validated for this peerinfo. 888 pi->verifiedAppKeyID = CFStringCreateCopy(kCFAllocatorDefault, userKeyID); 889 pi->verifiedResult = true; 890 891 result = SOSPeerInfoVerify(pi, error); 892 893 exit: 894 CFReleaseNull(userKeyID); 895 return result; 896 } 897 898 899 static CF_RETURNS_RETAINED CFDateRef sosPeerInfoGetDate(SOSPeerInfoRef pi, CFStringRef entry) { 900 if(!pi) return NULL; 901 CFDataRef sosdate = CFDictionaryGetValue(pi->description, entry); 902 if(!sosdate) return NULL; 903 CFDateRef date = sosCreateCFDate(sosdate); 904 905 return date; 906 } 907 908 CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetApplicationDate(SOSPeerInfoRef pi) { 909 return sosPeerInfoGetDate(pi, sApplicationDate); 910 } 911 912 CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetRetirementDate(SOSPeerInfoRef pi) { 913 return sosPeerInfoGetDate(pi, sRetirementDate); 914 } 915 916 917 918 // 919 // Gestalt helpers 920 // 921 922 CFStringRef SOSPeerGestaltGetName(CFDictionaryRef gestalt) { 923 CFStringRef name = SOSPeerGestaltGetAnswer(gestalt, kPIUserDefinedDeviceNameKey); 924 return isString(name) ? name : NULL; 925 } 926 927 CFTypeRef SOSPeerGestaltGetAnswer(CFDictionaryRef gestalt, CFStringRef question) { 928 return gestalt ? CFDictionaryGetValue(gestalt, question) : NULL; 929 } 930 931 // 932 // Peer Retirement 933 // 934 935 936 SOSPeerInfoRef SOSPeerInfoCreateRetirementTicket(CFAllocatorRef allocator, SecKeyRef privKey, SOSPeerInfoRef peer, CFErrorRef *error) { 937 // Copy PeerInfo 938 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, peer, error); 939 940 require(pi, fail); 941 942 // Fill out Resignation Date 943 CFDataRef resignationDate = sosCreateDate(); 944 CFDictionaryAddValue(pi->description, sRetirementDate, resignationDate); 945 CFReleaseNull(resignationDate); 946 947 require(SOSPeerInfoSign(privKey, pi, error), fail); 948 949 return pi; 950 951 fail: 952 CFReleaseNull(pi); 953 return NULL; 954 } 955 956 CFStringRef SOSPeerInfoInspectRetirementTicket(SOSPeerInfoRef pi, CFErrorRef *error) { 957 CFStringRef retval = NULL; 958 CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); 959 CFDateRef retirement = NULL; 960 961 require_quiet(SOSPeerInfoVerify(pi, error), err); 962 963 retirement = sosCreateCFDate(CFDictionaryGetValue(pi->description, sRetirementDate)); 964 require_action_quiet(retirement, err, 965 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Peer is not retired"), NULL, error)); 966 967 require_action_quiet(CFDateCompare(now, retirement, NULL) == kCFCompareGreaterThan, err, 968 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Retirement date is after current date"), NULL, error)); 969 970 retval = SOSPeerInfoGetPeerID(pi); 971 972 err: 973 CFReleaseNull(now); 974 CFReleaseNull(retirement); 975 return retval; 976 } 977 978 bool SOSPeerInfoRetireRetirementTicket(size_t max_seconds, SOSPeerInfoRef pi) { 979 CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); 980 CFDateRef retirement = sosCreateCFDate(CFDictionaryGetValue(pi->description, sRetirementDate)); 981 CFTimeInterval timediff = CFDateGetTimeIntervalSinceDate(now, retirement); // diff in seconds 982 CFReleaseNull(now); 983 CFReleaseNull(retirement); 984 if(timediff > (max_seconds)) return true; 985 return false; 986 } 987 988 static const void *SOSGetDescriptionValue(SOSPeerInfoRef pi, const void *key) { 989 if(pi && pi->description) { 990 return CFDictionaryGetValue(pi->description, key); 991 } 992 return NULL; 993 } 994 995 bool SOSPeerInfoIsRetirementTicket(SOSPeerInfoRef pi) { 996 CFDataRef flag = SOSGetDescriptionValue(pi, sRetirementDate); 997 return flag != NULL; 998 } 999 1000 bool SOSPeerInfoIsCloudIdentity(SOSPeerInfoRef pi) { 1001 CFTypeRef value = SOSGetDescriptionValue(pi, sCloudIdentityKey); 1002 return CFEqualSafe(value, kCFBooleanTrue); 1003 } 1004 1005 SOSPeerInfoRef SOSPeerInfoUpgradeSignatures(CFAllocatorRef allocator, SecKeyRef privKey, SecKeyRef peerKey, SOSPeerInfoRef peer, CFErrorRef *error) { 1006 SecKeyRef pubKey = SecKeyCreatePublicFromPrivate(privKey); 1007 SOSPeerInfoRef retval = NULL; 1008 1009 retval = SOSPeerInfoCopyAsApplication(peer, privKey, peerKey, error); 1010 CFReleaseNull(pubKey); 1011 return retval; 1012 } 1013 1014 bool SOSPeerInfoSetOctagonKeysInDescription(SOSPeerInfoRef peer, SecKeyRef octagonSigningKey, 1015 SecKeyRef octagonEncryptionKey, CFErrorRef *error) 1016 { 1017 bool ret = false; 1018 CFDataRef signingPublicKeyBytes = NULL; 1019 CFDataRef encryptionPublicKeyBytes = NULL; 1020 1021 OSStatus copySigningKeyResult = SecKeyCopyPublicBytes(octagonSigningKey, &signingPublicKeyBytes); 1022 OSStatus copyEncryptionKeyResult = SecKeyCopyPublicBytes(octagonEncryptionKey, &encryptionPublicKeyBytes); 1023 1024 require_action_quiet(errSecSuccess == copySigningKeyResult, fail, SecError(copySigningKeyResult, error, CFSTR("failed to copy signing public key bytes"))); 1025 require_action_quiet(errSecSuccess == copyEncryptionKeyResult, fail, SecError(copyEncryptionKeyResult, error, CFSTR("failed to copy encryption public key bytes"))); 1026 1027 1028 CFDictionarySetValue(peer->description, sOctagonPeerSigningPublicKeyKey, signingPublicKeyBytes); 1029 CFDictionarySetValue(peer->description, sOctagonPeerEncryptionPublicKeyKey, encryptionPublicKeyBytes); 1030 1031 ret = true; 1032 1033 fail: 1034 CFReleaseNull(signingPublicKeyBytes); 1035 CFReleaseNull(encryptionPublicKeyBytes); 1036 1037 return ret; 1038 } 1039 1040 1041 static SOSPeerInfoRef CF_RETURNS_RETAINED 1042 SOSPeerInfoSetBothOctagonKeys(CFAllocatorRef allocator, 1043 SOSPeerInfoRef toCopy, 1044 CFStringRef descriptionSigningKey, 1045 CFStringRef descriptionEncryptionKey, 1046 SecKeyRef octagonSigningKey, 1047 SecKeyRef octagonEncryptionKey, 1048 SecKeyRef signingKey, 1049 CFErrorRef *error) 1050 { 1051 CFDataRef signingPublicKeyBytes = NULL; 1052 CFDataRef encryptionPublicKeyBytes = NULL; 1053 1054 SOSPeerInfoRef pi = NULL; 1055 1056 OSStatus copySigningKeyResult = SecKeyCopyPublicBytes(octagonSigningKey, &signingPublicKeyBytes); 1057 OSStatus copyEncryptionKeyResult = SecKeyCopyPublicBytes(octagonEncryptionKey, &encryptionPublicKeyBytes); 1058 1059 require_action_quiet(0 == copySigningKeyResult, fail, SecError(copySigningKeyResult, error, CFSTR("failed to copy signing public key bytes"))); 1060 require_action_quiet(0 == copyEncryptionKeyResult, fail, SecError(copyEncryptionKeyResult, error, CFSTR("failed to copy encryption public key bytes"))); 1061 1062 pi = SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error, 1063 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) { 1064 if(peerToModify && peerToModify->description && signingPublicKeyBytes && descriptionSigningKey && encryptionPublicKeyBytes && descriptionEncryptionKey) { 1065 CFDictionarySetValue(peerToModify->description, descriptionSigningKey, signingPublicKeyBytes); 1066 CFDictionarySetValue(peerToModify->description, descriptionEncryptionKey, encryptionPublicKeyBytes); 1067 } else { 1068 SecError(errSecParam, error, CFSTR("Bad key bytes or dictionary key")); 1069 } 1070 return true; 1071 }); 1072 require(pi, fail); 1073 1074 fail: 1075 CFReleaseNull(signingPublicKeyBytes); 1076 CFReleaseNull(encryptionPublicKeyBytes); 1077 return pi; 1078 } 1079 1080 SOSPeerInfoRef CF_RETURNS_RETAINED 1081 SOSPeerInfoSetOctagonKeys(CFAllocatorRef allocator, 1082 SOSPeerInfoRef toCopy, 1083 SecKeyRef octagonSigningKey, 1084 SecKeyRef octagonEncryptionKey, 1085 SecKeyRef signingKey, 1086 CFErrorRef *error) 1087 { 1088 return SOSPeerInfoSetBothOctagonKeys(allocator, toCopy, sOctagonPeerSigningPublicKeyKey, sOctagonPeerEncryptionPublicKeyKey, octagonSigningKey, octagonEncryptionKey, signingKey, error); 1089 } 1090 1091 static SOSPeerInfoRef CF_RETURNS_RETAINED 1092 SOSPeerInfoSetOctagonKey(CFAllocatorRef allocator, 1093 SOSPeerInfoRef toCopy, 1094 CFStringRef descriptionKey, 1095 SecKeyRef octagonKey, 1096 SecKeyRef signingKey, 1097 CFErrorRef *error) 1098 { 1099 CFDataRef publicKeyBytes = NULL; 1100 SOSPeerInfoRef pi = NULL; 1101 1102 OSStatus copyResult = SecKeyCopyPublicBytes(octagonKey, &publicKeyBytes); 1103 require_action_quiet(0 == copyResult, fail, SecError(copyResult, error, CFSTR("failed to copy public key bytes"))); 1104 1105 pi = SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error, 1106 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) { 1107 if(peerToModify && peerToModify->description && publicKeyBytes && descriptionKey) { 1108 CFDictionarySetValue(peerToModify->description, descriptionKey, publicKeyBytes); 1109 } else { 1110 SecError(errSecParam, error, CFSTR("Bad key bytes or dictionary key")); 1111 } 1112 return true; 1113 }); 1114 require(pi, fail); 1115 1116 fail: 1117 CFReleaseNull(publicKeyBytes); 1118 return pi; 1119 } 1120 1121 SOSPeerInfoRef CF_RETURNS_RETAINED 1122 SOSPeerInfoSetOctagonSigningKey(CFAllocatorRef allocator, 1123 SOSPeerInfoRef toCopy, 1124 SecKeyRef octagonSigningKey, 1125 SecKeyRef signingKey, 1126 CFErrorRef *error) 1127 { 1128 return SOSPeerInfoSetOctagonKey(allocator, toCopy, sOctagonPeerSigningPublicKeyKey, octagonSigningKey, signingKey, error); 1129 } 1130 1131 SOSPeerInfoRef CF_RETURNS_RETAINED 1132 SOSPeerInfoSetOctagonEncryptionKey(CFAllocatorRef allocator, 1133 SOSPeerInfoRef toCopy, 1134 SecKeyRef octagonEncryptionKey, 1135 SecKeyRef signingKey, 1136 CFErrorRef *error) 1137 { 1138 return SOSPeerInfoSetOctagonKey(allocator, toCopy, sOctagonPeerEncryptionPublicKeyKey, octagonEncryptionKey, signingKey, error); 1139 } 1140 1141 CFStringRef SOSPeerInfoCopyTransportType(SOSPeerInfoRef peer){ 1142 CFStringRef transportType = (CFStringRef)SOSPeerInfoV2DictionaryCopyString(peer, sTransportType); 1143 return (transportType ? transportType : CFRetain(SOSTransportMessageTypeKVS)); 1144 } 1145 1146 bool SOSPeerInfoKVSOnly(SOSPeerInfoRef pi) { 1147 CFStringRef transportType = SOSPeerInfoCopyTransportType(pi); 1148 bool retval = CFEqualSafe(transportType, SOSTransportMessageTypeKVS); 1149 CFReleaseNull(transportType); 1150 return retval; 1151 } 1152 1153 CFStringRef SOSPeerInfoCopyDeviceID(SOSPeerInfoRef peer){ 1154 return CFSTR("not implemented"); 1155 } 1156 1157 SOSPeerInfoDeviceClass SOSPeerInfoGetClass(SOSPeerInfoRef pi) { 1158 static CFDictionaryRef devID2Class = NULL; 1159 static dispatch_once_t onceToken = 0; 1160 1161 dispatch_once(&onceToken, ^{ 1162 CFNumberRef cfSOSPeerInfo_macOS = CFNumberCreateWithCFIndex(kCFAllocatorDefault, SOSPeerInfo_macOS); 1163 CFNumberRef cfSOSPeerInfo_iOS = CFNumberCreateWithCFIndex(kCFAllocatorDefault, SOSPeerInfo_iOS); 1164 CFNumberRef cfSOSPeerInfo_iCloud = CFNumberCreateWithCFIndex(kCFAllocatorDefault, SOSPeerInfo_iCloud); 1165 // CFNumberRef cfSOSPeerInfo_watchOS = CFNumberCreateWithCFIndex(kCFAllocatorDefault, SOSPeerInfo_watchOS); 1166 // CFNumberRef cfSOSPeerInfo_tvOS = CFNumberCreateWithCFIndex(kCFAllocatorDefault, SOSPeerInfo_tvOS); 1167 1168 devID2Class = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 1169 CFSTR("Mac Pro"), cfSOSPeerInfo_macOS, 1170 CFSTR("MacBook"), cfSOSPeerInfo_macOS, 1171 CFSTR("MacBook Pro"), cfSOSPeerInfo_macOS, 1172 CFSTR("iCloud"), cfSOSPeerInfo_iCloud, 1173 CFSTR("iMac"), cfSOSPeerInfo_macOS, 1174 CFSTR("iPad"), cfSOSPeerInfo_iOS, 1175 CFSTR("iPhone"), cfSOSPeerInfo_iOS, 1176 CFSTR("iPod touch"), cfSOSPeerInfo_iOS, 1177 NULL); 1178 CFReleaseNull(cfSOSPeerInfo_macOS); 1179 CFReleaseNull(cfSOSPeerInfo_iOS); 1180 CFReleaseNull(cfSOSPeerInfo_iCloud); 1181 }); 1182 SOSPeerInfoDeviceClass retval = SOSPeerInfo_unknown; 1183 CFStringRef dt = SOSPeerInfoGetPeerDeviceType(pi); 1184 require_quiet(dt, errOut); 1185 CFNumberRef classNum = CFDictionaryGetValue(devID2Class, dt); 1186 require_quiet(classNum, errOut); 1187 CFIndex tmp; 1188 require_quiet(CFNumberGetValue(classNum, kCFNumberCFIndexType, &tmp), errOut); 1189 retval = (SOSPeerInfoDeviceClass) tmp; 1190 errOut: 1191 return retval; 1192 }