]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSPiggyback.m
Security-58286.31.2.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSPiggyback.m
1 /*
2 * Copyright (c) 2015 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 #include <Security/SecureObjectSync/SOSCloudCircle.h>
25 #include <Security/SecureObjectSync/SOSInternal.h>
26 #include <Security/SecureObjectSync/SOSCircleDer.h>
27 #include <Security/SecureObjectSync/SOSAccountPriv.h>
28
29 #include <Security/Security.h>
30 #include <Security/SecKeyPriv.h>
31
32 #include <securityd/SecItemSchema.h>
33 #include <Security/SecItem.h>
34 #include <Security/SecItemPriv.h>
35
36 #include "SOSPiggyback.h"
37
38 #include "utilities/der_date.h"
39 #include "utilities/der_plist.h"
40 #include <utilities/der_plist_internal.h>
41 #include <corecrypto/ccder.h>
42
43 static size_t SOSPiggyBackBlobGetDEREncodedSize(SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, CFErrorRef *error) {
44 size_t total_payload = 0;
45
46 CFDataRef publicBytes = NULL;
47 OSStatus result = SecKeyCopyPublicBytes(pubKey, &publicBytes);
48
49 if (result != errSecSuccess) {
50 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
51 return 0;
52 }
53
54 require_quiet(accumulate_size(&total_payload, der_sizeof_number(gencount, error)), errOut);
55 require_quiet(accumulate_size(&total_payload, der_sizeof_data_or_null(publicBytes, error)), errOut);
56 require_quiet(accumulate_size(&total_payload, der_sizeof_data_or_null(signature, error)), errOut);
57 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, total_payload);
58
59 errOut:
60 SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error);
61 return 0;
62 }
63
64
65 static uint8_t* SOSPiggyBackBlobEncodeToDER(SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) {
66 CFDataRef publicBytes = NULL;
67
68 OSStatus result = SecKeyCopyPublicBytes(pubKey, &publicBytes);
69
70 if (result != errSecSuccess) {
71 SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
72 return NULL;
73 }
74
75
76 der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
77 der_encode_number(gencount, error, der,
78 der_encode_data_or_null(publicBytes, error, der,
79 der_encode_data_or_null(signature, error, der, der_end))));
80 return der_end;
81 }
82
83 CFDataRef SOSPiggyBackBlobCopyEncodedData(SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, CFErrorRef *error)
84 {
85 return CFDataCreateWithDER(kCFAllocatorDefault, SOSPiggyBackBlobGetDEREncodedSize(gencount, pubKey, signature, error), ^uint8_t*(size_t size, uint8_t *buffer) {
86 return SOSPiggyBackBlobEncodeToDER(gencount, pubKey, signature, error, buffer, (uint8_t *) buffer + size);
87 });
88 }
89
90 bool SOSPiggyBackAddToKeychain(NSArray<NSData*>* identities, NSArray<NSDictionary*>* tlks)
91 {
92 [tlks enumerateObjectsUsingBlock:^(NSDictionary* item, NSUInteger idx, BOOL * _Nonnull stop) {
93
94 NSData* v_data = item[(__bridge NSString*)kSecValueData];
95 NSString *acct = item[(__bridge NSString*)kSecAttrAccount];
96 NSString *srvr = item[(__bridge NSString*)kSecAttrServer];
97
98 NSData* base64EncodedVData = [v_data base64EncodedDataWithOptions:0];
99
100 NSMutableDictionary* query = [@{
101 (id)kSecClass : (id)kSecClassInternetPassword,
102 (id)kSecAttrNoLegacy : @YES,
103 (id)kSecAttrAccessGroup: @"com.apple.security.ckks",
104 (id)kSecAttrDescription: @"tlk-piggy",
105 (id)kSecAttrSynchronizable : (id)kCFBooleanFalse,
106 (id)kSecAttrSyncViewHint : (id)kSecAttrViewHintPCSMasterKey,
107 (id)kSecAttrServer: srvr,
108 (id)kSecAttrAccount: [NSString stringWithFormat: @"%@-piggy", acct],
109 (id)kSecAttrPath: acct,
110 (id)kSecAttrIsInvisible: @YES,
111 (id)kSecValueData : base64EncodedVData,
112 } mutableCopy];
113
114 OSStatus status = SecItemAdd((__bridge CFDictionaryRef) query, NULL);
115
116 if(status == errSecDuplicateItem) {
117 // Sure, okay, fine, we'll update.
118 NSMutableDictionary* update = [@{(id)kSecValueData: v_data,
119 } mutableCopy];
120
121 status = SecItemUpdate((__bridge CFDictionaryRef) query, (__bridge CFDictionaryRef)update);
122 }
123
124 if(status) {
125 secerror("Couldn't save tlks to keychain %d", (int)status);
126 }
127 }];
128 [identities enumerateObjectsUsingBlock:^(NSData *v_data, NSUInteger idx, BOOL *stop) {
129 SecKeyRef publicKey = NULL;
130 SecKeyRef privKey = NULL;
131 CFDataRef public_key_hash = NULL;
132 NSMutableDictionary* query = NULL;
133 CFStringRef peerid = NULL;
134 OSStatus status;
135
136 NSDictionary *keyAttributes = @{
137 (__bridge id)kSecAttrKeyClass : (__bridge id)kSecAttrKeyClassPrivate,
138 (__bridge id)kSecAttrKeyType : (__bridge id)kSecAttrKeyTypeEC,
139 };
140 //create private key
141 privKey = SecKeyCreateWithData((__bridge CFDataRef)v_data, (__bridge CFDictionaryRef)keyAttributes, NULL);
142 require_action_quiet(privKey, exit, secnotice("piggy","privKey failed to be created"));
143 //create public key
144 publicKey = SecKeyCreatePublicFromPrivate(privKey);
145 require_action_quiet(privKey, exit, secnotice("piggy","public key failed to be created"));
146
147 //create public_key_hash
148 public_key_hash = SecKeyCopyPublicKeyHash(publicKey);
149 require_action_quiet(privKey, exit, secnotice("piggy","can't create public key hash"));
150
151 peerid = SOSCopyIDOfKey(publicKey, NULL);
152
153 query = [@{
154 (id)kSecClass : (id)kSecClassKey,
155 (id)kSecAttrNoLegacy : @YES,
156 (id)kSecAttrAccessGroup: @"com.apple.security.sos",
157 (id)kSecAttrApplicationLabel : (__bridge NSData*)public_key_hash,
158 (id)kSecAttrLabel : [NSString stringWithFormat: @"Cloud Identity-piggy-%@", peerid],
159 (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
160 (id)kSecUseTombstones : (id)kCFBooleanTrue,
161 (id)kSecValueData : v_data,
162 } mutableCopy];
163
164 status = SecItemAdd((__bridge CFDictionaryRef) query, NULL);
165
166 if(status == errSecDuplicateItem) {
167 // Sure, okay, fine, we'll update.
168 NSMutableDictionary* update = [@{
169 (id)kSecValueData: v_data,
170 } mutableCopy];
171 query[(id)kSecValueData] = nil;
172 status = SecItemUpdate((__bridge CFDictionaryRef) query, (__bridge CFDictionaryRef)update);
173 }
174
175 if(status) {
176 secerror("Couldn't save backupV0 to keychain %d", (int)status);
177 }
178
179 exit:
180 CFReleaseNull(publicKey);
181 CFReleaseNull(privKey);
182 CFReleaseNull(peerid);
183 CFReleaseNull(public_key_hash);
184 secnotice("piggy","key not available");
185 }];
186
187 return true;
188 }
189
190 static const uint8_t *
191 piggy_decode_data(const uint8_t *der, const uint8_t *der_end, NSData **data)
192 {
193 size_t body_length = 0;
194 const uint8_t *body = ccder_decode_tl(CCDER_OCTET_STRING, &body_length, der, der_end);
195 if(body == NULL)
196 return NULL;
197 *data = [NSData dataWithBytes:body length:body_length];
198 return body + body_length;
199
200 }
201
202 static NSMutableArray *
203 parse_identies(const uint8_t *der, const uint8_t *der_end)
204 {
205 NSMutableArray<NSData *>* array = [NSMutableArray array];
206
207 while (der != der_end) {
208 NSData *data = NULL;
209
210 der = piggy_decode_data(der, der_end, &data);
211 if (der == NULL)
212 return NULL;
213 if (data)
214 [array addObject:data];
215 }
216
217 return array;
218 }
219
220 static NSMutableArray *
221 SOSPiggyCreateDecodedTLKs(const uint8_t *der, const uint8_t *der_end)
222 {
223 NSMutableArray *array = [NSMutableArray array];
224
225 while (der != der_end) {
226 NSMutableDictionary<NSString *,id> *item = [NSMutableDictionary dictionary];
227 NSData *data = NULL;
228 size_t item_size = 0;
229
230 const uint8_t *item_der = ccder_decode_tl(CCDER_CONSTRUCTED_SEQUENCE, &item_size, der, der_end);
231 if (item_der == NULL)
232 return NULL;
233 const uint8_t *end_item_der = item_der + item_size;
234
235 item_der = piggy_decode_data(item_der, end_item_der, &data);
236 if (der == NULL)
237 return NULL;
238
239 item[(__bridge id)kSecValueData] = data;
240 data = NULL;
241
242 item_der = piggy_decode_data(item_der, end_item_der, &data);
243 if (item_der == NULL)
244 return NULL;
245 if ([data length] != sizeof(uuid_t)) {
246 return NULL;
247 }
248
249 NSString *uuidString = [[[NSUUID alloc] initWithUUIDBytes:[data bytes]] UUIDString];
250 item[(__bridge id)kSecAttrAccount] = uuidString;
251
252 NSString *view = NULL;
253 uint64_t r = 0;
254 const uint8_t *choice_der = NULL;
255 choice_der = ccder_decode_uint64(&r, item_der, end_item_der);
256 if (choice_der == NULL) {
257 /* try other branch of CHOICE, a string */
258 CFErrorRef localError = NULL;
259 CFStringRef string = NULL;
260
261 choice_der = der_decode_string(NULL, 0, &string, &localError, item_der, end_item_der);
262 if (choice_der == NULL || string == NULL) {
263 CFReleaseNull(string);
264 secnotice("piggy", "Failed to parse view name");
265 return NULL;
266 }
267 CFReleaseNull(localError);
268 item_der = choice_der;
269 view = CFBridgingRelease(string);
270 } else {
271 if (r == kTLKManatee)
272 view = @"Manatee";
273 else if (r == kTLKEngram)
274 view = @"Engram";
275 else if (r == kTLKAutoUnlock)
276 view = @"AutoUnlock";
277 else if (r == kTLKHealth)
278 view = @"Health";
279 else {
280 secnotice("piggy", "unexpected view number: %d", (int)r);
281 return NULL;
282 }
283 item_der = choice_der;
284 }
285 item[(__bridge id)kSecAttrServer] = view;
286
287 if (item_der != end_item_der) {
288 return NULL;
289 }
290 secnotice("piggy", "Adding %@ %@", view, uuidString);
291
292 [array addObject:item];
293
294 der = end_item_der;
295 }
296 return array;
297 }
298
299 NSDictionary *
300 SOSPiggyCopyInitialSyncData(const uint8_t** der, const uint8_t *der_end)
301 {
302 NSMutableDictionary *results = [NSMutableDictionary dictionary];
303 size_t seq_size;
304
305 const uint8_t *topSeq = ccder_decode_tl(CCDER_CONSTRUCTED_SEQUENCE, &seq_size, *der, der_end);
306 if(topSeq == NULL){
307 secnotice("piggy", "Failed to parse CONS SEQ");
308 return NULL;
309 }
310
311 /* parse idents */
312 const uint8_t *ider = ccder_decode_tl(CCDER_CONSTRUCTED_SEQUENCE, &seq_size, topSeq, der_end);
313 if (ider == NULL){
314 secnotice("piggy", "Failed to parse CONS SEQ of ident");
315 return NULL;
316 }
317 NSArray *idents = parse_identies(ider, ider + seq_size);
318 if (idents)
319 results[@"idents"] = idents;
320 topSeq = ider + seq_size;
321
322 /* parse tlks */
323 const uint8_t *tder = ccder_decode_tl(CCDER_CONSTRUCTED_SEQUENCE, &seq_size, topSeq, der_end);
324 if (tder == NULL){
325 secnotice("piggy", "Failed to parse CONS SEQ of TLKs");
326 return NULL;
327 }
328 NSMutableArray *tlks = SOSPiggyCreateDecodedTLKs(tder, tder + seq_size);
329 if (tlks)
330 results[@"tlks"] = tlks;
331 *der = tder + seq_size;
332
333 /* Don't check length here so we can add more data */
334
335 if(results.count == 0 || tlks.count == 0){
336 secnotice("piggy","NO DATA, falling back to waiting 5 minutes for initial sync to finish");
337 results = NULL;
338 }
339
340 return results;
341 }
342
343 bool
344 SOSPiggyBackBlobCreateFromDER(SOSGenCountRef *retGencount,
345 SecKeyRef *retPubKey,
346 CFDataRef *retSignature,
347 const uint8_t** der_p, const uint8_t *der_end,
348 PiggyBackProtocolVersion version,
349 bool *setInitialSyncTimeoutToV0,
350 CFErrorRef *error)
351 {
352 const uint8_t *sequence_end;
353 SOSGenCountRef gencount = NULL;
354 CFDataRef signature = NULL;
355 CFDataRef publicBytes = NULL;
356
357 bool res = true;
358
359 *setInitialSyncTimeoutToV0 = true;
360
361 *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
362 require_action_quiet(sequence_end != NULL, errOut,
363 SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Blob DER"), (error != NULL) ? *error : NULL, error));
364 *der_p = der_decode_number(kCFAllocatorDefault, 0, &gencount, error, *der_p, sequence_end);
365 *der_p = der_decode_data_or_null(kCFAllocatorDefault, &publicBytes, error, *der_p, sequence_end);
366 *der_p = der_decode_data_or_null(kCFAllocatorDefault, &signature, error, *der_p, sequence_end);
367
368 if(version == kPiggyV1){
369 NSDictionary* initialSyncDict = SOSPiggyCopyInitialSyncData(der_p, der_end);
370 if (initialSyncDict) {
371 NSArray* idents = initialSyncDict[@"idents"];
372 NSArray* tlks = initialSyncDict[@"tlks"];
373 SOSPiggyBackAddToKeychain(idents, tlks);
374 *setInitialSyncTimeoutToV0 = false;
375 }
376 /* Don't check length here so we can add more data */
377 }
378 else{ //V0
379 secnotice("piggy","Piggybacking version 0, setting initial sync timeout to 5 minutes");
380 *setInitialSyncTimeoutToV0 = true;
381 require_action_quiet(*der_p && *der_p == der_end, errOut,
382 SOSCreateError(kSOSErrorBadFormat, CFSTR("Didn't consume all bytes for pbblob"), (error != NULL) ? *error : NULL, error));
383 }
384
385 *retPubKey = SecKeyCreateFromPublicData(kCFAllocatorDefault, kSecECDSAAlgorithmID, publicBytes);
386 require_quiet(*retPubKey, errOut);
387 *retGencount = gencount;
388 *retSignature = signature;
389
390 res = true;
391
392 errOut:
393 if(!res) {
394 CFReleaseNull(gencount);
395 CFReleaseNull(publicBytes);
396 CFReleaseNull(signature);
397 }
398
399 return res;
400 }
401
402 bool
403 SOSPiggyBackBlobCreateFromData(SOSGenCountRef *gencount,
404 SecKeyRef *pubKey,
405 CFDataRef *signature,
406 CFDataRef blobData,
407 PiggyBackProtocolVersion version,
408 bool *setInitialSyncTimeoutToV0,
409 CFErrorRef *error)
410 {
411 size_t size = CFDataGetLength(blobData);
412 const uint8_t *der = CFDataGetBytePtr(blobData);
413 return SOSPiggyBackBlobCreateFromDER(gencount, pubKey, signature, &der, der + size, version, setInitialSyncTimeoutToV0, error);
414 }