]> git.saurik.com Git - apple/security.git/blob - sec/Security/SecPasswordGenerate.c
Security-55471.14.18.tar.gz
[apple/security.git] / sec / Security / SecPasswordGenerate.c
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.
42 CFStringRef kSecPasswordMinLengthKey = CFSTR("PasswordMinLength");
43 CFStringRef kSecPasswordMaxLengthKey = CFSTR("PasswordMaxLength");
44 CFStringRef kSecPasswordAllowedCharactersKey = CFSTR("PasswordAllowedCharacters");
45 CFStringRef kSecPasswordRequiredCharactersKey = CFSTR("PasswordRequiredCharacters");
46 CFStringRef kSecPasswordDefaultForType = CFSTR("PasswordDefaultForType");
47
48 CFStringRef kSecPasswordDisallowedCharacters = CFSTR("PasswordDisallowedCharacters");
49 CFStringRef kSecPasswordCantStartWithChars = CFSTR("PasswordCantStartWithChars");
50 CFStringRef kSecPasswordCantEndWithChars = CFSTR("PasswordCantEndWithChars");
51 CFStringRef kSecPasswordContainsNoMoreThanNSpecificCharacters = CFSTR("PasswordContainsNoMoreThanNSpecificCharacters");
52 CFStringRef kSecPasswordContainsAtLeastNSpecificCharacters = CFSTR("PasswordContainsAtLeastNSpecificCharacters");
53 CFStringRef kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters = CFSTR("PasswordContainsNoMoreThanNConsecutiveIdenticalCharacters");
54 CFStringRef kSecPasswordCharacterCount = CFSTR("PasswordCharacterCount");
55 CFStringRef kSecPasswordCharacters = CFSTR("PasswordCharacters");
56
57 CFStringRef kSecPasswordGroupSize = CFSTR("PasswordGroupSize");
58 CFStringRef kSecPasswordNumberOfGroups = CFSTR("PasswordNumberOfGroups");
59 CFStringRef kSecPasswordSeparator = CFSTR("SecPasswordSeparator");
60
61 // Keys for internally used dictionaries with password generation parameters (never exposed to external API).
62 static CFStringRef kSecUseDefaultPasswordFormatKey = CFSTR("UseDefaultPasswordFormat");
63 static CFStringRef kSecNumberOfRequiredRandomCharactersKey = CFSTR("NumberOfRequiredRandomCharacters");
64 static CFStringRef kSecAllowedCharactersKey = CFSTR("AllowedCharacters");
65 static CFStringRef kSecRequiredCharacterSetsKey = CFSTR("RequiredCharacterSets");
66
67 static CFIndex defaultNumberOfRandomCharacters = 20;
68 static CFIndex defaultPINLength = 4;
69 static CFIndex defaultiCloudPasswordLength = 24;
70 static CFIndex defaultWifiPasswordLength = 12;
71
72 static CFStringRef defaultWifiCharacters = CFSTR("abcdefghijklmnopqrstuvwxyz1234567890");
73 static CFStringRef defaultPINCharacters = CFSTR("0123456789");
74 static CFStringRef defaultiCloudCharacters = CFSTR("ABCDEFGHJKLMNPQRSTUVWXYZ23456789");
75 static CFStringRef defaultCharacters = CFSTR("abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789");
76
77 static CFCharacterSetRef uppercaseLetterCharacterSet;
78 static CFCharacterSetRef lowercaseLetterCharacterSet;
79 static CFCharacterSetRef decimalDigitCharacterSet;
80 static CFCharacterSetRef punctuationCharacterSet;
81
82 static CFIndex alphabetSetSize = 26;
83 static CFIndex decimalSetSize = 10;
84 static CFIndex punctuationSetSize = 33;
85 static 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 */
92 const 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. */
118 error "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
133 inline
134 #endif
135 #endif
136 static 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
172 static const char *blacklist[] = {"1234", "1004", "2000", "1122", "4321", "2001", "2580"};
173
174 bool 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
326 static 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
347 static 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
358 fail:
359 return false;
360
361 }
362
363 static 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;
382 fail:
383 return false;
384 }
385
386 static 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
403 static 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
420 static 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;
429 fail:
430 CFReleaseNull(characterSet);
431 return false;
432 }
433
434 static 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
470 static 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;
479 fail:
480 CFReleaseNull(characterSet);
481 return true;
482 }
483
484 static 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
493 fail:
494 CFReleaseNull(characterSet);
495 return true;
496 }
497
498 static 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 }
602 static 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
766 static 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 }
906 fail:
907 if (tempError != NULL) {
908 *error = tempError;
909 CFRetain(*error);
910 return false;
911 }
912
913 CFReleaseNull(tempError);
914 return true;
915 }
916
917 static 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
981 CF_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
1066 fail:
1067 CFReleaseNull(properlyFormattedRequirements);
1068 return NULL;
1069 }
1070
1071 const 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 }
1109 CFDictionaryRef 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 }