/ keychain / SecureObjectSync / SOSPeerInfo.m
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  }