]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecPasswordGenerate.c
Security-57337.40.85.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 static bool SecPasswordIsPasswordRepeatingTwoNumbers(CFStringRef passcode){
386 char* pin = CFStringToCString(passcode);
387
388 for(int i = 0; i < CFStringGetLength(passcode); i++)
389 {
390 if(i+2 == CFStringGetLength(passcode)-1){
391 free(pin);
392 return true;
393 }
394 else if(pin[i] == pin[i+2])
395 continue;
396 else
397 break;
398 }
399
400 free(pin);
401 return false;
402 }
403
404 bool SecPasswordIsPasswordWeak2(bool isSimple, CFStringRef passcode)
405 {
406 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
407 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
408 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
409 punctuationCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetPunctuation);
410
411
412 char* pin = NULL;
413
414 //length checks
415 if( CFStringGetLength(passcode) < 4 ){
416 return true; //weak password
417 }
418
419 bool isPasscodeNumber = true;
420 //check to see if passcode is a number
421 for(CFIndex i = 0; i < CFStringGetLength(passcode); i++){
422 if( CFStringFindCharacterFromSet(passcode, decimalDigitCharacterSet, CFRangeMake(i,1), 0, NULL))
423 continue;
424 else {
425 isPasscodeNumber = false;
426 break;
427 }
428 }
429
430 if(isSimple){
431 //checking to see if it's a 4 digit pin
432 if(isPasscodeNumber && CFStringGetLength(passcode) == 4){
433
434 pin = CFStringToCString(passcode);
435 if(in_word_set(pin, 4)){
436 free(pin);
437 return true;
438 }
439
440 CFIndex blacklistLength = (CFIndex)sizeof(blacklist)/sizeof(blacklist[0]);
441
442 //not all the same number
443 if(pin[0] == pin[1] == pin[2] == pin[3]){
444 free(pin);
445 return true; //weak password
446 }
447 //first two digits being the same and the last two digits being the same
448 if ( pin[0] == pin[1] && pin[2] == pin[3]){
449 free(pin);
450 return true; //weak password
451 }
452 //first two digits not being the same as the last two digits
453 if(pin[0] == pin[2] && pin[1] == pin[3]){
454 free(pin);
455 return true; //weak password
456 }
457 //not in this list
458 for(CFIndex i = 0; i < blacklistLength; i++)
459 {
460 const char* blackCode = blacklist[i];
461 if(0 == strcmp(blackCode, pin))
462 {
463 free(pin);
464 return true; //weak password
465 }
466 }
467 }
468 else if(isPasscodeNumber && CFStringGetLength(passcode) == 6){
469 pin = CFStringToCString(passcode);
470
471 //not all the same number
472 for(int i = 0; i < CFStringGetLength(passcode); i++){
473 if(i == CFStringGetLength(passcode)-1){
474 free(pin);
475 return true;
476 }
477 else if ((pin[i]) == pin[i+1])
478 continue;
479 else
480 break;
481 }
482 //passcode is incrementing ex 123456 or 654321
483 if(SecPasswordIsPasscodeIncrementingOrDecrementingDigits(passcode)) {
484 free(pin);
485 return true;
486 }
487 //passcode does not consist of 2 repeating digits
488 if(SecPasswordIsPasswordRepeatingTwoNumbers(passcode)){
489 free(pin);
490 return true;
491 }
492 }
493 else//should be a 4 or 6digit number
494 return false;
495 }
496 else if(isPasscodeNumber && !isSimple){ //dealing with a complex numeric passcode
497 pin = CFStringToCString(passcode);
498 //check if PIN is all the same number
499 for(int i = 0; i < CFStringGetLength(passcode); i++){
500 if(i+1 >= CFStringGetLength(passcode)){
501 free(pin);
502 return true;
503 }
504 else if (pin[i] == pin[i+1])
505 continue;
506 else
507 break;
508 }
509 if(SecPasswordIsPasscodeIncrementingOrDecrementingDigits(passcode)) {
510 free(pin);
511 return true;
512 }
513 if(SecPasswordIsPasswordRepeatingTwoNumbers(passcode)){
514 free(pin);
515 return true;
516 }
517
518 }
519 else{ // password is complex, evaluate entropy
520 int u = 0;
521 int l = 0;
522 int d = 0;
523 int p = 0;
524 int characterSet = 0;
525
526 //calculate new entropy
527 for(CFIndex i = 0; i < CFStringGetLength(passcode); i++){
528
529 if( CFStringFindCharacterFromSet(passcode, uppercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
530 u++;
531 continue;
532 }
533 if( CFStringFindCharacterFromSet(passcode, lowercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
534 l++;
535 continue;
536 }
537 if( CFStringFindCharacterFromSet(passcode, decimalDigitCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
538 d++;
539 continue;
540 }
541 if( CFStringFindCharacterFromSet(passcode, punctuationCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
542 p++;
543 continue;
544 }
545
546 }
547 if(u > 0){
548 characterSet += alphabetSetSize;
549 }
550 if(l > 0){
551 characterSet += alphabetSetSize;
552 }
553 if(d > 0){
554 characterSet += decimalSetSize;
555 }
556 if(p > 0){
557 characterSet += punctuationSetSize;
558 }
559
560 double strength = CFStringGetLength(passcode)*log2(characterSet);
561
562 if(strength < entropyStrengthThreshold){
563 return true; //weak
564 }
565 else
566 return false; //strong
567 }
568 if(pin)
569 free(pin);
570
571 return false; //strong password
572
573 }
574
575 static void getUniformRandomNumbers(uint8_t* buffer, size_t numberOfDesiredNumbers, uint8_t upperBound)
576 {
577
578 // The values returned by SecRandomCopyBytes are uniformly distributed in the range [0, 255]. If we try to map
579 // these values onto a smaller range using modulo we will introduce a bias towards lower numbers in situations
580 // where our smaller range doesn’t evenly divide in to [0, 255]. For example, with the desired range of [0, 54]
581 // the ranges 0..54, 55..109, 110..164, and 165..219 are uniformly distributed, but the range 220..255 modulo 55
582 // is only distributed over [0, 35], giving significant bias to these lower values. So, we ignore random numbers
583 // that would introduce this bias.
584 uint8_t limitAvoidingModuloBias = UCHAR_MAX - (UCHAR_MAX % upperBound);
585
586 for (size_t numberOfAcceptedNumbers = 0; numberOfAcceptedNumbers < numberOfDesiredNumbers; ) {
587 if (SecRandomCopyBytes(kSecRandomDefault, numberOfDesiredNumbers - numberOfAcceptedNumbers, buffer + numberOfAcceptedNumbers) == -1)
588 continue;
589 for (size_t i = numberOfAcceptedNumbers; i < numberOfDesiredNumbers; ++i) {
590 if (buffer[i] < limitAvoidingModuloBias)
591 buffer[numberOfAcceptedNumbers++] = buffer[i] % upperBound;
592 }
593 }
594 }
595
596 static bool passwordContainsRequiredCharacters(CFStringRef password, CFArrayRef requiredCharacterSets)
597 {
598 CFCharacterSetRef characterSet;
599
600 for (CFIndex i = 0; i< CFArrayGetCount(requiredCharacterSets); i++) {
601 characterSet = CFArrayGetValueAtIndex(requiredCharacterSets, i);
602 CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(password));
603 require_quiet(CFStringFindCharacterFromSet(password, characterSet, rangeToSearch, 0, NULL), fail);
604 }
605 return true;
606
607 fail:
608 return false;
609
610 }
611
612 static bool passwordContainsLessThanNIdenticalCharacters(CFStringRef password, CFIndex identicalCount)
613 {
614 unsigned char Char, nextChar;
615 int repeating = 0;
616
617 for(CFIndex i = 0; i < CFStringGetLength(password); i++){
618 Char = CFStringGetCharacterAtIndex(password, i);
619 for(CFIndex j = i; j< CFStringGetLength(password); j++){
620 nextChar = CFStringGetCharacterAtIndex(password, j);
621 require_quiet(repeating <= identicalCount, fail);
622 if(Char == nextChar){
623 repeating++;
624 }else{
625 repeating = 0;
626 break;
627 }
628 }
629 }
630 return true;
631 fail:
632 return false;
633 }
634
635 static bool passwordContainsAtLeastNCharacters(CFStringRef password, CFStringRef characters, CFIndex N)
636 {
637 CFCharacterSetRef characterSet = NULL;
638 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, characters);
639 CFIndex counter = 0;
640
641 for(CFIndex i = 0; i < CFStringGetLength(password); i++){
642 if(CFStringFindCharacterFromSet(password, characterSet, CFRangeMake(i, 1), 0, NULL))
643 counter++;
644 }
645 CFReleaseNull(characterSet);
646 if(counter < N)
647 return false;
648 else
649 return true;
650 }
651
652 static bool passwordContainsLessThanNCharacters(CFStringRef password, CFStringRef characters, CFIndex N)
653 {
654 CFCharacterSetRef characterSet = NULL;
655 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, characters);
656 CFIndex counter = 0;
657
658 for(CFIndex i = 0; i < CFStringGetLength(password); i++){
659 if(CFStringFindCharacterFromSet(password, characterSet, CFRangeMake(i, 1), 0, NULL))
660 counter++;
661 }
662 CFReleaseNull(characterSet);
663 if(counter > N)
664 return false;
665 else
666 return true;
667 }
668
669 static bool passwordDoesNotContainCharacters(CFStringRef password, CFStringRef prohibitedCharacters)
670 {
671 CFCharacterSetRef characterSet = NULL;
672 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
673 CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(password));
674
675 require_quiet(!CFStringFindCharacterFromSet(password, characterSet, rangeToSearch, 0, NULL), fail);
676 CFReleaseNull(characterSet);
677 return true;
678 fail:
679 CFReleaseNull(characterSet);
680 return false;
681 }
682
683 static void getPasswordRandomCharacters(CFStringRef *returned, CFDictionaryRef requirements, CFIndex *numberOfRandomCharacters, CFStringRef allowedCharacters)
684 {
685 uint8_t randomNumbers[*numberOfRandomCharacters];
686 unsigned char randomCharacters[*numberOfRandomCharacters];
687 getUniformRandomNumbers(randomNumbers, *numberOfRandomCharacters, CFStringGetLength(allowedCharacters));
688
689 CFTypeRef prohibitedCharacters = NULL;
690 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters))
691 prohibitedCharacters = NULL;
692
693 //it's faster for long characters to check each character produced for these cases
694 for (CFIndex i = 0; i < *numberOfRandomCharacters; ++i){
695 //check prohibited characters
696 UniChar randomChar[1];
697 randomChar[0] = CFStringGetCharacterAtIndex(allowedCharacters, randomNumbers[i]);
698 if (prohibitedCharacters != NULL)
699 {
700 CFStringRef temp = CFStringCreateWithCharacters(kCFAllocatorDefault, randomChar, 1);
701 bool pwdncc = passwordDoesNotContainCharacters(temp, prohibitedCharacters);
702 CFReleaseSafe(temp);
703 if (!pwdncc) {
704 //change up the random numbers so we don't get the same index into allowed
705 getUniformRandomNumbers(randomNumbers, *numberOfRandomCharacters, CFStringGetLength(allowedCharacters));
706 i--;
707 continue;
708 }
709 }
710 randomCharacters[i] = (unsigned char)randomChar[0];
711 }
712
713 *returned = CFStringCreateWithBytes(kCFAllocatorDefault, randomCharacters, *numberOfRandomCharacters, kCFStringEncodingUTF8, false);
714 }
715
716 static bool doesPasswordEndWith(CFStringRef password, CFStringRef prohibitedCharacters)
717 {
718 CFCharacterSetRef characterSet = NULL;
719 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
720
721 CFRange rangeToSearch = CFRangeMake(CFStringGetLength(password) - CFStringGetLength(prohibitedCharacters), CFStringGetLength(prohibitedCharacters));
722 require_quiet(0 == CFStringCompareWithOptions(password, prohibitedCharacters, rangeToSearch, 0), fail);
723 CFReleaseNull(characterSet);
724 return false;
725 fail:
726 CFReleaseNull(characterSet);
727 return true;
728 }
729
730 static bool doesPasswordStartWith(CFStringRef password, CFStringRef prohibitedCharacters)
731 {
732 CFCharacterSetRef characterSet = NULL;
733 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
734
735 CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(prohibitedCharacters));
736 require_quiet(0 == CFStringCompareWithOptions(password, prohibitedCharacters, rangeToSearch, 0), fail);
737 CFReleaseNull(characterSet);
738 return false; //does not start with prohibitedCharacters
739 fail:
740 CFReleaseNull(characterSet);
741 return true;
742 }
743
744 static CFDictionaryRef passwordGenerateCreateDefaultParametersDictionary(SecPasswordType type, CFDictionaryRef requirements){
745
746 CFMutableArrayRef requiredCharacterSets = NULL;
747 CFNumberRef numReqChars = NULL;
748 CFStringRef defaultPasswordFormat = NULL;
749 requiredCharacterSets = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
750 defaultPasswordFormat = CFSTR("true");
751 CFTypeRef groupSizeRef = NULL, numberOfGroupsRef = NULL;
752 CFIndex groupSize, numberOfGroups;
753 CFDictionaryRef returned = NULL;
754
755 switch(type){
756 case(kSecPasswordTypeiCloudRecovery):
757 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultiCloudPasswordLength);
758 groupSize = 4;
759 numberOfGroups = 6;
760 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
761 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
762
763 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
764 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
765 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
766 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
767 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
768 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
769 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
770 kSecAllowedCharactersKey, defaultiCloudCharacters,
771 kSecRequiredCharacterSetsKey, requiredCharacterSets,
772 kSecPasswordGroupSize, groupSizeRef,
773 kSecPasswordNumberOfGroups, numberOfGroupsRef,
774 NULL);
775 break;
776
777 case(kSecPasswordTypePIN):
778 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultPINLength);
779 groupSize = 4;
780 numberOfGroups = 1;
781 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
782 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
783
784 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
785 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
786 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
787 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
788 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
789 kSecAllowedCharactersKey, defaultPINCharacters,
790 kSecRequiredCharacterSetsKey, requiredCharacterSets,
791 kSecPasswordGroupSize, groupSizeRef,
792 kSecPasswordNumberOfGroups, numberOfGroupsRef,
793 NULL);
794 break;
795
796 case(kSecPasswordTypeWifi):
797 groupSize = 4;
798 numberOfGroups = 3;
799 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
800 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
801
802 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
803 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
804
805 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultWifiPasswordLength);
806 CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet);
807 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
808 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
809 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
810 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
811 kSecAllowedCharactersKey, defaultWifiCharacters,
812 kSecRequiredCharacterSetsKey, requiredCharacterSets,
813 kSecPasswordGroupSize, groupSizeRef,
814 kSecPasswordNumberOfGroups, numberOfGroupsRef,
815 NULL);
816 break;
817
818 default:
819 groupSize = 4;
820 numberOfGroups = 6;
821 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
822 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
823 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
824 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
825 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
826 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
827 CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet);
828 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
829
830 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultNumberOfRandomCharacters);
831 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
832 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
833 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
834 kSecAllowedCharactersKey, defaultCharacters,
835 kSecRequiredCharacterSetsKey, requiredCharacterSets,
836 kSecPasswordGroupSize, groupSizeRef,
837 kSecPasswordNumberOfGroups, numberOfGroupsRef,
838 NULL);
839
840
841
842 break;
843 }
844
845 CFReleaseNull(numReqChars);
846 CFReleaseNull(requiredCharacterSets);
847 CFReleaseNull(groupSizeRef);
848 CFReleaseNull(numberOfGroupsRef);
849 return returned;
850 }
851 static CFDictionaryRef passwordGenerationCreateParametersDictionary(SecPasswordType type, CFDictionaryRef requirements)
852 {
853 CFMutableArrayRef requiredCharacterSets = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
854 CFArrayRef requiredCharactersArray = NULL;
855 CFNumberRef numReqChars = NULL;
856 CFIndex numberOfRequiredRandomCharacters;
857 CFStringRef allowedCharacters = NULL, useDefaultPasswordFormat = NULL;
858 uint64_t valuePtr;
859 CFTypeRef prohibitedCharacters = NULL, endWith = NULL, startWith = NULL,
860 groupSizeRef = NULL, numberOfGroupsRef = NULL, separatorRef = NULL,
861 atMostCharactersRef = NULL,atLeastCharactersRef = NULL, identicalRef = NULL;
862
863 CFNumberRef min = (CFNumberRef)CFDictionaryGetValue(requirements, kSecPasswordMinLengthKey);
864 CFNumberRef max = (CFNumberRef)CFDictionaryGetValue(requirements, kSecPasswordMaxLengthKey);
865
866 CFNumberGetValue(min, kCFNumberSInt64Type, &valuePtr);
867 CFIndex minPasswordLength = (long)valuePtr;
868 CFNumberGetValue(max, kCFNumberSInt64Type, &valuePtr);
869 CFIndex maxPasswordLength = (long)valuePtr;
870
871 // If requirements allow, we will generate the password in default format.
872 useDefaultPasswordFormat = CFSTR("true");
873 numberOfRequiredRandomCharacters = defaultNumberOfRandomCharacters;
874
875 if(type == kSecPasswordTypePIN)
876 {
877 if( maxPasswordLength && minPasswordLength )
878 numberOfRequiredRandomCharacters = maxPasswordLength;
879 else if( !maxPasswordLength && minPasswordLength )
880 numberOfRequiredRandomCharacters = minPasswordLength;
881 else if( !minPasswordLength && maxPasswordLength )
882 numberOfRequiredRandomCharacters = maxPasswordLength;
883 else
884 numberOfRequiredRandomCharacters = defaultPINLength;
885
886 allowedCharacters = CFSTR("0123456789");
887 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
888 requiredCharactersArray = CFArrayCreateCopy(NULL, requiredCharacterSets);
889 useDefaultPasswordFormat = CFSTR("false");
890 }
891 else{
892 if (minPasswordLength && minPasswordLength > defaultNumberOfRandomCharacters) {
893 useDefaultPasswordFormat = CFSTR("false");
894 numberOfRequiredRandomCharacters = minPasswordLength;
895 }
896 if (maxPasswordLength && maxPasswordLength < defaultNumberOfRandomCharacters) {
897 useDefaultPasswordFormat = CFSTR("false");
898 numberOfRequiredRandomCharacters = maxPasswordLength;
899 }
900 if (maxPasswordLength && minPasswordLength && maxPasswordLength == minPasswordLength && maxPasswordLength != defaultNumberOfRandomCharacters){
901 useDefaultPasswordFormat = CFSTR("false");
902 numberOfRequiredRandomCharacters = maxPasswordLength;
903 }
904 allowedCharacters = (CFStringRef)CFDictionaryGetValue(requirements, kSecPasswordAllowedCharactersKey);
905 requiredCharactersArray = (CFArrayRef)CFDictionaryGetValue(requirements, kSecPasswordRequiredCharactersKey);
906 }
907 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters))
908 prohibitedCharacters = NULL;
909
910 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantEndWithChars, &endWith))
911 endWith = NULL;
912
913 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantStartWithChars, &startWith))
914 startWith = NULL;
915
916 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordGroupSize, &groupSizeRef))
917 groupSizeRef = NULL;
918
919 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef))
920 numberOfGroupsRef = NULL;
921
922 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordSeparator, &separatorRef))
923 separatorRef = NULL;
924
925 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharactersRef))
926 atMostCharactersRef = NULL;
927
928 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharactersRef))
929 atLeastCharactersRef = NULL;
930
931 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef))
932 identicalRef = NULL;
933
934 if (allowedCharacters) {
935 if( false == CFStringFindWithOptions(allowedCharacters, CFSTR("-"), CFRangeMake(0, CFStringGetLength(allowedCharacters)), kCFCompareCaseInsensitive, NULL))
936 useDefaultPasswordFormat = CFSTR("false");
937 } else
938 allowedCharacters = defaultCharacters;
939
940 // In default password format, we use dashes only as separators, not as symbols you can encounter at a random position.
941 if (useDefaultPasswordFormat == CFSTR("false")){
942 CFMutableStringRef mutatedAllowedCharacters = CFStringCreateMutableCopy(kCFAllocatorDefault, CFStringGetLength(allowedCharacters), allowedCharacters);
943 CFStringFindAndReplace (mutatedAllowedCharacters, CFSTR("-"), CFSTR(""), CFRangeMake(0, CFStringGetLength(allowedCharacters)),kCFCompareCaseInsensitive);
944 allowedCharacters = CFStringCreateCopy(kCFAllocatorDefault, mutatedAllowedCharacters);
945 }
946
947 if (requiredCharactersArray) {
948 for (CFIndex i = 0; i < CFArrayGetCount(requiredCharactersArray); i++){
949 CFCharacterSetRef stringWithRequiredCharacters = CFArrayGetValueAtIndex(requiredCharactersArray, i);
950 if(stringWithRequiredCharacters && CFStringFindCharacterFromSet(allowedCharacters, stringWithRequiredCharacters, CFRangeMake(0, CFStringGetLength(allowedCharacters)), 0, NULL)){
951 CFArrayAppendValue(requiredCharacterSets, stringWithRequiredCharacters);
952 }
953 }
954 } else{
955 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
956 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
957 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
958 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
959 CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet);
960 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
961 }
962
963
964 if (CFArrayGetCount(requiredCharacterSets) > numberOfRequiredRandomCharacters) {
965 CFReleaseNull(requiredCharacterSets);
966 requiredCharacterSets = NULL;
967 }
968 //create new CFDictionary
969 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, numberOfRequiredRandomCharacters);
970 CFMutableDictionaryRef updatedConstraints = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
971 CFDictionaryAddValue(updatedConstraints, kSecUseDefaultPasswordFormatKey, useDefaultPasswordFormat);
972 CFDictionarySetValue(updatedConstraints, kSecNumberOfRequiredRandomCharactersKey, numReqChars);
973 CFDictionaryAddValue(updatedConstraints, kSecAllowedCharactersKey, allowedCharacters);
974 if(requiredCharacterSets)
975 CFDictionaryAddValue(updatedConstraints, kSecRequiredCharacterSetsKey, requiredCharacterSets);
976
977 //add the prohibited characters string if it exists to the new dictionary
978 if(prohibitedCharacters)
979 CFDictionaryAddValue(updatedConstraints, kSecPasswordDisallowedCharacters, prohibitedCharacters);
980
981 //add the characters the password can't end with if it exists to the new dictionary
982 if(endWith)
983 CFDictionaryAddValue(updatedConstraints, kSecPasswordCantEndWithChars, endWith);
984
985 //add the characters the password can't start with if it exists to the new dictionary
986 if(startWith)
987 CFDictionaryAddValue(updatedConstraints, kSecPasswordCantStartWithChars, startWith);
988
989 if(groupSizeRef)
990 CFDictionaryAddValue(updatedConstraints, kSecPasswordGroupSize, groupSizeRef);
991
992 if(numberOfGroupsRef)
993 CFDictionaryAddValue(updatedConstraints, kSecPasswordNumberOfGroups, numberOfGroupsRef);
994
995 if(separatorRef)
996 CFDictionaryAddValue(updatedConstraints, kSecPasswordSeparator, separatorRef);
997
998 if(atMostCharactersRef)
999 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsNoMoreThanNSpecificCharacters, atMostCharactersRef);
1000
1001 if(atLeastCharactersRef)
1002 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsAtLeastNSpecificCharacters, atLeastCharactersRef);
1003
1004 if(identicalRef)
1005 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, identicalRef);
1006
1007 CFReleaseNull(useDefaultPasswordFormat);
1008 CFReleaseNull(numReqChars);
1009 CFReleaseNull(allowedCharacters);
1010 CFReleaseNull(requiredCharacterSets);
1011
1012 return CFDictionaryCreateCopy(kCFAllocatorDefault, updatedConstraints);
1013 }
1014
1015 static bool isDictionaryFormattedProperly(SecPasswordType type, CFDictionaryRef passwordRequirements, CFErrorRef *error){
1016
1017 CFTypeRef defaults = NULL;
1018 CFErrorRef tempError = NULL;
1019 if(passwordRequirements == NULL){
1020 return true;
1021 }
1022
1023 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) ){
1024 if(isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0)){
1025 return true;
1026 }
1027 }
1028 //only need to check max and min pin length formatting
1029 if(type == kSecPasswordTypePIN){
1030 CFTypeRef minTest = NULL, maxTest = NULL;
1031 uint64_t valuePtr;
1032 CFIndex minPasswordLength = 0, maxPasswordLength= 0;
1033
1034 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) ){
1035 if(isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0)){
1036 return true;
1037 }
1038 }
1039 //check if the values exist!
1040 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMaxLengthKey, &maxTest) ){
1041 require_action_quiet(isNull(maxTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL));
1042 require_action_quiet(isNumber(maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
1043
1044 }
1045 if (CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMinLengthKey, &minTest) ){
1046 require_action_quiet(isNull(minTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL));
1047 require_action_quiet(isNumber(minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
1048 }
1049 //check if the values exist!
1050 if(maxTest){
1051 CFNumberRef max = (CFNumberRef)maxTest;
1052 CFNumberGetValue(max, kCFNumberSInt64Type, &valuePtr);
1053 maxPasswordLength = (long)valuePtr;
1054 }
1055 if(minTest){
1056 CFNumberRef min = (CFNumberRef)minTest;
1057 CFNumberGetValue(min, kCFNumberSInt64Type, &valuePtr);
1058 minPasswordLength = (long)valuePtr;
1059 }
1060 //make sure min and max make sense respective to each other and that they aren't less than 4 digits.
1061 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));
1062 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));
1063 }
1064 else{
1065 CFTypeRef allowedTest, maxTest, minTest, requiredTest, prohibitedCharacters, endWith, startWith,
1066 groupSizeRef, numberOfGroupsRef, separatorRef, atMostCharactersRef,
1067 atLeastCharactersRef, thresholdRef, identicalRef, characters;
1068 uint64_t valuePtr;
1069
1070 //check if the values exist!
1071 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));
1072 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMaxLengthKey, &maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL));
1073 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMinLengthKey, &minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL));
1074 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));
1075
1076 //check if values are null?
1077 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));
1078 require_action_quiet(isNull(maxTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL));
1079 require_action_quiet(isNull(minTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL));
1080 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));
1081
1082 //check if the values are correct
1083 require_action_quiet(isString(allowedTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's allowed characters must be a CFStringRef"), (CFIndex)errSecBadReq, NULL));
1084 require_action_quiet(isNumber(maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
1085 require_action_quiet(isNumber(minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
1086 require_action_quiet(isArray(requiredTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's required characters must be an array of CFCharacterSetRefs"), (CFIndex)errSecBadReq, NULL));
1087
1088 CFNumberGetValue(minTest, kCFNumberSInt64Type, &valuePtr);
1089 CFIndex minPasswordLength = (long)valuePtr;
1090 CFNumberGetValue(maxTest, kCFNumberSInt64Type, &valuePtr);
1091 CFIndex maxPasswordLength = (long)valuePtr;
1092
1093 require_action_quiet(minPasswordLength <= maxPasswordLength, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex)errSecBadReq, NULL));
1094
1095 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));
1096 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));
1097
1098 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters)){
1099 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));
1100 require_action_quiet(isString(prohibitedCharacters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Disallowed Characters dictionary parameter is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1101 }
1102 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordCantEndWithChars, &endWith)){
1103 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));
1104 require_action_quiet(isString(endWith), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'EndWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1105 }
1106 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordCantStartWithChars, &startWith)){
1107 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));
1108 require_action_quiet(isString(startWith), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'StartWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1109 }
1110 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordGroupSize, &groupSizeRef)){
1111 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));
1112 require_action_quiet(isNumber(groupSizeRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'groupsize' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1113 }
1114 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef)){
1115 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));
1116 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));
1117 }
1118 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordSeparator, &separatorRef)){
1119 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));
1120 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));
1121 }
1122
1123 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharactersRef)){
1124 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));
1125 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));
1126
1127 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));
1128 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));
1129 require_action_quiet(isNumber(thresholdRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1130
1131 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));
1132 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));
1133 require_action_quiet(isString(characters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1134 }
1135
1136 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharactersRef)){
1137 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));
1138 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));
1139
1140 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));
1141
1142 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));
1143 require_action_quiet(isNumber(thresholdRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1144
1145 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));
1146 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));
1147 require_action_quiet(isString(characters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1148 }
1149
1150 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef)){
1151 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));
1152 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));
1153 }
1154 }
1155
1156 fail:
1157 {
1158 bool result = true;
1159 if (tempError != NULL) {
1160 if (error)
1161 *error = CFRetainSafe(tempError);
1162 result = false;
1163 }
1164
1165 CFReleaseNull(tempError);
1166 return result;
1167 }
1168 }
1169
1170 static bool doesFinalPasswordPass(bool isSimple, CFStringRef password, CFDictionaryRef requirements){
1171
1172 CFTypeRef characters, identicalRef = NULL, NRef = NULL, endWith= NULL, startWith= NULL, atLeastCharacters= NULL, atMostCharacters = NULL;
1173 uint64_t valuePtr;
1174 CFIndex N, identicalCount;
1175 CFArrayRef requiredCharacterSet = (CFArrayRef)CFDictionaryGetValue(requirements, kSecRequiredCharacterSetsKey);
1176
1177 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantEndWithChars, &endWith))
1178 endWith = NULL;
1179
1180 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantStartWithChars, &startWith))
1181 startWith = NULL;
1182
1183 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharacters))
1184 atLeastCharacters = NULL;
1185
1186 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharacters))
1187 atMostCharacters = NULL;
1188
1189 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef))
1190 identicalRef = NULL;
1191 else{
1192 CFNumberGetValue((CFNumberRef)identicalRef, kCFNumberSInt64Type, &valuePtr);
1193 identicalCount = (long)valuePtr;
1194 }
1195 if(endWith != NULL)
1196 {
1197 if(!doesPasswordEndWith(password, endWith))
1198 return false;
1199 }
1200 if(startWith != NULL){
1201 if(!doesPasswordStartWith(password, startWith))
1202 return false;
1203 }
1204 if(atLeastCharacters != NULL){
1205 NRef = CFDictionaryGetValue(atLeastCharacters, kSecPasswordCharacterCount);
1206 characters = CFDictionaryGetValue(atLeastCharacters, kSecPasswordCharacters);
1207 CFNumberGetValue((CFNumberRef)NRef, kCFNumberSInt64Type, &valuePtr);
1208 N = (long)valuePtr;
1209 if(!passwordContainsAtLeastNCharacters(password, characters, N))
1210 return false;
1211 }
1212 if(atMostCharacters != NULL){
1213 NRef = CFDictionaryGetValue(atMostCharacters, kSecPasswordCharacterCount);
1214 characters = CFDictionaryGetValue(atMostCharacters, kSecPasswordCharacters);
1215 CFNumberGetValue((CFNumberRef)NRef, kCFNumberSInt64Type, &valuePtr);
1216 N = (long)valuePtr;
1217 if(!passwordContainsLessThanNCharacters(password, characters, N))
1218 return false;
1219 }
1220 if(identicalRef != NULL){
1221 if(!passwordContainsLessThanNIdenticalCharacters(password, identicalCount))
1222 return false;
1223 }
1224 if (!passwordContainsRequiredCharacters(password, requiredCharacterSet))
1225 return false;
1226
1227 if(true == SecPasswordIsPasswordWeak2(isSimple, password))
1228 return false;
1229
1230 return true;
1231 }
1232
1233 //entry point into password generation
1234 CF_RETURNS_RETAINED CFStringRef SecPasswordGenerate(SecPasswordType type, CFErrorRef *error, CFDictionaryRef passwordRequirements){
1235 bool check = false, isSimple = false;
1236 CFTypeRef separator = NULL, defaults = NULL, groupSizeRef = NULL, numberOfGroupsRef = NULL;
1237 CFDictionaryRef properlyFormattedRequirements = NULL;
1238 CFErrorRef localError = NULL;
1239 uint64_t valuePtr, groupSize, numberOfGroups;
1240 CFNumberRef numberOfRequiredRandomCharacters;
1241 CFIndex requiredCharactersSize;
1242 CFStringRef randomCharacters = NULL, password = NULL, allowedChars = NULL;
1243 CFMutableStringRef finalPassword = NULL;
1244
1245 if(type == kSecPasswordTypePIN)
1246 isSimple = true;
1247 else
1248 isSimple = false;
1249 check = isDictionaryFormattedProperly(type, passwordRequirements, &localError);
1250 require_quiet(check != false, fail);
1251
1252 //should we generate defaults?
1253 if(passwordRequirements == NULL || (CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) && isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0) ))
1254 properlyFormattedRequirements = passwordGenerateCreateDefaultParametersDictionary(type, passwordRequirements);
1255 else
1256 properlyFormattedRequirements = passwordGenerationCreateParametersDictionary(type, passwordRequirements);
1257
1258 require_quiet(localError == NULL && properlyFormattedRequirements != NULL, fail);
1259
1260 numberOfRequiredRandomCharacters = (CFNumberRef)CFDictionaryGetValue(properlyFormattedRequirements, kSecNumberOfRequiredRandomCharactersKey);
1261 CFNumberGetValue(numberOfRequiredRandomCharacters, kCFNumberSInt64Type, &valuePtr);
1262 requiredCharactersSize = (long)valuePtr;
1263
1264 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordGroupSize, &groupSizeRef)){
1265 groupSizeRef = NULL;
1266 }
1267 else
1268 CFNumberGetValue((CFNumberRef)groupSizeRef, kCFNumberSInt64Type, &groupSize);
1269
1270 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef)){
1271 numberOfGroupsRef = NULL;
1272 }
1273 else
1274 CFNumberGetValue((CFNumberRef)numberOfGroupsRef, kCFNumberSInt64Type, &numberOfGroups);
1275
1276 while (true) {
1277 allowedChars = CFDictionaryGetValue(properlyFormattedRequirements, kSecAllowedCharactersKey);
1278 getPasswordRandomCharacters(&randomCharacters, properlyFormattedRequirements, &requiredCharactersSize, allowedChars);
1279
1280 if(numberOfGroupsRef && groupSizeRef){
1281 finalPassword = CFStringCreateMutable(kCFAllocatorDefault, 0);
1282
1283 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordSeparator, &separator))
1284 separator = NULL;
1285
1286 if(separator == NULL)
1287 separator = CFSTR("-");
1288
1289 CFIndex i = 0;
1290 while( i != requiredCharactersSize){
1291 if((i + (CFIndex)groupSize) < requiredCharactersSize){
1292 CFStringAppend(finalPassword, CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, (CFIndex)groupSize)));
1293 CFStringAppend(finalPassword, separator);
1294 i+=groupSize;
1295 }
1296 else if((i+(CFIndex)groupSize) == requiredCharactersSize){
1297 CFStringAppend(finalPassword, CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, (CFIndex)groupSize)));
1298 i+=groupSize;
1299 }
1300 else {
1301 CFStringAppend(finalPassword, CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, requiredCharactersSize - i)));
1302 i+=(requiredCharactersSize - i);
1303 }
1304 }
1305 password = CFStringCreateCopy(kCFAllocatorDefault, finalPassword);
1306 CFReleaseNull(finalPassword);
1307 }
1308 //no fancy formatting
1309 else {
1310 password = CFStringCreateCopy(kCFAllocatorDefault, randomCharacters);
1311 }
1312
1313 CFReleaseNull(randomCharacters);
1314 require_quiet(doesFinalPasswordPass(isSimple, password, properlyFormattedRequirements), no_pass);
1315 CFReleaseNull(properlyFormattedRequirements);
1316 return password;
1317
1318 no_pass:
1319 CFReleaseNull(password);
1320 }
1321
1322 fail:
1323 if (error && localError) {
1324 *error = localError;
1325 localError = NULL;
1326 }
1327
1328 CFReleaseSafe(localError);
1329 CFReleaseNull(properlyFormattedRequirements);
1330 return NULL;
1331 }
1332
1333 const char *in_word_set (const char *str, unsigned int len){
1334 static const char * wordlist[] =
1335 {
1336 "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
1337 "", "", "0103", "", "", "", "", "0123", "", "", "", "", "0303", "", "", "",
1338 "", "", "", "", "0110", "", "1103", "", "", "", "", "1123", "", "", "0000",
1339 "", "1203", "", "0404", "", "", "", "", "1234", "1110", "2015", "2013", "",
1340 "2014", "1010", "2005", "2003", "", "2004", "1210", "0505", "0111", "", "",
1341 "", "2008", "0101", "", "2007", "", "", "", "", "2006", "2010", "1995", "1993",
1342 "", "1994", "2000", "", "1111", "", "", "", "1998", "1101", "", "1997", "",
1343 "0808", "1211", "", "1996", "0102", "", "1201", "", "", "1990", "", "", "",
1344 "", "0202", "", "2011", "", "", "1112", "1958", "2001", "", "1957", "1102",
1345 "", "3333", "", "1956", "1212", "1985", "1983", "", "1984", "1202", "", "0909",
1346 "", "0606", "", "1988", "1991", "", "1987", "2012", "", "", "", "1986", "2002",
1347 "", "", "", "0707", "1980", "", "2009", "", "", "2222", "1965", "1963", "",
1348 "1964", "", "", "2229", "", "", "1992", "1968", "", "", "1967", "", "", "1999",
1349 "", "1966", "", "1975", "1973", "", "1974", "1960", "", "1981", "", "4444",
1350 "", "1978", "", "7465", "1977", "", "", "", "", "1976", "2580", "", "1959",
1351 "", "", "1970", "", "", "", "", "", "", "", "", "", "1982", "", "1961", "",
1352 "", "5252", "", "1989", "", "", "", "", "", "", "", "", "", "", "", "", "",
1353 "", "1971", "", "", "", "", "", "", "", "1962", "", "5683", "", "6666", "",
1354 "", "1969", "", "", "", "", "", "", "", "", "", "", "", "", "1972", "", "",
1355 "", "", "", "", "1979", "", "", "", "7667"
1356 };
1357
1358 if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
1359 {
1360 register int key = pinhash (str, len);
1361
1362 if (key <= MAX_HASH_VALUE && key >= 0)
1363 {
1364 register const char *s = wordlist[key];
1365 if (*str == *s && !strcmp (str + 1, s + 1))
1366 return s;
1367 }
1368 }
1369 return 0;
1370 }
1371 CFDictionaryRef SecPasswordCopyDefaultPasswordLength(SecPasswordType type, CFErrorRef *error){
1372
1373 CFIndex tupleLengthInt = 0, numOfTuplesInt = 0;
1374 CFNumberRef tupleLength = NULL;
1375 CFNumberRef numOfTuples = NULL;
1376
1377 CFMutableDictionaryRef passwordLengthDefaults = NULL;
1378 CFDictionaryRef result = NULL;
1379
1380 switch(type){
1381 case(kSecPasswordTypeiCloudRecovery):
1382 tupleLengthInt = 4;
1383 numOfTuplesInt = 6;
1384 break;
1385
1386 case(kSecPasswordTypePIN):
1387 tupleLengthInt = 4;
1388 numOfTuplesInt = 1;
1389 break;
1390
1391 case(kSecPasswordTypeSafari):
1392 tupleLengthInt = 4;
1393 numOfTuplesInt = 5;
1394 break;
1395
1396 case(kSecPasswordTypeWifi):
1397 tupleLengthInt = 4;
1398 numOfTuplesInt = 3;
1399 break;
1400
1401 default:
1402 if(SecError(errSecBadReq, error, CFSTR("Password type does not exist.")) == false)
1403 {
1404 secdebug("secpasswordcopydefaultpasswordlength", "could not create error!");
1405 }
1406 }
1407
1408 if (tupleLengthInt != 0 && numOfTuplesInt != 0) {
1409 tupleLength = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &tupleLengthInt);
1410 numOfTuples = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &numOfTuplesInt);
1411 passwordLengthDefaults = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1412 CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordGroupSize, tupleLength);
1413 CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordNumberOfGroups, numOfTuples);
1414 result = CFDictionaryCreateCopy(kCFAllocatorDefault, passwordLengthDefaults);
1415 }
1416
1417 CFReleaseSafe(tupleLength);
1418 CFReleaseSafe(numOfTuples);
1419 CFReleaseSafe(passwordLengthDefaults);
1420 return result;
1421 }