]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecPasswordGenerate.c
Security-58286.251.4.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
509 CFReleaseNull(highestRepeatingcount);
510 return finalRepeating;
511 }
512
513 static bool SecPasswordIsPalindrome(CFStringRef passcode){
514 char* pin = CFStringToCString(passcode);
515 long length = CFStringGetLength(passcode);
516 long j = length-1;
517
518 for(int i = 0; i < CFStringGetLength(passcode); i++)
519 {
520 if(length%2 == 1 && i == j){
521 free(pin);
522 return true;
523 }
524 else if(length%2 == 0 && i == j-1){
525 if(pin[i] == pin[j]){
526 free(pin);
527 return true;
528 }
529 else
530 break;
531 }
532 else if(pin[i] == pin[j]){
533 j--;
534 continue;
535 }
536 else
537 break;
538 }
539 free(pin);
540 return false;
541 }
542
543 static bool SecPasswordHasRepeatingGroups(CFStringRef passcode){
544 char* pin = CFStringToCString(passcode);
545
546 for(int i = 0; i < CFStringGetLength(passcode); i++)
547 {
548 if(i+4 == CFStringGetLength(passcode)){
549 if(pin[i] == pin[i+3]){
550 free(pin);
551 return true;
552 }
553 else
554 break;
555 }
556 else if(pin[i] == pin[i+3])
557 continue;
558 else
559 break;
560 }
561
562 free(pin);
563
564 return false;
565 }
566
567 bool SecPasswordIsPasswordWeak2(bool isSimple, CFStringRef passcode)
568 {
569 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
570 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
571 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
572 punctuationCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetPunctuation);
573
574
575 char* pin = NULL;
576
577 //length checks
578 if( CFStringGetLength(passcode) < 4 ){
579 return true; //weak password
580 }
581
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))
586 continue;
587 else {
588 isPasscodeNumber = false;
589 break;
590 }
591 }
592
593 if(isSimple){
594 //checking to see if it's a 4 digit pin
595 if(isPasscodeNumber && CFStringGetLength(passcode) == 4){
596
597 pin = CFStringToCString(passcode);
598 if(in_word_set(pin, 4)){
599 free(pin);
600 return true;
601 }
602
603 CFIndex blacklistLength = (CFIndex)sizeof(blacklist)/sizeof(blacklist[0]);
604
605 //not all the same number
606 if(pin[0] == pin[1] == pin[2] == pin[3]){
607 free(pin);
608 return true; //weak password
609 }
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]){
612 free(pin);
613 return true; //weak password
614 }
615 //first two digits not being the same as the last two digits
616 if(pin[0] == pin[2] && pin[1] == pin[3]){
617 free(pin);
618 return true; //weak password
619 }
620 //not in this list
621 for(CFIndex i = 0; i < blacklistLength; i++)
622 {
623 const char* blackCode = blacklist[i];
624 if(0 == strcmp(blackCode, pin))
625 {
626 free(pin);
627 return true; //weak password
628 }
629 }
630 }
631 else if(isPasscodeNumber && CFStringGetLength(passcode) == 6){
632 pin = CFStringToCString(passcode);
633
634 //not all the same number
635 for(int i = 0; i < CFStringGetLength(passcode); i++){
636 if(i == CFStringGetLength(passcode)-1){
637 free(pin);
638 return true;
639 }
640 else if ((pin[i]) == pin[i+1])
641 continue;
642 else
643 break;
644 }
645 //not in the top 10
646 if(isTopTenSixDigitPasscode(passcode)){
647 free(pin);
648 return true;
649 }
650 //palindrome test
651 if(SecPasswordIsPalindrome(passcode)){
652 free(pin);
653 return true;
654 }
655
656 //2 identical groups
657 if(SecPasswordHasRepeatingGroups(passcode)){
658 free(pin);
659 return true;
660 }
661 //passcode is incrementing ex 123456 or 654321
662 if(SecPasswordIsPasscodeIncrementingOrDecrementingDigits(passcode)) {
663 free(pin);
664 return true;
665 }
666 //passcode does not consist of 2 repeating digits
667 if(SecPasswordIsPasswordRepeatingTwoNumbers(passcode)){
668 free(pin);
669 return true;
670 }
671 }
672 else//should be a 4 or 6digit number
673 return false;
674 }
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)){
680 free(pin);
681 return true;
682 }
683 //palindrome test
684 if(SecPasswordIsPalindrome(passcode)){
685 free(pin);
686 return true;
687 }
688 //not in the top 10
689 if(isTopTenSixDigitPasscode(passcode)){
690 free(pin);
691 return true;
692 }
693 //2 identical groups
694 if(SecPasswordHasRepeatingGroups(passcode) && CFStringGetLength(passcode) >= 6){
695 free(pin);
696 return true;
697 }
698
699 if(SecPasswordIsPasscodeIncrementingOrDecrementingDigits(passcode)) {
700 free(pin);
701 return true;
702 }
703 if(SecPasswordIsPasswordRepeatingTwoNumbers(passcode)){
704 free(pin);
705 return true;
706 }
707
708 }
709 else{ // password is complex, evaluate entropy
710 int u = 0;
711 int l = 0;
712 int d = 0;
713 int p = 0;
714 int characterSet = 0;
715
716 //calculate new entropy
717 for(CFIndex i = 0; i < CFStringGetLength(passcode); i++){
718
719 if( CFStringFindCharacterFromSet(passcode, uppercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
720 u++;
721 continue;
722 }
723 if( CFStringFindCharacterFromSet(passcode, lowercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
724 l++;
725 continue;
726 }
727 if( CFStringFindCharacterFromSet(passcode, decimalDigitCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
728 d++;
729 continue;
730 }
731 if( CFStringFindCharacterFromSet(passcode, punctuationCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
732 p++;
733 continue;
734 }
735
736 }
737 if(u > 0){
738 characterSet += alphabetSetSize;
739 }
740 if(l > 0){
741 characterSet += alphabetSetSize;
742 }
743 if(d > 0){
744 characterSet += decimalSetSize;
745 }
746 if(p > 0){
747 characterSet += punctuationSetSize;
748 }
749
750 double strength = CFStringGetLength(passcode)*log2(characterSet);
751
752 if(strength < entropyStrengthThreshold){
753 return true; //weak
754 }
755 else
756 return false; //strong
757 }
758 if(pin)
759 free(pin);
760
761 return false; //strong password
762
763 }
764
765 static void getUniformRandomNumbers(uint8_t* buffer, size_t numberOfDesiredNumbers, uint8_t upperBound)
766 {
767
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);
775
776 for (size_t numberOfAcceptedNumbers = 0; numberOfAcceptedNumbers < numberOfDesiredNumbers; ) {
777 if (SecRandomCopyBytes(kSecRandomDefault, numberOfDesiredNumbers - numberOfAcceptedNumbers, buffer + numberOfAcceptedNumbers) == -1)
778 continue;
779 for (size_t i = numberOfAcceptedNumbers; i < numberOfDesiredNumbers; ++i) {
780 if (buffer[i] < limitAvoidingModuloBias)
781 buffer[numberOfAcceptedNumbers++] = buffer[i] % upperBound;
782 }
783 }
784 }
785
786 static bool passwordContainsRequiredCharacters(CFStringRef password, CFArrayRef requiredCharacterSets)
787 {
788 CFCharacterSetRef characterSet;
789
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);
794 }
795 return true;
796
797 fail:
798 return false;
799
800 }
801
802 static bool passwordContainsLessThanNIdenticalCharacters(CFStringRef password, CFIndex identicalCount)
803 {
804 unsigned char Char, nextChar;
805 int repeating = 0;
806
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){
813 repeating++;
814 }else{
815 repeating = 0;
816 break;
817 }
818 }
819 }
820 return true;
821 fail:
822 return false;
823 }
824
825 static bool passwordContainsAtLeastNCharacters(CFStringRef password, CFStringRef characters, CFIndex N)
826 {
827 CFCharacterSetRef characterSet = NULL;
828 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, characters);
829 CFIndex counter = 0;
830
831 for(CFIndex i = 0; i < CFStringGetLength(password); i++){
832 if(CFStringFindCharacterFromSet(password, characterSet, CFRangeMake(i, 1), 0, NULL))
833 counter++;
834 }
835 CFReleaseNull(characterSet);
836 if(counter < N)
837 return false;
838 else
839 return true;
840 }
841
842 static bool passwordContainsLessThanNCharacters(CFStringRef password, CFStringRef characters, CFIndex N)
843 {
844 CFCharacterSetRef characterSet = NULL;
845 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, characters);
846 CFIndex counter = 0;
847
848 for(CFIndex i = 0; i < CFStringGetLength(password); i++){
849 if(CFStringFindCharacterFromSet(password, characterSet, CFRangeMake(i, 1), 0, NULL))
850 counter++;
851 }
852 CFReleaseNull(characterSet);
853 if(counter > N)
854 return false;
855 else
856 return true;
857 }
858
859 static bool passwordDoesNotContainCharacters(CFStringRef password, CFStringRef prohibitedCharacters)
860 {
861 CFCharacterSetRef characterSet = NULL;
862 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
863 CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(password));
864
865 require_quiet(!CFStringFindCharacterFromSet(password, characterSet, rangeToSearch, 0, NULL), fail);
866 CFReleaseNull(characterSet);
867 return true;
868 fail:
869 CFReleaseNull(characterSet);
870 return false;
871 }
872
873 static OSStatus getPasswordRandomCharacters(CFStringRef *returned, CFDictionaryRef requirements, CFIndex *numberOfRandomCharacters, CFStringRef allowedCharacters)
874 {
875 uint8_t *randomNumbers = malloc(*numberOfRandomCharacters);
876 unsigned char *randomCharacters = malloc(*numberOfRandomCharacters);
877
878 if (randomNumbers == NULL || randomCharacters == NULL) {
879 free(randomNumbers);
880 free(randomCharacters);
881 return errSecMemoryError;
882 }
883
884 getUniformRandomNumbers(randomNumbers, *numberOfRandomCharacters, CFStringGetLength(allowedCharacters));
885
886 CFTypeRef prohibitedCharacters = NULL;
887 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters))
888 prohibitedCharacters = NULL;
889
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)
896 {
897 CFStringRef temp = CFStringCreateWithCharacters(kCFAllocatorDefault, randomChar, 1);
898 bool pwdncc = passwordDoesNotContainCharacters(temp, prohibitedCharacters);
899 CFReleaseSafe(temp);
900 if (!pwdncc) {
901 //change up the random numbers so we don't get the same index into allowed
902 getUniformRandomNumbers(randomNumbers, *numberOfRandomCharacters, CFStringGetLength(allowedCharacters));
903 i--;
904 continue;
905 }
906 }
907 randomCharacters[i] = (unsigned char)randomChar[0];
908 }
909
910 *returned = CFStringCreateWithBytes(kCFAllocatorDefault, randomCharacters, *numberOfRandomCharacters, kCFStringEncodingUTF8, false);
911
912 free(randomCharacters);
913 free(randomNumbers);
914
915 return errSecSuccess;
916 }
917
918 static bool doesPasswordEndWith(CFStringRef password, CFStringRef prohibitedCharacters)
919 {
920 CFCharacterSetRef characterSet = NULL;
921 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
922
923 CFRange rangeToSearch = CFRangeMake(CFStringGetLength(password) - CFStringGetLength(prohibitedCharacters), CFStringGetLength(prohibitedCharacters));
924 require_quiet(0 == CFStringCompareWithOptions(password, prohibitedCharacters, rangeToSearch, 0), fail);
925 CFReleaseNull(characterSet);
926 return false;
927 fail:
928 CFReleaseNull(characterSet);
929 return true;
930 }
931
932 static bool doesPasswordStartWith(CFStringRef password, CFStringRef prohibitedCharacters)
933 {
934 CFCharacterSetRef characterSet = NULL;
935 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
936
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
941 fail:
942 CFReleaseNull(characterSet);
943 return true;
944 }
945
946 static CFDictionaryRef passwordGenerateCreateDefaultParametersDictionary(SecPasswordType type, CFDictionaryRef requirements){
947
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;
956
957 switch(type){
958 case kSecPasswordTypeiCloudRecoveryKey:
959 groupSize = 4;
960 numberOfGroups = 7;
961 checksumSize = 2;
962 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, (groupSize * numberOfGroups) - checksumSize);
963 checksumChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, checksumSize);
964 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
965 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
966
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,
979 NULL);
980 break;
981 case(kSecPasswordTypeiCloudRecovery):
982 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultiCloudPasswordLength);
983 groupSize = 4;
984 numberOfGroups = 6;
985 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
986 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
987
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,
999 NULL);
1000 break;
1001
1002 case(kSecPasswordTypePIN):
1003 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultPINLength);
1004 groupSize = 4;
1005 numberOfGroups = 1;
1006 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
1007 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
1008
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,
1018 NULL);
1019 break;
1020
1021 case(kSecPasswordTypeWifi):
1022 groupSize = 4;
1023 numberOfGroups = 3;
1024 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
1025 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
1026
1027 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
1028 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
1029
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,
1040 NULL);
1041 break;
1042
1043 default:
1044 groupSize = 4;
1045 numberOfGroups = 6;
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);
1054
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,
1063 NULL);
1064
1065
1066
1067 break;
1068 }
1069
1070 CFReleaseNull(numReqChars);
1071 CFReleaseNull(requiredCharacterSets);
1072 CFReleaseNull(groupSizeRef);
1073 CFReleaseNull(numberOfGroupsRef);
1074 CFReleaseNull(checksumChars);
1075 return returned;
1076 }
1077 static CFDictionaryRef passwordGenerationCreateParametersDictionary(SecPasswordType type, CFDictionaryRef requirements)
1078 {
1079 CFMutableArrayRef requiredCharacterSets = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1080 CFNumberRef numReqChars = NULL;
1081 CFIndex numberOfRequiredRandomCharacters;
1082 CFStringRef allowedCharacters = NULL, useDefaultPasswordFormat = NULL;
1083 uint64_t valuePtr;
1084 CFTypeRef prohibitedCharacters = NULL, endWith = NULL, startWith = NULL,
1085 groupSizeRef = NULL, numberOfGroupsRef = NULL, separatorRef = NULL,
1086 atMostCharactersRef = NULL,atLeastCharactersRef = NULL, identicalRef = NULL;
1087
1088 CFNumberRef min = (CFNumberRef)CFDictionaryGetValue(requirements, kSecPasswordMinLengthKey);
1089 CFNumberRef max = (CFNumberRef)CFDictionaryGetValue(requirements, kSecPasswordMaxLengthKey);
1090
1091 CFNumberGetValue(min, kCFNumberSInt64Type, &valuePtr);
1092 CFIndex minPasswordLength = (long)valuePtr;
1093 CFNumberGetValue(max, kCFNumberSInt64Type, &valuePtr);
1094 CFIndex maxPasswordLength = (long)valuePtr;
1095
1096 // If requirements allow, we will generate the password in default format.
1097 useDefaultPasswordFormat = CFSTR("true");
1098 numberOfRequiredRandomCharacters = defaultNumberOfRandomCharacters;
1099
1100 if(type == kSecPasswordTypePIN)
1101 {
1102 if( maxPasswordLength && minPasswordLength )
1103 numberOfRequiredRandomCharacters = maxPasswordLength;
1104 else if( !maxPasswordLength && minPasswordLength )
1105 numberOfRequiredRandomCharacters = minPasswordLength;
1106 else if( !minPasswordLength && maxPasswordLength )
1107 numberOfRequiredRandomCharacters = maxPasswordLength;
1108 else
1109 numberOfRequiredRandomCharacters = defaultPINLength;
1110
1111 allowedCharacters = CFSTR("0123456789");
1112 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
1113 useDefaultPasswordFormat = CFSTR("false");
1114 }
1115 else{
1116 CFArrayRef requiredCharactersArray = NULL;
1117
1118 if (minPasswordLength && minPasswordLength > defaultNumberOfRandomCharacters) {
1119 useDefaultPasswordFormat = CFSTR("false");
1120 numberOfRequiredRandomCharacters = minPasswordLength;
1121 }
1122 if (maxPasswordLength && maxPasswordLength < defaultNumberOfRandomCharacters) {
1123 useDefaultPasswordFormat = CFSTR("false");
1124 numberOfRequiredRandomCharacters = maxPasswordLength;
1125 }
1126 if (maxPasswordLength && minPasswordLength && maxPasswordLength == minPasswordLength && maxPasswordLength != defaultNumberOfRandomCharacters){
1127 useDefaultPasswordFormat = CFSTR("false");
1128 numberOfRequiredRandomCharacters = maxPasswordLength;
1129 }
1130 allowedCharacters = (CFStringRef)CFRetainSafe(CFDictionaryGetValue(requirements, kSecPasswordAllowedCharactersKey));
1131 requiredCharactersArray = (CFArrayRef)CFDictionaryGetValue(requirements, kSecPasswordRequiredCharactersKey);
1132
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);
1138 }
1139 }
1140 } else{
1141 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
1142 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
1143 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
1144 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
1145 CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet);
1146 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
1147 }
1148 }
1149
1150 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters))
1151 prohibitedCharacters = NULL;
1152
1153 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantEndWithChars, &endWith))
1154 endWith = NULL;
1155
1156 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantStartWithChars, &startWith))
1157 startWith = NULL;
1158
1159 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordGroupSize, &groupSizeRef))
1160 groupSizeRef = NULL;
1161
1162 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef))
1163 numberOfGroupsRef = NULL;
1164
1165 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordSeparator, &separatorRef))
1166 separatorRef = NULL;
1167
1168 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharactersRef))
1169 atMostCharactersRef = NULL;
1170
1171 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharactersRef))
1172 atLeastCharactersRef = NULL;
1173
1174 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef))
1175 identicalRef = NULL;
1176
1177 if (allowedCharacters) {
1178 if( false == CFStringFindWithOptions(allowedCharacters, CFSTR("-"), CFRangeMake(0, CFStringGetLength(allowedCharacters)), kCFCompareCaseInsensitive, NULL))
1179 useDefaultPasswordFormat = CFSTR("false");
1180 } else
1181 allowedCharacters = CFRetainSafe(defaultCharacters);
1182
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;
1189 }
1190
1191 if (CFArrayGetCount(requiredCharacterSets) > numberOfRequiredRandomCharacters) {
1192 CFReleaseNull(requiredCharacterSets);
1193 requiredCharacterSets = NULL;
1194 }
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);
1203
1204 //add the prohibited characters string if it exists to the new dictionary
1205 if(prohibitedCharacters)
1206 CFDictionaryAddValue(updatedConstraints, kSecPasswordDisallowedCharacters, prohibitedCharacters);
1207
1208 //add the characters the password can't end with if it exists to the new dictionary
1209 if(endWith)
1210 CFDictionaryAddValue(updatedConstraints, kSecPasswordCantEndWithChars, endWith);
1211
1212 //add the characters the password can't start with if it exists to the new dictionary
1213 if(startWith)
1214 CFDictionaryAddValue(updatedConstraints, kSecPasswordCantStartWithChars, startWith);
1215
1216 if(groupSizeRef)
1217 CFDictionaryAddValue(updatedConstraints, kSecPasswordGroupSize, groupSizeRef);
1218
1219 if(numberOfGroupsRef)
1220 CFDictionaryAddValue(updatedConstraints, kSecPasswordNumberOfGroups, numberOfGroupsRef);
1221
1222 if(separatorRef)
1223 CFDictionaryAddValue(updatedConstraints, kSecPasswordSeparator, separatorRef);
1224
1225 if(atMostCharactersRef)
1226 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsNoMoreThanNSpecificCharacters, atMostCharactersRef);
1227
1228 if(atLeastCharactersRef)
1229 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsAtLeastNSpecificCharacters, atLeastCharactersRef);
1230
1231 if(identicalRef)
1232 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, identicalRef);
1233
1234 CFReleaseNull(useDefaultPasswordFormat);
1235 CFReleaseNull(numReqChars);
1236 CFReleaseNull(allowedCharacters);
1237 CFReleaseNull(requiredCharacterSets);
1238
1239 return updatedConstraints;
1240 }
1241
1242 static bool isDictionaryFormattedProperly(SecPasswordType type, CFDictionaryRef passwordRequirements, CFErrorRef *error){
1243
1244 CFTypeRef defaults = NULL;
1245 CFErrorRef tempError = NULL;
1246 if(passwordRequirements == NULL){
1247 return true;
1248 }
1249
1250 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) ){
1251 if(isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0)){
1252 return true;
1253 }
1254 }
1255 //only need to check max and min pin length formatting
1256 if(type == kSecPasswordTypePIN){
1257 CFTypeRef minTest = NULL, maxTest = NULL;
1258 uint64_t valuePtr;
1259 CFIndex minPasswordLength = 0, maxPasswordLength= 0;
1260
1261 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) ){
1262 if(isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0)){
1263 return true;
1264 }
1265 }
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));
1270
1271 }
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));
1275 }
1276 //check if the values exist!
1277 if(maxTest){
1278 CFNumberRef max = (CFNumberRef)maxTest;
1279 CFNumberGetValue(max, kCFNumberSInt64Type, &valuePtr);
1280 maxPasswordLength = (long)valuePtr;
1281 }
1282 if(minTest){
1283 CFNumberRef min = (CFNumberRef)minTest;
1284 CFNumberGetValue(min, kCFNumberSInt64Type, &valuePtr);
1285 minPasswordLength = (long)valuePtr;
1286 }
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));
1290 }
1291 else{
1292 CFTypeRef allowedTest, maxTest, minTest, requiredTest, prohibitedCharacters, endWith, startWith,
1293 groupSizeRef, numberOfGroupsRef, separatorRef, atMostCharactersRef,
1294 atLeastCharactersRef, thresholdRef, identicalRef, characters;
1295 uint64_t valuePtr;
1296
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));
1302
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));
1308
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));
1314
1315 CFNumberGetValue(minTest, kCFNumberSInt64Type, &valuePtr);
1316 CFIndex minPasswordLength = (long)valuePtr;
1317 CFNumberGetValue(maxTest, kCFNumberSInt64Type, &valuePtr);
1318 CFIndex maxPasswordLength = (long)valuePtr;
1319
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));
1321
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));
1324
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));
1328 }
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));
1332 }
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));
1336 }
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));
1340 }
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));
1344 }
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));
1348 }
1349
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));
1353
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));
1357
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));
1361 }
1362
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));
1366
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));
1368
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));
1371
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));
1375 }
1376
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));
1380 }
1381 }
1382
1383 fail:
1384 {
1385 bool result = true;
1386 if (tempError != NULL) {
1387 if (error)
1388 *error = CFRetainSafe(tempError);
1389 result = false;
1390 }
1391
1392 CFReleaseNull(tempError);
1393 return result;
1394 }
1395 }
1396
1397 static bool doesFinalPasswordPass(bool isSimple, CFStringRef password, CFDictionaryRef requirements){
1398
1399 CFTypeRef characters, identicalRef = NULL, NRef = NULL, endWith= NULL, startWith= NULL, atLeastCharacters= NULL, atMostCharacters = NULL;
1400 uint64_t valuePtr;
1401 CFIndex N, identicalCount = 0;
1402 CFArrayRef requiredCharacterSet = (CFArrayRef)CFDictionaryGetValue(requirements, kSecRequiredCharacterSetsKey);
1403
1404 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantEndWithChars, &endWith))
1405 endWith = NULL;
1406
1407 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantStartWithChars, &startWith))
1408 startWith = NULL;
1409
1410 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharacters))
1411 atLeastCharacters = NULL;
1412
1413 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharacters))
1414 atMostCharacters = NULL;
1415
1416 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef))
1417 identicalRef = NULL;
1418 else{
1419 CFNumberGetValue((CFNumberRef)identicalRef, kCFNumberSInt64Type, &valuePtr);
1420 identicalCount = (long)valuePtr;
1421 }
1422 if(endWith != NULL)
1423 {
1424 if(!doesPasswordEndWith(password, endWith))
1425 return false;
1426 }
1427 if(startWith != NULL){
1428 if(!doesPasswordStartWith(password, startWith))
1429 return false;
1430 }
1431 if(atLeastCharacters != NULL){
1432 NRef = CFDictionaryGetValue(atLeastCharacters, kSecPasswordCharacterCount);
1433 characters = CFDictionaryGetValue(atLeastCharacters, kSecPasswordCharacters);
1434 CFNumberGetValue((CFNumberRef)NRef, kCFNumberSInt64Type, &valuePtr);
1435 N = (long)valuePtr;
1436 if(!passwordContainsAtLeastNCharacters(password, characters, N))
1437 return false;
1438 }
1439 if(atMostCharacters != NULL){
1440 NRef = CFDictionaryGetValue(atMostCharacters, kSecPasswordCharacterCount);
1441 characters = CFDictionaryGetValue(atMostCharacters, kSecPasswordCharacters);
1442 CFNumberGetValue((CFNumberRef)NRef, kCFNumberSInt64Type, &valuePtr);
1443 N = (long)valuePtr;
1444 if(!passwordContainsLessThanNCharacters(password, characters, N))
1445 return false;
1446 }
1447 if(identicalRef != NULL){
1448 if(!passwordContainsLessThanNIdenticalCharacters(password, identicalCount))
1449 return false;
1450 }
1451 if (!passwordContainsRequiredCharacters(password, requiredCharacterSet))
1452 return false;
1453
1454 if(true == SecPasswordIsPasswordWeak2(isSimple, password))
1455 return false;
1456
1457 return true;
1458 }
1459
1460 static CFStringRef
1461 CreateChecksum(SecPasswordType type, CFStringRef password, CFIndex length, CFStringRef allowedChars)
1462 {
1463 if (type != kSecPasswordTypeiCloudRecoveryKey)
1464 return NULL;
1465
1466 CFMutableStringRef checksum = NULL;
1467 uint8_t digest[CCSHA256_OUTPUT_SIZE];
1468 if (length > (CFIndex)sizeof(digest))
1469 return NULL;
1470
1471 CFDataRef data = CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), password, kCFStringEncodingUTF8, 0);
1472 if (data == NULL)
1473 return NULL;
1474
1475 ccdigest(ccsha256_di(), CFDataGetLength(data), CFDataGetBytePtr(data), digest);
1476 CFReleaseNull(data);
1477
1478 CFIndex allowedCharLength = CFStringGetLength(allowedChars);
1479
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);
1485 }
1486
1487 return checksum;
1488 }
1489
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;
1501
1502 if(type == kSecPasswordTypePIN)
1503 isSimple = true;
1504 else
1505 isSimple = false;
1506 check = isDictionaryFormattedProperly(type, passwordRequirements, &localError);
1507 require_quiet(check != false, fail);
1508
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);
1512 else
1513 properlyFormattedRequirements = passwordGenerationCreateParametersDictionary(type, passwordRequirements);
1514
1515 require_quiet(localError == NULL && properlyFormattedRequirements != NULL, fail);
1516
1517 numberOfRequiredRandomCharacters = (CFNumberRef)CFDictionaryGetValue(properlyFormattedRequirements, kSecNumberOfRequiredRandomCharactersKey);
1518 if (isNumber(numberOfRequiredRandomCharacters) && CFNumberGetValue(numberOfRequiredRandomCharacters, kCFNumberSInt64Type, &valuePtr))
1519 requiredCharactersSize = (long)valuePtr;
1520
1521 checksumCharacters = (CFNumberRef)CFDictionaryGetValue(properlyFormattedRequirements, kSecNumberOfChecksumCharactersKey);
1522 if (isNumber(checksumCharacters) && CFNumberGetValue(checksumCharacters, kCFNumberSInt64Type, &valuePtr))
1523 checksumChars = (long)valuePtr;
1524
1525
1526 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordGroupSize, &groupSizeRef)){
1527 groupSizeRef = NULL;
1528 }
1529 else
1530 CFNumberGetValue((CFNumberRef)groupSizeRef, kCFNumberSInt64Type, &groupSize);
1531
1532 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef)){
1533 numberOfGroupsRef = NULL;
1534 }
1535 else
1536 CFNumberGetValue((CFNumberRef)numberOfGroupsRef, kCFNumberSInt64Type, &numberOfGroups);
1537
1538 require(requiredCharactersSize, fail);
1539
1540 while (true) {
1541 allowedChars = CFDictionaryGetValue(properlyFormattedRequirements, kSecAllowedCharactersKey);
1542 require_noerr(getPasswordRandomCharacters(&randomCharacters, properlyFormattedRequirements, &requiredCharactersSize, allowedChars), fail);
1543
1544 if(numberOfGroupsRef && groupSizeRef){
1545 finalPassword = CFStringCreateMutable(kCFAllocatorDefault, 0);
1546
1547 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordSeparator, &separator))
1548 separator = NULL;
1549
1550 if(separator == NULL)
1551 separator = CFSTR("-");
1552
1553 CFIndex i = 0;
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);
1560 i+=groupSize;
1561 }
1562 else if((i+(CFIndex)groupSize) == requiredCharactersSize){
1563 CFStringRef subString = CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, (CFIndex)groupSize));
1564 CFStringAppend(finalPassword, subString);
1565 CFReleaseSafe(subString);
1566 i+=groupSize;
1567 }
1568 else {
1569 CFStringRef subString = CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, requiredCharactersSize - i));
1570 CFStringAppend(finalPassword, subString);
1571 CFReleaseSafe(subString);
1572 i+=(requiredCharactersSize - i);
1573 }
1574 }
1575 if (checksumChars) {
1576 CFStringRef checksum = CreateChecksum(type, randomCharacters, (CFIndex)checksumChars, allowedChars);
1577 CFStringAppend(finalPassword, checksum);
1578 CFReleaseNull(checksum);
1579 }
1580 password = CFStringCreateCopy(kCFAllocatorDefault, finalPassword);
1581 CFReleaseNull(finalPassword);
1582 }
1583 //no fancy formatting
1584 else {
1585 password = CFStringCreateCopy(kCFAllocatorDefault, randomCharacters);
1586 }
1587
1588 CFReleaseNull(randomCharacters);
1589 require_quiet(doesFinalPasswordPass(isSimple, password, properlyFormattedRequirements), no_pass);
1590 CFReleaseNull(properlyFormattedRequirements);
1591 return password;
1592
1593 no_pass:
1594 CFReleaseNull(password);
1595 }
1596
1597 fail:
1598 if (error && localError) {
1599 *error = localError;
1600 localError = NULL;
1601 }
1602
1603 CFReleaseSafe(localError);
1604 CFReleaseNull(properlyFormattedRequirements);
1605 return NULL;
1606 }
1607
1608 const char *in_word_set (const char *str, unsigned int len){
1609 static const char * wordlist[] =
1610 {
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"
1631 };
1632
1633 if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
1634 {
1635 register int key = pinhash (str, len);
1636
1637 if (key <= MAX_HASH_VALUE && key >= 0)
1638 {
1639 register const char *s = wordlist[key];
1640 if (*str == *s && !strcmp (str + 1, s + 1))
1641 return s;
1642 }
1643 }
1644 return 0;
1645 }
1646 CFDictionaryRef SecPasswordCopyDefaultPasswordLength(SecPasswordType type, CFErrorRef *error){
1647
1648 CFIndex tupleLengthInt = 0, numOfTuplesInt = 0;
1649 CFNumberRef tupleLength = NULL;
1650 CFNumberRef numOfTuples = NULL;
1651
1652 CFMutableDictionaryRef passwordLengthDefaults = NULL;
1653 CFDictionaryRef result = NULL;
1654
1655 switch(type){
1656 case(kSecPasswordTypeiCloudRecoveryKey):
1657 tupleLengthInt = 4;
1658 numOfTuplesInt = 7;
1659 break;
1660
1661 case(kSecPasswordTypeiCloudRecovery):
1662 tupleLengthInt = 4;
1663 numOfTuplesInt = 6;
1664 break;
1665
1666 case(kSecPasswordTypePIN):
1667 tupleLengthInt = 4;
1668 numOfTuplesInt = 1;
1669 break;
1670
1671 case(kSecPasswordTypeSafari):
1672 tupleLengthInt = 4;
1673 numOfTuplesInt = 5;
1674 break;
1675
1676 case(kSecPasswordTypeWifi):
1677 tupleLengthInt = 4;
1678 numOfTuplesInt = 3;
1679 break;
1680
1681 default:
1682 if(SecError(errSecBadReq, error, CFSTR("Password type does not exist.")) == false)
1683 {
1684 secdebug("secpasswordcopydefaultpasswordlength", "could not create error!");
1685 }
1686 }
1687
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);
1695 }
1696
1697 CFReleaseSafe(tupleLength);
1698 CFReleaseSafe(numOfTuples);
1699 CFReleaseSafe(passwordLengthDefaults);
1700 return result;
1701 }
1702
1703 bool
1704 SecPasswordValidatePasswordFormat(SecPasswordType type, CFStringRef password, CFErrorRef *error)
1705 {
1706 CFIndex tupleLengthInt = 0, numOfTuplesInt = 0, checkSumChars = 0;
1707 CFStringRef checksum = NULL, madeChecksum = NULL, passwordNoChecksum = NULL;
1708 CFMutableStringRef randomChars = NULL;
1709 CFStringRef allowedChars = NULL;
1710 bool res = false;
1711
1712 switch (type) {
1713 case kSecPasswordTypeiCloudRecoveryKey:
1714 tupleLengthInt = 4;
1715 numOfTuplesInt = 7;
1716 checkSumChars = 2;
1717 allowedChars = defaultiCloudCharacters;
1718 break;
1719 case kSecPasswordTypeiCloudRecovery:
1720 tupleLengthInt = 4;
1721 numOfTuplesInt = 6;
1722 break;
1723 case(kSecPasswordTypePIN):
1724 tupleLengthInt = 4;
1725 numOfTuplesInt = 1;
1726 break;
1727 default:
1728 SecError(errSecBadReq, error, CFSTR("Password type does not exist."));
1729 return false;
1730 }
1731
1732
1733 if (numOfTuplesInt < 1)
1734 return false;
1735 if (checkSumChars > tupleLengthInt)
1736 return false;
1737
1738 CFIndex numberOfChars = numOfTuplesInt * tupleLengthInt + (numOfTuplesInt - 1);
1739
1740 /*
1741 * First check expected length
1742 */
1743
1744 require(CFStringGetLength(password) == numberOfChars, out); /* N groups of M with (N-1) seperator - in-between */
1745
1746 randomChars = CFStringCreateMutable(SecCFAllocatorZeroize(), 0);
1747 require(randomChars, out);
1748
1749 /*
1750 * make sure dash-es are at the expected spots
1751 */
1752
1753 for (CFIndex n = 0; n < numOfTuplesInt; n++) {
1754 if (n != 0) {
1755 UniChar c = CFStringGetCharacterAtIndex(password, (n * (tupleLengthInt + 1)) - 1);
1756 require(c == '-', out);
1757 }
1758 CFStringRef substr = CFStringCreateWithSubstring(SecCFAllocatorZeroize(), password, CFRangeMake(n * (tupleLengthInt + 1), tupleLengthInt));
1759 CFStringAppend(randomChars, substr);
1760 CFReleaseNull(substr);
1761 }
1762
1763 if (checkSumChars) {
1764 /*
1765 * Pull apart and password and checksum
1766 */
1767
1768 checksum = CFStringCreateWithSubstring(SecCFAllocatorZeroize(), randomChars, CFRangeMake((numOfTuplesInt * tupleLengthInt) - checkSumChars, checkSumChars));
1769 require(checksum, out);
1770
1771 passwordNoChecksum = CFStringCreateWithSubstring(SecCFAllocatorZeroize(), randomChars, CFRangeMake(0, (numOfTuplesInt * tupleLengthInt) - checkSumChars));
1772 require(passwordNoChecksum, out);
1773
1774 /*
1775 * Validate checksum
1776 */
1777
1778 madeChecksum = CreateChecksum(type, passwordNoChecksum, checkSumChars, allowedChars);
1779 require(madeChecksum, out);
1780
1781 require(CFEqual(madeChecksum, checksum), out);
1782 }
1783
1784 res = true;
1785 out:
1786 CFReleaseNull(randomChars);
1787 CFReleaseNull(madeChecksum);
1788 CFReleaseNull(checksum);
1789 CFReleaseNull(passwordNoChecksum);
1790
1791 return res;
1792 }