1// 2// SOSPeerInfo.c 3// sec 4// 5// Created by Mitch Adler on 7/19/12. 6// 7// 8 9#include <AssertMacros.h> 10#include <TargetConditionals.h> 11 12#include <SecureObjectSync/SOSPeerInfo.h> 13#include <SecureObjectSync/SOSPeerInfoInternal.h> 14#include <SecureObjectSync/SOSCircle.h> 15 16#include <SecureObjectSync/SOSInternal.h> 17#include <ipc/securityd_client.h> 18 19#include "Imported/SecuritydXPC.h" 20 21#include <CoreFoundation/CFArray.h> 22#include <dispatch/dispatch.h> 23 24#include <stdlib.h> 25#include <assert.h> 26 27#include <utilities/SecCFWrappers.h> 28#include <utilities/SecCFRelease.h> 29#include <utilities/SecCFError.h> 30#include <utilities/SecXPCError.h> 31 32#include <utilities/der_plist.h> 33#include <utilities/der_plist_internal.h> 34#include <corecrypto/ccder.h> 35#include <utilities/der_date.h> 36 37#include <corecrypto/ccdigest.h> 38#include <corecrypto/ccsha2.h> 39 40 41#include <CoreFoundation/CoreFoundation.h> 42#include <CoreFoundation/CFDate.h> 43 44#include <xpc/xpc.h> 45 46#if TARGET_OS_IPHONE || TARGET_OS_EMBEDDED 47#include <MobileGestalt.h> 48#endif 49 50#include <Security/SecBase64.h> 51#include <Security/SecKeyPriv.h> 52#include <Security/SecOTR.h> 53 54#if 0//TARGET_OS_MAC // TODO: this function is the only one that causes secd to need to link against Security.framework on OSX 55 56__BEGIN_DECLS 57SecKeyRef _SecKeyCreateFromPublicData(CFAllocatorRef allocator, CFIndex algorithmID, CFDataRef publicBytes); 58__END_DECLS 59 60#endif /* TARGET_OS_MAC */ 61 62struct __OpaqueSOSPeerInfo { 63 CFRuntimeBase _base; 64 65 // 66 CFMutableDictionaryRef description; 67 CFDataRef signature; 68 69 // Cached data 70 CFDictionaryRef gestalt; 71 CFStringRef id; 72 CFIndex version; 73}; 74 75CFGiblisWithHashFor(SOSPeerInfo); 76 77CFStringRef kPIUserDefinedDeviceName = CFSTR("ComputerName"); 78CFStringRef kPIDeviceModelName = CFSTR("ModelName"); 79 80// Description Dictionary Entries 81static CFStringRef sPublicKeyKey = CFSTR("PublicSigningKey"); 82static CFStringRef sGestaltKey = CFSTR("DeviceGestalt"); 83static CFStringRef sVersionKey = CFSTR("ConflictVersion"); 84static CFStringRef sCloudIdentityKey = CFSTR("CloudIdentity"); 85static CFStringRef sApplicationDate = CFSTR("ApplicationDate"); 86static CFStringRef sApplicationUsig = CFSTR("ApplicationUsig"); 87static CFStringRef sRetirementDate = CFSTR("RetirementDate"); 88 89// Peerinfo Entries 90CFStringRef kSOSPeerInfoDescriptionKey = CFSTR("SOSPeerInfoDescription"); 91CFStringRef kSOSPeerInfoSignatureKey = CFSTR("SOSPeerInfoSignature"); 92CFStringRef kSOSPeerInfoNameKey = CFSTR("SOSPeerInfoName"); 93 94 95SecKeyRef SOSPeerInfoCopyPubKey(SOSPeerInfoRef peer) { 96 CFDataRef pubKeyBytes = CFDictionaryGetValue(peer->description, sPublicKeyKey); 97 CFAllocatorRef allocator = CFGetAllocator(peer); 98 SecKeyRef pubKey = SecKeyCreateFromPublicData(allocator, kSecECDSAAlgorithmID, pubKeyBytes); 99 return pubKey; 100} 101 102 103static bool SOSDescriptionHash(SOSPeerInfoRef peer, const struct ccdigest_info *di, void *hashresult, CFErrorRef *error) { 104 ccdigest_di_decl(di, ctx); 105 ccdigest_init(di, ctx); 106 void *ctx_p = ctx; 107 if(!SOSPeerInfoUpdateDigestWithDescription(peer, di, ctx_p, error)) return false; 108 ccdigest_final(di, ctx, hashresult); 109 return true; 110} 111 112 113#define SIGLEN 128 114static CFDataRef sosSignHash(SecKeyRef privkey, const struct ccdigest_info *di, uint8_t *hbuf) { 115 OSStatus stat; 116 size_t siglen = SIGLEN; 117 uint8_t sig[siglen]; 118 if((stat = SecKeyRawSign(privkey, kSecPaddingNone, hbuf, di->output_size, sig, &siglen)) != 0) { 119 return NULL; 120 } 121 return CFDataCreate(NULL, sig, (CFIndex)siglen); 122} 123 124static bool sosVerifyHash(SecKeyRef pubkey, const struct ccdigest_info *di, uint8_t *hbuf, CFDataRef signature) { 125 return SecKeyRawVerify(pubkey, kSecPaddingNone, hbuf, di->output_size, 126 CFDataGetBytePtr(signature), CFDataGetLength(signature)) == errSecSuccess; 127} 128 129static bool SOSPeerInfoSign(SecKeyRef privKey, SOSPeerInfoRef peer, CFErrorRef *error) { 130 bool status = false; 131 const struct ccdigest_info *di = ccsha256_di(); 132 uint8_t hbuf[di->output_size]; 133 CFDataRef newSignature = NULL; 134 135 require_action_quiet(SOSDescriptionHash(peer, di, hbuf, error), fail, 136 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to hash description for peer"), NULL, error)); 137 138 newSignature = sosSignHash(privKey, di, hbuf); 139 require_action_quiet(newSignature, fail, SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to sign peerinfo for peer"), NULL, error)); 140 141 CFReleaseNull(peer->signature); 142 peer->signature = newSignature; 143 newSignature = NULL; 144 status = true; 145 146fail: 147 CFReleaseNull(newSignature); 148 return status; 149} 150 151// Return true (1) if the signature verifies. 152static bool SOSPeerInfoVerify(SOSPeerInfoRef peer, CFErrorRef *error) { 153 bool result = false; 154 const struct ccdigest_info *di = ccsha256_di(); 155 uint8_t hbuf[di->output_size]; 156 157 SecKeyRef pubKey = SOSPeerInfoCopyPubKey(peer); 158 require_quiet(pubKey, error_out); 159 160 require_quiet(SOSDescriptionHash(peer, di, hbuf, error), error_out); 161 162 result = sosVerifyHash(pubKey, di, hbuf, peer->signature); 163 164error_out: 165 CFReleaseNull(pubKey); 166 return result; 167} 168 169static SOSPeerInfoRef SOSPeerInfoCreate_Internal(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error, void (^ description_modifier)(CFMutableDictionaryRef description)) { 170 SOSPeerInfoRef pi = CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator); 171 pi->gestalt = gestalt; 172 CFRetain(pi->gestalt); 173 174 pi->version = kSOSPeerVersion; 175 176 CFDataRef publicBytes = NULL; 177 CFNumberRef versionNumber = NULL; 178 179 SecKeyRef publicKey = SecKeyCreatePublicFromPrivate(signingKey); 180 if (publicKey == NULL) { 181 SOSCreateError(kSOSErrorBadKey, CFSTR("Unable to get public"), NULL, error); 182 CFReleaseNull(pi); 183 goto exit; 184 } 185 186 OSStatus result = SecKeyCopyPublicBytes(publicKey, &publicBytes); 187 188 if (result != errSecSuccess) { 189 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error); 190 CFReleaseNull(pi); 191 goto exit; 192 } 193 194 pi->signature = CFDataCreateMutable(allocator, 0); 195 196 versionNumber = CFNumberCreateWithCFIndex(NULL, pi->version); 197 pi->description = CFDictionaryCreateMutableForCFTypesWith(allocator, 198 sVersionKey, versionNumber, 199 sPublicKeyKey, publicBytes, 200 sGestaltKey, pi->gestalt, 201 NULL); 202 description_modifier(pi->description); 203 204 pi->id = SOSCopyIDOfKey(publicKey, error); 205 CFReleaseNull(publicKey); 206 207 require_quiet(pi->id, exit); 208 209 if (!SOSPeerInfoSign(signingKey, pi, error)) { 210 CFReleaseNull(pi); 211 goto exit; 212 } 213 214exit: 215 CFReleaseNull(versionNumber); 216 CFReleaseNull(publicBytes); 217 return pi; 218} 219 220SOSPeerInfoRef SOSPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) { 221 return SOSPeerInfoCreate_Internal(allocator, gestalt, signingKey, error, ^(CFMutableDictionaryRef description) {}); 222} 223 224SOSPeerInfoRef SOSPeerInfoCreateCloudIdentity(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) { 225 return SOSPeerInfoCreate_Internal(allocator, gestalt, signingKey, error, ^(CFMutableDictionaryRef description) { 226 CFDictionarySetValue(description, sCloudIdentityKey, kCFBooleanTrue); 227 }); 228 229} 230 231 232SOSPeerInfoRef SOSPeerInfoCreateCopy(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFErrorRef* error) { 233 SOSPeerInfoRef pi = CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator); 234 235 pi->description = CFDictionaryCreateMutableCopy(allocator, 0, toCopy->description); 236 pi->signature = CFDataCreateCopy(allocator, toCopy->signature); 237 238 pi->gestalt = CFDictionaryCreateCopy(allocator, toCopy->gestalt); 239 pi->id = CFStringCreateCopy(allocator, toCopy->id); 240 241 pi->version = toCopy->version; 242 243 return pi; 244} 245 246SOSPeerInfoRef SOSPeerInfoCopyWithGestaltUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) { 247 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, toCopy, error); 248 249 CFRetainSafe(gestalt); 250 CFReleaseNull(pi->gestalt); 251 pi->gestalt = gestalt; 252 253 CFDictionarySetValue(pi->description, sGestaltKey, pi->gestalt); 254 255 SecKeyRef pub_key = SOSPeerInfoCopyPubKey(pi); 256 257 pi->id = SOSCopyIDOfKey(pub_key, error); 258 require_quiet(pi->id, exit); 259 260 require_action_quiet(SOSPeerInfoSign(signingKey, pi, error), exit, CFReleaseNull(pi)); 261 262exit: 263 CFReleaseNull(pub_key); 264 return pi; 265} 266 267SOSPeerInfoRef SOSPeerInfoCreateFromDER(CFAllocatorRef allocator, CFErrorRef* error, 268 const uint8_t** der_p, const uint8_t *der_end) { 269 SOSPeerInfoRef pi = CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator); 270 SecKeyRef pubKey = NULL; 271 272 const uint8_t *sequence_end; 273 274 CFPropertyListRef pl = NULL; 275 276 pi->gestalt = NULL; 277 pi->version = 0; // TODO: Encode this in the DER 278 279 *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end); 280 *der_p = der_decode_plist(allocator, kCFPropertyListImmutable, &pl, error, *der_p, sequence_end); 281 *der_p = der_decode_data(allocator, kCFPropertyListImmutable, &pi->signature, error, *der_p, sequence_end); 282 283 if (*der_p == NULL || *der_p != sequence_end) { 284 SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Format of Peer Info DER"), NULL, error); 285 goto fail; 286 } 287 288 if (CFGetTypeID(pl) != CFDictionaryGetTypeID()) { 289 CFStringRef description = CFCopyTypeIDDescription(CFGetTypeID(pl)); 290 SOSCreateErrorWithFormat(kSOSErrorUnexpectedType, NULL, error, NULL, 291 CFSTR("Expected dictionary got %@"), description); 292 CFReleaseSafe(description); 293 goto fail; 294 } 295 296 pi->description = (CFMutableDictionaryRef) pl; 297 CFRetain(pi->description); 298 CFReleaseNull(pl); 299 300 CFNumberRef versionNumber = CFDictionaryGetValue(pi->description, sVersionKey); 301 302 if (versionNumber) { 303 CFNumberGetValue(versionNumber, kCFNumberCFIndexType, &pi->version); 304 } 305 306 CFDictionaryRef gestalt = CFDictionaryGetValue(pi->description, sGestaltKey); 307 308 if (!isDictionary(gestalt)) { 309 CFStringRef description = CFCopyTypeIDDescription(CFGetTypeID(pl)); 310 SOSCreateErrorWithFormat(kSOSErrorUnexpectedType, NULL, error, NULL, 311 CFSTR("Expected dictionary got %@"), description); 312 CFReleaseSafe(description); 313 goto fail; 314 } 315 316 pi->gestalt = gestalt; 317 CFRetain(pi->gestalt); 318 319 pubKey = SOSPeerInfoCopyPubKey(pi); 320 require_quiet(pubKey, fail); 321 322 pi->id = SOSCopyIDOfKey(pubKey, error); 323 require_quiet(pi->id, fail); 324 325 if(!SOSPeerInfoVerify(pi, error)) { 326 SOSCreateErrorWithFormat(kSOSErrorBadSignature, NULL, error, NULL, CFSTR("Signature doesn't validate")); 327 if (error) 328 secerror("Can't validate PeerInfo: %@", *error); 329 goto fail; 330 } 331 CFReleaseNull(pubKey); 332 return pi; 333 334fail: 335 CFReleaseNull(pi); 336 CFReleaseNull(pl); 337 CFReleaseNull(pubKey); 338 339 return NULL; 340} 341 342SOSPeerInfoRef SOSPeerInfoCreateFromData(CFAllocatorRef allocator, CFErrorRef* error, 343 CFDataRef peerinfo_data) { 344 const uint8_t *der = CFDataGetBytePtr(peerinfo_data); 345 CFIndex len = CFDataGetLength(peerinfo_data); 346 return SOSPeerInfoCreateFromDER(NULL, error, &der, der+len); 347} 348 349static void SOSPeerInfoDestroy(CFTypeRef aObj) { 350 SOSPeerInfoRef pi = (SOSPeerInfoRef) aObj; 351 352 if(!pi) return; 353 CFReleaseNull(pi->description); 354 CFReleaseNull(pi->signature); 355 CFReleaseNull(pi->gestalt); 356 CFReleaseNull(pi->id); 357} 358 359static Boolean SOSPeerInfoCompare(CFTypeRef lhs, CFTypeRef rhs) { 360 SOSPeerInfoRef lpeer = (SOSPeerInfoRef) lhs; 361 SOSPeerInfoRef rpeer = (SOSPeerInfoRef) rhs; 362 if(!lpeer || !rpeer) return false; 363 return CFEqualSafe(lpeer->description, rpeer->description) && CFEqualSafe(lpeer->signature, rpeer->signature); 364} 365 366 367CFComparisonResult SOSPeerInfoCompareByID(const void *val1, const void *val2, void *context) { 368 // The code below is necessary but not sufficient; not returning a CFComparisonResult 369 // It probably is OK to say that a NULL is < <non-NULL> 370 if (val1 == NULL || val2 == NULL) { 371 ptrdiff_t dv = val1 - val2; 372 return dv < 0 ? kCFCompareLessThan : dv == 0 ? kCFCompareEqualTo : kCFCompareGreaterThan; 373 } 374 375 CFStringRef v1 = SOSPeerInfoGetPeerID((SOSPeerInfoRef) val1); 376 CFStringRef v2 = SOSPeerInfoGetPeerID((SOSPeerInfoRef) val2); 377 if (v1 == NULL || v2 == NULL) { 378 ptrdiff_t dv = (const void *)v1 - (const void *)v2; 379 return dv < 0 ? kCFCompareLessThan : dv == 0 ? kCFCompareEqualTo : kCFCompareGreaterThan; 380 } 381 382 return CFStringCompare(v1, v2, 0); 383} 384 385static CFHashCode SOSPeerInfoHash(CFTypeRef cf) { 386 SOSPeerInfoRef peer = (SOSPeerInfoRef) cf; 387 388 return CFHash(peer->description) ^ CFHash(peer->signature); 389} 390 391static CFStringRef SOSPeerInfoCopyDescription(CFTypeRef aObj) { 392 SOSPeerInfoRef pi = (SOSPeerInfoRef) aObj; 393 394 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SOSPeerInfo@%p: Name:'%@'%s Type: '%@' ID:'%@'>"), 395 pi, 396 CFDictionaryGetValue(pi->gestalt, kPIUserDefinedDeviceName), 397 SOSPeerInfoIsRetirementTicket(pi) ? " [retired]" : "", 398 CFDictionaryGetValue(pi->gestalt, kPIDeviceModelName), 399 pi->id); 400} 401 402CFDictionaryRef SOSPeerInfoCopyPeerGestalt(SOSPeerInfoRef pi) { 403 CFRetain(pi->gestalt); 404 return pi->gestalt; 405} 406 407CFStringRef SOSPeerInfoGetPeerName(SOSPeerInfoRef peer) { 408 return SOSPeerInfoLookupGestaltValue(peer, kPIUserDefinedDeviceName); 409} 410 411CFStringRef SOSPeerInfoGetPeerDeviceType(SOSPeerInfoRef peer) { 412 return SOSPeerInfoLookupGestaltValue(peer, kPIDeviceModelName); 413} 414 415CFTypeRef SOSPeerInfoLookupGestaltValue(SOSPeerInfoRef pi, CFStringRef key) { 416 return CFDictionaryGetValue(pi->gestalt, key); 417} 418 419CFStringRef SOSPeerInfoGetPeerID(SOSPeerInfoRef pi) { 420 return pi ? pi->id : NULL; 421} 422 423CFIndex SOSPeerInfoGetVersion(SOSPeerInfoRef pi) { 424 // TODO: Encode this in the DER. 425 return pi->version; 426} 427 428bool SOSPeerInfoUpdateDigestWithPublicKeyBytes(SOSPeerInfoRef peer, const struct ccdigest_info *di, 429 ccdigest_ctx_t ctx, CFErrorRef *error) { 430 CFDataRef pubKeyBytes = CFDictionaryGetValue(peer->description, sPublicKeyKey); 431 432 if(!pubKeyBytes) { 433 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Digest failed – no public key")); 434 return false; 435 } 436 437 ccdigest_update(di, ctx, CFDataGetLength(pubKeyBytes), CFDataGetBytePtr(pubKeyBytes)); 438 439 return true; 440} 441 442bool SOSPeerInfoUpdateDigestWithDescription(SOSPeerInfoRef peer, const struct ccdigest_info *di, 443 ccdigest_ctx_t ctx, CFErrorRef *error) { 444 size_t description_size = der_sizeof_plist(peer->description, error); 445 uint8_t data_begin[description_size]; 446 uint8_t *data_end = data_begin + description_size; 447 uint8_t *encoded = der_encode_plist(peer->description, error, data_begin, data_end); 448 449 if(!encoded) { 450 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Description encode failed")); 451 return false; 452 } 453 454 ccdigest_update(di, ctx, description_size, data_begin); 455 456 return true; 457} 458 459 460static CFDataRef sosCreateDate() { 461 CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); 462 size_t bufsiz = der_sizeof_date(now, NULL); 463 uint8_t buf[bufsiz]; 464 der_encode_date(now, NULL, buf, buf+bufsiz); 465 CFReleaseNull(now); 466 return CFDataCreate(NULL, buf, bufsiz); 467} 468 469static CFDateRef sosCreateCFDate(CFDataRef sosdate) { 470 CFDateRef date; 471 der_decode_date(NULL, 0, &date, NULL, CFDataGetBytePtr(sosdate), 472 CFDataGetBytePtr(sosdate) + CFDataGetLength(sosdate)); 473 return date; 474} 475 476static bool sospeer_application_hash(SOSPeerInfoRef pi, const struct ccdigest_info *di, uint8_t *hbuf) { 477 CFDataRef appdate = CFDictionaryGetValue(pi->description, sApplicationDate); 478 if(!appdate) return false; 479 ccdigest_di_decl(di, ctx); 480 ccdigest_init(di, ctx); 481 ccdigest_update(di, ctx, CFDataGetLength(appdate), CFDataGetBytePtr(appdate)); 482 if (!SOSPeerInfoUpdateDigestWithPublicKeyBytes(pi, di, ctx, NULL)) return false; 483 ccdigest_final(di, ctx, hbuf); 484 return true; 485} 486 487SOSPeerInfoRef SOSPeerInfoCopyAsApplication(SOSPeerInfoRef original, SecKeyRef userkey, SecKeyRef peerkey, CFErrorRef *error) { 488 SOSPeerInfoRef result = NULL; 489 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, original, error); 490 491 const struct ccdigest_info *di = ccsha256_di(); 492 uint8_t hbuf[di->output_size]; 493 CFDataRef usersig = NULL; 494 495 CFDataRef creationDate = sosCreateDate(); 496 CFDictionarySetValue(pi->description, sApplicationDate, creationDate); 497 CFReleaseNull(creationDate); 498 499 // Create User Application Signature 500 require_action_quiet(sospeer_application_hash(pi, di, hbuf), fail, 501 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to create hash for peer applicant"), NULL, error)); 502 503 usersig = sosSignHash(userkey, di, hbuf); 504 require_action_quiet(usersig, fail, 505 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to sign public key hash for peer"), NULL, error)); 506 507 CFDictionarySetValue(pi->description, sApplicationUsig, usersig); 508 509 require_quiet(SOSPeerInfoSign(peerkey, pi, error), fail); 510 511 result = pi; 512 pi = NULL; 513 514fail: 515 CFReleaseNull(usersig); 516 CFReleaseNull(pi); 517 return result; 518} 519 520bool SOSPeerInfoApplicationVerify(SOSPeerInfoRef pi, SecKeyRef userkey, CFErrorRef *error) { 521 const struct ccdigest_info *di = ccsha256_di(); 522 uint8_t hbuf[di->output_size]; 523 bool result = false; 524 525 CFDataRef usig = CFDictionaryGetValue(pi->description, sApplicationUsig); 526 require_action_quiet(usig, exit, 527 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Peer is not an applicant"), NULL, error)); 528 // Verify User Application Signature 529 require_action_quiet(sospeer_application_hash(pi, di, hbuf), exit, 530 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to create hash for peer applicant"), NULL, error)); 531 require_action_quiet(sosVerifyHash(userkey, di, hbuf, usig), exit, 532 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("user signature of public key hash fails to verify"), NULL, error)); 533 534 result = SOSPeerInfoVerify(pi, error); 535 536exit: 537 return result; 538} 539 540 541static CF_RETURNS_RETAINED CFDateRef sosPeerInfoGetDate(SOSPeerInfoRef pi, CFStringRef entry) { 542 if(!pi) return NULL; 543 CFDataRef sosdate = CFDictionaryGetValue(pi->description, entry); 544 if(!sosdate) return NULL; 545 CFDateRef date = sosCreateCFDate(sosdate); 546 547 return date; 548} 549 550CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetApplicationDate(SOSPeerInfoRef pi) { 551 return sosPeerInfoGetDate(pi, sApplicationDate); 552} 553 554CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetRetirementDate(SOSPeerInfoRef pi) { 555 return sosPeerInfoGetDate(pi, sRetirementDate); 556} 557 558 559size_t SOSPeerInfoGetDEREncodedSize(SOSPeerInfoRef peer, CFErrorRef *error) { 560 size_t plist_size = der_sizeof_plist(peer->description, error); 561 if (plist_size == 0) 562 return 0; 563 564 size_t signature_size = der_sizeof_data(peer->signature, error); 565 if (signature_size == 0) 566 return 0; 567 568 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, 569 plist_size + signature_size); 570} 571 572uint8_t* SOSPeerInfoEncodeToDER(SOSPeerInfoRef peer, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) { 573 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, 574 der_encode_plist(peer->description, error, der, 575 der_encode_data(peer->signature, error, der, der_end))); 576} 577 578CFDataRef SOSPeerInfoCopyEncodedData(SOSPeerInfoRef peer, CFAllocatorRef allocator, CFErrorRef *error) { 579 size_t size = SOSPeerInfoGetDEREncodedSize(peer, error); 580 if (size == 0) return NULL; 581 582 uint8_t buffer[size]; 583 uint8_t* start = SOSPeerInfoEncodeToDER(peer, error, buffer, buffer + sizeof(buffer)); 584 CFDataRef result = CFDataCreate(kCFAllocatorDefault, start, size); 585 return result; 586} 587 588 589// 590// PeerInfoArray encoding decoding 591// 592 593CFMutableArrayRef SOSPeerInfoArrayCreateFromDER(CFAllocatorRef allocator, CFErrorRef* error, 594 const uint8_t** der_p, const uint8_t *der_end) { 595 CFMutableArrayRef pia = CFArrayCreateMutableForCFTypes(allocator); 596 597 const uint8_t *sequence_end; 598 599 *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end); 600 601 require_action(*der_p, fail, SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Peer Info Array Sequence Header"), NULL, error)); 602 603 while (sequence_end != *der_p) { 604 SOSPeerInfoRef pi = SOSPeerInfoCreateFromDER(allocator, error, der_p, sequence_end); 605 606 if (pi == NULL || *der_p == NULL) { 607 SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Peer Info Array DER"), (error != NULL ? *error : NULL), error); 608 CFReleaseNull(pi); 609 goto fail; 610 } 611 612 CFArrayAppendValue(pia, pi); 613 CFReleaseSafe(pi); 614 } 615 616 if (!pia) 617 *der_p = NULL; 618 return pia; 619 620fail: 621 CFReleaseNull(pia); 622 *der_p = NULL; 623 return NULL; 624} 625 626size_t SOSPeerInfoArrayGetDEREncodedSize(CFArrayRef pia, CFErrorRef *error) { 627 size_t array_size = 0; 628 629 for(CFIndex count = CFArrayGetCount(pia); 630 count > 0; 631 --count) { 632 SOSPeerInfoRef pi = (SOSPeerInfoRef) CFArrayGetValueAtIndex(pia, count - 1); 633 634 if (CFGetTypeID(pi) != SOSPeerInfoGetTypeID()) { 635 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Non SOSPeerInfo in array"), (error != NULL ? *error : NULL), error); 636 return 0; 637 } 638 639 size_t pi_size = SOSPeerInfoGetDEREncodedSize(pi, error); 640 641 if (pi_size == 0) { 642 SOSCreateError(kSOSErrorEncodeFailure, CFSTR("Bad DER size"), (error != NULL ? *error : NULL), error); 643 return 0; 644 } 645 646 array_size += pi_size; 647 } 648 649 650 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, 651 array_size); 652} 653 654uint8_t* SOSPeerInfoArrayEncodeToDER(CFArrayRef pia, CFErrorRef* error, const uint8_t* der, uint8_t* der_end_param) { 655 656 uint8_t* const sequence_end = der_end_param; 657 __block uint8_t* der_end = der_end_param; 658 659 CFArrayForEachReverse(pia, ^(const void *value) { 660 SOSPeerInfoRef pi = (SOSPeerInfoRef) value; 661 if (CFGetTypeID(pi) != SOSPeerInfoGetTypeID()) { 662 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Non SOSPeerInfo in array"), NULL, error); 663 der_end = NULL; // Indicate error and continue. 664 } 665 if (der_end) 666 der_end = SOSPeerInfoEncodeToDER(pi, error, der, der_end); 667 }); 668 669 if (der_end == NULL) 670 return NULL; 671 672 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, sequence_end, der, der_end); 673} 674 675 676CFArrayRef CreateArrayOfPeerInfoWithXPCObject(xpc_object_t peerArray, CFErrorRef* error) { 677 if (!peerArray) { 678 SecCFCreateErrorWithFormat(kSecXPCErrorUnexpectedNull, sSecXPCErrorDomain, NULL, error, NULL, CFSTR("Unexpected Null Array to encode")); 679 return NULL; 680 } 681 682 if (xpc_get_type(peerArray) != XPC_TYPE_DATA) { 683 SecCFCreateErrorWithFormat(kSecXPCErrorUnexpectedType, sSecXPCErrorDomain, NULL, error, NULL, CFSTR("Array of peer info not array, got %@"), peerArray); 684 return NULL; 685 } 686 687 const uint8_t* der = xpc_data_get_bytes_ptr(peerArray); 688 const uint8_t* der_end = der + xpc_data_get_length(peerArray); 689 690 return SOSPeerInfoArrayCreateFromDER(kCFAllocatorDefault, error, &der, der_end); 691} 692 693xpc_object_t CreateXPCObjectWithArrayOfPeerInfo(CFArrayRef array, CFErrorRef *error) { 694 size_t data_size = SOSPeerInfoArrayGetDEREncodedSize(array, error); 695 if (data_size == 0) 696 return NULL; 697 uint8_t *data = (uint8_t *)malloc(data_size); 698 if (!data) return NULL; 699 700 xpc_object_t result = NULL; 701 if (SOSPeerInfoArrayEncodeToDER(array, error, data, data + data_size)) 702 result = xpc_data_create(data, data_size); 703 704 free(data); 705 return result; 706} 707 708// 709// Gestalt helpers 710// 711 712CFStringRef SOSPeerGestaltGetName(CFDictionaryRef gestalt) { 713 CFStringRef name = SOSPeerGestaltGetAnswer(gestalt, kPIUserDefinedDeviceName); 714 return isString(name) ? name : NULL; 715} 716 717CFTypeRef SOSPeerGestaltGetAnswer(CFDictionaryRef gestalt, CFStringRef question) { 718 return gestalt ? CFDictionaryGetValue(gestalt, question) : NULL; 719} 720 721// 722// Peer Retirement 723// 724 725 726SOSPeerInfoRef SOSPeerInfoCreateRetirementTicket(CFAllocatorRef allocator, SecKeyRef privKey, SOSPeerInfoRef peer, CFErrorRef *error) { 727 // Copy PeerInfo 728 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, peer, error); 729 730 require(pi, fail); 731 732 // Fill out Resignation Date 733 CFDataRef resignationDate = sosCreateDate(); 734 CFDictionaryAddValue(pi->description, sRetirementDate, resignationDate); 735 CFReleaseNull(resignationDate); 736 737 require(SOSPeerInfoSign(privKey, pi, error), fail); 738 739 return pi; 740 741fail: 742 CFReleaseNull(pi); 743 return NULL; 744} 745 746CFStringRef SOSPeerInfoInspectRetirementTicket(SOSPeerInfoRef pi, CFErrorRef *error) { 747 CFStringRef retval = NULL; 748 CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); 749 CFDateRef retirement = NULL; 750 751 require_quiet(SOSPeerInfoVerify(pi, error), err); 752 753 retirement = sosCreateCFDate(CFDictionaryGetValue(pi->description, sRetirementDate)); 754 755 require_action_quiet(retirement, err, 756 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Peer is not retired"), NULL, error)); 757 758 require_action_quiet(CFDateCompare(now, retirement, NULL) == kCFCompareGreaterThan, err, 759 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Retirement date is after current date"), NULL, error)); 760 761 retval = SOSPeerInfoGetPeerID(pi); 762 763err: 764 CFReleaseNull(now); 765 CFReleaseNull(retirement); 766 return retval; 767} 768 769bool SOSPeerInfoRetireRetirementTicket(size_t max_seconds, SOSPeerInfoRef pi) { 770 CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); 771 CFDateRef retirement = sosCreateCFDate(CFDictionaryGetValue(pi->description, sRetirementDate)); 772 CFTimeInterval timediff = CFDateGetTimeIntervalSinceDate(now, retirement); // diff in seconds 773 CFReleaseNull(now); 774 CFReleaseNull(retirement); 775 if(timediff > (max_seconds)) return true; 776 return false; 777} 778 779bool SOSPeerInfoIsRetirementTicket(SOSPeerInfoRef pi) { 780 CFDataRef flag = CFDictionaryGetValue(pi->description, sRetirementDate); 781 return flag != NULL; 782} 783 784bool SOSPeerInfoIsCloudIdentity(SOSPeerInfoRef pi) { 785 CFTypeRef value = CFDictionaryGetValue(pi->description, sCloudIdentityKey); 786 return CFEqualSafe(value, kCFBooleanTrue); 787} 788 789SOSPeerInfoRef SOSPeerInfoUpgradeSignatures(CFAllocatorRef allocator, SecKeyRef privKey, SecKeyRef peerKey, SOSPeerInfoRef peer, CFErrorRef *error) { 790 SecKeyRef pubKey = SecKeyCreatePublicFromPrivate(privKey); 791 SOSPeerInfoRef retval = NULL; 792 793 retval = SOSPeerInfoCopyAsApplication(peer, privKey, peerKey, error); 794 CFReleaseNull(pubKey); 795 return retval; 796} 797 798