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