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 description_modifier(pi
->description
);
244 pi
->id
= SOSCopyIDOfKey(publicKey
, error
);
245 CFReleaseNull(publicKey
);
247 require_quiet(pi
->id
, exit
);
249 // ================ V2 Additions Start
251 if(!SOSPeerInfoUpdateToV2(pi
, error
)) {
256 // V2DictionarySetValue handles NULL as remove
257 SOSPeerInfoV2DictionarySetValue(pi
, sBackupKeyKey
, backup_key
);
258 SOSPeerInfoV2DictionarySetValue(pi
, sDeviceID
, IDSID
);
259 SOSPeerInfoV2DictionarySetValue(pi
, sTransportType
, transportType
);
260 SOSPeerInfoV2DictionarySetValue(pi
, sPreferIDS
, preferIDS
);
262 SOSPeerInfoV2DictionarySetValue(pi
, sViewsKey
, enabledViews
);
264 // ================ V2 Additions End
266 if (!SOSPeerInfoSign(signingKey
, pi
, error
)) {
272 CFReleaseNull(versionNumber
);
273 CFReleaseNull(publicBytes
);
277 SOSPeerInfoRef
SOSPeerInfoCreate(CFAllocatorRef allocator
, CFDictionaryRef gestalt
, CFDataRef backup_key
, SecKeyRef signingKey
, CFErrorRef
* error
) {
278 return SOSPeerInfoCreate_Internal(allocator
, gestalt
, backup_key
, NULL
, NULL
, NULL
, NULL
, signingKey
, error
, ^(CFMutableDictionaryRef description
) {});
281 SOSPeerInfoRef
SOSPeerInfoCreateWithTransportAndViews(CFAllocatorRef allocator
, CFDictionaryRef gestalt
, CFDataRef backup_key
,
282 CFStringRef IDSID
, CFStringRef transportType
, CFBooleanRef preferIDS
,
283 CFSetRef enabledViews
,
284 SecKeyRef signingKey
, CFErrorRef
* error
)
286 return SOSPeerInfoCreate_Internal(allocator
, gestalt
, backup_key
, IDSID
, transportType
, preferIDS
, enabledViews
, signingKey
, error
, ^(CFMutableDictionaryRef description
) {});
290 SOSPeerInfoRef
SOSPeerInfoCreateCloudIdentity(CFAllocatorRef allocator
, CFDictionaryRef gestalt
, SecKeyRef signingKey
, CFErrorRef
* error
) {
291 return SOSPeerInfoCreate_Internal(allocator
, gestalt
, NULL
, NULL
, NULL
, NULL
, NULL
, signingKey
, error
, ^(CFMutableDictionaryRef description
) {
292 CFDictionarySetValue(description
, sCloudIdentityKey
, kCFBooleanTrue
);
298 SOSPeerInfoRef
SOSPeerInfoCreateCopy(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFErrorRef
* error
) {
299 if(!toCopy
) return NULL
;
300 SOSPeerInfoRef pi
= CFTypeAllocate(SOSPeerInfo
, struct __OpaqueSOSPeerInfo
, allocator
);
302 pi
->description
= CFDictionaryCreateMutableCopy(allocator
, 0, toCopy
->description
);
303 pi
->signature
= CFDataCreateCopy(allocator
, toCopy
->signature
);
305 pi
->gestalt
= CFDictionaryCreateCopy(allocator
, toCopy
->gestalt
);
306 pi
->id
= CFStringCreateCopy(allocator
, toCopy
->id
);
308 pi
->version
= toCopy
->version
;
309 if(!SOSPeerInfoVersionHasV2Data(pi
)) SOSPeerInfoExpandV2Data(pi
, error
);
315 bool SOSPeerInfoVersionIsCurrent(SOSPeerInfoRef pi
) {
316 return pi
->version
>= PEERINFO_CURRENT_VERSION
;
319 bool SOSPeerInfoVersionHasV2Data(SOSPeerInfoRef pi
) {
320 return pi
->version
>= kSOSPeerV2BaseVersion
;
323 SOSPeerInfoRef
SOSPeerInfoCreateCurrentCopy(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
,
324 CFStringRef IDSID
, CFStringRef transportType
, CFBooleanRef preferIDS
, CFSetRef enabledViews
,
325 SecKeyRef signingKey
, CFErrorRef
* error
) {
327 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(allocator
, toCopy
, error
);
328 if(!SOSPeerInfoVersionHasV2Data(pi
)) SOSPeerInfoUpdateToV2(pi
, error
);
330 SOSPeerInfoSetSerialNumber(pi
);
333 SOSPeerInfoV2DictionarySetValue(pi
, sDeviceID
, IDSID
);
336 SOSPeerInfoV2DictionarySetValue(pi
, sTransportType
, transportType
);
339 SOSPeerInfoV2DictionarySetValue(pi
, sPreferIDS
, preferIDS
);
342 SOSPeerInfoV2DictionarySetValue(pi
, sViewsKey
, enabledViews
);
345 if(!SOSPeerInfoSign(signingKey
, pi
, error
)) {
353 static SOSPeerInfoRef
SOSPeerInfoCopyWithModification(CFAllocatorRef allocator
, SOSPeerInfoRef original
,
354 SecKeyRef signingKey
, CFErrorRef
*error
,
355 bool (^modification
)(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
)) {
357 SOSPeerInfoRef result
= NULL
;
358 SOSPeerInfoRef copy
= SOSPeerInfoCreateCopy(allocator
, original
, error
);
360 require_quiet(modification(copy
, error
), fail
);
362 require_quiet(SOSPeerInfoSign(signingKey
, copy
, error
), fail
);
364 CFTransferRetained(result
, copy
);
372 SOSPeerInfoRef
SOSPeerInfoCopyWithGestaltUpdate(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFDictionaryRef gestalt
, SecKeyRef signingKey
, CFErrorRef
* error
) {
373 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
374 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
375 if(!gestalt
|| !peerToModify
) return false;
376 CFRetainAssign(peerToModify
->gestalt
, gestalt
);
377 CFDictionarySetValue(peerToModify
->description
, sGestaltKey
, peerToModify
->gestalt
);
384 SOSPeerInfoRef
SOSPeerInfoCopyWithBackupKeyUpdate(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFDataRef backupKey
, SecKeyRef signingKey
, CFErrorRef
*error
) {
385 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
386 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
387 if (backupKey
!= NULL
)
388 SOSPeerInfoV2DictionarySetValue(peerToModify
, sBackupKeyKey
, backupKey
);
390 SOSPeerInfoV2DictionaryRemoveValue(peerToModify
, sBackupKeyKey
);
395 static CFDictionaryRef
SOSPeerInfoUpdateAndCopyRecord(SOSPeerInfoRef peer
, CFStringRef dsid
, CFDictionaryRef escrowRecord
){
397 CFMutableDictionaryRef existingEscrowRecords
= SOSPeerInfoCopyEscrowRecord(peer
);
399 if(escrowRecord
== NULL
&& existingEscrowRecords
!= NULL
)
401 CFDictionaryRemoveValue(existingEscrowRecords
, dsid
);
402 return existingEscrowRecords
;
405 if(existingEscrowRecords
== NULL
)
406 existingEscrowRecords
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
408 CFDictionarySetValue(existingEscrowRecords
, dsid
, escrowRecord
);
410 return existingEscrowRecords
;
414 SOSPeerInfoRef
SOSPeerInfoCopyWithEscrowRecordUpdate(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFStringRef dsid
, CFDictionaryRef escrowRecord
, SecKeyRef signingKey
, CFErrorRef
*error
) {
415 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
416 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
418 CFDictionaryRef updatedEscrowRecords
= SOSPeerInfoUpdateAndCopyRecord(peerToModify
, dsid
, escrowRecord
);
419 SOSPeerInfoV2DictionarySetValue(peerToModify
, sEscrowRecord
, updatedEscrowRecords
);
420 CFReleaseNull(updatedEscrowRecords
);
425 SOSPeerInfoRef
SOSPeerInfoCopyWithReplacedEscrowRecords(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFDictionaryRef escrowRecords
, SecKeyRef signingKey
, CFErrorRef
*error
) {
426 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
427 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
428 if(escrowRecords
!= NULL
)
429 SOSPeerInfoV2DictionarySetValue(peerToModify
, sEscrowRecord
, escrowRecords
);
435 CFDataRef
SOSPeerInfoCopyBackupKey(SOSPeerInfoRef peer
) {
436 return SOSPeerInfoV2DictionaryCopyData(peer
, sBackupKeyKey
);
439 CFMutableDictionaryRef
SOSPeerInfoCopyEscrowRecord(SOSPeerInfoRef peer
){
440 return SOSPeerInfoV2DictionaryCopyDictionary(peer
, sEscrowRecord
);
443 bool SOSPeerInfoHasBackupKey(SOSPeerInfoRef peer
) {
444 CFDataRef bk
= SOSPeerInfoCopyBackupKey(peer
);
445 bool success
= bk
!= NULL
;
450 SOSPeerInfoRef
SOSPeerInfoCopyWithViewsChange(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
,
451 SOSViewActionCode action
, CFStringRef viewname
, SOSViewResultCode
*retval
,
452 SecKeyRef signingKey
, CFErrorRef
* error
) {
453 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(allocator
, toCopy
, error
);
454 if(action
== kSOSCCViewEnable
) {
455 *retval
= SOSViewsEnable(pi
, viewname
, error
);
456 require((kSOSCCViewMember
== *retval
), exit
);
457 } else if(action
== kSOSCCViewDisable
) {
458 *retval
= SOSViewsDisable(pi
, viewname
, error
);
459 require((kSOSCCViewNotMember
== *retval
), exit
);
462 require_action_quiet(SOSPeerInfoSign(signingKey
, pi
, error
), exit
, *retval
= kSOSCCGeneralViewError
);
471 CFStringRef sPingKey
= CFSTR("Ping");
473 SOSPeerInfoRef
SOSPeerInfoCopyWithPing(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, SecKeyRef signingKey
, CFErrorRef
* error
) {
474 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(allocator
, toCopy
, error
);
475 CFDataRef ping
= CFDataCreateWithRandomBytes(8);
476 SOSPeerInfoV2DictionarySetValue(pi
, sPingKey
, ping
);
477 SecKeyRef pub_key
= SOSPeerInfoCopyPubKey(pi
);
478 pi
->id
= SOSCopyIDOfKey(pub_key
, error
);
479 require_quiet(pi
->id
, exit
);
480 require_action_quiet(SOSPeerInfoSign(signingKey
, pi
, error
), exit
, CFReleaseNull(pi
));
483 CFReleaseNull(pub_key
);
488 SOSViewResultCode
SOSPeerInfoViewStatus(SOSPeerInfoRef pi
, CFStringRef view
, CFErrorRef
*error
) {
489 return SOSViewsQuery(pi
, view
, error
);
493 SOSPeerInfoRef
SOSPeerInfoCopyWithSecurityPropertyChange(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
,
494 SOSSecurityPropertyActionCode action
, CFStringRef property
, SOSSecurityPropertyResultCode
*retval
,
495 SecKeyRef signingKey
, CFErrorRef
* error
) {
496 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(allocator
, toCopy
, error
);
497 if(action
== kSOSCCSecurityPropertyEnable
) {
498 *retval
= SOSSecurityPropertyEnable(pi
, property
, error
);
499 require((kSOSCCSecurityPropertyValid
== *retval
), exit
);
500 } else if(action
== kSOSCCSecurityPropertyDisable
) {
501 *retval
= SOSSecurityPropertyDisable(pi
, property
, error
);
502 require((kSOSCCSecurityPropertyNotValid
== *retval
), exit
);
505 require_action_quiet(SOSPeerInfoSign(signingKey
, pi
, error
), exit
, *retval
= kSOSCCGeneralViewError
);
513 SOSViewResultCode
SOSPeerInfoSecurityPropertyStatus(SOSPeerInfoRef pi
, CFStringRef property
, CFErrorRef
*error
) {
514 return SOSSecurityPropertyQuery(pi
, property
, error
);
519 static void SOSPeerInfoDestroy(CFTypeRef aObj
) {
520 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) aObj
;
523 CFReleaseNull(pi
->description
);
524 CFReleaseNull(pi
->signature
);
525 CFReleaseNull(pi
->gestalt
);
526 CFReleaseNull(pi
->id
);
527 CFReleaseNull(pi
->v2Dictionary
);
528 CFReleaseNull(pi
->secproperties
);
531 static Boolean
SOSPeerInfoCompare(CFTypeRef lhs
, CFTypeRef rhs
) {
532 SOSPeerInfoRef lpeer
= (SOSPeerInfoRef
) lhs
;
533 SOSPeerInfoRef rpeer
= (SOSPeerInfoRef
) rhs
;
534 if(!lpeer
|| !rpeer
) return false;
535 return CFEqualSafe(lpeer
->description
, rpeer
->description
) && CFEqualSafe(lpeer
->signature
, rpeer
->signature
);
539 CFComparisonResult
SOSPeerInfoCompareByID(const void *val1
, const void *val2
, void *context
) {
540 // The code below is necessary but not sufficient; not returning a CFComparisonResult
541 // It probably is OK to say that a NULL is < <non-NULL>
542 if (val1
== NULL
|| val2
== NULL
) {
543 ptrdiff_t dv
= val1
- val2
;
544 return dv
< 0 ? kCFCompareLessThan
: dv
== 0 ? kCFCompareEqualTo
: kCFCompareGreaterThan
;
547 CFStringRef v1
= SOSPeerInfoGetPeerID((SOSPeerInfoRef
) val1
);
548 CFStringRef v2
= SOSPeerInfoGetPeerID((SOSPeerInfoRef
) val2
);
549 if (v1
== NULL
|| v2
== NULL
) {
550 ptrdiff_t dv
= (const void *)v1
- (const void *)v2
;
551 return dv
< 0 ? kCFCompareLessThan
: dv
== 0 ? kCFCompareEqualTo
: kCFCompareGreaterThan
;
554 return CFStringCompare(v1
, v2
, 0);
557 static CFHashCode
SOSPeerInfoHash(CFTypeRef cf
) {
558 SOSPeerInfoRef peer
= (SOSPeerInfoRef
) cf
;
560 return CFHash(peer
->description
) ^ CFHash(peer
->signature
);
563 static CFStringRef
copyDescriptionWithFormatOptions(CFTypeRef aObj
, CFDictionaryRef formatOptions
){
565 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) aObj
;
566 CFStringRef description
= NULL
;
569 // Get the format options we care about:
570 CFNumberRef peerIDLengthNumber
= formatOptions
? CFDictionaryGetValue(formatOptions
, peerIDLengthKey
) : NULL
;
571 bool useSyncDFormat
= formatOptions
&& CFDictionaryContainsKey(formatOptions
, CFSTR("SyncD"));
573 // Calculate the truncated length
574 CFIndex truncatedLength
= CFStringGetLength(pi
->id
);
575 if (isNumber(peerIDLengthNumber
)) {
576 CFIndex peerIDMaximumLength
= truncatedLength
;
577 CFNumberGetValue(peerIDLengthNumber
, kCFNumberCFIndexType
, &peerIDMaximumLength
);
578 truncatedLength
= MIN(truncatedLength
, peerIDMaximumLength
);
580 CFStringRef displayPeerID
= CFStringCreateWithSubstring(kCFAllocatorDefault
, pi
->id
, CFRangeMake(0, truncatedLength
));
582 CFStringRef objectPrefix
= useSyncDFormat
? CFSTR("PI") :
583 CFStringCreateWithFormat(kCFAllocatorDefault
, formatOptions
, CFSTR("PeerInfo@%p"), pi
);
585 CFStringRef osVersion
= CFDictionaryGetValue(pi
->gestalt
, kPIOSVersionKey
);
587 CFStringRef transportType
= SOSPeerInfoV2DictionaryCopyString(pi
, sTransportType
);
588 CFStringRef deviceID
= SOSPeerInfoV2DictionaryCopyString(pi
, sDeviceID
);
590 description
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<%@: %s%s '%@' %@ %@ %@ %@ %@ %ld>"),
592 SOSPeerInfoIsRetirementTicket(pi
) ? "R" : "-",
593 SOSPeerInfoHasBackupKey(pi
) ? "B" : "-",
594 CFDictionaryGetValue(pi
->gestalt
, kPIUserDefinedDeviceNameKey
),
595 CFDictionaryGetValue(pi
->gestalt
, kPIDeviceModelNameKey
),
596 osVersion
? osVersion
: CFSTR("????"),
597 transportType
? transportType
: CFSTR("KVS"),
598 deviceID
? deviceID
: CFSTR(""),
602 CFReleaseNull(transportType
);
603 CFReleaseNull(deviceID
);
605 CFReleaseNull(objectPrefix
);
606 CFReleaseNull(displayPeerID
);
611 static CFStringRef
SOSPeerInfoCopyFormatDescription(CFTypeRef aObj
, CFDictionaryRef formatOptions
) {
613 CFStringRef description
= NULL
;
615 description
= copyDescriptionWithFormatOptions(aObj
, formatOptions
);
620 CFDictionaryRef
SOSPeerInfoCopyPeerGestalt(SOSPeerInfoRef pi
) {
621 CFRetain(pi
->gestalt
);
625 CFDictionaryRef
SOSPeerGetGestalt(SOSPeerInfoRef pi
){
629 CFStringRef
SOSPeerInfoGetPeerName(SOSPeerInfoRef peer
) {
630 return SOSPeerInfoLookupGestaltValue(peer
, kPIUserDefinedDeviceNameKey
);
633 CFStringRef
SOSPeerInfoGetPeerDeviceType(SOSPeerInfoRef peer
) {
634 return SOSPeerInfoLookupGestaltValue(peer
, kPIDeviceModelNameKey
);
637 CFIndex
SOSPeerInfoGetPeerProtocolVersion(SOSPeerInfoRef peer
) {
638 CFIndex version
= PEERINFO_CURRENT_VERSION
;
639 CFTypeRef val
= SOSPeerInfoLookupGestaltValue(peer
, kPIMessageProtocolVersionKey
);
640 if (val
&& CFGetTypeID(val
) == CFNumberGetTypeID())
641 CFNumberGetValue(val
, kCFNumberCFIndexType
, &version
);
645 CFTypeRef
SOSPeerInfoLookupGestaltValue(SOSPeerInfoRef pi
, CFStringRef key
) {
646 return CFDictionaryGetValue(pi
->gestalt
, key
);
649 CFStringRef
SOSPeerInfoGetPeerID(SOSPeerInfoRef pi
) {
650 return pi
? pi
->id
: NULL
;
653 bool SOSPeerInfoPeerIDEqual(SOSPeerInfoRef pi
, CFStringRef myPeerID
) {
654 return CFEqualSafe(myPeerID
, SOSPeerInfoGetPeerID(pi
));
657 CFIndex
SOSPeerInfoGetVersion(SOSPeerInfoRef pi
) {
661 bool SOSPeerInfoUpdateDigestWithPublicKeyBytes(SOSPeerInfoRef peer
, const struct ccdigest_info
*di
,
662 ccdigest_ctx_t ctx
, CFErrorRef
*error
) {
663 CFDataRef pubKeyBytes
= CFDictionaryGetValue(peer
->description
, sPublicKeyKey
);
666 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, NULL
, error
, NULL
, CFSTR("Digest failed – no public key"));
670 ccdigest_update(di
, ctx
, CFDataGetLength(pubKeyBytes
), CFDataGetBytePtr(pubKeyBytes
));
675 bool SOSPeerInfoUpdateDigestWithDescription(SOSPeerInfoRef peer
, const struct ccdigest_info
*di
,
676 ccdigest_ctx_t ctx
, CFErrorRef
*error
) {
677 if(SOSPeerInfoVersionHasV2Data(peer
)) SOSPeerInfoPackV2Data(peer
);
678 size_t description_size
= der_sizeof_plist(peer
->description
, error
);
679 uint8_t data_begin
[description_size
];
680 uint8_t *data_end
= data_begin
+ description_size
;
681 uint8_t *encoded
= der_encode_plist(peer
->description
, error
, data_begin
, data_end
);
684 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, NULL
, error
, NULL
, CFSTR("Description encode failed"));
688 ccdigest_update(di
, ctx
, description_size
, data_begin
);
694 static CFDataRef
sosCreateDate() {
695 CFDateRef now
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
696 size_t bufsiz
= der_sizeof_date(now
, NULL
);
698 der_encode_date(now
, NULL
, buf
, buf
+bufsiz
);
700 return CFDataCreate(NULL
, buf
, bufsiz
);
703 static CFDateRef
sosCreateCFDate(CFDataRef sosdate
) {
705 der_decode_date(NULL
, 0, &date
, NULL
, CFDataGetBytePtr(sosdate
),
706 CFDataGetBytePtr(sosdate
) + CFDataGetLength(sosdate
));
710 static bool sospeer_application_hash(SOSPeerInfoRef pi
, const struct ccdigest_info
*di
, uint8_t *hbuf
) {
711 CFDataRef appdate
= CFDictionaryGetValue(pi
->description
, sApplicationDate
);
712 if(!appdate
) return false;
713 ccdigest_di_decl(di
, ctx
);
714 ccdigest_init(di
, ctx
);
715 ccdigest_update(di
, ctx
, CFDataGetLength(appdate
), CFDataGetBytePtr(appdate
));
716 if (!SOSPeerInfoUpdateDigestWithPublicKeyBytes(pi
, di
, ctx
, NULL
)) return false;
717 ccdigest_final(di
, ctx
, hbuf
);
721 SOSPeerInfoRef
SOSPeerInfoCopyAsApplication(SOSPeerInfoRef original
, SecKeyRef userkey
, SecKeyRef peerkey
, CFErrorRef
*error
) {
722 SOSPeerInfoRef result
= NULL
;
723 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(kCFAllocatorDefault
, original
, error
);
725 const struct ccdigest_info
*di
= ccsha256_di();
726 uint8_t hbuf
[di
->output_size
];
727 CFDataRef usersig
= NULL
;
729 CFDataRef creationDate
= sosCreateDate();
730 CFDictionarySetValue(pi
->description
, sApplicationDate
, creationDate
);
731 CFReleaseNull(creationDate
);
733 // Create User Application Signature
734 require_action_quiet(sospeer_application_hash(pi
, di
, hbuf
), fail
,
735 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Failed to create hash for peer applicant"), NULL
, error
));
737 usersig
= sosSignHash(userkey
, di
, hbuf
);
738 require_action_quiet(usersig
, fail
,
739 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Failed to sign public key hash for peer"), NULL
, error
));
741 CFDictionarySetValue(pi
->description
, sApplicationUsig
, usersig
);
743 require_quiet(SOSPeerInfoSign(peerkey
, pi
, error
), fail
);
749 CFReleaseNull(usersig
);
754 bool SOSPeerInfoApplicationVerify(SOSPeerInfoRef pi
, SecKeyRef userkey
, CFErrorRef
*error
) {
755 const struct ccdigest_info
*di
= ccsha256_di();
756 uint8_t hbuf
[di
->output_size
];
759 CFDataRef usig
= CFDictionaryGetValue(pi
->description
, sApplicationUsig
);
760 require_action_quiet(usig
, exit
,
761 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Peer is not an applicant"), NULL
, error
));
762 // Verify User Application Signature
763 require_action_quiet(sospeer_application_hash(pi
, di
, hbuf
), exit
,
764 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Failed to create hash for peer applicant"), NULL
, error
));
765 require_action_quiet(sosVerifyHash(userkey
, di
, hbuf
, usig
), exit
,
766 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("user signature of public key hash fails to verify"), NULL
, error
));
768 result
= SOSPeerInfoVerify(pi
, error
);
775 static CF_RETURNS_RETAINED CFDateRef
sosPeerInfoGetDate(SOSPeerInfoRef pi
, CFStringRef entry
) {
777 CFDataRef sosdate
= CFDictionaryGetValue(pi
->description
, entry
);
778 if(!sosdate
) return NULL
;
779 CFDateRef date
= sosCreateCFDate(sosdate
);
784 CF_RETURNS_RETAINED CFDateRef
SOSPeerInfoGetApplicationDate(SOSPeerInfoRef pi
) {
785 return sosPeerInfoGetDate(pi
, sApplicationDate
);
788 CF_RETURNS_RETAINED CFDateRef
SOSPeerInfoGetRetirementDate(SOSPeerInfoRef pi
) {
789 return sosPeerInfoGetDate(pi
, sRetirementDate
);
798 CFStringRef
SOSPeerGestaltGetName(CFDictionaryRef gestalt
) {
799 CFStringRef name
= SOSPeerGestaltGetAnswer(gestalt
, kPIUserDefinedDeviceNameKey
);
800 return isString(name
) ? name
: NULL
;
803 CFTypeRef
SOSPeerGestaltGetAnswer(CFDictionaryRef gestalt
, CFStringRef question
) {
804 return gestalt
? CFDictionaryGetValue(gestalt
, question
) : NULL
;
812 SOSPeerInfoRef
SOSPeerInfoCreateRetirementTicket(CFAllocatorRef allocator
, SecKeyRef privKey
, SOSPeerInfoRef peer
, CFErrorRef
*error
) {
814 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(allocator
, peer
, error
);
818 // Fill out Resignation Date
819 CFDataRef resignationDate
= sosCreateDate();
820 CFDictionaryAddValue(pi
->description
, sRetirementDate
, resignationDate
);
821 CFReleaseNull(resignationDate
);
823 require(SOSPeerInfoSign(privKey
, pi
, error
), fail
);
832 CFStringRef
SOSPeerInfoInspectRetirementTicket(SOSPeerInfoRef pi
, CFErrorRef
*error
) {
833 CFStringRef retval
= NULL
;
834 CFDateRef now
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
835 CFDateRef retirement
= NULL
;
837 require_quiet(SOSPeerInfoVerify(pi
, error
), err
);
839 retirement
= sosCreateCFDate(CFDictionaryGetValue(pi
->description
, sRetirementDate
));
841 require_action_quiet(retirement
, err
,
842 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Peer is not retired"), NULL
, error
));
844 require_action_quiet(CFDateCompare(now
, retirement
, NULL
) == kCFCompareGreaterThan
, err
,
845 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Retirement date is after current date"), NULL
, error
));
847 retval
= SOSPeerInfoGetPeerID(pi
);
851 CFReleaseNull(retirement
);
855 bool SOSPeerInfoRetireRetirementTicket(size_t max_seconds
, SOSPeerInfoRef pi
) {
856 CFDateRef now
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
857 CFDateRef retirement
= sosCreateCFDate(CFDictionaryGetValue(pi
->description
, sRetirementDate
));
858 CFTimeInterval timediff
= CFDateGetTimeIntervalSinceDate(now
, retirement
); // diff in seconds
860 CFReleaseNull(retirement
);
861 if(timediff
> (max_seconds
)) return true;
865 bool SOSPeerInfoIsRetirementTicket(SOSPeerInfoRef pi
) {
866 CFDataRef flag
= CFDictionaryGetValue(pi
->description
, sRetirementDate
);
870 bool SOSPeerInfoIsCloudIdentity(SOSPeerInfoRef pi
) {
871 CFTypeRef value
= CFDictionaryGetValue(pi
->description
, sCloudIdentityKey
);
872 return CFEqualSafe(value
, kCFBooleanTrue
);
875 SOSPeerInfoRef
SOSPeerInfoUpgradeSignatures(CFAllocatorRef allocator
, SecKeyRef privKey
, SecKeyRef peerKey
, SOSPeerInfoRef peer
, CFErrorRef
*error
) {
876 SecKeyRef pubKey
= SecKeyCreatePublicFromPrivate(privKey
);
877 SOSPeerInfoRef retval
= NULL
;
879 retval
= SOSPeerInfoCopyAsApplication(peer
, privKey
, peerKey
, error
);
880 CFReleaseNull(pubKey
);
884 CFBooleanRef
SOSPeerInfoCopyIDSPreference(SOSPeerInfoRef peer
){
885 CFBooleanRef preference
= (CFBooleanRef
)SOSPeerInfoV2DictionaryCopyBoolean(peer
, sPreferIDS
);
886 return (preference
? preference
: CFRetain(kCFBooleanFalse
));
890 SOSPeerInfoRef
SOSPeerInfoSetIDSPreference(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFBooleanRef preference
, SecKeyRef signingKey
, CFErrorRef
*error
){
891 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
892 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
893 SOSPeerInfoV2DictionarySetValue(peerToModify
, sPreferIDS
, preference
);
898 CFStringRef
SOSPeerInfoCopyTransportType(SOSPeerInfoRef peer
){
899 CFStringRef transportType
= (CFStringRef
)SOSPeerInfoV2DictionaryCopyString(peer
, sTransportType
);
900 return (transportType
? transportType
: CFRetain(SOSTransportMessageTypeKVS
));
903 SOSPeerInfoRef
SOSPeerInfoSetTransportType(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFStringRef transportType
, SecKeyRef signingKey
, CFErrorRef
*error
){
905 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
906 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
907 SOSPeerInfoV2DictionarySetValue(peerToModify
, sTransportType
, transportType
);
912 bool SOSPeerInfoHasDeviceID(SOSPeerInfoRef peer
) {
913 return SOSPeerInfoV2DictionaryHasString(peer
, sDeviceID
);
916 CFStringRef
SOSPeerInfoCopyDeviceID(SOSPeerInfoRef peer
){
917 return (CFStringRef
)SOSPeerInfoV2DictionaryCopyString(peer
, sDeviceID
);
920 SOSPeerInfoRef
SOSPeerInfoSetDeviceID(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFStringRef IDS
, SecKeyRef signingKey
, CFErrorRef
*error
){
922 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
923 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
924 SOSPeerInfoV2DictionarySetValue(peerToModify
, sDeviceID
, IDS
);
929 bool SOSPeerInfoShouldUseIDSTransport(SOSPeerInfoRef myPeer
, SOSPeerInfoRef theirPeer
){
931 CFBooleanRef myPreference
= SOSPeerInfoCopyIDSPreference(myPeer
);
932 CFStringRef myTransportType
= SOSPeerInfoCopyTransportType(myPeer
);
934 CFBooleanRef theirPreference
= SOSPeerInfoCopyIDSPreference(theirPeer
);
935 CFStringRef theirTransportType
= SOSPeerInfoCopyTransportType(theirPeer
);
937 bool success
= false;
938 //If I'm a galarch KVS + peer is KVS and ids only is true for both == KVS
939 if((CFStringCompare(myTransportType
, SOSTransportMessageTypeKVS
, 0) == 0 && CFStringCompare(theirTransportType
, SOSTransportMessageTypeKVS
, 0) == 0)&& (myPreference
== kCFBooleanTrue
&& theirPreference
== kCFBooleanTrue
))
942 //If transport is IDS Galarch +1 (Pref is true) and peer is KVS (ids is true) == IDS
943 else if ((CFStringCompare(myTransportType
, SOSTransportMessageTypeIDS
, 0) == 0 && CFStringCompare(theirTransportType
, SOSTransportMessageTypeKVS
, 0) == 0) && (myPreference
== kCFBooleanTrue
&& theirPreference
== kCFBooleanTrue
))
946 else if ((CFStringCompare(theirTransportType
, SOSTransportMessageTypeIDS
, 0) == 0 && CFStringCompare(myTransportType
, SOSTransportMessageTypeKVS
, 0) == 0) && (theirPreference
== kCFBooleanTrue
&& myPreference
== kCFBooleanTrue
))
949 //If transport is IDS Galarch +1 and peer is IDS Galarch +1 (prefer IDS is true) == IDS
950 else if ((CFStringCompare(myTransportType
, SOSTransportMessageTypeIDS
, 0) == 0 && CFStringCompare(theirTransportType
, SOSTransportMessageTypeIDS
, 0) == 0))
953 //If KVS and KVS prefer IDS is false (set to false or doesn't exist) == KVS
954 else if ((CFStringCompare(myTransportType
, SOSTransportMessageTypeKVS
, 0) == 0 && CFStringCompare(theirTransportType
, SOSTransportMessageTypeKVS
, 0) == 0) && (myPreference
== kCFBooleanFalse
&& theirPreference
== kCFBooleanFalse
))
960 CFReleaseSafe(myPreference
);
961 CFReleaseSafe(myTransportType
);
962 CFReleaseSafe(theirPreference
);
963 CFReleaseSafe(theirTransportType
);