]>
Commit | Line | Data |
---|---|---|
5c19dc3a A |
1 | /* |
2 | * Copyright (c) 2013-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 <stdio.h> | |
26 | #include <stdlib.h> | |
27 | #include <assert.h> | |
28 | #include <AssertMacros.h> | |
29 | #include "SOSAccountPriv.h" | |
30 | #include "SOSViews.h" | |
31 | ||
32 | #include <utilities/SecCFWrappers.h> | |
33 | #include <utilities/SecCoreCrypto.h> | |
fa7225c8 A |
34 | #include <utilities/SecBuffer.h> |
35 | ||
5c19dc3a A |
36 | #include <Security/SecureObjectSync/SOSKVSKeys.h> |
37 | #include <SOSPeerInfoDER.h> | |
38 | ||
e0e0d90e A |
39 | #include <Security/SecureObjectSync/SOSTransport.h> |
40 | ||
5c19dc3a | 41 | #include <Security/SecureObjectSync/SOSPeerInfoCollections.h> |
fa7225c8 | 42 | #include <os/state_private.h> |
5c19dc3a | 43 | |
5c19dc3a A |
44 | |
45 | static SOSAccountRef SOSAccountCreateFromRemainingDER_v6(CFAllocatorRef allocator, | |
46 | SOSDataSourceFactoryRef factory, | |
47 | CFErrorRef* error, | |
48 | const uint8_t** der_p, const uint8_t *der_end) | |
49 | { | |
50 | SOSAccountRef result = NULL; | |
51 | SOSAccountRef account = NULL; | |
52 | CFArrayRef array = NULL; | |
53 | CFDictionaryRef retiredPeers = NULL; | |
54 | CFStringRef circle_name = factory->copy_name(factory); | |
55 | ||
56 | { | |
57 | CFDictionaryRef decoded_gestalt = NULL; | |
58 | *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &decoded_gestalt, error, | |
59 | *der_p, der_end); | |
60 | ||
61 | if (*der_p == 0) | |
62 | return NULL; | |
63 | ||
64 | account = SOSAccountCreateBasic(allocator, decoded_gestalt, factory); | |
65 | CFReleaseNull(decoded_gestalt); | |
66 | } | |
67 | ||
68 | *der_p = der_decode_array(kCFAllocatorDefault, 0, &array, error, *der_p, der_end); | |
69 | ||
70 | uint64_t tmp_departure_code = kSOSNeverAppliedToCircle; | |
71 | *der_p = ccder_decode_uint64(&tmp_departure_code, *der_p, der_end); | |
72 | *der_p = ccder_decode_bool(&account->user_public_trusted, *der_p, der_end); | |
73 | *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->user_public, error, *der_p, der_end); | |
74 | *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->previous_public, error, *der_p, der_end); | |
75 | *der_p = der_decode_data_or_null(kCFAllocatorDefault, &account->user_key_parameters, error, *der_p, der_end); | |
76 | ||
77 | *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListMutableContainers, (CFDictionaryRef *) &retiredPeers, error, *der_p, der_end); | |
78 | require_action_quiet(*der_p == der_end, fail, *der_p = NULL); | |
79 | ||
80 | account->departure_code = (enum DepartureReason) tmp_departure_code; | |
81 | ||
82 | CFDictionaryForEach(retiredPeers, ^(const void *key, const void *value) { | |
83 | if (isData(value)) { | |
84 | SOSPeerInfoRef retiree = SOSPeerInfoCreateFromData(kCFAllocatorDefault, NULL, (CFDataRef) value); | |
85 | ||
86 | CFSetAddValue(account->retirees, retiree); | |
87 | ||
88 | CFReleaseNull(retiree); | |
89 | } | |
90 | }); | |
91 | ||
92 | require_quiet(array && *der_p, fail); | |
93 | ||
94 | CFArrayForEach(array, ^(const void *value) { | |
95 | CFDataRef circleData = NULL; | |
96 | CFDataRef fullPeerInfoData = NULL; | |
97 | ||
98 | if (isData(value)) { | |
99 | circleData = (CFDataRef) value; | |
100 | } else if (isArray(value)) { | |
101 | CFArrayRef pair = (CFArrayRef) value; | |
102 | ||
103 | CFTypeRef circleObject = CFArrayGetValueAtIndex(pair, 0); | |
104 | CFTypeRef fullPeerInfoObject = CFArrayGetValueAtIndex(pair, 1); | |
105 | ||
106 | if (CFArrayGetCount(pair) == 2 && isData(circleObject) && isData(fullPeerInfoObject)) { | |
107 | circleData = (CFDataRef) circleObject; | |
108 | fullPeerInfoData = (CFDataRef) fullPeerInfoObject; | |
109 | } | |
110 | } | |
111 | ||
112 | if (circleData) { | |
113 | SOSCircleRef circle = SOSCircleCreateFromData(kCFAllocatorDefault, circleData, error); | |
114 | require_quiet(circle && CFEqualSafe(circle_name, SOSCircleGetName(circle)), fail); | |
115 | ||
116 | account->trusted_circle = CFRetainSafe(circle); | |
117 | ||
118 | if(fullPeerInfoData) { | |
119 | account->my_identity = SOSFullPeerInfoCreateFromData(kCFAllocatorDefault, fullPeerInfoData, NULL); | |
120 | } | |
121 | ||
122 | fail: | |
123 | CFReleaseNull(circle); | |
124 | } | |
125 | }); | |
126 | CFReleaseNull(array); | |
127 | ||
128 | require_action_quiet(SOSAccountEnsureFactoryCircles(account), fail, | |
129 | SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error)); | |
130 | ||
131 | result = CFRetainSafe(account); | |
132 | ||
133 | fail: | |
134 | CFReleaseNull(account); | |
135 | return result; | |
136 | } | |
137 | ||
138 | static size_t der_sizeof_data_optional(CFDataRef data) | |
139 | { | |
140 | return data ? der_sizeof_data(data, NULL) : 0; | |
141 | ||
142 | } | |
143 | ||
144 | static uint8_t* der_encode_data_optional(CFDataRef data, CFErrorRef *error, | |
145 | const uint8_t *der, uint8_t *der_end) | |
146 | { | |
147 | return data ? der_encode_data(data, error, der, der_end) : der_end; | |
148 | ||
149 | } | |
150 | ||
151 | static const uint8_t* der_decode_data_optional(CFAllocatorRef allocator, CFOptionFlags mutability, | |
152 | CFDataRef* data, CFErrorRef *error, | |
153 | const uint8_t* der, const uint8_t *der_end) | |
154 | { | |
155 | const uint8_t *dt_end = der_decode_data(allocator, mutability, data, NULL, der, der_end); | |
156 | ||
157 | return dt_end ? dt_end : der; | |
158 | } | |
159 | ||
160 | static SOSAccountRef SOSAccountCreateFromRemainingDER_v7(CFAllocatorRef allocator, | |
161 | SOSDataSourceFactoryRef factory, | |
162 | CFErrorRef* error, | |
163 | const uint8_t** der_p, const uint8_t *der_end) | |
164 | { | |
165 | SOSAccountRef account = NULL; | |
166 | SOSAccountRef result = NULL; | |
167 | ||
168 | { | |
169 | CFDictionaryRef decoded_gestalt = NULL; | |
170 | *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &decoded_gestalt, error, | |
171 | *der_p, der_end); | |
172 | ||
173 | if (*der_p == 0) | |
174 | return NULL; | |
175 | ||
176 | account = SOSAccountCreateBasic(allocator, decoded_gestalt, factory); | |
177 | CFReleaseNull(decoded_gestalt); | |
178 | } | |
179 | ||
180 | account->trusted_circle = SOSCircleCreateFromDER(kCFAllocatorDefault, error, der_p, der_end); | |
181 | *der_p = der_decode_fullpeer_or_null(kCFAllocatorDefault, &account->my_identity, error, *der_p, der_end); | |
182 | ||
183 | uint64_t tmp_departure_code = kSOSNeverAppliedToCircle; | |
184 | *der_p = ccder_decode_uint64(&tmp_departure_code, *der_p, der_end); | |
185 | *der_p = ccder_decode_bool(&account->user_public_trusted, *der_p, der_end); | |
186 | *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->user_public, error, *der_p, der_end); | |
187 | *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->previous_public, error, *der_p, der_end); | |
188 | *der_p = der_decode_data_or_null(kCFAllocatorDefault, &account->user_key_parameters, error, *der_p, der_end); | |
189 | account->retirees = SOSPeerInfoSetCreateFromArrayDER(kCFAllocatorDefault, &kSOSPeerSetCallbacks, error, der_p, der_end); | |
190 | *der_p = der_decode_data_optional(kCFAllocatorDefault, kCFPropertyListImmutable, &account->backup_key, error, *der_p, der_end); | |
191 | ||
192 | account->departure_code = (enum DepartureReason) tmp_departure_code; | |
193 | ||
194 | require_action_quiet(*der_p && *der_p == der_end, fail, | |
195 | SOSCreateError(kSOSErrorBadFormat, CFSTR("Didn't consume all bytes v7"), (error != NULL) ? *error : NULL, error)); | |
196 | ||
197 | result = CFRetainSafe(account); | |
198 | ||
199 | fail: | |
200 | CFReleaseNull(account); | |
201 | return result; | |
202 | } | |
203 | ||
204 | ||
205 | static SOSAccountRef SOSAccountCreateFromRemainingDER_v8(CFAllocatorRef allocator, | |
206 | SOSDataSourceFactoryRef factory, | |
207 | CFErrorRef* error, | |
208 | const uint8_t** der_p, const uint8_t *der_end) | |
209 | { | |
210 | SOSAccountRef account = NULL; | |
211 | SOSAccountRef result = NULL; | |
212 | ||
213 | { | |
214 | CFDictionaryRef decoded_gestalt = NULL; | |
215 | *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &decoded_gestalt, error, | |
216 | *der_p, der_end); | |
217 | ||
218 | if (*der_p == 0) | |
219 | return NULL; | |
220 | ||
221 | account = SOSAccountCreateBasic(allocator, decoded_gestalt, factory); | |
222 | CFReleaseNull(decoded_gestalt); | |
223 | } | |
224 | CFReleaseNull(account->trusted_circle); | |
225 | account->trusted_circle = SOSCircleCreateFromDER(kCFAllocatorDefault, error, der_p, der_end); | |
226 | CFReleaseNull(account->my_identity); | |
227 | *der_p = der_decode_fullpeer_or_null(kCFAllocatorDefault, &account->my_identity, error, *der_p, der_end); | |
228 | ||
229 | uint64_t tmp_departure_code = kSOSNeverAppliedToCircle; | |
230 | *der_p = ccder_decode_uint64(&tmp_departure_code, *der_p, der_end); | |
231 | *der_p = ccder_decode_bool(&account->user_public_trusted, *der_p, der_end); | |
232 | *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->user_public, error, *der_p, der_end); | |
233 | *der_p = der_decode_public_bytes(kCFAllocatorDefault, kSecECDSAAlgorithmID, &account->previous_public, error, *der_p, der_end); | |
234 | *der_p = der_decode_data_or_null(kCFAllocatorDefault, &account->user_key_parameters, error, *der_p, der_end); | |
235 | CFReleaseNull(account->retirees); | |
236 | account->retirees = SOSPeerInfoSetCreateFromArrayDER(kCFAllocatorDefault, &kSOSPeerSetCallbacks, error, der_p, der_end); | |
237 | *der_p = der_decode_data_optional(kCFAllocatorDefault, kCFPropertyListImmutable, &account->backup_key, error, *der_p, der_end); | |
238 | { | |
239 | CFDictionaryRef expansion = NULL; | |
240 | *der_p = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &expansion, error, | |
241 | *der_p, der_end); | |
242 | ||
243 | if (*der_p == 0) | |
244 | return NULL; | |
245 | ||
246 | CFReleaseNull(account->expansion); | |
247 | account->expansion = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, expansion); | |
248 | CFReleaseNull(expansion); | |
249 | } | |
250 | ||
251 | account->departure_code = (enum DepartureReason) tmp_departure_code; | |
252 | ||
253 | require_action_quiet(*der_p && *der_p == der_end, fail, | |
254 | SOSCreateError(kSOSErrorBadFormat, CFSTR("Didn't consume all bytes v7"), (error != NULL) ? *error : NULL, error)); | |
255 | ||
256 | result = CFRetainSafe(account); | |
257 | ||
258 | fail: | |
259 | CFReleaseNull(account); | |
260 | return result; | |
261 | } | |
262 | ||
263 | // | |
264 | // Version History for Account | |
265 | // | |
266 | // 1-5 - InnsbruckTaos/Cab; Never supported even for upgrading. | |
267 | // 6 - First version used in the field. | |
268 | // 7 - One Circle version | |
269 | // 8 - Adding expansion dictionary | |
270 | // | |
271 | ||
272 | #define CURRENT_ACCOUNT_PERSISTENT_VERSION 8 | |
273 | ||
274 | SOSAccountRef SOSAccountCreateFromDER(CFAllocatorRef allocator, | |
275 | SOSDataSourceFactoryRef factory, | |
276 | CFErrorRef* error, | |
277 | const uint8_t** der_p, const uint8_t *der_end) | |
278 | { | |
279 | SOSAccountRef account = NULL; | |
280 | SOSAccountRef result = NULL; | |
281 | uint64_t version = 0; | |
282 | ||
283 | const uint8_t *sequence_end; | |
284 | *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end); | |
285 | *der_p = ccder_decode_uint64(&version, *der_p, sequence_end); | |
286 | require_action_quiet(*der_p, errOut, | |
287 | SOSCreateError(kSOSErrorBadFormat, CFSTR("Version parsing failed"), (error != NULL) ? *error : NULL, error)); | |
288 | ||
289 | switch (version) { | |
290 | case CURRENT_ACCOUNT_PERSISTENT_VERSION: | |
291 | account = SOSAccountCreateFromRemainingDER_v8(allocator, factory, error, der_p, sequence_end); | |
292 | break; | |
293 | ||
294 | case 7: | |
295 | account = SOSAccountCreateFromRemainingDER_v7(allocator, factory, error, der_p, sequence_end); | |
296 | break; | |
297 | ||
298 | case 6: | |
299 | account = SOSAccountCreateFromRemainingDER_v6(allocator, factory, error, der_p, sequence_end); | |
300 | break; | |
301 | ||
302 | default: | |
303 | SOSCreateErrorWithFormat(kSOSErrorBadFormat, (error != NULL) ? *error : NULL, error, | |
304 | NULL, CFSTR("Unsupported version (%llu)"), version); | |
305 | break; | |
306 | } | |
307 | ||
308 | require_quiet(account, errOut); | |
309 | ||
310 | require_quiet(*der_p && *der_p == sequence_end, errOut); | |
311 | ||
312 | /* I may not always have an identity, but when I do, it has a private key */ | |
313 | if(account->my_identity) { | |
314 | require_action_quiet(SOSFullPeerInfoPrivKeyExists(account->my_identity), errOut, secnotice("account", "No private key associated with my_identity, resetting")); | |
fa7225c8 A |
315 | notify_post(kSecServerPeerInfoAvailable); |
316 | if(account->deviceID) | |
317 | SOSFullPeerInfoUpdateDeviceID(account->my_identity, account->deviceID, error); | |
5c19dc3a A |
318 | } |
319 | ||
320 | require_action_quiet(SOSAccountEnsureFactoryCircles(account), errOut, | |
321 | SOSCreateError(kSOSErrorBadFormat, CFSTR("Cannot EnsureFactoryCircles"), (error != NULL) ? *error : NULL, error)); | |
322 | ||
323 | SOSPeerInfoRef myPI = SOSAccountGetMyPeerInfo(account); | |
324 | if (myPI) { | |
6b200bc3 | 325 | SOSAccountCheckForAlwaysOnViews(account); |
fa7225c8 A |
326 | SOSPeerInfoRef oldPI = myPI; |
327 | // if UpdateFullPeerInfo did something - we need to make sure we have the right Ref | |
328 | myPI = SOSAccountGetMyPeerInfo(account); | |
329 | if(oldPI != myPI) secnotice("canary", "Caught spot where PIs differ in account setup"); | |
330 | CFStringRef transportTypeInflatedFromDER = SOSPeerInfoCopyTransportType(myPI); | |
331 | if (CFStringCompare(transportTypeInflatedFromDER, CFSTR("IDS"), 0) == 0 || CFStringCompare(transportTypeInflatedFromDER, CFSTR("KVS"), 0) == 0) | |
332 | SOSFullPeerInfoUpdateTransportType(account->my_identity, SOSTransportMessageTypeIDSV2, NULL); //update the transport type to the current IDS V2 type | |
333 | ||
334 | CFReleaseNull(transportTypeInflatedFromDER); | |
5c19dc3a | 335 | } |
fa7225c8 | 336 | |
6b200bc3 A |
337 | SOSAccountEnsureRecoveryRing(account); |
338 | ||
fa7225c8 A |
339 | SOSAccountWithTransactionSync(account, ^(SOSAccountRef account, SOSAccountTransactionRef txn) { |
340 | account->key_interests_need_updating = true; | |
341 | }); | |
e0e0d90e | 342 | |
6b200bc3 A |
343 | SOSAccountEnsureUUID(account); |
344 | ||
5c19dc3a A |
345 | result = CFRetainSafe(account); |
346 | ||
347 | errOut: | |
348 | CFReleaseNull(account); | |
349 | return result; | |
350 | } | |
351 | ||
352 | SOSAccountRef SOSAccountCreateFromData(CFAllocatorRef allocator, CFDataRef circleData, | |
353 | SOSDataSourceFactoryRef factory, | |
354 | CFErrorRef* error) | |
355 | { | |
356 | size_t size = CFDataGetLength(circleData); | |
357 | const uint8_t *der = CFDataGetBytePtr(circleData); | |
358 | SOSAccountRef account = SOSAccountCreateFromDER(allocator, factory, | |
359 | error, | |
360 | &der, der + size); | |
361 | return account; | |
362 | } | |
363 | ||
364 | CFMutableSetRef SOSPeerInfoSetCreateFromArrayDER(CFAllocatorRef allocator, const CFSetCallBacks *callbacks, CFErrorRef* error, | |
365 | const uint8_t** der_p, const uint8_t *der_end); | |
366 | size_t SOSPeerInfoSetGetDEREncodedArraySize(CFSetRef pia, CFErrorRef *error); | |
367 | uint8_t* SOSPeerInfoSetEncodeToArrayDER(CFSetRef pia, CFErrorRef* error, const uint8_t* der, uint8_t* der_end); | |
368 | ||
369 | size_t SOSAccountGetDEREncodedSize(SOSAccountRef account, CFErrorRef *error) | |
370 | { | |
371 | size_t sequence_size = 0; | |
372 | uint64_t version = CURRENT_ACCOUNT_PERSISTENT_VERSION; | |
373 | ||
374 | require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(version)), fail); | |
375 | require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->gestalt, error)), fail); | |
376 | require_quiet(accumulate_size(&sequence_size, SOSCircleGetDEREncodedSize(account->trusted_circle, error)), fail); | |
377 | require_quiet(accumulate_size(&sequence_size, der_sizeof_fullpeer_or_null(account->my_identity, error)), fail); | |
378 | require_quiet(accumulate_size(&sequence_size, ccder_sizeof_uint64(account->departure_code)), fail); | |
379 | require_quiet(accumulate_size(&sequence_size, ccder_sizeof_bool(account->user_public_trusted, error)), fail); | |
380 | require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->user_public, error)), fail); | |
381 | require_quiet(accumulate_size(&sequence_size, der_sizeof_public_bytes(account->previous_public, error)), fail); | |
382 | require_quiet(accumulate_size(&sequence_size, der_sizeof_data_or_null(account->user_key_parameters, error)), fail); | |
383 | require_quiet(accumulate_size(&sequence_size, SOSPeerInfoSetGetDEREncodedArraySize(account->retirees, error)), fail); | |
384 | accumulate_size(&sequence_size, der_sizeof_data_optional(account->backup_key)); | |
385 | require_quiet(accumulate_size(&sequence_size, der_sizeof_dictionary(account->expansion, error)), fail); | |
386 | ||
387 | return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, sequence_size); | |
388 | ||
389 | fail: | |
390 | SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error); | |
391 | return 0; | |
392 | } | |
393 | ||
394 | uint8_t* SOSAccountEncodeToDER(SOSAccountRef account, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) | |
395 | { | |
396 | uint64_t version = CURRENT_ACCOUNT_PERSISTENT_VERSION; | |
397 | der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, | |
398 | ccder_encode_uint64(version, der, | |
399 | der_encode_dictionary(account->gestalt, error, der, | |
400 | SOSCircleEncodeToDER(account->trusted_circle, error, der, | |
401 | der_encode_fullpeer_or_null(account->my_identity, error, der, | |
402 | ccder_encode_uint64(account->departure_code, der, | |
403 | ccder_encode_bool(account->user_public_trusted, der, | |
404 | der_encode_public_bytes(account->user_public, error, der, | |
405 | der_encode_public_bytes(account->previous_public, error, der, | |
406 | der_encode_data_or_null(account->user_key_parameters, error, der, | |
407 | SOSPeerInfoSetEncodeToArrayDER(account->retirees, error, der, | |
408 | der_encode_data_optional(account->backup_key, error, der, | |
409 | der_encode_dictionary(account->expansion, error, der, | |
410 | ||
411 | ||
412 | der_end))))))))))))); | |
413 | ||
414 | return der_end; | |
415 | } | |
416 | ||
417 | /************************/ | |
418 | ||
419 | CFDataRef SOSAccountCopyEncodedData(SOSAccountRef account, CFAllocatorRef allocator, CFErrorRef *error) | |
420 | { | |
fa7225c8 A |
421 | return CFDataCreateWithDER(kCFAllocatorDefault, SOSAccountGetDEREncodedSize(account, error), ^uint8_t*(size_t size, uint8_t *buffer) { |
422 | return SOSAccountEncodeToDER(account, error, buffer, (uint8_t *) buffer + size); | |
423 | }); | |
5c19dc3a A |
424 | } |
425 | ||
fa7225c8 | 426 |