]> git.saurik.com Git - apple/security.git/blob - sec/SOSCircle/SecureObjectSync/SOSPeerInfo.c
Security-55471.tar.gz
[apple/security.git] / sec / SOSCircle / SecureObjectSync / SOSPeerInfo.c
1 //
2 // SOSPeerInfo.c
3 // sec
4 //
5 // Created by Mitch Adler on 7/19/12.
6 //
7 //
8
9 #include <AssertMacros.h>
10 #include <TargetConditionals.h>
11
12 #include <SecureObjectSync/SOSPeerInfo.h>
13 #include <SecureObjectSync/SOSPeerInfoInternal.h>
14 #include <SecureObjectSync/SOSCircle.h>
15
16 #include <SecureObjectSync/SOSInternal.h>
17 #include <ipc/securityd_client.h>
18
19 #include "Imported/SecuritydXPC.h"
20
21 #include <CoreFoundation/CFArray.h>
22 #include <dispatch/dispatch.h>
23
24 #include <stdlib.h>
25 #include <assert.h>
26
27 #include <utilities/SecCFWrappers.h>
28 #include <utilities/SecCFRelease.h>
29 #include <utilities/SecCFError.h>
30 #include <utilities/SecXPCError.h>
31
32 #include <utilities/der_plist.h>
33 #include <utilities/der_plist_internal.h>
34 #include <corecrypto/ccder.h>
35 #include <utilities/der_date.h>
36
37 #include <corecrypto/ccdigest.h>
38 #include <corecrypto/ccsha2.h>
39
40
41 #include <CoreFoundation/CoreFoundation.h>
42 #include <CoreFoundation/CFDate.h>
43
44 #include <xpc/xpc.h>
45
46 #if TARGET_OS_IPHONE || TARGET_OS_EMBEDDED
47 #include <MobileGestalt.h>
48 #endif
49
50 #include <Security/SecBase64.h>
51 #include <Security/SecKeyPriv.h>
52 #include <Security/SecOTR.h>
53
54 #if 0//TARGET_OS_MAC // TODO: this function is the only one that causes secd to need to link against Security.framework on OSX
55
56 __BEGIN_DECLS
57 SecKeyRef _SecKeyCreateFromPublicData(CFAllocatorRef allocator, CFIndex algorithmID, CFDataRef publicBytes);
58 __END_DECLS
59
60 #endif /* TARGET_OS_MAC */
61
62 struct __OpaqueSOSPeerInfo {
63 CFRuntimeBase _base;
64
65 //
66 CFMutableDictionaryRef description;
67 CFDataRef signature;
68
69 // Cached data
70 CFDictionaryRef gestalt;
71 CFStringRef id;
72 CFIndex version;
73 };
74
75 CFGiblisWithHashFor(SOSPeerInfo);
76
77 CFStringRef kPIUserDefinedDeviceName = CFSTR("ComputerName");
78 CFStringRef kPIDeviceModelName = CFSTR("ModelName");
79
80 // Description Dictionary Entries
81 static CFStringRef sPublicKeyKey = CFSTR("PublicSigningKey");
82 static CFStringRef sGestaltKey = CFSTR("DeviceGestalt");
83 static 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
95 SecKeyRef SOSPeerInfoCopyPubKey(SOSPeerInfoRef peer) {
96 CFDataRef pubKeyBytes = CFDictionaryGetValue(peer->description, sPublicKeyKey);
97 CFAllocatorRef allocator = CFGetAllocator(peer);
98 SecKeyRef pubKey = SecKeyCreateFromPublicData(allocator, kSecECDSAAlgorithmID, pubKeyBytes);
99 return pubKey;
100 }
101
102
103 static bool SOSDescriptionHash(SOSPeerInfoRef peer, const struct ccdigest_info *di, void *hashresult, CFErrorRef *error) {
104 ccdigest_di_decl(di, ctx);
105 ccdigest_init(di, ctx);
106 void *ctx_p = ctx;
107 if(!SOSPeerInfoUpdateDigestWithDescription(peer, di, ctx_p, error)) return false;
108 ccdigest_final(di, ctx, hashresult);
109 return true;
110 }
111
112
113 #define SIGLEN 128
114 static CFDataRef sosSignHash(SecKeyRef privkey, const struct ccdigest_info *di, uint8_t *hbuf) {
115 OSStatus stat;
116 size_t siglen = SIGLEN;
117 uint8_t sig[siglen];
118 if((stat = SecKeyRawSign(privkey, kSecPaddingNone, hbuf, di->output_size, sig, &siglen)) != 0) {
119 return NULL;
120 }
121 return CFDataCreate(NULL, sig, (CFIndex)siglen);
122 }
123
124 static bool sosVerifyHash(SecKeyRef pubkey, const struct ccdigest_info *di, uint8_t *hbuf, CFDataRef signature) {
125 return SecKeyRawVerify(pubkey, kSecPaddingNone, hbuf, di->output_size,
126 CFDataGetBytePtr(signature), CFDataGetLength(signature)) == errSecSuccess;
127 }
128
129 static bool SOSPeerInfoSign(SecKeyRef privKey, SOSPeerInfoRef peer, CFErrorRef *error) {
130 bool status = false;
131 const struct ccdigest_info *di = ccsha256_di();
132 uint8_t hbuf[di->output_size];
133 CFDataRef newSignature = NULL;
134
135 require_action_quiet(SOSDescriptionHash(peer, di, hbuf, error), fail,
136 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to hash description for peer"), NULL, error));
137
138 newSignature = sosSignHash(privKey, di, hbuf);
139 require_action_quiet(newSignature, fail, SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to sign peerinfo for peer"), NULL, error));
140
141 CFReleaseNull(peer->signature);
142 peer->signature = newSignature;
143 newSignature = NULL;
144 status = true;
145
146 fail:
147 CFReleaseNull(newSignature);
148 return status;
149 }
150
151 // Return true (1) if the signature verifies.
152 static bool SOSPeerInfoVerify(SOSPeerInfoRef peer, CFErrorRef *error) {
153 bool result = false;
154 const struct ccdigest_info *di = ccsha256_di();
155 uint8_t hbuf[di->output_size];
156
157 SecKeyRef pubKey = SOSPeerInfoCopyPubKey(peer);
158 require_quiet(pubKey, error_out);
159
160 require_quiet(SOSDescriptionHash(peer, di, hbuf, error), error_out);
161
162 result = sosVerifyHash(pubKey, di, hbuf, peer->signature);
163
164 error_out:
165 CFReleaseNull(pubKey);
166 return result;
167 }
168
169 static SOSPeerInfoRef SOSPeerInfoCreate_Internal(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error, void (^ description_modifier)(CFMutableDictionaryRef description)) {
170 SOSPeerInfoRef pi = CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator);
171 pi->gestalt = gestalt;
172 CFRetain(pi->gestalt);
173
174 pi->version = kSOSPeerVersion;
175
176 CFDataRef publicBytes = NULL;
177 CFNumberRef versionNumber = NULL;
178
179 SecKeyRef publicKey = SecKeyCreatePublicFromPrivate(signingKey);
180 if (publicKey == NULL) {
181 SOSCreateError(kSOSErrorBadKey, CFSTR("Unable to get public"), NULL, error);
182 CFReleaseNull(pi);
183 goto exit;
184 }
185
186 OSStatus result = SecKeyCopyPublicBytes(publicKey, &publicBytes);
187
188 if (result != errSecSuccess) {
189 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
190 CFReleaseNull(pi);
191 goto exit;
192 }
193
194 pi->signature = CFDataCreateMutable(allocator, 0);
195
196 versionNumber = CFNumberCreateWithCFIndex(NULL, pi->version);
197 pi->description = CFDictionaryCreateMutableForCFTypesWith(allocator,
198 sVersionKey, versionNumber,
199 sPublicKeyKey, publicBytes,
200 sGestaltKey, pi->gestalt,
201 NULL);
202 description_modifier(pi->description);
203
204 pi->id = SOSCopyIDOfKey(publicKey, error);
205 CFReleaseNull(publicKey);
206
207 require_quiet(pi->id, exit);
208
209 if (!SOSPeerInfoSign(signingKey, pi, error)) {
210 CFReleaseNull(pi);
211 goto exit;
212 }
213
214 exit:
215 CFReleaseNull(versionNumber);
216 CFReleaseNull(publicBytes);
217 return pi;
218 }
219
220 SOSPeerInfoRef SOSPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) {
221 return SOSPeerInfoCreate_Internal(allocator, gestalt, signingKey, error, ^(CFMutableDictionaryRef description) {});
222 }
223
224 SOSPeerInfoRef SOSPeerInfoCreateCloudIdentity(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) {
225 return SOSPeerInfoCreate_Internal(allocator, gestalt, signingKey, error, ^(CFMutableDictionaryRef description) {
226 CFDictionarySetValue(description, sCloudIdentityKey, kCFBooleanTrue);
227 });
228
229 }
230
231
232 SOSPeerInfoRef SOSPeerInfoCreateCopy(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFErrorRef* error) {
233 SOSPeerInfoRef pi = CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator);
234
235 pi->description = CFDictionaryCreateMutableCopy(allocator, 0, toCopy->description);
236 pi->signature = CFDataCreateCopy(allocator, toCopy->signature);
237
238 pi->gestalt = CFDictionaryCreateCopy(allocator, toCopy->gestalt);
239 pi->id = CFStringCreateCopy(allocator, toCopy->id);
240
241 pi->version = toCopy->version;
242
243 return pi;
244 }
245
246 SOSPeerInfoRef SOSPeerInfoCopyWithGestaltUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) {
247 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, toCopy, error);
248
249 CFRetainSafe(gestalt);
250 CFReleaseNull(pi->gestalt);
251 pi->gestalt = gestalt;
252
253 CFDictionarySetValue(pi->description, sGestaltKey, pi->gestalt);
254
255 SecKeyRef pub_key = SOSPeerInfoCopyPubKey(pi);
256
257 pi->id = SOSCopyIDOfKey(pub_key, error);
258 require_quiet(pi->id, exit);
259
260 require_action_quiet(SOSPeerInfoSign(signingKey, pi, error), exit, CFReleaseNull(pi));
261
262 exit:
263 CFReleaseNull(pub_key);
264 return pi;
265 }
266
267 SOSPeerInfoRef SOSPeerInfoCreateFromDER(CFAllocatorRef allocator, CFErrorRef* error,
268 const uint8_t** der_p, const uint8_t *der_end) {
269 SOSPeerInfoRef pi = CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator);
270 SecKeyRef pubKey = NULL;
271
272 const uint8_t *sequence_end;
273
274 CFPropertyListRef pl = NULL;
275
276 pi->gestalt = NULL;
277 pi->version = 0; // TODO: Encode this in the DER
278
279 *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
280 *der_p = der_decode_plist(allocator, kCFPropertyListImmutable, &pl, error, *der_p, sequence_end);
281 *der_p = der_decode_data(allocator, kCFPropertyListImmutable, &pi->signature, error, *der_p, sequence_end);
282
283 if (*der_p == NULL || *der_p != sequence_end) {
284 SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Format of Peer Info DER"), NULL, error);
285 goto fail;
286 }
287
288 if (CFGetTypeID(pl) != CFDictionaryGetTypeID()) {
289 CFStringRef description = CFCopyTypeIDDescription(CFGetTypeID(pl));
290 SOSCreateErrorWithFormat(kSOSErrorUnexpectedType, NULL, error, NULL,
291 CFSTR("Expected dictionary got %@"), description);
292 CFReleaseSafe(description);
293 goto fail;
294 }
295
296 pi->description = (CFMutableDictionaryRef) pl;
297 CFRetain(pi->description);
298 CFReleaseNull(pl);
299
300 CFNumberRef versionNumber = CFDictionaryGetValue(pi->description, sVersionKey);
301
302 if (versionNumber) {
303 CFNumberGetValue(versionNumber, kCFNumberCFIndexType, &pi->version);
304 }
305
306 CFDictionaryRef gestalt = CFDictionaryGetValue(pi->description, sGestaltKey);
307
308 if (!isDictionary(gestalt)) {
309 CFStringRef description = CFCopyTypeIDDescription(CFGetTypeID(pl));
310 SOSCreateErrorWithFormat(kSOSErrorUnexpectedType, NULL, error, NULL,
311 CFSTR("Expected dictionary got %@"), description);
312 CFReleaseSafe(description);
313 goto fail;
314 }
315
316 pi->gestalt = gestalt;
317 CFRetain(pi->gestalt);
318
319 pubKey = SOSPeerInfoCopyPubKey(pi);
320 require_quiet(pubKey, fail);
321
322 pi->id = SOSCopyIDOfKey(pubKey, error);
323 require_quiet(pi->id, fail);
324
325 if(!SOSPeerInfoVerify(pi, error)) {
326 SOSCreateErrorWithFormat(kSOSErrorBadSignature, NULL, error, NULL, CFSTR("Signature doesn't validate"));
327 if (error)
328 secerror("Can't validate PeerInfo: %@", *error);
329 goto fail;
330 }
331 CFReleaseNull(pubKey);
332 return pi;
333
334 fail:
335 CFReleaseNull(pi);
336 CFReleaseNull(pl);
337 CFReleaseNull(pubKey);
338
339 return NULL;
340 }
341
342 SOSPeerInfoRef SOSPeerInfoCreateFromData(CFAllocatorRef allocator, CFErrorRef* error,
343 CFDataRef peerinfo_data) {
344 const uint8_t *der = CFDataGetBytePtr(peerinfo_data);
345 CFIndex len = CFDataGetLength(peerinfo_data);
346 return SOSPeerInfoCreateFromDER(NULL, error, &der, der+len);
347 }
348
349 static void SOSPeerInfoDestroy(CFTypeRef aObj) {
350 SOSPeerInfoRef pi = (SOSPeerInfoRef) aObj;
351
352 if(!pi) return;
353 CFReleaseNull(pi->description);
354 CFReleaseNull(pi->signature);
355 CFReleaseNull(pi->gestalt);
356 CFReleaseNull(pi->id);
357 }
358
359 static Boolean SOSPeerInfoCompare(CFTypeRef lhs, CFTypeRef rhs) {
360 SOSPeerInfoRef lpeer = (SOSPeerInfoRef) lhs;
361 SOSPeerInfoRef rpeer = (SOSPeerInfoRef) rhs;
362 if(!lpeer || !rpeer) return false;
363 return CFEqualSafe(lpeer->description, rpeer->description) && CFEqualSafe(lpeer->signature, rpeer->signature);
364 }
365
366
367 CFComparisonResult SOSPeerInfoCompareByID(const void *val1, const void *val2, void *context) {
368 CFStringRef v1 = SOSPeerInfoGetPeerID((SOSPeerInfoRef) val1);
369 CFStringRef v2 = SOSPeerInfoGetPeerID((SOSPeerInfoRef) val2);
370 return CFStringCompare(v1, v2, 0);
371 }
372
373 static CFHashCode SOSPeerInfoHash(CFTypeRef cf) {
374 SOSPeerInfoRef peer = (SOSPeerInfoRef) cf;
375
376 return CFHash(peer->description) ^ CFHash(peer->signature);
377 }
378
379 static CFStringRef SOSPeerInfoCopyDescription(CFTypeRef aObj) {
380 SOSPeerInfoRef pi = (SOSPeerInfoRef) aObj;
381
382 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SOSPeerInfo@%p: Name:'%@'%s Type: '%@' ID:'%@'>"),
383 pi,
384 CFDictionaryGetValue(pi->gestalt, kPIUserDefinedDeviceName),
385 SOSPeerInfoIsRetirementTicket(pi) ? " [retired]" : "",
386 CFDictionaryGetValue(pi->gestalt, kPIDeviceModelName),
387 pi->id);
388 }
389
390 CFDictionaryRef SOSPeerInfoCopyPeerGestalt(SOSPeerInfoRef pi) {
391 CFRetain(pi->gestalt);
392 return pi->gestalt;
393 }
394
395 CFStringRef SOSPeerInfoGetPeerName(SOSPeerInfoRef peer) {
396 return SOSPeerInfoLookupGestaltValue(peer, kPIUserDefinedDeviceName);
397 }
398
399 CFStringRef SOSPeerInfoGetPeerDeviceType(SOSPeerInfoRef peer) {
400 return SOSPeerInfoLookupGestaltValue(peer, kPIDeviceModelName);
401 }
402
403 CFTypeRef SOSPeerInfoLookupGestaltValue(SOSPeerInfoRef pi, CFStringRef key) {
404 return CFDictionaryGetValue(pi->gestalt, key);
405 }
406
407 CFStringRef SOSPeerInfoGetPeerID(SOSPeerInfoRef pi) {
408 return pi->id;
409 }
410
411 CFIndex SOSPeerInfoGetVersion(SOSPeerInfoRef pi) {
412 // TODO: Encode this in the DER.
413 return pi->version;
414 }
415
416 bool SOSPeerInfoUpdateDigestWithPublicKeyBytes(SOSPeerInfoRef peer, const struct ccdigest_info *di,
417 ccdigest_ctx_t ctx, CFErrorRef *error) {
418 CFDataRef pubKeyBytes = CFDictionaryGetValue(peer->description, sPublicKeyKey);
419
420 if(!pubKeyBytes) {
421 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Digest failed – no public key"));
422 return false;
423 }
424
425 ccdigest_update(di, ctx, CFDataGetLength(pubKeyBytes), CFDataGetBytePtr(pubKeyBytes));
426
427 return true;
428 }
429
430 bool SOSPeerInfoUpdateDigestWithDescription(SOSPeerInfoRef peer, const struct ccdigest_info *di,
431 ccdigest_ctx_t ctx, CFErrorRef *error) {
432 size_t description_size = der_sizeof_plist(peer->description, error);
433 uint8_t data_begin[description_size];
434 uint8_t *data_end = data_begin + description_size;
435 uint8_t *encoded = der_encode_plist(peer->description, error, data_begin, data_end);
436
437 if(!encoded) {
438 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Description encode failed"));
439 return false;
440 }
441
442 ccdigest_update(di, ctx, description_size, data_begin);
443
444 return true;
445 }
446
447
448 static CFDataRef sosCreateDate() {
449 CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
450 size_t bufsiz = der_sizeof_date(now, NULL);
451 uint8_t buf[bufsiz];
452 der_encode_date(now, NULL, buf, buf+bufsiz);
453 CFReleaseNull(now);
454 return CFDataCreate(NULL, buf, bufsiz);
455 }
456
457 static CFDateRef sosCreateCFDate(CFDataRef sosdate) {
458 CFDateRef date;
459 der_decode_date(NULL, 0, &date, NULL, CFDataGetBytePtr(sosdate),
460 CFDataGetBytePtr(sosdate) + CFDataGetLength(sosdate));
461 return date;
462 }
463
464 static bool sospeer_application_hash(SOSPeerInfoRef pi, const struct ccdigest_info *di, uint8_t *hbuf) {
465 CFDataRef appdate = CFDictionaryGetValue(pi->description, sApplicationDate);
466 if(!appdate) return false;
467 ccdigest_di_decl(di, ctx);
468 ccdigest_init(di, ctx);
469 ccdigest_update(di, ctx, CFDataGetLength(appdate), CFDataGetBytePtr(appdate));
470 if (!SOSPeerInfoUpdateDigestWithPublicKeyBytes(pi, di, ctx, NULL)) return false;
471 ccdigest_final(di, ctx, hbuf);
472 return true;
473 }
474
475 SOSPeerInfoRef SOSPeerInfoCopyAsApplication(SOSPeerInfoRef original, SecKeyRef userkey, SecKeyRef peerkey, CFErrorRef *error) {
476 SOSPeerInfoRef result = NULL;
477 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, original, error);
478
479 const struct ccdigest_info *di = ccsha256_di();
480 uint8_t hbuf[di->output_size];
481 CFDataRef usersig = NULL;
482
483 CFDataRef creationDate = sosCreateDate();
484 CFDictionarySetValue(pi->description, sApplicationDate, creationDate);
485 CFReleaseNull(creationDate);
486
487 // Create User Application Signature
488 require_action_quiet(sospeer_application_hash(pi, di, hbuf), fail,
489 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to create hash for peer applicant"), NULL, error));
490
491 usersig = sosSignHash(userkey, di, hbuf);
492 require_action_quiet(usersig, fail,
493 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to sign public key hash for peer"), NULL, error));
494
495 CFDictionarySetValue(pi->description, sApplicationUsig, usersig);
496
497 require_quiet(SOSPeerInfoSign(peerkey, pi, error), fail);
498
499 result = pi;
500 pi = NULL;
501
502 fail:
503 CFReleaseNull(usersig);
504 CFReleaseNull(pi);
505 return result;
506 }
507
508 bool SOSPeerInfoApplicationVerify(SOSPeerInfoRef pi, SecKeyRef userkey, CFErrorRef *error) {
509 const struct ccdigest_info *di = ccsha256_di();
510 uint8_t hbuf[di->output_size];
511 bool result = false;
512
513 CFDataRef usig = CFDictionaryGetValue(pi->description, sApplicationUsig);
514 require_action_quiet(usig, exit,
515 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Peer is not an applicant"), NULL, error));
516 // Verify User Application Signature
517 require_action_quiet(sospeer_application_hash(pi, di, hbuf), exit,
518 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to create hash for peer applicant"), NULL, error));
519 require_action_quiet(sosVerifyHash(userkey, di, hbuf, usig), exit,
520 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("user signature of public key hash fails to verify"), NULL, error));
521
522 result = SOSPeerInfoVerify(pi, error);
523
524 exit:
525 return result;
526 }
527
528
529 static CF_RETURNS_RETAINED CFDateRef sosPeerInfoGetDate(SOSPeerInfoRef pi, CFStringRef entry) {
530 if(!pi) return NULL;
531 CFDataRef sosdate = CFDictionaryGetValue(pi->description, entry);
532 if(!sosdate) return NULL;
533 CFDateRef date = sosCreateCFDate(sosdate);
534
535 return date;
536 }
537
538 CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetApplicationDate(SOSPeerInfoRef pi) {
539 return sosPeerInfoGetDate(pi, sApplicationDate);
540 }
541
542 CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetRetirementDate(SOSPeerInfoRef pi) {
543 return sosPeerInfoGetDate(pi, sRetirementDate);
544 }
545
546
547 size_t SOSPeerInfoGetDEREncodedSize(SOSPeerInfoRef peer, CFErrorRef *error) {
548 size_t plist_size = der_sizeof_plist(peer->description, error);
549 if (plist_size == 0)
550 return 0;
551
552 size_t signature_size = der_sizeof_data(peer->signature, error);
553 if (signature_size == 0)
554 return 0;
555
556 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
557 plist_size + signature_size);
558 }
559
560 uint8_t* SOSPeerInfoEncodeToDER(SOSPeerInfoRef peer, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) {
561 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
562 der_encode_plist(peer->description, error, der,
563 der_encode_data(peer->signature, error, der, der_end)));
564 }
565
566 CFDataRef SOSPeerInfoCopyEncodedData(SOSPeerInfoRef peer, CFAllocatorRef allocator, CFErrorRef *error) {
567 size_t size = SOSPeerInfoGetDEREncodedSize(peer, error);
568 if (size == 0) return NULL;
569
570 uint8_t buffer[size];
571 uint8_t* start = SOSPeerInfoEncodeToDER(peer, error, buffer, buffer + sizeof(buffer));
572 CFDataRef result = CFDataCreate(kCFAllocatorDefault, start, size);
573 return result;
574 }
575
576
577 //
578 // PeerInfoArray encoding decoding
579 //
580
581 CFMutableArrayRef SOSPeerInfoArrayCreateFromDER(CFAllocatorRef allocator, CFErrorRef* error,
582 const uint8_t** der_p, const uint8_t *der_end) {
583 CFMutableArrayRef pia = CFArrayCreateMutableForCFTypes(allocator);
584
585 const uint8_t *sequence_end;
586
587 *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
588
589 require_action(*der_p, fail, SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Peer Info Array Sequence Header"), NULL, error));
590
591 while (sequence_end != *der_p) {
592 SOSPeerInfoRef pi = SOSPeerInfoCreateFromDER(allocator, error, der_p, sequence_end);
593
594 if (pi == NULL || *der_p == NULL) {
595 SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Peer Info Array DER"), (error != NULL ? *error : NULL), error);
596 CFReleaseNull(pi);
597 goto fail;
598 }
599
600 CFArrayAppendValue(pia, pi);
601 CFReleaseSafe(pi);
602 }
603
604 if (!pia)
605 *der_p = NULL;
606 return pia;
607
608 fail:
609 CFReleaseNull(pia);
610 *der_p = NULL;
611 return NULL;
612 }
613
614 size_t SOSPeerInfoArrayGetDEREncodedSize(CFArrayRef pia, CFErrorRef *error) {
615 size_t array_size = 0;
616
617 for(CFIndex count = CFArrayGetCount(pia);
618 count > 0;
619 --count) {
620 SOSPeerInfoRef pi = (SOSPeerInfoRef) CFArrayGetValueAtIndex(pia, count - 1);
621
622 if (CFGetTypeID(pi) != SOSPeerInfoGetTypeID()) {
623 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Non SOSPeerInfo in array"), (error != NULL ? *error : NULL), error);
624 return 0;
625 }
626
627 size_t pi_size = SOSPeerInfoGetDEREncodedSize(pi, error);
628
629 if (pi_size == 0) {
630 SOSCreateError(kSOSErrorEncodeFailure, CFSTR("Bad DER size"), (error != NULL ? *error : NULL), error);
631 return 0;
632 }
633
634 array_size += pi_size;
635 }
636
637
638 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
639 array_size);
640 }
641
642 uint8_t* SOSPeerInfoArrayEncodeToDER(CFArrayRef pia, CFErrorRef* error, const uint8_t* der, uint8_t* der_end_param) {
643
644 uint8_t* const sequence_end = der_end_param;
645 __block uint8_t* der_end = der_end_param;
646
647 CFArrayForEachReverse(pia, ^(const void *value) {
648 SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
649 if (CFGetTypeID(pi) != SOSPeerInfoGetTypeID()) {
650 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Non SOSPeerInfo in array"), NULL, error);
651 der_end = NULL; // Indicate error and continue.
652 }
653 if (der_end)
654 der_end = SOSPeerInfoEncodeToDER(pi, error, der, der_end);
655 });
656
657 if (der_end == NULL)
658 return NULL;
659
660 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, sequence_end, der, der_end);
661 }
662
663
664 CFArrayRef CreateArrayOfPeerInfoWithXPCObject(xpc_object_t peerArray, CFErrorRef* error) {
665 if (!peerArray) {
666 SecCFCreateErrorWithFormat(kSecXPCErrorUnexpectedNull, sSecXPCErrorDomain, NULL, error, NULL, CFSTR("Unexpected Null Array to encode"));
667 return NULL;
668 }
669
670 if (xpc_get_type(peerArray) != XPC_TYPE_DATA) {
671 SecCFCreateErrorWithFormat(kSecXPCErrorUnexpectedType, sSecXPCErrorDomain, NULL, error, NULL, CFSTR("Array of peer info not array, got %@"), peerArray);
672 return NULL;
673 }
674
675 const uint8_t* der = xpc_data_get_bytes_ptr(peerArray);
676 const uint8_t* der_end = der + xpc_data_get_length(peerArray);
677
678 return SOSPeerInfoArrayCreateFromDER(kCFAllocatorDefault, error, &der, der_end);
679 }
680
681 xpc_object_t CreateXPCObjectWithArrayOfPeerInfo(CFArrayRef array, CFErrorRef *error) {
682 size_t data_size = SOSPeerInfoArrayGetDEREncodedSize(array, error);
683 if (data_size == 0)
684 return NULL;
685 uint8_t *data = (uint8_t *)malloc(data_size);
686 if (!data) return NULL;
687
688 xpc_object_t result = NULL;
689 if (SOSPeerInfoArrayEncodeToDER(array, error, data, data + data_size))
690 result = xpc_data_create(data, data_size);
691
692 free(data);
693 return result;
694 }
695
696 //
697 // Gestalt helpers
698 //
699
700 CFStringRef SOSPeerGestaltGetName(CFDictionaryRef gestalt) {
701 CFStringRef name = SOSPeerGestaltGetAnswer(gestalt, kPIUserDefinedDeviceName);
702 return isString(name) ? name : NULL;
703 }
704
705 CFTypeRef SOSPeerGestaltGetAnswer(CFDictionaryRef gestalt, CFStringRef question) {
706 return gestalt ? CFDictionaryGetValue(gestalt, question) : NULL;
707 }
708
709 //
710 // Peer Retirement
711 //
712
713
714 SOSPeerInfoRef SOSPeerInfoCreateRetirementTicket(CFAllocatorRef allocator, SecKeyRef privKey, SOSPeerInfoRef peer, CFErrorRef *error) {
715 // Copy PeerInfo
716 SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, peer, error);
717
718 require(pi, fail);
719
720 // Fill out Resignation Date
721 CFDataRef resignationDate = sosCreateDate();
722 CFDictionaryAddValue(pi->description, sRetirementDate, resignationDate);
723 CFReleaseNull(resignationDate);
724
725 require(SOSPeerInfoSign(privKey, pi, error), fail);
726
727 return pi;
728
729 fail:
730 CFReleaseNull(pi);
731 return NULL;
732 }
733
734 CFStringRef SOSPeerInfoInspectRetirementTicket(SOSPeerInfoRef pi, CFErrorRef *error) {
735 CFStringRef retval = NULL;
736 CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
737 CFDateRef retirement = NULL;
738
739 require_quiet(SOSPeerInfoVerify(pi, error), err);
740
741 retirement = sosCreateCFDate(CFDictionaryGetValue(pi->description, sRetirementDate));
742
743 require_action_quiet(retirement, err,
744 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Peer is not retired"), NULL, error));
745
746 require_action_quiet(CFDateCompare(now, retirement, NULL) == kCFCompareGreaterThan, err,
747 SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Retirement date is after current date"), NULL, error));
748
749 retval = SOSPeerInfoGetPeerID(pi);
750
751 err:
752 CFReleaseNull(now);
753 CFReleaseNull(retirement);
754 return retval;
755 }
756
757 bool SOSPeerInfoRetireRetirementTicket(size_t max_seconds, SOSPeerInfoRef pi) {
758 CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
759 CFDateRef retirement = sosCreateCFDate(CFDictionaryGetValue(pi->description, sRetirementDate));
760 CFTimeInterval timediff = CFDateGetTimeIntervalSinceDate(now, retirement); // diff in seconds
761 CFReleaseNull(now);
762 CFReleaseNull(retirement);
763 if(timediff > (max_seconds)) return true;
764 return false;
765 }
766
767 bool SOSPeerInfoIsRetirementTicket(SOSPeerInfoRef pi) {
768 CFDataRef flag = CFDictionaryGetValue(pi->description, sRetirementDate);
769 return flag != NULL;
770 }
771
772 bool SOSPeerInfoIsCloudIdentity(SOSPeerInfoRef pi) {
773 CFTypeRef value = CFDictionaryGetValue(pi->description, sCloudIdentityKey);
774 return CFEqualSafe(value, kCFBooleanTrue);
775 }
776
777 SOSPeerInfoRef SOSPeerInfoUpgradeSignatures(CFAllocatorRef allocator, SecKeyRef privKey, SecKeyRef peerKey, SOSPeerInfoRef peer, CFErrorRef *error) {
778 SecKeyRef pubKey = SecKeyCreatePublicFromPrivate(privKey);
779 SOSPeerInfoRef retval = NULL;
780
781 retval = SOSPeerInfoCopyAsApplication(peer, privKey, peerKey, error);
782 CFReleaseNull(pubKey);
783 return retval;
784 }
785