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