]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecPasswordGenerate.c
Security-58286.70.7.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 void getPasswordRandomCharacters(CFStringRef *returned, CFDictionaryRef requirements, CFIndex *numberOfRandomCharacters, CFStringRef allowedCharacters)
874 {
875 uint8_t randomNumbers[*numberOfRandomCharacters];
876 unsigned char randomCharacters[*numberOfRandomCharacters];
877 getUniformRandomNumbers(randomNumbers, *numberOfRandomCharacters, CFStringGetLength(allowedCharacters));
878
879 CFTypeRef prohibitedCharacters = NULL;
880 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters))
881 prohibitedCharacters = NULL;
882
883 //it's faster for long characters to check each character produced for these cases
884 for (CFIndex i = 0; i < *numberOfRandomCharacters; ++i){
885 //check prohibited characters
886 UniChar randomChar[1];
887 randomChar[0] = CFStringGetCharacterAtIndex(allowedCharacters, randomNumbers[i]);
888 if (prohibitedCharacters != NULL)
889 {
890 CFStringRef temp = CFStringCreateWithCharacters(kCFAllocatorDefault, randomChar, 1);
891 bool pwdncc = passwordDoesNotContainCharacters(temp, prohibitedCharacters);
892 CFReleaseSafe(temp);
893 if (!pwdncc) {
894 //change up the random numbers so we don't get the same index into allowed
895 getUniformRandomNumbers(randomNumbers, *numberOfRandomCharacters, CFStringGetLength(allowedCharacters));
896 i--;
897 continue;
898 }
899 }
900 randomCharacters[i] = (unsigned char)randomChar[0];
901 }
902
903 *returned = CFStringCreateWithBytes(kCFAllocatorDefault, randomCharacters, *numberOfRandomCharacters, kCFStringEncodingUTF8, false);
904 }
905
906 static bool doesPasswordEndWith(CFStringRef password, CFStringRef prohibitedCharacters)
907 {
908 CFCharacterSetRef characterSet = NULL;
909 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
910
911 CFRange rangeToSearch = CFRangeMake(CFStringGetLength(password) - CFStringGetLength(prohibitedCharacters), CFStringGetLength(prohibitedCharacters));
912 require_quiet(0 == CFStringCompareWithOptions(password, prohibitedCharacters, rangeToSearch, 0), fail);
913 CFReleaseNull(characterSet);
914 return false;
915 fail:
916 CFReleaseNull(characterSet);
917 return true;
918 }
919
920 static bool doesPasswordStartWith(CFStringRef password, CFStringRef prohibitedCharacters)
921 {
922 CFCharacterSetRef characterSet = NULL;
923 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
924
925 CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(prohibitedCharacters));
926 require_quiet(0 == CFStringCompareWithOptions(password, prohibitedCharacters, rangeToSearch, 0), fail);
927 CFReleaseNull(characterSet);
928 return false; //does not start with prohibitedCharacters
929 fail:
930 CFReleaseNull(characterSet);
931 return true;
932 }
933
934 static CFDictionaryRef passwordGenerateCreateDefaultParametersDictionary(SecPasswordType type, CFDictionaryRef requirements){
935
936 CFMutableArrayRef requiredCharacterSets = NULL;
937 CFNumberRef numReqChars = NULL, checksumChars = NULL;
938 CFStringRef defaultPasswordFormat = NULL;
939 requiredCharacterSets = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
940 defaultPasswordFormat = CFSTR("true");
941 CFTypeRef groupSizeRef = NULL, numberOfGroupsRef = NULL;
942 CFIndex groupSize, numberOfGroups, checksumSize = 0;
943 CFDictionaryRef returned = NULL;
944
945 switch(type){
946 case kSecPasswordTypeiCloudRecoveryKey:
947 groupSize = 4;
948 numberOfGroups = 7;
949 checksumSize = 2;
950 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, (groupSize * numberOfGroups) - checksumSize);
951 checksumChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, checksumSize);
952 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
953 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
954
955 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
956 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
957 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
958 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
959 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
960 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
961 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
962 kSecNumberOfChecksumCharactersKey, checksumChars,
963 kSecAllowedCharactersKey, defaultiCloudCharacters,
964 kSecRequiredCharacterSetsKey, requiredCharacterSets,
965 kSecPasswordGroupSize, groupSizeRef,
966 kSecPasswordNumberOfGroups, numberOfGroupsRef,
967 NULL);
968 break;
969 case(kSecPasswordTypeiCloudRecovery):
970 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultiCloudPasswordLength);
971 groupSize = 4;
972 numberOfGroups = 6;
973 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
974 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
975
976 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
977 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
978 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
979 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
980 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
981 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
982 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
983 kSecAllowedCharactersKey, defaultiCloudCharacters,
984 kSecRequiredCharacterSetsKey, requiredCharacterSets,
985 kSecPasswordGroupSize, groupSizeRef,
986 kSecPasswordNumberOfGroups, numberOfGroupsRef,
987 NULL);
988 break;
989
990 case(kSecPasswordTypePIN):
991 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultPINLength);
992 groupSize = 4;
993 numberOfGroups = 1;
994 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
995 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
996
997 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
998 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
999 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1000 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
1001 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
1002 kSecAllowedCharactersKey, defaultPINCharacters,
1003 kSecRequiredCharacterSetsKey, requiredCharacterSets,
1004 kSecPasswordGroupSize, groupSizeRef,
1005 kSecPasswordNumberOfGroups, numberOfGroupsRef,
1006 NULL);
1007 break;
1008
1009 case(kSecPasswordTypeWifi):
1010 groupSize = 4;
1011 numberOfGroups = 3;
1012 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
1013 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
1014
1015 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
1016 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
1017
1018 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultWifiPasswordLength);
1019 CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet);
1020 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
1021 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1022 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
1023 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
1024 kSecAllowedCharactersKey, defaultWifiCharacters,
1025 kSecRequiredCharacterSetsKey, requiredCharacterSets,
1026 kSecPasswordGroupSize, groupSizeRef,
1027 kSecPasswordNumberOfGroups, numberOfGroupsRef,
1028 NULL);
1029 break;
1030
1031 default:
1032 groupSize = 4;
1033 numberOfGroups = 6;
1034 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize);
1035 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups);
1036 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
1037 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
1038 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
1039 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
1040 CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet);
1041 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
1042
1043 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultNumberOfRandomCharacters);
1044 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
1045 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat,
1046 kSecNumberOfRequiredRandomCharactersKey, numReqChars,
1047 kSecAllowedCharactersKey, defaultCharacters,
1048 kSecRequiredCharacterSetsKey, requiredCharacterSets,
1049 kSecPasswordGroupSize, groupSizeRef,
1050 kSecPasswordNumberOfGroups, numberOfGroupsRef,
1051 NULL);
1052
1053
1054
1055 break;
1056 }
1057
1058 CFReleaseNull(numReqChars);
1059 CFReleaseNull(requiredCharacterSets);
1060 CFReleaseNull(groupSizeRef);
1061 CFReleaseNull(numberOfGroupsRef);
1062 CFReleaseNull(checksumChars);
1063 return returned;
1064 }
1065 static CFDictionaryRef passwordGenerationCreateParametersDictionary(SecPasswordType type, CFDictionaryRef requirements)
1066 {
1067 CFMutableArrayRef requiredCharacterSets = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
1068 CFNumberRef numReqChars = NULL;
1069 CFIndex numberOfRequiredRandomCharacters;
1070 CFStringRef allowedCharacters = NULL, useDefaultPasswordFormat = NULL;
1071 uint64_t valuePtr;
1072 CFTypeRef prohibitedCharacters = NULL, endWith = NULL, startWith = NULL,
1073 groupSizeRef = NULL, numberOfGroupsRef = NULL, separatorRef = NULL,
1074 atMostCharactersRef = NULL,atLeastCharactersRef = NULL, identicalRef = NULL;
1075
1076 CFNumberRef min = (CFNumberRef)CFDictionaryGetValue(requirements, kSecPasswordMinLengthKey);
1077 CFNumberRef max = (CFNumberRef)CFDictionaryGetValue(requirements, kSecPasswordMaxLengthKey);
1078
1079 CFNumberGetValue(min, kCFNumberSInt64Type, &valuePtr);
1080 CFIndex minPasswordLength = (long)valuePtr;
1081 CFNumberGetValue(max, kCFNumberSInt64Type, &valuePtr);
1082 CFIndex maxPasswordLength = (long)valuePtr;
1083
1084 // If requirements allow, we will generate the password in default format.
1085 useDefaultPasswordFormat = CFSTR("true");
1086 numberOfRequiredRandomCharacters = defaultNumberOfRandomCharacters;
1087
1088 if(type == kSecPasswordTypePIN)
1089 {
1090 if( maxPasswordLength && minPasswordLength )
1091 numberOfRequiredRandomCharacters = maxPasswordLength;
1092 else if( !maxPasswordLength && minPasswordLength )
1093 numberOfRequiredRandomCharacters = minPasswordLength;
1094 else if( !minPasswordLength && maxPasswordLength )
1095 numberOfRequiredRandomCharacters = maxPasswordLength;
1096 else
1097 numberOfRequiredRandomCharacters = defaultPINLength;
1098
1099 allowedCharacters = CFSTR("0123456789");
1100 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
1101 useDefaultPasswordFormat = CFSTR("false");
1102 }
1103 else{
1104 CFArrayRef requiredCharactersArray = NULL;
1105
1106 if (minPasswordLength && minPasswordLength > defaultNumberOfRandomCharacters) {
1107 useDefaultPasswordFormat = CFSTR("false");
1108 numberOfRequiredRandomCharacters = minPasswordLength;
1109 }
1110 if (maxPasswordLength && maxPasswordLength < defaultNumberOfRandomCharacters) {
1111 useDefaultPasswordFormat = CFSTR("false");
1112 numberOfRequiredRandomCharacters = maxPasswordLength;
1113 }
1114 if (maxPasswordLength && minPasswordLength && maxPasswordLength == minPasswordLength && maxPasswordLength != defaultNumberOfRandomCharacters){
1115 useDefaultPasswordFormat = CFSTR("false");
1116 numberOfRequiredRandomCharacters = maxPasswordLength;
1117 }
1118 allowedCharacters = (CFStringRef)CFRetainSafe(CFDictionaryGetValue(requirements, kSecPasswordAllowedCharactersKey));
1119 requiredCharactersArray = (CFArrayRef)CFDictionaryGetValue(requirements, kSecPasswordRequiredCharactersKey);
1120
1121 if (requiredCharactersArray) {
1122 for (CFIndex i = 0; i < CFArrayGetCount(requiredCharactersArray); i++){
1123 CFCharacterSetRef stringWithRequiredCharacters = CFArrayGetValueAtIndex(requiredCharactersArray, i);
1124 if(stringWithRequiredCharacters && CFStringFindCharacterFromSet(allowedCharacters, stringWithRequiredCharacters, CFRangeMake(0, CFStringGetLength(allowedCharacters)), 0, NULL)){
1125 CFArrayAppendValue(requiredCharacterSets, stringWithRequiredCharacters);
1126 }
1127 }
1128 } else{
1129 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
1130 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
1131 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
1132 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
1133 CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet);
1134 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
1135 }
1136 }
1137
1138 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters))
1139 prohibitedCharacters = NULL;
1140
1141 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantEndWithChars, &endWith))
1142 endWith = NULL;
1143
1144 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantStartWithChars, &startWith))
1145 startWith = NULL;
1146
1147 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordGroupSize, &groupSizeRef))
1148 groupSizeRef = NULL;
1149
1150 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef))
1151 numberOfGroupsRef = NULL;
1152
1153 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordSeparator, &separatorRef))
1154 separatorRef = NULL;
1155
1156 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharactersRef))
1157 atMostCharactersRef = NULL;
1158
1159 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharactersRef))
1160 atLeastCharactersRef = NULL;
1161
1162 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef))
1163 identicalRef = NULL;
1164
1165 if (allowedCharacters) {
1166 if( false == CFStringFindWithOptions(allowedCharacters, CFSTR("-"), CFRangeMake(0, CFStringGetLength(allowedCharacters)), kCFCompareCaseInsensitive, NULL))
1167 useDefaultPasswordFormat = CFSTR("false");
1168 } else
1169 allowedCharacters = CFRetainSafe(defaultCharacters);
1170
1171 // In default password format, we use dashes only as separators, not as symbols you can encounter at a random position.
1172 if (useDefaultPasswordFormat == CFSTR("false")){
1173 CFMutableStringRef mutatedAllowedCharacters = CFStringCreateMutableCopy(kCFAllocatorDefault, CFStringGetLength(allowedCharacters), allowedCharacters);
1174 CFStringFindAndReplace (mutatedAllowedCharacters, CFSTR("-"), CFSTR(""), CFRangeMake(0, CFStringGetLength(allowedCharacters)),kCFCompareCaseInsensitive);
1175 CFReleaseSafe(allowedCharacters);
1176 allowedCharacters = mutatedAllowedCharacters;
1177 }
1178
1179 if (CFArrayGetCount(requiredCharacterSets) > numberOfRequiredRandomCharacters) {
1180 CFReleaseNull(requiredCharacterSets);
1181 requiredCharacterSets = NULL;
1182 }
1183 //create new CFDictionary
1184 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, numberOfRequiredRandomCharacters);
1185 CFMutableDictionaryRef updatedConstraints = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1186 CFDictionaryAddValue(updatedConstraints, kSecUseDefaultPasswordFormatKey, useDefaultPasswordFormat);
1187 CFDictionarySetValue(updatedConstraints, kSecNumberOfRequiredRandomCharactersKey, numReqChars);
1188 CFDictionaryAddValue(updatedConstraints, kSecAllowedCharactersKey, allowedCharacters);
1189 if(requiredCharacterSets)
1190 CFDictionaryAddValue(updatedConstraints, kSecRequiredCharacterSetsKey, requiredCharacterSets);
1191
1192 //add the prohibited characters string if it exists to the new dictionary
1193 if(prohibitedCharacters)
1194 CFDictionaryAddValue(updatedConstraints, kSecPasswordDisallowedCharacters, prohibitedCharacters);
1195
1196 //add the characters the password can't end with if it exists to the new dictionary
1197 if(endWith)
1198 CFDictionaryAddValue(updatedConstraints, kSecPasswordCantEndWithChars, endWith);
1199
1200 //add the characters the password can't start with if it exists to the new dictionary
1201 if(startWith)
1202 CFDictionaryAddValue(updatedConstraints, kSecPasswordCantStartWithChars, startWith);
1203
1204 if(groupSizeRef)
1205 CFDictionaryAddValue(updatedConstraints, kSecPasswordGroupSize, groupSizeRef);
1206
1207 if(numberOfGroupsRef)
1208 CFDictionaryAddValue(updatedConstraints, kSecPasswordNumberOfGroups, numberOfGroupsRef);
1209
1210 if(separatorRef)
1211 CFDictionaryAddValue(updatedConstraints, kSecPasswordSeparator, separatorRef);
1212
1213 if(atMostCharactersRef)
1214 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsNoMoreThanNSpecificCharacters, atMostCharactersRef);
1215
1216 if(atLeastCharactersRef)
1217 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsAtLeastNSpecificCharacters, atLeastCharactersRef);
1218
1219 if(identicalRef)
1220 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, identicalRef);
1221
1222 CFReleaseNull(useDefaultPasswordFormat);
1223 CFReleaseNull(numReqChars);
1224 CFReleaseNull(allowedCharacters);
1225 CFReleaseNull(requiredCharacterSets);
1226
1227 return updatedConstraints;
1228 }
1229
1230 static bool isDictionaryFormattedProperly(SecPasswordType type, CFDictionaryRef passwordRequirements, CFErrorRef *error){
1231
1232 CFTypeRef defaults = NULL;
1233 CFErrorRef tempError = NULL;
1234 if(passwordRequirements == NULL){
1235 return true;
1236 }
1237
1238 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) ){
1239 if(isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0)){
1240 return true;
1241 }
1242 }
1243 //only need to check max and min pin length formatting
1244 if(type == kSecPasswordTypePIN){
1245 CFTypeRef minTest = NULL, maxTest = NULL;
1246 uint64_t valuePtr;
1247 CFIndex minPasswordLength = 0, maxPasswordLength= 0;
1248
1249 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) ){
1250 if(isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0)){
1251 return true;
1252 }
1253 }
1254 //check if the values exist!
1255 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMaxLengthKey, &maxTest) ){
1256 require_action_quiet(isNull(maxTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL));
1257 require_action_quiet(isNumber(maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
1258
1259 }
1260 if (CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMinLengthKey, &minTest) ){
1261 require_action_quiet(isNull(minTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL));
1262 require_action_quiet(isNumber(minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
1263 }
1264 //check if the values exist!
1265 if(maxTest){
1266 CFNumberRef max = (CFNumberRef)maxTest;
1267 CFNumberGetValue(max, kCFNumberSInt64Type, &valuePtr);
1268 maxPasswordLength = (long)valuePtr;
1269 }
1270 if(minTest){
1271 CFNumberRef min = (CFNumberRef)minTest;
1272 CFNumberGetValue(min, kCFNumberSInt64Type, &valuePtr);
1273 minPasswordLength = (long)valuePtr;
1274 }
1275 //make sure min and max make sense respective to each other and that they aren't less than 4 digits.
1276 require_action_quiet(minPasswordLength && maxPasswordLength && minPasswordLength <= maxPasswordLength, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex)errSecBadReq, NULL));
1277 require_action_quiet((minPasswordLength && minPasswordLength >= 4) || (maxPasswordLength && maxPasswordLength >= 4), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex)errSecBadReq, NULL));
1278 }
1279 else{
1280 CFTypeRef allowedTest, maxTest, minTest, requiredTest, prohibitedCharacters, endWith, startWith,
1281 groupSizeRef, numberOfGroupsRef, separatorRef, atMostCharactersRef,
1282 atLeastCharactersRef, thresholdRef, identicalRef, characters;
1283 uint64_t valuePtr;
1284
1285 //check if the values exist!
1286 require_action_quiet(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordAllowedCharactersKey, &allowedTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need a string of characters; password must only contain characters in this string"), (CFIndex)errSecBadReq, NULL));
1287 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMaxLengthKey, &maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL));
1288 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMinLengthKey, &minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL));
1289 require_action_quiet(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordRequiredCharactersKey, &requiredTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need an array of character sets, password must have at least 1 character from each set"), (CFIndex)errSecBadReq, NULL));
1290
1291 //check if values are null?
1292 require_action_quiet(isNull(allowedTest) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need a string of characters; password must only contain characters in this string"), (CFIndex)errSecBadReq, NULL));
1293 require_action_quiet(isNull(maxTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL));
1294 require_action_quiet(isNull(minTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL));
1295 require_action_quiet(isNull(requiredTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need an array of character sets, password must have at least 1 character from each set"), (CFIndex)errSecBadReq, NULL));
1296
1297 //check if the values are correct
1298 require_action_quiet(isString(allowedTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's allowed characters must be a CFStringRef"), (CFIndex)errSecBadReq, NULL));
1299 require_action_quiet(isNumber(maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
1300 require_action_quiet(isNumber(minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
1301 require_action_quiet(isArray(requiredTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's required characters must be an array of CFCharacterSetRefs"), (CFIndex)errSecBadReq, NULL));
1302
1303 CFNumberGetValue(minTest, kCFNumberSInt64Type, &valuePtr);
1304 CFIndex minPasswordLength = (long)valuePtr;
1305 CFNumberGetValue(maxTest, kCFNumberSInt64Type, &valuePtr);
1306 CFIndex maxPasswordLength = (long)valuePtr;
1307
1308 require_action_quiet(minPasswordLength <= maxPasswordLength, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex)errSecBadReq, NULL));
1309
1310 require_action_quiet(CFStringGetLength((CFStringRef)allowedTest) != 0, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need a string of characters; password must only contain characters in this string"), (CFIndex)errSecBadReq, NULL));
1311 require_action_quiet(CFArrayGetCount((CFArrayRef)requiredTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need an array of character sets, password must have at least 1 character from each set"), (CFIndex)errSecBadReq, NULL));
1312
1313 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters)){
1314 require_action_quiet(isNull(prohibitedCharacters) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Disallowed Characters dictionary parameter is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1315 require_action_quiet(isString(prohibitedCharacters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Disallowed Characters dictionary parameter is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1316 }
1317 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordCantEndWithChars, &endWith)){
1318 require_action_quiet(isNull(endWith) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'EndWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1319 require_action_quiet(isString(endWith), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'EndWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1320 }
1321 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordCantStartWithChars, &startWith)){
1322 require_action_quiet(isNull(startWith) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'StartWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1323 require_action_quiet(isString(startWith), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'StartWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1324 }
1325 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordGroupSize, &groupSizeRef)){
1326 require_action_quiet(isNull(groupSizeRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'groupsize' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1327 require_action_quiet(isNumber(groupSizeRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'groupsize' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1328 }
1329 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef)){
1330 require_action_quiet(isNull(numberOfGroupsRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'number of groupds' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1331 require_action_quiet(isNumber(numberOfGroupsRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'number of groupds' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1332 }
1333 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordSeparator, &separatorRef)){
1334 require_action_quiet(isNull(separatorRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'password separator character' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1335 require_action_quiet(isString(separatorRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'password separator character' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1336 }
1337
1338 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharactersRef)){
1339 require_action_quiet(isNull(atMostCharactersRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Most N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1340 require_action_quiet(isDictionary(atMostCharactersRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Most N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1341
1342 require_action_quiet(CFDictionaryGetValueIfPresent(atMostCharactersRef, kSecPasswordCharacterCount, &thresholdRef) != false, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Most N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1343 require_action_quiet(isNull(thresholdRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1344 require_action_quiet(isNumber(thresholdRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1345
1346 require_action_quiet(CFDictionaryGetValueIfPresent(atMostCharactersRef, kSecPasswordCharacters, &characters)!= false, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Most N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1347 require_action_quiet(isNull(characters) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1348 require_action_quiet(isString(characters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1349 }
1350
1351 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharactersRef)){
1352 require_action_quiet(isNull(atLeastCharactersRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Least N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1353 require_action_quiet(isDictionary(atLeastCharactersRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Least N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1354
1355 require_action_quiet(CFDictionaryGetValueIfPresent(atLeastCharactersRef, kSecPasswordCharacterCount, &thresholdRef) != false, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Least N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1356
1357 require_action_quiet(isNull(thresholdRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1358 require_action_quiet(isNumber(thresholdRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1359
1360 require_action_quiet(CFDictionaryGetValueIfPresent(atLeastCharactersRef, kSecPasswordCharacters, &characters) != false, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Least N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1361 require_action_quiet(isNull(characters) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1362 require_action_quiet(isString(characters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
1363 }
1364
1365 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef)){
1366 require_action_quiet(isNull(identicalRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Identical Consecutive Characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1367 require_action_quiet(isNumber(identicalRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Identical Consecutive Characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
1368 }
1369 }
1370
1371 fail:
1372 {
1373 bool result = true;
1374 if (tempError != NULL) {
1375 if (error)
1376 *error = CFRetainSafe(tempError);
1377 result = false;
1378 }
1379
1380 CFReleaseNull(tempError);
1381 return result;
1382 }
1383 }
1384
1385 static bool doesFinalPasswordPass(bool isSimple, CFStringRef password, CFDictionaryRef requirements){
1386
1387 CFTypeRef characters, identicalRef = NULL, NRef = NULL, endWith= NULL, startWith= NULL, atLeastCharacters= NULL, atMostCharacters = NULL;
1388 uint64_t valuePtr;
1389 CFIndex N, identicalCount = 0;
1390 CFArrayRef requiredCharacterSet = (CFArrayRef)CFDictionaryGetValue(requirements, kSecRequiredCharacterSetsKey);
1391
1392 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantEndWithChars, &endWith))
1393 endWith = NULL;
1394
1395 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantStartWithChars, &startWith))
1396 startWith = NULL;
1397
1398 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharacters))
1399 atLeastCharacters = NULL;
1400
1401 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharacters))
1402 atMostCharacters = NULL;
1403
1404 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef))
1405 identicalRef = NULL;
1406 else{
1407 CFNumberGetValue((CFNumberRef)identicalRef, kCFNumberSInt64Type, &valuePtr);
1408 identicalCount = (long)valuePtr;
1409 }
1410 if(endWith != NULL)
1411 {
1412 if(!doesPasswordEndWith(password, endWith))
1413 return false;
1414 }
1415 if(startWith != NULL){
1416 if(!doesPasswordStartWith(password, startWith))
1417 return false;
1418 }
1419 if(atLeastCharacters != NULL){
1420 NRef = CFDictionaryGetValue(atLeastCharacters, kSecPasswordCharacterCount);
1421 characters = CFDictionaryGetValue(atLeastCharacters, kSecPasswordCharacters);
1422 CFNumberGetValue((CFNumberRef)NRef, kCFNumberSInt64Type, &valuePtr);
1423 N = (long)valuePtr;
1424 if(!passwordContainsAtLeastNCharacters(password, characters, N))
1425 return false;
1426 }
1427 if(atMostCharacters != NULL){
1428 NRef = CFDictionaryGetValue(atMostCharacters, kSecPasswordCharacterCount);
1429 characters = CFDictionaryGetValue(atMostCharacters, kSecPasswordCharacters);
1430 CFNumberGetValue((CFNumberRef)NRef, kCFNumberSInt64Type, &valuePtr);
1431 N = (long)valuePtr;
1432 if(!passwordContainsLessThanNCharacters(password, characters, N))
1433 return false;
1434 }
1435 if(identicalRef != NULL){
1436 if(!passwordContainsLessThanNIdenticalCharacters(password, identicalCount))
1437 return false;
1438 }
1439 if (!passwordContainsRequiredCharacters(password, requiredCharacterSet))
1440 return false;
1441
1442 if(true == SecPasswordIsPasswordWeak2(isSimple, password))
1443 return false;
1444
1445 return true;
1446 }
1447
1448 static CFStringRef
1449 CreateChecksum(SecPasswordType type, CFStringRef password, CFIndex length, CFStringRef allowedChars)
1450 {
1451 if (type != kSecPasswordTypeiCloudRecoveryKey)
1452 return NULL;
1453
1454 CFMutableStringRef checksum = NULL;
1455 uint8_t digest[CCSHA256_OUTPUT_SIZE];
1456 if (length > (CFIndex)sizeof(digest))
1457 return NULL;
1458
1459 CFDataRef data = CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), password, kCFStringEncodingUTF8, 0);
1460 if (data == NULL)
1461 return NULL;
1462
1463 ccdigest(ccsha256_di(), CFDataGetLength(data), CFDataGetBytePtr(data), digest);
1464 CFReleaseNull(data);
1465
1466 CFIndex allowedCharLength = CFStringGetLength(allowedChars);
1467
1468 checksum = CFStringCreateMutable(SecCFAllocatorZeroize(), 0);
1469 for (CFIndex n = 0; n < length; n++) {
1470 CFIndex selection = digest[n] % allowedCharLength;
1471 UniChar c = CFStringGetCharacterAtIndex(allowedChars, selection);
1472 CFStringAppendCharacters(checksum, &c, 1);
1473 }
1474
1475 return checksum;
1476 }
1477
1478 //entry point into password generation
1479 CF_RETURNS_RETAINED CFStringRef SecPasswordGenerate(SecPasswordType type, CFErrorRef *error, CFDictionaryRef passwordRequirements){
1480 bool check = false, isSimple = false;
1481 CFTypeRef separator = NULL, defaults = NULL, groupSizeRef = NULL, numberOfGroupsRef = NULL;
1482 CFDictionaryRef properlyFormattedRequirements = NULL;
1483 CFErrorRef localError = NULL;
1484 uint64_t valuePtr, groupSize = 0, numberOfGroups, checksumChars = 0;
1485 CFNumberRef numberOfRequiredRandomCharacters, checksumCharacters;
1486 CFIndex requiredCharactersSize = 0;
1487 CFStringRef randomCharacters = NULL, password = NULL, allowedChars = NULL;
1488 CFMutableStringRef finalPassword = NULL;
1489
1490 if(type == kSecPasswordTypePIN)
1491 isSimple = true;
1492 else
1493 isSimple = false;
1494 check = isDictionaryFormattedProperly(type, passwordRequirements, &localError);
1495 require_quiet(check != false, fail);
1496
1497 //should we generate defaults?
1498 if(passwordRequirements == NULL || (CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) && isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0) ))
1499 properlyFormattedRequirements = passwordGenerateCreateDefaultParametersDictionary(type, passwordRequirements);
1500 else
1501 properlyFormattedRequirements = passwordGenerationCreateParametersDictionary(type, passwordRequirements);
1502
1503 require_quiet(localError == NULL && properlyFormattedRequirements != NULL, fail);
1504
1505 numberOfRequiredRandomCharacters = (CFNumberRef)CFDictionaryGetValue(properlyFormattedRequirements, kSecNumberOfRequiredRandomCharactersKey);
1506 if (isNumber(numberOfRequiredRandomCharacters) && CFNumberGetValue(numberOfRequiredRandomCharacters, kCFNumberSInt64Type, &valuePtr))
1507 requiredCharactersSize = (long)valuePtr;
1508
1509 checksumCharacters = (CFNumberRef)CFDictionaryGetValue(properlyFormattedRequirements, kSecNumberOfChecksumCharactersKey);
1510 if (isNumber(checksumCharacters) && CFNumberGetValue(checksumCharacters, kCFNumberSInt64Type, &valuePtr))
1511 checksumChars = (long)valuePtr;
1512
1513
1514 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordGroupSize, &groupSizeRef)){
1515 groupSizeRef = NULL;
1516 }
1517 else
1518 CFNumberGetValue((CFNumberRef)groupSizeRef, kCFNumberSInt64Type, &groupSize);
1519
1520 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef)){
1521 numberOfGroupsRef = NULL;
1522 }
1523 else
1524 CFNumberGetValue((CFNumberRef)numberOfGroupsRef, kCFNumberSInt64Type, &numberOfGroups);
1525
1526 require(requiredCharactersSize, fail);
1527
1528 while (true) {
1529 allowedChars = CFDictionaryGetValue(properlyFormattedRequirements, kSecAllowedCharactersKey);
1530 getPasswordRandomCharacters(&randomCharacters, properlyFormattedRequirements, &requiredCharactersSize, allowedChars);
1531
1532 if(numberOfGroupsRef && groupSizeRef){
1533 finalPassword = CFStringCreateMutable(kCFAllocatorDefault, 0);
1534
1535 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordSeparator, &separator))
1536 separator = NULL;
1537
1538 if(separator == NULL)
1539 separator = CFSTR("-");
1540
1541 CFIndex i = 0;
1542 while( i != requiredCharactersSize){
1543 if((i + (CFIndex)groupSize) < requiredCharactersSize){
1544 CFStringRef subString = CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, (CFIndex)groupSize));
1545 CFStringAppend(finalPassword, subString);
1546 CFStringAppend(finalPassword, separator);
1547 CFReleaseSafe(subString);
1548 i+=groupSize;
1549 }
1550 else if((i+(CFIndex)groupSize) == requiredCharactersSize){
1551 CFStringRef subString = CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, (CFIndex)groupSize));
1552 CFStringAppend(finalPassword, subString);
1553 CFReleaseSafe(subString);
1554 i+=groupSize;
1555 }
1556 else {
1557 CFStringRef subString = CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, requiredCharactersSize - i));
1558 CFStringAppend(finalPassword, subString);
1559 CFReleaseSafe(subString);
1560 i+=(requiredCharactersSize - i);
1561 }
1562 }
1563 if (checksumChars) {
1564 CFStringRef checksum = CreateChecksum(type, randomCharacters, (CFIndex)checksumChars, allowedChars);
1565 CFStringAppend(finalPassword, checksum);
1566 CFReleaseNull(checksum);
1567 }
1568 password = CFStringCreateCopy(kCFAllocatorDefault, finalPassword);
1569 CFReleaseNull(finalPassword);
1570 }
1571 //no fancy formatting
1572 else {
1573 password = CFStringCreateCopy(kCFAllocatorDefault, randomCharacters);
1574 }
1575
1576 CFReleaseNull(randomCharacters);
1577 require_quiet(doesFinalPasswordPass(isSimple, password, properlyFormattedRequirements), no_pass);
1578 CFReleaseNull(properlyFormattedRequirements);
1579 return password;
1580
1581 no_pass:
1582 CFReleaseNull(password);
1583 }
1584
1585 fail:
1586 if (error && localError) {
1587 *error = localError;
1588 localError = NULL;
1589 }
1590
1591 CFReleaseSafe(localError);
1592 CFReleaseNull(properlyFormattedRequirements);
1593 return NULL;
1594 }
1595
1596 const char *in_word_set (const char *str, unsigned int len){
1597 static const char * wordlist[] =
1598 {
1599 "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
1600 "", "", "0103", "", "", "", "", "0123", "", "", "", "", "0303", "", "", "",
1601 "", "", "", "", "0110", "", "1103", "", "", "", "", "1123", "", "", "0000",
1602 "", "1203", "", "0404", "", "", "", "", "1234", "1110", "2015", "2013", "",
1603 "2014", "1010", "2005", "2003", "", "2004", "1210", "0505", "0111", "", "",
1604 "", "2008", "0101", "", "2007", "", "", "", "", "2006", "2010", "1995", "1993",
1605 "", "1994", "2000", "", "1111", "", "", "", "1998", "1101", "", "1997", "",
1606 "0808", "1211", "", "1996", "0102", "", "1201", "", "", "1990", "", "", "",
1607 "", "0202", "", "2011", "", "", "1112", "1958", "2001", "", "1957", "1102",
1608 "", "3333", "", "1956", "1212", "1985", "1983", "", "1984", "1202", "", "0909",
1609 "", "0606", "", "1988", "1991", "", "1987", "2012", "", "", "", "1986", "2002",
1610 "", "", "", "0707", "1980", "", "2009", "", "", "2222", "1965", "1963", "",
1611 "1964", "", "", "2229", "", "", "1992", "1968", "", "", "1967", "", "", "1999",
1612 "", "1966", "", "1975", "1973", "", "1974", "1960", "", "1981", "", "4444",
1613 "", "1978", "", "7465", "1977", "", "", "", "", "1976", "2580", "", "1959",
1614 "", "", "1970", "", "", "", "", "", "", "", "", "", "1982", "", "1961", "",
1615 "", "5252", "", "1989", "", "", "", "", "", "", "", "", "", "", "", "", "",
1616 "", "1971", "", "", "", "", "", "", "", "1962", "", "5683", "", "6666", "",
1617 "", "1969", "", "", "", "", "", "", "", "", "", "", "", "", "1972", "", "",
1618 "", "", "", "", "1979", "", "", "", "7667"
1619 };
1620
1621 if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
1622 {
1623 register int key = pinhash (str, len);
1624
1625 if (key <= MAX_HASH_VALUE && key >= 0)
1626 {
1627 register const char *s = wordlist[key];
1628 if (*str == *s && !strcmp (str + 1, s + 1))
1629 return s;
1630 }
1631 }
1632 return 0;
1633 }
1634 CFDictionaryRef SecPasswordCopyDefaultPasswordLength(SecPasswordType type, CFErrorRef *error){
1635
1636 CFIndex tupleLengthInt = 0, numOfTuplesInt = 0;
1637 CFNumberRef tupleLength = NULL;
1638 CFNumberRef numOfTuples = NULL;
1639
1640 CFMutableDictionaryRef passwordLengthDefaults = NULL;
1641 CFDictionaryRef result = NULL;
1642
1643 switch(type){
1644 case(kSecPasswordTypeiCloudRecoveryKey):
1645 tupleLengthInt = 4;
1646 numOfTuplesInt = 7;
1647 break;
1648
1649 case(kSecPasswordTypeiCloudRecovery):
1650 tupleLengthInt = 4;
1651 numOfTuplesInt = 6;
1652 break;
1653
1654 case(kSecPasswordTypePIN):
1655 tupleLengthInt = 4;
1656 numOfTuplesInt = 1;
1657 break;
1658
1659 case(kSecPasswordTypeSafari):
1660 tupleLengthInt = 4;
1661 numOfTuplesInt = 5;
1662 break;
1663
1664 case(kSecPasswordTypeWifi):
1665 tupleLengthInt = 4;
1666 numOfTuplesInt = 3;
1667 break;
1668
1669 default:
1670 if(SecError(errSecBadReq, error, CFSTR("Password type does not exist.")) == false)
1671 {
1672 secdebug("secpasswordcopydefaultpasswordlength", "could not create error!");
1673 }
1674 }
1675
1676 if (tupleLengthInt != 0 && numOfTuplesInt != 0) {
1677 tupleLength = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &tupleLengthInt);
1678 numOfTuples = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &numOfTuplesInt);
1679 passwordLengthDefaults = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
1680 CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordGroupSize, tupleLength);
1681 CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordNumberOfGroups, numOfTuples);
1682 result = CFDictionaryCreateCopy(kCFAllocatorDefault, passwordLengthDefaults);
1683 }
1684
1685 CFReleaseSafe(tupleLength);
1686 CFReleaseSafe(numOfTuples);
1687 CFReleaseSafe(passwordLengthDefaults);
1688 return result;
1689 }
1690
1691 bool
1692 SecPasswordValidatePasswordFormat(SecPasswordType type, CFStringRef password, CFErrorRef *error)
1693 {
1694 CFIndex tupleLengthInt = 0, numOfTuplesInt = 0, checkSumChars = 0;
1695 CFStringRef checksum = NULL, madeChecksum = NULL, passwordNoChecksum = NULL;
1696 CFMutableStringRef randomChars = NULL;
1697 CFStringRef allowedChars = NULL;
1698 bool res = false;
1699
1700 switch (type) {
1701 case kSecPasswordTypeiCloudRecoveryKey:
1702 tupleLengthInt = 4;
1703 numOfTuplesInt = 7;
1704 checkSumChars = 2;
1705 allowedChars = defaultiCloudCharacters;
1706 break;
1707 case kSecPasswordTypeiCloudRecovery:
1708 tupleLengthInt = 4;
1709 numOfTuplesInt = 6;
1710 break;
1711 case(kSecPasswordTypePIN):
1712 tupleLengthInt = 4;
1713 numOfTuplesInt = 1;
1714 break;
1715 default:
1716 SecError(errSecBadReq, error, CFSTR("Password type does not exist."));
1717 return false;
1718 }
1719
1720
1721 if (numOfTuplesInt < 1)
1722 return false;
1723 if (checkSumChars > tupleLengthInt)
1724 return false;
1725
1726 CFIndex numberOfChars = numOfTuplesInt * tupleLengthInt + (numOfTuplesInt - 1);
1727
1728 /*
1729 * First check expected length
1730 */
1731
1732 require(CFStringGetLength(password) == numberOfChars, out); /* N groups of M with (N-1) seperator - in-between */
1733
1734 randomChars = CFStringCreateMutable(SecCFAllocatorZeroize(), 0);
1735 require(randomChars, out);
1736
1737 /*
1738 * make sure dash-es are at the expected spots
1739 */
1740
1741 for (CFIndex n = 0; n < numOfTuplesInt; n++) {
1742 if (n != 0) {
1743 UniChar c = CFStringGetCharacterAtIndex(password, (n * (tupleLengthInt + 1)) - 1);
1744 require(c == '-', out);
1745 }
1746 CFStringRef substr = CFStringCreateWithSubstring(SecCFAllocatorZeroize(), password, CFRangeMake(n * (tupleLengthInt + 1), tupleLengthInt));
1747 CFStringAppend(randomChars, substr);
1748 CFReleaseNull(substr);
1749 }
1750
1751 if (checkSumChars) {
1752 /*
1753 * Pull apart and password and checksum
1754 */
1755
1756 checksum = CFStringCreateWithSubstring(SecCFAllocatorZeroize(), randomChars, CFRangeMake((numOfTuplesInt * tupleLengthInt) - checkSumChars, checkSumChars));
1757 require(checksum, out);
1758
1759 passwordNoChecksum = CFStringCreateWithSubstring(SecCFAllocatorZeroize(), randomChars, CFRangeMake(0, (numOfTuplesInt * tupleLengthInt) - checkSumChars));
1760 require(passwordNoChecksum, out);
1761
1762 /*
1763 * Validate checksum
1764 */
1765
1766 madeChecksum = CreateChecksum(type, passwordNoChecksum, checkSumChars, allowedChars);
1767 require(madeChecksum, out);
1768
1769 require(CFEqual(madeChecksum, checksum), out);
1770 }
1771
1772 res = true;
1773 out:
1774 CFReleaseNull(randomChars);
1775 CFReleaseNull(madeChecksum);
1776 CFReleaseNull(checksum);
1777 CFReleaseNull(passwordNoChecksum);
1778
1779 return res;
1780 }