1//
2//  SOSFullPeerInfo.c
3//  sec
4//
5//  Created by Mitch Adler on 10/26/12.
6//
7//
8
9#include <AssertMacros.h>
10
11#include <SecureObjectSync/SOSFullPeerInfo.h>
12#include <SecureObjectSync/SOSCircle.h>
13
14#include <SecureObjectSync/SOSInternal.h>
15
16#include <Security/SecKeyPriv.h>
17#include <Security/SecItemPriv.h>
18#include <Security/SecOTR.h>
19#include <CoreFoundation/CFArray.h>
20#include <dispatch/dispatch.h>
21
22#include <stdlib.h>
23#include <assert.h>
24
25#include <utilities/SecCFWrappers.h>
26#include <utilities/SecCFRelease.h>
27
28#include <utilities/der_plist.h>
29#include <utilities/der_plist_internal.h>
30#include <corecrypto/ccder.h>
31
32#include <CommonCrypto/CommonDigest.h>
33#include <CommonCrypto/CommonDigestSPI.h>
34
35#include <CoreFoundation/CoreFoundation.h>
36
37#include "utilities/iOSforOSX.h"
38
39#include <AssertMacros.h>
40
41#include <utilities/SecCFError.h>
42
43// for OS X
44#ifdef __cplusplus
45extern "C" {
46#endif
47
48//---- missing
49
50extern OSStatus SecKeyCopyPublicBytes(SecKeyRef key, CFDataRef* publicBytes);
51extern SecKeyRef SecKeyCreatePublicFromPrivate(SecKeyRef privateKey);
52#ifdef __cplusplus
53}
54#endif
55
56struct __OpaqueSOSFullPeerInfo {
57    CFRuntimeBase          _base;
58
59    SOSPeerInfoRef         peer_info;
60    CFDataRef              key_ref;
61};
62
63CFGiblisWithHashFor(SOSFullPeerInfo);
64
65
66static CFStringRef sPublicKeyKey = CFSTR("PublicSigningKey");
67static CFStringRef sNameKey      = CFSTR("DeviceName");
68static CFStringRef sVersionKey   = CFSTR("ConflictVersion");
69
70CFStringRef kSOSFullPeerInfoDescriptionKey = CFSTR("SOSFullPeerInfoDescription");
71CFStringRef kSOSFullPeerInfoSignatureKey = CFSTR("SOSFullPeerInfoSignature");
72CFStringRef kSOSFullPeerInfoNameKey = CFSTR("SOSFullPeerInfoName");
73
74SOSFullPeerInfoRef SOSFullPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) {
75    SOSFullPeerInfoRef fpi = CFTypeAllocate(SOSFullPeerInfo, struct __OpaqueSOSFullPeerInfo, allocator);
76
77    fpi->peer_info = SOSPeerInfoCreate(allocator, gestalt, signingKey, error);
78    if (fpi->peer_info == NULL) {
79        CFReleaseNull(fpi);
80        goto exit;
81    }
82
83    OSStatus result = SecKeyCopyPersistentRef(signingKey, &fpi->key_ref);
84
85    if (result != errSecSuccess) {
86        CFReleaseNull(fpi);
87        goto exit;
88    }
89
90exit:
91    return fpi;
92}
93
94
95
96SOSFullPeerInfoRef SOSFullPeerInfoCreateCloudIdentity(CFAllocatorRef allocator, SOSPeerInfoRef peer, CFErrorRef* error) {
97    SOSFullPeerInfoRef fpi = CFTypeAllocate(SOSFullPeerInfo, struct __OpaqueSOSFullPeerInfo, allocator);
98
99    SecKeyRef pubKey = NULL;
100
101    fpi->peer_info = peer;
102    CFRetainSafe(fpi->peer_info);
103    if (fpi->peer_info == NULL) {
104        CFReleaseNull(fpi);
105        goto exit;
106    }
107
108    pubKey = SOSPeerInfoCopyPubKey(peer);
109
110    fpi->key_ref = SecKeyCreatePersistentRefToMatchingPrivateKey(pubKey, error);
111
112    if (fpi->key_ref == NULL) {
113        CFReleaseNull(fpi);
114        goto exit;
115    }
116
117exit:
118    CFReleaseNull(pubKey);
119    return fpi;
120}
121
122
123SOSFullPeerInfoRef SOSFullPeerInfoCreateFromDER(CFAllocatorRef allocator, CFErrorRef* error,
124                                        const uint8_t** der_p, const uint8_t *der_end) {
125    SOSFullPeerInfoRef fpi = CFTypeAllocate(SOSFullPeerInfo, struct __OpaqueSOSFullPeerInfo, allocator);
126
127    const uint8_t *sequence_end;
128
129    *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
130
131    fpi->peer_info = SOSPeerInfoCreateFromDER(allocator, error, der_p, der_end);
132    require_quiet(fpi->peer_info != NULL, fail);
133
134    *der_p = der_decode_data(allocator, kCFPropertyListImmutable, &fpi->key_ref, error, *der_p, sequence_end);
135    require_quiet(*der_p != NULL, fail);
136
137    return fpi;
138
139fail:
140    CFReleaseNull(fpi);
141    return NULL;
142}
143
144SOSFullPeerInfoRef SOSFullPeerInfoCreateFromData(CFAllocatorRef allocator, CFDataRef fullPeerData, CFErrorRef *error)
145{
146    size_t size = CFDataGetLength(fullPeerData);
147    const uint8_t *der = CFDataGetBytePtr(fullPeerData);
148    SOSFullPeerInfoRef inflated = SOSFullPeerInfoCreateFromDER(allocator, error, &der, der + size);
149    return inflated;
150}
151
152static void SOSFullPeerInfoDestroy(CFTypeRef aObj) {
153    SOSFullPeerInfoRef fpi = (SOSFullPeerInfoRef) aObj;
154
155    CFReleaseNull(fpi->peer_info);
156    CFReleaseNull(fpi->key_ref);
157}
158
159static Boolean SOSFullPeerInfoCompare(CFTypeRef lhs, CFTypeRef rhs) {
160    SOSFullPeerInfoRef lpeer = (SOSFullPeerInfoRef) lhs;
161    SOSFullPeerInfoRef rpeer = (SOSFullPeerInfoRef) rhs;
162
163    if (!CFEqual(lpeer->peer_info, rpeer->peer_info))
164        return false;
165
166    if (CFEqual(lpeer->key_ref, rpeer->key_ref))
167        return true;
168
169    SecKeyRef lpk = SOSFullPeerInfoCopyDeviceKey(lpeer, NULL);
170    SecKeyRef rpk = SOSFullPeerInfoCopyDeviceKey(rpeer, NULL);
171
172    bool match = lpk && rpk && CFEqual(lpk, rpk);
173
174    CFReleaseNull(lpk);
175    CFReleaseNull(rpk);
176
177    return match;
178}
179
180static CFHashCode   SOSFullPeerInfoHash(CFTypeRef cf) {
181    SOSFullPeerInfoRef peer = (SOSFullPeerInfoRef) cf;
182
183    return CFHash(peer->peer_info);
184}
185
186static CFStringRef SOSFullPeerInfoCopyDescription(CFTypeRef aObj) {
187    SOSFullPeerInfoRef fpi = (SOSFullPeerInfoRef) aObj;
188
189    return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SOSFullPeerInfo@%p: \"%@\">"), fpi, fpi->peer_info);
190}
191
192bool SOSFullPeerInfoUpdateGestalt(SOSFullPeerInfoRef peer, CFDictionaryRef gestalt, CFErrorRef* error)
193{
194    SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(peer, error);
195    require_quiet(device_key, fail);
196
197    SOSPeerInfoRef newPeer = SOSPeerInfoCopyWithGestaltUpdate(kCFAllocatorDefault, peer->peer_info,
198                                                              gestalt, device_key, error);
199
200    require_quiet(newPeer, fail);
201
202    CFReleaseNull(peer->peer_info);
203    peer->peer_info = newPeer;
204    newPeer = NULL;
205
206    CFReleaseNull(device_key);
207    return true;
208
209fail:
210    CFReleaseNull(device_key);
211    return false;
212}
213
214
215bool SOSFullPeerInfoValidate(SOSFullPeerInfoRef peer, CFErrorRef* error) {
216    return true;
217}
218
219bool SOSFullPeerInfoPurgePersistentKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error) {
220    CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
221                                                         kSecValuePersistentRef, fullPeer->key_ref,
222                                                         kSecUseTombstones, kCFBooleanFalse,
223                                                         NULL);
224    SecItemDelete(query);
225    CFReleaseNull(query);
226
227    return true;
228}
229
230SOSPeerInfoRef SOSFullPeerInfoGetPeerInfo(SOSFullPeerInfoRef fullPeer)
231{
232    return fullPeer?fullPeer->peer_info:NULL;
233}
234
235SecKeyRef  SOSFullPeerInfoCopyDeviceKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error)
236{
237    SecKeyRef device_key = NULL;
238
239    require(fullPeer->key_ref, fail);
240
241    OSStatus result = SecKeyFindWithPersistentRef(fullPeer->key_ref, &device_key);
242
243    require_action_quiet(result == errSecSuccess, fail, SecError(result, error, CFSTR("Finding Persistent Ref")));
244
245    return device_key;
246
247fail:
248    CFReleaseNull(device_key);
249    return NULL;
250}
251
252//
253// MARK: Encode and decode
254//
255size_t      SOSFullPeerInfoGetDEREncodedSize(SOSFullPeerInfoRef peer, CFErrorRef *error)
256{
257    size_t peer_size = SOSPeerInfoGetDEREncodedSize(peer->peer_info, error);
258    if (peer_size == 0)
259        return 0;
260
261    size_t ref_size = der_sizeof_data(peer->key_ref, error);
262    if (ref_size == 0)
263        return 0;
264
265    return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
266                        peer_size + ref_size);
267}
268
269uint8_t* SOSFullPeerInfoEncodeToDER(SOSFullPeerInfoRef peer, CFErrorRef* error, const uint8_t* der, uint8_t* der_end)
270{
271    return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
272           SOSPeerInfoEncodeToDER(peer->peer_info, error, der,
273           der_encode_data(peer->key_ref, error, der, der_end)));
274}
275
276CFDataRef SOSFullPeerInfoCopyEncodedData(SOSFullPeerInfoRef peer, CFAllocatorRef allocator, CFErrorRef *error)
277{
278    size_t size = SOSFullPeerInfoGetDEREncodedSize(peer, error);
279    if (size == 0)
280        return NULL;
281    uint8_t buffer[size];
282    uint8_t* start = SOSFullPeerInfoEncodeToDER(peer, error, buffer, buffer + sizeof(buffer));
283    CFDataRef result = CFDataCreate(kCFAllocatorDefault, start, size);
284    return result;
285}
286
287bool SOSFullPeerInfoPromoteToApplication(SOSFullPeerInfoRef fpi, SecKeyRef user_key, CFErrorRef *error)
288{
289    bool success = false;
290    SOSPeerInfoRef old_pi = NULL;
291
292    SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(fpi, error);
293    require_quiet(device_key, exit);
294
295    old_pi = fpi->peer_info;
296    fpi->peer_info = SOSPeerInfoCopyAsApplication(old_pi, user_key, device_key, error);
297
298    require_action_quiet(fpi->peer_info, exit, fpi->peer_info = old_pi; old_pi = NULL);
299
300    success = true;
301
302exit:
303    CFReleaseSafe(old_pi);
304    CFReleaseSafe(device_key);
305    return success;
306}
307
308bool SOSFullPeerInfoUpgradeSignatures(SOSFullPeerInfoRef fpi, SecKeyRef user_key, CFErrorRef *error)
309{
310    bool success = false;
311    SOSPeerInfoRef old_pi = NULL;
312
313    SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(fpi, error);
314    require_quiet(device_key, exit);
315
316    old_pi = fpi->peer_info;
317    fpi->peer_info = SOSPeerInfoUpgradeSignatures(NULL, user_key, device_key, old_pi, error);
318
319    require_action_quiet(fpi->peer_info, exit, fpi->peer_info = old_pi; old_pi = NULL);
320
321    success = true;
322
323exit:
324    CFReleaseSafe(old_pi);
325    CFReleaseSafe(device_key);
326    return success;
327}
328
329//
330//
331//
332
333SOSPeerInfoRef SOSFullPeerInfoPromoteToRetiredAndCopy(SOSFullPeerInfoRef fpi, CFErrorRef *error)
334{
335    SOSPeerInfoRef peer_to_free = NULL;
336    SOSPeerInfoRef retired_peer = NULL;
337    SecKeyRef key = SOSFullPeerInfoCopyDeviceKey(fpi, error);
338    require_quiet(key, error_out);
339
340    retired_peer = SOSPeerInfoCreateRetirementTicket(NULL, key, fpi->peer_info, error);
341
342    require_quiet(retired_peer, error_out);
343
344    peer_to_free = fpi->peer_info;
345    fpi->peer_info = retired_peer;
346    CFRetainSafe(fpi->peer_info);
347
348error_out:
349    CFReleaseNull(key);
350    CFReleaseNull(peer_to_free);
351    return retired_peer;
352}
353
354
355
356