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