1/*
2 * Copyright (c) 2013 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
41// Keys for external dictionaries with password generation requirements we read from plist.
42CFStringRef kSecPasswordMinLengthKey = CFSTR("PasswordMinLength");
43CFStringRef kSecPasswordMaxLengthKey = CFSTR("PasswordMaxLength");
44CFStringRef kSecPasswordAllowedCharactersKey = CFSTR("PasswordAllowedCharacters");
45CFStringRef kSecPasswordRequiredCharactersKey = CFSTR("PasswordRequiredCharacters");
46CFStringRef kSecPasswordDefaultForType = CFSTR("PasswordDefaultForType");
47
48CFStringRef kSecPasswordDisallowedCharacters = CFSTR("PasswordDisallowedCharacters");
49CFStringRef kSecPasswordCantStartWithChars = CFSTR("PasswordCantStartWithChars");
50CFStringRef kSecPasswordCantEndWithChars = CFSTR("PasswordCantEndWithChars");
51CFStringRef kSecPasswordContainsNoMoreThanNSpecificCharacters = CFSTR("PasswordContainsNoMoreThanNSpecificCharacters");
52CFStringRef kSecPasswordContainsAtLeastNSpecificCharacters = CFSTR("PasswordContainsAtLeastNSpecificCharacters");
53CFStringRef kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters = CFSTR("PasswordContainsNoMoreThanNConsecutiveIdenticalCharacters");
54CFStringRef kSecPasswordCharacterCount = CFSTR("PasswordCharacterCount");
55CFStringRef kSecPasswordCharacters = CFSTR("PasswordCharacters");
56
57CFStringRef kSecPasswordGroupSize = CFSTR("PasswordGroupSize");
58CFStringRef kSecPasswordNumberOfGroups = CFSTR("PasswordNumberOfGroups");
59CFStringRef kSecPasswordSeparator = CFSTR("SecPasswordSeparator");
60
61// Keys for internally used dictionaries with password generation parameters (never exposed to external API).
62static CFStringRef kSecUseDefaultPasswordFormatKey = CFSTR("UseDefaultPasswordFormat");
63static CFStringRef kSecNumberOfRequiredRandomCharactersKey = CFSTR("NumberOfRequiredRandomCharacters");
64static CFStringRef kSecAllowedCharactersKey = CFSTR("AllowedCharacters");
65static CFStringRef kSecRequiredCharacterSetsKey = CFSTR("RequiredCharacterSets");
66
67static CFIndex defaultNumberOfRandomCharacters = 20;
68static CFIndex defaultPINLength = 4;
69static CFIndex defaultiCloudPasswordLength = 24;
70static CFIndex defaultWifiPasswordLength = 12;
71
72static CFStringRef defaultWifiCharacters = CFSTR("abcdefghijklmnopqrstuvwxyz1234567890");
73static CFStringRef defaultPINCharacters = CFSTR("0123456789");
74static CFStringRef defaultiCloudCharacters = CFSTR("ABCDEFGHJKLMNPQRSTUVWXYZ23456789");
75static CFStringRef defaultCharacters = CFSTR("abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789");
76
77static CFCharacterSetRef uppercaseLetterCharacterSet;
78static CFCharacterSetRef lowercaseLetterCharacterSet;
79static CFCharacterSetRef decimalDigitCharacterSet;
80static CFCharacterSetRef punctuationCharacterSet;
81
82static CFIndex alphabetSetSize = 26;
83static CFIndex decimalSetSize = 10;
84static CFIndex punctuationSetSize = 33;
85static double entropyStrengthThreshold = 35.0;
86
87/*
88 generated with ruby badpins.rb | gperf
89 See this for PIN list:
90 A birthday present every eleven wallets? The security of customer-chosen banking PINs (2012),  by Joseph Bonneau , Sören Preibusch , Ross Anderson
91 */
92const char *in_word_set (const char *str, unsigned int len);
93
94#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
95&& ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
96&& (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
97&& ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
98&& ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
99&& ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
100&& ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
101&& ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
102&& ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
103&& ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
104&& ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
105&& ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
106&& ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
107&& ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
108&& ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
109&& ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
110&& ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
111&& ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
112&& ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
113&& ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
114&& ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
115&& ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
116&& ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
117/* The character set is not based on ISO-646.  */
118error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
119#endif
120
121
122#define TOTAL_KEYWORDS 100
123#define MIN_WORD_LENGTH 4
124#define MAX_WORD_LENGTH 4
125#define MIN_HASH_VALUE 21
126#define MAX_HASH_VALUE 275
127/* maximum key range = 255, duplicates = 0 */
128
129#ifdef __GNUC__
130__inline
131#else
132#ifdef __cplusplus
133inline
134#endif
135#endif
136static unsigned int pinhash (const char *str, unsigned int len)
137{
138    static unsigned short asso_values[] =
139    {
140        276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
141        276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
142        276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
143        276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
144        276, 276, 276, 276, 276, 276, 276, 276,   5,   0,
145        10,  10,  30,  50, 100, 120,  70,  25,  57,  85,
146        2,   4,   1,  19,  14,  11,  92, 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, 276, 276,
150        276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
151        276, 276, 276, 276, 276, 276, 276, 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
167    };
168    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];
169}
170
171//pins that reached the top 20 list
172static const char *blacklist[] = {"1234", "1004", "2000", "1122", "4321", "2001", "2580"};
173
174bool SecPasswordIsPasswordWeak(CFStringRef passcode)
175{
176    uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
177    lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
178    decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
179    punctuationCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetPunctuation);
180
181    bool isNumber = true;
182    char* pin = NULL;
183
184    //length checks
185    if( CFStringGetLength(passcode) < 4 ){
186        return true; //weak password
187    }
188    //check to see if passcode is a number
189    for(CFIndex i = 0; i < CFStringGetLength(passcode); i++){
190        if( CFStringFindCharacterFromSet(passcode, decimalDigitCharacterSet, CFRangeMake(i,1), 0, NULL))
191            continue;
192        else {
193            isNumber = false;
194            break;
195        }
196    }
197    //checking to see if it's a 4 digit pin
198    if(isNumber && CFStringGetLength(passcode) == 4){
199
200        pin = CFStringToCString(passcode);
201        if(in_word_set(pin, 4)){
202            free(pin);
203            return true;
204        }
205
206        CFIndex blacklistLength = (CFIndex)sizeof(blacklist)/sizeof(blacklist[0]);
207
208        //not all the same number
209        if(pin[0] == pin[1] == pin[2] == pin[3]){
210            free(pin);
211            return true; //weak password
212        }
213        //first two digits being the same and the last two digits being the same
214        if ( pin[0] == pin[1] && pin[2] == pin[3]){
215            free(pin);
216            return true; //weak password
217        }
218        //first two digits not being the same as the last two digits
219        if(pin[0] == pin[2] && pin[1] == pin[3]){
220            free(pin);
221            return true; //weak password
222        }
223        //not in this list
224        for(CFIndex i = 0; i < blacklistLength; i++)
225        {
226            const char* blackCode = blacklist[i];
227            if(0 == strcmp(blackCode, pin))
228            {
229                free(pin);
230                return true; //weak password
231            }
232        }
233    }
234    else if(isNumber){ //dealing with a numeric PIN
235        pin = CFStringToCString(passcode);
236        //check if PIN is all the same number
237        for(int i = 0; i < CFStringGetLength(passcode); i++){
238            if(i+1 >= CFStringGetLength(passcode)){
239                free(pin);
240                return true;
241            }
242            else if (pin[i] == pin[i+1])
243                continue;
244            else
245                break;
246        }
247        //check if PIN is a bunch of incrementing numbers
248        for(int i = 0; i < CFStringGetLength(passcode); i++){
249            if(i == CFStringGetLength(passcode)-1){
250                free(pin);
251                return true;
252            }
253            else if ((pin[i] + 1) == pin[i+1])
254                continue;
255            else
256                break;
257        }
258        //check if PIN is a bunch of decrementing numbers
259        for(int i = 0; i < CFStringGetLength(passcode); i++){
260            if(i == CFStringGetLength(passcode)-1){
261                free(pin);
262                return true;
263            }
264            else if ((pin[i]) == (pin[i+1] +1))
265                continue;
266            else
267                break;
268        }
269    }
270    else{ // password is complex, evaluate entropy
271        int u = 0;
272        int l = 0;
273        int d = 0;
274        int p = 0;
275        int characterSet = 0;
276
277        //calculate new entropy
278        for(CFIndex i = 0; i < CFStringGetLength(passcode); i++){
279
280            if( CFStringFindCharacterFromSet(passcode, uppercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
281                u++;
282                continue;
283            }
284            if( CFStringFindCharacterFromSet(passcode, lowercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
285                l++;
286                continue;
287            }
288            if( CFStringFindCharacterFromSet(passcode, decimalDigitCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
289                d++;
290                continue;
291            }
292            if( CFStringFindCharacterFromSet(passcode, punctuationCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){
293                p++;
294                continue;
295            }
296
297        }
298        if(u > 0){
299            characterSet += alphabetSetSize;
300        }
301        if(l > 0){
302            characterSet += alphabetSetSize;
303        }
304        if(d > 0){
305            characterSet += decimalSetSize;
306        }
307        if(p > 0){
308            characterSet += punctuationSetSize;
309        }
310
311        double strength = CFStringGetLength(passcode)*log2(characterSet);
312
313        if(strength < entropyStrengthThreshold){
314            return true; //weak
315        }
316        else
317            return false; //strong
318    }
319    if(pin)
320        free(pin);
321
322    return false; //strong password
323
324}
325
326static void getUniformRandomNumbers(uint8_t* buffer, size_t numberOfDesiredNumbers, uint8_t upperBound)
327{
328
329    // The values returned by SecRandomCopyBytes are uniformly distributed in the range [0, 255]. If we try to map
330    // these values onto a smaller range using modulo we will introduce a bias towards lower numbers in situations
331    // where our smaller range doesn’t evenly divide in to [0, 255]. For example, with the desired range of [0, 54]
332    // the ranges 0..54, 55..109, 110..164, and 165..219 are uniformly distributed, but the range 220..255 modulo 55
333    // is only distributed over [0, 35], giving significant bias to these lower values. So, we ignore random numbers
334    // that would introduce this bias.
335    uint8_t limitAvoidingModuloBias = UCHAR_MAX - (UCHAR_MAX % upperBound);
336
337    for (size_t numberOfAcceptedNumbers = 0; numberOfAcceptedNumbers < numberOfDesiredNumbers; ) {
338        if (SecRandomCopyBytes(kSecRandomDefault, numberOfDesiredNumbers - numberOfAcceptedNumbers, buffer + numberOfAcceptedNumbers) == -1)
339            continue;
340        for (size_t i = numberOfAcceptedNumbers; i < numberOfDesiredNumbers; ++i) {
341            if (buffer[i] < limitAvoidingModuloBias)
342                buffer[numberOfAcceptedNumbers++] = buffer[i] % upperBound;
343        }
344    }
345}
346
347static bool passwordContainsRequiredCharacters(CFStringRef password, CFArrayRef requiredCharacterSets)
348{
349    CFCharacterSetRef characterSet;
350
351    for (CFIndex i = 0; i< CFArrayGetCount(requiredCharacterSets); i++) {
352        characterSet = CFArrayGetValueAtIndex(requiredCharacterSets, i);
353        CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(password));
354        require_quiet(CFStringFindCharacterFromSet(password, characterSet, rangeToSearch, 0, NULL), fail);
355    }
356    return true;
357
358fail:
359    return false;
360
361}
362
363static bool passwordContainsLessThanNIdenticalCharacters(CFStringRef password, CFIndex identicalCount)
364{
365    unsigned char Char, nextChar;
366    int repeating = 0;
367
368    for(CFIndex i = 0; i < CFStringGetLength(password); i++){
369        Char = CFStringGetCharacterAtIndex(password, i);
370        for(CFIndex j = i; j< CFStringGetLength(password); j++){
371            nextChar = CFStringGetCharacterAtIndex(password, j);
372            require_quiet(repeating <= identicalCount, fail);
373            if(Char == nextChar){
374                repeating++;
375            }else{
376                repeating = 0;
377                break;
378            }
379        }
380    }
381    return true;
382fail:
383    return false;
384}
385
386static bool passwordContainsAtLeastNCharacters(CFStringRef password, CFStringRef characters, CFIndex N)
387{
388    CFCharacterSetRef characterSet = NULL;
389    characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, characters);
390    CFIndex counter = 0;
391
392    for(CFIndex i = 0; i < CFStringGetLength(password); i++){
393        if(CFStringFindCharacterFromSet(password, characterSet, CFRangeMake(i, 1), 0, NULL))
394            counter++;
395    }
396    CFReleaseNull(characterSet);
397    if(counter < N)
398        return false;
399    else
400        return true;
401}
402
403static bool passwordContainsLessThanNCharacters(CFStringRef password, CFStringRef characters, CFIndex N)
404{
405    CFCharacterSetRef characterSet = NULL;
406    characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, characters);
407    CFIndex counter = 0;
408
409    for(CFIndex i = 0; i < CFStringGetLength(password); i++){
410        if(CFStringFindCharacterFromSet(password, characterSet, CFRangeMake(i, 1), 0, NULL))
411            counter++;
412    }
413    CFReleaseNull(characterSet);
414    if(counter > N)
415        return false;
416    else
417        return true;
418}
419
420static bool passwordDoesNotContainCharacters(CFStringRef password, CFStringRef prohibitedCharacters)
421{
422    CFCharacterSetRef characterSet = NULL;
423    characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
424    CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(password));
425
426    require_quiet(!CFStringFindCharacterFromSet(password, characterSet, rangeToSearch, 0, NULL), fail);
427    CFReleaseNull(characterSet);
428    return true;
429fail:
430    CFReleaseNull(characterSet);
431    return false;
432}
433
434static void getPasswordRandomCharacters(CFStringRef *returned, CFDictionaryRef requirements, CFIndex *numberOfRandomCharacters, CFStringRef allowedCharacters)
435{
436    uint8_t randomNumbers[*numberOfRandomCharacters];
437    unsigned char randomCharacters[*numberOfRandomCharacters];
438    getUniformRandomNumbers(randomNumbers, *numberOfRandomCharacters, CFStringGetLength(allowedCharacters));
439
440    CFTypeRef prohibitedCharacters = NULL;
441    CFStringRef temp = NULL;
442
443    if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters))
444        prohibitedCharacters = NULL;
445
446    //it's faster for long characters to check each character produced for these cases
447    for (CFIndex i = 0; i < *numberOfRandomCharacters; ++i){
448        //check prohibited characters
449        UniChar randomChar[1];
450        randomChar[0] = CFStringGetCharacterAtIndex(allowedCharacters, randomNumbers[i]);
451        temp = CFStringCreateWithCharacters(kCFAllocatorDefault, randomChar, 1);
452
453        if(prohibitedCharacters != NULL)
454        {
455            if(!passwordDoesNotContainCharacters(temp, prohibitedCharacters)){
456                //change up the random numbers so we don't get the same index into allowed
457                getUniformRandomNumbers(randomNumbers, *numberOfRandomCharacters, CFStringGetLength(allowedCharacters));
458                i--;
459                continue;
460            }
461        }
462        randomCharacters[i] = (unsigned char)randomChar[0];
463    }
464
465    CFReleaseNull(temp);
466
467    *returned = CFStringCreateWithBytes(kCFAllocatorDefault, randomCharacters, *numberOfRandomCharacters, kCFStringEncodingUTF8, false);
468}
469
470static bool doesPasswordEndWith(CFStringRef password, CFStringRef prohibitedCharacters)
471{
472    CFCharacterSetRef characterSet = NULL;
473    characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
474
475    CFRange rangeToSearch = CFRangeMake(CFStringGetLength(password) - CFStringGetLength(prohibitedCharacters), CFStringGetLength(prohibitedCharacters));
476    require_quiet(0 == CFStringCompareWithOptions(password, prohibitedCharacters, rangeToSearch, 0), fail);
477    CFReleaseNull(characterSet);
478    return false;
479fail:
480    CFReleaseNull(characterSet);
481    return true;
482}
483
484static bool doesPasswordStartWith(CFStringRef password, CFStringRef prohibitedCharacters)
485{
486    CFCharacterSetRef characterSet = NULL;
487    characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters);
488
489    CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(prohibitedCharacters));
490    require_quiet(0 == CFStringCompareWithOptions(password, prohibitedCharacters, rangeToSearch, 0), fail);
491    CFReleaseNull(characterSet);
492    return false; //does not start with prohibitedCharacters
493fail:
494    CFReleaseNull(characterSet);
495    return true;
496}
497
498static void passwordGenerateDefaultParametersDictionary(CFDictionaryRef *returned, SecPasswordType type, CFDictionaryRef requirements){
499
500    CFMutableArrayRef requiredCharacterSets = NULL;
501    CFNumberRef numReqChars = NULL;
502    CFStringRef defaultPasswordFormat = NULL;
503    requiredCharacterSets = CFArrayCreateMutable(NULL, 0, NULL);
504    defaultPasswordFormat = CFSTR("true");
505    CFTypeRef groupSizeRef = NULL, numberOfGroupsRef = NULL;
506    CFIndex groupSize, numberOfGroups;
507    switch(type){
508        case(kSecPasswordTypeiCloudRecovery):
509            numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultiCloudPasswordLength);
510            groupSize = 4;
511            numberOfGroups = 6;
512            groupSizeRef = CFNumberCreate(NULL, kCFNumberIntType, &groupSize);
513            numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberIntType, &numberOfGroups);
514
515            uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
516            decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
517            CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
518            CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
519            *returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
520                                                    kSecUseDefaultPasswordFormatKey,   defaultPasswordFormat,
521                                                    kSecNumberOfRequiredRandomCharactersKey, numReqChars,
522                                                    kSecAllowedCharactersKey,   defaultiCloudCharacters,
523                                                    kSecRequiredCharacterSetsKey, requiredCharacterSets,
524                                                    kSecPasswordGroupSize, groupSizeRef,
525                                                    kSecPasswordNumberOfGroups, numberOfGroupsRef,
526                                                    NULL);
527            break;
528
529        case(kSecPasswordTypePIN):
530            numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultPINLength);
531            groupSize = 4;
532            numberOfGroups = 1;
533            groupSizeRef = CFNumberCreate(NULL, kCFNumberIntType, &groupSize);
534            numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberIntType, &numberOfGroups);
535
536            decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
537            CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
538            *returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
539                                                                         kSecUseDefaultPasswordFormatKey,   defaultPasswordFormat,
540                                                                         kSecNumberOfRequiredRandomCharactersKey, numReqChars,
541                                                                         kSecAllowedCharactersKey,   defaultPINCharacters,
542                                                                         kSecRequiredCharacterSetsKey, requiredCharacterSets,
543                                                                         kSecPasswordGroupSize, groupSizeRef,
544                                                                         kSecPasswordNumberOfGroups, numberOfGroupsRef,
545                                                                         NULL);
546            break;
547
548        case(kSecPasswordTypeWifi):
549            groupSize = 4;
550            numberOfGroups = 3;
551            groupSizeRef = CFNumberCreate(NULL, kCFNumberIntType, &groupSize);
552            numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberIntType, &numberOfGroups);
553
554            lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
555            decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
556
557            numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultWifiPasswordLength);
558            CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet);
559            CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
560            *returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
561                                                                         kSecUseDefaultPasswordFormatKey,   defaultPasswordFormat,
562                                                                         kSecNumberOfRequiredRandomCharactersKey, numReqChars,
563                                                                         kSecAllowedCharactersKey,   defaultWifiCharacters,
564                                                                         kSecRequiredCharacterSetsKey, requiredCharacterSets,
565                                                                         kSecPasswordGroupSize, groupSizeRef,
566                                                                         kSecPasswordNumberOfGroups, numberOfGroupsRef,
567                                                                         NULL);
568            break;
569
570        default:
571            groupSize = 4;
572            numberOfGroups = 6;
573            groupSizeRef = CFNumberCreate(NULL, kCFNumberIntType, &groupSize);
574            numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberIntType, &numberOfGroups);
575            uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
576            lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
577            decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
578            CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
579            CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet);
580            CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
581
582            numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultNumberOfRandomCharacters);
583            *returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
584                                                                         kSecUseDefaultPasswordFormatKey,   defaultPasswordFormat,
585                                                                         kSecNumberOfRequiredRandomCharactersKey, numReqChars,
586                                                                         kSecAllowedCharactersKey,   defaultCharacters,
587                                                                         kSecRequiredCharacterSetsKey, requiredCharacterSets,
588                                                                         kSecPasswordGroupSize, groupSizeRef,
589                                                                         kSecPasswordNumberOfGroups, numberOfGroupsRef,
590                                                                         NULL);
591
592
593
594            break;
595    }
596
597    CFReleaseNull(numReqChars);
598    CFReleaseNull(requiredCharacterSets);
599    CFReleaseNull(groupSizeRef);
600    CFReleaseNull(numberOfGroupsRef);
601}
602static void passwordGenerationParametersDictionary(CFDictionaryRef *returned, SecPasswordType type, CFDictionaryRef requirements)
603{
604    CFMutableArrayRef requiredCharacterSets = CFArrayCreateMutable(NULL, 0, NULL);
605    CFArrayRef requiredCharactersArray = NULL;
606    CFNumberRef numReqChars = NULL;
607    CFIndex numberOfRequiredRandomCharacters;
608    CFStringRef allowedCharacters = NULL, useDefaultPasswordFormat = NULL;
609    uint64_t valuePtr;
610    CFTypeRef prohibitedCharacters = NULL, endWith = NULL, startWith = NULL,
611            groupSizeRef = NULL, numberOfGroupsRef = NULL, separatorRef = NULL,
612            atMostCharactersRef = NULL,atLeastCharactersRef = NULL, identicalRef = NULL;
613
614    CFNumberRef min = (CFNumberRef)CFDictionaryGetValue(requirements, kSecPasswordMinLengthKey);
615    CFNumberRef max = (CFNumberRef)CFDictionaryGetValue(requirements, kSecPasswordMaxLengthKey);
616
617    CFNumberGetValue(min, kCFNumberSInt64Type, &valuePtr);
618    CFIndex minPasswordLength = (long)valuePtr;
619    CFNumberGetValue(max, kCFNumberSInt64Type, &valuePtr);
620    CFIndex maxPasswordLength = (long)valuePtr;
621
622    // If requirements allow, we will generate the password in default format.
623    useDefaultPasswordFormat = CFSTR("true");
624    numberOfRequiredRandomCharacters = defaultNumberOfRandomCharacters;
625
626    if(type == kSecPasswordTypePIN)
627    {
628        if( maxPasswordLength && minPasswordLength )
629            numberOfRequiredRandomCharacters = maxPasswordLength;
630        else if( !maxPasswordLength && minPasswordLength )
631            numberOfRequiredRandomCharacters = minPasswordLength;
632        else if( !minPasswordLength && maxPasswordLength )
633            numberOfRequiredRandomCharacters = maxPasswordLength;
634        else
635            numberOfRequiredRandomCharacters = defaultPINLength;
636
637        allowedCharacters = CFSTR("0123456789");
638        CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
639        requiredCharactersArray = CFArrayCreateCopy(NULL, requiredCharacterSets);
640        useDefaultPasswordFormat = CFSTR("false");
641    }
642    else{
643        if (minPasswordLength && minPasswordLength > defaultNumberOfRandomCharacters) {
644            useDefaultPasswordFormat = CFSTR("false");
645            numberOfRequiredRandomCharacters = minPasswordLength;
646        }
647        if (maxPasswordLength && maxPasswordLength < defaultNumberOfRandomCharacters) {
648            useDefaultPasswordFormat = CFSTR("false");
649            numberOfRequiredRandomCharacters = maxPasswordLength;
650        }
651        if (maxPasswordLength && minPasswordLength && maxPasswordLength == minPasswordLength && maxPasswordLength != defaultNumberOfRandomCharacters){
652            useDefaultPasswordFormat = CFSTR("false");
653            numberOfRequiredRandomCharacters = maxPasswordLength;
654        }
655        allowedCharacters = (CFStringRef)CFDictionaryGetValue(requirements, kSecPasswordAllowedCharactersKey);
656        requiredCharactersArray = (CFArrayRef)CFDictionaryGetValue(requirements, kSecPasswordRequiredCharactersKey);
657    }
658    if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters))
659        prohibitedCharacters = NULL;
660
661    if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantEndWithChars, &endWith))
662        endWith = NULL;
663
664    if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantStartWithChars, &startWith))
665        startWith = NULL;
666
667    if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordGroupSize, &groupSizeRef))
668        groupSizeRef = NULL;
669
670    if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef))
671        numberOfGroupsRef = NULL;
672
673    if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordSeparator, &separatorRef))
674        separatorRef = NULL;
675
676    if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharactersRef))
677        atMostCharactersRef = NULL;
678
679    if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharactersRef))
680        atLeastCharactersRef = NULL;
681
682    if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef))
683        identicalRef = NULL;
684
685    if (allowedCharacters) {
686        if( false == CFStringFindWithOptions(allowedCharacters, CFSTR("-"), CFRangeMake(0, CFStringGetLength(allowedCharacters)), kCFCompareCaseInsensitive, NULL))
687            useDefaultPasswordFormat = CFSTR("false");
688    } else
689        allowedCharacters = defaultCharacters;
690
691    // In default password format, we use dashes only as separators, not as symbols you can encounter at a random position.
692    if (useDefaultPasswordFormat == CFSTR("false")){
693        CFMutableStringRef mutatedAllowedCharacters = CFStringCreateMutableCopy(kCFAllocatorDefault, CFStringGetLength(allowedCharacters), allowedCharacters);
694        CFStringFindAndReplace (mutatedAllowedCharacters, CFSTR("-"), CFSTR(""), CFRangeMake(0, CFStringGetLength(allowedCharacters)),kCFCompareCaseInsensitive);
695        allowedCharacters = CFStringCreateCopy(kCFAllocatorDefault, mutatedAllowedCharacters);
696    }
697
698    if (requiredCharactersArray) {
699        for (CFIndex i = 0; i < CFArrayGetCount(requiredCharactersArray); i++){
700            CFCharacterSetRef stringWithRequiredCharacters = CFArrayGetValueAtIndex(requiredCharactersArray, i);
701            if( CFStringFindCharacterFromSet(allowedCharacters, stringWithRequiredCharacters, CFRangeMake(0, CFStringGetLength(allowedCharacters)), 0, NULL)){
702                CFArrayAppendValue(requiredCharacterSets, stringWithRequiredCharacters);
703            }
704        }
705    } else{
706        uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter);
707        lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter);
708        decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit);
709        CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet);
710        CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet);
711        CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet);
712    }
713
714
715    if (CFArrayGetCount(requiredCharacterSets) > numberOfRequiredRandomCharacters) {
716        CFReleaseNull(requiredCharacterSets);
717        requiredCharacterSets = NULL;
718    }
719    //create new CFDictionary
720    numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, numberOfRequiredRandomCharacters);
721    CFMutableDictionaryRef updatedConstraints = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
722    CFDictionaryAddValue(updatedConstraints, kSecUseDefaultPasswordFormatKey, useDefaultPasswordFormat);
723    CFDictionarySetValue(updatedConstraints, kSecNumberOfRequiredRandomCharactersKey, numReqChars);
724    CFDictionaryAddValue(updatedConstraints, kSecAllowedCharactersKey, allowedCharacters);
725    if(requiredCharacterSets)
726        CFDictionaryAddValue(updatedConstraints, kSecRequiredCharacterSetsKey, requiredCharacterSets);
727
728    //add the prohibited characters string if it exists to the new dictionary
729    if(prohibitedCharacters)
730        CFDictionaryAddValue(updatedConstraints, kSecPasswordDisallowedCharacters, prohibitedCharacters);
731
732    //add the characters the password can't end with if it exists to the new dictionary
733    if(endWith)
734        CFDictionaryAddValue(updatedConstraints, kSecPasswordCantEndWithChars, endWith);
735
736    //add the characters the password can't start with if it exists to the new dictionary
737    if(startWith)
738        CFDictionaryAddValue(updatedConstraints, kSecPasswordCantStartWithChars, startWith);
739
740    if(groupSizeRef)
741        CFDictionaryAddValue(updatedConstraints, kSecPasswordGroupSize, groupSizeRef);
742
743    if(numberOfGroupsRef)
744        CFDictionaryAddValue(updatedConstraints, kSecPasswordNumberOfGroups, numberOfGroupsRef);
745
746    if(separatorRef)
747        CFDictionaryAddValue(updatedConstraints, kSecPasswordSeparator, separatorRef);
748
749    if(atMostCharactersRef)
750        CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsNoMoreThanNSpecificCharacters, atMostCharactersRef);
751
752    if(atLeastCharactersRef)
753        CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsAtLeastNSpecificCharacters, atLeastCharactersRef);
754
755    if(identicalRef)
756        CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, identicalRef);
757
758    CFReleaseNull(useDefaultPasswordFormat);
759    CFReleaseNull(numReqChars);
760    CFReleaseNull(allowedCharacters);
761    CFReleaseNull(requiredCharacterSets);
762
763    *returned = CFDictionaryCreateCopy(kCFAllocatorDefault, updatedConstraints);
764}
765
766static bool isDictionaryFormattedProperly(SecPasswordType type, CFDictionaryRef passwordRequirements, CFErrorRef *error){
767
768    CFTypeRef defaults = NULL;
769    CFErrorRef tempError = NULL;
770    if(passwordRequirements == NULL){
771        return true;
772    }
773
774    if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) ){
775        if(isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0)){
776            return true;
777        }
778    }
779    //only need to check max and min pin length formatting
780    if(type == kSecPasswordTypePIN){
781        CFTypeRef minTest = NULL, maxTest = NULL;
782        uint64_t valuePtr;
783        CFIndex minPasswordLength = 0, maxPasswordLength= 0;
784
785        if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) ){
786            if(isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0)){
787                return true;
788            }
789        }
790        //check if the values exist!
791        if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMaxLengthKey, &maxTest) ){
792            require_action_quiet(isNull(maxTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL));
793            require_action_quiet(isNumber(maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
794
795        }
796        if (CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMinLengthKey, &minTest) ){
797            require_action_quiet(isNull(minTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL));
798            require_action_quiet(isNumber(minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
799        }
800        //check if the values exist!
801        if(maxTest){
802            CFNumberRef max = (CFNumberRef)maxTest;
803            CFNumberGetValue(max, kCFNumberSInt64Type, &valuePtr);
804            maxPasswordLength = (long)valuePtr;
805        }
806        if(minTest){
807            CFNumberRef min = (CFNumberRef)minTest;
808            CFNumberGetValue(min, kCFNumberSInt64Type, &valuePtr);
809            minPasswordLength = (long)valuePtr;
810        }
811        //make sure min and max make sense respective to each other and that they aren't less than 4 digits.
812        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));
813        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));
814    }
815    else{
816        CFTypeRef allowedTest, maxTest, minTest, requiredTest, prohibitedCharacters, endWith, startWith,
817        groupSizeRef, numberOfGroupsRef, separatorRef, atMostCharactersRef,
818        atLeastCharactersRef, thresholdRef, identicalRef, characters;
819        uint64_t valuePtr;
820
821        //check if the values exist!
822        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));
823        require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMaxLengthKey, &maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL));
824        require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMinLengthKey, &minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL));
825        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));
826
827        //check if values are null?
828        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));
829        require_action_quiet(isNull(maxTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL));
830        require_action_quiet(isNull(minTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL));
831        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));
832
833        //check if the values are correct
834        require_action_quiet(isString(allowedTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's allowed characters must be a CFStringRef"), (CFIndex)errSecBadReq, NULL));
835        require_action_quiet(isNumber(maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
836        require_action_quiet(isNumber(minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL));
837        require_action_quiet(isArray(requiredTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's required characters must be an array of CFCharacterSetRefs"), (CFIndex)errSecBadReq, NULL));
838
839        CFNumberGetValue(minTest, kCFNumberSInt64Type, &valuePtr);
840        CFIndex minPasswordLength = (long)valuePtr;
841        CFNumberGetValue(maxTest, kCFNumberSInt64Type, &valuePtr);
842        CFIndex maxPasswordLength = (long)valuePtr;
843
844        require_action_quiet(minPasswordLength <= maxPasswordLength, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's length parameters make no sense ( is max < min ?)"),  (CFIndex)errSecBadReq, NULL));
845
846        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));
847        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));
848
849        if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters)){
850            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));
851            require_action_quiet(isString(prohibitedCharacters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Disallowed Characters dictionary parameter is either null or not a string"), (CFIndex)errSecBadReq, NULL));
852        }
853        if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordCantEndWithChars, &endWith)){
854            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));
855            require_action_quiet(isString(endWith), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'EndWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
856        }
857        if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordCantStartWithChars, &startWith)){
858            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));
859            require_action_quiet(isString(startWith), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'StartWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
860        }
861        if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordGroupSize, &groupSizeRef)){
862            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));
863            require_action_quiet(isNumber(groupSizeRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'groupsize' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
864        }
865        if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef)){
866            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));
867            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));
868        }
869        if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordSeparator, &separatorRef)){
870            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));
871            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));
872        }
873
874        if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharactersRef)){
875            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));
876            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));
877
878            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));
879            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));
880            require_action_quiet(isNumber(thresholdRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
881
882            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));
883            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));
884            require_action_quiet(isString(characters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
885        }
886
887        if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharactersRef)){
888            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));
889            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));
890
891            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));
892
893            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));
894            require_action_quiet(isNumber(thresholdRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL));
895
896            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));
897            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));
898            require_action_quiet(isString(characters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL));
899        }
900
901        if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef)){
902            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));
903            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));
904        }
905    }
906fail:
907    if (tempError != NULL) {
908        *error = tempError;
909        CFRetain(*error);
910        return false;
911    }
912
913    CFReleaseNull(tempError);
914    return true;
915}
916
917static bool doesFinalPasswordPass(CFStringRef password, CFDictionaryRef requirements){
918
919    CFTypeRef characters, identicalRef = NULL, NRef = NULL, endWith= NULL, startWith= NULL, atLeastCharacters= NULL, atMostCharacters = NULL;
920    uint64_t valuePtr;
921    CFIndex N, identicalCount;
922    CFArrayRef requiredCharacterSet = (CFArrayRef)CFDictionaryGetValue(requirements, kSecRequiredCharacterSetsKey);
923
924    if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantEndWithChars, &endWith))
925        endWith = NULL;
926
927    if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantStartWithChars, &startWith))
928        startWith = NULL;
929
930    if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharacters))
931        atLeastCharacters = NULL;
932
933    if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharacters))
934        atMostCharacters = NULL;
935
936    if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef))
937        identicalRef = NULL;
938    else{
939        CFNumberGetValue((CFNumberRef)identicalRef, kCFNumberSInt64Type, &valuePtr);
940        identicalCount = (long)valuePtr;
941    }
942    if(endWith != NULL)
943    {
944        if(!doesPasswordEndWith(password, endWith))
945            return false;
946    }
947    if(startWith != NULL){
948        if(!doesPasswordStartWith(password, startWith))
949            return false;
950    }
951    if(atLeastCharacters != NULL){
952        NRef = CFDictionaryGetValue(atLeastCharacters, kSecPasswordCharacterCount);
953        characters = CFDictionaryGetValue(atLeastCharacters, kSecPasswordCharacters);
954        CFNumberGetValue((CFNumberRef)NRef, kCFNumberSInt64Type, &valuePtr);
955        N = (long)valuePtr;
956        if(!passwordContainsAtLeastNCharacters(password, characters, N))
957            return false;
958    }
959    if(atMostCharacters != NULL){
960        NRef = CFDictionaryGetValue(atMostCharacters, kSecPasswordCharacterCount);
961        characters = CFDictionaryGetValue(atMostCharacters, kSecPasswordCharacters);
962        CFNumberGetValue((CFNumberRef)NRef, kCFNumberSInt64Type, &valuePtr);
963        N = (long)valuePtr;
964        if(!passwordContainsLessThanNCharacters(password, characters, N))
965            return false;
966    }
967    if(identicalRef != NULL){
968        if(!passwordContainsLessThanNIdenticalCharacters(password, identicalCount))
969            return false;
970    }
971    if (!passwordContainsRequiredCharacters(password, requiredCharacterSet))
972        return false;
973
974    if(true == SecPasswordIsPasswordWeak(password))
975        return false;
976
977    return true;
978}
979
980//entry point into password generation
981CF_RETURNS_RETAINED CFStringRef SecPasswordGenerate(SecPasswordType type, CFErrorRef *error, CFDictionaryRef passwordRequirements){
982    bool check = false;
983    CFTypeRef separator = NULL, defaults = NULL, groupSizeRef = NULL, numberOfGroupsRef = NULL;
984    CFDictionaryRef properlyFormattedRequirements = NULL;
985    *error = NULL;
986    uint64_t valuePtr, groupSize, numberOfGroups;
987    CFNumberRef numberOfRequiredRandomCharacters;
988    CFIndex requiredCharactersSize;
989    CFStringRef randomCharacters = NULL, password = NULL, allowedChars = NULL;
990    CFMutableStringRef finalPassword = NULL;
991
992    check = isDictionaryFormattedProperly(type, passwordRequirements, error);
993    require_quiet(check != false, fail);
994
995    //should we generate defaults?
996    if(passwordRequirements == NULL || (CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) && isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0) ))
997        passwordGenerateDefaultParametersDictionary(&properlyFormattedRequirements, type, passwordRequirements);
998    else
999        passwordGenerationParametersDictionary(&properlyFormattedRequirements, type, passwordRequirements);
1000
1001    CFRetain(properlyFormattedRequirements);
1002
1003    require_quiet(*error == NULL && properlyFormattedRequirements != NULL, fail);
1004
1005    numberOfRequiredRandomCharacters = (CFNumberRef)CFDictionaryGetValue(properlyFormattedRequirements, kSecNumberOfRequiredRandomCharactersKey);
1006    CFNumberGetValue(numberOfRequiredRandomCharacters, kCFNumberSInt64Type, &valuePtr);
1007    requiredCharactersSize = (long)valuePtr;
1008
1009    if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordGroupSize, &groupSizeRef)){
1010        groupSizeRef = NULL;
1011    }
1012    else
1013        CFNumberGetValue((CFNumberRef)groupSizeRef, kCFNumberSInt64Type, &groupSize);
1014
1015    if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef)){
1016        numberOfGroupsRef = NULL;
1017    }
1018    else
1019        CFNumberGetValue((CFNumberRef)numberOfGroupsRef, kCFNumberSInt64Type, &numberOfGroups);
1020
1021    while (true) {
1022        allowedChars = CFDictionaryGetValue(properlyFormattedRequirements, kSecAllowedCharactersKey);
1023        getPasswordRandomCharacters(&randomCharacters, properlyFormattedRequirements, &requiredCharactersSize, allowedChars);
1024
1025        if(numberOfGroupsRef && groupSizeRef){
1026            finalPassword = CFStringCreateMutable(kCFAllocatorDefault, 0);
1027
1028            if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordSeparator, &separator))
1029                separator = NULL;
1030
1031            if(separator == NULL)
1032                separator = CFSTR("-");
1033
1034            CFIndex i = 0;
1035            while( i != requiredCharactersSize){
1036                if((i + (CFIndex)groupSize) < requiredCharactersSize){
1037                    CFStringAppend(finalPassword, CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, (CFIndex)groupSize)));
1038                    CFStringAppend(finalPassword, separator);
1039                    i+=groupSize;
1040                }
1041                else if((i+(CFIndex)groupSize) == requiredCharactersSize){
1042                    CFStringAppend(finalPassword, CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, (CFIndex)groupSize)));
1043                    i+=groupSize;
1044                }
1045                else {
1046                    CFStringAppend(finalPassword, CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, requiredCharactersSize - i)));
1047                    i+=(requiredCharactersSize - i);
1048                }
1049            }
1050            password = CFStringCreateCopy(kCFAllocatorDefault, finalPassword);
1051            CFReleaseNull(finalPassword);
1052        }
1053        //no fancy formatting
1054        else {
1055            password = CFStringCreateCopy(kCFAllocatorDefault, randomCharacters);
1056        }
1057
1058        CFReleaseNull(randomCharacters);
1059        require_quiet(doesFinalPasswordPass(password, properlyFormattedRequirements), no_pass);
1060        return password;
1061
1062    no_pass:
1063        CFReleaseNull(password);
1064    }
1065
1066fail:
1067    CFReleaseNull(properlyFormattedRequirements);
1068    return NULL;
1069}
1070
1071const char *in_word_set (const char *str, unsigned int len){
1072    static const char * wordlist[] =
1073    {
1074        "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
1075        "", "", "0103", "", "", "", "", "0123", "", "", "", "", "0303", "", "", "",
1076        "", "", "", "", "0110", "", "1103", "", "", "", "", "1123", "", "", "0000",
1077        "", "1203", "", "0404", "", "", "", "", "1234", "1110", "2015", "2013", "",
1078        "2014", "1010", "2005", "2003", "", "2004", "1210", "0505", "0111", "", "",
1079        "", "2008", "0101", "", "2007", "", "", "", "", "2006", "2010", "1995", "1993",
1080        "", "1994", "2000", "", "1111", "", "", "", "1998", "1101", "", "1997", "",
1081        "0808", "1211", "", "1996", "0102", "", "1201", "", "", "1990", "", "", "",
1082        "", "0202", "", "2011", "", "", "1112", "1958", "2001", "", "1957", "1102",
1083        "", "3333", "", "1956", "1212", "1985", "1983", "", "1984", "1202", "", "0909",
1084        "", "0606", "", "1988", "1991", "", "1987", "2012", "", "", "", "1986", "2002",
1085        "", "", "", "0707", "1980", "", "2009", "", "", "2222", "1965", "1963", "",
1086        "1964", "", "", "2229", "", "", "1992", "1968", "", "", "1967", "", "", "1999",
1087        "", "1966", "", "1975", "1973", "", "1974", "1960", "", "1981", "", "4444",
1088        "", "1978", "", "7465", "1977", "", "", "", "", "1976", "2580", "", "1959",
1089        "", "", "1970", "", "", "", "", "", "", "", "", "", "1982", "", "1961", "",
1090        "", "5252", "", "1989", "", "", "", "", "", "", "", "", "", "", "", "", "",
1091        "", "1971", "", "", "", "", "", "", "", "1962", "", "5683", "", "6666", "",
1092        "", "1969", "", "", "", "", "", "", "", "", "", "", "", "", "1972", "", "",
1093        "", "", "", "", "1979", "", "", "", "7667"
1094    };
1095
1096    if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
1097    {
1098        register int key = pinhash (str, len);
1099
1100        if (key <= MAX_HASH_VALUE && key >= 0)
1101        {
1102            register const char *s = wordlist[key];
1103            if (*str == *s && !strcmp (str + 1, s + 1))
1104                return s;
1105        }
1106    }
1107    return 0;
1108}
1109CFDictionaryRef SecPasswordCopyDefaultPasswordLength(SecPasswordType type, CFErrorRef *error){
1110
1111    CFIndex tupleLengthInt, numOfTuplesInt;
1112    CFNumberRef tupleLength = NULL;
1113    CFNumberRef numOfTuples = NULL;
1114
1115    CFMutableDictionaryRef passwordLengthDefaults = NULL;
1116
1117    switch(type){
1118        case(kSecPasswordTypeiCloudRecovery):
1119            tupleLengthInt = 4;
1120            numOfTuplesInt = 6;
1121            tupleLength = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &tupleLengthInt);
1122            numOfTuples = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &numOfTuplesInt);
1123            passwordLengthDefaults = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 0, 0);
1124            CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordGroupSize, tupleLength);
1125            CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordNumberOfGroups, numOfTuples);
1126            return CFDictionaryCreateCopy(kCFAllocatorDefault, passwordLengthDefaults);
1127
1128        case(kSecPasswordTypePIN):
1129            tupleLengthInt = 4;
1130            numOfTuplesInt = 1;
1131            tupleLength = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &tupleLengthInt);
1132            numOfTuples = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &numOfTuplesInt);
1133            passwordLengthDefaults = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 0, 0);
1134            CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordGroupSize, tupleLength);
1135            CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordNumberOfGroups, numOfTuples);
1136            return CFDictionaryCreateCopy(kCFAllocatorDefault, passwordLengthDefaults);
1137
1138        case(kSecPasswordTypeSafari):
1139            tupleLengthInt = 4;
1140            numOfTuplesInt = 5;
1141            tupleLength = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &tupleLengthInt);
1142            numOfTuples = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &numOfTuplesInt);
1143            passwordLengthDefaults = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 0, 0);
1144            CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordGroupSize, tupleLength);
1145            CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordNumberOfGroups, numOfTuples);
1146            return CFDictionaryCreateCopy(kCFAllocatorDefault, passwordLengthDefaults);
1147
1148
1149        case(kSecPasswordTypeWifi):
1150            tupleLengthInt = 4;
1151            numOfTuplesInt = 3;
1152            tupleLength = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &tupleLengthInt);
1153            numOfTuples = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &numOfTuplesInt);
1154            passwordLengthDefaults = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 0, 0);
1155            CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordGroupSize, tupleLength);
1156            CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordNumberOfGroups, numOfTuples);
1157            return CFDictionaryCreateCopy(kCFAllocatorDefault, passwordLengthDefaults);
1158
1159
1160        default:
1161            if(SecError(errSecBadReq, error, CFSTR("Password type does not exist.")) == false)
1162            {
1163                secdebug("secpasswordcopydefaultpasswordlength", "could not create error!");
1164            }
1165            return NULL;
1166    }
1167}
1168