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