]> git.saurik.com Git - apple/security.git/blame - OSX/libsecurity_keychain/lib/SecRecoveryPassword.c
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / SecRecoveryPassword.c
CommitLineData
b1ab9ed8 1/*
d8f41ccd 2 * Copyright (c) 2010-2012 Apple Inc. All Rights Reserved.
b1ab9ed8
A
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 "SecRecoveryPassword.h"
25#include <Security/SecTransform.h>
26#include <Security/SecEncodeTransform.h>
27#include <Security/SecDecodeTransform.h>
28#include <Security/SecDigestTransform.h>
29#include <Security/SecEncryptTransform.h>
30#include <Security/SecItem.h>
31#include <Security/SecKey.h>
866f8763 32#include <Security/SecRandom.h>
b1ab9ed8
A
33#include <CommonCrypto/CommonKeyDerivation.h>
34#include <CommonCrypto/CommonCryptor.h>
35#include <CoreFoundation/CFBase.h>
36#include <fcntl.h>
37#include <asl.h>
38#include <stdarg.h>
39#include <string.h>
40#include <stdio.h>
6b200bc3 41#include <utilities/SecCFRelease.h>
914fc88e 42#include <utilities/debugging.h>
b1ab9ed8
A
43
44CFStringRef kSecRecVersionNumber = CFSTR("SRVersionNumber");
45CFStringRef kSecRecQuestions = CFSTR("SRQuestions");
46CFStringRef kSecRecLocale = CFSTR("SRLocale");
47CFStringRef kSecRecIV = CFSTR("SRiv");
48CFStringRef kSecRecWrappedPassword = CFSTR("SRWrappedPassword");
49
50
51static char *std_log_prefix = "###SecRecovery Function: %s - %s";
52static const char *std_ident = "Security.framework";
53static const char *std_facility = "InfoSec";
54static uint32_t std_options = 0;
55
56static aslclient aslhandle = NULL;
57static aslmsg msgptr = NULL;
58
59// Error Reporting
60
61void ccdebug_imp(int level, char *funcname, char *format, ...);
62
63#define secDebug(lvl,fmt,...) sec_debug_imp(lvl, __PRETTY_FUNCTION__, fmt, __VA_ARGS__)
64
65static void
66sec_debug_init() {
67 char *ccEnvStdErr = getenv("CC_STDERR");
68
69 if(ccEnvStdErr != NULL && strncmp(ccEnvStdErr, "yes", 3) == 0) std_options |= ASL_OPT_STDERR;
70 aslhandle = asl_open(std_ident, std_facility, std_options);
71
72 msgptr = asl_new(ASL_TYPE_MSG);
73 asl_set(msgptr, ASL_KEY_FACILITY, "com.apple.infosec");
74}
75
76
77static void
78sec_debug_imp(int level, const char *funcname, char *format, ...) {
79 va_list argp;
80 char fmtbuffer[256];
81
82 if(aslhandle == NULL) sec_debug_init();
83
84 sprintf(fmtbuffer, std_log_prefix, funcname, format);
85 va_start(argp, format);
86 asl_vlog(aslhandle, msgptr, level, fmtbuffer, argp);
87 va_end(argp);
88}
89
90// Read /dev/random for random bytes
91
92static CFDataRef
866f8763 93createRandomBytes(size_t len)
b1ab9ed8 94{
866f8763
A
95 CFMutableDataRef data = CFDataCreateMutable(NULL, len);
96 if (data == NULL)
97 return NULL;
98 CFDataSetLength(data, len);
99 if (SecRandomCopyBytes(kSecRandomDefault, len, CFDataGetMutableBytePtr(data)) != noErr) {
100 CFRelease(data);
101 return NULL;
102 }
103 return data;
b1ab9ed8
A
104}
105
106// This is the normalization routine - subject to change. We need to make sure that whitespace is removed and
107// that upper/lower case is normalized, etc for all possible languages.
108
109static void secNormalize(CFMutableStringRef theString, CFLocaleRef theLocale)
110{
111 CFRange theRange;
112
113 CFStringFold(theString, kCFCompareCaseInsensitive | kCFCompareDiacriticInsensitive | kCFCompareWidthInsensitive, theLocale);
114 CFStringNormalize(theString, kCFStringNormalizationFormKC);
115 CFStringTrimWhitespace(theString);
116 while(CFStringFindCharacterFromSet(theString, CFCharacterSetGetPredefined(kCFCharacterSetWhitespace), CFRangeMake(0, CFStringGetLength(theString)), kCFCompareBackwards, &theRange))
117 CFStringDelete(theString, theRange);
118}
119
120/*
121 * This will derive a 128 bit (16 byte) key from a set of answers to questions in a CFArray of CFStrings.
122 * it normalizes each answer and concats them into a collector buffer. The resulting string is run through
123 * PBKDF2-HMAC-SHA256 to form a key.
124 *
125 * Todo: For version 2 it would be better to randomly generate the salt and make the iteration count flexible.
126 * This would require a different return value because that information would need to be returned up the stack
127 * to the callers. Given the time left in this release (Lion) we're going with set values for this.
128 */
129
130#define RETURN_KEY_SIZE 16
131#define MAXANSWERBUFF 4096
132#define PBKDF_ROUNDS 100000
b1ab9ed8 133
866f8763
A
134static SecKeyRef CF_RETURNS_RETAINED
135secDeriveKeyFromAnswers(CFArrayRef answers, CFLocaleRef theLocale)
b1ab9ed8
A
136{
137 static const uint8_t salt[16] = { 0x0A, 0x1F, 0x0A, 0x1F, 0x0A, 0x1F, 0x0A, 0x1F, 0x0A, 0x1F, 0x0A, 0x1F, 0x0A, 0x1F, 0x0A, 0x1F };
138 static const int saltLen = sizeof(salt);
139
140 SecKeyRef theKey = NULL;
141 uint8_t rawKeyData[RETURN_KEY_SIZE];
142
143 CFIndex encodedAnswers = 0;
144 CFIndex numAnswers = CFArrayGetCount(answers);
145 const size_t concatenatedAnswersSize = MAXANSWERBUFF * numAnswers;
146
147 char *concatenatedAnswers = (char *)malloc(concatenatedAnswersSize);
148 if (concatenatedAnswers == NULL) {
149 return NULL;
150 }
151
152 concatenatedAnswers[0] = 0; // NUL terminate
153
154 int i;
155 for (i = 0; i < numAnswers; i++) {
156 CFMutableStringRef answer = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFArrayGetValueAtIndex(answers, i));
157 if (answer) {
158 secNormalize(answer, theLocale);
159
160 CFIndex theAnswerLen = CFStringGetLength(answer);
161 CFIndex theAnswerSize = CFStringGetMaximumSizeForEncoding(theAnswerLen, kCFStringEncodingUTF8);
162 char *theAnswer = (char *)malloc(theAnswerSize + 1); // add space for NUL byte
163 if (theAnswer) {
164 if (theAnswerLen == CFStringGetBytes(answer, CFRangeMake(0, CFStringGetLength(answer)), kCFStringEncodingUTF8, '?', FALSE, (UInt8*)theAnswer, theAnswerSize, &theAnswerSize)) {
165 theAnswer[theAnswerSize] = 0; // NUL terminate
166 if (strlcat(concatenatedAnswers, theAnswer, concatenatedAnswersSize) < concatenatedAnswersSize) {
167 encodedAnswers += 1;
168 }
169 }
170 bzero(theAnswer, theAnswerSize);
171 free(theAnswer);
172 }
173 CFRelease(answer);
174 }
175 }
176
177 // one or more of the answers failed to encode
178 if (encodedAnswers != numAnswers) {
179 free(concatenatedAnswers);
180 return NULL;
181 }
182
183 if (CCKeyDerivationPBKDF(kCCPBKDF2, concatenatedAnswers, strlen(concatenatedAnswers), salt, saltLen, kCCPRFHmacAlgSHA256, PBKDF_ROUNDS, rawKeyData, RETURN_KEY_SIZE)) {
184 free(concatenatedAnswers);
185 return NULL;
186 }
187
188 CFDataRef keyData = CFDataCreate(kCFAllocatorDefault, rawKeyData, RETURN_KEY_SIZE);
189 if (keyData) {
190 CFMutableDictionaryRef params = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
191 if (params) {
192 CFErrorRef error = NULL;
193 CFDictionaryAddValue(params, kSecAttrKeyType, kSecAttrKeyTypeAES);
194 theKey = SecKeyCreateFromData(params, keyData, &error);
195 if (error) {
196 CFRelease(error);
197 }
198 CFRelease(params);
199 }
200 CFRelease(keyData);
201 }
202
203 bzero(rawKeyData, RETURN_KEY_SIZE);
204 bzero(concatenatedAnswers, concatenatedAnswersSize);
205 free(concatenatedAnswers);
206 return theKey;
207}
208
209
210// Single shot CFString processing routines for digests/encoding/encrypt/decrypt
211
212static CFDataRef
866f8763 213createDigestString(CFStringRef str)
b1ab9ed8
A
214{
215 CFDataRef retval = NULL;
216 CFErrorRef error = NULL;
217
218 CFDataRef inputString = CFStringCreateExternalRepresentation(kCFAllocatorDefault, str, kCFStringEncodingUTF8, 0xff);
219
220 SecTransformRef digestTrans = SecDigestTransformCreate(kSecDigestSHA2, 256, &error);
221 if(error == NULL) {
222 SecTransformSetAttribute(digestTrans, kSecTransformInputAttributeName, inputString, &error);
223 if(error == NULL) {
224 retval = SecTransformExecute(digestTrans, &error);
225 if(retval == NULL) {
866f8763
A
226 CFStringRef errorReason = CFErrorCopyFailureReason(error);
227 secDebug(ASL_LEVEL_ERR, "Couldn't create digest %s\n", CFStringGetCStringPtr(errorReason, kCFStringEncodingUTF8));
228 CFReleaseNull(errorReason);
b1ab9ed8
A
229 }
230 }
b1ab9ed8 231 }
866f8763
A
232 CFReleaseNull(digestTrans);
233 CFReleaseNull(inputString);
b1ab9ed8
A
234 return retval;
235}
236
6b200bc3 237static CFDataRef CF_RETURNS_RETAINED
b1ab9ed8
A
238b64encode(CFDataRef input)
239{
427c49bc 240 CFDataRef retval = NULL;
b1ab9ed8
A
241 CFErrorRef error = NULL;
242 SecTransformRef encodeTrans = SecEncodeTransformCreate(kSecBase64Encoding, &error);
243 if(error == NULL) SecTransformSetAttribute(encodeTrans, kSecTransformInputAttributeName, input, &error);
244 if(error == NULL) retval = SecTransformExecute(encodeTrans, &error);
245 if(encodeTrans) CFRelease(encodeTrans);
246 return retval;
247}
248
6b200bc3 249static CFDataRef CF_RETURNS_RETAINED
b1ab9ed8
A
250b64decode(CFDataRef input)
251{
427c49bc 252 CFDataRef retval = NULL;
b1ab9ed8
A
253 CFErrorRef error = NULL;
254 SecTransformRef decodeTrans = SecDecodeTransformCreate(kSecBase64Encoding, &error);
255 if(error == NULL) SecTransformSetAttribute(decodeTrans, kSecTransformInputAttributeName, input, &error);
256 if(error == NULL) retval = SecTransformExecute(decodeTrans, &error);
257 if(decodeTrans) CFRelease(decodeTrans);
258 return retval;
259}
260
866f8763 261static CFDataRef CF_RETURNS_RETAINED
b1ab9ed8
A
262encryptString(SecKeyRef wrapKey, CFDataRef iv, CFStringRef str)
263{
264 CFDataRef retval = NULL;
265 CFErrorRef error = NULL;
266 CFDataRef inputString = CFStringCreateExternalRepresentation(kCFAllocatorDefault, str, kCFStringEncodingMacRoman, 0xff);
914fc88e
A
267 SecTransformRef encrypt = NULL;
268 SecTransformRef encode = NULL;
269 SecTransformRef group = NULL;
270
271 encrypt = SecEncryptTransformCreate(wrapKey, &error);
272 if (error) goto out;
273 SecTransformSetAttribute(encrypt, kSecEncryptionMode, kSecModeCBCKey, &error);
274 if (error) goto out;
275 SecTransformSetAttribute(encrypt, kSecPaddingKey, kSecPaddingPKCS7Key, &error);
276 if (error) goto out;
277 SecTransformSetAttribute(encrypt, kSecTransformInputAttributeName, inputString, &error);
278 if (error) goto out;
279 SecTransformSetAttribute(encrypt, kSecIVKey, iv, &error);
280 if (error) goto out;
281
282 encode = SecEncodeTransformCreate(kSecBase64Encoding, &error);
283 if (error) goto out;
284
285 group = SecTransformCreateGroupTransform();
286 SecTransformConnectTransforms(encrypt, kSecTransformOutputAttributeName, encode, kSecTransformInputAttributeName, group, &error);
287 if (error) goto out;
288 retval = SecTransformExecute(group, &error);
289 if (error) goto out;
290
291out:
292 if (error) {
293 secerror("Failed to encrypt recovery password: %@", error);
b1ab9ed8 294 }
914fc88e
A
295
296 CFReleaseNull(error);
297 CFReleaseNull(inputString);
298 CFReleaseNull(encrypt);
299 CFReleaseNull(encode);
300 CFReleaseNull(group);
301
b1ab9ed8
A
302 return retval;
303}
304
305
6b200bc3 306static CFStringRef CF_RETURNS_RETAINED
b1ab9ed8
A
307decryptString(SecKeyRef wrapKey, CFDataRef iv, CFDataRef wrappedPassword)
308{
309 CFStringRef retval = NULL;
310 CFDataRef retData = NULL;
914fc88e
A
311 CFErrorRef error = NULL;
312 SecTransformRef decode = NULL;
313 SecTransformRef decrypt = NULL;
314 SecTransformRef group = NULL;
315
316 decode = SecDecodeTransformCreate(kSecBase64Encoding, &error);
317 if (error) goto out;
318 SecTransformSetAttribute(decode, kSecTransformInputAttributeName, wrappedPassword, &error);
319 if (error) goto out;
320
321 decrypt = SecDecryptTransformCreate(wrapKey, &error);
322 if (error) goto out;
323 SecTransformSetAttribute(decrypt, kSecEncryptionMode, kSecModeCBCKey, &error);
324 if (error) goto out;
325 SecTransformSetAttribute(decrypt, kSecPaddingKey, kSecPaddingPKCS7Key, &error);
326 if (error) goto out;
327 SecTransformSetAttribute(decrypt, kSecIVKey, iv, &error);
328 if (error) goto out;
329
330 group = SecTransformCreateGroupTransform();
331 SecTransformConnectTransforms(decode, kSecTransformOutputAttributeName, decrypt, kSecTransformInputAttributeName, group, &error);
332 if (error) goto out;
333 retData = SecTransformExecute(group, &error);
334 if (error) goto out;
335 retval = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, retData, kCFStringEncodingMacRoman);
336
337out:
338 if (error) {
339 secerror("Failed to decrypt recovery password: %@", error);
6b200bc3 340 }
914fc88e
A
341
342 CFReleaseNull(retData);
343 CFReleaseNull(error);
344 CFReleaseNull(decode);
345 CFReleaseNull(decrypt);
346 CFReleaseNull(group);
347
6b200bc3 348 return retval;
b1ab9ed8
A
349}
350
351// IV for the recovery ref is currently the leftmost 16 bytes of the digest of the recovery password.
352#define IVBYTECOUNT 16
353
354static CFDataRef
355createIVFromPassword(CFStringRef password)
356{
357 CFDataRef hashedPassword, retval;
358 CFMutableDataRef iv;
866f8763 359 if((hashedPassword = createDigestString(password)) == NULL) return NULL;
b1ab9ed8
A
360 iv = CFDataCreateMutableCopy(kCFAllocatorDefault, CFDataGetLength(hashedPassword)+1, hashedPassword);
361 CFDataDeleteBytes(iv, CFRangeMake(IVBYTECOUNT, CFDataGetLength(iv)-IVBYTECOUNT));
362 retval = CFDataCreateCopy(kCFAllocatorDefault, iv);
363 CFRelease(hashedPassword);
364 CFRelease(iv);
365 return retval;
366}
367
368
369/*
370 * API functions
371 */
372
373/*
374 * Function: SecWrapRecoveryPasswordWithAnswers
375 * Description: This will wrap a password by using answers to generate a key. The resulting
376 * wrapped password and the questions used to get the answers are saved in a
377 * recovery dictionary.
378 */
379
866f8763 380CFDictionaryRef CF_RETURNS_RETAINED
b1ab9ed8
A
381SecWrapRecoveryPasswordWithAnswers(CFStringRef password, CFArrayRef questions, CFArrayRef answers)
382{
383 uint32_t vers = 1;
866f8763
A
384 CFDataRef iv = NULL;
385 CFDataRef wrappedPassword = NULL;
b1ab9ed8
A
386 CFMutableDictionaryRef retval = NULL;
387 CFLocaleRef theLocale = CFLocaleCopyCurrent();
388 CFStringRef theLocaleString = CFLocaleGetIdentifier(theLocale);
866f8763 389 SecKeyRef wrapKey = NULL;
b1ab9ed8 390
427c49bc 391 CFIndex ix, limit;
b1ab9ed8 392
866f8763
A
393 if (!password || !questions || !answers) {
394 goto error;
395 }
b1ab9ed8
A
396
397 limit = CFArrayGetCount(answers);
866f8763
A
398 if (limit != CFArrayGetCount(questions)) {
399 goto error;
400 }
b1ab9ed8
A
401 CFTypeRef chkval;
402 for (ix=0; ix<limit; ix++)
403 {
404 chkval = CFArrayGetValueAtIndex(answers, ix);
866f8763
A
405 if (!chkval || CFGetTypeID(chkval)!=CFStringGetTypeID() || CFEqual((CFStringRef)chkval, CFSTR(""))) {
406 goto error;
407 }
b1ab9ed8 408 chkval = CFArrayGetValueAtIndex(questions, ix);
866f8763
A
409 if (!chkval || CFGetTypeID(chkval)!=CFStringGetTypeID() || CFEqual((CFStringRef)chkval, CFSTR(""))) {
410 goto error;
411 }
b1ab9ed8
A
412 }
413
414 iv = createIVFromPassword(password);
415
866f8763 416 wrapKey = secDeriveKeyFromAnswers(answers, theLocale);
b1ab9ed8
A
417
418 if((wrappedPassword = encryptString(wrapKey, iv, password)) != NULL) {
419 retval = CFDictionaryCreateMutable(kCFAllocatorDefault, 5, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
866f8763
A
420 CFNumberRef num = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vers);
421 CFDictionaryAddValue(retval, kSecRecVersionNumber, num);
422 CFReleaseNull(num);
b1ab9ed8
A
423 CFDictionaryAddValue(retval, kSecRecQuestions, questions);
424 CFDictionaryAddValue(retval, kSecRecLocale, theLocaleString);
866f8763
A
425 CFDataRef ivdata = b64encode(iv);
426 CFDictionaryAddValue(retval, kSecRecIV, ivdata);
427 CFReleaseNull(ivdata);
b1ab9ed8
A
428 CFDictionaryAddValue(retval, kSecRecWrappedPassword, wrappedPassword);
429 }
430
866f8763
A
431 CFReleaseNull(wrappedPassword);
432 goto out;
433error:
434 CFReleaseNull(retval);
435out:
436 CFReleaseNull(iv);
437 CFReleaseNull(wrapKey);
438 CFReleaseNull(theLocale);
439
b1ab9ed8
A
440 return retval;
441}
442
443/*
444 * Function: SecUnwrapRecoveryPasswordWithAnswers
445 * Description: This will unwrap a password contained in a recovery dictionary by using answers
446 * to generate a key.
447 */
448
866f8763 449CFStringRef CF_RETURNS_RETAINED
b1ab9ed8
A
450SecUnwrapRecoveryPasswordWithAnswers(CFDictionaryRef recref, CFArrayRef answers)
451{
452 if(answers == NULL || CFArrayGetCount(answers) < 3) return NULL;
453
454 CFStringRef theLocaleString = (CFStringRef) CFDictionaryGetValue(recref, kSecRecLocale);
455 CFDataRef tmpIV = (CFDataRef) CFDictionaryGetValue(recref, kSecRecIV);
456 CFDataRef wrappedPassword = (CFDataRef) CFDictionaryGetValue(recref, kSecRecWrappedPassword);
457
458 if(theLocaleString == NULL || tmpIV == NULL || wrappedPassword == NULL) {
459 return NULL;
460 }
461
462
463 CFLocaleRef theLocale = CFLocaleCreate(kCFAllocatorDefault, theLocaleString);
464 SecKeyRef wrapKey = secDeriveKeyFromAnswers(answers, theLocale);
b1ab9ed8
A
465 CFRelease(theLocale);
466
467 CFDataRef iv = b64decode(tmpIV);
468
469 CFStringRef recoveryPassword = decryptString(wrapKey, iv, wrappedPassword);
470 CFRelease(wrapKey);
471
472 if(recoveryPassword != NULL) {
473 CFDataRef comphash = createIVFromPassword(recoveryPassword);
474 if(!CFEqual(comphash, iv)) {
475 secDebug(ASL_LEVEL_ERR, "Failed reconstitution of password for recovery\n", NULL);
476 CFRelease(recoveryPassword);
477 recoveryPassword = NULL;
478 }
479 CFRelease(comphash);
480 }
481 CFRelease(iv);
482 return recoveryPassword;
483}
484
485/*
486 * Function: SecCreateRecoveryPassword
487 * Description: This function will get a random 128 bit number and base32
488 * encode and format that value
489 */
490
491CFStringRef
427c49bc 492SecCreateRecoveryPassword(void)
b1ab9ed8
A
493{
494 CFStringRef result = NULL;
495 CFErrorRef error = NULL;
496 CFDataRef encodedData = NULL;
866f8763 497 CFDataRef randData = createRandomBytes(16);
b1ab9ed8
A
498 int i;
499
500 // base32FDE is a "private" base32 encoding, it has no 0/O or L/l/1 in it (it uses 8 and 9).
501 SecTransformRef encodeTrans = SecEncodeTransformCreate(CFSTR("base32FDE"), &error);
502 if(error == NULL) {
503 SecTransformSetAttribute(encodeTrans, kSecTransformInputAttributeName, randData, &error);
504 if(error == NULL) encodedData = SecTransformExecute(encodeTrans, &error);
b1ab9ed8 505 }
866f8763 506 CFReleaseNull(encodeTrans);
b1ab9ed8
A
507 CFRelease(randData);
508
509 if(encodedData != NULL && error == NULL) {
510 CFStringRef b32string = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, encodedData, kCFStringEncodingMacRoman);
511 CFMutableStringRef encodedString = CFStringCreateMutableCopy(kCFAllocatorDefault, 64, b32string);
512
513 // Add some hyphens to make the generated password easier to use
514 for(i = 4; i < 34; i += 5) CFStringInsert(encodedString, i, CFSTR("-"));
515 // Trim so the last section is 4 characters long
516 CFStringDelete(encodedString, CFRangeMake(29,CFStringGetLength(encodedString)-29));
517 result = CFStringCreateCopy(kCFAllocatorDefault, encodedString);
518 CFRelease(encodedString);
519 CFRelease(b32string);
b1ab9ed8
A
520 } else {
521 secDebug(ASL_LEVEL_ERR, "Failed to base32 encode random data for recovery password\n", NULL);
522 }
866f8763 523 CFReleaseNull(encodedData);
b1ab9ed8
A
524
525 return result;
526
527}