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