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
57SecKeyRef _SecKeyCreateFromPublicData(CFAllocatorRef allocator, CFIndex algorithmID, CFDataRef publicBytes);
58__END_DECLS
59
60#endif  /* TARGET_OS_MAC */
61
62struct __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
75CFGiblisWithHashFor(SOSPeerInfo);
76
77CFStringRef kPIUserDefinedDeviceName = CFSTR("ComputerName");
78CFStringRef kPIDeviceModelName = CFSTR("ModelName");
79
80// Description Dictionary Entries
81static CFStringRef sPublicKeyKey = CFSTR("PublicSigningKey");
82static CFStringRef sGestaltKey   = CFSTR("DeviceGestalt");
83static CFStringRef sVersionKey   = CFSTR("ConflictVersion");
84static CFStringRef sCloudIdentityKey   = CFSTR("CloudIdentity");
85static CFStringRef sApplicationDate   = CFSTR("ApplicationDate");
86static CFStringRef sApplicationUsig   = CFSTR("ApplicationUsig");
87static CFStringRef sRetirementDate   = CFSTR("RetirementDate");
88
89// Peerinfo Entries
90CFStringRef kSOSPeerInfoDescriptionKey = CFSTR("SOSPeerInfoDescription");
91CFStringRef kSOSPeerInfoSignatureKey = CFSTR("SOSPeerInfoSignature");
92CFStringRef kSOSPeerInfoNameKey = CFSTR("SOSPeerInfoName");
93
94
95SecKeyRef 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
103static 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
114static 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
124static 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
129static 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
146fail:
147    CFReleaseNull(newSignature);
148    return status;
149}
150
151// Return true (1) if the signature verifies.
152static 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
164error_out:
165    CFReleaseNull(pubKey);
166    return result;
167}
168
169static 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
214exit:
215    CFReleaseNull(versionNumber);
216    CFReleaseNull(publicBytes);
217    return pi;
218}
219
220SOSPeerInfoRef SOSPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) {
221    return SOSPeerInfoCreate_Internal(allocator, gestalt, signingKey, error, ^(CFMutableDictionaryRef description) {});
222}
223
224SOSPeerInfoRef 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
232SOSPeerInfoRef 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
246SOSPeerInfoRef 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
262exit:
263    CFReleaseNull(pub_key);
264    return pi;
265}
266
267SOSPeerInfoRef 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
334fail:
335    CFReleaseNull(pi);
336    CFReleaseNull(pl);
337    CFReleaseNull(pubKey);
338
339    return NULL;
340}
341
342SOSPeerInfoRef 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
349static 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
359static 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
367CFComparisonResult SOSPeerInfoCompareByID(const void *val1, const void *val2, void *context) {
368    // The code below is necessary but not sufficient; not returning a CFComparisonResult
369    // It probably is OK to say that a NULL is <  <non-NULL>
370    if (val1 == NULL || val2 == NULL) {
371	    ptrdiff_t dv = val1 - val2;
372		return dv < 0 ? kCFCompareLessThan : dv == 0 ? kCFCompareEqualTo : kCFCompareGreaterThan;
373    }
374
375	CFStringRef v1 = SOSPeerInfoGetPeerID((SOSPeerInfoRef) val1);
376	CFStringRef v2 = SOSPeerInfoGetPeerID((SOSPeerInfoRef) val2);
377    if (v1 == NULL || v2 == NULL) {
378	    ptrdiff_t dv = (const void *)v1 - (const void *)v2;
379        return dv < 0 ? kCFCompareLessThan : dv == 0 ? kCFCompareEqualTo : kCFCompareGreaterThan;
380    }
381
382    return CFStringCompare(v1, v2, 0);
383}
384
385static CFHashCode SOSPeerInfoHash(CFTypeRef cf) {
386    SOSPeerInfoRef peer = (SOSPeerInfoRef) cf;
387
388    return CFHash(peer->description) ^ CFHash(peer->signature);
389}
390
391static CFStringRef SOSPeerInfoCopyDescription(CFTypeRef aObj) {
392    SOSPeerInfoRef pi = (SOSPeerInfoRef) aObj;
393
394    return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SOSPeerInfo@%p: Name:'%@'%s Type: '%@' ID:'%@'>"),
395                                    pi,
396                                    CFDictionaryGetValue(pi->gestalt, kPIUserDefinedDeviceName),
397                                    SOSPeerInfoIsRetirementTicket(pi) ? " [retired]" : "",
398                                    CFDictionaryGetValue(pi->gestalt, kPIDeviceModelName),
399                                    pi->id);
400}
401
402CFDictionaryRef SOSPeerInfoCopyPeerGestalt(SOSPeerInfoRef pi) {
403    CFRetain(pi->gestalt);
404    return pi->gestalt;
405}
406
407CFStringRef SOSPeerInfoGetPeerName(SOSPeerInfoRef peer) {
408    return SOSPeerInfoLookupGestaltValue(peer, kPIUserDefinedDeviceName);
409}
410
411CFStringRef SOSPeerInfoGetPeerDeviceType(SOSPeerInfoRef peer) {
412    return SOSPeerInfoLookupGestaltValue(peer, kPIDeviceModelName);
413}
414
415CFTypeRef SOSPeerInfoLookupGestaltValue(SOSPeerInfoRef pi, CFStringRef key) {
416    return CFDictionaryGetValue(pi->gestalt, key);
417}
418
419CFStringRef SOSPeerInfoGetPeerID(SOSPeerInfoRef pi) {
420    return pi ? pi->id : NULL;
421}
422
423CFIndex SOSPeerInfoGetVersion(SOSPeerInfoRef pi) {
424    // TODO: Encode this in the DER.
425    return pi->version;
426}
427
428bool SOSPeerInfoUpdateDigestWithPublicKeyBytes(SOSPeerInfoRef peer, const struct ccdigest_info *di,
429                                               ccdigest_ctx_t ctx, CFErrorRef *error) {
430    CFDataRef pubKeyBytes = CFDictionaryGetValue(peer->description, sPublicKeyKey);
431
432    if(!pubKeyBytes) {
433        SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Digest failed – no public key"));
434        return false;
435    }
436
437    ccdigest_update(di, ctx, CFDataGetLength(pubKeyBytes), CFDataGetBytePtr(pubKeyBytes));
438
439    return true;
440}
441
442bool SOSPeerInfoUpdateDigestWithDescription(SOSPeerInfoRef peer, const struct ccdigest_info *di,
443                                            ccdigest_ctx_t ctx, CFErrorRef *error) {
444    size_t description_size = der_sizeof_plist(peer->description, error);
445    uint8_t data_begin[description_size];
446    uint8_t *data_end = data_begin + description_size;
447    uint8_t *encoded = der_encode_plist(peer->description, error, data_begin, data_end);
448
449    if(!encoded) {
450        SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Description encode failed"));
451        return false;
452    }
453
454    ccdigest_update(di, ctx, description_size, data_begin);
455
456    return true;
457}
458
459
460static CFDataRef sosCreateDate() {
461    CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
462    size_t bufsiz = der_sizeof_date(now, NULL);
463    uint8_t buf[bufsiz];
464    der_encode_date(now, NULL, buf, buf+bufsiz);
465    CFReleaseNull(now);
466    return CFDataCreate(NULL, buf, bufsiz);
467}
468
469static CFDateRef sosCreateCFDate(CFDataRef sosdate) {
470    CFDateRef date;
471    der_decode_date(NULL, 0, &date, NULL, CFDataGetBytePtr(sosdate),
472                    CFDataGetBytePtr(sosdate) + CFDataGetLength(sosdate));
473    return date;
474}
475
476static bool sospeer_application_hash(SOSPeerInfoRef pi, const struct ccdigest_info *di, uint8_t *hbuf) {
477    CFDataRef appdate = CFDictionaryGetValue(pi->description, sApplicationDate);
478    if(!appdate) return false;
479    ccdigest_di_decl(di, ctx);
480    ccdigest_init(di, ctx);
481    ccdigest_update(di, ctx, CFDataGetLength(appdate), CFDataGetBytePtr(appdate));
482    if (!SOSPeerInfoUpdateDigestWithPublicKeyBytes(pi, di, ctx, NULL)) return false;
483    ccdigest_final(di, ctx, hbuf);
484    return true;
485}
486
487SOSPeerInfoRef SOSPeerInfoCopyAsApplication(SOSPeerInfoRef original, SecKeyRef userkey, SecKeyRef peerkey, CFErrorRef *error) {
488    SOSPeerInfoRef result = NULL;
489    SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, original, error);
490
491    const struct ccdigest_info *di = ccsha256_di();
492    uint8_t hbuf[di->output_size];
493    CFDataRef usersig = NULL;
494
495    CFDataRef creationDate = sosCreateDate();
496    CFDictionarySetValue(pi->description, sApplicationDate, creationDate);
497    CFReleaseNull(creationDate);
498
499    // Create User Application Signature
500    require_action_quiet(sospeer_application_hash(pi, di, hbuf), fail,
501                         SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to create hash for peer applicant"), NULL, error));
502
503    usersig = sosSignHash(userkey, di, hbuf);
504    require_action_quiet(usersig, fail,
505                        SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to sign public key hash for peer"), NULL, error));
506
507    CFDictionarySetValue(pi->description, sApplicationUsig, usersig);
508
509    require_quiet(SOSPeerInfoSign(peerkey, pi, error), fail);
510
511    result = pi;
512    pi = NULL;
513
514fail:
515    CFReleaseNull(usersig);
516    CFReleaseNull(pi);
517    return result;
518}
519
520bool SOSPeerInfoApplicationVerify(SOSPeerInfoRef pi, SecKeyRef userkey, CFErrorRef *error) {
521    const struct ccdigest_info *di = ccsha256_di();
522    uint8_t hbuf[di->output_size];
523    bool result = false;
524
525    CFDataRef usig = CFDictionaryGetValue(pi->description, sApplicationUsig);
526    require_action_quiet(usig, exit,
527                         SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Peer is not an applicant"), NULL, error));
528    // Verify User Application Signature
529    require_action_quiet(sospeer_application_hash(pi, di, hbuf), exit,
530                         SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to create hash for peer applicant"), NULL, error));
531    require_action_quiet(sosVerifyHash(userkey, di, hbuf, usig), exit,
532                         SOSCreateError(kSOSErrorUnexpectedType, CFSTR("user signature of public key hash fails to verify"), NULL, error));
533
534    result = SOSPeerInfoVerify(pi, error);
535
536exit:
537    return result;
538}
539
540
541static CF_RETURNS_RETAINED CFDateRef sosPeerInfoGetDate(SOSPeerInfoRef pi, CFStringRef entry) {
542    if(!pi) return NULL;
543    CFDataRef sosdate = CFDictionaryGetValue(pi->description, entry);
544    if(!sosdate) return NULL;
545    CFDateRef date = sosCreateCFDate(sosdate);
546
547    return date;
548}
549
550CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetApplicationDate(SOSPeerInfoRef pi) {
551    return sosPeerInfoGetDate(pi, sApplicationDate);
552}
553
554CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetRetirementDate(SOSPeerInfoRef pi) {
555    return sosPeerInfoGetDate(pi, sRetirementDate);
556}
557
558
559size_t SOSPeerInfoGetDEREncodedSize(SOSPeerInfoRef peer, CFErrorRef *error) {
560    size_t plist_size = der_sizeof_plist(peer->description, error);
561    if (plist_size == 0)
562        return 0;
563
564    size_t signature_size = der_sizeof_data(peer->signature, error);
565    if (signature_size == 0)
566        return 0;
567
568    return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
569                        plist_size + signature_size);
570}
571
572uint8_t* SOSPeerInfoEncodeToDER(SOSPeerInfoRef peer, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) {
573    return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
574                                       der_encode_plist(peer->description, error, der,
575                                       der_encode_data(peer->signature, error, der, der_end)));
576}
577
578CFDataRef SOSPeerInfoCopyEncodedData(SOSPeerInfoRef peer, CFAllocatorRef allocator, CFErrorRef *error) {
579    size_t size = SOSPeerInfoGetDEREncodedSize(peer, error);
580    if (size == 0) return NULL;
581
582    uint8_t buffer[size];
583    uint8_t* start = SOSPeerInfoEncodeToDER(peer, error, buffer, buffer + sizeof(buffer));
584    CFDataRef result = CFDataCreate(kCFAllocatorDefault, start, size);
585    return result;
586}
587
588
589//
590// PeerInfoArray encoding decoding
591//
592
593CFMutableArrayRef SOSPeerInfoArrayCreateFromDER(CFAllocatorRef allocator, CFErrorRef* error,
594                                                const uint8_t** der_p, const uint8_t *der_end) {
595    CFMutableArrayRef pia = CFArrayCreateMutableForCFTypes(allocator);
596
597    const uint8_t *sequence_end;
598
599    *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
600
601    require_action(*der_p, fail, SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Peer Info Array Sequence Header"), NULL, error));
602
603    while (sequence_end != *der_p) {
604        SOSPeerInfoRef pi = SOSPeerInfoCreateFromDER(allocator, error, der_p, sequence_end);
605
606        if (pi == NULL || *der_p == NULL) {
607            SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Peer Info Array DER"), (error != NULL ? *error : NULL), error);
608            CFReleaseNull(pi);
609            goto fail;
610        }
611
612        CFArrayAppendValue(pia, pi);
613        CFReleaseSafe(pi);
614    }
615
616    if (!pia)
617        *der_p = NULL;
618    return pia;
619
620fail:
621    CFReleaseNull(pia);
622    *der_p = NULL;
623    return NULL;
624}
625
626size_t SOSPeerInfoArrayGetDEREncodedSize(CFArrayRef pia, CFErrorRef *error) {
627    size_t array_size = 0;
628
629    for(CFIndex count = CFArrayGetCount(pia);
630        count > 0;
631        --count) {
632        SOSPeerInfoRef pi = (SOSPeerInfoRef) CFArrayGetValueAtIndex(pia, count - 1);
633
634        if (CFGetTypeID(pi) != SOSPeerInfoGetTypeID()) {
635            SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Non SOSPeerInfo in array"), (error != NULL ? *error : NULL), error);
636            return 0;
637        }
638
639        size_t pi_size = SOSPeerInfoGetDEREncodedSize(pi, error);
640
641        if (pi_size == 0) {
642            SOSCreateError(kSOSErrorEncodeFailure, CFSTR("Bad DER size"), (error != NULL ? *error : NULL), error);
643            return 0;
644        }
645
646        array_size += pi_size;
647    }
648
649
650    return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
651                        array_size);
652}
653
654uint8_t* SOSPeerInfoArrayEncodeToDER(CFArrayRef pia, CFErrorRef* error, const uint8_t* der, uint8_t* der_end_param) {
655
656    uint8_t* const sequence_end = der_end_param;
657    __block uint8_t* der_end = der_end_param;
658
659    CFArrayForEachReverse(pia, ^(const void *value) {
660        SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
661        if (CFGetTypeID(pi) != SOSPeerInfoGetTypeID()) {
662            SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Non SOSPeerInfo in array"), NULL, error);
663            der_end = NULL; // Indicate error and continue.
664        }
665        if (der_end)
666            der_end = SOSPeerInfoEncodeToDER(pi, error, der, der_end);
667    });
668
669    if (der_end == NULL)
670        return NULL;
671
672    return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, sequence_end, der, der_end);
673}
674
675
676CFArrayRef CreateArrayOfPeerInfoWithXPCObject(xpc_object_t peerArray, CFErrorRef* error) {
677    if (!peerArray) {
678        SecCFCreateErrorWithFormat(kSecXPCErrorUnexpectedNull, sSecXPCErrorDomain, NULL, error, NULL, CFSTR("Unexpected Null Array to encode"));
679        return NULL;
680    }
681
682    if (xpc_get_type(peerArray) != XPC_TYPE_DATA) {
683        SecCFCreateErrorWithFormat(kSecXPCErrorUnexpectedType, sSecXPCErrorDomain, NULL, error, NULL, CFSTR("Array of peer info not array, got %@"), peerArray);
684        return NULL;
685    }
686
687    const uint8_t* der = xpc_data_get_bytes_ptr(peerArray);
688    const uint8_t* der_end = der + xpc_data_get_length(peerArray);
689
690    return SOSPeerInfoArrayCreateFromDER(kCFAllocatorDefault, error, &der, der_end);
691}
692
693xpc_object_t CreateXPCObjectWithArrayOfPeerInfo(CFArrayRef array, CFErrorRef *error) {
694    size_t data_size = SOSPeerInfoArrayGetDEREncodedSize(array, error);
695    if (data_size == 0)
696        return NULL;
697    uint8_t *data = (uint8_t *)malloc(data_size);
698    if (!data) return NULL;
699
700    xpc_object_t result = NULL;
701    if (SOSPeerInfoArrayEncodeToDER(array, error, data, data + data_size))
702        result = xpc_data_create(data, data_size);
703
704    free(data);
705    return result;
706}
707
708//
709// Gestalt helpers
710//
711
712CFStringRef SOSPeerGestaltGetName(CFDictionaryRef gestalt) {
713    CFStringRef name = SOSPeerGestaltGetAnswer(gestalt, kPIUserDefinedDeviceName);
714    return isString(name) ? name : NULL;
715}
716
717CFTypeRef SOSPeerGestaltGetAnswer(CFDictionaryRef gestalt, CFStringRef question) {
718    return gestalt ? CFDictionaryGetValue(gestalt, question) : NULL;
719}
720
721//
722// Peer Retirement
723//
724
725
726SOSPeerInfoRef SOSPeerInfoCreateRetirementTicket(CFAllocatorRef allocator, SecKeyRef privKey, SOSPeerInfoRef peer, CFErrorRef *error) {
727    // Copy PeerInfo
728    SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, peer, error);
729
730    require(pi, fail);
731
732    // Fill out Resignation Date
733    CFDataRef resignationDate = sosCreateDate();
734    CFDictionaryAddValue(pi->description, sRetirementDate, resignationDate);
735    CFReleaseNull(resignationDate);
736
737    require(SOSPeerInfoSign(privKey, pi, error), fail);
738
739    return pi;
740
741fail:
742    CFReleaseNull(pi);
743    return NULL;
744}
745
746CFStringRef SOSPeerInfoInspectRetirementTicket(SOSPeerInfoRef pi, CFErrorRef *error) {
747    CFStringRef retval = NULL;
748    CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
749    CFDateRef retirement = NULL;
750
751    require_quiet(SOSPeerInfoVerify(pi, error), err);
752
753    retirement = sosCreateCFDate(CFDictionaryGetValue(pi->description, sRetirementDate));
754
755    require_action_quiet(retirement, err,
756                         SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Peer is not retired"), NULL, error));
757
758    require_action_quiet(CFDateCompare(now, retirement, NULL) == kCFCompareGreaterThan, err,
759                         SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Retirement date is after current date"), NULL, error));
760
761    retval = SOSPeerInfoGetPeerID(pi);
762
763err:
764    CFReleaseNull(now);
765    CFReleaseNull(retirement);
766    return retval;
767}
768
769bool SOSPeerInfoRetireRetirementTicket(size_t max_seconds, SOSPeerInfoRef pi) {
770    CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
771    CFDateRef retirement = sosCreateCFDate(CFDictionaryGetValue(pi->description, sRetirementDate));
772    CFTimeInterval timediff = CFDateGetTimeIntervalSinceDate(now, retirement); // diff in seconds
773    CFReleaseNull(now);
774    CFReleaseNull(retirement);
775    if(timediff > (max_seconds)) return true;
776    return false;
777}
778
779bool SOSPeerInfoIsRetirementTicket(SOSPeerInfoRef pi) {
780    CFDataRef flag = CFDictionaryGetValue(pi->description, sRetirementDate);
781    return flag != NULL;
782}
783
784bool SOSPeerInfoIsCloudIdentity(SOSPeerInfoRef pi) {
785    CFTypeRef value = CFDictionaryGetValue(pi->description, sCloudIdentityKey);
786    return CFEqualSafe(value, kCFBooleanTrue);
787}
788
789SOSPeerInfoRef SOSPeerInfoUpgradeSignatures(CFAllocatorRef allocator, SecKeyRef privKey, SecKeyRef peerKey, SOSPeerInfoRef peer, CFErrorRef *error) {
790    SecKeyRef pubKey = SecKeyCreatePublicFromPrivate(privKey);
791    SOSPeerInfoRef retval = NULL;
792
793    retval = SOSPeerInfoCopyAsApplication(peer, privKey, peerKey, error);
794    CFReleaseNull(pubKey);
795    return retval;
796}
797
798