2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 #include <AssertMacros.h>
26 #include <TargetConditionals.h>
28 #include <Security/SecureObjectSync/SOSPeerInfo.h>
29 #include <Security/SecureObjectSync/SOSPeerInfoInternal.h>
30 #include <Security/SecureObjectSync/SOSPeerInfoPriv.h>
31 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
32 #include <Security/SecureObjectSync/SOSCircle.h>
33 #include <Security/SecureObjectSync/SOSAccountPriv.h>
34 #include <Security/SecureObjectSync/SOSInternal.h>
35 #include <ipc/securityd_client.h>
37 #include <CoreFoundation/CFArray.h>
38 #include <dispatch/dispatch.h>
43 #include <utilities/SecCFWrappers.h>
44 #include <utilities/SecCFRelease.h>
45 #include <utilities/SecCFError.h>
46 #include <utilities/SecXPCError.h>
48 #include <utilities/der_plist.h>
49 #include <utilities/der_plist_internal.h>
50 #include <corecrypto/ccder.h>
51 #include <utilities/der_date.h>
53 #include <corecrypto/ccdigest.h>
54 #include <corecrypto/ccsha2.h>
57 #include <CoreFoundation/CoreFoundation.h>
58 #include <CoreFoundation/CFDate.h>
62 #if TARGET_OS_IPHONE || TARGET_OS_EMBEDDED
63 #include <MobileGestalt.h>
66 #include <Security/SecBase64.h>
67 #include <Security/SecKeyPriv.h>
68 #include <Security/SecOTR.h>
69 #include <Security/SecuritydXPC.h>
71 CFGiblisWithHashFor(SOSPeerInfo
);
74 const CFStringRef kPIUserDefinedDeviceNameKey
= CFSTR("ComputerName");
75 const CFStringRef kPIDeviceModelNameKey
= CFSTR("ModelName");
76 const CFStringRef kPIMessageProtocolVersionKey
= CFSTR("MessageProtocolVersion");
77 const CFStringRef kPIOSVersionKey
= CFSTR("OSVersion");
79 // Description Dictionary Entries
80 static CFStringRef sPublicKeyKey
= CFSTR("PublicSigningKey");
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");
89 CFStringRef kSOSPeerInfoDescriptionKey
= CFSTR("SOSPeerInfoDescription");
90 CFStringRef kSOSPeerInfoSignatureKey
= CFSTR("SOSPeerInfoSignature");
91 CFStringRef kSOSPeerInfoNameKey
= CFSTR("SOSPeerInfoName");
93 //Peer Info V2 Dictionary IDS keys
94 CFStringRef sPreferIDS
= CFSTR("PreferIDS");
95 CFStringRef sTransportType
= CFSTR("TransportType");
96 CFStringRef sDeviceID
= CFSTR("DeviceID");
98 const CFStringRef peerIDLengthKey
= CFSTR("idLength");
100 SOSPeerInfoRef
SOSPeerInfoAllocate(CFAllocatorRef allocator
) {
101 return CFTypeAllocate(SOSPeerInfo
, struct __OpaqueSOSPeerInfo
, allocator
);
104 SecKeyRef
SOSPeerInfoCopyPubKey(SOSPeerInfoRef peer
) {
105 CFDataRef pubKeyBytes
= CFDictionaryGetValue(peer
->description
, sPublicKeyKey
);
106 if (pubKeyBytes
== NULL
)
108 CFAllocatorRef allocator
= CFGetAllocator(peer
);
109 SecKeyRef pubKey
= SecKeyCreateFromPublicData(allocator
, kSecECDSAAlgorithmID
, pubKeyBytes
);
113 CFDataRef
SOSPeerInfoGetAutoAcceptInfo(SOSPeerInfoRef peer
) {
114 CFDataRef pubKeyBytes
= NULL
;
116 pubKeyBytes
= CFDictionaryGetValue(peer
->description
, sPublicKeyKey
);
117 if (!pubKeyBytes
|| CFGetTypeID(pubKeyBytes
) != CFDataGetTypeID()) {
124 static bool SOSDescriptionHash(SOSPeerInfoRef peer
, const struct ccdigest_info
*di
, void *hashresult
, CFErrorRef
*error
) {
125 ccdigest_di_decl(di
, ctx
);
126 ccdigest_init(di
, ctx
);
128 if(!SOSPeerInfoUpdateDigestWithDescription(peer
, di
, ctx_p
, error
)) return false;
129 ccdigest_final(di
, ctx
, hashresult
);
135 static CFDataRef
sosSignHash(SecKeyRef privkey
, const struct ccdigest_info
*di
, uint8_t *hbuf
) {
137 size_t siglen
= SIGLEN
;
139 if((stat
= SecKeyRawSign(privkey
, kSecPaddingNone
, hbuf
, di
->output_size
, sig
, &siglen
)) != 0) {
142 return CFDataCreate(NULL
, sig
, (CFIndex
)siglen
);
145 static 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
;
150 bool SOSPeerInfoSign(SecKeyRef privKey
, SOSPeerInfoRef peer
, CFErrorRef
*error
) {
152 const struct ccdigest_info
*di
= ccsha256_di();
153 uint8_t hbuf
[di
->output_size
];
154 CFDataRef newSignature
= NULL
;
156 require_action_quiet(SOSDescriptionHash(peer
, di
, hbuf
, error
), fail
,
157 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Failed to hash description for peer"), NULL
, error
));
159 newSignature
= sosSignHash(privKey
, di
, hbuf
);
160 require_action_quiet(newSignature
, fail
, SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Failed to sign peerinfo for peer"), NULL
, error
));
162 CFReleaseNull(peer
->signature
);
163 peer
->signature
= newSignature
;
168 CFReleaseNull(newSignature
);
172 // Return true (1) if the signature verifies.
173 bool SOSPeerInfoVerify(SOSPeerInfoRef peer
, CFErrorRef
*error
) {
175 const struct ccdigest_info
*di
= ccsha256_di();
176 uint8_t hbuf
[di
->output_size
];
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
));
183 require_quiet(SOSDescriptionHash(peer
, di
, hbuf
, error
), error_out
);
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
));
191 CFReleaseNull(pubKey
);
195 void SOSPeerInfoSetVersionNumber(SOSPeerInfoRef pi
, int version
) {
196 pi
->version
= version
;
197 CFNumberRef versionNumber
= CFNumberCreateWithCFIndex(NULL
, pi
->version
);
198 CFDictionarySetValue(pi
->description
, sVersionKey
, versionNumber
);
199 CFReleaseNull(versionNumber
);
202 static SOSPeerInfoRef
SOSPeerInfoCreate_Internal(CFAllocatorRef allocator
,
203 CFDictionaryRef gestalt
, CFDataRef backup_key
,
204 CFStringRef IDSID
, CFStringRef transportType
, CFBooleanRef preferIDS
,
205 CFSetRef enabledViews
,
206 SecKeyRef signingKey
, CFErrorRef
* error
,
207 void (^ description_modifier
)(CFMutableDictionaryRef description
)) {
208 SOSPeerInfoRef pi
= CFTypeAllocate(SOSPeerInfo
, struct __OpaqueSOSPeerInfo
, allocator
);
209 pi
->gestalt
= gestalt
;
210 CFRetain(pi
->gestalt
);
212 pi
->version
= SOSPeerInfoGetPeerProtocolVersion(pi
);
213 CFDataRef publicBytes
= NULL
;
214 CFNumberRef versionNumber
= NULL
;
216 SecKeyRef publicKey
= SecKeyCreatePublicFromPrivate(signingKey
);
217 if (publicKey
== NULL
) {
218 SOSCreateError(kSOSErrorBadKey
, CFSTR("Unable to get public"), NULL
, error
);
223 OSStatus result
= SecKeyCopyPublicBytes(publicKey
, &publicBytes
);
225 if (result
!= errSecSuccess
) {
226 SOSCreateError(kSOSErrorBadKey
, CFSTR("Failed to export public bytes"), NULL
, error
);
231 pi
->signature
= CFDataCreateMutable(allocator
, 0);
233 versionNumber
= CFNumberCreateWithCFIndex(NULL
, pi
->version
);
235 pi
->description
= CFDictionaryCreateMutableForCFTypesWith(allocator
,
236 sVersionKey
, versionNumber
,
237 sPublicKeyKey
, publicBytes
,
238 sGestaltKey
, pi
->gestalt
,
241 if (backup_key
!= NULL
)
242 SOSPeerInfoV2DictionarySetValue(pi
, sBackupKeyKey
, backup_key
);
244 description_modifier(pi
->description
);
247 pi
->id
= SOSCopyIDOfKey(publicKey
, error
);
248 CFReleaseNull(publicKey
);
250 require_quiet(pi
->id
, exit
);
252 // ================ V2 Additions Start
254 if(!SOSPeerInfoUpdateToV2(pi
, error
)) {
259 // V2DictionarySetValue handles NULL as remove
260 SOSPeerInfoV2DictionarySetValue(pi
, sBackupKeyKey
, backup_key
);
261 SOSPeerInfoV2DictionarySetValue(pi
, sDeviceID
, IDSID
);
262 SOSPeerInfoV2DictionarySetValue(pi
, sTransportType
, transportType
);
263 SOSPeerInfoV2DictionarySetValue(pi
, sPreferIDS
, preferIDS
);
265 SOSPeerInfoV2DictionarySetValue(pi
, sViewsKey
, enabledViews
);
267 // ================ V2 Additions End
269 if (!SOSPeerInfoSign(signingKey
, pi
, error
)) {
275 CFReleaseNull(versionNumber
);
276 CFReleaseNull(publicBytes
);
280 SOSPeerInfoRef
SOSPeerInfoCreate(CFAllocatorRef allocator
, CFDictionaryRef gestalt
, CFDataRef backup_key
, SecKeyRef signingKey
, CFErrorRef
* error
) {
281 return SOSPeerInfoCreate_Internal(allocator
, gestalt
, backup_key
, NULL
, NULL
, NULL
, NULL
, signingKey
, error
, ^(CFMutableDictionaryRef description
) {});
284 SOSPeerInfoRef
SOSPeerInfoCreateWithTransportAndViews(CFAllocatorRef allocator
, CFDictionaryRef gestalt
, CFDataRef backup_key
,
285 CFStringRef IDSID
, CFStringRef transportType
, CFBooleanRef preferIDS
,
286 CFSetRef enabledViews
,
287 SecKeyRef signingKey
, CFErrorRef
* error
)
289 return SOSPeerInfoCreate_Internal(allocator
, gestalt
, backup_key
, IDSID
, transportType
, preferIDS
, enabledViews
, signingKey
, error
, ^(CFMutableDictionaryRef description
) {});
293 SOSPeerInfoRef
SOSPeerInfoCreateCloudIdentity(CFAllocatorRef allocator
, CFDictionaryRef gestalt
, SecKeyRef signingKey
, CFErrorRef
* error
) {
294 return SOSPeerInfoCreate_Internal(allocator
, gestalt
, NULL
, NULL
, NULL
, NULL
, NULL
, signingKey
, error
, ^(CFMutableDictionaryRef description
) {
295 CFDictionarySetValue(description
, sCloudIdentityKey
, kCFBooleanTrue
);
301 SOSPeerInfoRef
SOSPeerInfoCreateCopy(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFErrorRef
* error
) {
302 if(!toCopy
) return NULL
;
303 SOSPeerInfoRef pi
= CFTypeAllocate(SOSPeerInfo
, struct __OpaqueSOSPeerInfo
, allocator
);
305 pi
->description
= CFDictionaryCreateMutableCopy(allocator
, 0, toCopy
->description
);
306 pi
->signature
= CFDataCreateCopy(allocator
, toCopy
->signature
);
308 pi
->gestalt
= CFDictionaryCreateCopy(allocator
, toCopy
->gestalt
);
309 pi
->id
= CFStringCreateCopy(allocator
, toCopy
->id
);
311 pi
->version
= toCopy
->version
;
312 if(!SOSPeerInfoVersionHasV2Data(pi
)) SOSPeerInfoExpandV2Data(pi
, error
);
318 bool SOSPeerInfoVersionIsCurrent(SOSPeerInfoRef pi
) {
319 return pi
->version
>= PEERINFO_CURRENT_VERSION
;
322 bool SOSPeerInfoVersionHasV2Data(SOSPeerInfoRef pi
) {
323 return pi
->version
>= kSOSPeerV2BaseVersion
;
326 SOSPeerInfoRef
SOSPeerInfoCreateCurrentCopy(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
,
327 CFStringRef IDSID
, CFStringRef transportType
, CFBooleanRef preferIDS
, CFSetRef enabledViews
,
328 SecKeyRef signingKey
, CFErrorRef
* error
) {
330 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(allocator
, toCopy
, error
);
331 if(!SOSPeerInfoVersionHasV2Data(pi
)) SOSPeerInfoUpdateToV2(pi
, error
);
333 SOSPeerInfoSetSerialNumber(pi
);
336 SOSPeerInfoV2DictionarySetValue(pi
, sDeviceID
, IDSID
);
339 SOSPeerInfoV2DictionarySetValue(pi
, sTransportType
, transportType
);
342 SOSPeerInfoV2DictionarySetValue(pi
, sPreferIDS
, preferIDS
);
345 SOSPeerInfoV2DictionarySetValue(pi
, sViewsKey
, enabledViews
);
348 if(!SOSPeerInfoSign(signingKey
, pi
, error
)) {
356 static SOSPeerInfoRef
SOSPeerInfoCopyWithModification(CFAllocatorRef allocator
, SOSPeerInfoRef original
,
357 SecKeyRef signingKey
, CFErrorRef
*error
,
358 bool (^modification
)(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
)) {
360 SOSPeerInfoRef result
= NULL
;
361 SOSPeerInfoRef copy
= SOSPeerInfoCreateCopy(allocator
, original
, error
);
363 require_quiet(modification(copy
, error
), fail
);
365 require_quiet(SOSPeerInfoSign(signingKey
, copy
, error
), fail
);
367 CFTransferRetained(result
, copy
);
375 SOSPeerInfoRef
SOSPeerInfoCopyWithGestaltUpdate(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFDictionaryRef gestalt
, SecKeyRef signingKey
, CFErrorRef
* error
) {
376 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
377 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
378 if(!gestalt
|| !peerToModify
) return false;
379 CFRetainAssign(peerToModify
->gestalt
, gestalt
);
380 CFDictionarySetValue(peerToModify
->description
, sGestaltKey
, peerToModify
->gestalt
);
387 SOSPeerInfoRef
SOSPeerInfoCopyWithBackupKeyUpdate(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFDataRef backupKey
, SecKeyRef signingKey
, CFErrorRef
*error
) {
388 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
389 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
390 if (backupKey
!= NULL
)
391 SOSPeerInfoV2DictionarySetValue(peerToModify
, sBackupKeyKey
, backupKey
);
393 SOSPeerInfoV2DictionaryRemoveValue(peerToModify
, sBackupKeyKey
);
398 static CFDictionaryRef
SOSPeerInfoUpdateAndCopyRecord(SOSPeerInfoRef peer
, CFStringRef dsid
, CFDictionaryRef escrowRecord
){
400 CFMutableDictionaryRef existingEscrowRecords
= SOSPeerInfoCopyEscrowRecord(peer
);
402 if(escrowRecord
== NULL
&& existingEscrowRecords
!= NULL
)
404 CFDictionaryRemoveValue(existingEscrowRecords
, dsid
);
405 return existingEscrowRecords
;
408 if(existingEscrowRecords
== NULL
)
409 existingEscrowRecords
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
411 CFDictionarySetValue(existingEscrowRecords
, dsid
, escrowRecord
);
413 return existingEscrowRecords
;
417 SOSPeerInfoRef
SOSPeerInfoCopyWithEscrowRecordUpdate(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFStringRef dsid
, CFDictionaryRef escrowRecord
, SecKeyRef signingKey
, CFErrorRef
*error
) {
418 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
419 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
421 CFDictionaryRef updatedEscrowRecords
= SOSPeerInfoUpdateAndCopyRecord(peerToModify
, dsid
, escrowRecord
);
422 SOSPeerInfoV2DictionarySetValue(peerToModify
, sEscrowRecord
, updatedEscrowRecords
);
423 CFReleaseNull(updatedEscrowRecords
);
428 SOSPeerInfoRef
SOSPeerInfoCopyWithReplacedEscrowRecords(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFDictionaryRef escrowRecords
, SecKeyRef signingKey
, CFErrorRef
*error
) {
429 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
430 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
431 if(escrowRecords
!= NULL
)
432 SOSPeerInfoV2DictionarySetValue(peerToModify
, sEscrowRecord
, escrowRecords
);
438 CFDataRef
SOSPeerInfoCopyBackupKey(SOSPeerInfoRef peer
) {
439 return SOSPeerInfoV2DictionaryCopyData(peer
, sBackupKeyKey
);
442 CFMutableDictionaryRef
SOSPeerInfoCopyEscrowRecord(SOSPeerInfoRef peer
){
443 return SOSPeerInfoV2DictionaryCopyDictionary(peer
, sEscrowRecord
);
446 bool SOSPeerInfoHasBackupKey(SOSPeerInfoRef peer
) {
447 CFDataRef bk
= SOSPeerInfoCopyBackupKey(peer
);
448 bool success
= bk
!= NULL
;
453 SOSPeerInfoRef
SOSPeerInfoCopyWithViewsChange(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
,
454 SOSViewActionCode action
, CFStringRef viewname
, SOSViewResultCode
*retval
,
455 SecKeyRef signingKey
, CFErrorRef
* error
) {
456 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(allocator
, toCopy
, error
);
457 if(action
== kSOSCCViewEnable
) {
458 *retval
= SOSViewsEnable(pi
, viewname
, error
);
459 require((kSOSCCViewMember
== *retval
), exit
);
460 } else if(action
== kSOSCCViewDisable
) {
461 *retval
= SOSViewsDisable(pi
, viewname
, error
);
462 require((kSOSCCViewNotMember
== *retval
), exit
);
465 require_action_quiet(SOSPeerInfoSign(signingKey
, pi
, error
), exit
, *retval
= kSOSCCGeneralViewError
);
474 CFStringRef sPingKey
= CFSTR("Ping");
476 SOSPeerInfoRef
SOSPeerInfoCopyWithPing(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, SecKeyRef signingKey
, CFErrorRef
* error
) {
477 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(allocator
, toCopy
, error
);
478 CFDataRef ping
= CFDataCreateWithRandomBytes(8);
479 SOSPeerInfoV2DictionarySetValue(pi
, sPingKey
, ping
);
480 SecKeyRef pub_key
= SOSPeerInfoCopyPubKey(pi
);
481 pi
->id
= SOSCopyIDOfKey(pub_key
, error
);
482 require_quiet(pi
->id
, exit
);
483 require_action_quiet(SOSPeerInfoSign(signingKey
, pi
, error
), exit
, CFReleaseNull(pi
));
486 CFReleaseNull(pub_key
);
491 SOSViewResultCode
SOSPeerInfoViewStatus(SOSPeerInfoRef pi
, CFStringRef view
, CFErrorRef
*error
) {
492 return SOSViewsQuery(pi
, view
, error
);
496 SOSPeerInfoRef
SOSPeerInfoCopyWithSecurityPropertyChange(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
,
497 SOSSecurityPropertyActionCode action
, CFStringRef property
, SOSSecurityPropertyResultCode
*retval
,
498 SecKeyRef signingKey
, CFErrorRef
* error
) {
499 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(allocator
, toCopy
, error
);
500 if(action
== kSOSCCSecurityPropertyEnable
) {
501 *retval
= SOSSecurityPropertyEnable(pi
, property
, error
);
502 require((kSOSCCSecurityPropertyValid
== *retval
), exit
);
503 } else if(action
== kSOSCCSecurityPropertyDisable
) {
504 *retval
= SOSSecurityPropertyDisable(pi
, property
, error
);
505 require((kSOSCCSecurityPropertyNotValid
== *retval
), exit
);
508 require_action_quiet(SOSPeerInfoSign(signingKey
, pi
, error
), exit
, *retval
= kSOSCCGeneralViewError
);
516 SOSViewResultCode
SOSPeerInfoSecurityPropertyStatus(SOSPeerInfoRef pi
, CFStringRef property
, CFErrorRef
*error
) {
517 return SOSSecurityPropertyQuery(pi
, property
, error
);
522 static void SOSPeerInfoDestroy(CFTypeRef aObj
) {
523 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) aObj
;
526 CFReleaseNull(pi
->description
);
527 CFReleaseNull(pi
->signature
);
528 CFReleaseNull(pi
->gestalt
);
529 CFReleaseNull(pi
->id
);
530 CFReleaseNull(pi
->v2Dictionary
);
531 CFReleaseNull(pi
->secproperties
);
534 static Boolean
SOSPeerInfoCompare(CFTypeRef lhs
, CFTypeRef rhs
) {
535 SOSPeerInfoRef lpeer
= (SOSPeerInfoRef
) lhs
;
536 SOSPeerInfoRef rpeer
= (SOSPeerInfoRef
) rhs
;
537 if(!lpeer
|| !rpeer
) return false;
538 return CFEqualSafe(lpeer
->description
, rpeer
->description
) && CFEqualSafe(lpeer
->signature
, rpeer
->signature
);
542 CFComparisonResult
SOSPeerInfoCompareByID(const void *val1
, const void *val2
, void *context
) {
543 // The code below is necessary but not sufficient; not returning a CFComparisonResult
544 // It probably is OK to say that a NULL is < <non-NULL>
545 if (val1
== NULL
|| val2
== NULL
) {
546 ptrdiff_t dv
= val1
- val2
;
547 return dv
< 0 ? kCFCompareLessThan
: dv
== 0 ? kCFCompareEqualTo
: kCFCompareGreaterThan
;
550 CFStringRef v1
= SOSPeerInfoGetPeerID((SOSPeerInfoRef
) val1
);
551 CFStringRef v2
= SOSPeerInfoGetPeerID((SOSPeerInfoRef
) val2
);
552 if (v1
== NULL
|| v2
== NULL
) {
553 ptrdiff_t dv
= (const void *)v1
- (const void *)v2
;
554 return dv
< 0 ? kCFCompareLessThan
: dv
== 0 ? kCFCompareEqualTo
: kCFCompareGreaterThan
;
557 return CFStringCompare(v1
, v2
, 0);
560 static CFHashCode
SOSPeerInfoHash(CFTypeRef cf
) {
561 SOSPeerInfoRef peer
= (SOSPeerInfoRef
) cf
;
563 return CFHash(peer
->description
) ^ CFHash(peer
->signature
);
566 static CFStringRef
copyDescriptionWithFormatOptions(CFTypeRef aObj
, CFDictionaryRef formatOptions
){
568 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) aObj
;
569 CFStringRef description
= NULL
;
572 // Get the format options we care about:
573 CFNumberRef peerIDLengthNumber
= formatOptions
? CFDictionaryGetValue(formatOptions
, peerIDLengthKey
) : NULL
;
574 bool useSyncDFormat
= formatOptions
&& CFDictionaryContainsKey(formatOptions
, CFSTR("SyncD"));
576 // Calculate the truncated length
577 CFIndex truncatedLength
= CFStringGetLength(pi
->id
);
578 if (isNumber(peerIDLengthNumber
)) {
579 CFIndex peerIDMaximumLength
= truncatedLength
;
580 CFNumberGetValue(peerIDLengthNumber
, kCFNumberCFIndexType
, &peerIDMaximumLength
);
581 truncatedLength
= MIN(truncatedLength
, peerIDMaximumLength
);
583 CFStringRef displayPeerID
= CFStringCreateWithSubstring(kCFAllocatorDefault
, pi
->id
, CFRangeMake(0, truncatedLength
));
585 CFStringRef objectPrefix
= useSyncDFormat
? CFSTR("PI") :
586 CFStringCreateWithFormat(kCFAllocatorDefault
, formatOptions
, CFSTR("PeerInfo@%p"), pi
);
588 CFStringRef osVersion
= CFDictionaryGetValue(pi
->gestalt
, kPIOSVersionKey
);
590 CFStringRef transportType
= SOSPeerInfoV2DictionaryCopyString(pi
, sTransportType
);
591 CFStringRef deviceID
= SOSPeerInfoV2DictionaryCopyString(pi
, sDeviceID
);
593 description
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<%@: %s%s '%@' %@ %@ %@ %@ %@ %ld>"),
595 SOSPeerInfoIsRetirementTicket(pi
) ? "R" : "-",
596 SOSPeerInfoHasBackupKey(pi
) ? "B" : "-",
597 CFDictionaryGetValue(pi
->gestalt
, kPIUserDefinedDeviceNameKey
),
598 CFDictionaryGetValue(pi
->gestalt
, kPIDeviceModelNameKey
),
599 osVersion
? osVersion
: CFSTR("????"),
600 transportType
? transportType
: CFSTR("KVS"),
601 deviceID
? deviceID
: CFSTR(""),
605 CFReleaseNull(transportType
);
606 CFReleaseNull(deviceID
);
608 CFReleaseNull(objectPrefix
);
609 CFReleaseNull(displayPeerID
);
614 static CFStringRef
SOSPeerInfoCopyFormatDescription(CFTypeRef aObj
, CFDictionaryRef formatOptions
) {
616 CFStringRef description
= NULL
;
618 description
= copyDescriptionWithFormatOptions(aObj
, formatOptions
);
623 CFDictionaryRef
SOSPeerInfoCopyPeerGestalt(SOSPeerInfoRef pi
) {
624 CFRetain(pi
->gestalt
);
628 CFDictionaryRef
SOSPeerGetGestalt(SOSPeerInfoRef pi
){
632 CFStringRef
SOSPeerInfoGetPeerName(SOSPeerInfoRef peer
) {
633 return SOSPeerInfoLookupGestaltValue(peer
, kPIUserDefinedDeviceNameKey
);
636 CFStringRef
SOSPeerInfoGetPeerDeviceType(SOSPeerInfoRef peer
) {
637 return SOSPeerInfoLookupGestaltValue(peer
, kPIDeviceModelNameKey
);
640 CFIndex
SOSPeerInfoGetPeerProtocolVersion(SOSPeerInfoRef peer
) {
641 CFIndex version
= PEERINFO_CURRENT_VERSION
;
642 CFTypeRef val
= SOSPeerInfoLookupGestaltValue(peer
, kPIMessageProtocolVersionKey
);
643 if (val
&& CFGetTypeID(val
) == CFNumberGetTypeID())
644 CFNumberGetValue(val
, kCFNumberCFIndexType
, &version
);
648 CFTypeRef
SOSPeerInfoLookupGestaltValue(SOSPeerInfoRef pi
, CFStringRef key
) {
649 return CFDictionaryGetValue(pi
->gestalt
, key
);
652 CFStringRef
SOSPeerInfoGetPeerID(SOSPeerInfoRef pi
) {
653 return pi
? pi
->id
: NULL
;
656 bool SOSPeerInfoPeerIDEqual(SOSPeerInfoRef pi
, CFStringRef myPeerID
) {
657 return CFEqualSafe(myPeerID
, SOSPeerInfoGetPeerID(pi
));
660 CFIndex
SOSPeerInfoGetVersion(SOSPeerInfoRef pi
) {
664 bool SOSPeerInfoUpdateDigestWithPublicKeyBytes(SOSPeerInfoRef peer
, const struct ccdigest_info
*di
,
665 ccdigest_ctx_t ctx
, CFErrorRef
*error
) {
666 CFDataRef pubKeyBytes
= CFDictionaryGetValue(peer
->description
, sPublicKeyKey
);
669 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, NULL
, error
, NULL
, CFSTR("Digest failed – no public key"));
673 ccdigest_update(di
, ctx
, CFDataGetLength(pubKeyBytes
), CFDataGetBytePtr(pubKeyBytes
));
678 bool SOSPeerInfoUpdateDigestWithDescription(SOSPeerInfoRef peer
, const struct ccdigest_info
*di
,
679 ccdigest_ctx_t ctx
, CFErrorRef
*error
) {
680 if(SOSPeerInfoVersionHasV2Data(peer
)) SOSPeerInfoPackV2Data(peer
);
681 size_t description_size
= der_sizeof_plist(peer
->description
, error
);
682 uint8_t data_begin
[description_size
];
683 uint8_t *data_end
= data_begin
+ description_size
;
684 uint8_t *encoded
= der_encode_plist(peer
->description
, error
, data_begin
, data_end
);
687 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, NULL
, error
, NULL
, CFSTR("Description encode failed"));
691 ccdigest_update(di
, ctx
, description_size
, data_begin
);
697 static CFDataRef
sosCreateDate() {
698 CFDateRef now
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
699 size_t bufsiz
= der_sizeof_date(now
, NULL
);
701 der_encode_date(now
, NULL
, buf
, buf
+bufsiz
);
703 return CFDataCreate(NULL
, buf
, bufsiz
);
706 static CFDateRef
sosCreateCFDate(CFDataRef sosdate
) {
708 der_decode_date(NULL
, 0, &date
, NULL
, CFDataGetBytePtr(sosdate
),
709 CFDataGetBytePtr(sosdate
) + CFDataGetLength(sosdate
));
713 static bool sospeer_application_hash(SOSPeerInfoRef pi
, const struct ccdigest_info
*di
, uint8_t *hbuf
) {
714 CFDataRef appdate
= CFDictionaryGetValue(pi
->description
, sApplicationDate
);
715 if(!appdate
) return false;
716 ccdigest_di_decl(di
, ctx
);
717 ccdigest_init(di
, ctx
);
718 ccdigest_update(di
, ctx
, CFDataGetLength(appdate
), CFDataGetBytePtr(appdate
));
719 if (!SOSPeerInfoUpdateDigestWithPublicKeyBytes(pi
, di
, ctx
, NULL
)) return false;
720 ccdigest_final(di
, ctx
, hbuf
);
724 SOSPeerInfoRef
SOSPeerInfoCopyAsApplication(SOSPeerInfoRef original
, SecKeyRef userkey
, SecKeyRef peerkey
, CFErrorRef
*error
) {
725 SOSPeerInfoRef result
= NULL
;
726 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(kCFAllocatorDefault
, original
, error
);
728 const struct ccdigest_info
*di
= ccsha256_di();
729 uint8_t hbuf
[di
->output_size
];
730 CFDataRef usersig
= NULL
;
732 CFDataRef creationDate
= sosCreateDate();
733 CFDictionarySetValue(pi
->description
, sApplicationDate
, creationDate
);
734 CFReleaseNull(creationDate
);
736 // Create User Application Signature
737 require_action_quiet(sospeer_application_hash(pi
, di
, hbuf
), fail
,
738 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Failed to create hash for peer applicant"), NULL
, error
));
740 usersig
= sosSignHash(userkey
, di
, hbuf
);
741 require_action_quiet(usersig
, fail
,
742 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Failed to sign public key hash for peer"), NULL
, error
));
744 CFDictionarySetValue(pi
->description
, sApplicationUsig
, usersig
);
746 require_quiet(SOSPeerInfoSign(peerkey
, pi
, error
), fail
);
752 CFReleaseNull(usersig
);
757 bool SOSPeerInfoApplicationVerify(SOSPeerInfoRef pi
, SecKeyRef userkey
, CFErrorRef
*error
) {
758 const struct ccdigest_info
*di
= ccsha256_di();
759 uint8_t hbuf
[di
->output_size
];
762 CFDataRef usig
= CFDictionaryGetValue(pi
->description
, sApplicationUsig
);
763 require_action_quiet(usig
, exit
,
764 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Peer is not an applicant"), NULL
, error
));
765 // Verify User Application Signature
766 require_action_quiet(sospeer_application_hash(pi
, di
, hbuf
), exit
,
767 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Failed to create hash for peer applicant"), NULL
, error
));
768 require_action_quiet(sosVerifyHash(userkey
, di
, hbuf
, usig
), exit
,
769 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("user signature of public key hash fails to verify"), NULL
, error
));
771 result
= SOSPeerInfoVerify(pi
, error
);
778 static CF_RETURNS_RETAINED CFDateRef
sosPeerInfoGetDate(SOSPeerInfoRef pi
, CFStringRef entry
) {
780 CFDataRef sosdate
= CFDictionaryGetValue(pi
->description
, entry
);
781 if(!sosdate
) return NULL
;
782 CFDateRef date
= sosCreateCFDate(sosdate
);
787 CF_RETURNS_RETAINED CFDateRef
SOSPeerInfoGetApplicationDate(SOSPeerInfoRef pi
) {
788 return sosPeerInfoGetDate(pi
, sApplicationDate
);
791 CF_RETURNS_RETAINED CFDateRef
SOSPeerInfoGetRetirementDate(SOSPeerInfoRef pi
) {
792 return sosPeerInfoGetDate(pi
, sRetirementDate
);
801 CFStringRef
SOSPeerGestaltGetName(CFDictionaryRef gestalt
) {
802 CFStringRef name
= SOSPeerGestaltGetAnswer(gestalt
, kPIUserDefinedDeviceNameKey
);
803 return isString(name
) ? name
: NULL
;
806 CFTypeRef
SOSPeerGestaltGetAnswer(CFDictionaryRef gestalt
, CFStringRef question
) {
807 return gestalt
? CFDictionaryGetValue(gestalt
, question
) : NULL
;
815 SOSPeerInfoRef
SOSPeerInfoCreateRetirementTicket(CFAllocatorRef allocator
, SecKeyRef privKey
, SOSPeerInfoRef peer
, CFErrorRef
*error
) {
817 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(allocator
, peer
, error
);
821 // Fill out Resignation Date
822 CFDataRef resignationDate
= sosCreateDate();
823 CFDictionaryAddValue(pi
->description
, sRetirementDate
, resignationDate
);
824 CFReleaseNull(resignationDate
);
826 require(SOSPeerInfoSign(privKey
, pi
, error
), fail
);
835 CFStringRef
SOSPeerInfoInspectRetirementTicket(SOSPeerInfoRef pi
, CFErrorRef
*error
) {
836 CFStringRef retval
= NULL
;
837 CFDateRef now
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
838 CFDateRef retirement
= NULL
;
840 require_quiet(SOSPeerInfoVerify(pi
, error
), err
);
842 retirement
= sosCreateCFDate(CFDictionaryGetValue(pi
->description
, sRetirementDate
));
844 require_action_quiet(retirement
, err
,
845 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Peer is not retired"), NULL
, error
));
847 require_action_quiet(CFDateCompare(now
, retirement
, NULL
) == kCFCompareGreaterThan
, err
,
848 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Retirement date is after current date"), NULL
, error
));
850 retval
= SOSPeerInfoGetPeerID(pi
);
854 CFReleaseNull(retirement
);
858 bool SOSPeerInfoRetireRetirementTicket(size_t max_seconds
, SOSPeerInfoRef pi
) {
859 CFDateRef now
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
860 CFDateRef retirement
= sosCreateCFDate(CFDictionaryGetValue(pi
->description
, sRetirementDate
));
861 CFTimeInterval timediff
= CFDateGetTimeIntervalSinceDate(now
, retirement
); // diff in seconds
863 CFReleaseNull(retirement
);
864 if(timediff
> (max_seconds
)) return true;
868 bool SOSPeerInfoIsRetirementTicket(SOSPeerInfoRef pi
) {
869 CFDataRef flag
= CFDictionaryGetValue(pi
->description
, sRetirementDate
);
873 bool SOSPeerInfoIsCloudIdentity(SOSPeerInfoRef pi
) {
874 CFTypeRef value
= CFDictionaryGetValue(pi
->description
, sCloudIdentityKey
);
875 return CFEqualSafe(value
, kCFBooleanTrue
);
878 SOSPeerInfoRef
SOSPeerInfoUpgradeSignatures(CFAllocatorRef allocator
, SecKeyRef privKey
, SecKeyRef peerKey
, SOSPeerInfoRef peer
, CFErrorRef
*error
) {
879 SecKeyRef pubKey
= SecKeyCreatePublicFromPrivate(privKey
);
880 SOSPeerInfoRef retval
= NULL
;
882 retval
= SOSPeerInfoCopyAsApplication(peer
, privKey
, peerKey
, error
);
883 CFReleaseNull(pubKey
);
887 CFBooleanRef
SOSPeerInfoCopyIDSPreference(SOSPeerInfoRef peer
){
888 CFBooleanRef preference
= (CFBooleanRef
)SOSPeerInfoV2DictionaryCopyBoolean(peer
, sPreferIDS
);
889 return (preference
? preference
: CFRetain(kCFBooleanFalse
));
893 SOSPeerInfoRef
SOSPeerInfoSetIDSPreference(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFBooleanRef preference
, SecKeyRef signingKey
, CFErrorRef
*error
){
894 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
895 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
896 SOSPeerInfoV2DictionarySetValue(peerToModify
, sPreferIDS
, preference
);
901 CFStringRef
SOSPeerInfoCopyTransportType(SOSPeerInfoRef peer
){
902 CFStringRef transportType
= (CFStringRef
)SOSPeerInfoV2DictionaryCopyString(peer
, sTransportType
);
903 return (transportType
? transportType
: CFRetain(SOSTransportMessageTypeKVS
));
906 SOSPeerInfoRef
SOSPeerInfoSetTransportType(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFStringRef transportType
, SecKeyRef signingKey
, CFErrorRef
*error
){
908 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
909 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
910 SOSPeerInfoV2DictionarySetValue(peerToModify
, sTransportType
, transportType
);
915 bool SOSPeerInfoHasDeviceID(SOSPeerInfoRef peer
) {
916 return SOSPeerInfoV2DictionaryHasString(peer
, sDeviceID
);
919 CFStringRef
SOSPeerInfoCopyDeviceID(SOSPeerInfoRef peer
){
920 return (CFStringRef
)SOSPeerInfoV2DictionaryCopyString(peer
, sDeviceID
);
923 SOSPeerInfoRef
SOSPeerInfoSetDeviceID(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFStringRef IDS
, SecKeyRef signingKey
, CFErrorRef
*error
){
925 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
926 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
927 SOSPeerInfoV2DictionarySetValue(peerToModify
, sDeviceID
, IDS
);
932 bool SOSPeerInfoShouldUseIDSTransport(SOSPeerInfoRef myPeer
, SOSPeerInfoRef theirPeer
){
934 CFBooleanRef myPreference
= SOSPeerInfoCopyIDSPreference(myPeer
);
935 CFStringRef myTransportType
= SOSPeerInfoCopyTransportType(myPeer
);
937 CFBooleanRef theirPreference
= SOSPeerInfoCopyIDSPreference(theirPeer
);
938 CFStringRef theirTransportType
= SOSPeerInfoCopyTransportType(theirPeer
);
940 bool success
= false;
941 //If I'm a galarch KVS + peer is KVS and ids only is true for both == KVS
942 if((CFStringCompare(myTransportType
, SOSTransportMessageTypeKVS
, 0) == 0 && CFStringCompare(theirTransportType
, SOSTransportMessageTypeKVS
, 0) == 0)&& (myPreference
== kCFBooleanTrue
&& theirPreference
== kCFBooleanTrue
))
945 //If transport is IDS Galarch +1 (Pref is true) and peer is KVS (ids is true) == IDS
946 else if ((CFStringCompare(myTransportType
, SOSTransportMessageTypeIDS
, 0) == 0 && CFStringCompare(theirTransportType
, SOSTransportMessageTypeKVS
, 0) == 0) && (myPreference
== kCFBooleanTrue
&& theirPreference
== kCFBooleanTrue
))
949 else if ((CFStringCompare(theirTransportType
, SOSTransportMessageTypeIDS
, 0) == 0 && CFStringCompare(myTransportType
, SOSTransportMessageTypeKVS
, 0) == 0) && (theirPreference
== kCFBooleanTrue
&& myPreference
== kCFBooleanTrue
))
952 //If transport is IDS Galarch +1 and peer is IDS Galarch +1 (prefer IDS is true) == IDS
953 else if ((CFStringCompare(myTransportType
, SOSTransportMessageTypeIDS
, 0) == 0 && CFStringCompare(theirTransportType
, SOSTransportMessageTypeIDS
, 0) == 0))
956 //If KVS and KVS prefer IDS is false (set to false or doesn't exist) == KVS
957 else if ((CFStringCompare(myTransportType
, SOSTransportMessageTypeKVS
, 0) == 0 && CFStringCompare(theirTransportType
, SOSTransportMessageTypeKVS
, 0) == 0) && (myPreference
== kCFBooleanFalse
&& theirPreference
== kCFBooleanFalse
))
963 CFReleaseSafe(myPreference
);
964 CFReleaseSafe(myTransportType
);
965 CFReleaseSafe(theirPreference
);
966 CFReleaseSafe(theirTransportType
);