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