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>
40 #include <corecrypto/ccdigest.h>
41 #include <corecrypto/ccsha2.h>
42 #include "SecCFAllocator.h"
45 // Keys for external dictionaries with password generation requirements we read from plist.
46 CFStringRef kSecPasswordMinLengthKey
= CFSTR("PasswordMinLength");
47 CFStringRef kSecPasswordMaxLengthKey
= CFSTR("PasswordMaxLength");
48 CFStringRef kSecPasswordAllowedCharactersKey
= CFSTR("PasswordAllowedCharacters");
49 CFStringRef kSecPasswordRequiredCharactersKey
= CFSTR("PasswordRequiredCharacters");
50 CFStringRef kSecPasswordDefaultForType
= CFSTR("PasswordDefaultForType");
52 CFStringRef kSecPasswordDisallowedCharacters
= CFSTR("PasswordDisallowedCharacters");
53 CFStringRef kSecPasswordCantStartWithChars
= CFSTR("PasswordCantStartWithChars");
54 CFStringRef kSecPasswordCantEndWithChars
= CFSTR("PasswordCantEndWithChars");
55 CFStringRef kSecPasswordContainsNoMoreThanNSpecificCharacters
= CFSTR("PasswordContainsNoMoreThanNSpecificCharacters");
56 CFStringRef kSecPasswordContainsAtLeastNSpecificCharacters
= CFSTR("PasswordContainsAtLeastNSpecificCharacters");
57 CFStringRef kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
= CFSTR("PasswordContainsNoMoreThanNConsecutiveIdenticalCharacters");
58 CFStringRef kSecPasswordCharacterCount
= CFSTR("PasswordCharacterCount");
59 CFStringRef kSecPasswordCharacters
= CFSTR("PasswordCharacters");
61 CFStringRef kSecPasswordGroupSize
= CFSTR("PasswordGroupSize");
62 CFStringRef kSecPasswordNumberOfGroups
= CFSTR("PasswordNumberOfGroups");
63 CFStringRef kSecPasswordSeparator
= CFSTR("SecPasswordSeparator");
65 // Keys for internally used dictionaries with password generation parameters (never exposed to external API).
66 static CFStringRef kSecUseDefaultPasswordFormatKey
= CFSTR("UseDefaultPasswordFormat");
67 static CFStringRef kSecNumberOfRequiredRandomCharactersKey
= CFSTR("NumberOfRequiredRandomCharacters");
68 static CFStringRef kSecNumberOfChecksumCharactersKey
= CFSTR("NumberOfChecksumCharacters");
69 static CFStringRef kSecAllowedCharactersKey
= CFSTR("AllowedCharacters");
70 static CFStringRef kSecRequiredCharacterSetsKey
= CFSTR("RequiredCharacterSets");
72 static CFIndex defaultNumberOfRandomCharacters
= 20;
73 static CFIndex defaultPINLength
= 4;
74 static CFIndex defaultiCloudPasswordLength
= 24;
75 static CFIndex defaultWifiPasswordLength
= 12;
77 static CFStringRef defaultWifiCharacters
= CFSTR("abcdefghijklmnopqrstuvwxyz1234567890");
78 static CFStringRef defaultPINCharacters
= CFSTR("0123456789");
79 static CFStringRef defaultiCloudCharacters
= CFSTR("ABCDEFGHJKLMNPQRSTUVWXYZ23456789");
80 static CFStringRef defaultCharacters
= CFSTR("abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789");
82 static CFCharacterSetRef uppercaseLetterCharacterSet
;
83 static CFCharacterSetRef lowercaseLetterCharacterSet
;
84 static CFCharacterSetRef decimalDigitCharacterSet
;
85 static CFCharacterSetRef punctuationCharacterSet
;
87 static CFIndex alphabetSetSize
= 26;
88 static CFIndex decimalSetSize
= 10;
89 static CFIndex punctuationSetSize
= 33;
90 static double entropyStrengthThreshold
= 35.0;
93 generated with ruby badpins.rb | gperf
94 See this for PIN list:
95 A birthday present every eleven wallets? The security of customer-chosen banking PINs (2012), by Joseph Bonneau , Sören Preibusch , Ross Anderson
97 const char *in_word_set (const char *str
, unsigned int len
);
99 #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
100 && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
101 && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
102 && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
103 && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
104 && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
105 && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
106 && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
107 && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
108 && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
109 && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
110 && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
111 && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
112 && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
113 && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
114 && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
115 && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
116 && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
117 && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
118 && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
119 && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
120 && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
121 && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
122 /* The character set is not based on ISO-646. */
123 error
"gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
127 #define TOTAL_KEYWORDS 100
128 #define MIN_WORD_LENGTH 4
129 #define MAX_WORD_LENGTH 4
130 #define MIN_HASH_VALUE 21
131 #define MAX_HASH_VALUE 275
132 /* maximum key range = 255, duplicates = 0 */
141 static unsigned int pinhash (const char *str
, unsigned int len
)
143 static unsigned short asso_values
[] =
145 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
146 276, 276, 276, 276, 276, 276, 276, 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, 5, 0,
150 10, 10, 30, 50, 100, 120, 70, 25, 57, 85,
151 2, 4, 1, 19, 14, 11, 92, 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, 276, 276, 276, 276, 276,
167 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
168 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
169 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
170 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
171 276, 276, 276, 276, 276
173 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];
176 ///<://problem/29089896> warn against using these common PINs
177 static bool isTopTenSixDigitPasscode(CFStringRef passcode
){
179 CFMutableArrayRef topTen
= CFArrayCreateMutableForCFTypesWith(kCFAllocatorDefault
,
191 CFSTR("789456"), NULL
);
193 for(CFIndex i
= 0; i
< CFArrayGetCount(topTen
); i
++){
194 if(CFEqualSafe(passcode
, CFArrayGetValueAtIndex(topTen
, i
))){
199 CFReleaseNull(topTen
);
203 CFStringRef
SecPasswordCreateWithRandomDigits(int n
, CFErrorRef
*error
){
207 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
208 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
209 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
210 punctuationCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetPunctuation
);
212 CFNumberRef minRef
= CFNumberCreate(NULL
, kCFNumberIntType
, &min
);
213 CFNumberRef maxRef
= CFNumberCreate(NULL
, kCFNumberIntType
, &max
);
215 CFMutableDictionaryRef passwordRequirements
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
216 CFDictionaryAddValue(passwordRequirements
, kSecPasswordMinLengthKey
, minRef
);
217 CFDictionaryAddValue(passwordRequirements
, kSecPasswordMaxLengthKey
, maxRef
);
218 CFStringRef allowedCharacters
= CFSTR("0123456789");
220 CFDictionaryAddValue(passwordRequirements
, kSecPasswordAllowedCharactersKey
, allowedCharacters
);
222 CFStringRef password
= SecPasswordGenerate(kSecPasswordTypePIN
, error
, passwordRequirements
);
224 CFReleaseNull(minRef
);
225 CFReleaseNull(maxRef
);
226 CFReleaseNull(passwordRequirements
);
233 //pins that reached the top 20 list
234 static const char *blacklist
[] = {"1234", "1004", "2000", "1122", "4321", "2001", "2580"};
235 bool SecPasswordIsPasswordWeak(CFStringRef passcode
)
237 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
238 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
239 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
240 punctuationCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetPunctuation
);
242 bool isNumber
= true;
246 if( CFStringGetLength(passcode
) < 4 ){
247 return true; //weak password
249 //check to see if passcode is a number
250 for(CFIndex i
= 0; i
< CFStringGetLength(passcode
); i
++){
251 if( CFStringFindCharacterFromSet(passcode
, decimalDigitCharacterSet
, CFRangeMake(i
,1), 0, NULL
))
258 //checking to see if it's a 4 digit pin
259 if(isNumber
&& CFStringGetLength(passcode
) == 4){
261 pin
= CFStringToCString(passcode
);
262 if(in_word_set(pin
, 4)){
267 CFIndex blacklistLength
= (CFIndex
)sizeof(blacklist
)/sizeof(blacklist
[0]);
269 //not all the same number
270 if(pin
[0] == pin
[1] == pin
[2] == pin
[3]){
272 return true; //weak password
274 //first two digits being the same and the last two digits being the same
275 if ( pin
[0] == pin
[1] && pin
[2] == pin
[3]){
277 return true; //weak password
279 //first two digits not being the same as the last two digits
280 if(pin
[0] == pin
[2] && pin
[1] == pin
[3]){
282 return true; //weak password
284 //check if PIN is a bunch of incrementing numbers
285 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
286 if(i
== CFStringGetLength(passcode
)-1){
290 else if ((pin
[i
] + 1) == pin
[i
+1])
295 //check if PIN is a bunch of decrementing numbers
296 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
297 if(i
== CFStringGetLength(passcode
)-1){
301 else if ((pin
[i
]) == (pin
[i
+1] +1))
303 else if ((i
== 0) && (pin
[i
] == '0') && (pin
[i
+1] == '9'))
310 for(CFIndex i
= 0; i
< blacklistLength
; i
++)
312 const char* blackCode
= blacklist
[i
];
313 if(0 == strcmp(blackCode
, pin
))
316 return true; //weak password
320 else if(isNumber
){ //dealing with a numeric PIN
321 pin
= CFStringToCString(passcode
);
322 //check if PIN is all the same number
323 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
324 if(i
+1 >= CFStringGetLength(passcode
)){
328 else if (pin
[i
] == pin
[i
+1])
333 //check if PIN is a bunch of incrementing numbers
334 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
335 if(i
== CFStringGetLength(passcode
)-1){
339 else if ((pin
[i
] + 1) == pin
[i
+1])
344 //check if PIN is a bunch of decrementing numbers
345 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
346 if(i
== CFStringGetLength(passcode
)-1){
350 else if ((pin
[i
]) == (pin
[i
+1] +1))
352 else if ((i
== 0) && (pin
[i
] == '0') && (pin
[i
+1] == '9'))
358 else{ // password is complex, evaluate entropy
363 int characterSet
= 0;
365 //calculate new entropy
366 for(CFIndex i
= 0; i
< CFStringGetLength(passcode
); i
++){
368 if( CFStringFindCharacterFromSet(passcode
, uppercaseLetterCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
372 if( CFStringFindCharacterFromSet(passcode
, lowercaseLetterCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
376 if( CFStringFindCharacterFromSet(passcode
, decimalDigitCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
380 if( CFStringFindCharacterFromSet(passcode
, punctuationCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
387 characterSet
+= alphabetSetSize
;
390 characterSet
+= alphabetSetSize
;
393 characterSet
+= decimalSetSize
;
396 characterSet
+= punctuationSetSize
;
399 double strength
= CFStringGetLength(passcode
)*log2(characterSet
);
401 if(strength
< entropyStrengthThreshold
){
405 return false; //strong
410 return false; //strong password
414 static bool SecPasswordIsPasscodeIncrementingOrDecrementingDigits(CFStringRef passcode
)
416 char* pin
= CFStringToCString(passcode
);
418 //check if PIN is a bunch of incrementing numbers
419 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
420 if(i
== CFStringGetLength(passcode
)-1){
424 else if ((pin
[i
] + 1) == pin
[i
+1])
429 //check if PIN is a bunch of decrementing numbers
430 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
431 if(i
== CFStringGetLength(passcode
)-1){
435 else if ((pin
[i
]) == (pin
[i
+1] +1))
437 else if ((i
== 0) && (pin
[i
] == '0') && (pin
[i
+1] == '9'))
446 static bool SecPasswordIsPasswordRepeatingTwoNumbers(CFStringRef passcode
){
447 char* pin
= CFStringToCString(passcode
);
449 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++)
451 if(i
+2 == CFStringGetLength(passcode
)-1){
455 else if(pin
[i
] == pin
[i
+2])
465 static int SecPasswordNumberOfRepeatedDigits(CFStringRef passcode
){
467 CFIndex length
= CFStringGetLength(passcode
);
468 CFNumberRef highest
= NULL
;
469 CFMutableArrayRef highestRepeatingcount
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
471 for(int i
= 0; i
< length
; i
++){
474 CFNumberRef newRepeatingAddition
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &repeating
);
475 CFArrayAppendValue(highestRepeatingcount
, newRepeatingAddition
);
476 CFReleaseNull(newRepeatingAddition
);
479 if(CFStringGetCharacterAtIndex(passcode
, i
) == CFStringGetCharacterAtIndex(passcode
,i
+1))
484 CFNumberRef newRepeatingAddition
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &repeating
);
485 CFArrayAppendValue(highestRepeatingcount
, newRepeatingAddition
);
486 CFReleaseNull(newRepeatingAddition
);
493 for(int i
=0; i
< CFArrayGetCount(highestRepeatingcount
); i
++){
495 highest
= CFArrayGetValueAtIndex(highestRepeatingcount
, i
);
499 CFNumberRef competitor
= CFArrayGetValueAtIndex(highestRepeatingcount
, i
);
500 if(CFNumberCompare(competitor
, highest
, NULL
) == kCFCompareGreaterThan
){
501 highest
= competitor
;
505 int finalRepeating
= 0;
507 CFNumberGetValue(highest
, kCFNumberIntType
, &finalRepeating
);
509 CFReleaseNull(highestRepeatingcount
);
510 return finalRepeating
;
513 static bool SecPasswordIsPalindrome(CFStringRef passcode
){
514 char* pin
= CFStringToCString(passcode
);
515 long length
= CFStringGetLength(passcode
);
518 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++)
520 if(length%2
== 1 && i
== j
){
524 else if(length%2
== 0 && i
== j
-1){
525 if(pin
[i
] == pin
[j
]){
532 else if(pin
[i
] == pin
[j
]){
543 static bool SecPasswordHasRepeatingGroups(CFStringRef passcode
){
544 char* pin
= CFStringToCString(passcode
);
546 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++)
548 if(i
+4 == CFStringGetLength(passcode
)){
549 if(pin
[i
] == pin
[i
+3]){
556 else if(pin
[i
] == pin
[i
+3])
567 bool SecPasswordIsPasswordWeak2(bool isSimple
, CFStringRef passcode
)
569 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
570 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
571 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
572 punctuationCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetPunctuation
);
578 if( CFStringGetLength(passcode
) < 4 ){
579 return true; //weak password
582 bool isPasscodeNumber
= true;
583 //check to see if passcode is a number
584 for(CFIndex i
= 0; i
< CFStringGetLength(passcode
); i
++){
585 if( CFStringFindCharacterFromSet(passcode
, decimalDigitCharacterSet
, CFRangeMake(i
,1), 0, NULL
))
588 isPasscodeNumber
= false;
594 //checking to see if it's a 4 digit pin
595 if(isPasscodeNumber
&& CFStringGetLength(passcode
) == 4){
597 pin
= CFStringToCString(passcode
);
598 if(in_word_set(pin
, 4)){
603 CFIndex blacklistLength
= (CFIndex
)sizeof(blacklist
)/sizeof(blacklist
[0]);
605 //not all the same number
606 if(pin
[0] == pin
[1] == pin
[2] == pin
[3]){
608 return true; //weak password
610 //first two digits being the same and the last two digits being the same
611 if ( pin
[0] == pin
[1] && pin
[2] == pin
[3]){
613 return true; //weak password
615 //first two digits not being the same as the last two digits
616 if(pin
[0] == pin
[2] && pin
[1] == pin
[3]){
618 return true; //weak password
621 for(CFIndex i
= 0; i
< blacklistLength
; i
++)
623 const char* blackCode
= blacklist
[i
];
624 if(0 == strcmp(blackCode
, pin
))
627 return true; //weak password
631 else if(isPasscodeNumber
&& CFStringGetLength(passcode
) == 6){
632 pin
= CFStringToCString(passcode
);
634 //not all the same number
635 for(int i
= 0; i
< CFStringGetLength(passcode
); i
++){
636 if(i
== CFStringGetLength(passcode
)-1){
640 else if ((pin
[i
]) == pin
[i
+1])
646 if(isTopTenSixDigitPasscode(passcode
)){
651 if(SecPasswordIsPalindrome(passcode
)){
657 if(SecPasswordHasRepeatingGroups(passcode
)){
661 //passcode is incrementing ex 123456 or 654321
662 if(SecPasswordIsPasscodeIncrementingOrDecrementingDigits(passcode
)) {
666 //passcode does not consist of 2 repeating digits
667 if(SecPasswordIsPasswordRepeatingTwoNumbers(passcode
)){
672 else//should be a 4 or 6digit number
675 else if(isPasscodeNumber
&& !isSimple
){ //dealing with a complex numeric passcode
676 pin
= CFStringToCString(passcode
);
677 //check if PIN is all the same number
678 int repeatingDigits
= SecPasswordNumberOfRepeatedDigits(passcode
);
679 if(repeatingDigits
>= (CFStringGetLength(passcode
)/2)){
684 if(SecPasswordIsPalindrome(passcode
)){
689 if(isTopTenSixDigitPasscode(passcode
)){
694 if(SecPasswordHasRepeatingGroups(passcode
) && CFStringGetLength(passcode
) >= 6){
699 if(SecPasswordIsPasscodeIncrementingOrDecrementingDigits(passcode
)) {
703 if(SecPasswordIsPasswordRepeatingTwoNumbers(passcode
)){
709 else{ // password is complex, evaluate entropy
714 int characterSet
= 0;
716 //calculate new entropy
717 for(CFIndex i
= 0; i
< CFStringGetLength(passcode
); i
++){
719 if( CFStringFindCharacterFromSet(passcode
, uppercaseLetterCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
723 if( CFStringFindCharacterFromSet(passcode
, lowercaseLetterCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
727 if( CFStringFindCharacterFromSet(passcode
, decimalDigitCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
731 if( CFStringFindCharacterFromSet(passcode
, punctuationCharacterSet
, CFRangeMake(i
,1), kCFCompareBackwards
, NULL
)){
738 characterSet
+= alphabetSetSize
;
741 characterSet
+= alphabetSetSize
;
744 characterSet
+= decimalSetSize
;
747 characterSet
+= punctuationSetSize
;
750 double strength
= CFStringGetLength(passcode
)*log2(characterSet
);
752 if(strength
< entropyStrengthThreshold
){
756 return false; //strong
761 return false; //strong password
765 static void getUniformRandomNumbers(uint8_t* buffer
, size_t numberOfDesiredNumbers
, uint8_t upperBound
)
768 // The values returned by SecRandomCopyBytes are uniformly distributed in the range [0, 255]. If we try to map
769 // these values onto a smaller range using modulo we will introduce a bias towards lower numbers in situations
770 // where our smaller range doesn’t evenly divide in to [0, 255]. For example, with the desired range of [0, 54]
771 // the ranges 0..54, 55..109, 110..164, and 165..219 are uniformly distributed, but the range 220..255 modulo 55
772 // is only distributed over [0, 35], giving significant bias to these lower values. So, we ignore random numbers
773 // that would introduce this bias.
774 uint8_t limitAvoidingModuloBias
= UCHAR_MAX
- (UCHAR_MAX
% upperBound
);
776 for (size_t numberOfAcceptedNumbers
= 0; numberOfAcceptedNumbers
< numberOfDesiredNumbers
; ) {
777 if (SecRandomCopyBytes(kSecRandomDefault
, numberOfDesiredNumbers
- numberOfAcceptedNumbers
, buffer
+ numberOfAcceptedNumbers
) == -1)
779 for (size_t i
= numberOfAcceptedNumbers
; i
< numberOfDesiredNumbers
; ++i
) {
780 if (buffer
[i
] < limitAvoidingModuloBias
)
781 buffer
[numberOfAcceptedNumbers
++] = buffer
[i
] % upperBound
;
786 static bool passwordContainsRequiredCharacters(CFStringRef password
, CFArrayRef requiredCharacterSets
)
788 CFCharacterSetRef characterSet
;
790 for (CFIndex i
= 0; i
< CFArrayGetCount(requiredCharacterSets
); i
++) {
791 characterSet
= CFArrayGetValueAtIndex(requiredCharacterSets
, i
);
792 CFRange rangeToSearch
= CFRangeMake(0, CFStringGetLength(password
));
793 require_quiet(CFStringFindCharacterFromSet(password
, characterSet
, rangeToSearch
, 0, NULL
), fail
);
802 static bool passwordContainsLessThanNIdenticalCharacters(CFStringRef password
, CFIndex identicalCount
)
804 unsigned char Char
, nextChar
;
807 for(CFIndex i
= 0; i
< CFStringGetLength(password
); i
++){
808 Char
= CFStringGetCharacterAtIndex(password
, i
);
809 for(CFIndex j
= i
; j
< CFStringGetLength(password
); j
++){
810 nextChar
= CFStringGetCharacterAtIndex(password
, j
);
811 require_quiet(repeating
<= identicalCount
, fail
);
812 if(Char
== nextChar
){
825 static bool passwordContainsAtLeastNCharacters(CFStringRef password
, CFStringRef characters
, CFIndex N
)
827 CFCharacterSetRef characterSet
= NULL
;
828 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, characters
);
831 for(CFIndex i
= 0; i
< CFStringGetLength(password
); i
++){
832 if(CFStringFindCharacterFromSet(password
, characterSet
, CFRangeMake(i
, 1), 0, NULL
))
835 CFReleaseNull(characterSet
);
842 static bool passwordContainsLessThanNCharacters(CFStringRef password
, CFStringRef characters
, CFIndex N
)
844 CFCharacterSetRef characterSet
= NULL
;
845 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, characters
);
848 for(CFIndex i
= 0; i
< CFStringGetLength(password
); i
++){
849 if(CFStringFindCharacterFromSet(password
, characterSet
, CFRangeMake(i
, 1), 0, NULL
))
852 CFReleaseNull(characterSet
);
859 static bool passwordDoesNotContainCharacters(CFStringRef password
, CFStringRef prohibitedCharacters
)
861 CFCharacterSetRef characterSet
= NULL
;
862 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, prohibitedCharacters
);
863 CFRange rangeToSearch
= CFRangeMake(0, CFStringGetLength(password
));
865 require_quiet(!CFStringFindCharacterFromSet(password
, characterSet
, rangeToSearch
, 0, NULL
), fail
);
866 CFReleaseNull(characterSet
);
869 CFReleaseNull(characterSet
);
873 static void getPasswordRandomCharacters(CFStringRef
*returned
, CFDictionaryRef requirements
, CFIndex
*numberOfRandomCharacters
, CFStringRef allowedCharacters
)
875 uint8_t randomNumbers
[*numberOfRandomCharacters
];
876 unsigned char randomCharacters
[*numberOfRandomCharacters
];
877 getUniformRandomNumbers(randomNumbers
, *numberOfRandomCharacters
, CFStringGetLength(allowedCharacters
));
879 CFTypeRef prohibitedCharacters
= NULL
;
880 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordDisallowedCharacters
, &prohibitedCharacters
))
881 prohibitedCharacters
= NULL
;
883 //it's faster for long characters to check each character produced for these cases
884 for (CFIndex i
= 0; i
< *numberOfRandomCharacters
; ++i
){
885 //check prohibited characters
886 UniChar randomChar
[1];
887 randomChar
[0] = CFStringGetCharacterAtIndex(allowedCharacters
, randomNumbers
[i
]);
888 if (prohibitedCharacters
!= NULL
)
890 CFStringRef temp
= CFStringCreateWithCharacters(kCFAllocatorDefault
, randomChar
, 1);
891 bool pwdncc
= passwordDoesNotContainCharacters(temp
, prohibitedCharacters
);
894 //change up the random numbers so we don't get the same index into allowed
895 getUniformRandomNumbers(randomNumbers
, *numberOfRandomCharacters
, CFStringGetLength(allowedCharacters
));
900 randomCharacters
[i
] = (unsigned char)randomChar
[0];
903 *returned
= CFStringCreateWithBytes(kCFAllocatorDefault
, randomCharacters
, *numberOfRandomCharacters
, kCFStringEncodingUTF8
, false);
906 static bool doesPasswordEndWith(CFStringRef password
, CFStringRef prohibitedCharacters
)
908 CFCharacterSetRef characterSet
= NULL
;
909 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, prohibitedCharacters
);
911 CFRange rangeToSearch
= CFRangeMake(CFStringGetLength(password
) - CFStringGetLength(prohibitedCharacters
), CFStringGetLength(prohibitedCharacters
));
912 require_quiet(0 == CFStringCompareWithOptions(password
, prohibitedCharacters
, rangeToSearch
, 0), fail
);
913 CFReleaseNull(characterSet
);
916 CFReleaseNull(characterSet
);
920 static bool doesPasswordStartWith(CFStringRef password
, CFStringRef prohibitedCharacters
)
922 CFCharacterSetRef characterSet
= NULL
;
923 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, prohibitedCharacters
);
925 CFRange rangeToSearch
= CFRangeMake(0, CFStringGetLength(prohibitedCharacters
));
926 require_quiet(0 == CFStringCompareWithOptions(password
, prohibitedCharacters
, rangeToSearch
, 0), fail
);
927 CFReleaseNull(characterSet
);
928 return false; //does not start with prohibitedCharacters
930 CFReleaseNull(characterSet
);
934 static CFDictionaryRef
passwordGenerateCreateDefaultParametersDictionary(SecPasswordType type
, CFDictionaryRef requirements
){
936 CFMutableArrayRef requiredCharacterSets
= NULL
;
937 CFNumberRef numReqChars
= NULL
, checksumChars
= NULL
;
938 CFStringRef defaultPasswordFormat
= NULL
;
939 requiredCharacterSets
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
940 defaultPasswordFormat
= CFSTR("true");
941 CFTypeRef groupSizeRef
= NULL
, numberOfGroupsRef
= NULL
;
942 CFIndex groupSize
, numberOfGroups
, checksumSize
= 0;
943 CFDictionaryRef returned
= NULL
;
946 case kSecPasswordTypeiCloudRecoveryKey
:
950 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, (groupSize
* numberOfGroups
) - checksumSize
);
951 checksumChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, checksumSize
);
952 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &groupSize
);
953 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &numberOfGroups
);
955 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
956 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
957 CFArrayAppendValue(requiredCharacterSets
, uppercaseLetterCharacterSet
);
958 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
959 returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
960 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
961 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
962 kSecNumberOfChecksumCharactersKey
, checksumChars
,
963 kSecAllowedCharactersKey
, defaultiCloudCharacters
,
964 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
965 kSecPasswordGroupSize
, groupSizeRef
,
966 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
969 case(kSecPasswordTypeiCloudRecovery
):
970 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, defaultiCloudPasswordLength
);
973 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &groupSize
);
974 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &numberOfGroups
);
976 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
977 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
978 CFArrayAppendValue(requiredCharacterSets
, uppercaseLetterCharacterSet
);
979 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
980 returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
981 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
982 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
983 kSecAllowedCharactersKey
, defaultiCloudCharacters
,
984 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
985 kSecPasswordGroupSize
, groupSizeRef
,
986 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
990 case(kSecPasswordTypePIN
):
991 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, defaultPINLength
);
994 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &groupSize
);
995 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &numberOfGroups
);
997 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
998 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
999 returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
1000 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
1001 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
1002 kSecAllowedCharactersKey
, defaultPINCharacters
,
1003 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
1004 kSecPasswordGroupSize
, groupSizeRef
,
1005 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
1009 case(kSecPasswordTypeWifi
):
1012 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &groupSize
);
1013 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &numberOfGroups
);
1015 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
1016 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
1018 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, defaultWifiPasswordLength
);
1019 CFArrayAppendValue(requiredCharacterSets
, lowercaseLetterCharacterSet
);
1020 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
1021 returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
1022 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
1023 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
1024 kSecAllowedCharactersKey
, defaultWifiCharacters
,
1025 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
1026 kSecPasswordGroupSize
, groupSizeRef
,
1027 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
1034 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &groupSize
);
1035 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &numberOfGroups
);
1036 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
1037 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
1038 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
1039 CFArrayAppendValue(requiredCharacterSets
, uppercaseLetterCharacterSet
);
1040 CFArrayAppendValue(requiredCharacterSets
, lowercaseLetterCharacterSet
);
1041 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
1043 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, defaultNumberOfRandomCharacters
);
1044 returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
1045 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
1046 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
1047 kSecAllowedCharactersKey
, defaultCharacters
,
1048 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
1049 kSecPasswordGroupSize
, groupSizeRef
,
1050 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
1058 CFReleaseNull(numReqChars
);
1059 CFReleaseNull(requiredCharacterSets
);
1060 CFReleaseNull(groupSizeRef
);
1061 CFReleaseNull(numberOfGroupsRef
);
1062 CFReleaseNull(checksumChars
);
1065 static CFDictionaryRef
passwordGenerationCreateParametersDictionary(SecPasswordType type
, CFDictionaryRef requirements
)
1067 CFMutableArrayRef requiredCharacterSets
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1068 CFNumberRef numReqChars
= NULL
;
1069 CFIndex numberOfRequiredRandomCharacters
;
1070 CFStringRef allowedCharacters
= NULL
, useDefaultPasswordFormat
= NULL
;
1072 CFTypeRef prohibitedCharacters
= NULL
, endWith
= NULL
, startWith
= NULL
,
1073 groupSizeRef
= NULL
, numberOfGroupsRef
= NULL
, separatorRef
= NULL
,
1074 atMostCharactersRef
= NULL
,atLeastCharactersRef
= NULL
, identicalRef
= NULL
;
1076 CFNumberRef min
= (CFNumberRef
)CFDictionaryGetValue(requirements
, kSecPasswordMinLengthKey
);
1077 CFNumberRef max
= (CFNumberRef
)CFDictionaryGetValue(requirements
, kSecPasswordMaxLengthKey
);
1079 CFNumberGetValue(min
, kCFNumberSInt64Type
, &valuePtr
);
1080 CFIndex minPasswordLength
= (long)valuePtr
;
1081 CFNumberGetValue(max
, kCFNumberSInt64Type
, &valuePtr
);
1082 CFIndex maxPasswordLength
= (long)valuePtr
;
1084 // If requirements allow, we will generate the password in default format.
1085 useDefaultPasswordFormat
= CFSTR("true");
1086 numberOfRequiredRandomCharacters
= defaultNumberOfRandomCharacters
;
1088 if(type
== kSecPasswordTypePIN
)
1090 if( maxPasswordLength
&& minPasswordLength
)
1091 numberOfRequiredRandomCharacters
= maxPasswordLength
;
1092 else if( !maxPasswordLength
&& minPasswordLength
)
1093 numberOfRequiredRandomCharacters
= minPasswordLength
;
1094 else if( !minPasswordLength
&& maxPasswordLength
)
1095 numberOfRequiredRandomCharacters
= maxPasswordLength
;
1097 numberOfRequiredRandomCharacters
= defaultPINLength
;
1099 allowedCharacters
= CFSTR("0123456789");
1100 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
1101 useDefaultPasswordFormat
= CFSTR("false");
1104 CFArrayRef requiredCharactersArray
= NULL
;
1106 if (minPasswordLength
&& minPasswordLength
> defaultNumberOfRandomCharacters
) {
1107 useDefaultPasswordFormat
= CFSTR("false");
1108 numberOfRequiredRandomCharacters
= minPasswordLength
;
1110 if (maxPasswordLength
&& maxPasswordLength
< defaultNumberOfRandomCharacters
) {
1111 useDefaultPasswordFormat
= CFSTR("false");
1112 numberOfRequiredRandomCharacters
= maxPasswordLength
;
1114 if (maxPasswordLength
&& minPasswordLength
&& maxPasswordLength
== minPasswordLength
&& maxPasswordLength
!= defaultNumberOfRandomCharacters
){
1115 useDefaultPasswordFormat
= CFSTR("false");
1116 numberOfRequiredRandomCharacters
= maxPasswordLength
;
1118 allowedCharacters
= (CFStringRef
)CFRetainSafe(CFDictionaryGetValue(requirements
, kSecPasswordAllowedCharactersKey
));
1119 requiredCharactersArray
= (CFArrayRef
)CFDictionaryGetValue(requirements
, kSecPasswordRequiredCharactersKey
);
1121 if (requiredCharactersArray
) {
1122 for (CFIndex i
= 0; i
< CFArrayGetCount(requiredCharactersArray
); i
++){
1123 CFCharacterSetRef stringWithRequiredCharacters
= CFArrayGetValueAtIndex(requiredCharactersArray
, i
);
1124 if(stringWithRequiredCharacters
&& CFStringFindCharacterFromSet(allowedCharacters
, stringWithRequiredCharacters
, CFRangeMake(0, CFStringGetLength(allowedCharacters
)), 0, NULL
)){
1125 CFArrayAppendValue(requiredCharacterSets
, stringWithRequiredCharacters
);
1129 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
1130 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
1131 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
1132 CFArrayAppendValue(requiredCharacterSets
, uppercaseLetterCharacterSet
);
1133 CFArrayAppendValue(requiredCharacterSets
, lowercaseLetterCharacterSet
);
1134 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
1138 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordDisallowedCharacters
, &prohibitedCharacters
))
1139 prohibitedCharacters
= NULL
;
1141 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordCantEndWithChars
, &endWith
))
1144 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordCantStartWithChars
, &startWith
))
1147 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordGroupSize
, &groupSizeRef
))
1148 groupSizeRef
= NULL
;
1150 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordNumberOfGroups
, &numberOfGroupsRef
))
1151 numberOfGroupsRef
= NULL
;
1153 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordSeparator
, &separatorRef
))
1154 separatorRef
= NULL
;
1156 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsNoMoreThanNSpecificCharacters
, &atMostCharactersRef
))
1157 atMostCharactersRef
= NULL
;
1159 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsAtLeastNSpecificCharacters
, &atLeastCharactersRef
))
1160 atLeastCharactersRef
= NULL
;
1162 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
, &identicalRef
))
1163 identicalRef
= NULL
;
1165 if (allowedCharacters
) {
1166 if( false == CFStringFindWithOptions(allowedCharacters
, CFSTR("-"), CFRangeMake(0, CFStringGetLength(allowedCharacters
)), kCFCompareCaseInsensitive
, NULL
))
1167 useDefaultPasswordFormat
= CFSTR("false");
1169 allowedCharacters
= CFRetainSafe(defaultCharacters
);
1171 // In default password format, we use dashes only as separators, not as symbols you can encounter at a random position.
1172 if (useDefaultPasswordFormat
== CFSTR("false")){
1173 CFMutableStringRef mutatedAllowedCharacters
= CFStringCreateMutableCopy(kCFAllocatorDefault
, CFStringGetLength(allowedCharacters
), allowedCharacters
);
1174 CFStringFindAndReplace (mutatedAllowedCharacters
, CFSTR("-"), CFSTR(""), CFRangeMake(0, CFStringGetLength(allowedCharacters
)),kCFCompareCaseInsensitive
);
1175 CFReleaseSafe(allowedCharacters
);
1176 allowedCharacters
= mutatedAllowedCharacters
;
1179 if (CFArrayGetCount(requiredCharacterSets
) > numberOfRequiredRandomCharacters
) {
1180 CFReleaseNull(requiredCharacterSets
);
1181 requiredCharacterSets
= NULL
;
1183 //create new CFDictionary
1184 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, numberOfRequiredRandomCharacters
);
1185 CFMutableDictionaryRef updatedConstraints
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1186 CFDictionaryAddValue(updatedConstraints
, kSecUseDefaultPasswordFormatKey
, useDefaultPasswordFormat
);
1187 CFDictionarySetValue(updatedConstraints
, kSecNumberOfRequiredRandomCharactersKey
, numReqChars
);
1188 CFDictionaryAddValue(updatedConstraints
, kSecAllowedCharactersKey
, allowedCharacters
);
1189 if(requiredCharacterSets
)
1190 CFDictionaryAddValue(updatedConstraints
, kSecRequiredCharacterSetsKey
, requiredCharacterSets
);
1192 //add the prohibited characters string if it exists to the new dictionary
1193 if(prohibitedCharacters
)
1194 CFDictionaryAddValue(updatedConstraints
, kSecPasswordDisallowedCharacters
, prohibitedCharacters
);
1196 //add the characters the password can't end with if it exists to the new dictionary
1198 CFDictionaryAddValue(updatedConstraints
, kSecPasswordCantEndWithChars
, endWith
);
1200 //add the characters the password can't start with if it exists to the new dictionary
1202 CFDictionaryAddValue(updatedConstraints
, kSecPasswordCantStartWithChars
, startWith
);
1205 CFDictionaryAddValue(updatedConstraints
, kSecPasswordGroupSize
, groupSizeRef
);
1207 if(numberOfGroupsRef
)
1208 CFDictionaryAddValue(updatedConstraints
, kSecPasswordNumberOfGroups
, numberOfGroupsRef
);
1211 CFDictionaryAddValue(updatedConstraints
, kSecPasswordSeparator
, separatorRef
);
1213 if(atMostCharactersRef
)
1214 CFDictionaryAddValue(updatedConstraints
, kSecPasswordContainsNoMoreThanNSpecificCharacters
, atMostCharactersRef
);
1216 if(atLeastCharactersRef
)
1217 CFDictionaryAddValue(updatedConstraints
, kSecPasswordContainsAtLeastNSpecificCharacters
, atLeastCharactersRef
);
1220 CFDictionaryAddValue(updatedConstraints
, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
, identicalRef
);
1222 CFReleaseNull(useDefaultPasswordFormat
);
1223 CFReleaseNull(numReqChars
);
1224 CFReleaseNull(allowedCharacters
);
1225 CFReleaseNull(requiredCharacterSets
);
1227 return updatedConstraints
;
1230 static bool isDictionaryFormattedProperly(SecPasswordType type
, CFDictionaryRef passwordRequirements
, CFErrorRef
*error
){
1232 CFTypeRef defaults
= NULL
;
1233 CFErrorRef tempError
= NULL
;
1234 if(passwordRequirements
== NULL
){
1238 if( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordDefaultForType
, &defaults
) ){
1239 if(isString(defaults
) == true && 0 == CFStringCompare(defaults
, CFSTR("true"), 0)){
1243 //only need to check max and min pin length formatting
1244 if(type
== kSecPasswordTypePIN
){
1245 CFTypeRef minTest
= NULL
, maxTest
= NULL
;
1247 CFIndex minPasswordLength
= 0, maxPasswordLength
= 0;
1249 if( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordDefaultForType
, &defaults
) ){
1250 if(isString(defaults
) == true && 0 == CFStringCompare(defaults
, CFSTR("true"), 0)){
1254 //check if the values exist!
1255 if( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordMaxLengthKey
, &maxTest
) ){
1256 require_action_quiet(isNull(maxTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a max length"), (CFIndex
)errSecBadReq
, NULL
));
1257 require_action_quiet(isNumber(maxTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex
)errSecBadReq
, NULL
));
1260 if (CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordMinLengthKey
, &minTest
) ){
1261 require_action_quiet(isNull(minTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a min length"), (CFIndex
)errSecBadReq
, NULL
));
1262 require_action_quiet(isNumber(minTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex
)errSecBadReq
, NULL
));
1264 //check if the values exist!
1266 CFNumberRef max
= (CFNumberRef
)maxTest
;
1267 CFNumberGetValue(max
, kCFNumberSInt64Type
, &valuePtr
);
1268 maxPasswordLength
= (long)valuePtr
;
1271 CFNumberRef min
= (CFNumberRef
)minTest
;
1272 CFNumberGetValue(min
, kCFNumberSInt64Type
, &valuePtr
);
1273 minPasswordLength
= (long)valuePtr
;
1275 //make sure min and max make sense respective to each other and that they aren't less than 4 digits.
1276 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
));
1277 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
));
1280 CFTypeRef allowedTest
, maxTest
, minTest
, requiredTest
, prohibitedCharacters
, endWith
, startWith
,
1281 groupSizeRef
, numberOfGroupsRef
, separatorRef
, atMostCharactersRef
,
1282 atLeastCharactersRef
, thresholdRef
, identicalRef
, characters
;
1285 //check if the values exist!
1286 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
));
1287 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordMaxLengthKey
, &maxTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a max length"), (CFIndex
)errSecBadReq
, NULL
));
1288 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordMinLengthKey
, &minTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a min length"), (CFIndex
)errSecBadReq
, NULL
));
1289 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
));
1291 //check if values are null?
1292 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
));
1293 require_action_quiet(isNull(maxTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a max length"), (CFIndex
)errSecBadReq
, NULL
));
1294 require_action_quiet(isNull(minTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a min length"), (CFIndex
)errSecBadReq
, NULL
));
1295 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
));
1297 //check if the values are correct
1298 require_action_quiet(isString(allowedTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's allowed characters must be a CFStringRef"), (CFIndex
)errSecBadReq
, NULL
));
1299 require_action_quiet(isNumber(maxTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex
)errSecBadReq
, NULL
));
1300 require_action_quiet(isNumber(minTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex
)errSecBadReq
, NULL
));
1301 require_action_quiet(isArray(requiredTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's required characters must be an array of CFCharacterSetRefs"), (CFIndex
)errSecBadReq
, NULL
));
1303 CFNumberGetValue(minTest
, kCFNumberSInt64Type
, &valuePtr
);
1304 CFIndex minPasswordLength
= (long)valuePtr
;
1305 CFNumberGetValue(maxTest
, kCFNumberSInt64Type
, &valuePtr
);
1306 CFIndex maxPasswordLength
= (long)valuePtr
;
1308 require_action_quiet(minPasswordLength
<= maxPasswordLength
, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex
)errSecBadReq
, NULL
));
1310 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
));
1311 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
));
1313 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordDisallowedCharacters
, &prohibitedCharacters
)){
1314 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
));
1315 require_action_quiet(isString(prohibitedCharacters
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("Disallowed Characters dictionary parameter is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1317 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordCantEndWithChars
, &endWith
)){
1318 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
));
1319 require_action_quiet(isString(endWith
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'EndWith' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1321 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordCantStartWithChars
, &startWith
)){
1322 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
));
1323 require_action_quiet(isString(startWith
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'StartWith' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1325 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordGroupSize
, &groupSizeRef
)){
1326 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
));
1327 require_action_quiet(isNumber(groupSizeRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'groupsize' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
1329 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordNumberOfGroups
, &numberOfGroupsRef
)){
1330 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
));
1331 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
));
1333 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordSeparator
, &separatorRef
)){
1334 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
));
1335 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
));
1338 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordContainsNoMoreThanNSpecificCharacters
, &atMostCharactersRef
)){
1339 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
));
1340 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
));
1342 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
));
1343 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
));
1344 require_action_quiet(isNumber(thresholdRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
1346 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
));
1347 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
));
1348 require_action_quiet(isString(characters
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1351 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordContainsAtLeastNSpecificCharacters
, &atLeastCharactersRef
)){
1352 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
));
1353 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
));
1355 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
));
1357 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
));
1358 require_action_quiet(isNumber(thresholdRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
1360 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
));
1361 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
));
1362 require_action_quiet(isString(characters
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1365 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
, &identicalRef
)){
1366 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
));
1367 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
));
1374 if (tempError
!= NULL
) {
1376 *error
= CFRetainSafe(tempError
);
1380 CFReleaseNull(tempError
);
1385 static bool doesFinalPasswordPass(bool isSimple
, CFStringRef password
, CFDictionaryRef requirements
){
1387 CFTypeRef characters
, identicalRef
= NULL
, NRef
= NULL
, endWith
= NULL
, startWith
= NULL
, atLeastCharacters
= NULL
, atMostCharacters
= NULL
;
1389 CFIndex N
, identicalCount
= 0;
1390 CFArrayRef requiredCharacterSet
= (CFArrayRef
)CFDictionaryGetValue(requirements
, kSecRequiredCharacterSetsKey
);
1392 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordCantEndWithChars
, &endWith
))
1395 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordCantStartWithChars
, &startWith
))
1398 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsAtLeastNSpecificCharacters
, &atLeastCharacters
))
1399 atLeastCharacters
= NULL
;
1401 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsNoMoreThanNSpecificCharacters
, &atMostCharacters
))
1402 atMostCharacters
= NULL
;
1404 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
, &identicalRef
))
1405 identicalRef
= NULL
;
1407 CFNumberGetValue((CFNumberRef
)identicalRef
, kCFNumberSInt64Type
, &valuePtr
);
1408 identicalCount
= (long)valuePtr
;
1412 if(!doesPasswordEndWith(password
, endWith
))
1415 if(startWith
!= NULL
){
1416 if(!doesPasswordStartWith(password
, startWith
))
1419 if(atLeastCharacters
!= NULL
){
1420 NRef
= CFDictionaryGetValue(atLeastCharacters
, kSecPasswordCharacterCount
);
1421 characters
= CFDictionaryGetValue(atLeastCharacters
, kSecPasswordCharacters
);
1422 CFNumberGetValue((CFNumberRef
)NRef
, kCFNumberSInt64Type
, &valuePtr
);
1424 if(!passwordContainsAtLeastNCharacters(password
, characters
, N
))
1427 if(atMostCharacters
!= NULL
){
1428 NRef
= CFDictionaryGetValue(atMostCharacters
, kSecPasswordCharacterCount
);
1429 characters
= CFDictionaryGetValue(atMostCharacters
, kSecPasswordCharacters
);
1430 CFNumberGetValue((CFNumberRef
)NRef
, kCFNumberSInt64Type
, &valuePtr
);
1432 if(!passwordContainsLessThanNCharacters(password
, characters
, N
))
1435 if(identicalRef
!= NULL
){
1436 if(!passwordContainsLessThanNIdenticalCharacters(password
, identicalCount
))
1439 if (!passwordContainsRequiredCharacters(password
, requiredCharacterSet
))
1442 if(true == SecPasswordIsPasswordWeak2(isSimple
, password
))
1449 CreateChecksum(SecPasswordType type
, CFStringRef password
, CFIndex length
, CFStringRef allowedChars
)
1451 if (type
!= kSecPasswordTypeiCloudRecoveryKey
)
1454 CFMutableStringRef checksum
= NULL
;
1455 uint8_t digest
[CCSHA256_OUTPUT_SIZE
];
1456 if (length
> (CFIndex
)sizeof(digest
))
1459 CFDataRef data
= CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), password
, kCFStringEncodingUTF8
, 0);
1463 ccdigest(ccsha256_di(), CFDataGetLength(data
), CFDataGetBytePtr(data
), digest
);
1464 CFReleaseNull(data
);
1466 CFIndex allowedCharLength
= CFStringGetLength(allowedChars
);
1468 checksum
= CFStringCreateMutable(SecCFAllocatorZeroize(), 0);
1469 for (CFIndex n
= 0; n
< length
; n
++) {
1470 CFIndex selection
= digest
[n
] % allowedCharLength
;
1471 UniChar c
= CFStringGetCharacterAtIndex(allowedChars
, selection
);
1472 CFStringAppendCharacters(checksum
, &c
, 1);
1478 //entry point into password generation
1479 CF_RETURNS_RETAINED CFStringRef
SecPasswordGenerate(SecPasswordType type
, CFErrorRef
*error
, CFDictionaryRef passwordRequirements
){
1480 bool check
= false, isSimple
= false;
1481 CFTypeRef separator
= NULL
, defaults
= NULL
, groupSizeRef
= NULL
, numberOfGroupsRef
= NULL
;
1482 CFDictionaryRef properlyFormattedRequirements
= NULL
;
1483 CFErrorRef localError
= NULL
;
1484 uint64_t valuePtr
, groupSize
= 0, numberOfGroups
, checksumChars
= 0;
1485 CFNumberRef numberOfRequiredRandomCharacters
, checksumCharacters
;
1486 CFIndex requiredCharactersSize
= 0;
1487 CFStringRef randomCharacters
= NULL
, password
= NULL
, allowedChars
= NULL
;
1488 CFMutableStringRef finalPassword
= NULL
;
1490 if(type
== kSecPasswordTypePIN
)
1494 check
= isDictionaryFormattedProperly(type
, passwordRequirements
, &localError
);
1495 require_quiet(check
!= false, fail
);
1497 //should we generate defaults?
1498 if(passwordRequirements
== NULL
|| (CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordDefaultForType
, &defaults
) && isString(defaults
) == true && 0 == CFStringCompare(defaults
, CFSTR("true"), 0) ))
1499 properlyFormattedRequirements
= passwordGenerateCreateDefaultParametersDictionary(type
, passwordRequirements
);
1501 properlyFormattedRequirements
= passwordGenerationCreateParametersDictionary(type
, passwordRequirements
);
1503 require_quiet(localError
== NULL
&& properlyFormattedRequirements
!= NULL
, fail
);
1505 numberOfRequiredRandomCharacters
= (CFNumberRef
)CFDictionaryGetValue(properlyFormattedRequirements
, kSecNumberOfRequiredRandomCharactersKey
);
1506 if (isNumber(numberOfRequiredRandomCharacters
) && CFNumberGetValue(numberOfRequiredRandomCharacters
, kCFNumberSInt64Type
, &valuePtr
))
1507 requiredCharactersSize
= (long)valuePtr
;
1509 checksumCharacters
= (CFNumberRef
)CFDictionaryGetValue(properlyFormattedRequirements
, kSecNumberOfChecksumCharactersKey
);
1510 if (isNumber(checksumCharacters
) && CFNumberGetValue(checksumCharacters
, kCFNumberSInt64Type
, &valuePtr
))
1511 checksumChars
= (long)valuePtr
;
1514 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements
, kSecPasswordGroupSize
, &groupSizeRef
)){
1515 groupSizeRef
= NULL
;
1518 CFNumberGetValue((CFNumberRef
)groupSizeRef
, kCFNumberSInt64Type
, &groupSize
);
1520 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements
, kSecPasswordNumberOfGroups
, &numberOfGroupsRef
)){
1521 numberOfGroupsRef
= NULL
;
1524 CFNumberGetValue((CFNumberRef
)numberOfGroupsRef
, kCFNumberSInt64Type
, &numberOfGroups
);
1526 require(requiredCharactersSize
, fail
);
1529 allowedChars
= CFDictionaryGetValue(properlyFormattedRequirements
, kSecAllowedCharactersKey
);
1530 getPasswordRandomCharacters(&randomCharacters
, properlyFormattedRequirements
, &requiredCharactersSize
, allowedChars
);
1532 if(numberOfGroupsRef
&& groupSizeRef
){
1533 finalPassword
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1535 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements
, kSecPasswordSeparator
, &separator
))
1538 if(separator
== NULL
)
1539 separator
= CFSTR("-");
1542 while( i
!= requiredCharactersSize
){
1543 if((i
+ (CFIndex
)groupSize
) < requiredCharactersSize
){
1544 CFStringRef subString
= CFStringCreateWithSubstring(kCFAllocatorDefault
, randomCharacters
, CFRangeMake(i
, (CFIndex
)groupSize
));
1545 CFStringAppend(finalPassword
, subString
);
1546 CFStringAppend(finalPassword
, separator
);
1547 CFReleaseSafe(subString
);
1550 else if((i
+(CFIndex
)groupSize
) == requiredCharactersSize
){
1551 CFStringRef subString
= CFStringCreateWithSubstring(kCFAllocatorDefault
, randomCharacters
, CFRangeMake(i
, (CFIndex
)groupSize
));
1552 CFStringAppend(finalPassword
, subString
);
1553 CFReleaseSafe(subString
);
1557 CFStringRef subString
= CFStringCreateWithSubstring(kCFAllocatorDefault
, randomCharacters
, CFRangeMake(i
, requiredCharactersSize
- i
));
1558 CFStringAppend(finalPassword
, subString
);
1559 CFReleaseSafe(subString
);
1560 i
+=(requiredCharactersSize
- i
);
1563 if (checksumChars
) {
1564 CFStringRef checksum
= CreateChecksum(type
, randomCharacters
, (CFIndex
)checksumChars
, allowedChars
);
1565 CFStringAppend(finalPassword
, checksum
);
1566 CFReleaseNull(checksum
);
1568 password
= CFStringCreateCopy(kCFAllocatorDefault
, finalPassword
);
1569 CFReleaseNull(finalPassword
);
1571 //no fancy formatting
1573 password
= CFStringCreateCopy(kCFAllocatorDefault
, randomCharacters
);
1576 CFReleaseNull(randomCharacters
);
1577 require_quiet(doesFinalPasswordPass(isSimple
, password
, properlyFormattedRequirements
), no_pass
);
1578 CFReleaseNull(properlyFormattedRequirements
);
1582 CFReleaseNull(password
);
1586 if (error
&& localError
) {
1587 *error
= localError
;
1591 CFReleaseSafe(localError
);
1592 CFReleaseNull(properlyFormattedRequirements
);
1596 const char *in_word_set (const char *str
, unsigned int len
){
1597 static const char * wordlist
[] =
1599 "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
1600 "", "", "0103", "", "", "", "", "0123", "", "", "", "", "0303", "", "", "",
1601 "", "", "", "", "0110", "", "1103", "", "", "", "", "1123", "", "", "0000",
1602 "", "1203", "", "0404", "", "", "", "", "1234", "1110", "2015", "2013", "",
1603 "2014", "1010", "2005", "2003", "", "2004", "1210", "0505", "0111", "", "",
1604 "", "2008", "0101", "", "2007", "", "", "", "", "2006", "2010", "1995", "1993",
1605 "", "1994", "2000", "", "1111", "", "", "", "1998", "1101", "", "1997", "",
1606 "0808", "1211", "", "1996", "0102", "", "1201", "", "", "1990", "", "", "",
1607 "", "0202", "", "2011", "", "", "1112", "1958", "2001", "", "1957", "1102",
1608 "", "3333", "", "1956", "1212", "1985", "1983", "", "1984", "1202", "", "0909",
1609 "", "0606", "", "1988", "1991", "", "1987", "2012", "", "", "", "1986", "2002",
1610 "", "", "", "0707", "1980", "", "2009", "", "", "2222", "1965", "1963", "",
1611 "1964", "", "", "2229", "", "", "1992", "1968", "", "", "1967", "", "", "1999",
1612 "", "1966", "", "1975", "1973", "", "1974", "1960", "", "1981", "", "4444",
1613 "", "1978", "", "7465", "1977", "", "", "", "", "1976", "2580", "", "1959",
1614 "", "", "1970", "", "", "", "", "", "", "", "", "", "1982", "", "1961", "",
1615 "", "5252", "", "1989", "", "", "", "", "", "", "", "", "", "", "", "", "",
1616 "", "1971", "", "", "", "", "", "", "", "1962", "", "5683", "", "6666", "",
1617 "", "1969", "", "", "", "", "", "", "", "", "", "", "", "", "1972", "", "",
1618 "", "", "", "", "1979", "", "", "", "7667"
1621 if (len
<= MAX_WORD_LENGTH
&& len
>= MIN_WORD_LENGTH
)
1623 register int key
= pinhash (str
, len
);
1625 if (key
<= MAX_HASH_VALUE
&& key
>= 0)
1627 register const char *s
= wordlist
[key
];
1628 if (*str
== *s
&& !strcmp (str
+ 1, s
+ 1))
1634 CFDictionaryRef
SecPasswordCopyDefaultPasswordLength(SecPasswordType type
, CFErrorRef
*error
){
1636 CFIndex tupleLengthInt
= 0, numOfTuplesInt
= 0;
1637 CFNumberRef tupleLength
= NULL
;
1638 CFNumberRef numOfTuples
= NULL
;
1640 CFMutableDictionaryRef passwordLengthDefaults
= NULL
;
1641 CFDictionaryRef result
= NULL
;
1644 case(kSecPasswordTypeiCloudRecoveryKey
):
1649 case(kSecPasswordTypeiCloudRecovery
):
1654 case(kSecPasswordTypePIN
):
1659 case(kSecPasswordTypeSafari
):
1664 case(kSecPasswordTypeWifi
):
1670 if(SecError(errSecBadReq
, error
, CFSTR("Password type does not exist.")) == false)
1672 secdebug("secpasswordcopydefaultpasswordlength", "could not create error!");
1676 if (tupleLengthInt
!= 0 && numOfTuplesInt
!= 0) {
1677 tupleLength
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberCFIndexType
, &tupleLengthInt
);
1678 numOfTuples
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberCFIndexType
, &numOfTuplesInt
);
1679 passwordLengthDefaults
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1680 CFDictionaryAddValue(passwordLengthDefaults
, kSecPasswordGroupSize
, tupleLength
);
1681 CFDictionaryAddValue(passwordLengthDefaults
, kSecPasswordNumberOfGroups
, numOfTuples
);
1682 result
= CFDictionaryCreateCopy(kCFAllocatorDefault
, passwordLengthDefaults
);
1685 CFReleaseSafe(tupleLength
);
1686 CFReleaseSafe(numOfTuples
);
1687 CFReleaseSafe(passwordLengthDefaults
);
1692 SecPasswordValidatePasswordFormat(SecPasswordType type
, CFStringRef password
, CFErrorRef
*error
)
1694 CFIndex tupleLengthInt
= 0, numOfTuplesInt
= 0, checkSumChars
= 0;
1695 CFStringRef checksum
= NULL
, madeChecksum
= NULL
, passwordNoChecksum
= NULL
;
1696 CFMutableStringRef randomChars
= NULL
;
1697 CFStringRef allowedChars
= NULL
;
1701 case kSecPasswordTypeiCloudRecoveryKey
:
1705 allowedChars
= defaultiCloudCharacters
;
1707 case kSecPasswordTypeiCloudRecovery
:
1711 case(kSecPasswordTypePIN
):
1716 SecError(errSecBadReq
, error
, CFSTR("Password type does not exist."));
1721 if (numOfTuplesInt
< 1)
1723 if (checkSumChars
> tupleLengthInt
)
1726 CFIndex numberOfChars
= numOfTuplesInt
* tupleLengthInt
+ (numOfTuplesInt
- 1);
1729 * First check expected length
1732 require(CFStringGetLength(password
) == numberOfChars
, out
); /* N groups of M with (N-1) seperator - in-between */
1734 randomChars
= CFStringCreateMutable(SecCFAllocatorZeroize(), 0);
1735 require(randomChars
, out
);
1738 * make sure dash-es are at the expected spots
1741 for (CFIndex n
= 0; n
< numOfTuplesInt
; n
++) {
1743 UniChar c
= CFStringGetCharacterAtIndex(password
, (n
* (tupleLengthInt
+ 1)) - 1);
1744 require(c
== '-', out
);
1746 CFStringRef substr
= CFStringCreateWithSubstring(SecCFAllocatorZeroize(), password
, CFRangeMake(n
* (tupleLengthInt
+ 1), tupleLengthInt
));
1747 CFStringAppend(randomChars
, substr
);
1748 CFReleaseNull(substr
);
1751 if (checkSumChars
) {
1753 * Pull apart and password and checksum
1756 checksum
= CFStringCreateWithSubstring(SecCFAllocatorZeroize(), randomChars
, CFRangeMake((numOfTuplesInt
* tupleLengthInt
) - checkSumChars
, checkSumChars
));
1757 require(checksum
, out
);
1759 passwordNoChecksum
= CFStringCreateWithSubstring(SecCFAllocatorZeroize(), randomChars
, CFRangeMake(0, (numOfTuplesInt
* tupleLengthInt
) - checkSumChars
));
1760 require(passwordNoChecksum
, out
);
1766 madeChecksum
= CreateChecksum(type
, passwordNoChecksum
, checkSumChars
, allowedChars
);
1767 require(madeChecksum
, out
);
1769 require(CFEqual(madeChecksum
, checksum
), out
);
1774 CFReleaseNull(randomChars
);
1775 CFReleaseNull(madeChecksum
);
1776 CFReleaseNull(checksum
);
1777 CFReleaseNull(passwordNoChecksum
);