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
);
317 bool SOSPeerInfoVersionIsCurrent(SOSPeerInfoRef pi
) {
318 return pi
->version
>= PEERINFO_CURRENT_VERSION
;
321 bool SOSPeerInfoVersionHasV2Data(SOSPeerInfoRef pi
) {
322 return pi
->version
>= kSOSPeerV2BaseVersion
;
325 SOSPeerInfoRef
SOSPeerInfoCreateCurrentCopy(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
,
326 CFStringRef IDSID
, CFStringRef transportType
, CFBooleanRef preferIDS
, CFSetRef enabledViews
,
327 SecKeyRef signingKey
, CFErrorRef
* error
) {
329 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(allocator
, toCopy
, error
);
330 if(!SOSPeerInfoVersionHasV2Data(pi
)) SOSPeerInfoUpdateToV2(pi
, error
);
332 SOSPeerInfoSetSerialNumber(pi
);
335 SOSPeerInfoV2DictionarySetValue(pi
, sDeviceID
, IDSID
);
338 SOSPeerInfoV2DictionarySetValue(pi
, sTransportType
, transportType
);
341 SOSPeerInfoV2DictionarySetValue(pi
, sPreferIDS
, preferIDS
);
344 SOSPeerInfoV2DictionarySetValue(pi
, sViewsKey
, enabledViews
);
347 if(!SOSPeerInfoSign(signingKey
, pi
, error
)) {
355 static SOSPeerInfoRef
SOSPeerInfoCopyWithModification(CFAllocatorRef allocator
, SOSPeerInfoRef original
,
356 SecKeyRef signingKey
, CFErrorRef
*error
,
357 bool (^modification
)(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
)) {
359 SOSPeerInfoRef result
= NULL
;
360 SOSPeerInfoRef copy
= SOSPeerInfoCreateCopy(allocator
, original
, error
);
362 require_quiet(modification(copy
, error
), fail
);
364 require_quiet(SOSPeerInfoSign(signingKey
, copy
, error
), fail
);
366 CFTransferRetained(result
, copy
);
374 SOSPeerInfoRef
SOSPeerInfoCopyWithGestaltUpdate(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFDictionaryRef gestalt
, SecKeyRef signingKey
, CFErrorRef
* error
) {
375 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
376 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
377 CFRetainAssign(peerToModify
->gestalt
, gestalt
);
378 CFDictionarySetValue(peerToModify
->description
, sGestaltKey
, peerToModify
->gestalt
);
385 SOSPeerInfoRef
SOSPeerInfoCopyWithBackupKeyUpdate(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFDataRef backupKey
, SecKeyRef signingKey
, CFErrorRef
*error
) {
386 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
387 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
388 if (backupKey
!= NULL
)
389 SOSPeerInfoV2DictionarySetValue(peerToModify
, sBackupKeyKey
, backupKey
);
391 SOSPeerInfoV2DictionaryRemoveValue(peerToModify
, sBackupKeyKey
);
396 CFDataRef
SOSPeerInfoCopyBackupKey(SOSPeerInfoRef peer
) {
397 return SOSPeerInfoV2DictionaryCopyData(peer
, sBackupKeyKey
);
400 bool SOSPeerInfoHasBackupKey(SOSPeerInfoRef peer
) {
401 CFDataRef bk
= SOSPeerInfoCopyBackupKey(peer
);
402 bool success
= bk
!= NULL
;
407 SOSPeerInfoRef
SOSPeerInfoCopyWithViewsChange(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
,
408 SOSViewActionCode action
, CFStringRef viewname
, SOSViewResultCode
*retval
,
409 SecKeyRef signingKey
, CFErrorRef
* error
) {
410 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(allocator
, toCopy
, error
);
411 if(action
== kSOSCCViewEnable
) {
412 *retval
= SOSViewsEnable(pi
, viewname
, error
);
413 require((kSOSCCViewMember
== *retval
), exit
);
414 } else if(action
== kSOSCCViewDisable
) {
415 *retval
= SOSViewsDisable(pi
, viewname
, error
);
416 require((kSOSCCViewNotMember
== *retval
), exit
);
419 require_action_quiet(SOSPeerInfoSign(signingKey
, pi
, error
), exit
, *retval
= kSOSCCGeneralViewError
);
428 CFStringRef sPingKey
= CFSTR("Ping");
430 SOSPeerInfoRef
SOSPeerInfoCopyWithPing(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, SecKeyRef signingKey
, CFErrorRef
* error
) {
431 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(allocator
, toCopy
, error
);
432 CFDataRef ping
= CFDataCreateWithRandomBytes(8);
433 SOSPeerInfoV2DictionarySetValue(pi
, sPingKey
, ping
);
434 SecKeyRef pub_key
= SOSPeerInfoCopyPubKey(pi
);
435 pi
->id
= SOSCopyIDOfKey(pub_key
, error
);
436 require_quiet(pi
->id
, exit
);
437 require_action_quiet(SOSPeerInfoSign(signingKey
, pi
, error
), exit
, CFReleaseNull(pi
));
440 CFReleaseNull(pub_key
);
445 SOSViewResultCode
SOSPeerInfoViewStatus(SOSPeerInfoRef pi
, CFStringRef view
, CFErrorRef
*error
) {
446 return SOSViewsQuery(pi
, view
, error
);
450 SOSPeerInfoRef
SOSPeerInfoCopyWithSecurityPropertyChange(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
,
451 SOSSecurityPropertyActionCode action
, CFStringRef property
, SOSSecurityPropertyResultCode
*retval
,
452 SecKeyRef signingKey
, CFErrorRef
* error
) {
453 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(allocator
, toCopy
, error
);
454 if(action
== kSOSCCSecurityPropertyEnable
) {
455 *retval
= SOSSecurityPropertyEnable(pi
, property
, error
);
456 require((kSOSCCSecurityPropertyValid
== *retval
), exit
);
457 } else if(action
== kSOSCCSecurityPropertyDisable
) {
458 *retval
= SOSSecurityPropertyDisable(pi
, property
, error
);
459 require((kSOSCCSecurityPropertyNotValid
== *retval
), exit
);
462 require_action_quiet(SOSPeerInfoSign(signingKey
, pi
, error
), exit
, *retval
= kSOSCCGeneralViewError
);
470 SOSViewResultCode
SOSPeerInfoSecurityPropertyStatus(SOSPeerInfoRef pi
, CFStringRef property
, CFErrorRef
*error
) {
471 return SOSSecurityPropertyQuery(pi
, property
, error
);
476 static void SOSPeerInfoDestroy(CFTypeRef aObj
) {
477 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) aObj
;
480 CFReleaseNull(pi
->description
);
481 CFReleaseNull(pi
->signature
);
482 CFReleaseNull(pi
->gestalt
);
483 CFReleaseNull(pi
->id
);
484 CFReleaseNull(pi
->v2Dictionary
);
485 CFReleaseNull(pi
->secproperties
);
488 static Boolean
SOSPeerInfoCompare(CFTypeRef lhs
, CFTypeRef rhs
) {
489 SOSPeerInfoRef lpeer
= (SOSPeerInfoRef
) lhs
;
490 SOSPeerInfoRef rpeer
= (SOSPeerInfoRef
) rhs
;
491 if(!lpeer
|| !rpeer
) return false;
492 return CFEqualSafe(lpeer
->description
, rpeer
->description
) && CFEqualSafe(lpeer
->signature
, rpeer
->signature
);
496 CFComparisonResult
SOSPeerInfoCompareByID(const void *val1
, const void *val2
, void *context
) {
497 // The code below is necessary but not sufficient; not returning a CFComparisonResult
498 // It probably is OK to say that a NULL is < <non-NULL>
499 if (val1
== NULL
|| val2
== NULL
) {
500 ptrdiff_t dv
= val1
- val2
;
501 return dv
< 0 ? kCFCompareLessThan
: dv
== 0 ? kCFCompareEqualTo
: kCFCompareGreaterThan
;
504 CFStringRef v1
= SOSPeerInfoGetPeerID((SOSPeerInfoRef
) val1
);
505 CFStringRef v2
= SOSPeerInfoGetPeerID((SOSPeerInfoRef
) val2
);
506 if (v1
== NULL
|| v2
== NULL
) {
507 ptrdiff_t dv
= (const void *)v1
- (const void *)v2
;
508 return dv
< 0 ? kCFCompareLessThan
: dv
== 0 ? kCFCompareEqualTo
: kCFCompareGreaterThan
;
511 return CFStringCompare(v1
, v2
, 0);
514 static CFHashCode
SOSPeerInfoHash(CFTypeRef cf
) {
515 SOSPeerInfoRef peer
= (SOSPeerInfoRef
) cf
;
517 return CFHash(peer
->description
) ^ CFHash(peer
->signature
);
520 static CFStringRef
copyDescriptionWithFormatOptions(CFTypeRef aObj
, CFDictionaryRef formatOptions
){
522 SOSPeerInfoRef pi
= (SOSPeerInfoRef
) aObj
;
523 CFStringRef description
= NULL
;
526 // Get the format options we care about:
527 CFNumberRef peerIDLengthNumber
= formatOptions
? CFDictionaryGetValue(formatOptions
, peerIDLengthKey
) : NULL
;
528 bool useSyncDFormat
= formatOptions
&& CFDictionaryContainsKey(formatOptions
, CFSTR("SyncD"));
530 // Calculate the truncated length
531 CFIndex truncatedLength
= CFStringGetLength(pi
->id
);
532 if (isNumber(peerIDLengthNumber
)) {
533 CFIndex peerIDMaximumLength
= truncatedLength
;
534 CFNumberGetValue(peerIDLengthNumber
, kCFNumberCFIndexType
, &peerIDMaximumLength
);
535 truncatedLength
= MIN(truncatedLength
, peerIDMaximumLength
);
537 CFStringRef displayPeerID
= CFStringCreateWithSubstring(kCFAllocatorDefault
, pi
->id
, CFRangeMake(0, truncatedLength
));
539 CFStringRef objectPrefix
= useSyncDFormat
? CFSTR("PI") :
540 CFStringCreateWithFormat(kCFAllocatorDefault
, formatOptions
, CFSTR("PeerInfo@%p"), pi
);
542 CFStringRef osVersion
= CFDictionaryGetValue(pi
->gestalt
, kPIOSVersionKey
);
544 CFStringRef transportType
= SOSPeerInfoV2DictionaryCopyString(pi
, sTransportType
);
545 CFStringRef deviceID
= SOSPeerInfoV2DictionaryCopyString(pi
, sDeviceID
);
547 description
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<%@: %s%s '%@' %@ %@ %@ %@ %@ %ld>"),
549 SOSPeerInfoIsRetirementTicket(pi
) ? "R" : "-",
550 SOSPeerInfoHasBackupKey(pi
) ? "B" : "-",
551 CFDictionaryGetValue(pi
->gestalt
, kPIUserDefinedDeviceNameKey
),
552 CFDictionaryGetValue(pi
->gestalt
, kPIDeviceModelNameKey
),
553 osVersion
? osVersion
: CFSTR("????"),
554 transportType
? transportType
: CFSTR("KVS"),
555 deviceID
? deviceID
: CFSTR(""),
559 CFReleaseNull(transportType
);
560 CFReleaseNull(deviceID
);
562 CFReleaseNull(objectPrefix
);
563 CFReleaseNull(displayPeerID
);
568 static CFStringRef
SOSPeerInfoCopyFormatDescription(CFTypeRef aObj
, CFDictionaryRef formatOptions
) {
570 CFStringRef description
= NULL
;
572 description
= copyDescriptionWithFormatOptions(aObj
, formatOptions
);
577 CFDictionaryRef
SOSPeerInfoCopyPeerGestalt(SOSPeerInfoRef pi
) {
578 CFRetain(pi
->gestalt
);
582 CFDictionaryRef
SOSPeerGetGestalt(SOSPeerInfoRef pi
){
586 CFStringRef
SOSPeerInfoGetPeerName(SOSPeerInfoRef peer
) {
587 return SOSPeerInfoLookupGestaltValue(peer
, kPIUserDefinedDeviceNameKey
);
590 CFStringRef
SOSPeerInfoGetPeerDeviceType(SOSPeerInfoRef peer
) {
591 return SOSPeerInfoLookupGestaltValue(peer
, kPIDeviceModelNameKey
);
594 CFIndex
SOSPeerInfoGetPeerProtocolVersion(SOSPeerInfoRef peer
) {
595 CFIndex version
= PEERINFO_CURRENT_VERSION
;
596 CFTypeRef val
= SOSPeerInfoLookupGestaltValue(peer
, kPIMessageProtocolVersionKey
);
597 if (val
&& CFGetTypeID(val
) == CFNumberGetTypeID())
598 CFNumberGetValue(val
, kCFNumberCFIndexType
, &version
);
602 CFTypeRef
SOSPeerInfoLookupGestaltValue(SOSPeerInfoRef pi
, CFStringRef key
) {
603 return CFDictionaryGetValue(pi
->gestalt
, key
);
606 CFStringRef
SOSPeerInfoGetPeerID(SOSPeerInfoRef pi
) {
607 return pi
? pi
->id
: NULL
;
610 bool SOSPeerInfoPeerIDEqual(SOSPeerInfoRef pi
, CFStringRef myPeerID
) {
611 return CFEqualSafe(myPeerID
, SOSPeerInfoGetPeerID(pi
));
614 CFIndex
SOSPeerInfoGetVersion(SOSPeerInfoRef pi
) {
618 bool SOSPeerInfoUpdateDigestWithPublicKeyBytes(SOSPeerInfoRef peer
, const struct ccdigest_info
*di
,
619 ccdigest_ctx_t ctx
, CFErrorRef
*error
) {
620 CFDataRef pubKeyBytes
= CFDictionaryGetValue(peer
->description
, sPublicKeyKey
);
623 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, NULL
, error
, NULL
, CFSTR("Digest failed – no public key"));
627 ccdigest_update(di
, ctx
, CFDataGetLength(pubKeyBytes
), CFDataGetBytePtr(pubKeyBytes
));
632 bool SOSPeerInfoUpdateDigestWithDescription(SOSPeerInfoRef peer
, const struct ccdigest_info
*di
,
633 ccdigest_ctx_t ctx
, CFErrorRef
*error
) {
634 if(SOSPeerInfoVersionHasV2Data(peer
)) SOSPeerInfoPackV2Data(peer
);
635 size_t description_size
= der_sizeof_plist(peer
->description
, error
);
636 uint8_t data_begin
[description_size
];
637 uint8_t *data_end
= data_begin
+ description_size
;
638 uint8_t *encoded
= der_encode_plist(peer
->description
, error
, data_begin
, data_end
);
641 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure
, NULL
, error
, NULL
, CFSTR("Description encode failed"));
645 ccdigest_update(di
, ctx
, description_size
, data_begin
);
651 static CFDataRef
sosCreateDate() {
652 CFDateRef now
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
653 size_t bufsiz
= der_sizeof_date(now
, NULL
);
655 der_encode_date(now
, NULL
, buf
, buf
+bufsiz
);
657 return CFDataCreate(NULL
, buf
, bufsiz
);
660 static CFDateRef
sosCreateCFDate(CFDataRef sosdate
) {
662 der_decode_date(NULL
, 0, &date
, NULL
, CFDataGetBytePtr(sosdate
),
663 CFDataGetBytePtr(sosdate
) + CFDataGetLength(sosdate
));
667 static bool sospeer_application_hash(SOSPeerInfoRef pi
, const struct ccdigest_info
*di
, uint8_t *hbuf
) {
668 CFDataRef appdate
= CFDictionaryGetValue(pi
->description
, sApplicationDate
);
669 if(!appdate
) return false;
670 ccdigest_di_decl(di
, ctx
);
671 ccdigest_init(di
, ctx
);
672 ccdigest_update(di
, ctx
, CFDataGetLength(appdate
), CFDataGetBytePtr(appdate
));
673 if (!SOSPeerInfoUpdateDigestWithPublicKeyBytes(pi
, di
, ctx
, NULL
)) return false;
674 ccdigest_final(di
, ctx
, hbuf
);
678 SOSPeerInfoRef
SOSPeerInfoCopyAsApplication(SOSPeerInfoRef original
, SecKeyRef userkey
, SecKeyRef peerkey
, CFErrorRef
*error
) {
679 SOSPeerInfoRef result
= NULL
;
680 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(kCFAllocatorDefault
, original
, error
);
681 const struct ccdigest_info
*di
= ccsha256_di();
682 uint8_t hbuf
[di
->output_size
];
683 CFDataRef usersig
= NULL
;
685 CFDataRef creationDate
= sosCreateDate();
686 CFDictionarySetValue(pi
->description
, sApplicationDate
, creationDate
);
687 CFReleaseNull(creationDate
);
689 // Create User Application Signature
690 require_action_quiet(sospeer_application_hash(pi
, di
, hbuf
), fail
,
691 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Failed to create hash for peer applicant"), NULL
, error
));
693 usersig
= sosSignHash(userkey
, di
, hbuf
);
694 require_action_quiet(usersig
, fail
,
695 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Failed to sign public key hash for peer"), NULL
, error
));
697 CFDictionarySetValue(pi
->description
, sApplicationUsig
, usersig
);
699 require_quiet(SOSPeerInfoSign(peerkey
, pi
, error
), fail
);
705 CFReleaseNull(usersig
);
710 bool SOSPeerInfoApplicationVerify(SOSPeerInfoRef pi
, SecKeyRef userkey
, CFErrorRef
*error
) {
711 const struct ccdigest_info
*di
= ccsha256_di();
712 uint8_t hbuf
[di
->output_size
];
715 CFDataRef usig
= CFDictionaryGetValue(pi
->description
, sApplicationUsig
);
716 require_action_quiet(usig
, exit
,
717 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Peer is not an applicant"), NULL
, error
));
718 // Verify User Application Signature
719 require_action_quiet(sospeer_application_hash(pi
, di
, hbuf
), exit
,
720 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Failed to create hash for peer applicant"), NULL
, error
));
721 require_action_quiet(sosVerifyHash(userkey
, di
, hbuf
, usig
), exit
,
722 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("user signature of public key hash fails to verify"), NULL
, error
));
724 result
= SOSPeerInfoVerify(pi
, error
);
731 static CF_RETURNS_RETAINED CFDateRef
sosPeerInfoGetDate(SOSPeerInfoRef pi
, CFStringRef entry
) {
733 CFDataRef sosdate
= CFDictionaryGetValue(pi
->description
, entry
);
734 if(!sosdate
) return NULL
;
735 CFDateRef date
= sosCreateCFDate(sosdate
);
740 CF_RETURNS_RETAINED CFDateRef
SOSPeerInfoGetApplicationDate(SOSPeerInfoRef pi
) {
741 return sosPeerInfoGetDate(pi
, sApplicationDate
);
744 CF_RETURNS_RETAINED CFDateRef
SOSPeerInfoGetRetirementDate(SOSPeerInfoRef pi
) {
745 return sosPeerInfoGetDate(pi
, sRetirementDate
);
754 CFStringRef
SOSPeerGestaltGetName(CFDictionaryRef gestalt
) {
755 CFStringRef name
= SOSPeerGestaltGetAnswer(gestalt
, kPIUserDefinedDeviceNameKey
);
756 return isString(name
) ? name
: NULL
;
759 CFTypeRef
SOSPeerGestaltGetAnswer(CFDictionaryRef gestalt
, CFStringRef question
) {
760 return gestalt
? CFDictionaryGetValue(gestalt
, question
) : NULL
;
768 SOSPeerInfoRef
SOSPeerInfoCreateRetirementTicket(CFAllocatorRef allocator
, SecKeyRef privKey
, SOSPeerInfoRef peer
, CFErrorRef
*error
) {
770 SOSPeerInfoRef pi
= SOSPeerInfoCreateCopy(allocator
, peer
, error
);
774 // Fill out Resignation Date
775 CFDataRef resignationDate
= sosCreateDate();
776 CFDictionaryAddValue(pi
->description
, sRetirementDate
, resignationDate
);
777 CFReleaseNull(resignationDate
);
779 require(SOSPeerInfoSign(privKey
, pi
, error
), fail
);
788 CFStringRef
SOSPeerInfoInspectRetirementTicket(SOSPeerInfoRef pi
, CFErrorRef
*error
) {
789 CFStringRef retval
= NULL
;
790 CFDateRef now
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
791 CFDateRef retirement
= NULL
;
793 require_quiet(SOSPeerInfoVerify(pi
, error
), err
);
795 retirement
= sosCreateCFDate(CFDictionaryGetValue(pi
->description
, sRetirementDate
));
797 require_action_quiet(retirement
, err
,
798 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Peer is not retired"), NULL
, error
));
800 require_action_quiet(CFDateCompare(now
, retirement
, NULL
) == kCFCompareGreaterThan
, err
,
801 SOSCreateError(kSOSErrorUnexpectedType
, CFSTR("Retirement date is after current date"), NULL
, error
));
803 retval
= SOSPeerInfoGetPeerID(pi
);
807 CFReleaseNull(retirement
);
811 bool SOSPeerInfoRetireRetirementTicket(size_t max_seconds
, SOSPeerInfoRef pi
) {
812 CFDateRef now
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
813 CFDateRef retirement
= sosCreateCFDate(CFDictionaryGetValue(pi
->description
, sRetirementDate
));
814 CFTimeInterval timediff
= CFDateGetTimeIntervalSinceDate(now
, retirement
); // diff in seconds
816 CFReleaseNull(retirement
);
817 if(timediff
> (max_seconds
)) return true;
821 bool SOSPeerInfoIsRetirementTicket(SOSPeerInfoRef pi
) {
822 CFDataRef flag
= CFDictionaryGetValue(pi
->description
, sRetirementDate
);
826 bool SOSPeerInfoIsCloudIdentity(SOSPeerInfoRef pi
) {
827 CFTypeRef value
= CFDictionaryGetValue(pi
->description
, sCloudIdentityKey
);
828 return CFEqualSafe(value
, kCFBooleanTrue
);
831 SOSPeerInfoRef
SOSPeerInfoUpgradeSignatures(CFAllocatorRef allocator
, SecKeyRef privKey
, SecKeyRef peerKey
, SOSPeerInfoRef peer
, CFErrorRef
*error
) {
832 SecKeyRef pubKey
= SecKeyCreatePublicFromPrivate(privKey
);
833 SOSPeerInfoRef retval
= NULL
;
835 retval
= SOSPeerInfoCopyAsApplication(peer
, privKey
, peerKey
, error
);
836 CFReleaseNull(pubKey
);
840 CFBooleanRef
SOSPeerInfoCopyIDSPreference(SOSPeerInfoRef peer
){
841 CFBooleanRef preference
= (CFBooleanRef
)SOSPeerInfoV2DictionaryCopyBoolean(peer
, sPreferIDS
);
842 return (preference
? preference
: CFRetain(kCFBooleanFalse
));
846 SOSPeerInfoRef
SOSPeerInfoSetIDSPreference(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFBooleanRef preference
, SecKeyRef signingKey
, CFErrorRef
*error
){
847 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
848 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
849 SOSPeerInfoV2DictionarySetValue(peerToModify
, sPreferIDS
, preference
);
854 CFStringRef
SOSPeerInfoCopyTransportType(SOSPeerInfoRef peer
){
855 CFStringRef transportType
= (CFStringRef
)SOSPeerInfoV2DictionaryCopyString(peer
, sTransportType
);
856 return (transportType
? transportType
: CFRetain(SOSTransportMessageTypeKVS
));
859 SOSPeerInfoRef
SOSPeerInfoSetTransportType(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFStringRef transportType
, SecKeyRef signingKey
, CFErrorRef
*error
){
861 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
862 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
863 SOSPeerInfoV2DictionarySetValue(peerToModify
, sTransportType
, transportType
);
868 bool SOSPeerInfoHasDeviceID(SOSPeerInfoRef peer
) {
869 return SOSPeerInfoV2DictionaryHasString(peer
, sDeviceID
);
872 CFStringRef
SOSPeerInfoCopyDeviceID(SOSPeerInfoRef peer
){
873 return (CFStringRef
)SOSPeerInfoV2DictionaryCopyString(peer
, sDeviceID
);
876 SOSPeerInfoRef
SOSPeerInfoSetDeviceID(CFAllocatorRef allocator
, SOSPeerInfoRef toCopy
, CFStringRef IDS
, SecKeyRef signingKey
, CFErrorRef
*error
){
878 return SOSPeerInfoCopyWithModification(allocator
, toCopy
, signingKey
, error
,
879 ^bool(SOSPeerInfoRef peerToModify
, CFErrorRef
*error
) {
880 SOSPeerInfoV2DictionarySetValue(peerToModify
, sDeviceID
, IDS
);
885 bool SOSPeerInfoShouldUseIDSTransport(SOSPeerInfoRef myPeer
, SOSPeerInfoRef theirPeer
){
887 CFBooleanRef myPreference
= SOSPeerInfoCopyIDSPreference(myPeer
);
888 CFStringRef myTransportType
= SOSPeerInfoCopyTransportType(myPeer
);
890 CFBooleanRef theirPreference
= SOSPeerInfoCopyIDSPreference(theirPeer
);
891 CFStringRef theirTransportType
= SOSPeerInfoCopyTransportType(theirPeer
);
893 bool success
= false;
894 //If I'm a galarch KVS + peer is KVS and ids only is true for both == KVS
895 if((CFStringCompare(myTransportType
, SOSTransportMessageTypeKVS
, 0) == 0 && CFStringCompare(theirTransportType
, SOSTransportMessageTypeKVS
, 0) == 0)&& (myPreference
== kCFBooleanTrue
&& theirPreference
== kCFBooleanTrue
))
898 //If transport is IDS Galarch +1 (Pref is true) and peer is KVS (ids is true) == IDS
899 else if ((CFStringCompare(myTransportType
, SOSTransportMessageTypeIDS
, 0) == 0 && CFStringCompare(theirTransportType
, SOSTransportMessageTypeKVS
, 0) == 0) && (myPreference
== kCFBooleanTrue
&& theirPreference
== kCFBooleanTrue
))
902 else if ((CFStringCompare(theirTransportType
, SOSTransportMessageTypeIDS
, 0) == 0 && CFStringCompare(myTransportType
, SOSTransportMessageTypeKVS
, 0) == 0) && (theirPreference
== kCFBooleanTrue
&& myPreference
== kCFBooleanTrue
))
905 //If transport is IDS Galarch +1 and peer is IDS Galarch +1 (prefer IDS is true) == IDS
906 else if ((CFStringCompare(myTransportType
, SOSTransportMessageTypeIDS
, 0) == 0 && CFStringCompare(theirTransportType
, SOSTransportMessageTypeIDS
, 0) == 0))
909 //If KVS and KVS prefer IDS is false (set to false or doesn't exist) == KVS
910 else if ((CFStringCompare(myTransportType
, SOSTransportMessageTypeKVS
, 0) == 0 && CFStringCompare(theirTransportType
, SOSTransportMessageTypeKVS
, 0) == 0) && (myPreference
== kCFBooleanFalse
&& theirPreference
== kCFBooleanFalse
))
916 CFReleaseSafe(myPreference
);
917 CFReleaseSafe(myTransportType
);
918 CFReleaseSafe(theirPreference
);
919 CFReleaseSafe(theirTransportType
);