]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSPeerInfo.m
Security-58286.270.3.0.1.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSPeerInfo.m
1 /*
2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 #include <AssertMacros.h>
26 #include <TargetConditionals.h>
27
28 #include <Security/SecureObjectSync/SOSPeerInfo.h>
29 #include <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/SOSInternal.h>
34 #include <ipc/securityd_client.h>
35
36 #include <CoreFoundation/CFArray.h>
37 #include <dispatch/dispatch.h>
38
39 #include <stdlib.h>
40 #include <assert.h>
41
42 #include <utilities/SecCFWrappers.h>
43 #include <utilities/SecCFRelease.h>
44 #include <utilities/SecCFError.h>
45 #include <utilities/SecXPCError.h>
46
47 #include <utilities/der_plist.h>
48 #include <utilities/der_plist_internal.h>
49 #include <corecrypto/ccder.h>
50 #include <utilities/der_date.h>
51
52 #include <corecrypto/ccdigest.h>
53 #include <corecrypto/ccsha2.h>
54
55
56 #include <CoreFoundation/CoreFoundation.h>
57 #include <CoreFoundation/CFDate.h>
58
59 #include <xpc/xpc.h>
60
61 #if TARGET_OS_IPHONE || TARGET_OS_EMBEDDED
62 #include <MobileGestalt.h>
63 #endif
64
65 #include <Security/SecBase64.h>
66 #include <Security/SecKeyPriv.h>
67 #include <Security/SecOTR.h>
68 #include <Security/SecuritydXPC.h>
69
70 CFGiblisWithHashFor(SOSPeerInfo);
71
72
73 const CFStringRef kPIUserDefinedDeviceNameKey = CFSTR("ComputerName");
74 const CFStringRef kPIDeviceModelNameKey = CFSTR("ModelName");
75 const CFStringRef kPIMessageProtocolVersionKey = CFSTR("MessageProtocolVersion");
76 const CFStringRef kPIOSVersionKey = CFSTR("OSVersion");
77
78 // Description Dictionary Entries
79 static CFStringRef sPublicKeyKey = CFSTR("PublicSigningKey");
80 static CFStringRef sOctagonPeerSigningPublicKeyKey = CFSTR("OctagonPublicSigningKey");
81 static CFStringRef sOctagonPeerEncryptionPublicKeyKey = CFSTR("OctagonPublicEncryptionKey");
82 const CFStringRef sGestaltKey = CFSTR("DeviceGestalt");
83 const CFStringRef sVersionKey = CFSTR("ConflictVersion");
84 static CFStringRef sCloudIdentityKey = CFSTR("CloudIdentity");
85 static CFStringRef sApplicationDate = CFSTR("ApplicationDate");
86 static CFStringRef sApplicationUsig = CFSTR("ApplicationUsig");
87 static CFStringRef sRetirementDate = CFSTR("RetirementDate");
88
89 // Peerinfo Entries
90 CFStringRef kSOSPeerInfoDescriptionKey = CFSTR("SOSPeerInfoDescription");
91 CFStringRef kSOSPeerInfoSignatureKey = CFSTR("SOSPeerInfoSignature");
92 CFStringRef kSOSPeerInfoNameKey = CFSTR("SOSPeerInfoName");
93
94 //Peer Info V2 Dictionary IDS keys
95 CFStringRef sPreferIDS = CFSTR("PreferIDS");
96 CFStringRef sPreferIDSFragmentation = CFSTR("PreferIDFragmentation");
97 CFStringRef sPreferIDSACKModel = CFSTR("PreferIDSAckModel");
98 CFStringRef sTransportType = CFSTR("TransportType");
99 CFStringRef sDeviceID = CFSTR("DeviceID");
100
101 const CFStringRef peerIDLengthKey = CFSTR("idLength");
102
103 SOSPeerInfoRef SOSPeerInfoAllocate(CFAllocatorRef allocator) {
104 return CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator);
105 }
106
107 static SecKeyRef _SOSPeerInfoCopyPubKey(SOSPeerInfoRef peer, CFStringRef keyDictionaryKey, CFErrorRef* error)
108 {
109 SecKeyRef result = NULL;
110
111 CFDataRef pubKeyBytes = asData(CFDictionaryGetValue(peer->description, keyDictionaryKey), error);
112 require_quiet(pubKeyBytes, fail);
113
114 CFAllocatorRef allocator = CFGetAllocator(peer);
115 result = SecKeyCreateFromPublicData(allocator, kSecECDSAAlgorithmID, pubKeyBytes);
116
117 require_quiet(SecAllocationError(result, error, CFSTR("Failed to create public key from data %@"), pubKeyBytes), fail);
118
119 fail:
120 return result;
121 }
122
123 SecKeyRef SOSPeerInfoCopyPubKey(SOSPeerInfoRef peer, CFErrorRef* error) {
124 return _SOSPeerInfoCopyPubKey(peer, sPublicKeyKey, error);
125 }
126
127 SecKeyRef SOSPeerInfoCopyOctagonSigningPublicKey(SOSPeerInfoRef peer, CFErrorRef* error)
128 {
129 return _SOSPeerInfoCopyPubKey(peer, sOctagonPeerSigningPublicKeyKey, error);
130 }
131
132 SecKeyRef SOSPeerInfoCopyOctagonEncryptionPublicKey(SOSPeerInfoRef peer, CFErrorRef* error)
133 {
134 return _SOSPeerInfoCopyPubKey(peer, sOctagonPeerEncryptionPublicKeyKey, error);
135 }
136
137
138 CFDataRef SOSPeerInfoGetAutoAcceptInfo(SOSPeerInfoRef peer) {
139 CFDataRef pubKeyBytes = NULL;
140
141 pubKeyBytes = CFDictionaryGetValue(peer->description, sPublicKeyKey);
142 if (!pubKeyBytes || CFGetTypeID(pubKeyBytes) != CFDataGetTypeID()) {
143 pubKeyBytes = NULL;
144 }
145
146 return pubKeyBytes;
147 }
148
149 static bool SOSDescriptionHash(SOSPeerInfoRef peer, const struct ccdigest_info *di, void *hashresult, CFErrorRef *error) {
150 ccdigest_di_decl(di, ctx);
151 ccdigest_init(di, ctx);
152 void *ctx_p = ctx;
153 if(!SOSPeerInfoUpdateDigestWithDescription(peer, di, ctx_p, error)) return false;
154 ccdigest_final(di, ctx, hashresult);
155 return true;
156 }
157
158
159 #define SIGLEN 128
160 static CFDataRef sosCopySignedHash(SecKeyRef privkey, const struct ccdigest_info *di, uint8_t *hbuf) {
161 OSStatus stat;
162 size_t siglen = SIGLEN;
163 uint8_t sig[siglen];
164 if((stat = SecKeyRawSign(privkey, kSecPaddingNone, hbuf, di->output_size, sig, &siglen)) != 0) {
165 return NULL;
166 }
167 return CFDataCreate(NULL, sig, (CFIndex)siglen);
168 }
169
170 static bool sosVerifyHash(SecKeyRef pubkey, const struct ccdigest_info *di, uint8_t *hbuf, CFDataRef signature) {
171 return SecKeyRawVerify(pubkey, kSecPaddingNone, hbuf, di->output_size,
172 CFDataGetBytePtr(signature), CFDataGetLength(signature)) == errSecSuccess;
173 }
174
175 bool SOSPeerInfoSign(SecKeyRef privKey, SOSPeerInfoRef peer, CFErrorRef *error) {
176 bool status = false;
177 const struct ccdigest_info *di = ccsha256_di();
178 uint8_t hbuf[di->output_size];
179 CFDataRef newSignature = NULL;
180
181 require_action_quiet(SOSDescriptionHash(peer, di, hbuf, error), fail,
182 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to hash description for peer"), NULL, error));
183
184 newSignature = sosCopySignedHash(privKey, di, hbuf);
185 require_action_quiet(newSignature, fail, SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to sign peerinfo for peer"), NULL, error));
186
187 CFReleaseNull(peer->signature);
188 peer->signature = newSignature;
189 newSignature = NULL;
190 status = true;
191
192 fail:
193 CFReleaseNull(newSignature);
194 return status;
195 }
196
197 // Return true (1) if the signature verifies.
198 bool SOSPeerInfoVerify(SOSPeerInfoRef peer, CFErrorRef *error) {
199 bool result = false;
200 const struct ccdigest_info *di = ccsha256_di();
201 uint8_t hbuf[di->output_size];
202
203 SecKeyRef pubKey = SOSPeerInfoCopyPubKey(peer, error);
204 require_quiet(pubKey, error_out);
205
206 require_quiet(SOSDescriptionHash(peer, di, hbuf, error), error_out);
207
208 require_action_quiet(sosVerifyHash(pubKey, di, hbuf, peer->signature), error_out,
209 SOSErrorCreate(kSOSErrorBadSignature, error, NULL,
210 CFSTR("Signature didn't verify for %@"), peer));
211 result = true;
212
213 error_out:
214 CFReleaseNull(pubKey);
215 return result;
216 }
217
218 void SOSPeerInfoSetVersionNumber(SOSPeerInfoRef pi, int version) {
219 pi->version = version;
220 CFNumberRef versionNumber = CFNumberCreateWithCFIndex(NULL, pi->version);
221 CFDictionarySetValue(pi->description, sVersionKey, versionNumber);
222 CFReleaseNull(versionNumber);
223 }
224
225 static SOSPeerInfoRef SOSPeerInfoCreate_Internal(CFAllocatorRef allocator,
226 CFDictionaryRef gestalt, CFDataRef backup_key,
227 CFStringRef IDSID, CFStringRef transportType, CFBooleanRef preferIDS,
228 CFBooleanRef preferFragmentation, CFBooleanRef preferAckModel,
229 CFSetRef enabledViews,
230 SecKeyRef signingKey,
231 SecKeyRef octagonPeerSigningKey,
232 SecKeyRef octagonPeerEncryptionKey,
233 CFErrorRef* error,
234 void (^ description_modifier)(CFMutableDictionaryRef description)) {
235 SOSPeerInfoRef pi = CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator);
236 pi->gestalt = gestalt;
237 CFRetain(pi->gestalt);
238
239 pi->version = SOSPeerInfoGetPeerProtocolVersion(pi);
240 CFDataRef publicBytes = NULL;
241 CFDataRef octagonPeerSigningPublicBytes = NULL;
242 CFDataRef octagonPeerEncryptionPublicBytes = NULL;
243 CFNumberRef versionNumber = NULL;
244
245 SecKeyRef publicKey = SecKeyCreatePublicFromPrivate(signingKey);
246 if (publicKey == NULL) {
247 SOSCreateError(kSOSErrorBadKey, CFSTR("Unable to get public"), NULL, error);
248 CFReleaseNull(pi);
249 goto exit;
250 }
251
252 OSStatus result = SecKeyCopyPublicBytes(publicKey, &publicBytes);
253
254 if (result != errSecSuccess) {
255 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
256 CFReleaseNull(pi);
257 goto exit;
258 }
259
260 if (octagonPeerSigningKey) {
261 SecKeyRef octagonPeerSigningPublicKey = SecKeyCreatePublicFromPrivate(octagonPeerSigningKey);
262 if (octagonPeerSigningPublicKey == NULL) {
263 SOSCreateError(kSOSErrorBadKey, CFSTR("Unable to get public key"), NULL, error);
264 CFReleaseNull(pi);
265 goto exit;
266 }
267
268 result = SecKeyCopyPublicBytes(octagonPeerSigningPublicKey, &octagonPeerSigningPublicBytes);
269 CFReleaseNull(octagonPeerSigningPublicKey);
270 if (result != errSecSuccess) {
271 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
272 CFReleaseNull(pi);
273 goto exit;
274 }
275 }
276
277 if (octagonPeerEncryptionKey) {
278 SecKeyRef octagonPeerEncryptionPublicKey = SecKeyCreatePublicFromPrivate(octagonPeerEncryptionKey);
279 if (octagonPeerEncryptionPublicKey == NULL) {
280 SOSCreateError(kSOSErrorBadKey, CFSTR("Unable to get public key"), NULL, error);
281 CFReleaseNull(pi);
282 goto exit;
283 }
284
285 result = SecKeyCopyPublicBytes(octagonPeerEncryptionPublicKey, &octagonPeerEncryptionPublicBytes);
286 CFReleaseNull(octagonPeerEncryptionPublicKey);
287 if (result != errSecSuccess) {
288 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
289 CFReleaseNull(pi);
290 goto exit;
291 }
292 }
293
294 pi->signature = CFDataCreateMutable(allocator, 0);
295
296 versionNumber = CFNumberCreateWithCFIndex(NULL, pi->version);
297
298 pi->description = CFDictionaryCreateMutableForCFTypesWith(allocator,
299 sVersionKey, versionNumber,
300 sPublicKeyKey, publicBytes,
301 sGestaltKey, pi->gestalt,
302 NULL);
303 if (octagonPeerSigningPublicBytes) {
304 CFDictionarySetValue(pi->description, sOctagonPeerSigningPublicKeyKey, octagonPeerSigningPublicBytes);
305 }
306 if (octagonPeerEncryptionPublicBytes) {
307 CFDictionarySetValue(pi->description, sOctagonPeerEncryptionPublicKeyKey, octagonPeerEncryptionPublicBytes);
308 }
309
310
311 description_modifier(pi->description);
312
313 pi->peerID = SOSCopyIDOfKey(publicKey, error);
314
315 pi->verifiedAppKeyID = NULL;
316 pi->verifiedResult = false;
317
318 require_quiet(pi->peerID, exit);
319
320 // ================ V2 Additions Start
321
322 if(!SOSPeerInfoUpdateToV2(pi, error)) {
323 CFReleaseNull(pi);
324 goto exit;
325 }
326
327 // V2DictionarySetValue handles NULL as remove
328 if (backup_key != NULL) SOSPeerInfoV2DictionarySetValue(pi, sBackupKeyKey, backup_key);
329 SOSPeerInfoV2DictionarySetValue(pi, sViewsKey, enabledViews);
330
331 // ================ V2 Additions End
332
333 if (!SOSPeerInfoSign(signingKey, pi, error)) {
334 CFReleaseNull(pi);
335 goto exit;
336 }
337
338 exit:
339 CFReleaseNull(versionNumber);
340 CFReleaseNull(publicKey);
341 CFReleaseNull(publicBytes);
342 CFReleaseNull(octagonPeerSigningPublicBytes);
343 CFReleaseNull(octagonPeerEncryptionPublicBytes);
344 return pi;
345 }
346
347 SOSPeerInfoRef SOSPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, CFDataRef backup_key, SecKeyRef signingKey, SecKeyRef octagonPeerSigningKey, SecKeyRef octagonPeerEncryptionKey, CFErrorRef* error) {
348 return SOSPeerInfoCreate_Internal(allocator, gestalt, backup_key, NULL, NULL, NULL, NULL, NULL, NULL, signingKey, octagonPeerSigningKey, octagonPeerEncryptionKey, error, ^(CFMutableDictionaryRef description) {});
349 }
350
351 SOSPeerInfoRef SOSPeerInfoCreateWithTransportAndViews(CFAllocatorRef allocator, CFDictionaryRef gestalt, CFDataRef backup_key,
352 CFStringRef IDSID, CFStringRef transportType, CFBooleanRef preferIDS,
353 CFBooleanRef preferFragmentation, CFBooleanRef preferAckModel, CFSetRef enabledViews,
354 SecKeyRef signingKey,
355 SecKeyRef octagonPeerSigningKey,
356 SecKeyRef octagonPeerEncryptionKey,
357 CFErrorRef* error)
358 {
359 return SOSPeerInfoCreate_Internal(allocator, gestalt, backup_key, IDSID, transportType, preferIDS, preferFragmentation, preferAckModel, enabledViews, signingKey, octagonPeerSigningKey, octagonPeerEncryptionKey, error, ^(CFMutableDictionaryRef description) {});
360 }
361
362
363 SOSPeerInfoRef SOSPeerInfoCreateCloudIdentity(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) {
364 return SOSPeerInfoCreate_Internal(allocator, gestalt, NULL, NULL, NULL, NULL, NULL, NULL, NULL, signingKey, NULL, NULL, error, ^(CFMutableDictionaryRef description) {
365 CFDictionarySetValue(description, sCloudIdentityKey, kCFBooleanTrue);
366 });
367
368 }
369
370
371 SOSPeerInfoRef SOSPeerInfoCreateCopy(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFErrorRef* error) {
372 if(!toCopy) return NULL;
373 SOSPeerInfoRef pi = CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator);
374
375 pi->description = CFDictionaryCreateMutableCopy(allocator, 0, toCopy->description);
376 pi->signature = CFDataCreateCopy(allocator, toCopy->signature);
377
378 pi->gestalt = CFDictionaryCreateCopy(allocator, toCopy->gestalt);
379 pi->peerID = CFStringCreateCopy(allocator, toCopy->peerID);
380 pi->verifiedAppKeyID = NULL; // The peer resulting from this will need to be re-evaluated for an application signature.
381 pi->verifiedResult = false;
382
383 pi->version = toCopy->version;
384 if(!SOSPeerInfoVersionHasV2Data(pi)) SOSPeerInfoExpandV2Data(pi, error);
385
386 return pi;
387 }
388
389
390 bool SOSPeerInfoVersionIsCurrent(SOSPeerInfoRef pi) {
391 return pi->version >= PEERINFO_CURRENT_VERSION;
392 }
393
394 bool SOSPeerInfoVersionHasV2Data(SOSPeerInfoRef pi) {
395 return pi->version >= kSOSPeerV2BaseVersion;
396 }
397
398 SOSPeerInfoRef SOSPeerInfoCreateCurrentCopy(CFAllocatorRef allocator, SOSPeerInfoRef toCopy,
399 CFStringRef IDSID, CFStringRef transportType, CFBooleanRef preferIDS, CFBooleanRef preferFragmentation, CFBooleanRef preferAckModel, CFSetRef enabledViews,
400 SecKeyRef signingKey, CFErrorRef* error) {
401
402 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, toCopy, error);
403 if(!SOSPeerInfoVersionHasV2Data(pi)) SOSPeerInfoUpdateToV2(pi, error);
404
405 if (enabledViews) {
406 SOSPeerInfoV2DictionarySetValue(pi, sViewsKey, enabledViews);
407 }
408
409 if(!SOSPeerInfoSign(signingKey, pi, error)) {
410 CFReleaseNull(pi);
411 }
412
413 return pi;
414 }
415
416
417 static SOSPeerInfoRef SOSPeerInfoCopyWithModification(CFAllocatorRef allocator, SOSPeerInfoRef original,
418 SecKeyRef signingKey, CFErrorRef *error,
419 bool (^modification)(SOSPeerInfoRef peerToModify, CFErrorRef *error)) {
420
421 SOSPeerInfoRef result = NULL;
422 SOSPeerInfoRef copy = SOSPeerInfoCreateCopy(allocator, original, error);
423
424 require_quiet(modification(copy, error), fail);
425
426 require_quiet(SOSPeerInfoSign(signingKey, copy, error), fail);
427
428 CFTransferRetained(result, copy);
429
430 fail:
431 CFReleaseNull(copy);
432 return result;
433
434 }
435
436 SOSPeerInfoRef SOSPeerInfoCopyWithGestaltUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) {
437 return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
438 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
439 if(!gestalt || !peerToModify) return false;
440 CFRetainAssign(peerToModify->gestalt, gestalt);
441 CFDictionarySetValue(peerToModify->description, sGestaltKey, peerToModify->gestalt);
442 return true;
443
444 });
445 }
446
447
448 SOSPeerInfoRef SOSPeerInfoCopyWithBackupKeyUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDataRef backupKey, SecKeyRef signingKey, CFErrorRef *error) {
449 return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
450 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
451 if (backupKey != NULL)
452 SOSPeerInfoV2DictionarySetValue(peerToModify, sBackupKeyKey, backupKey);
453 else
454 SOSPeerInfoV2DictionaryRemoveValue(peerToModify, sBackupKeyKey);
455 return true;
456 });
457 }
458
459 static CFDictionaryRef SOSPeerInfoUpdateAndCopyRecord(SOSPeerInfoRef peer, CFStringRef dsid, CFDictionaryRef escrowRecord){
460
461 CFMutableDictionaryRef existingEscrowRecords = SOSPeerInfoCopyEscrowRecord(peer);
462
463 if(escrowRecord == NULL && existingEscrowRecords != NULL)
464 {
465 CFDictionaryRemoveValue(existingEscrowRecords, dsid);
466 return existingEscrowRecords;
467 }
468
469 if(existingEscrowRecords == NULL)
470 existingEscrowRecords = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
471
472 CFDictionarySetValue(existingEscrowRecords, dsid, escrowRecord);
473
474 return existingEscrowRecords;
475 }
476
477
478 SOSPeerInfoRef SOSPeerInfoCopyWithEscrowRecordUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFStringRef dsid, CFDictionaryRef escrowRecord, SecKeyRef signingKey, CFErrorRef *error) {
479 return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
480 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
481
482 CFDictionaryRef updatedEscrowRecords = SOSPeerInfoUpdateAndCopyRecord(peerToModify, dsid, escrowRecord);
483 SOSPeerInfoV2DictionarySetValue(peerToModify, sEscrowRecord, updatedEscrowRecords);
484 CFReleaseNull(updatedEscrowRecords);
485 return true;
486 });
487 }
488
489 SOSPeerInfoRef SOSPeerInfoCopyWithReplacedEscrowRecords(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef escrowRecords, SecKeyRef signingKey, CFErrorRef *error) {
490 return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
491 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
492 if(escrowRecords != NULL)
493 SOSPeerInfoV2DictionarySetValue(peerToModify, sEscrowRecord, escrowRecords);
494
495 return true;
496 });
497 }
498
499 CFDataRef SOSPeerInfoCopyBackupKey(SOSPeerInfoRef peer) {
500 return SOSPeerInfoV2DictionaryCopyData(peer, sBackupKeyKey);
501 }
502
503 CFMutableDictionaryRef SOSPeerInfoCopyEscrowRecord(SOSPeerInfoRef peer){
504 return SOSPeerInfoV2DictionaryCopyDictionary(peer, sEscrowRecord);
505 }
506
507 bool SOSPeerInfoHasBackupKey(SOSPeerInfoRef peer) {
508 CFDataRef bk = SOSPeerInfoCopyBackupKey(peer);
509 bool success = bk != NULL;
510 CFReleaseNull(bk);
511 return success;
512 }
513
514 SOSPeerInfoRef SOSPeerInfoCopyWithViewsChange(CFAllocatorRef allocator, SOSPeerInfoRef toCopy,
515 SOSViewActionCode action, CFStringRef viewname, SOSViewResultCode *retval,
516 SecKeyRef signingKey, CFErrorRef* error) {
517 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, toCopy, error);
518 if(action == kSOSCCViewEnable) {
519 *retval = SOSViewsEnable(pi, viewname, error);
520 require((kSOSCCViewMember == *retval), exit);
521 } else if(action == kSOSCCViewDisable) {
522 *retval = SOSViewsDisable(pi, viewname, error);
523 require((kSOSCCViewNotMember == *retval), exit);
524 }
525
526 require_action_quiet(SOSPeerInfoSign(signingKey, pi, error), exit, *retval = kSOSCCGeneralViewError);
527 return pi;
528
529 exit:
530 CFReleaseNull(pi);
531 return NULL;
532 }
533
534
535 CFStringRef sPingKey = CFSTR("Ping");
536
537 SOSPeerInfoRef SOSPeerInfoCopyWithPing(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, SecKeyRef signingKey, CFErrorRef* error) {
538 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, toCopy, error);
539 CFDataRef ping = CFDataCreateWithRandomBytes(8);
540 SOSPeerInfoV2DictionarySetValue(pi, sPingKey, ping);
541 SecKeyRef pub_key = SOSPeerInfoCopyPubKey(pi, error);
542 require_quiet(pub_key, exit);
543 pi->peerID = SOSCopyIDOfKey(pub_key, error);
544 require_quiet(pi->peerID, exit);
545 require_action_quiet(SOSPeerInfoSign(signingKey, pi, error), exit, CFReleaseNull(pi));
546 exit:
547 CFReleaseNull(ping);
548 CFReleaseNull(pub_key);
549 return pi;
550 }
551
552
553 SOSViewResultCode SOSPeerInfoViewStatus(SOSPeerInfoRef pi, CFStringRef view, CFErrorRef *error) {
554 return SOSViewsQuery(pi, view, error);
555 }
556
557 static void SOSPeerInfoDestroy(CFTypeRef aObj) {
558 SOSPeerInfoRef pi = (SOSPeerInfoRef) aObj;
559
560 if(!pi) return;
561 CFReleaseNull(pi->description);
562 CFReleaseNull(pi->signature);
563 CFReleaseNull(pi->gestalt);
564 CFReleaseNull(pi->peerID);
565 CFReleaseNull(pi->v2Dictionary);
566 CFReleaseNull(pi->verifiedAppKeyID);
567 pi->verifiedResult = false;
568 }
569
570 static Boolean SOSPeerInfoCompare(CFTypeRef lhs, CFTypeRef rhs) {
571 SOSPeerInfoRef lpeer = (SOSPeerInfoRef) lhs;
572 SOSPeerInfoRef rpeer = (SOSPeerInfoRef) rhs;
573 if(!lpeer || !rpeer) return false;
574 return CFEqualSafe(lpeer->description, rpeer->description) && CFEqualSafe(lpeer->signature, rpeer->signature);
575 }
576
577
578 CFComparisonResult SOSPeerInfoCompareByID(const void *val1, const void *val2, void *context) {
579 // The code below is necessary but not sufficient; not returning a CFComparisonResult
580 // It probably is OK to say that a NULL is < <non-NULL>
581 if (val1 == NULL || val2 == NULL) {
582 ptrdiff_t dv = val1 - val2;
583 return dv < 0 ? kCFCompareLessThan : dv == 0 ? kCFCompareEqualTo : kCFCompareGreaterThan;
584 }
585
586 CFStringRef v1 = SOSPeerInfoGetPeerID((SOSPeerInfoRef) val1);
587 CFStringRef v2 = SOSPeerInfoGetPeerID((SOSPeerInfoRef) val2);
588 if (v1 == NULL || v2 == NULL) {
589 ptrdiff_t dv = (const void *)v1 - (const void *)v2;
590 return dv < 0 ? kCFCompareLessThan : dv == 0 ? kCFCompareEqualTo : kCFCompareGreaterThan;
591 }
592
593 return CFStringCompare(v1, v2, 0);
594 }
595
596 static CFHashCode SOSPeerInfoHash(CFTypeRef cf) {
597 SOSPeerInfoRef peer = (SOSPeerInfoRef) cf;
598
599 return CFHash(peer->description) ^ CFHash(peer->signature);
600 }
601
602
603 static char boolToChars(bool val, char truechar, char falsechar) {
604 return val? truechar: falsechar;
605 }
606
607 static CFStringRef isKnown(CFStringRef ref) {
608 return ref? ref: CFSTR("Unknown ");
609 }
610
611 static CFStringRef copyDescriptionWithFormatOptions(CFTypeRef aObj, CFDictionaryRef formatOptions){
612
613 SOSPeerInfoRef pi = (SOSPeerInfoRef) aObj;
614 if(!pi) return NULL;
615
616 CFStringRef description = NULL;
617 // Get the format options we care about:
618 bool retired = SOSPeerInfoIsRetirementTicket(pi);
619 bool selfValid = SOSPeerInfoVerify(pi, NULL);
620 bool backingUp = SOSPeerInfoHasBackupKey(pi);
621 bool isKVS = SOSPeerInfoKVSOnly(pi);
622 CFStringRef osVersion = CFDictionaryGetValue(pi->gestalt, kPIOSVersionKey);
623 CFStringRef tmp = SOSPeerInfoV2DictionaryCopyString(pi, sDeviceID);
624 CFStringRef deviceID = CFStringCreateTruncatedCopy(tmp, 8);
625 CFReleaseNull(tmp);
626 CFStringRef serialNum = SOSPeerInfoCopySerialNumber(pi);
627 CFStringRef peerID = CFStringCreateTruncatedCopy(SOSPeerInfoGetPeerID(pi), 8);
628
629 // Calculate the truncated length
630
631 CFStringRef objectPrefix = CFStringCreateWithFormat(kCFAllocatorDefault, formatOptions, CFSTR("PI@%p"), pi);
632
633 description = CFStringCreateWithFormat(kCFAllocatorDefault, formatOptions,
634 CFSTR("<%@: [name: %20@] [%c%c%c%c%c%c%c] [type: %-20@] [spid: %8@] [os: %10@] [devid: %10@] [serial: %12@]"),
635 objectPrefix,
636 isKnown(SOSPeerInfoGetPeerName(pi)),
637 '-',
638 '-',
639 boolToChars(selfValid, 'S', 's'),
640 boolToChars(retired, 'R', 'r'),
641 boolToChars(backingUp, 'B', 'b'),
642 boolToChars(isKVS, 'K', 'I'),
643 '-',
644 isKnown(SOSPeerInfoGetPeerDeviceType(pi)), isKnown(peerID),
645 isKnown(osVersion), isKnown(deviceID), isKnown(serialNum));
646
647 CFReleaseNull(peerID);
648 CFReleaseNull(deviceID);
649 CFReleaseNull(serialNum);
650 CFReleaseNull(objectPrefix);
651
652 return description;
653 }
654
655 static CFStringRef SOSPeerInfoCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) {
656
657 CFStringRef description = NULL;
658
659 description = copyDescriptionWithFormatOptions(aObj, formatOptions);
660
661 return description;
662 }
663
664 void SOSPeerInfoLogState(char *category, SOSPeerInfoRef pi, SecKeyRef pubKey, CFStringRef myPID, char sigchr) {
665 if(!pi) return;
666 bool appValid = SOSPeerInfoApplicationVerify(pi, pubKey, NULL);
667 bool retired = SOSPeerInfoIsRetirementTicket(pi);
668 // We won't inflate invalid peerInfos. Mark this true to keep scanning utilities from breaking.
669 bool selfValid = true;
670 bool backingUp = SOSPeerInfoHasBackupKey(pi);
671 bool isMe = CFEqualSafe(SOSPeerInfoGetPeerID(pi), myPID) == true;
672 bool isKVS = SOSPeerInfoKVSOnly(pi);
673 CFStringRef osVersion = CFDictionaryGetValue(pi->gestalt, kPIOSVersionKey);
674 CFStringRef tmp = SOSPeerInfoV2DictionaryCopyString(pi, sDeviceID);
675 CFStringRef deviceID = CFStringCreateTruncatedCopy(tmp, 8);
676 CFReleaseNull(tmp);
677 CFStringRef serialNum = SOSPeerInfoCopySerialNumber(pi);
678 CFStringRef peerID = CFStringCreateTruncatedCopy(SOSPeerInfoGetPeerID(pi), 8);
679
680 secnotice(category, "PI: [name: %-20@] [%c%c%c%c%c%c%c] [type: %-20@] [spid: %8@] [os: %10@] [devid: %10@] [serial: %12@]", isKnown(SOSPeerInfoGetPeerName(pi)),
681 boolToChars(isMe, 'M', 'm'),
682 boolToChars(appValid, 'A', 'a'),
683 boolToChars(selfValid, 'S', 's'),
684 boolToChars(retired, 'R', 'r'),
685 boolToChars(backingUp, 'B', 'b'),
686 boolToChars(isKVS, 'K', 'I'),
687 sigchr,
688 isKnown(SOSPeerInfoGetPeerDeviceType(pi)), isKnown(peerID),
689 isKnown(osVersion), isKnown(deviceID), isKnown(serialNum));
690
691 CFReleaseNull(peerID);
692 CFReleaseNull(deviceID);
693 CFReleaseNull(serialNum);
694 }
695
696 CFDictionaryRef SOSPeerInfoCopyPeerGestalt(SOSPeerInfoRef pi) {
697 CFRetain(pi->gestalt);
698 return pi->gestalt;
699 }
700
701 CFDictionaryRef SOSPeerGetGestalt(SOSPeerInfoRef pi){
702 return pi->gestalt;
703 }
704
705 CFStringRef SOSPeerInfoGetPeerName(SOSPeerInfoRef peer) {
706 return SOSPeerInfoLookupGestaltValue(peer, kPIUserDefinedDeviceNameKey);
707 }
708
709 CFStringRef SOSPeerInfoGetPeerDeviceType(SOSPeerInfoRef peer) {
710 return SOSPeerInfoLookupGestaltValue(peer, kPIDeviceModelNameKey);
711 }
712
713 CFIndex SOSPeerInfoGetPeerProtocolVersion(SOSPeerInfoRef peer) {
714 CFIndex version = PEERINFO_CURRENT_VERSION;
715 CFTypeRef val = SOSPeerInfoLookupGestaltValue(peer, kPIMessageProtocolVersionKey);
716 if (val && CFGetTypeID(val) == CFNumberGetTypeID())
717 CFNumberGetValue(val, kCFNumberCFIndexType, &version);
718 return version;
719 }
720
721 CFTypeRef SOSPeerInfoLookupGestaltValue(SOSPeerInfoRef pi, CFStringRef key) {
722 return CFDictionaryGetValue(pi->gestalt, key);
723 }
724
725 CFStringRef SOSPeerInfoGetPeerID(SOSPeerInfoRef pi) {
726 return pi ? pi->peerID : NULL;
727 }
728
729 bool SOSPeerInfoPeerIDEqual(SOSPeerInfoRef pi, CFStringRef myPeerID) {
730 return CFEqualSafe(myPeerID, SOSPeerInfoGetPeerID(pi));
731 }
732
733 CFIndex SOSPeerInfoGetVersion(SOSPeerInfoRef pi) {
734 return pi->version;
735 }
736
737 bool SOSPeerInfoUpdateDigestWithPublicKeyBytes(SOSPeerInfoRef peer, const struct ccdigest_info *di,
738 ccdigest_ctx_t ctx, CFErrorRef *error) {
739 CFDataRef pubKeyBytes = CFDictionaryGetValue(peer->description, sPublicKeyKey);
740
741 if(!pubKeyBytes) {
742 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Digest failed – no public key"));
743 return false;
744 }
745
746 ccdigest_update(di, ctx, CFDataGetLength(pubKeyBytes), CFDataGetBytePtr(pubKeyBytes));
747
748 return true;
749 }
750
751 bool SOSPeerInfoUpdateDigestWithDescription(SOSPeerInfoRef peer, const struct ccdigest_info *di,
752 ccdigest_ctx_t ctx, CFErrorRef *error) {
753 if(SOSPeerInfoVersionHasV2Data(peer)) SOSPeerInfoPackV2Data(peer);
754 size_t description_size = der_sizeof_plist(peer->description, error);
755 if (description_size == 0) {
756 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Description length failed"));
757 return false;
758 }
759
760 uint8_t * data = malloc(description_size);
761 if (data == NULL) {
762 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Description alloc failed"));
763 return false;
764 }
765 uint8_t *data_end = data + description_size;
766 uint8_t *encoded = der_encode_plist(peer->description, error, data, data_end);
767
768 if(!encoded) {
769 free(data);
770 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Description encode failed"));
771 return false;
772 }
773
774 ccdigest_update(di, ctx, description_size, data);
775
776 free(data);
777
778 return true;
779 }
780
781
782 static CFDataRef sosCreateDate() {
783 CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
784 size_t bufsiz = der_sizeof_date(now, NULL);
785 uint8_t buf[bufsiz];
786 der_encode_date(now, NULL, buf, buf+bufsiz);
787 CFReleaseNull(now);
788 return CFDataCreate(NULL, buf, bufsiz);
789 }
790
791 static CFDateRef sosCreateCFDate(CFDataRef sosdate) {
792 CFDateRef date;
793 der_decode_date(NULL, 0, &date, NULL, CFDataGetBytePtr(sosdate),
794 CFDataGetBytePtr(sosdate) + CFDataGetLength(sosdate));
795 return date;
796 }
797
798 static bool sospeer_application_hash(SOSPeerInfoRef pi, const struct ccdigest_info *di, uint8_t *hbuf) {
799 CFDataRef appdate = asData(CFDictionaryGetValue(pi->description, sApplicationDate), NULL);
800 if(!appdate) return false;
801 ccdigest_di_decl(di, ctx);
802 ccdigest_init(di, ctx);
803 ccdigest_update(di, ctx, CFDataGetLength(appdate), CFDataGetBytePtr(appdate));
804 if (!SOSPeerInfoUpdateDigestWithPublicKeyBytes(pi, di, ctx, NULL)) return false;
805 ccdigest_final(di, ctx, hbuf);
806 return true;
807 }
808
809 SOSPeerInfoRef SOSPeerInfoCopyAsApplication(SOSPeerInfoRef original, SecKeyRef userkey, SecKeyRef peerkey, CFErrorRef *error) {
810 SOSPeerInfoRef result = NULL;
811 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, original, error);
812
813 const struct ccdigest_info *di = ccsha256_di();
814 uint8_t hbuf[di->output_size];
815 CFDataRef usersig = NULL;
816
817 CFDataRef creationDate = sosCreateDate();
818 CFDictionarySetValue(pi->description, sApplicationDate, creationDate);
819 CFReleaseNull(creationDate);
820
821 // Create User Application Signature
822 require_action_quiet(sospeer_application_hash(pi, di, hbuf), fail,
823 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to create hash for peer applicant"), NULL, error));
824
825 usersig = sosCopySignedHash(userkey, di, hbuf);
826 require_action_quiet(usersig, fail,
827 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to sign public key hash for peer"), NULL, error));
828
829 CFDictionarySetValue(pi->description, sApplicationUsig, usersig);
830 pi->verifiedAppKeyID = SOSCopyIDOfKey(userkey, error);
831 if(pi->verifiedAppKeyID == NULL) {
832 secnotice("PICache", "failed to get userKeyID");
833 } else {
834 pi->verifiedResult = true;
835 }
836 require_quiet(SOSPeerInfoSign(peerkey, pi, error), fail);
837
838 result = pi;
839 pi = NULL;
840
841 fail:
842 CFReleaseNull(usersig);
843 CFReleaseNull(pi);
844 return result;
845 }
846
847 bool SOSPeerInfoApplicationVerify(SOSPeerInfoRef pi, SecKeyRef userkey, CFErrorRef *error) {
848 const struct ccdigest_info *di = ccsha256_di();
849 uint8_t hbuf[di->output_size];
850 bool result = false;
851 CFStringRef userKeyID = NULL;
852
853 // If we've already succeeded signature check with this key move on.
854 require_action_quiet(userkey, exit, SOSErrorCreate(kSOSErrorNoKey, error, NULL, CFSTR("Can't validate PeerInfos with no userKey")));
855 userKeyID = SOSCopyIDOfKey(userkey, error);
856 require_action_quiet(!CFEqualSafe(userKeyID, pi->verifiedAppKeyID), exit, result = pi->verifiedResult);
857
858 // verifiedAppKeyID was NULL or not the key we're looking for - clear it.
859 CFReleaseNull(pi->verifiedAppKeyID);
860 pi->verifiedResult = false;
861 CFDataRef usig = CFDictionaryGetValue(pi->description, sApplicationUsig);
862 require_action_quiet(usig, exit,
863 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Peer is not an applicant"), NULL, error));
864 // Verify User Application Signature
865 require_action_quiet(sospeer_application_hash(pi, di, hbuf), exit,
866 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to create hash for peer applicant"), NULL, error));
867 require_action_quiet(sosVerifyHash(userkey, di, hbuf, usig), exit,
868 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("user signature of public key hash fails to verify"), NULL, error));
869 // Remember the userkey we validated for this peerinfo.
870 pi->verifiedAppKeyID = CFStringCreateCopy(kCFAllocatorDefault, userKeyID);
871 pi->verifiedResult = true;
872
873 result = SOSPeerInfoVerify(pi, error);
874
875 exit:
876 CFReleaseNull(userKeyID);
877 return result;
878 }
879
880
881 static CF_RETURNS_RETAINED CFDateRef sosPeerInfoGetDate(SOSPeerInfoRef pi, CFStringRef entry) {
882 if(!pi) return NULL;
883 CFDataRef sosdate = CFDictionaryGetValue(pi->description, entry);
884 if(!sosdate) return NULL;
885 CFDateRef date = sosCreateCFDate(sosdate);
886
887 return date;
888 }
889
890 CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetApplicationDate(SOSPeerInfoRef pi) {
891 return sosPeerInfoGetDate(pi, sApplicationDate);
892 }
893
894 CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetRetirementDate(SOSPeerInfoRef pi) {
895 return sosPeerInfoGetDate(pi, sRetirementDate);
896 }
897
898
899
900 //
901 // Gestalt helpers
902 //
903
904 CFStringRef SOSPeerGestaltGetName(CFDictionaryRef gestalt) {
905 CFStringRef name = SOSPeerGestaltGetAnswer(gestalt, kPIUserDefinedDeviceNameKey);
906 return isString(name) ? name : NULL;
907 }
908
909 CFTypeRef SOSPeerGestaltGetAnswer(CFDictionaryRef gestalt, CFStringRef question) {
910 return gestalt ? CFDictionaryGetValue(gestalt, question) : NULL;
911 }
912
913 //
914 // Peer Retirement
915 //
916
917
918 SOSPeerInfoRef SOSPeerInfoCreateRetirementTicket(CFAllocatorRef allocator, SecKeyRef privKey, SOSPeerInfoRef peer, CFErrorRef *error) {
919 // Copy PeerInfo
920 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, peer, error);
921
922 require(pi, fail);
923
924 // Fill out Resignation Date
925 CFDataRef resignationDate = sosCreateDate();
926 CFDictionaryAddValue(pi->description, sRetirementDate, resignationDate);
927 CFReleaseNull(resignationDate);
928
929 require(SOSPeerInfoSign(privKey, pi, error), fail);
930
931 return pi;
932
933 fail:
934 CFReleaseNull(pi);
935 return NULL;
936 }
937
938 CFStringRef SOSPeerInfoInspectRetirementTicket(SOSPeerInfoRef pi, CFErrorRef *error) {
939 CFStringRef retval = NULL;
940 CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
941 CFDateRef retirement = NULL;
942
943 require_quiet(SOSPeerInfoVerify(pi, error), err);
944
945 retirement = sosCreateCFDate(CFDictionaryGetValue(pi->description, sRetirementDate));
946 require_action_quiet(retirement, err,
947 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Peer is not retired"), NULL, error));
948
949 require_action_quiet(CFDateCompare(now, retirement, NULL) == kCFCompareGreaterThan, err,
950 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Retirement date is after current date"), NULL, error));
951
952 retval = SOSPeerInfoGetPeerID(pi);
953
954 err:
955 CFReleaseNull(now);
956 CFReleaseNull(retirement);
957 return retval;
958 }
959
960 bool SOSPeerInfoRetireRetirementTicket(size_t max_seconds, SOSPeerInfoRef pi) {
961 CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
962 CFDateRef retirement = sosCreateCFDate(CFDictionaryGetValue(pi->description, sRetirementDate));
963 CFTimeInterval timediff = CFDateGetTimeIntervalSinceDate(now, retirement); // diff in seconds
964 CFReleaseNull(now);
965 CFReleaseNull(retirement);
966 if(timediff > (max_seconds)) return true;
967 return false;
968 }
969
970 bool SOSPeerInfoIsRetirementTicket(SOSPeerInfoRef pi) {
971 CFDataRef flag = CFDictionaryGetValue(pi->description, sRetirementDate);
972 return flag != NULL;
973 }
974
975 bool SOSPeerInfoIsCloudIdentity(SOSPeerInfoRef pi) {
976 CFTypeRef value = CFDictionaryGetValue(pi->description, sCloudIdentityKey);
977 return CFEqualSafe(value, kCFBooleanTrue);
978 }
979
980 SOSPeerInfoRef SOSPeerInfoUpgradeSignatures(CFAllocatorRef allocator, SecKeyRef privKey, SecKeyRef peerKey, SOSPeerInfoRef peer, CFErrorRef *error) {
981 SecKeyRef pubKey = SecKeyCreatePublicFromPrivate(privKey);
982 SOSPeerInfoRef retval = NULL;
983
984 retval = SOSPeerInfoCopyAsApplication(peer, privKey, peerKey, error);
985 CFReleaseNull(pubKey);
986 return retval;
987 }
988
989 static SOSPeerInfoRef CF_RETURNS_RETAINED
990 SOSPeerInfoSetOctagonKey(CFAllocatorRef allocator,
991 SOSPeerInfoRef toCopy,
992 CFStringRef descriptionKey,
993 SecKeyRef octagonKey,
994 SecKeyRef signingKey,
995 CFErrorRef *error)
996 {
997 CFDataRef publicKeyBytes = NULL;
998 SOSPeerInfoRef pi = NULL;
999
1000 OSStatus copyResult = SecKeyCopyPublicBytes(octagonKey, &publicKeyBytes);
1001 require_action_quiet(0 == copyResult, fail, SecError(copyResult, error, CFSTR("failed to copy public key bytes")));
1002
1003 pi = SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
1004 ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
1005 if(peerToModify && peerToModify->description && publicKeyBytes && descriptionKey) {
1006 CFDictionarySetValue(peerToModify->description, descriptionKey, publicKeyBytes);
1007 } else {
1008 SecError(errSecParam, error, CFSTR("Bad key bytes or dictionary key"));
1009 }
1010 return true;
1011 });
1012 require(pi, fail);
1013
1014 fail:
1015 CFReleaseNull(publicKeyBytes);
1016 return pi;
1017 }
1018
1019 SOSPeerInfoRef CF_RETURNS_RETAINED
1020 SOSPeerInfoSetOctagonSigningKey(CFAllocatorRef allocator,
1021 SOSPeerInfoRef toCopy,
1022 SecKeyRef octagonSigningKey,
1023 SecKeyRef signingKey,
1024 CFErrorRef *error)
1025 {
1026 return SOSPeerInfoSetOctagonKey(allocator, toCopy, sOctagonPeerSigningPublicKeyKey, octagonSigningKey, signingKey, error);
1027 }
1028
1029 SOSPeerInfoRef CF_RETURNS_RETAINED
1030 SOSPeerInfoSetOctagonEncryptionKey(CFAllocatorRef allocator,
1031 SOSPeerInfoRef toCopy,
1032 SecKeyRef octagonEncryptionKey,
1033 SecKeyRef signingKey,
1034 CFErrorRef *error)
1035 {
1036 return SOSPeerInfoSetOctagonKey(allocator, toCopy, sOctagonPeerEncryptionPublicKeyKey, octagonEncryptionKey, signingKey, error);
1037 }
1038
1039 CFStringRef SOSPeerInfoCopyTransportType(SOSPeerInfoRef peer){
1040 CFStringRef transportType = (CFStringRef)SOSPeerInfoV2DictionaryCopyString(peer, sTransportType);
1041 return (transportType ? transportType : CFRetain(SOSTransportMessageTypeKVS));
1042 }
1043
1044 bool SOSPeerInfoKVSOnly(SOSPeerInfoRef pi) {
1045 CFStringRef transportType = SOSPeerInfoCopyTransportType(pi);
1046 bool retval = CFEqualSafe(transportType, SOSTransportMessageTypeKVS);
1047 CFReleaseNull(transportType);
1048 return retval;
1049 }
1050
1051 CFStringRef SOSPeerInfoCopyDeviceID(SOSPeerInfoRef peer){
1052 return CFSTR("not implemented");
1053 }
1054
1055 SOSPeerInfoDeviceClass SOSPeerInfoGetClass(SOSPeerInfoRef pi) {
1056 static CFDictionaryRef devID2Class = NULL;
1057 static dispatch_once_t onceToken = 0;
1058
1059 dispatch_once(&onceToken, ^{
1060 CFNumberRef cfSOSPeerInfo_macOS = CFNumberCreateWithCFIndex(kCFAllocatorDefault, SOSPeerInfo_macOS);
1061 CFNumberRef cfSOSPeerInfo_iOS = CFNumberCreateWithCFIndex(kCFAllocatorDefault, SOSPeerInfo_iOS);
1062 CFNumberRef cfSOSPeerInfo_iCloud = CFNumberCreateWithCFIndex(kCFAllocatorDefault, SOSPeerInfo_iCloud);
1063 // CFNumberRef cfSOSPeerInfo_watchOS = CFNumberCreateWithCFIndex(kCFAllocatorDefault, SOSPeerInfo_watchOS);
1064 // CFNumberRef cfSOSPeerInfo_tvOS = CFNumberCreateWithCFIndex(kCFAllocatorDefault, SOSPeerInfo_tvOS);
1065
1066 devID2Class = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1067 CFSTR("Mac Pro"), cfSOSPeerInfo_macOS,
1068 CFSTR("MacBook"), cfSOSPeerInfo_macOS,
1069 CFSTR("MacBook Pro"), cfSOSPeerInfo_macOS,
1070 CFSTR("iCloud"), cfSOSPeerInfo_iCloud,
1071 CFSTR("iMac"), cfSOSPeerInfo_macOS,
1072 CFSTR("iPad"), cfSOSPeerInfo_iOS,
1073 CFSTR("iPhone"), cfSOSPeerInfo_iOS,
1074 CFSTR("iPod touch"), cfSOSPeerInfo_iOS,
1075 NULL);
1076 CFReleaseNull(cfSOSPeerInfo_macOS);
1077 CFReleaseNull(cfSOSPeerInfo_iOS);
1078 CFReleaseNull(cfSOSPeerInfo_iCloud);
1079 });
1080 SOSPeerInfoDeviceClass retval = SOSPeerInfo_unknown;
1081 CFStringRef dt = SOSPeerInfoGetPeerDeviceType(pi);
1082 require_quiet(dt, errOut);
1083 CFNumberRef classNum = CFDictionaryGetValue(devID2Class, dt);
1084 require_quiet(classNum, errOut);
1085 CFIndex tmp;
1086 require_quiet(CFNumberGetValue(classNum, kCFNumberCFIndexType, &tmp), errOut);
1087 retval = (SOSPeerInfoDeviceClass) tmp;
1088 errOut:
1089 return retval;
1090 }
1091
1092 bool SOSPeerInfoHasOctagonSigningPubKey(SOSPeerInfoRef peer){
1093 bool hasKey = false;
1094 SecKeyRef pubKey = SOSPeerInfoCopyOctagonSigningPublicKey(peer, NULL);
1095 if(pubKey)
1096 hasKey = true;
1097 CFReleaseNull(pubKey);
1098 return hasKey;
1099 }
1100
1101 bool SOSPeerInfoHasOctagonEncryptionPubKey(SOSPeerInfoRef peer){
1102 bool hasKey = false;
1103 SecKeyRef pubKey = SOSPeerInfoCopyOctagonEncryptionPublicKey(peer, NULL);
1104 if(pubKey)
1105 hasKey = true;
1106 CFReleaseNull(pubKey);
1107 return hasKey;
1108 }