]> git.saurik.com Git - apple/security.git/blob - sec/SOSCircle/SecureObjectSync/SOSPeerInfo.c
Security-55471.14.8.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 // 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
385 static CFHashCode SOSPeerInfoHash(CFTypeRef cf) {
386 SOSPeerInfoRef peer = (SOSPeerInfoRef) cf;
387
388 return CFHash(peer->description) ^ CFHash(peer->signature);
389 }
390
391 static 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
402 CFDictionaryRef SOSPeerInfoCopyPeerGestalt(SOSPeerInfoRef pi) {
403 CFRetain(pi->gestalt);
404 return pi->gestalt;
405 }
406
407 CFStringRef SOSPeerInfoGetPeerName(SOSPeerInfoRef peer) {
408 return SOSPeerInfoLookupGestaltValue(peer, kPIUserDefinedDeviceName);
409 }
410
411 CFStringRef SOSPeerInfoGetPeerDeviceType(SOSPeerInfoRef peer) {
412 return SOSPeerInfoLookupGestaltValue(peer, kPIDeviceModelName);
413 }
414
415 CFTypeRef SOSPeerInfoLookupGestaltValue(SOSPeerInfoRef pi, CFStringRef key) {
416 return CFDictionaryGetValue(pi->gestalt, key);
417 }
418
419 CFStringRef SOSPeerInfoGetPeerID(SOSPeerInfoRef pi) {
420 return pi ? pi->id : NULL;
421 }
422
423 CFIndex SOSPeerInfoGetVersion(SOSPeerInfoRef pi) {
424 // TODO: Encode this in the DER.
425 return pi->version;
426 }
427
428 bool 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
442 bool 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
460 static 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
469 static 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
476 static 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
487 SOSPeerInfoRef 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
514 fail:
515 CFReleaseNull(usersig);
516 CFReleaseNull(pi);
517 return result;
518 }
519
520 bool 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
536 exit:
537 return result;
538 }
539
540
541 static 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
550 CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetApplicationDate(SOSPeerInfoRef pi) {
551 return sosPeerInfoGetDate(pi, sApplicationDate);
552 }
553
554 CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetRetirementDate(SOSPeerInfoRef pi) {
555 return sosPeerInfoGetDate(pi, sRetirementDate);
556 }
557
558
559 size_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
572 uint8_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
578 CFDataRef 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
593 CFMutableArrayRef 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
620 fail:
621 CFReleaseNull(pia);
622 *der_p = NULL;
623 return NULL;
624 }
625
626 size_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
654 uint8_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
676 CFArrayRef 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
693 xpc_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
712 CFStringRef SOSPeerGestaltGetName(CFDictionaryRef gestalt) {
713 CFStringRef name = SOSPeerGestaltGetAnswer(gestalt, kPIUserDefinedDeviceName);
714 return isString(name) ? name : NULL;
715 }
716
717 CFTypeRef SOSPeerGestaltGetAnswer(CFDictionaryRef gestalt, CFStringRef question) {
718 return gestalt ? CFDictionaryGetValue(gestalt, question) : NULL;
719 }
720
721 //
722 // Peer Retirement
723 //
724
725
726 SOSPeerInfoRef 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
741 fail:
742 CFReleaseNull(pi);
743 return NULL;
744 }
745
746 CFStringRef 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
763 err:
764 CFReleaseNull(now);
765 CFReleaseNull(retirement);
766 return retval;
767 }
768
769 bool 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
779 bool SOSPeerInfoIsRetirementTicket(SOSPeerInfoRef pi) {
780 CFDataRef flag = CFDictionaryGetValue(pi->description, sRetirementDate);
781 return flag != NULL;
782 }
783
784 bool SOSPeerInfoIsCloudIdentity(SOSPeerInfoRef pi) {
785 CFTypeRef value = CFDictionaryGetValue(pi->description, sCloudIdentityKey);
786 return CFEqualSafe(value, kCFBooleanTrue);
787 }
788
789 SOSPeerInfoRef 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