2 * Copyright (c) 2013 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 //pins that reached the top 20 list
172 static const char *blacklist
[] = {"1234", "1004", "2000", "1122", "4321", "2001", "2580"};
174 bool SecPasswordIsPasswordWeak(CFStringRef passcode
)
176 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
177 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
178 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
179 punctuationCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetPunctuation
);
181 bool isNumber
= true;
185 if( CFStringGetLength(passcode
) < 4 ){
186 return true; //weak password
188 //check to see if passcode is a number
189 for(CFIndex i
= 0; i
< CFStringGetLength(passcode
); i
++){
190 if( CFStringFindCharacterFromSet(passcode
, decimalDigitCharacterSet
, CFRangeMake(i
,1), 0, NULL
))
197 //checking to see if it's a 4 digit pin
198 if(isNumber
&& CFStringGetLength(passcode
) == 4){
200 pin
= CFStringToCString(passcode
);
201 if(in_word_set(pin
, 4)){
206 CFIndex blacklistLength
= (CFIndex
)sizeof(blacklist
)/sizeof(blacklist
[0]);
208 //not all the same number
209 if(pin
[0] == pin
[1] == pin
[2] == pin
[3]){
211 return true; //weak password
213 //first two digits being the same and the last two digits being the same
214 if ( pin
[0] == pin
[1] && pin
[2] == pin
[3]){
216 return true; //weak password
218 //first two digits not being the same as the last two digits
219 if(pin
[0] == pin
[2] && pin
[1] == pin
[3]){
221 return true; //weak password
224 for(CFIndex i
= 0; i
< blacklistLength
; i
++)
226 const char* blackCode
= blacklist
[i
];
227 if(0 == strcmp(blackCode
, pin
))
230 return true; //weak password
234 else if(isNumber
){ //dealing with a numeric PIN
235 pin
= CFStringToCString(passcode
);
236 //check if PIN is all the same number
237 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
238 if(i
+1 >= CFStringGetLength(passcode
)){
242 else if (pin
[i
] == pin
[i
+1])
247 //check if PIN is a bunch of incrementing numbers
248 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
249 if(i
== CFStringGetLength(passcode
)-1){
253 else if ((pin
[i
] + 1) == pin
[i
+1])
258 //check if PIN is a bunch of decrementing numbers
259 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
260 if(i
== CFStringGetLength(passcode
)-1){
264 else if ((pin
[i
]) == (pin
[i
+1] +1))
270 else{ // password is complex, evaluate entropy
275 int characterSet
= 0;
277 //calculate new entropy
278 for(CFIndex i
= 0; i
< CFStringGetLength(passcode
); i
++){
280 if( CFStringFindCharacterFromSet(passcode
, uppercaseLetterCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
284 if( CFStringFindCharacterFromSet(passcode
, lowercaseLetterCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
288 if( CFStringFindCharacterFromSet(passcode
, decimalDigitCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
292 if( CFStringFindCharacterFromSet(passcode
, punctuationCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
299 characterSet
+= alphabetSetSize
;
302 characterSet
+= alphabetSetSize
;
305 characterSet
+= decimalSetSize
;
308 characterSet
+= punctuationSetSize
;
311 double strength
= CFStringGetLength(passcode
)*log2(characterSet
);
313 if(strength
< entropyStrengthThreshold
){
317 return false; //strong
322 return false; //strong password
326 static void getUniformRandomNumbers(uint8_t* buffer
, size_t numberOfDesiredNumbers
, uint8_t upperBound
)
329 // The values returned by SecRandomCopyBytes are uniformly distributed in the range [0, 255]. If we try to map
330 // these values onto a smaller range using modulo we will introduce a bias towards lower numbers in situations
331 // where our smaller range doesn’t evenly divide in to [0, 255]. For example, with the desired range of [0, 54]
332 // the ranges 0..54, 55..109, 110..164, and 165..219 are uniformly distributed, but the range 220..255 modulo 55
333 // is only distributed over [0, 35], giving significant bias to these lower values. So, we ignore random numbers
334 // that would introduce this bias.
335 uint8_t limitAvoidingModuloBias
= UCHAR_MAX
- (UCHAR_MAX
% upperBound
);
337 for (size_t numberOfAcceptedNumbers
= 0; numberOfAcceptedNumbers
< numberOfDesiredNumbers
; ) {
338 if (SecRandomCopyBytes(kSecRandomDefault
, numberOfDesiredNumbers
- numberOfAcceptedNumbers
, buffer
+ numberOfAcceptedNumbers
) == -1)
340 for (size_t i
= numberOfAcceptedNumbers
; i
< numberOfDesiredNumbers
; ++i
) {
341 if (buffer
[i
] < limitAvoidingModuloBias
)
342 buffer
[numberOfAcceptedNumbers
++] = buffer
[i
] % upperBound
;
347 static bool passwordContainsRequiredCharacters(CFStringRef password
, CFArrayRef requiredCharacterSets
)
349 CFCharacterSetRef characterSet
;
351 for (CFIndex i
= 0; i
< CFArrayGetCount(requiredCharacterSets
); i
++) {
352 characterSet
= CFArrayGetValueAtIndex(requiredCharacterSets
, i
);
353 CFRange rangeToSearch
= CFRangeMake(0, CFStringGetLength(password
));
354 require_quiet(CFStringFindCharacterFromSet(password
, characterSet
, rangeToSearch
, 0, NULL
), fail
);
363 static bool passwordContainsLessThanNIdenticalCharacters(CFStringRef password
, CFIndex identicalCount
)
365 unsigned char Char
, nextChar
;
368 for(CFIndex i
= 0; i
< CFStringGetLength(password
); i
++){
369 Char
= CFStringGetCharacterAtIndex(password
, i
);
370 for(CFIndex j
= i
; j
< CFStringGetLength(password
); j
++){
371 nextChar
= CFStringGetCharacterAtIndex(password
, j
);
372 require_quiet(repeating
<= identicalCount
, fail
);
373 if(Char
== nextChar
){
386 static bool passwordContainsAtLeastNCharacters(CFStringRef password
, CFStringRef characters
, CFIndex N
)
388 CFCharacterSetRef characterSet
= NULL
;
389 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, characters
);
392 for(CFIndex i
= 0; i
< CFStringGetLength(password
); i
++){
393 if(CFStringFindCharacterFromSet(password
, characterSet
, CFRangeMake(i
, 1), 0, NULL
))
396 CFReleaseNull(characterSet
);
403 static bool passwordContainsLessThanNCharacters(CFStringRef password
, CFStringRef characters
, CFIndex N
)
405 CFCharacterSetRef characterSet
= NULL
;
406 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, characters
);
409 for(CFIndex i
= 0; i
< CFStringGetLength(password
); i
++){
410 if(CFStringFindCharacterFromSet(password
, characterSet
, CFRangeMake(i
, 1), 0, NULL
))
413 CFReleaseNull(characterSet
);
420 static bool passwordDoesNotContainCharacters(CFStringRef password
, CFStringRef prohibitedCharacters
)
422 CFCharacterSetRef characterSet
= NULL
;
423 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, prohibitedCharacters
);
424 CFRange rangeToSearch
= CFRangeMake(0, CFStringGetLength(password
));
426 require_quiet(!CFStringFindCharacterFromSet(password
, characterSet
, rangeToSearch
, 0, NULL
), fail
);
427 CFReleaseNull(characterSet
);
430 CFReleaseNull(characterSet
);
434 static void getPasswordRandomCharacters(CFStringRef
*returned
, CFDictionaryRef requirements
, CFIndex
*numberOfRandomCharacters
, CFStringRef allowedCharacters
)
436 uint8_t randomNumbers
[*numberOfRandomCharacters
];
437 unsigned char randomCharacters
[*numberOfRandomCharacters
];
438 getUniformRandomNumbers(randomNumbers
, *numberOfRandomCharacters
, CFStringGetLength(allowedCharacters
));
440 CFTypeRef prohibitedCharacters
= NULL
;
441 CFStringRef temp
= NULL
;
443 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordDisallowedCharacters
, &prohibitedCharacters
))
444 prohibitedCharacters
= NULL
;
446 //it's faster for long characters to check each character produced for these cases
447 for (CFIndex i
= 0; i
< *numberOfRandomCharacters
; ++i
){
448 //check prohibited characters
449 UniChar randomChar
[1];
450 randomChar
[0] = CFStringGetCharacterAtIndex(allowedCharacters
, randomNumbers
[i
]);
451 temp
= CFStringCreateWithCharacters(kCFAllocatorDefault
, randomChar
, 1);
453 if(prohibitedCharacters
!= NULL
)
455 if(!passwordDoesNotContainCharacters(temp
, prohibitedCharacters
)){
456 //change up the random numbers so we don't get the same index into allowed
457 getUniformRandomNumbers(randomNumbers
, *numberOfRandomCharacters
, CFStringGetLength(allowedCharacters
));
462 randomCharacters
[i
] = (unsigned char)randomChar
[0];
467 *returned
= CFStringCreateWithBytes(kCFAllocatorDefault
, randomCharacters
, *numberOfRandomCharacters
, kCFStringEncodingUTF8
, false);
470 static bool doesPasswordEndWith(CFStringRef password
, CFStringRef prohibitedCharacters
)
472 CFCharacterSetRef characterSet
= NULL
;
473 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, prohibitedCharacters
);
475 CFRange rangeToSearch
= CFRangeMake(CFStringGetLength(password
) - CFStringGetLength(prohibitedCharacters
), CFStringGetLength(prohibitedCharacters
));
476 require_quiet(0 == CFStringCompareWithOptions(password
, prohibitedCharacters
, rangeToSearch
, 0), fail
);
477 CFReleaseNull(characterSet
);
480 CFReleaseNull(characterSet
);
484 static bool doesPasswordStartWith(CFStringRef password
, CFStringRef prohibitedCharacters
)
486 CFCharacterSetRef characterSet
= NULL
;
487 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, prohibitedCharacters
);
489 CFRange rangeToSearch
= CFRangeMake(0, CFStringGetLength(prohibitedCharacters
));
490 require_quiet(0 == CFStringCompareWithOptions(password
, prohibitedCharacters
, rangeToSearch
, 0), fail
);
491 CFReleaseNull(characterSet
);
492 return false; //does not start with prohibitedCharacters
494 CFReleaseNull(characterSet
);
498 static void passwordGenerateDefaultParametersDictionary(CFDictionaryRef
*returned
, SecPasswordType type
, CFDictionaryRef requirements
){
500 CFMutableArrayRef requiredCharacterSets
= NULL
;
501 CFNumberRef numReqChars
= NULL
;
502 CFStringRef defaultPasswordFormat
= NULL
;
503 requiredCharacterSets
= CFArrayCreateMutable(NULL
, 0, NULL
);
504 defaultPasswordFormat
= CFSTR("true");
505 CFTypeRef groupSizeRef
= NULL
, numberOfGroupsRef
= NULL
;
506 CFIndex groupSize
, numberOfGroups
;
508 case(kSecPasswordTypeiCloudRecovery
):
509 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, defaultiCloudPasswordLength
);
512 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberIntType
, &groupSize
);
513 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberIntType
, &numberOfGroups
);
515 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
516 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
517 CFArrayAppendValue(requiredCharacterSets
, uppercaseLetterCharacterSet
);
518 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
519 *returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
520 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
521 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
522 kSecAllowedCharactersKey
, defaultiCloudCharacters
,
523 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
524 kSecPasswordGroupSize
, groupSizeRef
,
525 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
529 case(kSecPasswordTypePIN
):
530 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, defaultPINLength
);
533 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberIntType
, &groupSize
);
534 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberIntType
, &numberOfGroups
);
536 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
537 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
538 *returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
539 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
540 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
541 kSecAllowedCharactersKey
, defaultPINCharacters
,
542 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
543 kSecPasswordGroupSize
, groupSizeRef
,
544 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
548 case(kSecPasswordTypeWifi
):
551 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberIntType
, &groupSize
);
552 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberIntType
, &numberOfGroups
);
554 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
555 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
557 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, defaultWifiPasswordLength
);
558 CFArrayAppendValue(requiredCharacterSets
, lowercaseLetterCharacterSet
);
559 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
560 *returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
561 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
562 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
563 kSecAllowedCharactersKey
, defaultWifiCharacters
,
564 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
565 kSecPasswordGroupSize
, groupSizeRef
,
566 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
573 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberIntType
, &groupSize
);
574 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberIntType
, &numberOfGroups
);
575 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
576 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
577 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
578 CFArrayAppendValue(requiredCharacterSets
, uppercaseLetterCharacterSet
);
579 CFArrayAppendValue(requiredCharacterSets
, lowercaseLetterCharacterSet
);
580 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
582 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, defaultNumberOfRandomCharacters
);
583 *returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
584 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
585 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
586 kSecAllowedCharactersKey
, defaultCharacters
,
587 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
588 kSecPasswordGroupSize
, groupSizeRef
,
589 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
597 CFReleaseNull(numReqChars
);
598 CFReleaseNull(requiredCharacterSets
);
599 CFReleaseNull(groupSizeRef
);
600 CFReleaseNull(numberOfGroupsRef
);
602 static void passwordGenerationParametersDictionary(CFDictionaryRef
*returned
, SecPasswordType type
, CFDictionaryRef requirements
)
604 CFMutableArrayRef requiredCharacterSets
= CFArrayCreateMutable(NULL
, 0, NULL
);
605 CFArrayRef requiredCharactersArray
= NULL
;
606 CFNumberRef numReqChars
= NULL
;
607 CFIndex numberOfRequiredRandomCharacters
;
608 CFStringRef allowedCharacters
= NULL
, useDefaultPasswordFormat
= NULL
;
610 CFTypeRef prohibitedCharacters
= NULL
, endWith
= NULL
, startWith
= NULL
,
611 groupSizeRef
= NULL
, numberOfGroupsRef
= NULL
, separatorRef
= NULL
,
612 atMostCharactersRef
= NULL
,atLeastCharactersRef
= NULL
, identicalRef
= NULL
;
614 CFNumberRef min
= (CFNumberRef
)CFDictionaryGetValue(requirements
, kSecPasswordMinLengthKey
);
615 CFNumberRef max
= (CFNumberRef
)CFDictionaryGetValue(requirements
, kSecPasswordMaxLengthKey
);
617 CFNumberGetValue(min
, kCFNumberSInt64Type
, &valuePtr
);
618 CFIndex minPasswordLength
= (long)valuePtr
;
619 CFNumberGetValue(max
, kCFNumberSInt64Type
, &valuePtr
);
620 CFIndex maxPasswordLength
= (long)valuePtr
;
622 // If requirements allow, we will generate the password in default format.
623 useDefaultPasswordFormat
= CFSTR("true");
624 numberOfRequiredRandomCharacters
= defaultNumberOfRandomCharacters
;
626 if(type
== kSecPasswordTypePIN
)
628 if( maxPasswordLength
&& minPasswordLength
)
629 numberOfRequiredRandomCharacters
= maxPasswordLength
;
630 else if( !maxPasswordLength
&& minPasswordLength
)
631 numberOfRequiredRandomCharacters
= minPasswordLength
;
632 else if( !minPasswordLength
&& maxPasswordLength
)
633 numberOfRequiredRandomCharacters
= maxPasswordLength
;
635 numberOfRequiredRandomCharacters
= defaultPINLength
;
637 allowedCharacters
= CFSTR("0123456789");
638 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
639 requiredCharactersArray
= CFArrayCreateCopy(NULL
, requiredCharacterSets
);
640 useDefaultPasswordFormat
= CFSTR("false");
643 if (minPasswordLength
&& minPasswordLength
> defaultNumberOfRandomCharacters
) {
644 useDefaultPasswordFormat
= CFSTR("false");
645 numberOfRequiredRandomCharacters
= minPasswordLength
;
647 if (maxPasswordLength
&& maxPasswordLength
< defaultNumberOfRandomCharacters
) {
648 useDefaultPasswordFormat
= CFSTR("false");
649 numberOfRequiredRandomCharacters
= maxPasswordLength
;
651 if (maxPasswordLength
&& minPasswordLength
&& maxPasswordLength
== minPasswordLength
&& maxPasswordLength
!= defaultNumberOfRandomCharacters
){
652 useDefaultPasswordFormat
= CFSTR("false");
653 numberOfRequiredRandomCharacters
= maxPasswordLength
;
655 allowedCharacters
= (CFStringRef
)CFDictionaryGetValue(requirements
, kSecPasswordAllowedCharactersKey
);
656 requiredCharactersArray
= (CFArrayRef
)CFDictionaryGetValue(requirements
, kSecPasswordRequiredCharactersKey
);
658 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordDisallowedCharacters
, &prohibitedCharacters
))
659 prohibitedCharacters
= NULL
;
661 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordCantEndWithChars
, &endWith
))
664 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordCantStartWithChars
, &startWith
))
667 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordGroupSize
, &groupSizeRef
))
670 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordNumberOfGroups
, &numberOfGroupsRef
))
671 numberOfGroupsRef
= NULL
;
673 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordSeparator
, &separatorRef
))
676 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsNoMoreThanNSpecificCharacters
, &atMostCharactersRef
))
677 atMostCharactersRef
= NULL
;
679 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsAtLeastNSpecificCharacters
, &atLeastCharactersRef
))
680 atLeastCharactersRef
= NULL
;
682 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
, &identicalRef
))
685 if (allowedCharacters
) {
686 if( false == CFStringFindWithOptions(allowedCharacters
, CFSTR("-"), CFRangeMake(0, CFStringGetLength(allowedCharacters
)), kCFCompareCaseInsensitive
, NULL
))
687 useDefaultPasswordFormat
= CFSTR("false");
689 allowedCharacters
= defaultCharacters
;
691 // In default password format, we use dashes only as separators, not as symbols you can encounter at a random position.
692 if (useDefaultPasswordFormat
== CFSTR("false")){
693 CFMutableStringRef mutatedAllowedCharacters
= CFStringCreateMutableCopy(kCFAllocatorDefault
, CFStringGetLength(allowedCharacters
), allowedCharacters
);
694 CFStringFindAndReplace (mutatedAllowedCharacters
, CFSTR("-"), CFSTR(""), CFRangeMake(0, CFStringGetLength(allowedCharacters
)),kCFCompareCaseInsensitive
);
695 allowedCharacters
= CFStringCreateCopy(kCFAllocatorDefault
, mutatedAllowedCharacters
);
698 if (requiredCharactersArray
) {
699 for (CFIndex i
= 0; i
< CFArrayGetCount(requiredCharactersArray
); i
++){
700 CFCharacterSetRef stringWithRequiredCharacters
= CFArrayGetValueAtIndex(requiredCharactersArray
, i
);
701 if( CFStringFindCharacterFromSet(allowedCharacters
, stringWithRequiredCharacters
, CFRangeMake(0, CFStringGetLength(allowedCharacters
)), 0, NULL
)){
702 CFArrayAppendValue(requiredCharacterSets
, stringWithRequiredCharacters
);
706 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
707 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
708 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
709 CFArrayAppendValue(requiredCharacterSets
, uppercaseLetterCharacterSet
);
710 CFArrayAppendValue(requiredCharacterSets
, lowercaseLetterCharacterSet
);
711 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
715 if (CFArrayGetCount(requiredCharacterSets
) > numberOfRequiredRandomCharacters
) {
716 CFReleaseNull(requiredCharacterSets
);
717 requiredCharacterSets
= NULL
;
719 //create new CFDictionary
720 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, numberOfRequiredRandomCharacters
);
721 CFMutableDictionaryRef updatedConstraints
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
722 CFDictionaryAddValue(updatedConstraints
, kSecUseDefaultPasswordFormatKey
, useDefaultPasswordFormat
);
723 CFDictionarySetValue(updatedConstraints
, kSecNumberOfRequiredRandomCharactersKey
, numReqChars
);
724 CFDictionaryAddValue(updatedConstraints
, kSecAllowedCharactersKey
, allowedCharacters
);
725 if(requiredCharacterSets
)
726 CFDictionaryAddValue(updatedConstraints
, kSecRequiredCharacterSetsKey
, requiredCharacterSets
);
728 //add the prohibited characters string if it exists to the new dictionary
729 if(prohibitedCharacters
)
730 CFDictionaryAddValue(updatedConstraints
, kSecPasswordDisallowedCharacters
, prohibitedCharacters
);
732 //add the characters the password can't end with if it exists to the new dictionary
734 CFDictionaryAddValue(updatedConstraints
, kSecPasswordCantEndWithChars
, endWith
);
736 //add the characters the password can't start with if it exists to the new dictionary
738 CFDictionaryAddValue(updatedConstraints
, kSecPasswordCantStartWithChars
, startWith
);
741 CFDictionaryAddValue(updatedConstraints
, kSecPasswordGroupSize
, groupSizeRef
);
743 if(numberOfGroupsRef
)
744 CFDictionaryAddValue(updatedConstraints
, kSecPasswordNumberOfGroups
, numberOfGroupsRef
);
747 CFDictionaryAddValue(updatedConstraints
, kSecPasswordSeparator
, separatorRef
);
749 if(atMostCharactersRef
)
750 CFDictionaryAddValue(updatedConstraints
, kSecPasswordContainsNoMoreThanNSpecificCharacters
, atMostCharactersRef
);
752 if(atLeastCharactersRef
)
753 CFDictionaryAddValue(updatedConstraints
, kSecPasswordContainsAtLeastNSpecificCharacters
, atLeastCharactersRef
);
756 CFDictionaryAddValue(updatedConstraints
, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
, identicalRef
);
758 CFReleaseNull(useDefaultPasswordFormat
);
759 CFReleaseNull(numReqChars
);
760 CFReleaseNull(allowedCharacters
);
761 CFReleaseNull(requiredCharacterSets
);
763 *returned
= CFDictionaryCreateCopy(kCFAllocatorDefault
, updatedConstraints
);
766 static bool isDictionaryFormattedProperly(SecPasswordType type
, CFDictionaryRef passwordRequirements
, CFErrorRef
*error
){
768 CFTypeRef defaults
= NULL
;
769 CFErrorRef tempError
= NULL
;
770 if(passwordRequirements
== NULL
){
774 if( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordDefaultForType
, &defaults
) ){
775 if(isString(defaults
) == true && 0 == CFStringCompare(defaults
, CFSTR("true"), 0)){
779 //only need to check max and min pin length formatting
780 if(type
== kSecPasswordTypePIN
){
781 CFTypeRef minTest
= NULL
, maxTest
= NULL
;
783 CFIndex minPasswordLength
= 0, maxPasswordLength
= 0;
785 if( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordDefaultForType
, &defaults
) ){
786 if(isString(defaults
) == true && 0 == CFStringCompare(defaults
, CFSTR("true"), 0)){
790 //check if the values exist!
791 if( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordMaxLengthKey
, &maxTest
) ){
792 require_action_quiet(isNull(maxTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a max length"), (CFIndex
)errSecBadReq
, NULL
));
793 require_action_quiet(isNumber(maxTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex
)errSecBadReq
, NULL
));
796 if (CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordMinLengthKey
, &minTest
) ){
797 require_action_quiet(isNull(minTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a min length"), (CFIndex
)errSecBadReq
, NULL
));
798 require_action_quiet(isNumber(minTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex
)errSecBadReq
, NULL
));
800 //check if the values exist!
802 CFNumberRef max
= (CFNumberRef
)maxTest
;
803 CFNumberGetValue(max
, kCFNumberSInt64Type
, &valuePtr
);
804 maxPasswordLength
= (long)valuePtr
;
807 CFNumberRef min
= (CFNumberRef
)minTest
;
808 CFNumberGetValue(min
, kCFNumberSInt64Type
, &valuePtr
);
809 minPasswordLength
= (long)valuePtr
;
811 //make sure min and max make sense respective to each other and that they aren't less than 4 digits.
812 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
));
813 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
));
816 CFTypeRef allowedTest
, maxTest
, minTest
, requiredTest
, prohibitedCharacters
, endWith
, startWith
,
817 groupSizeRef
, numberOfGroupsRef
, separatorRef
, atMostCharactersRef
,
818 atLeastCharactersRef
, thresholdRef
, identicalRef
, characters
;
821 //check if the values exist!
822 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
));
823 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordMaxLengthKey
, &maxTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a max length"), (CFIndex
)errSecBadReq
, NULL
));
824 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordMinLengthKey
, &minTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a min length"), (CFIndex
)errSecBadReq
, NULL
));
825 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
));
827 //check if values are null?
828 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
));
829 require_action_quiet(isNull(maxTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a max length"), (CFIndex
)errSecBadReq
, NULL
));
830 require_action_quiet(isNull(minTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a min length"), (CFIndex
)errSecBadReq
, NULL
));
831 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
));
833 //check if the values are correct
834 require_action_quiet(isString(allowedTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's allowed characters must be a CFStringRef"), (CFIndex
)errSecBadReq
, NULL
));
835 require_action_quiet(isNumber(maxTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex
)errSecBadReq
, NULL
));
836 require_action_quiet(isNumber(minTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex
)errSecBadReq
, NULL
));
837 require_action_quiet(isArray(requiredTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's required characters must be an array of CFCharacterSetRefs"), (CFIndex
)errSecBadReq
, NULL
));
839 CFNumberGetValue(minTest
, kCFNumberSInt64Type
, &valuePtr
);
840 CFIndex minPasswordLength
= (long)valuePtr
;
841 CFNumberGetValue(maxTest
, kCFNumberSInt64Type
, &valuePtr
);
842 CFIndex maxPasswordLength
= (long)valuePtr
;
844 require_action_quiet(minPasswordLength
<= maxPasswordLength
, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex
)errSecBadReq
, NULL
));
846 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
));
847 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
));
849 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordDisallowedCharacters
, &prohibitedCharacters
)){
850 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
));
851 require_action_quiet(isString(prohibitedCharacters
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("Disallowed Characters dictionary parameter is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
853 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordCantEndWithChars
, &endWith
)){
854 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
));
855 require_action_quiet(isString(endWith
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'EndWith' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
857 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordCantStartWithChars
, &startWith
)){
858 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
));
859 require_action_quiet(isString(startWith
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'StartWith' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
861 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordGroupSize
, &groupSizeRef
)){
862 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
));
863 require_action_quiet(isNumber(groupSizeRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'groupsize' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
865 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordNumberOfGroups
, &numberOfGroupsRef
)){
866 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
));
867 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
));
869 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordSeparator
, &separatorRef
)){
870 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
));
871 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
));
874 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordContainsNoMoreThanNSpecificCharacters
, &atMostCharactersRef
)){
875 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
));
876 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
));
878 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
));
879 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
));
880 require_action_quiet(isNumber(thresholdRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
882 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
));
883 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
));
884 require_action_quiet(isString(characters
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
887 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordContainsAtLeastNSpecificCharacters
, &atLeastCharactersRef
)){
888 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
));
889 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
));
891 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
));
893 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
));
894 require_action_quiet(isNumber(thresholdRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
896 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
));
897 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
));
898 require_action_quiet(isString(characters
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
901 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
, &identicalRef
)){
902 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
));
903 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
));
907 if (tempError
!= NULL
) {
913 CFReleaseNull(tempError
);
917 static bool doesFinalPasswordPass(CFStringRef password
, CFDictionaryRef requirements
){
919 CFTypeRef characters
, identicalRef
= NULL
, NRef
= NULL
, endWith
= NULL
, startWith
= NULL
, atLeastCharacters
= NULL
, atMostCharacters
= NULL
;
921 CFIndex N
, identicalCount
;
922 CFArrayRef requiredCharacterSet
= (CFArrayRef
)CFDictionaryGetValue(requirements
, kSecRequiredCharacterSetsKey
);
924 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordCantEndWithChars
, &endWith
))
927 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordCantStartWithChars
, &startWith
))
930 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsAtLeastNSpecificCharacters
, &atLeastCharacters
))
931 atLeastCharacters
= NULL
;
933 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsNoMoreThanNSpecificCharacters
, &atMostCharacters
))
934 atMostCharacters
= NULL
;
936 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
, &identicalRef
))
939 CFNumberGetValue((CFNumberRef
)identicalRef
, kCFNumberSInt64Type
, &valuePtr
);
940 identicalCount
= (long)valuePtr
;
944 if(!doesPasswordEndWith(password
, endWith
))
947 if(startWith
!= NULL
){
948 if(!doesPasswordStartWith(password
, startWith
))
951 if(atLeastCharacters
!= NULL
){
952 NRef
= CFDictionaryGetValue(atLeastCharacters
, kSecPasswordCharacterCount
);
953 characters
= CFDictionaryGetValue(atLeastCharacters
, kSecPasswordCharacters
);
954 CFNumberGetValue((CFNumberRef
)NRef
, kCFNumberSInt64Type
, &valuePtr
);
956 if(!passwordContainsAtLeastNCharacters(password
, characters
, N
))
959 if(atMostCharacters
!= NULL
){
960 NRef
= CFDictionaryGetValue(atMostCharacters
, kSecPasswordCharacterCount
);
961 characters
= CFDictionaryGetValue(atMostCharacters
, kSecPasswordCharacters
);
962 CFNumberGetValue((CFNumberRef
)NRef
, kCFNumberSInt64Type
, &valuePtr
);
964 if(!passwordContainsLessThanNCharacters(password
, characters
, N
))
967 if(identicalRef
!= NULL
){
968 if(!passwordContainsLessThanNIdenticalCharacters(password
, identicalCount
))
971 if (!passwordContainsRequiredCharacters(password
, requiredCharacterSet
))
974 if(true == SecPasswordIsPasswordWeak(password
))
980 //entry point into password generation
981 CF_RETURNS_RETAINED CFStringRef
SecPasswordGenerate(SecPasswordType type
, CFErrorRef
*error
, CFDictionaryRef passwordRequirements
){
983 CFTypeRef separator
= NULL
, defaults
= NULL
, groupSizeRef
= NULL
, numberOfGroupsRef
= NULL
;
984 CFDictionaryRef properlyFormattedRequirements
= NULL
;
986 uint64_t valuePtr
, groupSize
, numberOfGroups
;
987 CFNumberRef numberOfRequiredRandomCharacters
;
988 CFIndex requiredCharactersSize
;
989 CFStringRef randomCharacters
= NULL
, password
= NULL
, allowedChars
= NULL
;
990 CFMutableStringRef finalPassword
= NULL
;
992 check
= isDictionaryFormattedProperly(type
, passwordRequirements
, error
);
993 require_quiet(check
!= false, fail
);
995 //should we generate defaults?
996 if(passwordRequirements
== NULL
|| (CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordDefaultForType
, &defaults
) && isString(defaults
) == true && 0 == CFStringCompare(defaults
, CFSTR("true"), 0) ))
997 passwordGenerateDefaultParametersDictionary(&properlyFormattedRequirements
, type
, passwordRequirements
);
999 passwordGenerationParametersDictionary(&properlyFormattedRequirements
, type
, passwordRequirements
);
1001 CFRetain(properlyFormattedRequirements
);
1003 require_quiet(*error
== NULL
&& properlyFormattedRequirements
!= NULL
, fail
);
1005 numberOfRequiredRandomCharacters
= (CFNumberRef
)CFDictionaryGetValue(properlyFormattedRequirements
, kSecNumberOfRequiredRandomCharactersKey
);
1006 CFNumberGetValue(numberOfRequiredRandomCharacters
, kCFNumberSInt64Type
, &valuePtr
);
1007 requiredCharactersSize
= (long)valuePtr
;
1009 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements
, kSecPasswordGroupSize
, &groupSizeRef
)){
1010 groupSizeRef
= NULL
;
1013 CFNumberGetValue((CFNumberRef
)groupSizeRef
, kCFNumberSInt64Type
, &groupSize
);
1015 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements
, kSecPasswordNumberOfGroups
, &numberOfGroupsRef
)){
1016 numberOfGroupsRef
= NULL
;
1019 CFNumberGetValue((CFNumberRef
)numberOfGroupsRef
, kCFNumberSInt64Type
, &numberOfGroups
);
1022 allowedChars
= CFDictionaryGetValue(properlyFormattedRequirements
, kSecAllowedCharactersKey
);
1023 getPasswordRandomCharacters(&randomCharacters
, properlyFormattedRequirements
, &requiredCharactersSize
, allowedChars
);
1025 if(numberOfGroupsRef
&& groupSizeRef
){
1026 finalPassword
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1028 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements
, kSecPasswordSeparator
, &separator
))
1031 if(separator
== NULL
)
1032 separator
= CFSTR("-");
1035 while( i
!= requiredCharactersSize
){
1036 if((i
+ (CFIndex
)groupSize
) < requiredCharactersSize
){
1037 CFStringAppend(finalPassword
, CFStringCreateWithSubstring(kCFAllocatorDefault
, randomCharacters
, CFRangeMake(i
, (CFIndex
)groupSize
)));
1038 CFStringAppend(finalPassword
, separator
);
1041 else if((i
+(CFIndex
)groupSize
) == requiredCharactersSize
){
1042 CFStringAppend(finalPassword
, CFStringCreateWithSubstring(kCFAllocatorDefault
, randomCharacters
, CFRangeMake(i
, (CFIndex
)groupSize
)));
1046 CFStringAppend(finalPassword
, CFStringCreateWithSubstring(kCFAllocatorDefault
, randomCharacters
, CFRangeMake(i
, requiredCharactersSize
- i
)));
1047 i
+=(requiredCharactersSize
- i
);
1050 password
= CFStringCreateCopy(kCFAllocatorDefault
, finalPassword
);
1051 CFReleaseNull(finalPassword
);
1053 //no fancy formatting
1055 password
= CFStringCreateCopy(kCFAllocatorDefault
, randomCharacters
);
1058 CFReleaseNull(randomCharacters
);
1059 require_quiet(doesFinalPasswordPass(password
, properlyFormattedRequirements
), no_pass
);
1063 CFReleaseNull(password
);
1067 CFReleaseNull(properlyFormattedRequirements
);
1071 const char *in_word_set (const char *str
, unsigned int len
){
1072 static const char * wordlist
[] =
1074 "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
1075 "", "", "0103", "", "", "", "", "0123", "", "", "", "", "0303", "", "", "",
1076 "", "", "", "", "0110", "", "1103", "", "", "", "", "1123", "", "", "0000",
1077 "", "1203", "", "0404", "", "", "", "", "1234", "1110", "2015", "2013", "",
1078 "2014", "1010", "2005", "2003", "", "2004", "1210", "0505", "0111", "", "",
1079 "", "2008", "0101", "", "2007", "", "", "", "", "2006", "2010", "1995", "1993",
1080 "", "1994", "2000", "", "1111", "", "", "", "1998", "1101", "", "1997", "",
1081 "0808", "1211", "", "1996", "0102", "", "1201", "", "", "1990", "", "", "",
1082 "", "0202", "", "2011", "", "", "1112", "1958", "2001", "", "1957", "1102",
1083 "", "3333", "", "1956", "1212", "1985", "1983", "", "1984", "1202", "", "0909",
1084 "", "0606", "", "1988", "1991", "", "1987", "2012", "", "", "", "1986", "2002",
1085 "", "", "", "0707", "1980", "", "2009", "", "", "2222", "1965", "1963", "",
1086 "1964", "", "", "2229", "", "", "1992", "1968", "", "", "1967", "", "", "1999",
1087 "", "1966", "", "1975", "1973", "", "1974", "1960", "", "1981", "", "4444",
1088 "", "1978", "", "7465", "1977", "", "", "", "", "1976", "2580", "", "1959",
1089 "", "", "1970", "", "", "", "", "", "", "", "", "", "1982", "", "1961", "",
1090 "", "5252", "", "1989", "", "", "", "", "", "", "", "", "", "", "", "", "",
1091 "", "1971", "", "", "", "", "", "", "", "1962", "", "5683", "", "6666", "",
1092 "", "1969", "", "", "", "", "", "", "", "", "", "", "", "", "1972", "", "",
1093 "", "", "", "", "1979", "", "", "", "7667"
1096 if (len
<= MAX_WORD_LENGTH
&& len
>= MIN_WORD_LENGTH
)
1098 register int key
= pinhash (str
, len
);
1100 if (key
<= MAX_HASH_VALUE
&& key
>= 0)
1102 register const char *s
= wordlist
[key
];
1103 if (*str
== *s
&& !strcmp (str
+ 1, s
+ 1))
1109 CFDictionaryRef
SecPasswordCopyDefaultPasswordLength(SecPasswordType type
, CFErrorRef
*error
){
1111 CFIndex tupleLengthInt
, numOfTuplesInt
;
1112 CFNumberRef tupleLength
= NULL
;
1113 CFNumberRef numOfTuples
= NULL
;
1115 CFMutableDictionaryRef passwordLengthDefaults
= NULL
;
1118 case(kSecPasswordTypeiCloudRecovery
):
1121 tupleLength
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &tupleLengthInt
);
1122 numOfTuples
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &numOfTuplesInt
);
1123 passwordLengthDefaults
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, 0, 0);
1124 CFDictionaryAddValue(passwordLengthDefaults
, kSecPasswordGroupSize
, tupleLength
);
1125 CFDictionaryAddValue(passwordLengthDefaults
, kSecPasswordNumberOfGroups
, numOfTuples
);
1126 return CFDictionaryCreateCopy(kCFAllocatorDefault
, passwordLengthDefaults
);
1128 case(kSecPasswordTypePIN
):
1131 tupleLength
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &tupleLengthInt
);
1132 numOfTuples
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &numOfTuplesInt
);
1133 passwordLengthDefaults
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, 0, 0);
1134 CFDictionaryAddValue(passwordLengthDefaults
, kSecPasswordGroupSize
, tupleLength
);
1135 CFDictionaryAddValue(passwordLengthDefaults
, kSecPasswordNumberOfGroups
, numOfTuples
);
1136 return CFDictionaryCreateCopy(kCFAllocatorDefault
, passwordLengthDefaults
);
1138 case(kSecPasswordTypeSafari
):
1141 tupleLength
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &tupleLengthInt
);
1142 numOfTuples
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &numOfTuplesInt
);
1143 passwordLengthDefaults
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, 0, 0);
1144 CFDictionaryAddValue(passwordLengthDefaults
, kSecPasswordGroupSize
, tupleLength
);
1145 CFDictionaryAddValue(passwordLengthDefaults
, kSecPasswordNumberOfGroups
, numOfTuples
);
1146 return CFDictionaryCreateCopy(kCFAllocatorDefault
, passwordLengthDefaults
);
1149 case(kSecPasswordTypeWifi
):
1152 tupleLength
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &tupleLengthInt
);
1153 numOfTuples
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &numOfTuplesInt
);
1154 passwordLengthDefaults
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, 0, 0);
1155 CFDictionaryAddValue(passwordLengthDefaults
, kSecPasswordGroupSize
, tupleLength
);
1156 CFDictionaryAddValue(passwordLengthDefaults
, kSecPasswordNumberOfGroups
, numOfTuples
);
1157 return CFDictionaryCreateCopy(kCFAllocatorDefault
, passwordLengthDefaults
);
1161 if(SecError(errSecBadReq
, error
, CFSTR("Password type does not exist.")) == false)
1163 secdebug("secpasswordcopydefaultpasswordlength", "could not create error!");