]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecPasswordGenerate.c
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / sec / Security / SecPasswordGenerate.c
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 * SecPasswordStrength.c
26 */
27
28 #include <limits.h>
29 #include <CoreFoundation/CoreFoundation.h>
30 #include <Security/SecItem.h>
31 #include <Security/SecBase.h>
32 #include <Security/SecRandom.h>
33 #include "SecPasswordGenerate.h"
34 #include <AssertMacros.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <utilities/SecCFWrappers.h>
38 #include <utilities/SecCFRelease.h>
39 #include <utilities/SecCFError.h>
40
41 // Keys for external dictionaries with password generation requirements we read from plist.
42 CFStringRef kSecPasswordMinLengthKey = CFSTR("PasswordMinLength");
43 CFStringRef kSecPasswordMaxLengthKey = CFSTR("PasswordMaxLength");
44 CFStringRef kSecPasswordAllowedCharactersKey = CFSTR("PasswordAllowedCharacters");
45 CFStringRef kSecPasswordRequiredCharactersKey = CFSTR("PasswordRequiredCharacters");
46 CFStringRef kSecPasswordDefaultForType = CFSTR("PasswordDefaultForType");
47
48 CFStringRef kSecPasswordDisallowedCharacters = CFSTR("PasswordDisallowedCharacters");
49 CFStringRef kSecPasswordCantStartWithChars = CFSTR("PasswordCantStartWithChars");
50 CFStringRef kSecPasswordCantEndWithChars = CFSTR("PasswordCantEndWithChars");
51 CFStringRef kSecPasswordContainsNoMoreThanNSpecificCharacters = CFSTR("PasswordContainsNoMoreThanNSpecificCharacters");
52 CFStringRef kSecPasswordContainsAtLeastNSpecificCharacters = CFSTR("PasswordContainsAtLeastNSpecificCharacters");
53 CFStringRef kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters = CFSTR("PasswordContainsNoMoreThanNConsecutiveIdenticalCharacters");
54 CFStringRef kSecPasswordCharacterCount = CFSTR("PasswordCharacterCount");
55 CFStringRef kSecPasswordCharacters = CFSTR("PasswordCharacters");
56
57 CFStringRef kSecPasswordGroupSize = CFSTR("PasswordGroupSize");
58 CFStringRef kSecPasswordNumberOfGroups = CFSTR("PasswordNumberOfGroups");
59 CFStringRef kSecPasswordSeparator = CFSTR("SecPasswordSeparator");
60
61 // Keys for internally used dictionaries with password generation parameters (never exposed to external API).
62 static CFStringRef kSecUseDefaultPasswordFormatKey = CFSTR("UseDefaultPasswordFormat");
63 static CFStringRef kSecNumberOfRequiredRandomCharactersKey = CFSTR("NumberOfRequiredRandomCharacters");
64 static CFStringRef kSecAllowedCharactersKey = CFSTR("AllowedCharacters");
65 static CFStringRef kSecRequiredCharacterSetsKey = CFSTR("RequiredCharacterSets");
66
67 static CFIndex defaultNumberOfRandomCharacters = 20;
68 static CFIndex defaultPINLength = 4;
69 static CFIndex defaultiCloudPasswordLength = 24;
70 static CFIndex defaultWifiPasswordLength = 12;
71
72 static CFStringRef defaultWifiCharacters = CFSTR("abcdefghijklmnopqrstuvwxyz1234567890");
73 static CFStringRef defaultPINCharacters = CFSTR("0123456789");
74 static CFStringRef defaultiCloudCharacters = CFSTR("ABCDEFGHJKLMNPQRSTUVWXYZ23456789");
75 static CFStringRef defaultCharacters = CFSTR("abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789");
76
77 static CFCharacterSetRef uppercaseLetterCharacterSet;
78 static CFCharacterSetRef lowercaseLetterCharacterSet;
79 static CFCharacterSetRef decimalDigitCharacterSet;
80 static CFCharacterSetRef punctuationCharacterSet;
81
82 static CFIndex alphabetSetSize = 26;
83 static CFIndex decimalSetSize = 10;
84 static CFIndex punctuationSetSize = 33;
85 static double entropyStrengthThreshold = 35.0;
86
87 /*
88 generated with ruby badpins.rb | gperf
89 See this for PIN list:
90 A birthday present every eleven wallets? The security of customer-chosen banking PINs (2012), by Joseph Bonneau , Sören Preibusch , Ross Anderson
91 */
92 const char *in_word_set (const char *str, unsigned int len);
93
94 #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
95 && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
96 && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
97 && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
98 && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
99 && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
100 && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
101 && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
102 && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
103 && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
104 && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
105 && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
106 && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
107 && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
108 && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
109 && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
110 && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
111 && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
112 && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
113 && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
114 && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
115 && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
116 && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
117 /* The character set is not based on ISO-646. */
118 error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
119 #endif
120
121
122 #define TOTAL_KEYWORDS 100
123 #define MIN_WORD_LENGTH 4
124 #define MAX_WORD_LENGTH 4
125 #define MIN_HASH_VALUE 21
126 #define MAX_HASH_VALUE 275
127 /* maximum key range = 255, duplicates = 0 */
128
129 #ifdef __GNUC__
130 __inline
131 #else
132 #ifdef __cplusplus
133 inline
134 #endif
135 #endif
136 static unsigned int pinhash (const char *str, unsigned int len)
137 {
138 static unsigned short asso_values[] =
139 {
140 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
141 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
142 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
143 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
144 276, 276, 276, 276, 276, 276, 276, 276, 5, 0,
145 10, 10, 30, 50, 100, 120, 70, 25, 57, 85,
146 2, 4, 1, 19, 14, 11, 92, 276, 276, 276,
147 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
148 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
149 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
150 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
151 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
152 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
153 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
154 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
155 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
156 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
157 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
158 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
159 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
160 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
161 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
162 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
163 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
164 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
165 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
166 276, 276, 276, 276, 276
167 };
168 return len + asso_values[(unsigned char)str[3]+9] + asso_values[(unsigned char)str[2]] + asso_values[(unsigned char)str[1]] + asso_values[(unsigned char)str[0]+3];
169 }
170
171 CFStringRef SecPasswordCreateWithRandomDigits(int n, CFErrorRef *error){
172 int min = n;
173 int max = n;
174
175 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
176 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
177 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
178 punctuationCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetPunctuation);
179
180 CFNumberRef minRef = CFNumberCreate(NULL, kCFNumberIntType, &min);
181 CFNumberRef maxRef = CFNumberCreate(NULL, kCFNumberIntType, &max);
182
183 CFMutableDictionaryRef passwordRequirements = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
184 CFDictionaryAddValue(passwordRequirements, kSecPasswordMinLengthKey, minRef);
185 CFDictionaryAddValue(passwordRequirements, kSecPasswordMaxLengthKey, maxRef);
186 CFStringRef allowedCharacters = CFSTR("0123456789");
187
188 CFDictionaryAddValue(passwordRequirements, kSecPasswordAllowedCharactersKey, allowedCharacters);
189
190 CFStringRef password = SecPasswordGenerate(kSecPasswordTypePIN, error, passwordRequirements);
191
192 CFReleaseNull(minRef);
193 CFReleaseNull(maxRef);
194 CFReleaseNull(passwordRequirements);
195
196 return password;
197 }
198
199
200
201 //pins that reached the top 20 list
202 static const char *blacklist[] = {"1234", "1004", "2000", "1122", "4321", "2001", "2580"};
203 bool SecPasswordIsPasswordWeak(CFStringRef passcode)
204 {
205 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
206 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
207 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
208 punctuationCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetPunctuation);
209
210 bool isNumber = true;
211 char* pin = NULL;
212
213 //length checks
214 if( CFStringGetLength(passcode) < 4 ){
215 return true; //weak password
216 }
217 //check to see if passcode is a number
218 for(CFIndex i = 0; i < CFStringGetLength(passcode); i++){
219 if( CFStringFindCharacterFromSet(passcode, decimalDigitCharacterSet, CFRangeMake(i,1), 0, NULL))
220 continue;
221 else {
222 isNumber = false;
223 break;
224 }
225 }
226 //checking to see if it's a 4 digit pin
227 if(isNumber && CFStringGetLength(passcode) == 4){
228
229 pin = CFStringToCString(passcode);
230 if(in_word_set(pin, 4)){
231 free(pin);
232 return true;
233 }
234
235 CFIndex blacklistLength = (CFIndex)sizeof(blacklist)/sizeof(blacklist[0]);
236
237 //not all the same number
238 if(pin[0] == pin[1] == pin[2] == pin[3]){
239 free(pin);
240 return true; //weak password
241 }
242 //first two digits being the same and the last two digits being the same
243 if ( pin[0] == pin[1] && pin[2] == pin[3]){
244 free(pin);
245 return true; //weak password
246 }
247 //first two digits not being the same as the last two digits
248 if(pin[0] == pin[2] && pin[1] == pin[3]){
249 free(pin);
250 return true; //weak password
251 }
252 //not in this list
253 for(CFIndex i = 0; i < blacklistLength; i++)
254 {
255 const char* blackCode = blacklist[i];
256 if(0 == strcmp(blackCode, pin))
257 {
258 free(pin);
259 return true; //weak password
260 }
261 }
262 }
263 else if(isNumber){ //dealing with a numeric PIN
264 pin = CFStringToCString(passcode);
265 //check if PIN is all the same number
266 for(int i = 0; i < CFStringGetLength(passcode); i++){
267 if(i+1 >= CFStringGetLength(passcode)){
268 free(pin);
269 return true;
270 }
271 else if (pin[i] == pin[i+1])
272 continue;
273 else
274 break;
275 }
276 //check if PIN is a bunch of incrementing numbers
277 for(int i = 0; i < CFStringGetLength(passcode); i++){
278 if(i == CFStringGetLength(passcode)-1){
279 free(pin);
280 return true;
281 }
282 else if ((pin[i] + 1) == pin[i+1])
283 continue;
284 else
285 break;
286 }
287 //check if PIN is a bunch of decrementing numbers
288 for(int i = 0; i < CFStringGetLength(passcode); i++){
289 if(i == CFStringGetLength(passcode)-1){
290 free(pin);
291 return true;
292 }
293 else if ((pin[i]) == (pin[i+1] +1))
294 continue;
295 else
296 break;
297 }
298 }
299 else{ // password is complex, evaluate entropy
300 int u = 0;
301 int l = 0;
302 int d = 0;
303 int p = 0;
304 int characterSet = 0;
305
306 //calculate new entropy
307 for(CFIndex i = 0; i < CFStringGetLength(passcode); i++){
308
309 if( CFStringFindCharacterFromSet(passcode, uppercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
310 u++;
311 continue;
312 }
313 if( CFStringFindCharacterFromSet(passcode, lowercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
314 l++;
315 continue;
316 }
317 if( CFStringFindCharacterFromSet(passcode, decimalDigitCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
318 d++;
319 continue;
320 }
321 if( CFStringFindCharacterFromSet(passcode, punctuationCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
322 p++;
323 continue;
324 }
325
326 }
327 if(u > 0){
328 characterSet += alphabetSetSize;
329 }
330 if(l > 0){
331 characterSet += alphabetSetSize;
332 }
333 if(d > 0){
334 characterSet += decimalSetSize;
335 }
336 if(p > 0){
337 characterSet += punctuationSetSize;
338 }
339
340 double strength = CFStringGetLength(passcode)*log2(characterSet);
341
342 if(strength < entropyStrengthThreshold){
343 return true; //weak
344 }
345 else
346 return false; //strong
347 }
348 if(pin)
349 free(pin);
350
351 return false; //strong password
352
353 }
354
355 static bool SecPasswordIsPasscodeIncrementingOrDecrementingDigits(CFStringRef passcode)
356 {
357 char* pin = CFStringToCString(passcode);
358
359 //check if PIN is a bunch of incrementing numbers
360 for(int i = 0; i < CFStringGetLength(passcode); i++){
361 if(i == CFStringGetLength(passcode)-1){
362 free(pin);
363 return true;
364 }
365 else if ((pin[i] + 1) == pin[i+1])
366 continue;
367 else
368 break;
369 }
370 //check if PIN is a bunch of decrementing numbers
371 for(int i = 0; i < CFStringGetLength(passcode); i++){
372 if(i == CFStringGetLength(passcode)-1){
373 free(pin);
374 return true;
375 }
376 else if ((pin[i]) == (pin[i+1] +1))
377 continue;
378 else
379 break;
380 }
381 free(pin);
382 return false;
383 }
384
385 bool SecPasswordIsPasswordWeak2(bool isSimple, CFStringRef passcode)
386 {
387 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
388 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
389 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
390 punctuationCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetPunctuation);
391
392
393 char* pin = NULL;
394
395 //length checks
396 if( CFStringGetLength(passcode) < 4 ){
397 return true; //weak password
398 }
399
400 bool isPasscodeNumber = true;
401 //check to see if passcode is a number
402 for(CFIndex i = 0; i < CFStringGetLength(passcode); i++){
403 if( CFStringFindCharacterFromSet(passcode, decimalDigitCharacterSet, CFRangeMake(i,1), 0, NULL))
404 continue;
405 else {
406 isPasscodeNumber = false;
407 break;
408 }
409 }
410
411 if(isSimple){
412 //checking to see if it's a 4 digit pin
413 if(isPasscodeNumber && CFStringGetLength(passcode) == 4){
414
415 pin = CFStringToCString(passcode);
416 if(in_word_set(pin, 4)){
417 free(pin);
418 return true;
419 }
420
421 CFIndex blacklistLength = (CFIndex)sizeof(blacklist)/sizeof(blacklist[0]);
422
423 //not all the same number
424 if(pin[0] == pin[1] == pin[2] == pin[3]){
425 free(pin);
426 return true; //weak password
427 }
428 //first two digits being the same and the last two digits being the same
429 if ( pin[0] == pin[1] && pin[2] == pin[3]){
430 free(pin);
431 return true; //weak password
432 }
433 //first two digits not being the same as the last two digits
434 if(pin[0] == pin[2] && pin[1] == pin[3]){
435 free(pin);
436 return true; //weak password
437 }
438 //not in this list
439 for(CFIndex i = 0; i < blacklistLength; i++)
440 {
441 const char* blackCode = blacklist[i];
442 if(0 == strcmp(blackCode, pin))
443 {
444 free(pin);
445 return true; //weak password
446 }
447 }
448 }
449 else if(isPasscodeNumber && CFStringGetLength(passcode) == 6){
450 pin = CFStringToCString(passcode);
451
452 //not all the same number
453 for(int i = 0; i < CFStringGetLength(passcode); i++){
454 if(i == CFStringGetLength(passcode)-1){
455 free(pin);
456 return true;
457 }
458 else if ((pin[i]) == pin[i+1])
459 continue;
460 else
461 break;
462 }
463 //passcode is incrementing ex 123456 or 654321
464 if(SecPasswordIsPasscodeIncrementingOrDecrementingDigits(passcode)) {
465 free(pin);
466 return true;
467 }
468 }
469 else//should be a 4 or 6digit number
470 return false;
471 }
472 else if(isPasscodeNumber && !isSimple){ //dealing with a complex numeric passcode
473 pin = CFStringToCString(passcode);
474 //check if PIN is all the same number
475 for(int i = 0; i < CFStringGetLength(passcode); i++){
476 if(i+1 >= CFStringGetLength(passcode)){
477 free(pin);
478 return true;
479 }
480 else if (pin[i] == pin[i+1])
481 continue;
482 else
483 break;
484 }
485 if(SecPasswordIsPasscodeIncrementingOrDecrementingDigits(passcode)) {
486 free(pin);
487 return true;
488 }
489 }
490 else{ // password is complex, evaluate entropy
491 int u = 0;
492 int l = 0;
493 int d = 0;
494 int p = 0;
495 int characterSet = 0;
496
497 //calculate new entropy
498 for(CFIndex i = 0; i < CFStringGetLength(passcode); i++){
499
500 if( CFStringFindCharacterFromSet(passcode, uppercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
501 u++;
502 continue;
503 }
504 if( CFStringFindCharacterFromSet(passcode, lowercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
505 l++;
506 continue;
507 }
508 if( CFStringFindCharacterFromSet(passcode, decimalDigitCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
509 d++;
510 continue;
511 }
512 if( CFStringFindCharacterFromSet(passcode, punctuationCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
513 p++;
514 continue;
515 }
516
517 }
518 if(u > 0){
519 characterSet += alphabetSetSize;
520 }
521 if(l > 0){
522 characterSet += alphabetSetSize;
523 }
524 if(d > 0){
525 characterSet += decimalSetSize;
526 }
527 if(p > 0){
528 characterSet += punctuationSetSize;
529 }
530
531 double strength = CFStringGetLength(passcode)*log2(characterSet);
532
533 if(strength < entropyStrengthThreshold){
534 return true; //weak
535 }
536 else
537 return false; //strong
538 }
539 if(pin)
540 free(pin);
541
542 return false; //strong password
543
544 }
545
546 static void getUniformRandomNumbers(uint8_t* buffer, size_t numberOfDesiredNumbers, uint8_t upperBound)
547 {
548
549 // The values returned by SecRandomCopyBytes are uniformly distributed in the range [0, 255]. If we try to map
550 // these values onto a smaller range using modulo we will introduce a bias towards lower numbers in situations
551 // where our smaller range doesn’t evenly divide in to [0, 255]. For example, with the desired range of [0, 54]
552 // the ranges 0..54, 55..109, 110..164, and 165..219 are uniformly distributed, but the range 220..255 modulo 55
553 // is only distributed over [0, 35], giving significant bias to these lower values. So, we ignore random numbers
554 // that would introduce this bias.
555 uint8_t limitAvoidingModuloBias = UCHAR_MAX - (UCHAR_MAX % upperBound);
556
557 for (size_t numberOfAcceptedNumbers = 0; numberOfAcceptedNumbers < numberOfDesiredNumbers; ) {
558 if (SecRandomCopyBytes(kSecRandomDefault, numberOfDesiredNumbers - numberOfAcceptedNumbers, buffer + numberOfAcceptedNumbers) == -1)
559 continue;
560 for (size_t i = numberOfAcceptedNumbers; i < numberOfDesiredNumbers; ++i) {
561 if (buffer[i] < limitAvoidingModuloBias)
562 buffer[numberOfAcceptedNumbers++] = buffer[i] % upperBound;
563 }
564 }
565 }
566
567 static bool passwordContainsRequiredCharacters(CFStringRef password, CFArrayRef requiredCharacterSets)
568 {
569 CFCharacterSetRef characterSet;
570
571 for (CFIndex i = 0; i< CFArrayGetCount(requiredCharacterSets); i++) {
572 characterSet = CFArrayGetValueAtIndex(requiredCharacterSets, i);
573 CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(password));
574 require_quiet(CFStringFindCharacterFromSet(password, characterSet, rangeToSearch, 0, NULL), fail);
575 }
576 return true;
577
578 fail:
579 return false;
580
581 }
582
583 static bool passwordContainsLessThanNIdenticalCharacters(CFStringRef password, CFIndex identicalCount)
584 {
585 unsigned char Char, nextChar;
586 int repeating = 0;
587
588 for(CFIndex i = 0; i < CFStringGetLength(password); i++){
589 Char = CFStringGetCharacterAtIndex(password, i);
590 for(CFIndex j = i; j< CFStringGetLength(password); j++){
591 nextChar = CFStringGetCharacterAtIndex(password, j);
592 require_quiet(repeating <= identicalCount, fail);
593 if(Char == nextChar){
594 repeating++;
595 }else{
596 repeating = 0;
597 break;
598 }
599 }
600 }
601 return true;
602 fail:
603 return false;
604 }
605
606 static bool passwordContainsAtLeastNCharacters(CFStringRef password, CFStringRef characters, CFIndex N)
607 {
608 CFCharacterSetRef characterSet = NULL;
609 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, characters);
610 CFIndex counter = 0;
611
612 for(CFIndex i = 0; i < CFStringGetLength(password); i++){
613 if(CFStringFindCharacterFromSet(password, characterSet, CFRangeMake(i, 1), 0, NULL))
614 counter++;
615 }
616 CFReleaseNull(characterSet);
617 if(counter < N)
618 return false;
619 else
620 return true;
621 }
622
623 static bool passwordContainsLessThanNCharacters(CFStringRef password, CFStringRef characters, CFIndex N)
624 {
625 CFCharacterSetRef characterSet = NULL;
626 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, characters);
627 CFIndex counter = 0;
628
629 for(CFIndex i = 0; i < CFStringGetLength(password); i++){
630 if(CFStringFindCharacterFromSet(password, characterSet, CFRangeMake(i, 1), 0, NULL))
631 counter++;
632 }
633 CFReleaseNull(characterSet);
634 if(counter > N)
635 return false;
636 else
637 return true;
638 }
639
640 static bool passwordDoesNotContainCharacters(CFStringRef password, CFStringRef prohibitedCharacters)
641 {
642 CFCharacterSetRef characterSet = NULL;
643 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
644 CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(password));
645
646 require_quiet(!CFStringFindCharacterFromSet(password, characterSet, rangeToSearch, 0, NULL), fail);
647 CFReleaseNull(characterSet);
648 return true;
649 fail:
650 CFReleaseNull(characterSet);
651 return false;
652 }
653
654 static void getPasswordRandomCharacters(CFStringRef *returned, CFDictionaryRef requirements, CFIndex *numberOfRandomCharacters, CFStringRef allowedCharacters)
655 {
656 uint8_t randomNumbers[*numberOfRandomCharacters];
657 unsigned char randomCharacters[*numberOfRandomCharacters];
658 getUniformRandomNumbers(randomNumbers, *numberOfRandomCharacters, CFStringGetLength(allowedCharacters));
659
660 CFTypeRef prohibitedCharacters = NULL;
661 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters))
662 prohibitedCharacters = NULL;
663
664 //it's faster for long characters to check each character produced for these cases
665 for (CFIndex i = 0; i < *numberOfRandomCharacters; ++i){
666 //check prohibited characters
667 UniChar randomChar[1];
668 randomChar[0] = CFStringGetCharacterAtIndex(allowedCharacters, randomNumbers[i]);
669 if (prohibitedCharacters != NULL)
670 {
671 CFStringRef temp = CFStringCreateWithCharacters(kCFAllocatorDefault, randomChar, 1);
672 bool pwdncc = passwordDoesNotContainCharacters(temp, prohibitedCharacters);
673 CFReleaseSafe(temp);
674 if (!pwdncc) {
675 //change up the random numbers so we don't get the same index into allowed
676 getUniformRandomNumbers(randomNumbers, *numberOfRandomCharacters, CFStringGetLength(allowedCharacters));
677 i--;
678 continue;
679 }
680 }
681 randomCharacters[i] = (unsigned char)randomChar[0];
682 }
683
684 *returned = CFStringCreateWithBytes(kCFAllocatorDefault, randomCharacters, *numberOfRandomCharacters, kCFStringEncodingUTF8, false);
685 }
686
687 static bool doesPasswordEndWith(CFStringRef password, CFStringRef prohibitedCharacters)
688 {
689 CFCharacterSetRef characterSet = NULL;
690 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
691
692 CFRange rangeToSearch = CFRangeMake(CFStringGetLength(password) - CFStringGetLength(prohibitedCharacters), CFStringGetLength(prohibitedCharacters));
693 require_quiet(0 == CFStringCompareWithOptions(password, prohibitedCharacters, rangeToSearch, 0), fail);
694 CFReleaseNull(characterSet);
695 return false;
696 fail:
697 CFReleaseNull(characterSet);
698 return true;
699 }
700
701 static bool doesPasswordStartWith(CFStringRef password, CFStringRef prohibitedCharacters)
702 {
703 CFCharacterSetRef characterSet = NULL;
704 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
705
706 CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(prohibitedCharacters));
707 require_quiet(0 == CFStringCompareWithOptions(password, prohibitedCharacters, rangeToSearch, 0), fail);
708 CFReleaseNull(characterSet);
709 return false; //does not start with prohibitedCharacters
710 fail:
711 CFReleaseNull(characterSet);
712 return true;
713 }
714
715 static CFDictionaryRef passwordGenerateCreateDefaultParametersDictionary(SecPasswordType type, CFDictionaryRef requirements){
716
717 CFMutableArrayRef requiredCharacterSets = NULL;
718 CFNumberRef numReqChars = NULL;
719 CFStringRef defaultPasswordFormat = NULL;
720 requiredCharacterSets = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
721 defaultPasswordFormat = CFSTR("true");
722 CFTypeRef groupSizeRef = NULL, numberOfGroupsRef = NULL;
723 CFIndex groupSize, numberOfGroups;
724 CFDictionaryRef returned = NULL;
725
726 switch(type){
727 case(kSecPasswordTypeiCloudRecovery):
728 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultiCloudPasswordLength);
729 groupSize = 4;
730 numberOfGroups = 6;
731 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
732 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
733
734 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
735 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
736 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
737 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
738 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
739 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
740 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
741 kSecAllowedCharactersKey, defaultiCloudCharacters,
742 kSecRequiredCharacterSetsKey, requiredCharacterSets,
743 kSecPasswordGroupSize, groupSizeRef,
744 kSecPasswordNumberOfGroups, numberOfGroupsRef,
745 NULL);
746 break;
747
748 case(kSecPasswordTypePIN):
749 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultPINLength);
750 groupSize = 4;
751 numberOfGroups = 1;
752 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
753 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
754
755 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
756 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
757 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
758 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
759 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
760 kSecAllowedCharactersKey, defaultPINCharacters,
761 kSecRequiredCharacterSetsKey, requiredCharacterSets,
762 kSecPasswordGroupSize, groupSizeRef,
763 kSecPasswordNumberOfGroups, numberOfGroupsRef,
764 NULL);
765 break;
766
767 case(kSecPasswordTypeWifi):
768 groupSize = 4;
769 numberOfGroups = 3;
770 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
771 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
772
773 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
774 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
775
776 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultWifiPasswordLength);
777 CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet);
778 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
779 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
780 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
781 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
782 kSecAllowedCharactersKey, defaultWifiCharacters,
783 kSecRequiredCharacterSetsKey, requiredCharacterSets,
784 kSecPasswordGroupSize, groupSizeRef,
785 kSecPasswordNumberOfGroups, numberOfGroupsRef,
786 NULL);
787 break;
788
789 default:
790 groupSize = 4;
791 numberOfGroups = 6;
792 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
793 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
794 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
795 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
796 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
797 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
798 CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet);
799 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
800
801 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultNumberOfRandomCharacters);
802 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
803 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
804 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
805 kSecAllowedCharactersKey, defaultCharacters,
806 kSecRequiredCharacterSetsKey, requiredCharacterSets,
807 kSecPasswordGroupSize, groupSizeRef,
808 kSecPasswordNumberOfGroups, numberOfGroupsRef,
809 NULL);
810
811
812
813 break;
814 }
815
816 CFReleaseNull(numReqChars);
817 CFReleaseNull(requiredCharacterSets);
818 CFReleaseNull(groupSizeRef);
819 CFReleaseNull(numberOfGroupsRef);
820 return returned;
821 }
822 static CFDictionaryRef passwordGenerationCreateParametersDictionary(SecPasswordType type, CFDictionaryRef requirements)
823 {
824 CFMutableArrayRef requiredCharacterSets = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
825 CFArrayRef requiredCharactersArray = NULL;
826 CFNumberRef numReqChars = NULL;
827 CFIndex numberOfRequiredRandomCharacters;
828 CFStringRef allowedCharacters = NULL, useDefaultPasswordFormat = NULL;
829 uint64_t valuePtr;
830 CFTypeRef prohibitedCharacters = NULL, endWith = NULL, startWith = NULL,
831 groupSizeRef = NULL, numberOfGroupsRef = NULL, separatorRef = NULL,
832 atMostCharactersRef = NULL,atLeastCharactersRef = NULL, identicalRef = NULL;
833
834 CFNumberRef min = (CFNumberRef)CFDictionaryGetValue(requirements, kSecPasswordMinLengthKey);
835 CFNumberRef max = (CFNumberRef)CFDictionaryGetValue(requirements, kSecPasswordMaxLengthKey);
836
837 CFNumberGetValue(min, kCFNumberSInt64Type, &valuePtr);
838 CFIndex minPasswordLength = (long)valuePtr;
839 CFNumberGetValue(max, kCFNumberSInt64Type, &valuePtr);
840 CFIndex maxPasswordLength = (long)valuePtr;
841
842 // If requirements allow, we will generate the password in default format.
843 useDefaultPasswordFormat = CFSTR("true");
844 numberOfRequiredRandomCharacters = defaultNumberOfRandomCharacters;
845
846 if(type == kSecPasswordTypePIN)
847 {
848 if( maxPasswordLength && minPasswordLength )
849 numberOfRequiredRandomCharacters = maxPasswordLength;
850 else if( !maxPasswordLength && minPasswordLength )
851 numberOfRequiredRandomCharacters = minPasswordLength;
852 else if( !minPasswordLength && maxPasswordLength )
853 numberOfRequiredRandomCharacters = maxPasswordLength;
854 else
855 numberOfRequiredRandomCharacters = defaultPINLength;
856
857 allowedCharacters = CFSTR("0123456789");
858 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
859 requiredCharactersArray = CFArrayCreateCopy(NULL, requiredCharacterSets);
860 useDefaultPasswordFormat = CFSTR("false");
861 }
862 else{
863 if (minPasswordLength && minPasswordLength > defaultNumberOfRandomCharacters) {
864 useDefaultPasswordFormat = CFSTR("false");
865 numberOfRequiredRandomCharacters = minPasswordLength;
866 }
867 if (maxPasswordLength && maxPasswordLength < defaultNumberOfRandomCharacters) {
868 useDefaultPasswordFormat = CFSTR("false");
869 numberOfRequiredRandomCharacters = maxPasswordLength;
870 }
871 if (maxPasswordLength && minPasswordLength && maxPasswordLength == minPasswordLength && maxPasswordLength != defaultNumberOfRandomCharacters){
872 useDefaultPasswordFormat = CFSTR("false");
873 numberOfRequiredRandomCharacters = maxPasswordLength;
874 }
875 allowedCharacters = (CFStringRef)CFDictionaryGetValue(requirements, kSecPasswordAllowedCharactersKey);
876 requiredCharactersArray = (CFArrayRef)CFDictionaryGetValue(requirements, kSecPasswordRequiredCharactersKey);
877 }
878 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters))
879 prohibitedCharacters = NULL;
880
881 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantEndWithChars, &endWith))
882 endWith = NULL;
883
884 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantStartWithChars, &startWith))
885 startWith = NULL;
886
887 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordGroupSize, &groupSizeRef))
888 groupSizeRef = NULL;
889
890 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef))
891 numberOfGroupsRef = NULL;
892
893 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordSeparator, &separatorRef))
894 separatorRef = NULL;
895
896 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharactersRef))
897 atMostCharactersRef = NULL;
898
899 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharactersRef))
900 atLeastCharactersRef = NULL;
901
902 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef))
903 identicalRef = NULL;
904
905 if (allowedCharacters) {
906 if( false == CFStringFindWithOptions(allowedCharacters, CFSTR("-"), CFRangeMake(0, CFStringGetLength(allowedCharacters)), kCFCompareCaseInsensitive, NULL))
907 useDefaultPasswordFormat = CFSTR("false");
908 } else
909 allowedCharacters = defaultCharacters;
910
911 // In default password format, we use dashes only as separators, not as symbols you can encounter at a random position.
912 if (useDefaultPasswordFormat == CFSTR("false")){
913 CFMutableStringRef mutatedAllowedCharacters = CFStringCreateMutableCopy(kCFAllocatorDefault, CFStringGetLength(allowedCharacters), allowedCharacters);
914 CFStringFindAndReplace (mutatedAllowedCharacters, CFSTR("-"), CFSTR(""), CFRangeMake(0, CFStringGetLength(allowedCharacters)),kCFCompareCaseInsensitive);
915 allowedCharacters = CFStringCreateCopy(kCFAllocatorDefault, mutatedAllowedCharacters);
916 }
917
918 if (requiredCharactersArray) {
919 for (CFIndex i = 0; i < CFArrayGetCount(requiredCharactersArray); i++){
920 CFCharacterSetRef stringWithRequiredCharacters = CFArrayGetValueAtIndex(requiredCharactersArray, i);
921 if(stringWithRequiredCharacters && CFStringFindCharacterFromSet(allowedCharacters, stringWithRequiredCharacters, CFRangeMake(0, CFStringGetLength(allowedCharacters)), 0, NULL)){
922 CFArrayAppendValue(requiredCharacterSets, stringWithRequiredCharacters);
923 }
924 }
925 } else{
926 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
927 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
928 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
929 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
930 CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet);
931 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
932 }
933
934
935 if (CFArrayGetCount(requiredCharacterSets) > numberOfRequiredRandomCharacters) {
936 CFReleaseNull(requiredCharacterSets);
937 requiredCharacterSets = NULL;
938 }
939 //create new CFDictionary
940 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, numberOfRequiredRandomCharacters);
941 CFMutableDictionaryRef updatedConstraints = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
942 CFDictionaryAddValue(updatedConstraints, kSecUseDefaultPasswordFormatKey, useDefaultPasswordFormat);
943 CFDictionarySetValue(updatedConstraints, kSecNumberOfRequiredRandomCharactersKey, numReqChars);
944 CFDictionaryAddValue(updatedConstraints, kSecAllowedCharactersKey, allowedCharacters);
945 if(requiredCharacterSets)
946 CFDictionaryAddValue(updatedConstraints, kSecRequiredCharacterSetsKey, requiredCharacterSets);
947
948 //add the prohibited characters string if it exists to the new dictionary
949 if(prohibitedCharacters)
950 CFDictionaryAddValue(updatedConstraints, kSecPasswordDisallowedCharacters, prohibitedCharacters);
951
952 //add the characters the password can't end with if it exists to the new dictionary
953 if(endWith)
954 CFDictionaryAddValue(updatedConstraints, kSecPasswordCantEndWithChars, endWith);
955
956 //add the characters the password can't start with if it exists to the new dictionary
957 if(startWith)
958 CFDictionaryAddValue(updatedConstraints, kSecPasswordCantStartWithChars, startWith);
959
960 if(groupSizeRef)
961 CFDictionaryAddValue(updatedConstraints, kSecPasswordGroupSize, groupSizeRef);
962
963 if(numberOfGroupsRef)
964 CFDictionaryAddValue(updatedConstraints, kSecPasswordNumberOfGroups, numberOfGroupsRef);
965
966 if(separatorRef)
967 CFDictionaryAddValue(updatedConstraints, kSecPasswordSeparator, separatorRef);
968
969 if(atMostCharactersRef)
970 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsNoMoreThanNSpecificCharacters, atMostCharactersRef);
971
972 if(atLeastCharactersRef)
973 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsAtLeastNSpecificCharacters, atLeastCharactersRef);
974
975 if(identicalRef)
976 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, identicalRef);
977
978 CFReleaseNull(useDefaultPasswordFormat);
979 CFReleaseNull(numReqChars);
980 CFReleaseNull(allowedCharacters);
981 CFReleaseNull(requiredCharacterSets);
982
983 return CFDictionaryCreateCopy(kCFAllocatorDefault, updatedConstraints);
984 }
985
986 static bool isDictionaryFormattedProperly(SecPasswordType type, CFDictionaryRef passwordRequirements, CFErrorRef *error){
987
988 CFTypeRef defaults = NULL;
989 CFErrorRef tempError = NULL;
990 if(passwordRequirements == NULL){
991 return true;
992 }
993
994 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) ){
995 if(isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0)){
996 return true;
997 }
998 }
999 //only need to check max and min pin length formatting
1000 if(type == kSecPasswordTypePIN){
1001 CFTypeRef minTest = NULL, maxTest = NULL;
1002 uint64_t valuePtr;
1003 CFIndex minPasswordLength = 0, maxPasswordLength= 0;
1004
1005 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) ){
1006 if(isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0)){
1007 return true;
1008 }
1009 }
1010 //check if the values exist!
1011 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMaxLengthKey, &maxTest) ){
1012 require_action_quiet(isNull(maxTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL));
1013 require_action_quiet(isNumber(maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
1014
1015 }
1016 if (CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMinLengthKey, &minTest) ){
1017 require_action_quiet(isNull(minTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL));
1018 require_action_quiet(isNumber(minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
1019 }
1020 //check if the values exist!
1021 if(maxTest){
1022 CFNumberRef max = (CFNumberRef)maxTest;
1023 CFNumberGetValue(max, kCFNumberSInt64Type, &valuePtr);
1024 maxPasswordLength = (long)valuePtr;
1025 }
1026 if(minTest){
1027 CFNumberRef min = (CFNumberRef)minTest;
1028 CFNumberGetValue(min, kCFNumberSInt64Type, &valuePtr);
1029 minPasswordLength = (long)valuePtr;
1030 }
1031 //make sure min and max make sense respective to each other and that they aren't less than 4 digits.
1032 require_action_quiet(minPasswordLength && maxPasswordLength && minPasswordLength <= maxPasswordLength, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex)errSecBadReq, NULL));
1033 require_action_quiet((minPasswordLength && minPasswordLength >= 4) || (maxPasswordLength && maxPasswordLength >= 4), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex)errSecBadReq, NULL));
1034 }
1035 else{
1036 CFTypeRef allowedTest, maxTest, minTest, requiredTest, prohibitedCharacters, endWith, startWith,
1037 groupSizeRef, numberOfGroupsRef, separatorRef, atMostCharactersRef,
1038 atLeastCharactersRef, thresholdRef, identicalRef, characters;
1039 uint64_t valuePtr;
1040
1041 //check if the values exist!
1042 require_action_quiet(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordAllowedCharactersKey, &allowedTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need a string of characters; password must only contain characters in this string"), (CFIndex)errSecBadReq, NULL));
1043 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMaxLengthKey, &maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL));
1044 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMinLengthKey, &minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL));
1045 require_action_quiet(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordRequiredCharactersKey, &requiredTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need an array of character sets, password must have at least 1 character from each set"), (CFIndex)errSecBadReq, NULL));
1046
1047 //check if values are null?
1048 require_action_quiet(isNull(allowedTest) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need a string of characters; password must only contain characters in this string"), (CFIndex)errSecBadReq, NULL));
1049 require_action_quiet(isNull(maxTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL));
1050 require_action_quiet(isNull(minTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL));
1051 require_action_quiet(isNull(requiredTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need an array of character sets, password must have at least 1 character from each set"), (CFIndex)errSecBadReq, NULL));
1052
1053 //check if the values are correct
1054 require_action_quiet(isString(allowedTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's allowed characters must be a CFStringRef"), (CFIndex)errSecBadReq, NULL));
1055 require_action_quiet(isNumber(maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
1056 require_action_quiet(isNumber(minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
1057 require_action_quiet(isArray(requiredTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's required characters must be an array of CFCharacterSetRefs"), (CFIndex)errSecBadReq, NULL));
1058
1059 CFNumberGetValue(minTest, kCFNumberSInt64Type, &valuePtr);
1060 CFIndex minPasswordLength = (long)valuePtr;
1061 CFNumberGetValue(maxTest, kCFNumberSInt64Type, &valuePtr);
1062 CFIndex maxPasswordLength = (long)valuePtr;
1063
1064 require_action_quiet(minPasswordLength <= maxPasswordLength, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex)errSecBadReq, NULL));
1065
1066 require_action_quiet(CFStringGetLength((CFStringRef)allowedTest) != 0, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need a string of characters; password must only contain characters in this string"), (CFIndex)errSecBadReq, NULL));
1067 require_action_quiet(CFArrayGetCount((CFArrayRef)requiredTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need an array of character sets, password must have at least 1 character from each set"), (CFIndex)errSecBadReq, NULL));
1068
1069 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters)){
1070 require_action_quiet(isNull(prohibitedCharacters) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Disallowed Characters dictionary parameter is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1071 require_action_quiet(isString(prohibitedCharacters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Disallowed Characters dictionary parameter is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1072 }
1073 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordCantEndWithChars, &endWith)){
1074 require_action_quiet(isNull(endWith) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'EndWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1075 require_action_quiet(isString(endWith), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'EndWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1076 }
1077 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordCantStartWithChars, &startWith)){
1078 require_action_quiet(isNull(startWith) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'StartWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1079 require_action_quiet(isString(startWith), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'StartWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1080 }
1081 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordGroupSize, &groupSizeRef)){
1082 require_action_quiet(isNull(groupSizeRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'groupsize' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1083 require_action_quiet(isNumber(groupSizeRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'groupsize' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1084 }
1085 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef)){
1086 require_action_quiet(isNull(numberOfGroupsRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'number of groupds' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1087 require_action_quiet(isNumber(numberOfGroupsRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'number of groupds' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1088 }
1089 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordSeparator, &separatorRef)){
1090 require_action_quiet(isNull(separatorRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'password separator character' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1091 require_action_quiet(isString(separatorRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'password separator character' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1092 }
1093
1094 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharactersRef)){
1095 require_action_quiet(isNull(atMostCharactersRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Most N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1096 require_action_quiet(isDictionary(atMostCharactersRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Most N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1097
1098 require_action_quiet(CFDictionaryGetValueIfPresent(atMostCharactersRef, kSecPasswordCharacterCount, &thresholdRef) != false, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Most N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1099 require_action_quiet(isNull(thresholdRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1100 require_action_quiet(isNumber(thresholdRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1101
1102 require_action_quiet(CFDictionaryGetValueIfPresent(atMostCharactersRef, kSecPasswordCharacters, &characters)!= false, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Most N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1103 require_action_quiet(isNull(characters) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1104 require_action_quiet(isString(characters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1105 }
1106
1107 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharactersRef)){
1108 require_action_quiet(isNull(atLeastCharactersRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Least N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1109 require_action_quiet(isDictionary(atLeastCharactersRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Least N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1110
1111 require_action_quiet(CFDictionaryGetValueIfPresent(atLeastCharactersRef, kSecPasswordCharacterCount, &thresholdRef) != false, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Least N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1112
1113 require_action_quiet(isNull(thresholdRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1114 require_action_quiet(isNumber(thresholdRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1115
1116 require_action_quiet(CFDictionaryGetValueIfPresent(atLeastCharactersRef, kSecPasswordCharacters, &characters) != false, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Least N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1117 require_action_quiet(isNull(characters) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1118 require_action_quiet(isString(characters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1119 }
1120
1121 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef)){
1122 require_action_quiet(isNull(identicalRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Identical Consecutive Characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1123 require_action_quiet(isNumber(identicalRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Identical Consecutive Characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1124 }
1125 }
1126
1127 fail:
1128 {
1129 bool result = true;
1130 if (tempError != NULL) {
1131 if (error)
1132 *error = CFRetainSafe(tempError);
1133 result = false;
1134 }
1135
1136 CFReleaseNull(tempError);
1137 return result;
1138 }
1139 }
1140
1141 static bool doesFinalPasswordPass(bool isSimple, CFStringRef password, CFDictionaryRef requirements){
1142
1143 CFTypeRef characters, identicalRef = NULL, NRef = NULL, endWith= NULL, startWith= NULL, atLeastCharacters= NULL, atMostCharacters = NULL;
1144 uint64_t valuePtr;
1145 CFIndex N, identicalCount;
1146 CFArrayRef requiredCharacterSet = (CFArrayRef)CFDictionaryGetValue(requirements, kSecRequiredCharacterSetsKey);
1147
1148 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantEndWithChars, &endWith))
1149 endWith = NULL;
1150
1151 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantStartWithChars, &startWith))
1152 startWith = NULL;
1153
1154 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharacters))
1155 atLeastCharacters = NULL;
1156
1157 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharacters))
1158 atMostCharacters = NULL;
1159
1160 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef))
1161 identicalRef = NULL;
1162 else{
1163 CFNumberGetValue((CFNumberRef)identicalRef, kCFNumberSInt64Type, &valuePtr);
1164 identicalCount = (long)valuePtr;
1165 }
1166 if(endWith != NULL)
1167 {
1168 if(!doesPasswordEndWith(password, endWith))
1169 return false;
1170 }
1171 if(startWith != NULL){
1172 if(!doesPasswordStartWith(password, startWith))
1173 return false;
1174 }
1175 if(atLeastCharacters != NULL){
1176 NRef = CFDictionaryGetValue(atLeastCharacters, kSecPasswordCharacterCount);
1177 characters = CFDictionaryGetValue(atLeastCharacters, kSecPasswordCharacters);
1178 CFNumberGetValue((CFNumberRef)NRef, kCFNumberSInt64Type, &valuePtr);
1179 N = (long)valuePtr;
1180 if(!passwordContainsAtLeastNCharacters(password, characters, N))
1181 return false;
1182 }
1183 if(atMostCharacters != NULL){
1184 NRef = CFDictionaryGetValue(atMostCharacters, kSecPasswordCharacterCount);
1185 characters = CFDictionaryGetValue(atMostCharacters, kSecPasswordCharacters);
1186 CFNumberGetValue((CFNumberRef)NRef, kCFNumberSInt64Type, &valuePtr);
1187 N = (long)valuePtr;
1188 if(!passwordContainsLessThanNCharacters(password, characters, N))
1189 return false;
1190 }
1191 if(identicalRef != NULL){
1192 if(!passwordContainsLessThanNIdenticalCharacters(password, identicalCount))
1193 return false;
1194 }
1195 if (!passwordContainsRequiredCharacters(password, requiredCharacterSet))
1196 return false;
1197
1198 if(true == SecPasswordIsPasswordWeak2(isSimple, password))
1199 return false;
1200
1201 return true;
1202 }
1203
1204 //entry point into password generation
1205 CF_RETURNS_RETAINED CFStringRef SecPasswordGenerate(SecPasswordType type, CFErrorRef *error, CFDictionaryRef passwordRequirements){
1206 bool check = false, isSimple = false;
1207 CFTypeRef separator = NULL, defaults = NULL, groupSizeRef = NULL, numberOfGroupsRef = NULL;
1208 CFDictionaryRef properlyFormattedRequirements = NULL;
1209 CFErrorRef localError = NULL;
1210 uint64_t valuePtr, groupSize, numberOfGroups;
1211 CFNumberRef numberOfRequiredRandomCharacters;
1212 CFIndex requiredCharactersSize;
1213 CFStringRef randomCharacters = NULL, password = NULL, allowedChars = NULL;
1214 CFMutableStringRef finalPassword = NULL;
1215
1216 if(type == kSecPasswordTypePIN)
1217 isSimple = true;
1218 else
1219 isSimple = false;
1220 check = isDictionaryFormattedProperly(type, passwordRequirements, &localError);
1221 require_quiet(check != false, fail);
1222
1223 //should we generate defaults?
1224 if(passwordRequirements == NULL || (CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) && isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0) ))
1225 properlyFormattedRequirements = passwordGenerateCreateDefaultParametersDictionary(type, passwordRequirements);
1226 else
1227 properlyFormattedRequirements = passwordGenerationCreateParametersDictionary(type, passwordRequirements);
1228
1229 require_quiet(localError == NULL && properlyFormattedRequirements != NULL, fail);
1230
1231 numberOfRequiredRandomCharacters = (CFNumberRef)CFDictionaryGetValue(properlyFormattedRequirements, kSecNumberOfRequiredRandomCharactersKey);
1232 CFNumberGetValue(numberOfRequiredRandomCharacters, kCFNumberSInt64Type, &valuePtr);
1233 requiredCharactersSize = (long)valuePtr;
1234
1235 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordGroupSize, &groupSizeRef)){
1236 groupSizeRef = NULL;
1237 }
1238 else
1239 CFNumberGetValue((CFNumberRef)groupSizeRef, kCFNumberSInt64Type, &groupSize);
1240
1241 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef)){
1242 numberOfGroupsRef = NULL;
1243 }
1244 else
1245 CFNumberGetValue((CFNumberRef)numberOfGroupsRef, kCFNumberSInt64Type, &numberOfGroups);
1246
1247 while (true) {
1248 allowedChars = CFDictionaryGetValue(properlyFormattedRequirements, kSecAllowedCharactersKey);
1249 getPasswordRandomCharacters(&randomCharacters, properlyFormattedRequirements, &requiredCharactersSize, allowedChars);
1250
1251 if(numberOfGroupsRef && groupSizeRef){
1252 finalPassword = CFStringCreateMutable(kCFAllocatorDefault, 0);
1253
1254 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordSeparator, &separator))
1255 separator = NULL;
1256
1257 if(separator == NULL)
1258 separator = CFSTR("-");
1259
1260 CFIndex i = 0;
1261 while( i != requiredCharactersSize){
1262 if((i + (CFIndex)groupSize) < requiredCharactersSize){
1263 CFStringAppend(finalPassword, CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, (CFIndex)groupSize)));
1264 CFStringAppend(finalPassword, separator);
1265 i+=groupSize;
1266 }
1267 else if((i+(CFIndex)groupSize) == requiredCharactersSize){
1268 CFStringAppend(finalPassword, CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, (CFIndex)groupSize)));
1269 i+=groupSize;
1270 }
1271 else {
1272 CFStringAppend(finalPassword, CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, requiredCharactersSize - i)));
1273 i+=(requiredCharactersSize - i);
1274 }
1275 }
1276 password = CFStringCreateCopy(kCFAllocatorDefault, finalPassword);
1277 CFReleaseNull(finalPassword);
1278 }
1279 //no fancy formatting
1280 else {
1281 password = CFStringCreateCopy(kCFAllocatorDefault, randomCharacters);
1282 }
1283
1284 CFReleaseNull(randomCharacters);
1285 require_quiet(doesFinalPasswordPass(isSimple, password, properlyFormattedRequirements), no_pass);
1286 CFReleaseNull(properlyFormattedRequirements);
1287 return password;
1288
1289 no_pass:
1290 CFReleaseNull(password);
1291 }
1292
1293 fail:
1294 if (error && localError) {
1295 *error = localError;
1296 localError = NULL;
1297 }
1298
1299 CFReleaseSafe(localError);
1300 CFReleaseNull(properlyFormattedRequirements);
1301 return NULL;
1302 }
1303
1304 const char *in_word_set (const char *str, unsigned int len){
1305 static const char * wordlist[] =
1306 {
1307 "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
1308 "", "", "0103", "", "", "", "", "0123", "", "", "", "", "0303", "", "", "",
1309 "", "", "", "", "0110", "", "1103", "", "", "", "", "1123", "", "", "0000",
1310 "", "1203", "", "0404", "", "", "", "", "1234", "1110", "2015", "2013", "",
1311 "2014", "1010", "2005", "2003", "", "2004", "1210", "0505", "0111", "", "",
1312 "", "2008", "0101", "", "2007", "", "", "", "", "2006", "2010", "1995", "1993",
1313 "", "1994", "2000", "", "1111", "", "", "", "1998", "1101", "", "1997", "",
1314 "0808", "1211", "", "1996", "0102", "", "1201", "", "", "1990", "", "", "",
1315 "", "0202", "", "2011", "", "", "1112", "1958", "2001", "", "1957", "1102",
1316 "", "3333", "", "1956", "1212", "1985", "1983", "", "1984", "1202", "", "0909",
1317 "", "0606", "", "1988", "1991", "", "1987", "2012", "", "", "", "1986", "2002",
1318 "", "", "", "0707", "1980", "", "2009", "", "", "2222", "1965", "1963", "",
1319 "1964", "", "", "2229", "", "", "1992", "1968", "", "", "1967", "", "", "1999",
1320 "", "1966", "", "1975", "1973", "", "1974", "1960", "", "1981", "", "4444",
1321 "", "1978", "", "7465", "1977", "", "", "", "", "1976", "2580", "", "1959",
1322 "", "", "1970", "", "", "", "", "", "", "", "", "", "1982", "", "1961", "",
1323 "", "5252", "", "1989", "", "", "", "", "", "", "", "", "", "", "", "", "",
1324 "", "1971", "", "", "", "", "", "", "", "1962", "", "5683", "", "6666", "",
1325 "", "1969", "", "", "", "", "", "", "", "", "", "", "", "", "1972", "", "",
1326 "", "", "", "", "1979", "", "", "", "7667"
1327 };
1328
1329 if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
1330 {
1331 register int key = pinhash (str, len);
1332
1333 if (key <= MAX_HASH_VALUE && key >= 0)
1334 {
1335 register const char *s = wordlist[key];
1336 if (*str == *s && !strcmp (str + 1, s + 1))
1337 return s;
1338 }
1339 }
1340 return 0;
1341 }
1342 CFDictionaryRef SecPasswordCopyDefaultPasswordLength(SecPasswordType type, CFErrorRef *error){
1343
1344 CFIndex tupleLengthInt = 0, numOfTuplesInt = 0;
1345 CFNumberRef tupleLength = NULL;
1346 CFNumberRef numOfTuples = NULL;
1347
1348 CFMutableDictionaryRef passwordLengthDefaults = NULL;
1349 CFDictionaryRef result = NULL;
1350
1351 switch(type){
1352 case(kSecPasswordTypeiCloudRecovery):
1353 tupleLengthInt = 4;
1354 numOfTuplesInt = 6;
1355 break;
1356
1357 case(kSecPasswordTypePIN):
1358 tupleLengthInt = 4;
1359 numOfTuplesInt = 1;
1360 break;
1361
1362 case(kSecPasswordTypeSafari):
1363 tupleLengthInt = 4;
1364 numOfTuplesInt = 5;
1365 break;
1366
1367 case(kSecPasswordTypeWifi):
1368 tupleLengthInt = 4;
1369 numOfTuplesInt = 3;
1370 break;
1371
1372 default:
1373 if(SecError(errSecBadReq, error, CFSTR("Password type does not exist.")) == false)
1374 {
1375 secdebug("secpasswordcopydefaultpasswordlength", "could not create error!");
1376 }
1377 }
1378
1379 if (tupleLengthInt != 0 && numOfTuplesInt != 0) {
1380 tupleLength = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &tupleLengthInt);
1381 numOfTuples = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &numOfTuplesInt);
1382 passwordLengthDefaults = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1383 CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordGroupSize, tupleLength);
1384 CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordNumberOfGroups, numOfTuples);
1385 result = CFDictionaryCreateCopy(kCFAllocatorDefault, passwordLengthDefaults);
1386 }
1387
1388 CFReleaseSafe(tupleLength);
1389 CFReleaseSafe(numOfTuples);
1390 CFReleaseSafe(passwordLengthDefaults);
1391 return result;
1392 }