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