2 * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecPasswordStrength.c
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>
37 #include <utilities/SecCFWrappers.h>
38 #include <utilities/SecCFRelease.h>
39 #include <utilities/SecCFError.h>
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");
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");
57 CFStringRef kSecPasswordGroupSize
= CFSTR("PasswordGroupSize");
58 CFStringRef kSecPasswordNumberOfGroups
= CFSTR("PasswordNumberOfGroups");
59 CFStringRef kSecPasswordSeparator
= CFSTR("SecPasswordSeparator");
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");
67 static CFIndex defaultNumberOfRandomCharacters
= 20;
68 static CFIndex defaultPINLength
= 4;
69 static CFIndex defaultiCloudPasswordLength
= 24;
70 static CFIndex defaultWifiPasswordLength
= 12;
72 static CFStringRef defaultWifiCharacters
= CFSTR("abcdefghijklmnopqrstuvwxyz1234567890");
73 static CFStringRef defaultPINCharacters
= CFSTR("0123456789");
74 static CFStringRef defaultiCloudCharacters
= CFSTR("ABCDEFGHJKLMNPQRSTUVWXYZ23456789");
75 static CFStringRef defaultCharacters
= CFSTR("abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789");
77 static CFCharacterSetRef uppercaseLetterCharacterSet
;
78 static CFCharacterSetRef lowercaseLetterCharacterSet
;
79 static CFCharacterSetRef decimalDigitCharacterSet
;
80 static CFCharacterSetRef punctuationCharacterSet
;
82 static CFIndex alphabetSetSize
= 26;
83 static CFIndex decimalSetSize
= 10;
84 static CFIndex punctuationSetSize
= 33;
85 static double entropyStrengthThreshold
= 35.0;
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
92 const char *in_word_set (const char *str
, unsigned int len
);
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>."
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 */
136 static unsigned int pinhash (const char *str
, unsigned int len
)
138 static unsigned short asso_values
[] =
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
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];
171 CFStringRef
SecPasswordCreateWithRandomDigits(int n
, CFErrorRef
*error
){
175 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
176 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
177 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
178 punctuationCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetPunctuation
);
180 CFNumberRef minRef
= CFNumberCreate(NULL
, kCFNumberIntType
, &min
);
181 CFNumberRef maxRef
= CFNumberCreate(NULL
, kCFNumberIntType
, &max
);
183 CFMutableDictionaryRef passwordRequirements
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
184 CFDictionaryAddValue(passwordRequirements
, kSecPasswordMinLengthKey
, minRef
);
185 CFDictionaryAddValue(passwordRequirements
, kSecPasswordMaxLengthKey
, maxRef
);
186 CFStringRef allowedCharacters
= CFSTR("0123456789");
188 CFDictionaryAddValue(passwordRequirements
, kSecPasswordAllowedCharactersKey
, allowedCharacters
);
190 CFStringRef password
= SecPasswordGenerate(kSecPasswordTypePIN
, error
, passwordRequirements
);
192 CFReleaseNull(minRef
);
193 CFReleaseNull(maxRef
);
194 CFReleaseNull(passwordRequirements
);
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
)
205 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
206 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
207 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
208 punctuationCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetPunctuation
);
210 bool isNumber
= true;
214 if( CFStringGetLength(passcode
) < 4 ){
215 return true; //weak password
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
))
226 //checking to see if it's a 4 digit pin
227 if(isNumber
&& CFStringGetLength(passcode
) == 4){
229 pin
= CFStringToCString(passcode
);
230 if(in_word_set(pin
, 4)){
235 CFIndex blacklistLength
= (CFIndex
)sizeof(blacklist
)/sizeof(blacklist
[0]);
237 //not all the same number
238 if(pin
[0] == pin
[1] == pin
[2] == pin
[3]){
240 return true; //weak password
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]){
245 return true; //weak password
247 //first two digits not being the same as the last two digits
248 if(pin
[0] == pin
[2] && pin
[1] == pin
[3]){
250 return true; //weak password
253 for(CFIndex i
= 0; i
< blacklistLength
; i
++)
255 const char* blackCode
= blacklist
[i
];
256 if(0 == strcmp(blackCode
, pin
))
259 return true; //weak password
263 else if(isNumber
){ //dealing with a numeric PIN
264 pin
= CFStringToCString(passcode
);
265 //check if PIN is all the same number
266 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
267 if(i
+1 >= CFStringGetLength(passcode
)){
271 else if (pin
[i
] == pin
[i
+1])
276 //check if PIN is a bunch of incrementing numbers
277 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
278 if(i
== CFStringGetLength(passcode
)-1){
282 else if ((pin
[i
] + 1) == pin
[i
+1])
287 //check if PIN is a bunch of decrementing numbers
288 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
289 if(i
== CFStringGetLength(passcode
)-1){
293 else if ((pin
[i
]) == (pin
[i
+1] +1))
299 else{ // password is complex, evaluate entropy
304 int characterSet
= 0;
306 //calculate new entropy
307 for(CFIndex i
= 0; i
< CFStringGetLength(passcode
); i
++){
309 if( CFStringFindCharacterFromSet(passcode
, uppercaseLetterCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
313 if( CFStringFindCharacterFromSet(passcode
, lowercaseLetterCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
317 if( CFStringFindCharacterFromSet(passcode
, decimalDigitCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
321 if( CFStringFindCharacterFromSet(passcode
, punctuationCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
328 characterSet
+= alphabetSetSize
;
331 characterSet
+= alphabetSetSize
;
334 characterSet
+= decimalSetSize
;
337 characterSet
+= punctuationSetSize
;
340 double strength
= CFStringGetLength(passcode
)*log2(characterSet
);
342 if(strength
< entropyStrengthThreshold
){
346 return false; //strong
351 return false; //strong password
355 static bool SecPasswordIsPasscodeIncrementingOrDecrementingDigits(CFStringRef passcode
)
357 char* pin
= CFStringToCString(passcode
);
359 //check if PIN is a bunch of incrementing numbers
360 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
361 if(i
== CFStringGetLength(passcode
)-1){
365 else if ((pin
[i
] + 1) == pin
[i
+1])
370 //check if PIN is a bunch of decrementing numbers
371 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
372 if(i
== CFStringGetLength(passcode
)-1){
376 else if ((pin
[i
]) == (pin
[i
+1] +1))
385 bool SecPasswordIsPasswordWeak2(bool isSimple
, CFStringRef passcode
)
387 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
388 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
389 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
390 punctuationCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetPunctuation
);
396 if( CFStringGetLength(passcode
) < 4 ){
397 return true; //weak password
400 bool isPasscodeNumber
= true;
401 //check to see if passcode is a number
402 for(CFIndex i
= 0; i
< CFStringGetLength(passcode
); i
++){
403 if( CFStringFindCharacterFromSet(passcode
, decimalDigitCharacterSet
, CFRangeMake(i
,1), 0, NULL
))
406 isPasscodeNumber
= false;
412 //checking to see if it's a 4 digit pin
413 if(isPasscodeNumber
&& CFStringGetLength(passcode
) == 4){
415 pin
= CFStringToCString(passcode
);
416 if(in_word_set(pin
, 4)){
421 CFIndex blacklistLength
= (CFIndex
)sizeof(blacklist
)/sizeof(blacklist
[0]);
423 //not all the same number
424 if(pin
[0] == pin
[1] == pin
[2] == pin
[3]){
426 return true; //weak password
428 //first two digits being the same and the last two digits being the same
429 if ( pin
[0] == pin
[1] && pin
[2] == pin
[3]){
431 return true; //weak password
433 //first two digits not being the same as the last two digits
434 if(pin
[0] == pin
[2] && pin
[1] == pin
[3]){
436 return true; //weak password
439 for(CFIndex i
= 0; i
< blacklistLength
; i
++)
441 const char* blackCode
= blacklist
[i
];
442 if(0 == strcmp(blackCode
, pin
))
445 return true; //weak password
449 else if(isPasscodeNumber
&& CFStringGetLength(passcode
) == 6){
450 pin
= CFStringToCString(passcode
);
452 //not all the same number
453 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
454 if(i
== CFStringGetLength(passcode
)-1){
458 else if ((pin
[i
]) == pin
[i
+1])
463 //passcode is incrementing ex 123456 or 654321
464 if(SecPasswordIsPasscodeIncrementingOrDecrementingDigits(passcode
)) {
469 else//should be a 4 or 6digit number
472 else if(isPasscodeNumber
&& !isSimple
){ //dealing with a complex numeric passcode
473 pin
= CFStringToCString(passcode
);
474 //check if PIN is all the same number
475 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
476 if(i
+1 >= CFStringGetLength(passcode
)){
480 else if (pin
[i
] == pin
[i
+1])
485 if(SecPasswordIsPasscodeIncrementingOrDecrementingDigits(passcode
)) {
490 else{ // password is complex, evaluate entropy
495 int characterSet
= 0;
497 //calculate new entropy
498 for(CFIndex i
= 0; i
< CFStringGetLength(passcode
); i
++){
500 if( CFStringFindCharacterFromSet(passcode
, uppercaseLetterCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
504 if( CFStringFindCharacterFromSet(passcode
, lowercaseLetterCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
508 if( CFStringFindCharacterFromSet(passcode
, decimalDigitCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
512 if( CFStringFindCharacterFromSet(passcode
, punctuationCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
519 characterSet
+= alphabetSetSize
;
522 characterSet
+= alphabetSetSize
;
525 characterSet
+= decimalSetSize
;
528 characterSet
+= punctuationSetSize
;
531 double strength
= CFStringGetLength(passcode
)*log2(characterSet
);
533 if(strength
< entropyStrengthThreshold
){
537 return false; //strong
542 return false; //strong password
546 static void getUniformRandomNumbers(uint8_t* buffer
, size_t numberOfDesiredNumbers
, uint8_t upperBound
)
549 // The values returned by SecRandomCopyBytes are uniformly distributed in the range [0, 255]. If we try to map
550 // these values onto a smaller range using modulo we will introduce a bias towards lower numbers in situations
551 // where our smaller range doesn’t evenly divide in to [0, 255]. For example, with the desired range of [0, 54]
552 // the ranges 0..54, 55..109, 110..164, and 165..219 are uniformly distributed, but the range 220..255 modulo 55
553 // is only distributed over [0, 35], giving significant bias to these lower values. So, we ignore random numbers
554 // that would introduce this bias.
555 uint8_t limitAvoidingModuloBias
= UCHAR_MAX
- (UCHAR_MAX
% upperBound
);
557 for (size_t numberOfAcceptedNumbers
= 0; numberOfAcceptedNumbers
< numberOfDesiredNumbers
; ) {
558 if (SecRandomCopyBytes(kSecRandomDefault
, numberOfDesiredNumbers
- numberOfAcceptedNumbers
, buffer
+ numberOfAcceptedNumbers
) == -1)
560 for (size_t i
= numberOfAcceptedNumbers
; i
< numberOfDesiredNumbers
; ++i
) {
561 if (buffer
[i
] < limitAvoidingModuloBias
)
562 buffer
[numberOfAcceptedNumbers
++] = buffer
[i
] % upperBound
;
567 static bool passwordContainsRequiredCharacters(CFStringRef password
, CFArrayRef requiredCharacterSets
)
569 CFCharacterSetRef characterSet
;
571 for (CFIndex i
= 0; i
< CFArrayGetCount(requiredCharacterSets
); i
++) {
572 characterSet
= CFArrayGetValueAtIndex(requiredCharacterSets
, i
);
573 CFRange rangeToSearch
= CFRangeMake(0, CFStringGetLength(password
));
574 require_quiet(CFStringFindCharacterFromSet(password
, characterSet
, rangeToSearch
, 0, NULL
), fail
);
583 static bool passwordContainsLessThanNIdenticalCharacters(CFStringRef password
, CFIndex identicalCount
)
585 unsigned char Char
, nextChar
;
588 for(CFIndex i
= 0; i
< CFStringGetLength(password
); i
++){
589 Char
= CFStringGetCharacterAtIndex(password
, i
);
590 for(CFIndex j
= i
; j
< CFStringGetLength(password
); j
++){
591 nextChar
= CFStringGetCharacterAtIndex(password
, j
);
592 require_quiet(repeating
<= identicalCount
, fail
);
593 if(Char
== nextChar
){
606 static bool passwordContainsAtLeastNCharacters(CFStringRef password
, CFStringRef characters
, CFIndex N
)
608 CFCharacterSetRef characterSet
= NULL
;
609 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, characters
);
612 for(CFIndex i
= 0; i
< CFStringGetLength(password
); i
++){
613 if(CFStringFindCharacterFromSet(password
, characterSet
, CFRangeMake(i
, 1), 0, NULL
))
616 CFReleaseNull(characterSet
);
623 static bool passwordContainsLessThanNCharacters(CFStringRef password
, CFStringRef characters
, CFIndex N
)
625 CFCharacterSetRef characterSet
= NULL
;
626 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, characters
);
629 for(CFIndex i
= 0; i
< CFStringGetLength(password
); i
++){
630 if(CFStringFindCharacterFromSet(password
, characterSet
, CFRangeMake(i
, 1), 0, NULL
))
633 CFReleaseNull(characterSet
);
640 static bool passwordDoesNotContainCharacters(CFStringRef password
, CFStringRef prohibitedCharacters
)
642 CFCharacterSetRef characterSet
= NULL
;
643 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, prohibitedCharacters
);
644 CFRange rangeToSearch
= CFRangeMake(0, CFStringGetLength(password
));
646 require_quiet(!CFStringFindCharacterFromSet(password
, characterSet
, rangeToSearch
, 0, NULL
), fail
);
647 CFReleaseNull(characterSet
);
650 CFReleaseNull(characterSet
);
654 static void getPasswordRandomCharacters(CFStringRef
*returned
, CFDictionaryRef requirements
, CFIndex
*numberOfRandomCharacters
, CFStringRef allowedCharacters
)
656 uint8_t randomNumbers
[*numberOfRandomCharacters
];
657 unsigned char randomCharacters
[*numberOfRandomCharacters
];
658 getUniformRandomNumbers(randomNumbers
, *numberOfRandomCharacters
, CFStringGetLength(allowedCharacters
));
660 CFTypeRef prohibitedCharacters
= NULL
;
661 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordDisallowedCharacters
, &prohibitedCharacters
))
662 prohibitedCharacters
= NULL
;
664 //it's faster for long characters to check each character produced for these cases
665 for (CFIndex i
= 0; i
< *numberOfRandomCharacters
; ++i
){
666 //check prohibited characters
667 UniChar randomChar
[1];
668 randomChar
[0] = CFStringGetCharacterAtIndex(allowedCharacters
, randomNumbers
[i
]);
669 if (prohibitedCharacters
!= NULL
)
671 CFStringRef temp
= CFStringCreateWithCharacters(kCFAllocatorDefault
, randomChar
, 1);
672 bool pwdncc
= passwordDoesNotContainCharacters(temp
, prohibitedCharacters
);
675 //change up the random numbers so we don't get the same index into allowed
676 getUniformRandomNumbers(randomNumbers
, *numberOfRandomCharacters
, CFStringGetLength(allowedCharacters
));
681 randomCharacters
[i
] = (unsigned char)randomChar
[0];
684 *returned
= CFStringCreateWithBytes(kCFAllocatorDefault
, randomCharacters
, *numberOfRandomCharacters
, kCFStringEncodingUTF8
, false);
687 static bool doesPasswordEndWith(CFStringRef password
, CFStringRef prohibitedCharacters
)
689 CFCharacterSetRef characterSet
= NULL
;
690 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, prohibitedCharacters
);
692 CFRange rangeToSearch
= CFRangeMake(CFStringGetLength(password
) - CFStringGetLength(prohibitedCharacters
), CFStringGetLength(prohibitedCharacters
));
693 require_quiet(0 == CFStringCompareWithOptions(password
, prohibitedCharacters
, rangeToSearch
, 0), fail
);
694 CFReleaseNull(characterSet
);
697 CFReleaseNull(characterSet
);
701 static bool doesPasswordStartWith(CFStringRef password
, CFStringRef prohibitedCharacters
)
703 CFCharacterSetRef characterSet
= NULL
;
704 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, prohibitedCharacters
);
706 CFRange rangeToSearch
= CFRangeMake(0, CFStringGetLength(prohibitedCharacters
));
707 require_quiet(0 == CFStringCompareWithOptions(password
, prohibitedCharacters
, rangeToSearch
, 0), fail
);
708 CFReleaseNull(characterSet
);
709 return false; //does not start with prohibitedCharacters
711 CFReleaseNull(characterSet
);
715 static CFDictionaryRef
passwordGenerateCreateDefaultParametersDictionary(SecPasswordType type
, CFDictionaryRef requirements
){
717 CFMutableArrayRef requiredCharacterSets
= NULL
;
718 CFNumberRef numReqChars
= NULL
;
719 CFStringRef defaultPasswordFormat
= NULL
;
720 requiredCharacterSets
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
721 defaultPasswordFormat
= CFSTR("true");
722 CFTypeRef groupSizeRef
= NULL
, numberOfGroupsRef
= NULL
;
723 CFIndex groupSize
, numberOfGroups
;
724 CFDictionaryRef returned
= NULL
;
727 case(kSecPasswordTypeiCloudRecovery
):
728 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, defaultiCloudPasswordLength
);
731 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &groupSize
);
732 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &numberOfGroups
);
734 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
735 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
736 CFArrayAppendValue(requiredCharacterSets
, uppercaseLetterCharacterSet
);
737 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
738 returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
739 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
740 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
741 kSecAllowedCharactersKey
, defaultiCloudCharacters
,
742 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
743 kSecPasswordGroupSize
, groupSizeRef
,
744 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
748 case(kSecPasswordTypePIN
):
749 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, defaultPINLength
);
752 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &groupSize
);
753 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &numberOfGroups
);
755 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
756 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
757 returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
758 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
759 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
760 kSecAllowedCharactersKey
, defaultPINCharacters
,
761 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
762 kSecPasswordGroupSize
, groupSizeRef
,
763 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
767 case(kSecPasswordTypeWifi
):
770 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &groupSize
);
771 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &numberOfGroups
);
773 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
774 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
776 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, defaultWifiPasswordLength
);
777 CFArrayAppendValue(requiredCharacterSets
, lowercaseLetterCharacterSet
);
778 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
779 returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
780 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
781 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
782 kSecAllowedCharactersKey
, defaultWifiCharacters
,
783 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
784 kSecPasswordGroupSize
, groupSizeRef
,
785 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
792 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &groupSize
);
793 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &numberOfGroups
);
794 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
795 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
796 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
797 CFArrayAppendValue(requiredCharacterSets
, uppercaseLetterCharacterSet
);
798 CFArrayAppendValue(requiredCharacterSets
, lowercaseLetterCharacterSet
);
799 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
801 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, defaultNumberOfRandomCharacters
);
802 returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
803 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
804 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
805 kSecAllowedCharactersKey
, defaultCharacters
,
806 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
807 kSecPasswordGroupSize
, groupSizeRef
,
808 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
816 CFReleaseNull(numReqChars
);
817 CFReleaseNull(requiredCharacterSets
);
818 CFReleaseNull(groupSizeRef
);
819 CFReleaseNull(numberOfGroupsRef
);
822 static CFDictionaryRef
passwordGenerationCreateParametersDictionary(SecPasswordType type
, CFDictionaryRef requirements
)
824 CFMutableArrayRef requiredCharacterSets
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
825 CFArrayRef requiredCharactersArray
= NULL
;
826 CFNumberRef numReqChars
= NULL
;
827 CFIndex numberOfRequiredRandomCharacters
;
828 CFStringRef allowedCharacters
= NULL
, useDefaultPasswordFormat
= NULL
;
830 CFTypeRef prohibitedCharacters
= NULL
, endWith
= NULL
, startWith
= NULL
,
831 groupSizeRef
= NULL
, numberOfGroupsRef
= NULL
, separatorRef
= NULL
,
832 atMostCharactersRef
= NULL
,atLeastCharactersRef
= NULL
, identicalRef
= NULL
;
834 CFNumberRef min
= (CFNumberRef
)CFDictionaryGetValue(requirements
, kSecPasswordMinLengthKey
);
835 CFNumberRef max
= (CFNumberRef
)CFDictionaryGetValue(requirements
, kSecPasswordMaxLengthKey
);
837 CFNumberGetValue(min
, kCFNumberSInt64Type
, &valuePtr
);
838 CFIndex minPasswordLength
= (long)valuePtr
;
839 CFNumberGetValue(max
, kCFNumberSInt64Type
, &valuePtr
);
840 CFIndex maxPasswordLength
= (long)valuePtr
;
842 // If requirements allow, we will generate the password in default format.
843 useDefaultPasswordFormat
= CFSTR("true");
844 numberOfRequiredRandomCharacters
= defaultNumberOfRandomCharacters
;
846 if(type
== kSecPasswordTypePIN
)
848 if( maxPasswordLength
&& minPasswordLength
)
849 numberOfRequiredRandomCharacters
= maxPasswordLength
;
850 else if( !maxPasswordLength
&& minPasswordLength
)
851 numberOfRequiredRandomCharacters
= minPasswordLength
;
852 else if( !minPasswordLength
&& maxPasswordLength
)
853 numberOfRequiredRandomCharacters
= maxPasswordLength
;
855 numberOfRequiredRandomCharacters
= defaultPINLength
;
857 allowedCharacters
= CFSTR("0123456789");
858 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
859 requiredCharactersArray
= CFArrayCreateCopy(NULL
, requiredCharacterSets
);
860 useDefaultPasswordFormat
= CFSTR("false");
863 if (minPasswordLength
&& minPasswordLength
> defaultNumberOfRandomCharacters
) {
864 useDefaultPasswordFormat
= CFSTR("false");
865 numberOfRequiredRandomCharacters
= minPasswordLength
;
867 if (maxPasswordLength
&& maxPasswordLength
< defaultNumberOfRandomCharacters
) {
868 useDefaultPasswordFormat
= CFSTR("false");
869 numberOfRequiredRandomCharacters
= maxPasswordLength
;
871 if (maxPasswordLength
&& minPasswordLength
&& maxPasswordLength
== minPasswordLength
&& maxPasswordLength
!= defaultNumberOfRandomCharacters
){
872 useDefaultPasswordFormat
= CFSTR("false");
873 numberOfRequiredRandomCharacters
= maxPasswordLength
;
875 allowedCharacters
= (CFStringRef
)CFDictionaryGetValue(requirements
, kSecPasswordAllowedCharactersKey
);
876 requiredCharactersArray
= (CFArrayRef
)CFDictionaryGetValue(requirements
, kSecPasswordRequiredCharactersKey
);
878 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordDisallowedCharacters
, &prohibitedCharacters
))
879 prohibitedCharacters
= NULL
;
881 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordCantEndWithChars
, &endWith
))
884 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordCantStartWithChars
, &startWith
))
887 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordGroupSize
, &groupSizeRef
))
890 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordNumberOfGroups
, &numberOfGroupsRef
))
891 numberOfGroupsRef
= NULL
;
893 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordSeparator
, &separatorRef
))
896 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsNoMoreThanNSpecificCharacters
, &atMostCharactersRef
))
897 atMostCharactersRef
= NULL
;
899 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsAtLeastNSpecificCharacters
, &atLeastCharactersRef
))
900 atLeastCharactersRef
= NULL
;
902 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
, &identicalRef
))
905 if (allowedCharacters
) {
906 if( false == CFStringFindWithOptions(allowedCharacters
, CFSTR("-"), CFRangeMake(0, CFStringGetLength(allowedCharacters
)), kCFCompareCaseInsensitive
, NULL
))
907 useDefaultPasswordFormat
= CFSTR("false");
909 allowedCharacters
= defaultCharacters
;
911 // In default password format, we use dashes only as separators, not as symbols you can encounter at a random position.
912 if (useDefaultPasswordFormat
== CFSTR("false")){
913 CFMutableStringRef mutatedAllowedCharacters
= CFStringCreateMutableCopy(kCFAllocatorDefault
, CFStringGetLength(allowedCharacters
), allowedCharacters
);
914 CFStringFindAndReplace (mutatedAllowedCharacters
, CFSTR("-"), CFSTR(""), CFRangeMake(0, CFStringGetLength(allowedCharacters
)),kCFCompareCaseInsensitive
);
915 allowedCharacters
= CFStringCreateCopy(kCFAllocatorDefault
, mutatedAllowedCharacters
);
918 if (requiredCharactersArray
) {
919 for (CFIndex i
= 0; i
< CFArrayGetCount(requiredCharactersArray
); i
++){
920 CFCharacterSetRef stringWithRequiredCharacters
= CFArrayGetValueAtIndex(requiredCharactersArray
, i
);
921 if(stringWithRequiredCharacters
&& CFStringFindCharacterFromSet(allowedCharacters
, stringWithRequiredCharacters
, CFRangeMake(0, CFStringGetLength(allowedCharacters
)), 0, NULL
)){
922 CFArrayAppendValue(requiredCharacterSets
, stringWithRequiredCharacters
);
926 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
927 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
928 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
929 CFArrayAppendValue(requiredCharacterSets
, uppercaseLetterCharacterSet
);
930 CFArrayAppendValue(requiredCharacterSets
, lowercaseLetterCharacterSet
);
931 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
935 if (CFArrayGetCount(requiredCharacterSets
) > numberOfRequiredRandomCharacters
) {
936 CFReleaseNull(requiredCharacterSets
);
937 requiredCharacterSets
= NULL
;
939 //create new CFDictionary
940 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, numberOfRequiredRandomCharacters
);
941 CFMutableDictionaryRef updatedConstraints
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
942 CFDictionaryAddValue(updatedConstraints
, kSecUseDefaultPasswordFormatKey
, useDefaultPasswordFormat
);
943 CFDictionarySetValue(updatedConstraints
, kSecNumberOfRequiredRandomCharactersKey
, numReqChars
);
944 CFDictionaryAddValue(updatedConstraints
, kSecAllowedCharactersKey
, allowedCharacters
);
945 if(requiredCharacterSets
)
946 CFDictionaryAddValue(updatedConstraints
, kSecRequiredCharacterSetsKey
, requiredCharacterSets
);
948 //add the prohibited characters string if it exists to the new dictionary
949 if(prohibitedCharacters
)
950 CFDictionaryAddValue(updatedConstraints
, kSecPasswordDisallowedCharacters
, prohibitedCharacters
);
952 //add the characters the password can't end with if it exists to the new dictionary
954 CFDictionaryAddValue(updatedConstraints
, kSecPasswordCantEndWithChars
, endWith
);
956 //add the characters the password can't start with if it exists to the new dictionary
958 CFDictionaryAddValue(updatedConstraints
, kSecPasswordCantStartWithChars
, startWith
);
961 CFDictionaryAddValue(updatedConstraints
, kSecPasswordGroupSize
, groupSizeRef
);
963 if(numberOfGroupsRef
)
964 CFDictionaryAddValue(updatedConstraints
, kSecPasswordNumberOfGroups
, numberOfGroupsRef
);
967 CFDictionaryAddValue(updatedConstraints
, kSecPasswordSeparator
, separatorRef
);
969 if(atMostCharactersRef
)
970 CFDictionaryAddValue(updatedConstraints
, kSecPasswordContainsNoMoreThanNSpecificCharacters
, atMostCharactersRef
);
972 if(atLeastCharactersRef
)
973 CFDictionaryAddValue(updatedConstraints
, kSecPasswordContainsAtLeastNSpecificCharacters
, atLeastCharactersRef
);
976 CFDictionaryAddValue(updatedConstraints
, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
, identicalRef
);
978 CFReleaseNull(useDefaultPasswordFormat
);
979 CFReleaseNull(numReqChars
);
980 CFReleaseNull(allowedCharacters
);
981 CFReleaseNull(requiredCharacterSets
);
983 return CFDictionaryCreateCopy(kCFAllocatorDefault
, updatedConstraints
);
986 static bool isDictionaryFormattedProperly(SecPasswordType type
, CFDictionaryRef passwordRequirements
, CFErrorRef
*error
){
988 CFTypeRef defaults
= NULL
;
989 CFErrorRef tempError
= NULL
;
990 if(passwordRequirements
== NULL
){
994 if( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordDefaultForType
, &defaults
) ){
995 if(isString(defaults
) == true && 0 == CFStringCompare(defaults
, CFSTR("true"), 0)){
999 //only need to check max and min pin length formatting
1000 if(type
== kSecPasswordTypePIN
){
1001 CFTypeRef minTest
= NULL
, maxTest
= NULL
;
1003 CFIndex minPasswordLength
= 0, maxPasswordLength
= 0;
1005 if( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordDefaultForType
, &defaults
) ){
1006 if(isString(defaults
) == true && 0 == CFStringCompare(defaults
, CFSTR("true"), 0)){
1010 //check if the values exist!
1011 if( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordMaxLengthKey
, &maxTest
) ){
1012 require_action_quiet(isNull(maxTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a max length"), (CFIndex
)errSecBadReq
, NULL
));
1013 require_action_quiet(isNumber(maxTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex
)errSecBadReq
, NULL
));
1016 if (CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordMinLengthKey
, &minTest
) ){
1017 require_action_quiet(isNull(minTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a min length"), (CFIndex
)errSecBadReq
, NULL
));
1018 require_action_quiet(isNumber(minTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex
)errSecBadReq
, NULL
));
1020 //check if the values exist!
1022 CFNumberRef max
= (CFNumberRef
)maxTest
;
1023 CFNumberGetValue(max
, kCFNumberSInt64Type
, &valuePtr
);
1024 maxPasswordLength
= (long)valuePtr
;
1027 CFNumberRef min
= (CFNumberRef
)minTest
;
1028 CFNumberGetValue(min
, kCFNumberSInt64Type
, &valuePtr
);
1029 minPasswordLength
= (long)valuePtr
;
1031 //make sure min and max make sense respective to each other and that they aren't less than 4 digits.
1032 require_action_quiet(minPasswordLength
&& maxPasswordLength
&& minPasswordLength
<= maxPasswordLength
, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex
)errSecBadReq
, NULL
));
1033 require_action_quiet((minPasswordLength
&& minPasswordLength
>= 4) || (maxPasswordLength
&& maxPasswordLength
>= 4), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex
)errSecBadReq
, NULL
));
1036 CFTypeRef allowedTest
, maxTest
, minTest
, requiredTest
, prohibitedCharacters
, endWith
, startWith
,
1037 groupSizeRef
, numberOfGroupsRef
, separatorRef
, atMostCharactersRef
,
1038 atLeastCharactersRef
, thresholdRef
, identicalRef
, characters
;
1041 //check if the values exist!
1042 require_action_quiet(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordAllowedCharactersKey
, &allowedTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("Need a string of characters; password must only contain characters in this string"), (CFIndex
)errSecBadReq
, NULL
));
1043 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordMaxLengthKey
, &maxTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a max length"), (CFIndex
)errSecBadReq
, NULL
));
1044 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordMinLengthKey
, &minTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a min length"), (CFIndex
)errSecBadReq
, NULL
));
1045 require_action_quiet(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordRequiredCharactersKey
, &requiredTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("Need an array of character sets, password must have at least 1 character from each set"), (CFIndex
)errSecBadReq
, NULL
));
1047 //check if values are null?
1048 require_action_quiet(isNull(allowedTest
) != true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("Need a string of characters; password must only contain characters in this string"), (CFIndex
)errSecBadReq
, NULL
));
1049 require_action_quiet(isNull(maxTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a max length"), (CFIndex
)errSecBadReq
, NULL
));
1050 require_action_quiet(isNull(minTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a min length"), (CFIndex
)errSecBadReq
, NULL
));
1051 require_action_quiet(isNull(requiredTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("Need an array of character sets, password must have at least 1 character from each set"), (CFIndex
)errSecBadReq
, NULL
));
1053 //check if the values are correct
1054 require_action_quiet(isString(allowedTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's allowed characters must be a CFStringRef"), (CFIndex
)errSecBadReq
, NULL
));
1055 require_action_quiet(isNumber(maxTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex
)errSecBadReq
, NULL
));
1056 require_action_quiet(isNumber(minTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex
)errSecBadReq
, NULL
));
1057 require_action_quiet(isArray(requiredTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's required characters must be an array of CFCharacterSetRefs"), (CFIndex
)errSecBadReq
, NULL
));
1059 CFNumberGetValue(minTest
, kCFNumberSInt64Type
, &valuePtr
);
1060 CFIndex minPasswordLength
= (long)valuePtr
;
1061 CFNumberGetValue(maxTest
, kCFNumberSInt64Type
, &valuePtr
);
1062 CFIndex maxPasswordLength
= (long)valuePtr
;
1064 require_action_quiet(minPasswordLength
<= maxPasswordLength
, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex
)errSecBadReq
, NULL
));
1066 require_action_quiet(CFStringGetLength((CFStringRef
)allowedTest
) != 0, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("Need a string of characters; password must only contain characters in this string"), (CFIndex
)errSecBadReq
, NULL
));
1067 require_action_quiet(CFArrayGetCount((CFArrayRef
)requiredTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("Need an array of character sets, password must have at least 1 character from each set"), (CFIndex
)errSecBadReq
, NULL
));
1069 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordDisallowedCharacters
, &prohibitedCharacters
)){
1070 require_action_quiet(isNull(prohibitedCharacters
) != true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("Disallowed Characters dictionary parameter is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1071 require_action_quiet(isString(prohibitedCharacters
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("Disallowed Characters dictionary parameter is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1073 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordCantEndWithChars
, &endWith
)){
1074 require_action_quiet(isNull(endWith
) != true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'EndWith' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1075 require_action_quiet(isString(endWith
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'EndWith' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1077 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordCantStartWithChars
, &startWith
)){
1078 require_action_quiet(isNull(startWith
) != true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'StartWith' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1079 require_action_quiet(isString(startWith
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'StartWith' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1081 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordGroupSize
, &groupSizeRef
)){
1082 require_action_quiet(isNull(groupSizeRef
) != true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'groupsize' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
1083 require_action_quiet(isNumber(groupSizeRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'groupsize' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
1085 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordNumberOfGroups
, &numberOfGroupsRef
)){
1086 require_action_quiet(isNull(numberOfGroupsRef
) != true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'number of groupds' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
1087 require_action_quiet(isNumber(numberOfGroupsRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'number of groupds' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
1089 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordSeparator
, &separatorRef
)){
1090 require_action_quiet(isNull(separatorRef
) != true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'password separator character' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1091 require_action_quiet(isString(separatorRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'password separator character' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1094 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordContainsNoMoreThanNSpecificCharacters
, &atMostCharactersRef
)){
1095 require_action_quiet(isNull(atMostCharactersRef
) != true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'At Most N Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1096 require_action_quiet(isDictionary(atMostCharactersRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'At Most N Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1098 require_action_quiet(CFDictionaryGetValueIfPresent(atMostCharactersRef
, kSecPasswordCharacterCount
, &thresholdRef
) != false, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'At Most N Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1099 require_action_quiet(isNull(thresholdRef
) != true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
1100 require_action_quiet(isNumber(thresholdRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
1102 require_action_quiet(CFDictionaryGetValueIfPresent(atMostCharactersRef
, kSecPasswordCharacters
, &characters
)!= false, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'At Most N Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1103 require_action_quiet(isNull(characters
) != true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1104 require_action_quiet(isString(characters
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1107 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordContainsAtLeastNSpecificCharacters
, &atLeastCharactersRef
)){
1108 require_action_quiet(isNull(atLeastCharactersRef
) != true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'At Least N Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1109 require_action_quiet(isDictionary(atLeastCharactersRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'At Least N Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1111 require_action_quiet(CFDictionaryGetValueIfPresent(atLeastCharactersRef
, kSecPasswordCharacterCount
, &thresholdRef
) != false, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'At Least N Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1113 require_action_quiet(isNull(thresholdRef
) != true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
1114 require_action_quiet(isNumber(thresholdRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
1116 require_action_quiet(CFDictionaryGetValueIfPresent(atLeastCharactersRef
, kSecPasswordCharacters
, &characters
) != false, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'At Least N Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1117 require_action_quiet(isNull(characters
) != true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1118 require_action_quiet(isString(characters
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1121 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
, &identicalRef
)){
1122 require_action_quiet(isNull(identicalRef
) != true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'Identical Consecutive Characters' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
1123 require_action_quiet(isNumber(identicalRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'Identical Consecutive Characters' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
1130 if (tempError
!= NULL
) {
1132 *error
= CFRetainSafe(tempError
);
1136 CFReleaseNull(tempError
);
1141 static bool doesFinalPasswordPass(bool isSimple
, CFStringRef password
, CFDictionaryRef requirements
){
1143 CFTypeRef characters
, identicalRef
= NULL
, NRef
= NULL
, endWith
= NULL
, startWith
= NULL
, atLeastCharacters
= NULL
, atMostCharacters
= NULL
;
1145 CFIndex N
, identicalCount
;
1146 CFArrayRef requiredCharacterSet
= (CFArrayRef
)CFDictionaryGetValue(requirements
, kSecRequiredCharacterSetsKey
);
1148 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordCantEndWithChars
, &endWith
))
1151 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordCantStartWithChars
, &startWith
))
1154 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsAtLeastNSpecificCharacters
, &atLeastCharacters
))
1155 atLeastCharacters
= NULL
;
1157 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsNoMoreThanNSpecificCharacters
, &atMostCharacters
))
1158 atMostCharacters
= NULL
;
1160 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
, &identicalRef
))
1161 identicalRef
= NULL
;
1163 CFNumberGetValue((CFNumberRef
)identicalRef
, kCFNumberSInt64Type
, &valuePtr
);
1164 identicalCount
= (long)valuePtr
;
1168 if(!doesPasswordEndWith(password
, endWith
))
1171 if(startWith
!= NULL
){
1172 if(!doesPasswordStartWith(password
, startWith
))
1175 if(atLeastCharacters
!= NULL
){
1176 NRef
= CFDictionaryGetValue(atLeastCharacters
, kSecPasswordCharacterCount
);
1177 characters
= CFDictionaryGetValue(atLeastCharacters
, kSecPasswordCharacters
);
1178 CFNumberGetValue((CFNumberRef
)NRef
, kCFNumberSInt64Type
, &valuePtr
);
1180 if(!passwordContainsAtLeastNCharacters(password
, characters
, N
))
1183 if(atMostCharacters
!= NULL
){
1184 NRef
= CFDictionaryGetValue(atMostCharacters
, kSecPasswordCharacterCount
);
1185 characters
= CFDictionaryGetValue(atMostCharacters
, kSecPasswordCharacters
);
1186 CFNumberGetValue((CFNumberRef
)NRef
, kCFNumberSInt64Type
, &valuePtr
);
1188 if(!passwordContainsLessThanNCharacters(password
, characters
, N
))
1191 if(identicalRef
!= NULL
){
1192 if(!passwordContainsLessThanNIdenticalCharacters(password
, identicalCount
))
1195 if (!passwordContainsRequiredCharacters(password
, requiredCharacterSet
))
1198 if(true == SecPasswordIsPasswordWeak2(isSimple
, password
))
1204 //entry point into password generation
1205 CF_RETURNS_RETAINED CFStringRef
SecPasswordGenerate(SecPasswordType type
, CFErrorRef
*error
, CFDictionaryRef passwordRequirements
){
1206 bool check
= false, isSimple
= false;
1207 CFTypeRef separator
= NULL
, defaults
= NULL
, groupSizeRef
= NULL
, numberOfGroupsRef
= NULL
;
1208 CFDictionaryRef properlyFormattedRequirements
= NULL
;
1209 CFErrorRef localError
= NULL
;
1210 uint64_t valuePtr
, groupSize
, numberOfGroups
;
1211 CFNumberRef numberOfRequiredRandomCharacters
;
1212 CFIndex requiredCharactersSize
;
1213 CFStringRef randomCharacters
= NULL
, password
= NULL
, allowedChars
= NULL
;
1214 CFMutableStringRef finalPassword
= NULL
;
1216 if(type
== kSecPasswordTypePIN
)
1220 check
= isDictionaryFormattedProperly(type
, passwordRequirements
, &localError
);
1221 require_quiet(check
!= false, fail
);
1223 //should we generate defaults?
1224 if(passwordRequirements
== NULL
|| (CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordDefaultForType
, &defaults
) && isString(defaults
) == true && 0 == CFStringCompare(defaults
, CFSTR("true"), 0) ))
1225 properlyFormattedRequirements
= passwordGenerateCreateDefaultParametersDictionary(type
, passwordRequirements
);
1227 properlyFormattedRequirements
= passwordGenerationCreateParametersDictionary(type
, passwordRequirements
);
1229 require_quiet(localError
== NULL
&& properlyFormattedRequirements
!= NULL
, fail
);
1231 numberOfRequiredRandomCharacters
= (CFNumberRef
)CFDictionaryGetValue(properlyFormattedRequirements
, kSecNumberOfRequiredRandomCharactersKey
);
1232 CFNumberGetValue(numberOfRequiredRandomCharacters
, kCFNumberSInt64Type
, &valuePtr
);
1233 requiredCharactersSize
= (long)valuePtr
;
1235 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements
, kSecPasswordGroupSize
, &groupSizeRef
)){
1236 groupSizeRef
= NULL
;
1239 CFNumberGetValue((CFNumberRef
)groupSizeRef
, kCFNumberSInt64Type
, &groupSize
);
1241 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements
, kSecPasswordNumberOfGroups
, &numberOfGroupsRef
)){
1242 numberOfGroupsRef
= NULL
;
1245 CFNumberGetValue((CFNumberRef
)numberOfGroupsRef
, kCFNumberSInt64Type
, &numberOfGroups
);
1248 allowedChars
= CFDictionaryGetValue(properlyFormattedRequirements
, kSecAllowedCharactersKey
);
1249 getPasswordRandomCharacters(&randomCharacters
, properlyFormattedRequirements
, &requiredCharactersSize
, allowedChars
);
1251 if(numberOfGroupsRef
&& groupSizeRef
){
1252 finalPassword
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1254 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements
, kSecPasswordSeparator
, &separator
))
1257 if(separator
== NULL
)
1258 separator
= CFSTR("-");
1261 while( i
!= requiredCharactersSize
){
1262 if((i
+ (CFIndex
)groupSize
) < requiredCharactersSize
){
1263 CFStringAppend(finalPassword
, CFStringCreateWithSubstring(kCFAllocatorDefault
, randomCharacters
, CFRangeMake(i
, (CFIndex
)groupSize
)));
1264 CFStringAppend(finalPassword
, separator
);
1267 else if((i
+(CFIndex
)groupSize
) == requiredCharactersSize
){
1268 CFStringAppend(finalPassword
, CFStringCreateWithSubstring(kCFAllocatorDefault
, randomCharacters
, CFRangeMake(i
, (CFIndex
)groupSize
)));
1272 CFStringAppend(finalPassword
, CFStringCreateWithSubstring(kCFAllocatorDefault
, randomCharacters
, CFRangeMake(i
, requiredCharactersSize
- i
)));
1273 i
+=(requiredCharactersSize
- i
);
1276 password
= CFStringCreateCopy(kCFAllocatorDefault
, finalPassword
);
1277 CFReleaseNull(finalPassword
);
1279 //no fancy formatting
1281 password
= CFStringCreateCopy(kCFAllocatorDefault
, randomCharacters
);
1284 CFReleaseNull(randomCharacters
);
1285 require_quiet(doesFinalPasswordPass(isSimple
, password
, properlyFormattedRequirements
), no_pass
);
1286 CFReleaseNull(properlyFormattedRequirements
);
1290 CFReleaseNull(password
);
1294 if (error
&& localError
) {
1295 *error
= localError
;
1299 CFReleaseSafe(localError
);
1300 CFReleaseNull(properlyFormattedRequirements
);
1304 const char *in_word_set (const char *str
, unsigned int len
){
1305 static const char * wordlist
[] =
1307 "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
1308 "", "", "0103", "", "", "", "", "0123", "", "", "", "", "0303", "", "", "",
1309 "", "", "", "", "0110", "", "1103", "", "", "", "", "1123", "", "", "0000",
1310 "", "1203", "", "0404", "", "", "", "", "1234", "1110", "2015", "2013", "",
1311 "2014", "1010", "2005", "2003", "", "2004", "1210", "0505", "0111", "", "",
1312 "", "2008", "0101", "", "2007", "", "", "", "", "2006", "2010", "1995", "1993",
1313 "", "1994", "2000", "", "1111", "", "", "", "1998", "1101", "", "1997", "",
1314 "0808", "1211", "", "1996", "0102", "", "1201", "", "", "1990", "", "", "",
1315 "", "0202", "", "2011", "", "", "1112", "1958", "2001", "", "1957", "1102",
1316 "", "3333", "", "1956", "1212", "1985", "1983", "", "1984", "1202", "", "0909",
1317 "", "0606", "", "1988", "1991", "", "1987", "2012", "", "", "", "1986", "2002",
1318 "", "", "", "0707", "1980", "", "2009", "", "", "2222", "1965", "1963", "",
1319 "1964", "", "", "2229", "", "", "1992", "1968", "", "", "1967", "", "", "1999",
1320 "", "1966", "", "1975", "1973", "", "1974", "1960", "", "1981", "", "4444",
1321 "", "1978", "", "7465", "1977", "", "", "", "", "1976", "2580", "", "1959",
1322 "", "", "1970", "", "", "", "", "", "", "", "", "", "1982", "", "1961", "",
1323 "", "5252", "", "1989", "", "", "", "", "", "", "", "", "", "", "", "", "",
1324 "", "1971", "", "", "", "", "", "", "", "1962", "", "5683", "", "6666", "",
1325 "", "1969", "", "", "", "", "", "", "", "", "", "", "", "", "1972", "", "",
1326 "", "", "", "", "1979", "", "", "", "7667"
1329 if (len
<= MAX_WORD_LENGTH
&& len
>= MIN_WORD_LENGTH
)
1331 register int key
= pinhash (str
, len
);
1333 if (key
<= MAX_HASH_VALUE
&& key
>= 0)
1335 register const char *s
= wordlist
[key
];
1336 if (*str
== *s
&& !strcmp (str
+ 1, s
+ 1))
1342 CFDictionaryRef
SecPasswordCopyDefaultPasswordLength(SecPasswordType type
, CFErrorRef
*error
){
1344 CFIndex tupleLengthInt
= 0, numOfTuplesInt
= 0;
1345 CFNumberRef tupleLength
= NULL
;
1346 CFNumberRef numOfTuples
= NULL
;
1348 CFMutableDictionaryRef passwordLengthDefaults
= NULL
;
1349 CFDictionaryRef result
= NULL
;
1352 case(kSecPasswordTypeiCloudRecovery
):
1357 case(kSecPasswordTypePIN
):
1362 case(kSecPasswordTypeSafari
):
1367 case(kSecPasswordTypeWifi
):
1373 if(SecError(errSecBadReq
, error
, CFSTR("Password type does not exist.")) == false)
1375 secdebug("secpasswordcopydefaultpasswordlength", "could not create error!");
1379 if (tupleLengthInt
!= 0 && numOfTuplesInt
!= 0) {
1380 tupleLength
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberCFIndexType
, &tupleLengthInt
);
1381 numOfTuples
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberCFIndexType
, &numOfTuplesInt
);
1382 passwordLengthDefaults
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1383 CFDictionaryAddValue(passwordLengthDefaults
, kSecPasswordGroupSize
, tupleLength
);
1384 CFDictionaryAddValue(passwordLengthDefaults
, kSecPasswordNumberOfGroups
, numOfTuples
);
1385 result
= CFDictionaryCreateCopy(kCFAllocatorDefault
, passwordLengthDefaults
);
1388 CFReleaseSafe(tupleLength
);
1389 CFReleaseSafe(numOfTuples
);
1390 CFReleaseSafe(passwordLengthDefaults
);