]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecPasswordGenerate.c
Security-57740.60.18.tar.gz
[apple/security.git] / OSX / sec / Security / SecPasswordGenerate.c
1 /*
2 * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * SecPasswordStrength.c
26 */
27
28 #include <limits.h>
29 #include <CoreFoundation/CoreFoundation.h>
30 #include <Security/SecItem.h>
31 #include <Security/SecBase.h>
32 #include <Security/SecRandom.h>
33 #include "SecPasswordGenerate.h"
34 #include <AssertMacros.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <utilities/SecCFWrappers.h>
38 #include <utilities/SecCFRelease.h>
39 #include <utilities/SecCFError.h>
40 #include <corecrypto/ccdigest.h>
41 #include <corecrypto/ccsha2.h>
42 #include "SecCFAllocator.h"
43
44
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");
51
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");
60
61 CFStringRef kSecPasswordGroupSize = CFSTR("PasswordGroupSize");
62 CFStringRef kSecPasswordNumberOfGroups = CFSTR("PasswordNumberOfGroups");
63 CFStringRef kSecPasswordSeparator = CFSTR("SecPasswordSeparator");
64
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");
71
72 static CFIndex defaultNumberOfRandomCharacters = 20;
73 static CFIndex defaultPINLength = 4;
74 static CFIndex defaultiCloudPasswordLength = 24;
75 static CFIndex defaultWifiPasswordLength = 12;
76
77 static CFStringRef defaultWifiCharacters = CFSTR("abcdefghijklmnopqrstuvwxyz1234567890");
78 static CFStringRef defaultPINCharacters = CFSTR("0123456789");
79 static CFStringRef defaultiCloudCharacters = CFSTR("ABCDEFGHJKLMNPQRSTUVWXYZ23456789");
80 static CFStringRef defaultCharacters = CFSTR("abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789");
81
82 static CFCharacterSetRef uppercaseLetterCharacterSet;
83 static CFCharacterSetRef lowercaseLetterCharacterSet;
84 static CFCharacterSetRef decimalDigitCharacterSet;
85 static CFCharacterSetRef punctuationCharacterSet;
86
87 static CFIndex alphabetSetSize = 26;
88 static CFIndex decimalSetSize = 10;
89 static CFIndex punctuationSetSize = 33;
90 static double entropyStrengthThreshold = 35.0;
91
92 /*
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
96 */
97 const char *in_word_set (const char *str, unsigned int len);
98
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>."
124 #endif
125
126
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 */
133
134 #ifdef __GNUC__
135 __inline
136 #else
137 #ifdef __cplusplus
138 inline
139 #endif
140 #endif
141 static unsigned int pinhash (const char *str, unsigned int len)
142 {
143 static unsigned short asso_values[] =
144 {
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
172 };
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];
174 }
175
176 ///<://problem/29089896> warn against using these common PINs
177 static bool isTopTenSixDigitPasscode(CFStringRef passcode){
178 bool result = false;
179 CFMutableArrayRef topTen = CFArrayCreateMutableForCFTypesWith(kCFAllocatorDefault,
180 CFSTR("030379"),
181 CFSTR("101471"),
182 CFSTR("112233"),
183 CFSTR("123123"),
184 CFSTR("123321"),
185 CFSTR("123654"),
186 CFSTR("147258"),
187 CFSTR("159753"),
188 CFSTR("321654"),
189 CFSTR("520131"),
190 CFSTR("520520"),
191 CFSTR("789456"), NULL);
192
193 for(CFIndex i = 0; i < CFArrayGetCount(topTen); i++){
194 if(CFEqualSafe(passcode, CFArrayGetValueAtIndex(topTen, i))){
195 result = true;
196 break;
197 }
198 }
199 CFReleaseNull(topTen);
200 return result;
201 }
202
203 CFStringRef SecPasswordCreateWithRandomDigits(int n, CFErrorRef *error){
204 int min = n;
205 int max = n;
206
207 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
208 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
209 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
210 punctuationCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetPunctuation);
211
212 CFNumberRef minRef = CFNumberCreate(NULL, kCFNumberIntType, &min);
213 CFNumberRef maxRef = CFNumberCreate(NULL, kCFNumberIntType, &max);
214
215 CFMutableDictionaryRef passwordRequirements = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
216 CFDictionaryAddValue(passwordRequirements, kSecPasswordMinLengthKey, minRef);
217 CFDictionaryAddValue(passwordRequirements, kSecPasswordMaxLengthKey, maxRef);
218 CFStringRef allowedCharacters = CFSTR("0123456789");
219
220 CFDictionaryAddValue(passwordRequirements, kSecPasswordAllowedCharactersKey, allowedCharacters);
221
222 CFStringRef password = SecPasswordGenerate(kSecPasswordTypePIN, error, passwordRequirements);
223
224 CFReleaseNull(minRef);
225 CFReleaseNull(maxRef);
226 CFReleaseNull(passwordRequirements);
227
228 return password;
229 }
230
231
232
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)
236 {
237 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
238 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
239 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
240 punctuationCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetPunctuation);
241
242 bool isNumber = true;
243 char* pin = NULL;
244
245 //length checks
246 if( CFStringGetLength(passcode) < 4 ){
247 return true; //weak password
248 }
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))
252 continue;
253 else {
254 isNumber = false;
255 break;
256 }
257 }
258 //checking to see if it's a 4 digit pin
259 if(isNumber && CFStringGetLength(passcode) == 4){
260
261 pin = CFStringToCString(passcode);
262 if(in_word_set(pin, 4)){
263 free(pin);
264 return true;
265 }
266
267 CFIndex blacklistLength = (CFIndex)sizeof(blacklist)/sizeof(blacklist[0]);
268
269 //not all the same number
270 if(pin[0] == pin[1] == pin[2] == pin[3]){
271 free(pin);
272 return true; //weak password
273 }
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]){
276 free(pin);
277 return true; //weak password
278 }
279 //first two digits not being the same as the last two digits
280 if(pin[0] == pin[2] && pin[1] == pin[3]){
281 free(pin);
282 return true; //weak password
283 }
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){
287 free(pin);
288 return true;
289 }
290 else if ((pin[i] + 1) == pin[i+1])
291 continue;
292 else
293 break;
294 }
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){
298 free(pin);
299 return true;
300 }
301 else if ((pin[i]) == (pin[i+1] +1))
302 continue;
303 else if ((i == 0) && (pin[i] == '0') && (pin[i+1] == '9'))
304 continue;
305 else
306 break;
307 }
308
309 //not in this list
310 for(CFIndex i = 0; i < blacklistLength; i++)
311 {
312 const char* blackCode = blacklist[i];
313 if(0 == strcmp(blackCode, pin))
314 {
315 free(pin);
316 return true; //weak password
317 }
318 }
319 }
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)){
325 free(pin);
326 return true;
327 }
328 else if (pin[i] == pin[i+1])
329 continue;
330 else
331 break;
332 }
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){
336 free(pin);
337 return true;
338 }
339 else if ((pin[i] + 1) == pin[i+1])
340 continue;
341 else
342 break;
343 }
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){
347 free(pin);
348 return true;
349 }
350 else if ((pin[i]) == (pin[i+1] +1))
351 continue;
352 else if ((i == 0) && (pin[i] == '0') && (pin[i+1] == '9'))
353 continue;
354 else
355 break;
356 }
357 }
358 else{ // password is complex, evaluate entropy
359 int u = 0;
360 int l = 0;
361 int d = 0;
362 int p = 0;
363 int characterSet = 0;
364
365 //calculate new entropy
366 for(CFIndex i = 0; i < CFStringGetLength(passcode); i++){
367
368 if( CFStringFindCharacterFromSet(passcode, uppercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
369 u++;
370 continue;
371 }
372 if( CFStringFindCharacterFromSet(passcode, lowercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
373 l++;
374 continue;
375 }
376 if( CFStringFindCharacterFromSet(passcode, decimalDigitCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
377 d++;
378 continue;
379 }
380 if( CFStringFindCharacterFromSet(passcode, punctuationCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
381 p++;
382 continue;
383 }
384
385 }
386 if(u > 0){
387 characterSet += alphabetSetSize;
388 }
389 if(l > 0){
390 characterSet += alphabetSetSize;
391 }
392 if(d > 0){
393 characterSet += decimalSetSize;
394 }
395 if(p > 0){
396 characterSet += punctuationSetSize;
397 }
398
399 double strength = CFStringGetLength(passcode)*log2(characterSet);
400
401 if(strength < entropyStrengthThreshold){
402 return true; //weak
403 }
404 else
405 return false; //strong
406 }
407 if(pin)
408 free(pin);
409
410 return false; //strong password
411
412 }
413
414 static bool SecPasswordIsPasscodeIncrementingOrDecrementingDigits(CFStringRef passcode)
415 {
416 char* pin = CFStringToCString(passcode);
417
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){
421 free(pin);
422 return true;
423 }
424 else if ((pin[i] + 1) == pin[i+1])
425 continue;
426 else
427 break;
428 }
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){
432 free(pin);
433 return true;
434 }
435 else if ((pin[i]) == (pin[i+1] +1))
436 continue;
437 else if ((i == 0) && (pin[i] == '0') && (pin[i+1] == '9'))
438 continue;
439 else
440 break;
441 }
442 free(pin);
443 return false;
444 }
445
446 static bool SecPasswordIsPasswordRepeatingTwoNumbers(CFStringRef passcode){
447 char* pin = CFStringToCString(passcode);
448
449 for(int i = 0; i < CFStringGetLength(passcode); i++)
450 {
451 if(i+2 == CFStringGetLength(passcode)-1){
452 free(pin);
453 return true;
454 }
455 else if(pin[i] == pin[i+2])
456 continue;
457 else
458 break;
459 }
460
461 free(pin);
462 return false;
463 }
464
465 static int SecPasswordNumberOfRepeatedDigits(CFStringRef passcode){
466 int repeating = 1;
467 CFIndex length = CFStringGetLength(passcode);
468 CFNumberRef highest = NULL;
469 CFMutableArrayRef highestRepeatingcount = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
470
471 for(int i = 0; i < length; i++){
472
473 if(i+1 == length){
474 CFNumberRef newRepeatingAddition = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &repeating);
475 CFArrayAppendValue(highestRepeatingcount, newRepeatingAddition);
476 CFReleaseNull(newRepeatingAddition);
477 break;
478 }
479 if(CFStringGetCharacterAtIndex(passcode, i) == CFStringGetCharacterAtIndex(passcode,i+1))
480 repeating++;
481 else{
482 if(repeating != 1)
483 {
484 CFNumberRef newRepeatingAddition = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &repeating);
485 CFArrayAppendValue(highestRepeatingcount, newRepeatingAddition);
486 CFReleaseNull(newRepeatingAddition);
487 }
488 repeating = 1;
489
490 }
491 }
492
493 for(int i =0; i< CFArrayGetCount(highestRepeatingcount); i++){
494 if(i == 0){
495 highest = CFArrayGetValueAtIndex(highestRepeatingcount, i);
496 continue;
497 }
498 else{
499 CFNumberRef competitor = CFArrayGetValueAtIndex(highestRepeatingcount, i);
500 if(CFNumberCompare(competitor, highest, NULL) == kCFCompareGreaterThan){
501 highest = competitor;
502 }
503 }
504 }
505 int finalRepeating = 0;
506 if(highest != NULL)
507 CFNumberGetValue(highest, kCFNumberIntType, &finalRepeating);
508 return finalRepeating;
509 }
510
511 static bool SecPasswordIsPalindrome(CFStringRef passcode){
512 char* pin = CFStringToCString(passcode);
513 long length = CFStringGetLength(passcode);
514 long j = length-1;
515
516 for(int i = 0; i < CFStringGetLength(passcode); i++)
517 {
518 if(length%2 == 1 && i == j){
519 free(pin);
520 return true;
521 }
522 else if(length%2 == 0 && i == j-1){
523 if(pin[i] == pin[j]){
524 free(pin);
525 return true;
526 }
527 else
528 break;
529 }
530 else if(pin[i] == pin[j]){
531 j--;
532 continue;
533 }
534 else
535 break;
536 }
537 free(pin);
538 return false;
539 }
540
541 static bool SecPasswordHasRepeatingGroups(CFStringRef passcode){
542 char* pin = CFStringToCString(passcode);
543
544 for(int i = 0; i < CFStringGetLength(passcode); i++)
545 {
546 if(i+4 == CFStringGetLength(passcode)){
547 if(pin[i] == pin[i+3]){
548 free(pin);
549 return true;
550 }
551 else
552 break;
553 }
554 else if(pin[i] == pin[i+3])
555 continue;
556 else
557 break;
558 }
559
560 free(pin);
561
562 return false;
563 }
564
565 bool SecPasswordIsPasswordWeak2(bool isSimple, CFStringRef passcode)
566 {
567 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
568 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
569 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
570 punctuationCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetPunctuation);
571
572
573 char* pin = NULL;
574
575 //length checks
576 if( CFStringGetLength(passcode) < 4 ){
577 return true; //weak password
578 }
579
580 bool isPasscodeNumber = true;
581 //check to see if passcode is a number
582 for(CFIndex i = 0; i < CFStringGetLength(passcode); i++){
583 if( CFStringFindCharacterFromSet(passcode, decimalDigitCharacterSet, CFRangeMake(i,1), 0, NULL))
584 continue;
585 else {
586 isPasscodeNumber = false;
587 break;
588 }
589 }
590
591 if(isSimple){
592 //checking to see if it's a 4 digit pin
593 if(isPasscodeNumber && CFStringGetLength(passcode) == 4){
594
595 pin = CFStringToCString(passcode);
596 if(in_word_set(pin, 4)){
597 free(pin);
598 return true;
599 }
600
601 CFIndex blacklistLength = (CFIndex)sizeof(blacklist)/sizeof(blacklist[0]);
602
603 //not all the same number
604 if(pin[0] == pin[1] == pin[2] == pin[3]){
605 free(pin);
606 return true; //weak password
607 }
608 //first two digits being the same and the last two digits being the same
609 if ( pin[0] == pin[1] && pin[2] == pin[3]){
610 free(pin);
611 return true; //weak password
612 }
613 //first two digits not being the same as the last two digits
614 if(pin[0] == pin[2] && pin[1] == pin[3]){
615 free(pin);
616 return true; //weak password
617 }
618 //not in this list
619 for(CFIndex i = 0; i < blacklistLength; i++)
620 {
621 const char* blackCode = blacklist[i];
622 if(0 == strcmp(blackCode, pin))
623 {
624 free(pin);
625 return true; //weak password
626 }
627 }
628 }
629 else if(isPasscodeNumber && CFStringGetLength(passcode) == 6){
630 pin = CFStringToCString(passcode);
631
632 //not all the same number
633 for(int i = 0; i < CFStringGetLength(passcode); i++){
634 if(i == CFStringGetLength(passcode)-1){
635 free(pin);
636 return true;
637 }
638 else if ((pin[i]) == pin[i+1])
639 continue;
640 else
641 break;
642 }
643 //not in the top 10
644 if(isTopTenSixDigitPasscode(passcode)){
645 free(pin);
646 return true;
647 }
648 //palindrome test
649 if(SecPasswordIsPalindrome(passcode)){
650 free(pin);
651 return true;
652 }
653
654 //2 identical groups
655 if(SecPasswordHasRepeatingGroups(passcode)){
656 free(pin);
657 return true;
658 }
659 //passcode is incrementing ex 123456 or 654321
660 if(SecPasswordIsPasscodeIncrementingOrDecrementingDigits(passcode)) {
661 free(pin);
662 return true;
663 }
664 //passcode does not consist of 2 repeating digits
665 if(SecPasswordIsPasswordRepeatingTwoNumbers(passcode)){
666 free(pin);
667 return true;
668 }
669 }
670 else//should be a 4 or 6digit number
671 return false;
672 }
673 else if(isPasscodeNumber && !isSimple){ //dealing with a complex numeric passcode
674 pin = CFStringToCString(passcode);
675 //check if PIN is all the same number
676 int repeatingDigits = SecPasswordNumberOfRepeatedDigits(passcode);
677 if(repeatingDigits >= (CFStringGetLength(passcode)/2)){
678 free(pin);
679 return true;
680 }
681 //palindrome test
682 if(SecPasswordIsPalindrome(passcode)){
683 free(pin);
684 return true;
685 }
686 //not in the top 10
687 if(isTopTenSixDigitPasscode(passcode)){
688 free(pin);
689 return true;
690 }
691 //2 identical groups
692 if(SecPasswordHasRepeatingGroups(passcode) && CFStringGetLength(passcode) >= 6){
693 free(pin);
694 return true;
695 }
696
697 if(SecPasswordIsPasscodeIncrementingOrDecrementingDigits(passcode)) {
698 free(pin);
699 return true;
700 }
701 if(SecPasswordIsPasswordRepeatingTwoNumbers(passcode)){
702 free(pin);
703 return true;
704 }
705
706 }
707 else{ // password is complex, evaluate entropy
708 int u = 0;
709 int l = 0;
710 int d = 0;
711 int p = 0;
712 int characterSet = 0;
713
714 //calculate new entropy
715 for(CFIndex i = 0; i < CFStringGetLength(passcode); i++){
716
717 if( CFStringFindCharacterFromSet(passcode, uppercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
718 u++;
719 continue;
720 }
721 if( CFStringFindCharacterFromSet(passcode, lowercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
722 l++;
723 continue;
724 }
725 if( CFStringFindCharacterFromSet(passcode, decimalDigitCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
726 d++;
727 continue;
728 }
729 if( CFStringFindCharacterFromSet(passcode, punctuationCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
730 p++;
731 continue;
732 }
733
734 }
735 if(u > 0){
736 characterSet += alphabetSetSize;
737 }
738 if(l > 0){
739 characterSet += alphabetSetSize;
740 }
741 if(d > 0){
742 characterSet += decimalSetSize;
743 }
744 if(p > 0){
745 characterSet += punctuationSetSize;
746 }
747
748 double strength = CFStringGetLength(passcode)*log2(characterSet);
749
750 if(strength < entropyStrengthThreshold){
751 return true; //weak
752 }
753 else
754 return false; //strong
755 }
756 if(pin)
757 free(pin);
758
759 return false; //strong password
760
761 }
762
763 static void getUniformRandomNumbers(uint8_t* buffer, size_t numberOfDesiredNumbers, uint8_t upperBound)
764 {
765
766 // The values returned by SecRandomCopyBytes are uniformly distributed in the range [0, 255]. If we try to map
767 // these values onto a smaller range using modulo we will introduce a bias towards lower numbers in situations
768 // where our smaller range doesn’t evenly divide in to [0, 255]. For example, with the desired range of [0, 54]
769 // the ranges 0..54, 55..109, 110..164, and 165..219 are uniformly distributed, but the range 220..255 modulo 55
770 // is only distributed over [0, 35], giving significant bias to these lower values. So, we ignore random numbers
771 // that would introduce this bias.
772 uint8_t limitAvoidingModuloBias = UCHAR_MAX - (UCHAR_MAX % upperBound);
773
774 for (size_t numberOfAcceptedNumbers = 0; numberOfAcceptedNumbers < numberOfDesiredNumbers; ) {
775 if (SecRandomCopyBytes(kSecRandomDefault, numberOfDesiredNumbers - numberOfAcceptedNumbers, buffer + numberOfAcceptedNumbers) == -1)
776 continue;
777 for (size_t i = numberOfAcceptedNumbers; i < numberOfDesiredNumbers; ++i) {
778 if (buffer[i] < limitAvoidingModuloBias)
779 buffer[numberOfAcceptedNumbers++] = buffer[i] % upperBound;
780 }
781 }
782 }
783
784 static bool passwordContainsRequiredCharacters(CFStringRef password, CFArrayRef requiredCharacterSets)
785 {
786 CFCharacterSetRef characterSet;
787
788 for (CFIndex i = 0; i< CFArrayGetCount(requiredCharacterSets); i++) {
789 characterSet = CFArrayGetValueAtIndex(requiredCharacterSets, i);
790 CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(password));
791 require_quiet(CFStringFindCharacterFromSet(password, characterSet, rangeToSearch, 0, NULL), fail);
792 }
793 return true;
794
795 fail:
796 return false;
797
798 }
799
800 static bool passwordContainsLessThanNIdenticalCharacters(CFStringRef password, CFIndex identicalCount)
801 {
802 unsigned char Char, nextChar;
803 int repeating = 0;
804
805 for(CFIndex i = 0; i < CFStringGetLength(password); i++){
806 Char = CFStringGetCharacterAtIndex(password, i);
807 for(CFIndex j = i; j< CFStringGetLength(password); j++){
808 nextChar = CFStringGetCharacterAtIndex(password, j);
809 require_quiet(repeating <= identicalCount, fail);
810 if(Char == nextChar){
811 repeating++;
812 }else{
813 repeating = 0;
814 break;
815 }
816 }
817 }
818 return true;
819 fail:
820 return false;
821 }
822
823 static bool passwordContainsAtLeastNCharacters(CFStringRef password, CFStringRef characters, CFIndex N)
824 {
825 CFCharacterSetRef characterSet = NULL;
826 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, characters);
827 CFIndex counter = 0;
828
829 for(CFIndex i = 0; i < CFStringGetLength(password); i++){
830 if(CFStringFindCharacterFromSet(password, characterSet, CFRangeMake(i, 1), 0, NULL))
831 counter++;
832 }
833 CFReleaseNull(characterSet);
834 if(counter < N)
835 return false;
836 else
837 return true;
838 }
839
840 static bool passwordContainsLessThanNCharacters(CFStringRef password, CFStringRef characters, CFIndex N)
841 {
842 CFCharacterSetRef characterSet = NULL;
843 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, characters);
844 CFIndex counter = 0;
845
846 for(CFIndex i = 0; i < CFStringGetLength(password); i++){
847 if(CFStringFindCharacterFromSet(password, characterSet, CFRangeMake(i, 1), 0, NULL))
848 counter++;
849 }
850 CFReleaseNull(characterSet);
851 if(counter > N)
852 return false;
853 else
854 return true;
855 }
856
857 static bool passwordDoesNotContainCharacters(CFStringRef password, CFStringRef prohibitedCharacters)
858 {
859 CFCharacterSetRef characterSet = NULL;
860 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
861 CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(password));
862
863 require_quiet(!CFStringFindCharacterFromSet(password, characterSet, rangeToSearch, 0, NULL), fail);
864 CFReleaseNull(characterSet);
865 return true;
866 fail:
867 CFReleaseNull(characterSet);
868 return false;
869 }
870
871 static void getPasswordRandomCharacters(CFStringRef *returned, CFDictionaryRef requirements, CFIndex *numberOfRandomCharacters, CFStringRef allowedCharacters)
872 {
873 uint8_t randomNumbers[*numberOfRandomCharacters];
874 unsigned char randomCharacters[*numberOfRandomCharacters];
875 getUniformRandomNumbers(randomNumbers, *numberOfRandomCharacters, CFStringGetLength(allowedCharacters));
876
877 CFTypeRef prohibitedCharacters = NULL;
878 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters))
879 prohibitedCharacters = NULL;
880
881 //it's faster for long characters to check each character produced for these cases
882 for (CFIndex i = 0; i < *numberOfRandomCharacters; ++i){
883 //check prohibited characters
884 UniChar randomChar[1];
885 randomChar[0] = CFStringGetCharacterAtIndex(allowedCharacters, randomNumbers[i]);
886 if (prohibitedCharacters != NULL)
887 {
888 CFStringRef temp = CFStringCreateWithCharacters(kCFAllocatorDefault, randomChar, 1);
889 bool pwdncc = passwordDoesNotContainCharacters(temp, prohibitedCharacters);
890 CFReleaseSafe(temp);
891 if (!pwdncc) {
892 //change up the random numbers so we don't get the same index into allowed
893 getUniformRandomNumbers(randomNumbers, *numberOfRandomCharacters, CFStringGetLength(allowedCharacters));
894 i--;
895 continue;
896 }
897 }
898 randomCharacters[i] = (unsigned char)randomChar[0];
899 }
900
901 *returned = CFStringCreateWithBytes(kCFAllocatorDefault, randomCharacters, *numberOfRandomCharacters, kCFStringEncodingUTF8, false);
902 }
903
904 static bool doesPasswordEndWith(CFStringRef password, CFStringRef prohibitedCharacters)
905 {
906 CFCharacterSetRef characterSet = NULL;
907 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
908
909 CFRange rangeToSearch = CFRangeMake(CFStringGetLength(password) - CFStringGetLength(prohibitedCharacters), CFStringGetLength(prohibitedCharacters));
910 require_quiet(0 == CFStringCompareWithOptions(password, prohibitedCharacters, rangeToSearch, 0), fail);
911 CFReleaseNull(characterSet);
912 return false;
913 fail:
914 CFReleaseNull(characterSet);
915 return true;
916 }
917
918 static bool doesPasswordStartWith(CFStringRef password, CFStringRef prohibitedCharacters)
919 {
920 CFCharacterSetRef characterSet = NULL;
921 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
922
923 CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(prohibitedCharacters));
924 require_quiet(0 == CFStringCompareWithOptions(password, prohibitedCharacters, rangeToSearch, 0), fail);
925 CFReleaseNull(characterSet);
926 return false; //does not start with prohibitedCharacters
927 fail:
928 CFReleaseNull(characterSet);
929 return true;
930 }
931
932 static CFDictionaryRef passwordGenerateCreateDefaultParametersDictionary(SecPasswordType type, CFDictionaryRef requirements){
933
934 CFMutableArrayRef requiredCharacterSets = NULL;
935 CFNumberRef numReqChars = NULL, checksumChars = NULL;
936 CFStringRef defaultPasswordFormat = NULL;
937 requiredCharacterSets = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
938 defaultPasswordFormat = CFSTR("true");
939 CFTypeRef groupSizeRef = NULL, numberOfGroupsRef = NULL;
940 CFIndex groupSize, numberOfGroups, checksumSize = 0;
941 CFDictionaryRef returned = NULL;
942
943 switch(type){
944 case kSecPasswordTypeiCloudRecoveryKey:
945 groupSize = 4;
946 numberOfGroups = 7;
947 checksumSize = 2;
948 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, (groupSize * numberOfGroups) - checksumSize);
949 checksumChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, checksumSize);
950 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
951 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
952
953 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
954 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
955 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
956 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
957 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
958 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
959 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
960 kSecNumberOfChecksumCharactersKey, checksumChars,
961 kSecAllowedCharactersKey, defaultiCloudCharacters,
962 kSecRequiredCharacterSetsKey, requiredCharacterSets,
963 kSecPasswordGroupSize, groupSizeRef,
964 kSecPasswordNumberOfGroups, numberOfGroupsRef,
965 NULL);
966 break;
967 case(kSecPasswordTypeiCloudRecovery):
968 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultiCloudPasswordLength);
969 groupSize = 4;
970 numberOfGroups = 6;
971 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
972 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
973
974 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
975 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
976 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
977 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
978 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
979 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
980 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
981 kSecAllowedCharactersKey, defaultiCloudCharacters,
982 kSecRequiredCharacterSetsKey, requiredCharacterSets,
983 kSecPasswordGroupSize, groupSizeRef,
984 kSecPasswordNumberOfGroups, numberOfGroupsRef,
985 NULL);
986 break;
987
988 case(kSecPasswordTypePIN):
989 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultPINLength);
990 groupSize = 4;
991 numberOfGroups = 1;
992 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
993 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
994
995 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
996 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
997 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
998 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
999 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
1000 kSecAllowedCharactersKey, defaultPINCharacters,
1001 kSecRequiredCharacterSetsKey, requiredCharacterSets,
1002 kSecPasswordGroupSize, groupSizeRef,
1003 kSecPasswordNumberOfGroups, numberOfGroupsRef,
1004 NULL);
1005 break;
1006
1007 case(kSecPasswordTypeWifi):
1008 groupSize = 4;
1009 numberOfGroups = 3;
1010 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
1011 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
1012
1013 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
1014 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
1015
1016 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultWifiPasswordLength);
1017 CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet);
1018 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
1019 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1020 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
1021 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
1022 kSecAllowedCharactersKey, defaultWifiCharacters,
1023 kSecRequiredCharacterSetsKey, requiredCharacterSets,
1024 kSecPasswordGroupSize, groupSizeRef,
1025 kSecPasswordNumberOfGroups, numberOfGroupsRef,
1026 NULL);
1027 break;
1028
1029 default:
1030 groupSize = 4;
1031 numberOfGroups = 6;
1032 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
1033 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
1034 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
1035 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
1036 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
1037 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
1038 CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet);
1039 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
1040
1041 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultNumberOfRandomCharacters);
1042 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1043 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
1044 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
1045 kSecAllowedCharactersKey, defaultCharacters,
1046 kSecRequiredCharacterSetsKey, requiredCharacterSets,
1047 kSecPasswordGroupSize, groupSizeRef,
1048 kSecPasswordNumberOfGroups, numberOfGroupsRef,
1049 NULL);
1050
1051
1052
1053 break;
1054 }
1055
1056 CFReleaseNull(numReqChars);
1057 CFReleaseNull(requiredCharacterSets);
1058 CFReleaseNull(groupSizeRef);
1059 CFReleaseNull(numberOfGroupsRef);
1060 CFReleaseNull(checksumChars);
1061 return returned;
1062 }
1063 static CFDictionaryRef passwordGenerationCreateParametersDictionary(SecPasswordType type, CFDictionaryRef requirements)
1064 {
1065 CFMutableArrayRef requiredCharacterSets = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1066 CFNumberRef numReqChars = NULL;
1067 CFIndex numberOfRequiredRandomCharacters;
1068 CFStringRef allowedCharacters = NULL, useDefaultPasswordFormat = NULL;
1069 uint64_t valuePtr;
1070 CFTypeRef prohibitedCharacters = NULL, endWith = NULL, startWith = NULL,
1071 groupSizeRef = NULL, numberOfGroupsRef = NULL, separatorRef = NULL,
1072 atMostCharactersRef = NULL,atLeastCharactersRef = NULL, identicalRef = NULL;
1073
1074 CFNumberRef min = (CFNumberRef)CFDictionaryGetValue(requirements, kSecPasswordMinLengthKey);
1075 CFNumberRef max = (CFNumberRef)CFDictionaryGetValue(requirements, kSecPasswordMaxLengthKey);
1076
1077 CFNumberGetValue(min, kCFNumberSInt64Type, &valuePtr);
1078 CFIndex minPasswordLength = (long)valuePtr;
1079 CFNumberGetValue(max, kCFNumberSInt64Type, &valuePtr);
1080 CFIndex maxPasswordLength = (long)valuePtr;
1081
1082 // If requirements allow, we will generate the password in default format.
1083 useDefaultPasswordFormat = CFSTR("true");
1084 numberOfRequiredRandomCharacters = defaultNumberOfRandomCharacters;
1085
1086 if(type == kSecPasswordTypePIN)
1087 {
1088 if( maxPasswordLength && minPasswordLength )
1089 numberOfRequiredRandomCharacters = maxPasswordLength;
1090 else if( !maxPasswordLength && minPasswordLength )
1091 numberOfRequiredRandomCharacters = minPasswordLength;
1092 else if( !minPasswordLength && maxPasswordLength )
1093 numberOfRequiredRandomCharacters = maxPasswordLength;
1094 else
1095 numberOfRequiredRandomCharacters = defaultPINLength;
1096
1097 allowedCharacters = CFSTR("0123456789");
1098 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
1099 useDefaultPasswordFormat = CFSTR("false");
1100 }
1101 else{
1102 CFArrayRef requiredCharactersArray = NULL;
1103
1104 if (minPasswordLength && minPasswordLength > defaultNumberOfRandomCharacters) {
1105 useDefaultPasswordFormat = CFSTR("false");
1106 numberOfRequiredRandomCharacters = minPasswordLength;
1107 }
1108 if (maxPasswordLength && maxPasswordLength < defaultNumberOfRandomCharacters) {
1109 useDefaultPasswordFormat = CFSTR("false");
1110 numberOfRequiredRandomCharacters = maxPasswordLength;
1111 }
1112 if (maxPasswordLength && minPasswordLength && maxPasswordLength == minPasswordLength && maxPasswordLength != defaultNumberOfRandomCharacters){
1113 useDefaultPasswordFormat = CFSTR("false");
1114 numberOfRequiredRandomCharacters = maxPasswordLength;
1115 }
1116 allowedCharacters = (CFStringRef)CFRetainSafe(CFDictionaryGetValue(requirements, kSecPasswordAllowedCharactersKey));
1117 requiredCharactersArray = (CFArrayRef)CFDictionaryGetValue(requirements, kSecPasswordRequiredCharactersKey);
1118
1119 if (requiredCharactersArray) {
1120 for (CFIndex i = 0; i < CFArrayGetCount(requiredCharactersArray); i++){
1121 CFCharacterSetRef stringWithRequiredCharacters = CFArrayGetValueAtIndex(requiredCharactersArray, i);
1122 if(stringWithRequiredCharacters && CFStringFindCharacterFromSet(allowedCharacters, stringWithRequiredCharacters, CFRangeMake(0, CFStringGetLength(allowedCharacters)), 0, NULL)){
1123 CFArrayAppendValue(requiredCharacterSets, stringWithRequiredCharacters);
1124 }
1125 }
1126 } else{
1127 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
1128 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
1129 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
1130 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
1131 CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet);
1132 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
1133 }
1134 }
1135
1136 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters))
1137 prohibitedCharacters = NULL;
1138
1139 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantEndWithChars, &endWith))
1140 endWith = NULL;
1141
1142 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantStartWithChars, &startWith))
1143 startWith = NULL;
1144
1145 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordGroupSize, &groupSizeRef))
1146 groupSizeRef = NULL;
1147
1148 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef))
1149 numberOfGroupsRef = NULL;
1150
1151 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordSeparator, &separatorRef))
1152 separatorRef = NULL;
1153
1154 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharactersRef))
1155 atMostCharactersRef = NULL;
1156
1157 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharactersRef))
1158 atLeastCharactersRef = NULL;
1159
1160 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef))
1161 identicalRef = NULL;
1162
1163 if (allowedCharacters) {
1164 if( false == CFStringFindWithOptions(allowedCharacters, CFSTR("-"), CFRangeMake(0, CFStringGetLength(allowedCharacters)), kCFCompareCaseInsensitive, NULL))
1165 useDefaultPasswordFormat = CFSTR("false");
1166 } else
1167 allowedCharacters = CFRetainSafe(defaultCharacters);
1168
1169 // In default password format, we use dashes only as separators, not as symbols you can encounter at a random position.
1170 if (useDefaultPasswordFormat == CFSTR("false")){
1171 CFMutableStringRef mutatedAllowedCharacters = CFStringCreateMutableCopy(kCFAllocatorDefault, CFStringGetLength(allowedCharacters), allowedCharacters);
1172 CFStringFindAndReplace (mutatedAllowedCharacters, CFSTR("-"), CFSTR(""), CFRangeMake(0, CFStringGetLength(allowedCharacters)),kCFCompareCaseInsensitive);
1173 CFReleaseSafe(allowedCharacters);
1174 allowedCharacters = mutatedAllowedCharacters;
1175 }
1176
1177 if (CFArrayGetCount(requiredCharacterSets) > numberOfRequiredRandomCharacters) {
1178 CFReleaseNull(requiredCharacterSets);
1179 requiredCharacterSets = NULL;
1180 }
1181 //create new CFDictionary
1182 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, numberOfRequiredRandomCharacters);
1183 CFMutableDictionaryRef updatedConstraints = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1184 CFDictionaryAddValue(updatedConstraints, kSecUseDefaultPasswordFormatKey, useDefaultPasswordFormat);
1185 CFDictionarySetValue(updatedConstraints, kSecNumberOfRequiredRandomCharactersKey, numReqChars);
1186 CFDictionaryAddValue(updatedConstraints, kSecAllowedCharactersKey, allowedCharacters);
1187 if(requiredCharacterSets)
1188 CFDictionaryAddValue(updatedConstraints, kSecRequiredCharacterSetsKey, requiredCharacterSets);
1189
1190 //add the prohibited characters string if it exists to the new dictionary
1191 if(prohibitedCharacters)
1192 CFDictionaryAddValue(updatedConstraints, kSecPasswordDisallowedCharacters, prohibitedCharacters);
1193
1194 //add the characters the password can't end with if it exists to the new dictionary
1195 if(endWith)
1196 CFDictionaryAddValue(updatedConstraints, kSecPasswordCantEndWithChars, endWith);
1197
1198 //add the characters the password can't start with if it exists to the new dictionary
1199 if(startWith)
1200 CFDictionaryAddValue(updatedConstraints, kSecPasswordCantStartWithChars, startWith);
1201
1202 if(groupSizeRef)
1203 CFDictionaryAddValue(updatedConstraints, kSecPasswordGroupSize, groupSizeRef);
1204
1205 if(numberOfGroupsRef)
1206 CFDictionaryAddValue(updatedConstraints, kSecPasswordNumberOfGroups, numberOfGroupsRef);
1207
1208 if(separatorRef)
1209 CFDictionaryAddValue(updatedConstraints, kSecPasswordSeparator, separatorRef);
1210
1211 if(atMostCharactersRef)
1212 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsNoMoreThanNSpecificCharacters, atMostCharactersRef);
1213
1214 if(atLeastCharactersRef)
1215 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsAtLeastNSpecificCharacters, atLeastCharactersRef);
1216
1217 if(identicalRef)
1218 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, identicalRef);
1219
1220 CFReleaseNull(useDefaultPasswordFormat);
1221 CFReleaseNull(numReqChars);
1222 CFReleaseNull(allowedCharacters);
1223 CFReleaseNull(requiredCharacterSets);
1224
1225 return updatedConstraints;
1226 }
1227
1228 static bool isDictionaryFormattedProperly(SecPasswordType type, CFDictionaryRef passwordRequirements, CFErrorRef *error){
1229
1230 CFTypeRef defaults = NULL;
1231 CFErrorRef tempError = NULL;
1232 if(passwordRequirements == NULL){
1233 return true;
1234 }
1235
1236 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) ){
1237 if(isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0)){
1238 return true;
1239 }
1240 }
1241 //only need to check max and min pin length formatting
1242 if(type == kSecPasswordTypePIN){
1243 CFTypeRef minTest = NULL, maxTest = NULL;
1244 uint64_t valuePtr;
1245 CFIndex minPasswordLength = 0, maxPasswordLength= 0;
1246
1247 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) ){
1248 if(isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0)){
1249 return true;
1250 }
1251 }
1252 //check if the values exist!
1253 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMaxLengthKey, &maxTest) ){
1254 require_action_quiet(isNull(maxTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL));
1255 require_action_quiet(isNumber(maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
1256
1257 }
1258 if (CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMinLengthKey, &minTest) ){
1259 require_action_quiet(isNull(minTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL));
1260 require_action_quiet(isNumber(minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
1261 }
1262 //check if the values exist!
1263 if(maxTest){
1264 CFNumberRef max = (CFNumberRef)maxTest;
1265 CFNumberGetValue(max, kCFNumberSInt64Type, &valuePtr);
1266 maxPasswordLength = (long)valuePtr;
1267 }
1268 if(minTest){
1269 CFNumberRef min = (CFNumberRef)minTest;
1270 CFNumberGetValue(min, kCFNumberSInt64Type, &valuePtr);
1271 minPasswordLength = (long)valuePtr;
1272 }
1273 //make sure min and max make sense respective to each other and that they aren't less than 4 digits.
1274 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));
1275 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));
1276 }
1277 else{
1278 CFTypeRef allowedTest, maxTest, minTest, requiredTest, prohibitedCharacters, endWith, startWith,
1279 groupSizeRef, numberOfGroupsRef, separatorRef, atMostCharactersRef,
1280 atLeastCharactersRef, thresholdRef, identicalRef, characters;
1281 uint64_t valuePtr;
1282
1283 //check if the values exist!
1284 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));
1285 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMaxLengthKey, &maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL));
1286 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMinLengthKey, &minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL));
1287 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));
1288
1289 //check if values are null?
1290 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));
1291 require_action_quiet(isNull(maxTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL));
1292 require_action_quiet(isNull(minTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL));
1293 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));
1294
1295 //check if the values are correct
1296 require_action_quiet(isString(allowedTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's allowed characters must be a CFStringRef"), (CFIndex)errSecBadReq, NULL));
1297 require_action_quiet(isNumber(maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
1298 require_action_quiet(isNumber(minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
1299 require_action_quiet(isArray(requiredTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's required characters must be an array of CFCharacterSetRefs"), (CFIndex)errSecBadReq, NULL));
1300
1301 CFNumberGetValue(minTest, kCFNumberSInt64Type, &valuePtr);
1302 CFIndex minPasswordLength = (long)valuePtr;
1303 CFNumberGetValue(maxTest, kCFNumberSInt64Type, &valuePtr);
1304 CFIndex maxPasswordLength = (long)valuePtr;
1305
1306 require_action_quiet(minPasswordLength <= maxPasswordLength, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex)errSecBadReq, NULL));
1307
1308 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));
1309 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));
1310
1311 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters)){
1312 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));
1313 require_action_quiet(isString(prohibitedCharacters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Disallowed Characters dictionary parameter is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1314 }
1315 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordCantEndWithChars, &endWith)){
1316 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));
1317 require_action_quiet(isString(endWith), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'EndWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1318 }
1319 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordCantStartWithChars, &startWith)){
1320 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));
1321 require_action_quiet(isString(startWith), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'StartWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1322 }
1323 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordGroupSize, &groupSizeRef)){
1324 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));
1325 require_action_quiet(isNumber(groupSizeRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'groupsize' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1326 }
1327 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef)){
1328 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));
1329 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));
1330 }
1331 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordSeparator, &separatorRef)){
1332 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));
1333 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));
1334 }
1335
1336 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharactersRef)){
1337 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));
1338 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));
1339
1340 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));
1341 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));
1342 require_action_quiet(isNumber(thresholdRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1343
1344 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));
1345 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));
1346 require_action_quiet(isString(characters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1347 }
1348
1349 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharactersRef)){
1350 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));
1351 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));
1352
1353 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));
1354
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));
1357
1358 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));
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));
1361 }
1362
1363 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef)){
1364 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));
1365 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));
1366 }
1367 }
1368
1369 fail:
1370 {
1371 bool result = true;
1372 if (tempError != NULL) {
1373 if (error)
1374 *error = CFRetainSafe(tempError);
1375 result = false;
1376 }
1377
1378 CFReleaseNull(tempError);
1379 return result;
1380 }
1381 }
1382
1383 static bool doesFinalPasswordPass(bool isSimple, CFStringRef password, CFDictionaryRef requirements){
1384
1385 CFTypeRef characters, identicalRef = NULL, NRef = NULL, endWith= NULL, startWith= NULL, atLeastCharacters= NULL, atMostCharacters = NULL;
1386 uint64_t valuePtr;
1387 CFIndex N, identicalCount = 0;
1388 CFArrayRef requiredCharacterSet = (CFArrayRef)CFDictionaryGetValue(requirements, kSecRequiredCharacterSetsKey);
1389
1390 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantEndWithChars, &endWith))
1391 endWith = NULL;
1392
1393 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantStartWithChars, &startWith))
1394 startWith = NULL;
1395
1396 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharacters))
1397 atLeastCharacters = NULL;
1398
1399 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharacters))
1400 atMostCharacters = NULL;
1401
1402 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef))
1403 identicalRef = NULL;
1404 else{
1405 CFNumberGetValue((CFNumberRef)identicalRef, kCFNumberSInt64Type, &valuePtr);
1406 identicalCount = (long)valuePtr;
1407 }
1408 if(endWith != NULL)
1409 {
1410 if(!doesPasswordEndWith(password, endWith))
1411 return false;
1412 }
1413 if(startWith != NULL){
1414 if(!doesPasswordStartWith(password, startWith))
1415 return false;
1416 }
1417 if(atLeastCharacters != NULL){
1418 NRef = CFDictionaryGetValue(atLeastCharacters, kSecPasswordCharacterCount);
1419 characters = CFDictionaryGetValue(atLeastCharacters, kSecPasswordCharacters);
1420 CFNumberGetValue((CFNumberRef)NRef, kCFNumberSInt64Type, &valuePtr);
1421 N = (long)valuePtr;
1422 if(!passwordContainsAtLeastNCharacters(password, characters, N))
1423 return false;
1424 }
1425 if(atMostCharacters != NULL){
1426 NRef = CFDictionaryGetValue(atMostCharacters, kSecPasswordCharacterCount);
1427 characters = CFDictionaryGetValue(atMostCharacters, kSecPasswordCharacters);
1428 CFNumberGetValue((CFNumberRef)NRef, kCFNumberSInt64Type, &valuePtr);
1429 N = (long)valuePtr;
1430 if(!passwordContainsLessThanNCharacters(password, characters, N))
1431 return false;
1432 }
1433 if(identicalRef != NULL){
1434 if(!passwordContainsLessThanNIdenticalCharacters(password, identicalCount))
1435 return false;
1436 }
1437 if (!passwordContainsRequiredCharacters(password, requiredCharacterSet))
1438 return false;
1439
1440 if(true == SecPasswordIsPasswordWeak2(isSimple, password))
1441 return false;
1442
1443 return true;
1444 }
1445
1446 static CFStringRef
1447 CreateChecksum(SecPasswordType type, CFStringRef password, CFIndex length, CFStringRef allowedChars)
1448 {
1449 if (type != kSecPasswordTypeiCloudRecoveryKey)
1450 return NULL;
1451
1452 CFMutableStringRef checksum = NULL;
1453 uint8_t digest[CCSHA256_OUTPUT_SIZE];
1454 if (length > (CFIndex)sizeof(digest))
1455 return NULL;
1456
1457 CFDataRef data = CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), password, kCFStringEncodingUTF8, 0);
1458 if (data == NULL)
1459 return NULL;
1460
1461 ccdigest(ccsha256_di(), CFDataGetLength(data), CFDataGetBytePtr(data), digest);
1462 CFReleaseNull(data);
1463
1464 CFIndex allowedCharLength = CFStringGetLength(allowedChars);
1465
1466 checksum = CFStringCreateMutable(SecCFAllocatorZeroize(), 0);
1467 for (CFIndex n = 0; n < length; n++) {
1468 CFIndex selection = digest[n] % allowedCharLength;
1469 UniChar c = CFStringGetCharacterAtIndex(allowedChars, selection);
1470 CFStringAppendCharacters(checksum, &c, 1);
1471 }
1472
1473 return checksum;
1474 }
1475
1476 //entry point into password generation
1477 CF_RETURNS_RETAINED CFStringRef SecPasswordGenerate(SecPasswordType type, CFErrorRef *error, CFDictionaryRef passwordRequirements){
1478 bool check = false, isSimple = false;
1479 CFTypeRef separator = NULL, defaults = NULL, groupSizeRef = NULL, numberOfGroupsRef = NULL;
1480 CFDictionaryRef properlyFormattedRequirements = NULL;
1481 CFErrorRef localError = NULL;
1482 uint64_t valuePtr, groupSize = 0, numberOfGroups, checksumChars = 0;
1483 CFNumberRef numberOfRequiredRandomCharacters, checksumCharacters;
1484 CFIndex requiredCharactersSize = 0;
1485 CFStringRef randomCharacters = NULL, password = NULL, allowedChars = NULL;
1486 CFMutableStringRef finalPassword = NULL;
1487
1488 if(type == kSecPasswordTypePIN)
1489 isSimple = true;
1490 else
1491 isSimple = false;
1492 check = isDictionaryFormattedProperly(type, passwordRequirements, &localError);
1493 require_quiet(check != false, fail);
1494
1495 //should we generate defaults?
1496 if(passwordRequirements == NULL || (CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) && isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0) ))
1497 properlyFormattedRequirements = passwordGenerateCreateDefaultParametersDictionary(type, passwordRequirements);
1498 else
1499 properlyFormattedRequirements = passwordGenerationCreateParametersDictionary(type, passwordRequirements);
1500
1501 require_quiet(localError == NULL && properlyFormattedRequirements != NULL, fail);
1502
1503 numberOfRequiredRandomCharacters = (CFNumberRef)CFDictionaryGetValue(properlyFormattedRequirements, kSecNumberOfRequiredRandomCharactersKey);
1504 if (isNumber(numberOfRequiredRandomCharacters) && CFNumberGetValue(numberOfRequiredRandomCharacters, kCFNumberSInt64Type, &valuePtr))
1505 requiredCharactersSize = (long)valuePtr;
1506
1507 checksumCharacters = (CFNumberRef)CFDictionaryGetValue(properlyFormattedRequirements, kSecNumberOfChecksumCharactersKey);
1508 if (isNumber(checksumCharacters) && CFNumberGetValue(checksumCharacters, kCFNumberSInt64Type, &valuePtr))
1509 checksumChars = (long)valuePtr;
1510
1511
1512 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordGroupSize, &groupSizeRef)){
1513 groupSizeRef = NULL;
1514 }
1515 else
1516 CFNumberGetValue((CFNumberRef)groupSizeRef, kCFNumberSInt64Type, &groupSize);
1517
1518 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef)){
1519 numberOfGroupsRef = NULL;
1520 }
1521 else
1522 CFNumberGetValue((CFNumberRef)numberOfGroupsRef, kCFNumberSInt64Type, &numberOfGroups);
1523
1524 require(requiredCharactersSize, fail);
1525
1526 while (true) {
1527 allowedChars = CFDictionaryGetValue(properlyFormattedRequirements, kSecAllowedCharactersKey);
1528 getPasswordRandomCharacters(&randomCharacters, properlyFormattedRequirements, &requiredCharactersSize, allowedChars);
1529
1530 if(numberOfGroupsRef && groupSizeRef){
1531 finalPassword = CFStringCreateMutable(kCFAllocatorDefault, 0);
1532
1533 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordSeparator, &separator))
1534 separator = NULL;
1535
1536 if(separator == NULL)
1537 separator = CFSTR("-");
1538
1539 CFIndex i = 0;
1540 while( i != requiredCharactersSize){
1541 if((i + (CFIndex)groupSize) < requiredCharactersSize){
1542 CFStringRef subString = CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, (CFIndex)groupSize));
1543 CFStringAppend(finalPassword, subString);
1544 CFStringAppend(finalPassword, separator);
1545 CFReleaseSafe(subString);
1546 i+=groupSize;
1547 }
1548 else if((i+(CFIndex)groupSize) == requiredCharactersSize){
1549 CFStringRef subString = CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, (CFIndex)groupSize));
1550 CFStringAppend(finalPassword, subString);
1551 CFReleaseSafe(subString);
1552 i+=groupSize;
1553 }
1554 else {
1555 CFStringRef subString = CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, requiredCharactersSize - i));
1556 CFStringAppend(finalPassword, subString);
1557 CFReleaseSafe(subString);
1558 i+=(requiredCharactersSize - i);
1559 }
1560 }
1561 if (checksumChars) {
1562 CFStringRef checksum = CreateChecksum(type, randomCharacters, (CFIndex)checksumChars, allowedChars);
1563 CFStringAppend(finalPassword, checksum);
1564 CFReleaseNull(checksum);
1565 }
1566 password = CFStringCreateCopy(kCFAllocatorDefault, finalPassword);
1567 CFReleaseNull(finalPassword);
1568 }
1569 //no fancy formatting
1570 else {
1571 password = CFStringCreateCopy(kCFAllocatorDefault, randomCharacters);
1572 }
1573
1574 CFReleaseNull(randomCharacters);
1575 require_quiet(doesFinalPasswordPass(isSimple, password, properlyFormattedRequirements), no_pass);
1576 CFReleaseNull(properlyFormattedRequirements);
1577 return password;
1578
1579 no_pass:
1580 CFReleaseNull(password);
1581 }
1582
1583 fail:
1584 if (error && localError) {
1585 *error = localError;
1586 localError = NULL;
1587 }
1588
1589 CFReleaseSafe(localError);
1590 CFReleaseNull(properlyFormattedRequirements);
1591 return NULL;
1592 }
1593
1594 const char *in_word_set (const char *str, unsigned int len){
1595 static const char * wordlist[] =
1596 {
1597 "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
1598 "", "", "0103", "", "", "", "", "0123", "", "", "", "", "0303", "", "", "",
1599 "", "", "", "", "0110", "", "1103", "", "", "", "", "1123", "", "", "0000",
1600 "", "1203", "", "0404", "", "", "", "", "1234", "1110", "2015", "2013", "",
1601 "2014", "1010", "2005", "2003", "", "2004", "1210", "0505", "0111", "", "",
1602 "", "2008", "0101", "", "2007", "", "", "", "", "2006", "2010", "1995", "1993",
1603 "", "1994", "2000", "", "1111", "", "", "", "1998", "1101", "", "1997", "",
1604 "0808", "1211", "", "1996", "0102", "", "1201", "", "", "1990", "", "", "",
1605 "", "0202", "", "2011", "", "", "1112", "1958", "2001", "", "1957", "1102",
1606 "", "3333", "", "1956", "1212", "1985", "1983", "", "1984", "1202", "", "0909",
1607 "", "0606", "", "1988", "1991", "", "1987", "2012", "", "", "", "1986", "2002",
1608 "", "", "", "0707", "1980", "", "2009", "", "", "2222", "1965", "1963", "",
1609 "1964", "", "", "2229", "", "", "1992", "1968", "", "", "1967", "", "", "1999",
1610 "", "1966", "", "1975", "1973", "", "1974", "1960", "", "1981", "", "4444",
1611 "", "1978", "", "7465", "1977", "", "", "", "", "1976", "2580", "", "1959",
1612 "", "", "1970", "", "", "", "", "", "", "", "", "", "1982", "", "1961", "",
1613 "", "5252", "", "1989", "", "", "", "", "", "", "", "", "", "", "", "", "",
1614 "", "1971", "", "", "", "", "", "", "", "1962", "", "5683", "", "6666", "",
1615 "", "1969", "", "", "", "", "", "", "", "", "", "", "", "", "1972", "", "",
1616 "", "", "", "", "1979", "", "", "", "7667"
1617 };
1618
1619 if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
1620 {
1621 register int key = pinhash (str, len);
1622
1623 if (key <= MAX_HASH_VALUE && key >= 0)
1624 {
1625 register const char *s = wordlist[key];
1626 if (*str == *s && !strcmp (str + 1, s + 1))
1627 return s;
1628 }
1629 }
1630 return 0;
1631 }
1632 CFDictionaryRef SecPasswordCopyDefaultPasswordLength(SecPasswordType type, CFErrorRef *error){
1633
1634 CFIndex tupleLengthInt = 0, numOfTuplesInt = 0;
1635 CFNumberRef tupleLength = NULL;
1636 CFNumberRef numOfTuples = NULL;
1637
1638 CFMutableDictionaryRef passwordLengthDefaults = NULL;
1639 CFDictionaryRef result = NULL;
1640
1641 switch(type){
1642 case(kSecPasswordTypeiCloudRecoveryKey):
1643 tupleLengthInt = 4;
1644 numOfTuplesInt = 7;
1645 break;
1646
1647 case(kSecPasswordTypeiCloudRecovery):
1648 tupleLengthInt = 4;
1649 numOfTuplesInt = 6;
1650 break;
1651
1652 case(kSecPasswordTypePIN):
1653 tupleLengthInt = 4;
1654 numOfTuplesInt = 1;
1655 break;
1656
1657 case(kSecPasswordTypeSafari):
1658 tupleLengthInt = 4;
1659 numOfTuplesInt = 5;
1660 break;
1661
1662 case(kSecPasswordTypeWifi):
1663 tupleLengthInt = 4;
1664 numOfTuplesInt = 3;
1665 break;
1666
1667 default:
1668 if(SecError(errSecBadReq, error, CFSTR("Password type does not exist.")) == false)
1669 {
1670 secdebug("secpasswordcopydefaultpasswordlength", "could not create error!");
1671 }
1672 }
1673
1674 if (tupleLengthInt != 0 && numOfTuplesInt != 0) {
1675 tupleLength = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &tupleLengthInt);
1676 numOfTuples = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &numOfTuplesInt);
1677 passwordLengthDefaults = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1678 CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordGroupSize, tupleLength);
1679 CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordNumberOfGroups, numOfTuples);
1680 result = CFDictionaryCreateCopy(kCFAllocatorDefault, passwordLengthDefaults);
1681 }
1682
1683 CFReleaseSafe(tupleLength);
1684 CFReleaseSafe(numOfTuples);
1685 CFReleaseSafe(passwordLengthDefaults);
1686 return result;
1687 }
1688
1689 bool
1690 SecPasswordValidatePasswordFormat(SecPasswordType type, CFStringRef password, CFErrorRef *error)
1691 {
1692 CFIndex tupleLengthInt = 0, numOfTuplesInt = 0, checkSumChars = 3;
1693 CFStringRef checksum = NULL, madeChecksum = NULL, passwordNoChecksum = NULL;
1694 CFMutableStringRef randomChars = NULL;
1695 CFStringRef allowedChars = NULL;
1696 bool res = false;
1697
1698 if (type != kSecPasswordTypeiCloudRecoveryKey) {
1699 return false;
1700 }
1701
1702 tupleLengthInt = 4;
1703 numOfTuplesInt = 7;
1704 checkSumChars = 2;
1705 allowedChars = defaultiCloudCharacters;
1706
1707 if (numOfTuplesInt < 1)
1708 return false;
1709 if (checkSumChars > tupleLengthInt)
1710 return false;
1711
1712 CFIndex numberOfChars = numOfTuplesInt * tupleLengthInt + (numOfTuplesInt - 1);
1713
1714 /*
1715 * First check expected length
1716 */
1717
1718 require(CFStringGetLength(password) == numberOfChars, out); /* N groups of M with (N-1) seperator - in-between */
1719
1720 randomChars = CFStringCreateMutable(SecCFAllocatorZeroize(), 0);
1721 require(randomChars, out);
1722
1723 /*
1724 * make sure dash-es are at the expected spots
1725 */
1726
1727 for (CFIndex n = 0; n < numOfTuplesInt; n++) {
1728 if (n != 0) {
1729 UniChar c = CFStringGetCharacterAtIndex(password, (n * (tupleLengthInt + 1)) - 1);
1730 require(c == '-', out);
1731 }
1732 CFStringRef substr = CFStringCreateWithSubstring(SecCFAllocatorZeroize(), password, CFRangeMake(n * (tupleLengthInt + 1), tupleLengthInt));
1733 CFStringAppend(randomChars, substr);
1734 CFReleaseNull(substr);
1735 }
1736
1737 /*
1738 * Pull apart and password and checksum
1739 */
1740
1741 checksum = CFStringCreateWithSubstring(SecCFAllocatorZeroize(), randomChars, CFRangeMake((numOfTuplesInt * tupleLengthInt) - checkSumChars, checkSumChars));
1742 require(checksum, out);
1743
1744 passwordNoChecksum = CFStringCreateWithSubstring(SecCFAllocatorZeroize(), randomChars, CFRangeMake(0, (numOfTuplesInt * tupleLengthInt) - checkSumChars));
1745 require(passwordNoChecksum, out);
1746
1747 /*
1748 * Validate checksum
1749 */
1750
1751 madeChecksum = CreateChecksum(type, passwordNoChecksum, checkSumChars, allowedChars);
1752 require(madeChecksum, out);
1753
1754 require(CFEqual(madeChecksum, checksum), out);
1755
1756 res = true;
1757 out:
1758 CFReleaseNull(randomChars);
1759 CFReleaseNull(madeChecksum);
1760 CFReleaseNull(checksum);
1761 CFReleaseNull(passwordNoChecksum);
1762
1763 return res;
1764 }