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 OSStatus
getPasswordRandomCharacters(CFStringRef
*returned
, CFDictionaryRef requirements
, CFIndex
*numberOfRandomCharacters
, CFStringRef allowedCharacters
)
875 uint8_t *randomNumbers
= malloc(*numberOfRandomCharacters
);
876 unsigned char *randomCharacters
= malloc(*numberOfRandomCharacters
);
878 if (randomNumbers
== NULL
|| randomCharacters
== NULL
) {
880 free(randomCharacters
);
881 return errSecMemoryError
;
884 getUniformRandomNumbers(randomNumbers
, *numberOfRandomCharacters
, CFStringGetLength(allowedCharacters
));
886 CFTypeRef prohibitedCharacters
= NULL
;
887 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordDisallowedCharacters
, &prohibitedCharacters
))
888 prohibitedCharacters
= NULL
;
890 //it's faster for long characters to check each character produced for these cases
891 for (CFIndex i
= 0; i
< *numberOfRandomCharacters
; ++i
){
892 //check prohibited characters
893 UniChar randomChar
[1];
894 randomChar
[0] = CFStringGetCharacterAtIndex(allowedCharacters
, randomNumbers
[i
]);
895 if (prohibitedCharacters
!= NULL
)
897 CFStringRef temp
= CFStringCreateWithCharacters(kCFAllocatorDefault
, randomChar
, 1);
898 bool pwdncc
= passwordDoesNotContainCharacters(temp
, prohibitedCharacters
);
901 //change up the random numbers so we don't get the same index into allowed
902 getUniformRandomNumbers(randomNumbers
, *numberOfRandomCharacters
, CFStringGetLength(allowedCharacters
));
907 randomCharacters
[i
] = (unsigned char)randomChar
[0];
910 *returned
= CFStringCreateWithBytes(kCFAllocatorDefault
, randomCharacters
, *numberOfRandomCharacters
, kCFStringEncodingUTF8
, false);
912 free(randomCharacters
);
915 return errSecSuccess
;
918 static bool doesPasswordEndWith(CFStringRef password
, CFStringRef prohibitedCharacters
)
920 CFCharacterSetRef characterSet
= NULL
;
921 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, prohibitedCharacters
);
923 CFRange rangeToSearch
= CFRangeMake(CFStringGetLength(password
) - CFStringGetLength(prohibitedCharacters
), CFStringGetLength(prohibitedCharacters
));
924 require_quiet(0 == CFStringCompareWithOptions(password
, prohibitedCharacters
, rangeToSearch
, 0), fail
);
925 CFReleaseNull(characterSet
);
928 CFReleaseNull(characterSet
);
932 static bool doesPasswordStartWith(CFStringRef password
, CFStringRef prohibitedCharacters
)
934 CFCharacterSetRef characterSet
= NULL
;
935 characterSet
= CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault
, prohibitedCharacters
);
937 CFRange rangeToSearch
= CFRangeMake(0, CFStringGetLength(prohibitedCharacters
));
938 require_quiet(0 == CFStringCompareWithOptions(password
, prohibitedCharacters
, rangeToSearch
, 0), fail
);
939 CFReleaseNull(characterSet
);
940 return false; //does not start with prohibitedCharacters
942 CFReleaseNull(characterSet
);
946 static CFDictionaryRef
passwordGenerateCreateDefaultParametersDictionary(SecPasswordType type
, CFDictionaryRef requirements
){
948 CFMutableArrayRef requiredCharacterSets
= NULL
;
949 CFNumberRef numReqChars
= NULL
, checksumChars
= NULL
;
950 CFStringRef defaultPasswordFormat
= NULL
;
951 requiredCharacterSets
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
952 defaultPasswordFormat
= CFSTR("true");
953 CFTypeRef groupSizeRef
= NULL
, numberOfGroupsRef
= NULL
;
954 CFIndex groupSize
, numberOfGroups
, checksumSize
= 0;
955 CFDictionaryRef returned
= NULL
;
958 case kSecPasswordTypeiCloudRecoveryKey
:
962 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, (groupSize
* numberOfGroups
) - checksumSize
);
963 checksumChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, checksumSize
);
964 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &groupSize
);
965 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &numberOfGroups
);
967 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
968 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
969 CFArrayAppendValue(requiredCharacterSets
, uppercaseLetterCharacterSet
);
970 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
971 returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
972 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
973 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
974 kSecNumberOfChecksumCharactersKey
, checksumChars
,
975 kSecAllowedCharactersKey
, defaultiCloudCharacters
,
976 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
977 kSecPasswordGroupSize
, groupSizeRef
,
978 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
981 case(kSecPasswordTypeiCloudRecovery
):
982 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, defaultiCloudPasswordLength
);
985 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &groupSize
);
986 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &numberOfGroups
);
988 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
989 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
990 CFArrayAppendValue(requiredCharacterSets
, uppercaseLetterCharacterSet
);
991 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
992 returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
993 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
994 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
995 kSecAllowedCharactersKey
, defaultiCloudCharacters
,
996 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
997 kSecPasswordGroupSize
, groupSizeRef
,
998 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
1002 case(kSecPasswordTypePIN
):
1003 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, defaultPINLength
);
1006 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &groupSize
);
1007 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &numberOfGroups
);
1009 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
1010 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
1011 returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
1012 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
1013 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
1014 kSecAllowedCharactersKey
, defaultPINCharacters
,
1015 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
1016 kSecPasswordGroupSize
, groupSizeRef
,
1017 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
1021 case(kSecPasswordTypeWifi
):
1024 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &groupSize
);
1025 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &numberOfGroups
);
1027 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
1028 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
1030 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, defaultWifiPasswordLength
);
1031 CFArrayAppendValue(requiredCharacterSets
, lowercaseLetterCharacterSet
);
1032 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
1033 returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
1034 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
1035 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
1036 kSecAllowedCharactersKey
, defaultWifiCharacters
,
1037 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
1038 kSecPasswordGroupSize
, groupSizeRef
,
1039 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
1046 groupSizeRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &groupSize
);
1047 numberOfGroupsRef
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &numberOfGroups
);
1048 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
1049 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
1050 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
1051 CFArrayAppendValue(requiredCharacterSets
, uppercaseLetterCharacterSet
);
1052 CFArrayAppendValue(requiredCharacterSets
, lowercaseLetterCharacterSet
);
1053 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
1055 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, defaultNumberOfRandomCharacters
);
1056 returned
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
1057 kSecUseDefaultPasswordFormatKey
, defaultPasswordFormat
,
1058 kSecNumberOfRequiredRandomCharactersKey
, numReqChars
,
1059 kSecAllowedCharactersKey
, defaultCharacters
,
1060 kSecRequiredCharacterSetsKey
, requiredCharacterSets
,
1061 kSecPasswordGroupSize
, groupSizeRef
,
1062 kSecPasswordNumberOfGroups
, numberOfGroupsRef
,
1070 CFReleaseNull(numReqChars
);
1071 CFReleaseNull(requiredCharacterSets
);
1072 CFReleaseNull(groupSizeRef
);
1073 CFReleaseNull(numberOfGroupsRef
);
1074 CFReleaseNull(checksumChars
);
1077 static CFDictionaryRef
passwordGenerationCreateParametersDictionary(SecPasswordType type
, CFDictionaryRef requirements
)
1079 CFMutableArrayRef requiredCharacterSets
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
1080 CFNumberRef numReqChars
= NULL
;
1081 CFIndex numberOfRequiredRandomCharacters
;
1082 CFStringRef allowedCharacters
= NULL
, useDefaultPasswordFormat
= NULL
;
1084 CFTypeRef prohibitedCharacters
= NULL
, endWith
= NULL
, startWith
= NULL
,
1085 groupSizeRef
= NULL
, numberOfGroupsRef
= NULL
, separatorRef
= NULL
,
1086 atMostCharactersRef
= NULL
,atLeastCharactersRef
= NULL
, identicalRef
= NULL
;
1088 CFNumberRef min
= (CFNumberRef
)CFDictionaryGetValue(requirements
, kSecPasswordMinLengthKey
);
1089 CFNumberRef max
= (CFNumberRef
)CFDictionaryGetValue(requirements
, kSecPasswordMaxLengthKey
);
1091 CFNumberGetValue(min
, kCFNumberSInt64Type
, &valuePtr
);
1092 CFIndex minPasswordLength
= (long)valuePtr
;
1093 CFNumberGetValue(max
, kCFNumberSInt64Type
, &valuePtr
);
1094 CFIndex maxPasswordLength
= (long)valuePtr
;
1096 // If requirements allow, we will generate the password in default format.
1097 useDefaultPasswordFormat
= CFSTR("true");
1098 numberOfRequiredRandomCharacters
= defaultNumberOfRandomCharacters
;
1100 if(type
== kSecPasswordTypePIN
)
1102 if( maxPasswordLength
&& minPasswordLength
)
1103 numberOfRequiredRandomCharacters
= maxPasswordLength
;
1104 else if( !maxPasswordLength
&& minPasswordLength
)
1105 numberOfRequiredRandomCharacters
= minPasswordLength
;
1106 else if( !minPasswordLength
&& maxPasswordLength
)
1107 numberOfRequiredRandomCharacters
= maxPasswordLength
;
1109 numberOfRequiredRandomCharacters
= defaultPINLength
;
1111 allowedCharacters
= CFSTR("0123456789");
1112 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
1113 useDefaultPasswordFormat
= CFSTR("false");
1116 CFArrayRef requiredCharactersArray
= NULL
;
1118 if (minPasswordLength
&& minPasswordLength
> defaultNumberOfRandomCharacters
) {
1119 useDefaultPasswordFormat
= CFSTR("false");
1120 numberOfRequiredRandomCharacters
= minPasswordLength
;
1122 if (maxPasswordLength
&& maxPasswordLength
< defaultNumberOfRandomCharacters
) {
1123 useDefaultPasswordFormat
= CFSTR("false");
1124 numberOfRequiredRandomCharacters
= maxPasswordLength
;
1126 if (maxPasswordLength
&& minPasswordLength
&& maxPasswordLength
== minPasswordLength
&& maxPasswordLength
!= defaultNumberOfRandomCharacters
){
1127 useDefaultPasswordFormat
= CFSTR("false");
1128 numberOfRequiredRandomCharacters
= maxPasswordLength
;
1130 allowedCharacters
= (CFStringRef
)CFRetainSafe(CFDictionaryGetValue(requirements
, kSecPasswordAllowedCharactersKey
));
1131 requiredCharactersArray
= (CFArrayRef
)CFDictionaryGetValue(requirements
, kSecPasswordRequiredCharactersKey
);
1133 if (requiredCharactersArray
) {
1134 for (CFIndex i
= 0; i
< CFArrayGetCount(requiredCharactersArray
); i
++){
1135 CFCharacterSetRef stringWithRequiredCharacters
= CFArrayGetValueAtIndex(requiredCharactersArray
, i
);
1136 if(stringWithRequiredCharacters
&& CFStringFindCharacterFromSet(allowedCharacters
, stringWithRequiredCharacters
, CFRangeMake(0, CFStringGetLength(allowedCharacters
)), 0, NULL
)){
1137 CFArrayAppendValue(requiredCharacterSets
, stringWithRequiredCharacters
);
1141 uppercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter
);
1142 lowercaseLetterCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter
);
1143 decimalDigitCharacterSet
= CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit
);
1144 CFArrayAppendValue(requiredCharacterSets
, uppercaseLetterCharacterSet
);
1145 CFArrayAppendValue(requiredCharacterSets
, lowercaseLetterCharacterSet
);
1146 CFArrayAppendValue(requiredCharacterSets
, decimalDigitCharacterSet
);
1150 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordDisallowedCharacters
, &prohibitedCharacters
))
1151 prohibitedCharacters
= NULL
;
1153 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordCantEndWithChars
, &endWith
))
1156 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordCantStartWithChars
, &startWith
))
1159 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordGroupSize
, &groupSizeRef
))
1160 groupSizeRef
= NULL
;
1162 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordNumberOfGroups
, &numberOfGroupsRef
))
1163 numberOfGroupsRef
= NULL
;
1165 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordSeparator
, &separatorRef
))
1166 separatorRef
= NULL
;
1168 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsNoMoreThanNSpecificCharacters
, &atMostCharactersRef
))
1169 atMostCharactersRef
= NULL
;
1171 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsAtLeastNSpecificCharacters
, &atLeastCharactersRef
))
1172 atLeastCharactersRef
= NULL
;
1174 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
, &identicalRef
))
1175 identicalRef
= NULL
;
1177 if (allowedCharacters
) {
1178 if( false == CFStringFindWithOptions(allowedCharacters
, CFSTR("-"), CFRangeMake(0, CFStringGetLength(allowedCharacters
)), kCFCompareCaseInsensitive
, NULL
))
1179 useDefaultPasswordFormat
= CFSTR("false");
1181 allowedCharacters
= CFRetainSafe(defaultCharacters
);
1183 // In default password format, we use dashes only as separators, not as symbols you can encounter at a random position.
1184 if (useDefaultPasswordFormat
== CFSTR("false")){
1185 CFMutableStringRef mutatedAllowedCharacters
= CFStringCreateMutableCopy(kCFAllocatorDefault
, CFStringGetLength(allowedCharacters
), allowedCharacters
);
1186 CFStringFindAndReplace (mutatedAllowedCharacters
, CFSTR("-"), CFSTR(""), CFRangeMake(0, CFStringGetLength(allowedCharacters
)),kCFCompareCaseInsensitive
);
1187 CFReleaseSafe(allowedCharacters
);
1188 allowedCharacters
= mutatedAllowedCharacters
;
1191 if (CFArrayGetCount(requiredCharacterSets
) > numberOfRequiredRandomCharacters
) {
1192 CFReleaseNull(requiredCharacterSets
);
1193 requiredCharacterSets
= NULL
;
1195 //create new CFDictionary
1196 numReqChars
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, numberOfRequiredRandomCharacters
);
1197 CFMutableDictionaryRef updatedConstraints
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1198 CFDictionaryAddValue(updatedConstraints
, kSecUseDefaultPasswordFormatKey
, useDefaultPasswordFormat
);
1199 CFDictionarySetValue(updatedConstraints
, kSecNumberOfRequiredRandomCharactersKey
, numReqChars
);
1200 CFDictionaryAddValue(updatedConstraints
, kSecAllowedCharactersKey
, allowedCharacters
);
1201 if(requiredCharacterSets
)
1202 CFDictionaryAddValue(updatedConstraints
, kSecRequiredCharacterSetsKey
, requiredCharacterSets
);
1204 //add the prohibited characters string if it exists to the new dictionary
1205 if(prohibitedCharacters
)
1206 CFDictionaryAddValue(updatedConstraints
, kSecPasswordDisallowedCharacters
, prohibitedCharacters
);
1208 //add the characters the password can't end with if it exists to the new dictionary
1210 CFDictionaryAddValue(updatedConstraints
, kSecPasswordCantEndWithChars
, endWith
);
1212 //add the characters the password can't start with if it exists to the new dictionary
1214 CFDictionaryAddValue(updatedConstraints
, kSecPasswordCantStartWithChars
, startWith
);
1217 CFDictionaryAddValue(updatedConstraints
, kSecPasswordGroupSize
, groupSizeRef
);
1219 if(numberOfGroupsRef
)
1220 CFDictionaryAddValue(updatedConstraints
, kSecPasswordNumberOfGroups
, numberOfGroupsRef
);
1223 CFDictionaryAddValue(updatedConstraints
, kSecPasswordSeparator
, separatorRef
);
1225 if(atMostCharactersRef
)
1226 CFDictionaryAddValue(updatedConstraints
, kSecPasswordContainsNoMoreThanNSpecificCharacters
, atMostCharactersRef
);
1228 if(atLeastCharactersRef
)
1229 CFDictionaryAddValue(updatedConstraints
, kSecPasswordContainsAtLeastNSpecificCharacters
, atLeastCharactersRef
);
1232 CFDictionaryAddValue(updatedConstraints
, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
, identicalRef
);
1234 CFReleaseNull(useDefaultPasswordFormat
);
1235 CFReleaseNull(numReqChars
);
1236 CFReleaseNull(allowedCharacters
);
1237 CFReleaseNull(requiredCharacterSets
);
1239 return updatedConstraints
;
1242 static bool isDictionaryFormattedProperly(SecPasswordType type
, CFDictionaryRef passwordRequirements
, CFErrorRef
*error
){
1244 CFTypeRef defaults
= NULL
;
1245 CFErrorRef tempError
= NULL
;
1246 if(passwordRequirements
== NULL
){
1250 if( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordDefaultForType
, &defaults
) ){
1251 if(isString(defaults
) == true && 0 == CFStringCompare(defaults
, CFSTR("true"), 0)){
1255 //only need to check max and min pin length formatting
1256 if(type
== kSecPasswordTypePIN
){
1257 CFTypeRef minTest
= NULL
, maxTest
= NULL
;
1259 CFIndex minPasswordLength
= 0, maxPasswordLength
= 0;
1261 if( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordDefaultForType
, &defaults
) ){
1262 if(isString(defaults
) == true && 0 == CFStringCompare(defaults
, CFSTR("true"), 0)){
1266 //check if the values exist!
1267 if( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordMaxLengthKey
, &maxTest
) ){
1268 require_action_quiet(isNull(maxTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a max length"), (CFIndex
)errSecBadReq
, NULL
));
1269 require_action_quiet(isNumber(maxTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex
)errSecBadReq
, NULL
));
1272 if (CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordMinLengthKey
, &minTest
) ){
1273 require_action_quiet(isNull(minTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a min length"), (CFIndex
)errSecBadReq
, NULL
));
1274 require_action_quiet(isNumber(minTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex
)errSecBadReq
, NULL
));
1276 //check if the values exist!
1278 CFNumberRef max
= (CFNumberRef
)maxTest
;
1279 CFNumberGetValue(max
, kCFNumberSInt64Type
, &valuePtr
);
1280 maxPasswordLength
= (long)valuePtr
;
1283 CFNumberRef min
= (CFNumberRef
)minTest
;
1284 CFNumberGetValue(min
, kCFNumberSInt64Type
, &valuePtr
);
1285 minPasswordLength
= (long)valuePtr
;
1287 //make sure min and max make sense respective to each other and that they aren't less than 4 digits.
1288 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
));
1289 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
));
1292 CFTypeRef allowedTest
, maxTest
, minTest
, requiredTest
, prohibitedCharacters
, endWith
, startWith
,
1293 groupSizeRef
, numberOfGroupsRef
, separatorRef
, atMostCharactersRef
,
1294 atLeastCharactersRef
, thresholdRef
, identicalRef
, characters
;
1297 //check if the values exist!
1298 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
));
1299 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordMaxLengthKey
, &maxTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a max length"), (CFIndex
)errSecBadReq
, NULL
));
1300 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordMinLengthKey
, &minTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a min length"), (CFIndex
)errSecBadReq
, NULL
));
1301 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
));
1303 //check if values are null?
1304 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
));
1305 require_action_quiet(isNull(maxTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a max length"), (CFIndex
)errSecBadReq
, NULL
));
1306 require_action_quiet(isNull(minTest
)!= true, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("To generate a password, need a min length"), (CFIndex
)errSecBadReq
, NULL
));
1307 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
));
1309 //check if the values are correct
1310 require_action_quiet(isString(allowedTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's allowed characters must be a CFStringRef"), (CFIndex
)errSecBadReq
, NULL
));
1311 require_action_quiet(isNumber(maxTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex
)errSecBadReq
, NULL
));
1312 require_action_quiet(isNumber(minTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex
)errSecBadReq
, NULL
));
1313 require_action_quiet(isArray(requiredTest
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's required characters must be an array of CFCharacterSetRefs"), (CFIndex
)errSecBadReq
, NULL
));
1315 CFNumberGetValue(minTest
, kCFNumberSInt64Type
, &valuePtr
);
1316 CFIndex minPasswordLength
= (long)valuePtr
;
1317 CFNumberGetValue(maxTest
, kCFNumberSInt64Type
, &valuePtr
);
1318 CFIndex maxPasswordLength
= (long)valuePtr
;
1320 require_action_quiet(minPasswordLength
<= maxPasswordLength
, fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex
)errSecBadReq
, NULL
));
1322 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
));
1323 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
));
1325 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordDisallowedCharacters
, &prohibitedCharacters
)){
1326 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
));
1327 require_action_quiet(isString(prohibitedCharacters
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("Disallowed Characters dictionary parameter is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1329 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordCantEndWithChars
, &endWith
)){
1330 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
));
1331 require_action_quiet(isString(endWith
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'EndWith' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1333 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordCantStartWithChars
, &startWith
)){
1334 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
));
1335 require_action_quiet(isString(startWith
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'StartWith' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1337 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordGroupSize
, &groupSizeRef
)){
1338 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
));
1339 require_action_quiet(isNumber(groupSizeRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'groupsize' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
1341 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordNumberOfGroups
, &numberOfGroupsRef
)){
1342 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
));
1343 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
));
1345 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordSeparator
, &separatorRef
)){
1346 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
));
1347 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
));
1350 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordContainsNoMoreThanNSpecificCharacters
, &atMostCharactersRef
)){
1351 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
));
1352 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
));
1354 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
));
1355 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
));
1356 require_action_quiet(isNumber(thresholdRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
1358 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
));
1359 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
));
1360 require_action_quiet(isString(characters
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1363 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordContainsAtLeastNSpecificCharacters
, &atLeastCharactersRef
)){
1364 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
));
1365 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
));
1367 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
));
1369 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
));
1370 require_action_quiet(isNumber(thresholdRef
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex
)errSecBadReq
, NULL
));
1372 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
));
1373 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
));
1374 require_action_quiet(isString(characters
), fail
, tempError
= CFErrorCreate(kCFAllocatorDefault
, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex
)errSecBadReq
, NULL
));
1377 if(CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
, &identicalRef
)){
1378 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
));
1379 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
));
1386 if (tempError
!= NULL
) {
1388 *error
= CFRetainSafe(tempError
);
1392 CFReleaseNull(tempError
);
1397 static bool doesFinalPasswordPass(bool isSimple
, CFStringRef password
, CFDictionaryRef requirements
){
1399 CFTypeRef characters
, identicalRef
= NULL
, NRef
= NULL
, endWith
= NULL
, startWith
= NULL
, atLeastCharacters
= NULL
, atMostCharacters
= NULL
;
1401 CFIndex N
, identicalCount
= 0;
1402 CFArrayRef requiredCharacterSet
= (CFArrayRef
)CFDictionaryGetValue(requirements
, kSecRequiredCharacterSetsKey
);
1404 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordCantEndWithChars
, &endWith
))
1407 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordCantStartWithChars
, &startWith
))
1410 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsAtLeastNSpecificCharacters
, &atLeastCharacters
))
1411 atLeastCharacters
= NULL
;
1413 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsNoMoreThanNSpecificCharacters
, &atMostCharacters
))
1414 atMostCharacters
= NULL
;
1416 if(!CFDictionaryGetValueIfPresent(requirements
, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters
, &identicalRef
))
1417 identicalRef
= NULL
;
1419 CFNumberGetValue((CFNumberRef
)identicalRef
, kCFNumberSInt64Type
, &valuePtr
);
1420 identicalCount
= (long)valuePtr
;
1424 if(!doesPasswordEndWith(password
, endWith
))
1427 if(startWith
!= NULL
){
1428 if(!doesPasswordStartWith(password
, startWith
))
1431 if(atLeastCharacters
!= NULL
){
1432 NRef
= CFDictionaryGetValue(atLeastCharacters
, kSecPasswordCharacterCount
);
1433 characters
= CFDictionaryGetValue(atLeastCharacters
, kSecPasswordCharacters
);
1434 CFNumberGetValue((CFNumberRef
)NRef
, kCFNumberSInt64Type
, &valuePtr
);
1436 if(!passwordContainsAtLeastNCharacters(password
, characters
, N
))
1439 if(atMostCharacters
!= NULL
){
1440 NRef
= CFDictionaryGetValue(atMostCharacters
, kSecPasswordCharacterCount
);
1441 characters
= CFDictionaryGetValue(atMostCharacters
, kSecPasswordCharacters
);
1442 CFNumberGetValue((CFNumberRef
)NRef
, kCFNumberSInt64Type
, &valuePtr
);
1444 if(!passwordContainsLessThanNCharacters(password
, characters
, N
))
1447 if(identicalRef
!= NULL
){
1448 if(!passwordContainsLessThanNIdenticalCharacters(password
, identicalCount
))
1451 if (!passwordContainsRequiredCharacters(password
, requiredCharacterSet
))
1454 if(true == SecPasswordIsPasswordWeak2(isSimple
, password
))
1461 CreateChecksum(SecPasswordType type
, CFStringRef password
, CFIndex length
, CFStringRef allowedChars
)
1463 if (type
!= kSecPasswordTypeiCloudRecoveryKey
)
1466 CFMutableStringRef checksum
= NULL
;
1467 uint8_t digest
[CCSHA256_OUTPUT_SIZE
];
1468 if (length
> (CFIndex
)sizeof(digest
))
1471 CFDataRef data
= CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), password
, kCFStringEncodingUTF8
, 0);
1475 ccdigest(ccsha256_di(), CFDataGetLength(data
), CFDataGetBytePtr(data
), digest
);
1476 CFReleaseNull(data
);
1478 CFIndex allowedCharLength
= CFStringGetLength(allowedChars
);
1480 checksum
= CFStringCreateMutable(SecCFAllocatorZeroize(), 0);
1481 for (CFIndex n
= 0; n
< length
; n
++) {
1482 CFIndex selection
= digest
[n
] % allowedCharLength
;
1483 UniChar c
= CFStringGetCharacterAtIndex(allowedChars
, selection
);
1484 CFStringAppendCharacters(checksum
, &c
, 1);
1490 //entry point into password generation
1491 CF_RETURNS_RETAINED CFStringRef
SecPasswordGenerate(SecPasswordType type
, CFErrorRef
*error
, CFDictionaryRef passwordRequirements
){
1492 bool check
= false, isSimple
= false;
1493 CFTypeRef separator
= NULL
, defaults
= NULL
, groupSizeRef
= NULL
, numberOfGroupsRef
= NULL
;
1494 CFDictionaryRef properlyFormattedRequirements
= NULL
;
1495 CFErrorRef localError
= NULL
;
1496 uint64_t valuePtr
, groupSize
= 0, numberOfGroups
, checksumChars
= 0;
1497 CFNumberRef numberOfRequiredRandomCharacters
, checksumCharacters
;
1498 CFIndex requiredCharactersSize
= 0;
1499 CFStringRef randomCharacters
= NULL
, password
= NULL
, allowedChars
= NULL
;
1500 CFMutableStringRef finalPassword
= NULL
;
1502 if(type
== kSecPasswordTypePIN
)
1506 check
= isDictionaryFormattedProperly(type
, passwordRequirements
, &localError
);
1507 require_quiet(check
!= false, fail
);
1509 //should we generate defaults?
1510 if(passwordRequirements
== NULL
|| (CFDictionaryGetValueIfPresent(passwordRequirements
, kSecPasswordDefaultForType
, &defaults
) && isString(defaults
) == true && 0 == CFStringCompare(defaults
, CFSTR("true"), 0) ))
1511 properlyFormattedRequirements
= passwordGenerateCreateDefaultParametersDictionary(type
, passwordRequirements
);
1513 properlyFormattedRequirements
= passwordGenerationCreateParametersDictionary(type
, passwordRequirements
);
1515 require_quiet(localError
== NULL
&& properlyFormattedRequirements
!= NULL
, fail
);
1517 numberOfRequiredRandomCharacters
= (CFNumberRef
)CFDictionaryGetValue(properlyFormattedRequirements
, kSecNumberOfRequiredRandomCharactersKey
);
1518 if (isNumber(numberOfRequiredRandomCharacters
) && CFNumberGetValue(numberOfRequiredRandomCharacters
, kCFNumberSInt64Type
, &valuePtr
))
1519 requiredCharactersSize
= (long)valuePtr
;
1521 checksumCharacters
= (CFNumberRef
)CFDictionaryGetValue(properlyFormattedRequirements
, kSecNumberOfChecksumCharactersKey
);
1522 if (isNumber(checksumCharacters
) && CFNumberGetValue(checksumCharacters
, kCFNumberSInt64Type
, &valuePtr
))
1523 checksumChars
= (long)valuePtr
;
1526 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements
, kSecPasswordGroupSize
, &groupSizeRef
)){
1527 groupSizeRef
= NULL
;
1530 CFNumberGetValue((CFNumberRef
)groupSizeRef
, kCFNumberSInt64Type
, &groupSize
);
1532 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements
, kSecPasswordNumberOfGroups
, &numberOfGroupsRef
)){
1533 numberOfGroupsRef
= NULL
;
1536 CFNumberGetValue((CFNumberRef
)numberOfGroupsRef
, kCFNumberSInt64Type
, &numberOfGroups
);
1538 require(requiredCharactersSize
, fail
);
1541 allowedChars
= CFDictionaryGetValue(properlyFormattedRequirements
, kSecAllowedCharactersKey
);
1542 require_noerr(getPasswordRandomCharacters(&randomCharacters
, properlyFormattedRequirements
, &requiredCharactersSize
, allowedChars
), fail
);
1544 if(numberOfGroupsRef
&& groupSizeRef
){
1545 finalPassword
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1547 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements
, kSecPasswordSeparator
, &separator
))
1550 if(separator
== NULL
)
1551 separator
= CFSTR("-");
1554 while( i
!= requiredCharactersSize
){
1555 if((i
+ (CFIndex
)groupSize
) < requiredCharactersSize
){
1556 CFStringRef subString
= CFStringCreateWithSubstring(kCFAllocatorDefault
, randomCharacters
, CFRangeMake(i
, (CFIndex
)groupSize
));
1557 CFStringAppend(finalPassword
, subString
);
1558 CFStringAppend(finalPassword
, separator
);
1559 CFReleaseSafe(subString
);
1562 else if((i
+(CFIndex
)groupSize
) == requiredCharactersSize
){
1563 CFStringRef subString
= CFStringCreateWithSubstring(kCFAllocatorDefault
, randomCharacters
, CFRangeMake(i
, (CFIndex
)groupSize
));
1564 CFStringAppend(finalPassword
, subString
);
1565 CFReleaseSafe(subString
);
1569 CFStringRef subString
= CFStringCreateWithSubstring(kCFAllocatorDefault
, randomCharacters
, CFRangeMake(i
, requiredCharactersSize
- i
));
1570 CFStringAppend(finalPassword
, subString
);
1571 CFReleaseSafe(subString
);
1572 i
+=(requiredCharactersSize
- i
);
1575 if (checksumChars
) {
1576 CFStringRef checksum
= CreateChecksum(type
, randomCharacters
, (CFIndex
)checksumChars
, allowedChars
);
1577 CFStringAppend(finalPassword
, checksum
);
1578 CFReleaseNull(checksum
);
1580 password
= CFStringCreateCopy(kCFAllocatorDefault
, finalPassword
);
1581 CFReleaseNull(finalPassword
);
1583 //no fancy formatting
1585 password
= CFStringCreateCopy(kCFAllocatorDefault
, randomCharacters
);
1588 CFReleaseNull(randomCharacters
);
1589 require_quiet(doesFinalPasswordPass(isSimple
, password
, properlyFormattedRequirements
), no_pass
);
1590 CFReleaseNull(properlyFormattedRequirements
);
1594 CFReleaseNull(password
);
1598 if (error
&& localError
) {
1599 *error
= localError
;
1603 CFReleaseSafe(localError
);
1604 CFReleaseNull(properlyFormattedRequirements
);
1608 const char *in_word_set (const char *str
, unsigned int len
){
1609 static const char * wordlist
[] =
1611 "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
1612 "", "", "0103", "", "", "", "", "0123", "", "", "", "", "0303", "", "", "",
1613 "", "", "", "", "0110", "", "1103", "", "", "", "", "1123", "", "", "0000",
1614 "", "1203", "", "0404", "", "", "", "", "1234", "1110", "2015", "2013", "",
1615 "2014", "1010", "2005", "2003", "", "2004", "1210", "0505", "0111", "", "",
1616 "", "2008", "0101", "", "2007", "", "", "", "", "2006", "2010", "1995", "1993",
1617 "", "1994", "2000", "", "1111", "", "", "", "1998", "1101", "", "1997", "",
1618 "0808", "1211", "", "1996", "0102", "", "1201", "", "", "1990", "", "", "",
1619 "", "0202", "", "2011", "", "", "1112", "1958", "2001", "", "1957", "1102",
1620 "", "3333", "", "1956", "1212", "1985", "1983", "", "1984", "1202", "", "0909",
1621 "", "0606", "", "1988", "1991", "", "1987", "2012", "", "", "", "1986", "2002",
1622 "", "", "", "0707", "1980", "", "2009", "", "", "2222", "1965", "1963", "",
1623 "1964", "", "", "2229", "", "", "1992", "1968", "", "", "1967", "", "", "1999",
1624 "", "1966", "", "1975", "1973", "", "1974", "1960", "", "1981", "", "4444",
1625 "", "1978", "", "7465", "1977", "", "", "", "", "1976", "2580", "", "1959",
1626 "", "", "1970", "", "", "", "", "", "", "", "", "", "1982", "", "1961", "",
1627 "", "5252", "", "1989", "", "", "", "", "", "", "", "", "", "", "", "", "",
1628 "", "1971", "", "", "", "", "", "", "", "1962", "", "5683", "", "6666", "",
1629 "", "1969", "", "", "", "", "", "", "", "", "", "", "", "", "1972", "", "",
1630 "", "", "", "", "1979", "", "", "", "7667"
1633 if (len
<= MAX_WORD_LENGTH
&& len
>= MIN_WORD_LENGTH
)
1635 register int key
= pinhash (str
, len
);
1637 if (key
<= MAX_HASH_VALUE
&& key
>= 0)
1639 register const char *s
= wordlist
[key
];
1640 if (*str
== *s
&& !strcmp (str
+ 1, s
+ 1))
1646 CFDictionaryRef
SecPasswordCopyDefaultPasswordLength(SecPasswordType type
, CFErrorRef
*error
){
1648 CFIndex tupleLengthInt
= 0, numOfTuplesInt
= 0;
1649 CFNumberRef tupleLength
= NULL
;
1650 CFNumberRef numOfTuples
= NULL
;
1652 CFMutableDictionaryRef passwordLengthDefaults
= NULL
;
1653 CFDictionaryRef result
= NULL
;
1656 case(kSecPasswordTypeiCloudRecoveryKey
):
1661 case(kSecPasswordTypeiCloudRecovery
):
1666 case(kSecPasswordTypePIN
):
1671 case(kSecPasswordTypeSafari
):
1676 case(kSecPasswordTypeWifi
):
1682 if(SecError(errSecBadReq
, error
, CFSTR("Password type does not exist.")) == false)
1684 secdebug("secpasswordcopydefaultpasswordlength", "could not create error!");
1688 if (tupleLengthInt
!= 0 && numOfTuplesInt
!= 0) {
1689 tupleLength
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberCFIndexType
, &tupleLengthInt
);
1690 numOfTuples
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberCFIndexType
, &numOfTuplesInt
);
1691 passwordLengthDefaults
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
1692 CFDictionaryAddValue(passwordLengthDefaults
, kSecPasswordGroupSize
, tupleLength
);
1693 CFDictionaryAddValue(passwordLengthDefaults
, kSecPasswordNumberOfGroups
, numOfTuples
);
1694 result
= CFDictionaryCreateCopy(kCFAllocatorDefault
, passwordLengthDefaults
);
1697 CFReleaseSafe(tupleLength
);
1698 CFReleaseSafe(numOfTuples
);
1699 CFReleaseSafe(passwordLengthDefaults
);
1704 SecPasswordValidatePasswordFormat(SecPasswordType type
, CFStringRef password
, CFErrorRef
*error
)
1706 CFIndex tupleLengthInt
= 0, numOfTuplesInt
= 0, checkSumChars
= 0;
1707 CFStringRef checksum
= NULL
, madeChecksum
= NULL
, passwordNoChecksum
= NULL
;
1708 CFMutableStringRef randomChars
= NULL
;
1709 CFStringRef allowedChars
= NULL
;
1713 case kSecPasswordTypeiCloudRecoveryKey
:
1717 allowedChars
= defaultiCloudCharacters
;
1719 case kSecPasswordTypeiCloudRecovery
:
1723 case(kSecPasswordTypePIN
):
1728 SecError(errSecBadReq
, error
, CFSTR("Password type does not exist."));
1733 if (numOfTuplesInt
< 1)
1735 if (checkSumChars
> tupleLengthInt
)
1738 CFIndex numberOfChars
= numOfTuplesInt
* tupleLengthInt
+ (numOfTuplesInt
- 1);
1741 * First check expected length
1744 require(CFStringGetLength(password
) == numberOfChars
, out
); /* N groups of M with (N-1) seperator - in-between */
1746 randomChars
= CFStringCreateMutable(SecCFAllocatorZeroize(), 0);
1747 require(randomChars
, out
);
1750 * make sure dash-es are at the expected spots
1753 for (CFIndex n
= 0; n
< numOfTuplesInt
; n
++) {
1755 UniChar c
= CFStringGetCharacterAtIndex(password
, (n
* (tupleLengthInt
+ 1)) - 1);
1756 require(c
== '-', out
);
1758 CFStringRef substr
= CFStringCreateWithSubstring(SecCFAllocatorZeroize(), password
, CFRangeMake(n
* (tupleLengthInt
+ 1), tupleLengthInt
));
1759 CFStringAppend(randomChars
, substr
);
1760 CFReleaseNull(substr
);
1763 if (checkSumChars
) {
1765 * Pull apart and password and checksum
1768 checksum
= CFStringCreateWithSubstring(SecCFAllocatorZeroize(), randomChars
, CFRangeMake((numOfTuplesInt
* tupleLengthInt
) - checkSumChars
, checkSumChars
));
1769 require(checksum
, out
);
1771 passwordNoChecksum
= CFStringCreateWithSubstring(SecCFAllocatorZeroize(), randomChars
, CFRangeMake(0, (numOfTuplesInt
* tupleLengthInt
) - checkSumChars
));
1772 require(passwordNoChecksum
, out
);
1778 madeChecksum
= CreateChecksum(type
, passwordNoChecksum
, checkSumChars
, allowedChars
);
1779 require(madeChecksum
, out
);
1781 require(CFEqual(madeChecksum
, checksum
), out
);
1786 CFReleaseNull(randomChars
);
1787 CFReleaseNull(madeChecksum
);
1788 CFReleaseNull(checksum
);
1789 CFReleaseNull(passwordNoChecksum
);