]> git.saurik.com Git - apple/security.git/blob - Security/sec/SOSCircle/SecureObjectSync/SOSPeerInfo.c
Security-57031.1.35.tar.gz
[apple/security.git] / Security / sec / SOSCircle / SecureObjectSync / SOSPeerInfo.c
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
72 SecKeyRef _SecKeyCreateFromPublicData(CFAllocatorRef allocator, CFIndex algorithmID, CFDataRef publicBytes);
73 __END_DECLS
74
75 #endif /* TARGET_OS_MAC */
76
77 struct __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
93 CFGiblisWithHashFor(SOSPeerInfo);
94
95 CFStringRef kPIUserDefinedDeviceName = CFSTR("ComputerName");
96 CFStringRef kPIDeviceModelName = CFSTR("ModelName");
97 CFStringRef kPIMessageProtocolVersion = CFSTR("MessageProtocolVersion");
98
99 // Description Dictionary Entries
100 static CFStringRef sPublicKeyKey = CFSTR("PublicSigningKey");
101 static CFStringRef sGestaltKey = CFSTR("DeviceGestalt");
102 static CFStringRef sVersionKey = CFSTR("ConflictVersion");
103 static CFStringRef sCloudIdentityKey = CFSTR("CloudIdentity");
104 static CFStringRef sApplicationDate = CFSTR("ApplicationDate");
105 static CFStringRef sApplicationUsig = CFSTR("ApplicationUsig");
106 static CFStringRef sRetirementDate = CFSTR("RetirementDate");
107
108 // Peerinfo Entries
109 CFStringRef kSOSPeerInfoDescriptionKey = CFSTR("SOSPeerInfoDescription");
110 CFStringRef kSOSPeerInfoSignatureKey = CFSTR("SOSPeerInfoSignature");
111 CFStringRef kSOSPeerInfoNameKey = CFSTR("SOSPeerInfoName");
112
113
114 SecKeyRef 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
124 static bool SOSDescriptionHash(SOSPeerInfoRef peer, const struct ccdigest_info *di, void *hashresult, CFErrorRef *error) {
125 ccdigest_di_decl(di, ctx);
126 ccdigest_init(di, ctx);
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
135 static 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
145 static bool sosVerifyHash(SecKeyRef pubkey, const struct ccdigest_info *di, uint8_t *hbuf, CFDataRef signature) {
146 return SecKeyRawVerify(pubkey, kSecPaddingNone, hbuf, di->output_size,
147 CFDataGetBytePtr(signature), CFDataGetLength(signature)) == errSecSuccess;
148 }
149
150 static 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
167 fail:
168 CFReleaseNull(newSignature);
169 return status;
170 }
171
172 // Return true (1) if the signature verifies.
173 static 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
190 error_out:
191 CFReleaseNull(pubKey);
192 return result;
193 }
194
195 static 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
256 exit:
257 CFReleaseNull(versionNumber);
258 CFReleaseNull(publicBytes);
259 return pi;
260 }
261
262 SOSPeerInfoRef SOSPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) {
263 return SOSPeerInfoCreate_Internal(allocator, gestalt, signingKey, error, ^(CFMutableDictionaryRef description) {});
264 }
265
266 SOSPeerInfoRef 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
274 SOSPeerInfoRef 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
289 SOSPeerInfoRef 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
305 exit:
306 CFReleaseNull(pub_key);
307 return pi;
308 }
309
310 SOSPeerInfoRef 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
406 fail:
407 CFReleaseNull(pi);
408 CFReleaseNull(pl);
409 CFReleaseNull(pubKey);
410
411 return NULL;
412 }
413
414 SOSPeerInfoRef 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
421 static 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
432 static 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
440 CFComparisonResult 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
458 static CFHashCode SOSPeerInfoHash(CFTypeRef cf) {
459 SOSPeerInfoRef peer = (SOSPeerInfoRef) cf;
460
461 return CFHash(peer->description) ^ CFHash(peer->signature);
462 }
463
464 static 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
477 CFDictionaryRef SOSPeerInfoCopyPeerGestalt(SOSPeerInfoRef pi) {
478 CFRetain(pi->gestalt);
479 return pi->gestalt;
480 }
481
482 CFStringRef SOSPeerInfoGetTransportType(SOSPeerInfoRef peer){
483 return peer->transportType;
484 }
485
486 CFStringRef SOSPeerInfoGetDeviceID(SOSPeerInfoRef peer){
487 return peer->deviceID;
488 }
489
490 void SOSPeerInfoSetDeviceID(SOSPeerInfoRef peer, CFStringRef IDS){
491 CFReleaseNull(peer->deviceID);
492 peer->deviceID = CFStringCreateCopy(kCFAllocatorDefault, IDS);
493 }
494
495 CFStringRef SOSPeerInfoGetPeerName(SOSPeerInfoRef peer) {
496 return SOSPeerInfoLookupGestaltValue(peer, kPIUserDefinedDeviceName);
497 }
498
499 CFStringRef SOSPeerInfoGetPeerDeviceType(SOSPeerInfoRef peer) {
500 return SOSPeerInfoLookupGestaltValue(peer, kPIDeviceModelName);
501 }
502
503 CFIndex 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
511 CFTypeRef SOSPeerInfoLookupGestaltValue(SOSPeerInfoRef pi, CFStringRef key) {
512 return CFDictionaryGetValue(pi->gestalt, key);
513 }
514
515 CFStringRef SOSPeerInfoGetPeerID(SOSPeerInfoRef pi) {
516 return pi ? pi->id : NULL;
517 }
518
519 CFIndex SOSPeerInfoGetVersion(SOSPeerInfoRef pi) {
520 // TODO: Encode this in the DER.
521 return pi->version;
522 }
523
524 bool 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
538 bool 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
556 static 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
565 static 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
572 static 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
583 SOSPeerInfoRef 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
610 fail:
611 CFReleaseNull(usersig);
612 CFReleaseNull(pi);
613 return result;
614 }
615
616 bool 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
632 exit:
633 return result;
634 }
635
636
637 static 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
646 CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetApplicationDate(SOSPeerInfoRef pi) {
647 return sosPeerInfoGetDate(pi, sApplicationDate);
648 }
649
650 CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetRetirementDate(SOSPeerInfoRef pi) {
651 return sosPeerInfoGetDate(pi, sRetirementDate);
652 }
653
654
655 size_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
668 uint8_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
674 CFDataRef 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
689 CFStringRef SOSPeerGestaltGetName(CFDictionaryRef gestalt) {
690 CFStringRef name = SOSPeerGestaltGetAnswer(gestalt, kPIUserDefinedDeviceName);
691 return isString(name) ? name : NULL;
692 }
693
694 CFTypeRef SOSPeerGestaltGetAnswer(CFDictionaryRef gestalt, CFStringRef question) {
695 return gestalt ? CFDictionaryGetValue(gestalt, question) : NULL;
696 }
697
698 //
699 // Peer Retirement
700 //
701
702
703 SOSPeerInfoRef 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
718 fail:
719 CFReleaseNull(pi);
720 return NULL;
721 }
722
723 CFStringRef 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
740 err:
741 CFReleaseNull(now);
742 CFReleaseNull(retirement);
743 return retval;
744 }
745
746 bool 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
756 bool SOSPeerInfoIsRetirementTicket(SOSPeerInfoRef pi) {
757 CFDataRef flag = CFDictionaryGetValue(pi->description, sRetirementDate);
758 return flag != NULL;
759 }
760
761 bool SOSPeerInfoIsCloudIdentity(SOSPeerInfoRef pi) {
762 CFTypeRef value = CFDictionaryGetValue(pi->description, sCloudIdentityKey);
763 return CFEqualSafe(value, kCFBooleanTrue);
764 }
765
766 SOSPeerInfoRef 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