SecPasswordGenerate.c
1 /* 2 * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 /* 25 * SecPasswordStrength.c 26 */ 27 28 #include <limits.h> 29 #include <CoreFoundation/CoreFoundation.h> 30 #include <Security/SecItem.h> 31 #include <Security/SecBase.h> 32 #include <Security/SecRandom.h> 33 #include "SecPasswordGenerate.h" 34 #include <AssertMacros.h> 35 #include <fcntl.h> 36 #include <unistd.h> 37 #include <utilities/SecCFWrappers.h> 38 #include <utilities/SecCFRelease.h> 39 #include <utilities/SecCFError.h> 40 #include <corecrypto/ccdigest.h> 41 #include <corecrypto/ccsha2.h> 42 #include "SecCFAllocator.h" 43 44 45 // Keys for external dictionaries with password generation requirements we read from plist. 46 CFStringRef kSecPasswordMinLengthKey = CFSTR("PasswordMinLength"); 47 CFStringRef kSecPasswordMaxLengthKey = CFSTR("PasswordMaxLength"); 48 CFStringRef kSecPasswordAllowedCharactersKey = CFSTR("PasswordAllowedCharacters"); 49 CFStringRef kSecPasswordRequiredCharactersKey = CFSTR("PasswordRequiredCharacters"); 50 CFStringRef kSecPasswordDefaultForType = CFSTR("PasswordDefaultForType"); 51 52 CFStringRef kSecPasswordDisallowedCharacters = CFSTR("PasswordDisallowedCharacters"); 53 CFStringRef kSecPasswordCantStartWithChars = CFSTR("PasswordCantStartWithChars"); 54 CFStringRef kSecPasswordCantEndWithChars = CFSTR("PasswordCantEndWithChars"); 55 CFStringRef kSecPasswordContainsNoMoreThanNSpecificCharacters = CFSTR("PasswordContainsNoMoreThanNSpecificCharacters"); 56 CFStringRef kSecPasswordContainsAtLeastNSpecificCharacters = CFSTR("PasswordContainsAtLeastNSpecificCharacters"); 57 CFStringRef kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters = CFSTR("PasswordContainsNoMoreThanNConsecutiveIdenticalCharacters"); 58 CFStringRef kSecPasswordCharacterCount = CFSTR("PasswordCharacterCount"); 59 CFStringRef kSecPasswordCharacters = CFSTR("PasswordCharacters"); 60 61 CFStringRef kSecPasswordGroupSize = CFSTR("PasswordGroupSize"); 62 CFStringRef kSecPasswordNumberOfGroups = CFSTR("PasswordNumberOfGroups"); 63 CFStringRef kSecPasswordSeparator = CFSTR("SecPasswordSeparator"); 64 65 // Keys for internally used dictionaries with password generation parameters (never exposed to external API). 66 static CFStringRef kSecUseDefaultPasswordFormatKey = CFSTR("UseDefaultPasswordFormat"); 67 static CFStringRef kSecNumberOfRequiredRandomCharactersKey = CFSTR("NumberOfRequiredRandomCharacters"); 68 static CFStringRef kSecNumberOfChecksumCharactersKey = CFSTR("NumberOfChecksumCharacters"); 69 static CFStringRef kSecAllowedCharactersKey = CFSTR("AllowedCharacters"); 70 static CFStringRef kSecRequiredCharacterSetsKey = CFSTR("RequiredCharacterSets"); 71 72 static CFIndex defaultNumberOfRandomCharacters = 20; 73 static CFIndex defaultPINLength = 4; 74 static CFIndex defaultiCloudPasswordLength = 24; 75 static CFIndex defaultWifiPasswordLength = 12; 76 77 static CFStringRef defaultWifiCharacters = CFSTR("abcdefghijklmnopqrstuvwxyz1234567890"); 78 static CFStringRef defaultPINCharacters = CFSTR("0123456789"); 79 static CFStringRef defaultiCloudCharacters = CFSTR("ABCDEFGHJKLMNPQRSTUVWXYZ23456789"); 80 static CFStringRef defaultCharacters = CFSTR("abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789"); 81 82 static CFCharacterSetRef uppercaseLetterCharacterSet; 83 static CFCharacterSetRef lowercaseLetterCharacterSet; 84 static CFCharacterSetRef decimalDigitCharacterSet; 85 static CFCharacterSetRef punctuationCharacterSet; 86 87 static CFIndex alphabetSetSize = 26; 88 static CFIndex decimalSetSize = 10; 89 static CFIndex punctuationSetSize = 33; 90 static double entropyStrengthThreshold = 35.0; 91 92 /* 93 generated with ruby badpins.rb | gperf 94 See this for PIN list: 95 A birthday present every eleven wallets? The security of customer-chosen banking PINs (2012), by Joseph Bonneau , Sören Preibusch , Ross Anderson 96 */ 97 const char *in_word_set (const char *str, unsigned int len); 98 99 #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ 100 && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ 101 && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ 102 && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ 103 && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ 104 && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ 105 && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ 106 && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ 107 && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ 108 && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ 109 && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ 110 && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ 111 && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ 112 && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ 113 && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ 114 && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ 115 && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ 116 && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ 117 && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ 118 && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ 119 && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ 120 && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ 121 && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) 122 /* The character set is not based on ISO-646. */ 123 error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>." 124 #endif 125 126 127 #define TOTAL_KEYWORDS 100 128 #define MIN_WORD_LENGTH 4 129 #define MAX_WORD_LENGTH 4 130 #define MIN_HASH_VALUE 21 131 #define MAX_HASH_VALUE 275 132 /* maximum key range = 255, duplicates = 0 */ 133 134 #ifdef __GNUC__ 135 __inline 136 #else 137 #ifdef __cplusplus 138 inline 139 #endif 140 #endif 141 static unsigned int pinhash (const char *str, unsigned int len) 142 { 143 static unsigned short asso_values[] = 144 { 145 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 146 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 147 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 148 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 149 276, 276, 276, 276, 276, 276, 276, 276, 5, 0, 150 10, 10, 30, 50, 100, 120, 70, 25, 57, 85, 151 2, 4, 1, 19, 14, 11, 92, 276, 276, 276, 152 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 153 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 154 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 155 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 156 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 157 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 158 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 159 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 160 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 161 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 162 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 163 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 164 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 165 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 166 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 167 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 168 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 169 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 170 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 171 276, 276, 276, 276, 276 172 }; 173 return len + asso_values[(unsigned char)str[3]+9] + asso_values[(unsigned char)str[2]] + asso_values[(unsigned char)str[1]] + asso_values[(unsigned char)str[0]+3]; 174 } 175 176 ///<://problem/29089896> warn against using these common PINs 177 static bool isTopTenSixDigitPasscode(CFStringRef passcode){ 178 bool result = false; 179 CFMutableArrayRef topTen = CFArrayCreateMutableForCFTypesWith(kCFAllocatorDefault, 180 CFSTR("030379"), 181 CFSTR("101471"), 182 CFSTR("112233"), 183 CFSTR("123123"), 184 CFSTR("123321"), 185 CFSTR("123654"), 186 CFSTR("147258"), 187 CFSTR("159753"), 188 CFSTR("321654"), 189 CFSTR("520131"), 190 CFSTR("520520"), 191 CFSTR("789456"), NULL); 192 193 for(CFIndex i = 0; i < CFArrayGetCount(topTen); i++){ 194 if(CFEqualSafe(passcode, CFArrayGetValueAtIndex(topTen, i))){ 195 result = true; 196 break; 197 } 198 } 199 CFReleaseNull(topTen); 200 return result; 201 } 202 203 CFStringRef SecPasswordCreateWithRandomDigits(int n, CFErrorRef *error){ 204 int min = n; 205 int max = n; 206 207 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter); 208 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter); 209 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit); 210 punctuationCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetPunctuation); 211 212 CFNumberRef minRef = CFNumberCreate(NULL, kCFNumberIntType, &min); 213 CFNumberRef maxRef = CFNumberCreate(NULL, kCFNumberIntType, &max); 214 215 CFMutableDictionaryRef passwordRequirements = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 216 CFDictionaryAddValue(passwordRequirements, kSecPasswordMinLengthKey, minRef); 217 CFDictionaryAddValue(passwordRequirements, kSecPasswordMaxLengthKey, maxRef); 218 CFStringRef allowedCharacters = CFSTR("0123456789"); 219 220 CFDictionaryAddValue(passwordRequirements, kSecPasswordAllowedCharactersKey, allowedCharacters); 221 222 CFStringRef password = SecPasswordGenerate(kSecPasswordTypePIN, error, passwordRequirements); 223 224 CFReleaseNull(minRef); 225 CFReleaseNull(maxRef); 226 CFReleaseNull(passwordRequirements); 227 228 return password; 229 } 230 231 232 233 //pins that reached the top 20 list 234 static const char *blacklist[] = {"1234", "1004", "2000", "1122", "4321", "2001", "2580"}; 235 bool SecPasswordIsPasswordWeak(CFStringRef passcode) 236 { 237 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter); 238 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter); 239 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit); 240 punctuationCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetPunctuation); 241 242 bool isNumber = true; 243 char* pin = NULL; 244 245 //length checks 246 if( CFStringGetLength(passcode) < 4 ){ 247 return true; //weak password 248 } 249 //check to see if passcode is a number 250 for(CFIndex i = 0; i < CFStringGetLength(passcode); i++){ 251 if( CFStringFindCharacterFromSet(passcode, decimalDigitCharacterSet, CFRangeMake(i,1), 0, NULL)) 252 continue; 253 else { 254 isNumber = false; 255 break; 256 } 257 } 258 //checking to see if it's a 4 digit pin 259 if(isNumber && CFStringGetLength(passcode) == 4){ 260 261 pin = CFStringToCString(passcode); 262 if(in_word_set(pin, 4)){ 263 free(pin); 264 return true; 265 } 266 267 CFIndex blacklistLength = (CFIndex)sizeof(blacklist)/sizeof(blacklist[0]); 268 269 //not all the same number 270 if(pin[0] == pin[1] == pin[2] == pin[3]){ 271 free(pin); 272 return true; //weak password 273 } 274 //first two digits being the same and the last two digits being the same 275 if ( pin[0] == pin[1] && pin[2] == pin[3]){ 276 free(pin); 277 return true; //weak password 278 } 279 //first two digits not being the same as the last two digits 280 if(pin[0] == pin[2] && pin[1] == pin[3]){ 281 free(pin); 282 return true; //weak password 283 } 284 //check if PIN is a bunch of incrementing numbers 285 for(int i = 0; i < CFStringGetLength(passcode); i++){ 286 if(i == CFStringGetLength(passcode)-1){ 287 free(pin); 288 return true; 289 } 290 else if ((pin[i] + 1) == pin[i+1]) 291 continue; 292 else 293 break; 294 } 295 //check if PIN is a bunch of decrementing numbers 296 for(int i = 0; i < CFStringGetLength(passcode); i++){ 297 if(i == CFStringGetLength(passcode)-1){ 298 free(pin); 299 return true; 300 } 301 else if ((pin[i]) == (pin[i+1] +1)) 302 continue; 303 else if ((i == 0) && (pin[i] == '0') && (pin[i+1] == '9')) 304 continue; 305 else 306 break; 307 } 308 309 //not in this list 310 for(CFIndex i = 0; i < blacklistLength; i++) 311 { 312 const char* blackCode = blacklist[i]; 313 if(0 == strcmp(blackCode, pin)) 314 { 315 free(pin); 316 return true; //weak password 317 } 318 } 319 } 320 else if(isNumber){ //dealing with a numeric PIN 321 pin = CFStringToCString(passcode); 322 //check if PIN is all the same number 323 for(int i = 0; i < CFStringGetLength(passcode); i++){ 324 if(i+1 >= CFStringGetLength(passcode)){ 325 free(pin); 326 return true; 327 } 328 else if (pin[i] == pin[i+1]) 329 continue; 330 else 331 break; 332 } 333 //check if PIN is a bunch of incrementing numbers 334 for(int i = 0; i < CFStringGetLength(passcode); i++){ 335 if(i == CFStringGetLength(passcode)-1){ 336 free(pin); 337 return true; 338 } 339 else if ((pin[i] + 1) == pin[i+1]) 340 continue; 341 else 342 break; 343 } 344 //check if PIN is a bunch of decrementing numbers 345 for(int i = 0; i < CFStringGetLength(passcode); i++){ 346 if(i == CFStringGetLength(passcode)-1){ 347 free(pin); 348 return true; 349 } 350 else if ((pin[i]) == (pin[i+1] +1)) 351 continue; 352 else if ((i == 0) && (pin[i] == '0') && (pin[i+1] == '9')) 353 continue; 354 else 355 break; 356 } 357 } 358 else{ // password is complex, evaluate entropy 359 int u = 0; 360 int l = 0; 361 int d = 0; 362 int p = 0; 363 int characterSet = 0; 364 365 //calculate new entropy 366 for(CFIndex i = 0; i < CFStringGetLength(passcode); i++){ 367 368 if( CFStringFindCharacterFromSet(passcode, uppercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){ 369 u++; 370 continue; 371 } 372 if( CFStringFindCharacterFromSet(passcode, lowercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){ 373 l++; 374 continue; 375 } 376 if( CFStringFindCharacterFromSet(passcode, decimalDigitCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){ 377 d++; 378 continue; 379 } 380 if( CFStringFindCharacterFromSet(passcode, punctuationCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){ 381 p++; 382 continue; 383 } 384 385 } 386 if(u > 0){ 387 characterSet += alphabetSetSize; 388 } 389 if(l > 0){ 390 characterSet += alphabetSetSize; 391 } 392 if(d > 0){ 393 characterSet += decimalSetSize; 394 } 395 if(p > 0){ 396 characterSet += punctuationSetSize; 397 } 398 399 double strength = CFStringGetLength(passcode)*log2(characterSet); 400 401 if(strength < entropyStrengthThreshold){ 402 return true; //weak 403 } 404 else 405 return false; //strong 406 } 407 if(pin) 408 free(pin); 409 410 return false; //strong password 411 412 } 413 414 static bool SecPasswordIsPasscodeIncrementingOrDecrementingDigits(CFStringRef passcode) 415 { 416 char* pin = CFStringToCString(passcode); 417 418 //check if PIN is a bunch of incrementing numbers 419 for(int i = 0; i < CFStringGetLength(passcode); i++){ 420 if(i == CFStringGetLength(passcode)-1){ 421 free(pin); 422 return true; 423 } 424 else if ((pin[i] + 1) == pin[i+1]) 425 continue; 426 else 427 break; 428 } 429 //check if PIN is a bunch of decrementing numbers 430 for(int i = 0; i < CFStringGetLength(passcode); i++){ 431 if(i == CFStringGetLength(passcode)-1){ 432 free(pin); 433 return true; 434 } 435 else if ((pin[i]) == (pin[i+1] +1)) 436 continue; 437 else if ((i == 0) && (pin[i] == '0') && (pin[i+1] == '9')) 438 continue; 439 else 440 break; 441 } 442 free(pin); 443 return false; 444 } 445 446 static bool SecPasswordIsPasswordRepeatingTwoNumbers(CFStringRef passcode){ 447 char* pin = CFStringToCString(passcode); 448 449 for(int i = 0; i < CFStringGetLength(passcode); i++) 450 { 451 if(i+2 == CFStringGetLength(passcode)-1){ 452 free(pin); 453 return true; 454 } 455 else if(pin[i] == pin[i+2]) 456 continue; 457 else 458 break; 459 } 460 461 free(pin); 462 return false; 463 } 464 465 static int SecPasswordNumberOfRepeatedDigits(CFStringRef passcode){ 466 int repeating = 1; 467 CFIndex length = CFStringGetLength(passcode); 468 CFNumberRef highest = NULL; 469 CFMutableArrayRef highestRepeatingcount = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 470 471 for(int i = 0; i < length; i++){ 472 473 if(i+1 == length){ 474 CFNumberRef newRepeatingAddition = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &repeating); 475 CFArrayAppendValue(highestRepeatingcount, newRepeatingAddition); 476 CFReleaseNull(newRepeatingAddition); 477 break; 478 } 479 if(CFStringGetCharacterAtIndex(passcode, i) == CFStringGetCharacterAtIndex(passcode,i+1)) 480 repeating++; 481 else{ 482 if(repeating != 1) 483 { 484 CFNumberRef newRepeatingAddition = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &repeating); 485 CFArrayAppendValue(highestRepeatingcount, newRepeatingAddition); 486 CFReleaseNull(newRepeatingAddition); 487 } 488 repeating = 1; 489 490 } 491 } 492 493 for(int i =0; i< CFArrayGetCount(highestRepeatingcount); i++){ 494 if(i == 0){ 495 highest = CFArrayGetValueAtIndex(highestRepeatingcount, i); 496 continue; 497 } 498 else{ 499 CFNumberRef competitor = CFArrayGetValueAtIndex(highestRepeatingcount, i); 500 if(CFNumberCompare(competitor, highest, NULL) == kCFCompareGreaterThan){ 501 highest = competitor; 502 } 503 } 504 } 505 int finalRepeating = 0; 506 if(highest != NULL) 507 CFNumberGetValue(highest, kCFNumberIntType, &finalRepeating); 508 509 CFReleaseNull(highestRepeatingcount); 510 return finalRepeating; 511 } 512 513 static bool SecPasswordIsPalindrome(CFStringRef passcode){ 514 char* pin = CFStringToCString(passcode); 515 long length = CFStringGetLength(passcode); 516 long j = length-1; 517 518 for(int i = 0; i < CFStringGetLength(passcode); i++) 519 { 520 if(length%2 == 1 && i == j){ 521 free(pin); 522 return true; 523 } 524 else if(length%2 == 0 && i == j-1){ 525 if(pin[i] == pin[j]){ 526 free(pin); 527 return true; 528 } 529 else 530 break; 531 } 532 else if(pin[i] == pin[j]){ 533 j--; 534 continue; 535 } 536 else 537 break; 538 } 539 free(pin); 540 return false; 541 } 542 543 static bool SecPasswordHasRepeatingGroups(CFStringRef passcode){ 544 char* pin = CFStringToCString(passcode); 545 546 for(int i = 0; i < CFStringGetLength(passcode); i++) 547 { 548 if(i+4 == CFStringGetLength(passcode)){ 549 if(pin[i] == pin[i+3]){ 550 free(pin); 551 return true; 552 } 553 else 554 break; 555 } 556 else if(pin[i] == pin[i+3]) 557 continue; 558 else 559 break; 560 } 561 562 free(pin); 563 564 return false; 565 } 566 567 bool SecPasswordIsPasswordWeak2(bool isSimple, CFStringRef passcode) 568 { 569 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter); 570 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter); 571 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit); 572 punctuationCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetPunctuation); 573 574 575 char* pin = NULL; 576 577 //length checks 578 if( CFStringGetLength(passcode) < 4 ){ 579 return true; //weak password 580 } 581 582 bool isPasscodeNumber = true; 583 //check to see if passcode is a number 584 for(CFIndex i = 0; i < CFStringGetLength(passcode); i++){ 585 if( CFStringFindCharacterFromSet(passcode, decimalDigitCharacterSet, CFRangeMake(i,1), 0, NULL)) 586 continue; 587 else { 588 isPasscodeNumber = false; 589 break; 590 } 591 } 592 593 if(isSimple){ 594 //checking to see if it's a 4 digit pin 595 if(isPasscodeNumber && CFStringGetLength(passcode) == 4){ 596 597 pin = CFStringToCString(passcode); 598 if(in_word_set(pin, 4)){ 599 free(pin); 600 return true; 601 } 602 603 CFIndex blacklistLength = (CFIndex)sizeof(blacklist)/sizeof(blacklist[0]); 604 605 //not all the same number 606 if(pin[0] == pin[1] == pin[2] == pin[3]){ 607 free(pin); 608 return true; //weak password 609 } 610 //first two digits being the same and the last two digits being the same 611 if ( pin[0] == pin[1] && pin[2] == pin[3]){ 612 free(pin); 613 return true; //weak password 614 } 615 //first two digits not being the same as the last two digits 616 if(pin[0] == pin[2] && pin[1] == pin[3]){ 617 free(pin); 618 return true; //weak password 619 } 620 //not in this list 621 for(CFIndex i = 0; i < blacklistLength; i++) 622 { 623 const char* blackCode = blacklist[i]; 624 if(0 == strcmp(blackCode, pin)) 625 { 626 free(pin); 627 return true; //weak password 628 } 629 } 630 } 631 else if(isPasscodeNumber && CFStringGetLength(passcode) == 6){ 632 pin = CFStringToCString(passcode); 633 634 //not all the same number 635 for(int i = 0; i < CFStringGetLength(passcode); i++){ 636 if(i == CFStringGetLength(passcode)-1){ 637 free(pin); 638 return true; 639 } 640 else if ((pin[i]) == pin[i+1]) 641 continue; 642 else 643 break; 644 } 645 //not in the top 10 646 if(isTopTenSixDigitPasscode(passcode)){ 647 free(pin); 648 return true; 649 } 650 //palindrome test 651 if(SecPasswordIsPalindrome(passcode)){ 652 free(pin); 653 return true; 654 } 655 656 //2 identical groups 657 if(SecPasswordHasRepeatingGroups(passcode)){ 658 free(pin); 659 return true; 660 } 661 //passcode is incrementing ex 123456 or 654321 662 if(SecPasswordIsPasscodeIncrementingOrDecrementingDigits(passcode)) { 663 free(pin); 664 return true; 665 } 666 //passcode does not consist of 2 repeating digits 667 if(SecPasswordIsPasswordRepeatingTwoNumbers(passcode)){ 668 free(pin); 669 return true; 670 } 671 } 672 else//should be a 4 or 6digit number 673 return false; 674 } 675 else if(isPasscodeNumber && !isSimple){ //dealing with a complex numeric passcode 676 pin = CFStringToCString(passcode); 677 //check if PIN is all the same number 678 int repeatingDigits = SecPasswordNumberOfRepeatedDigits(passcode); 679 if(repeatingDigits >= (CFStringGetLength(passcode)/2)){ 680 free(pin); 681 return true; 682 } 683 //palindrome test 684 if(SecPasswordIsPalindrome(passcode)){ 685 free(pin); 686 return true; 687 } 688 //not in the top 10 689 if(isTopTenSixDigitPasscode(passcode)){ 690 free(pin); 691 return true; 692 } 693 //2 identical groups 694 if(SecPasswordHasRepeatingGroups(passcode) && CFStringGetLength(passcode) >= 6){ 695 free(pin); 696 return true; 697 } 698 699 if(SecPasswordIsPasscodeIncrementingOrDecrementingDigits(passcode)) { 700 free(pin); 701 return true; 702 } 703 if(SecPasswordIsPasswordRepeatingTwoNumbers(passcode)){ 704 free(pin); 705 return true; 706 } 707 708 } 709 else{ // password is complex, evaluate entropy 710 int u = 0; 711 int l = 0; 712 int d = 0; 713 int p = 0; 714 int characterSet = 0; 715 716 //calculate new entropy 717 for(CFIndex i = 0; i < CFStringGetLength(passcode); i++){ 718 719 if( CFStringFindCharacterFromSet(passcode, uppercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){ 720 u++; 721 continue; 722 } 723 if( CFStringFindCharacterFromSet(passcode, lowercaseLetterCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){ 724 l++; 725 continue; 726 } 727 if( CFStringFindCharacterFromSet(passcode, decimalDigitCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){ 728 d++; 729 continue; 730 } 731 if( CFStringFindCharacterFromSet(passcode, punctuationCharacterSet, CFRangeMake(i,1), kCFCompareBackwards, NULL)){ 732 p++; 733 continue; 734 } 735 736 } 737 if(u > 0){ 738 characterSet += alphabetSetSize; 739 } 740 if(l > 0){ 741 characterSet += alphabetSetSize; 742 } 743 if(d > 0){ 744 characterSet += decimalSetSize; 745 } 746 if(p > 0){ 747 characterSet += punctuationSetSize; 748 } 749 750 double strength = CFStringGetLength(passcode)*log2(characterSet); 751 752 if(strength < entropyStrengthThreshold){ 753 return true; //weak 754 } 755 else 756 return false; //strong 757 } 758 if(pin) 759 free(pin); 760 761 return false; //strong password 762 763 } 764 765 static void getUniformRandomNumbers(uint8_t* buffer, size_t numberOfDesiredNumbers, uint8_t upperBound) 766 { 767 768 // The values returned by SecRandomCopyBytes are uniformly distributed in the range [0, 255]. If we try to map 769 // these values onto a smaller range using modulo we will introduce a bias towards lower numbers in situations 770 // where our smaller range doesn’t evenly divide in to [0, 255]. For example, with the desired range of [0, 54] 771 // the ranges 0..54, 55..109, 110..164, and 165..219 are uniformly distributed, but the range 220..255 modulo 55 772 // is only distributed over [0, 35], giving significant bias to these lower values. So, we ignore random numbers 773 // that would introduce this bias. 774 uint8_t limitAvoidingModuloBias = UCHAR_MAX - (UCHAR_MAX % upperBound); 775 776 for (size_t numberOfAcceptedNumbers = 0; numberOfAcceptedNumbers < numberOfDesiredNumbers; ) { 777 if (SecRandomCopyBytes(kSecRandomDefault, numberOfDesiredNumbers - numberOfAcceptedNumbers, buffer + numberOfAcceptedNumbers) == -1) 778 continue; 779 for (size_t i = numberOfAcceptedNumbers; i < numberOfDesiredNumbers; ++i) { 780 if (buffer[i] < limitAvoidingModuloBias) 781 buffer[numberOfAcceptedNumbers++] = buffer[i] % upperBound; 782 } 783 } 784 } 785 786 static bool passwordContainsRequiredCharacters(CFStringRef password, CFArrayRef requiredCharacterSets) 787 { 788 CFCharacterSetRef characterSet; 789 790 for (CFIndex i = 0; i< CFArrayGetCount(requiredCharacterSets); i++) { 791 characterSet = CFArrayGetValueAtIndex(requiredCharacterSets, i); 792 CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(password)); 793 require_quiet(CFStringFindCharacterFromSet(password, characterSet, rangeToSearch, 0, NULL), fail); 794 } 795 return true; 796 797 fail: 798 return false; 799 800 } 801 802 static bool passwordContainsLessThanNIdenticalCharacters(CFStringRef password, CFIndex identicalCount) 803 { 804 unsigned char Char, nextChar; 805 int repeating = 0; 806 807 for(CFIndex i = 0; i < CFStringGetLength(password); i++){ 808 Char = CFStringGetCharacterAtIndex(password, i); 809 for(CFIndex j = i; j< CFStringGetLength(password); j++){ 810 nextChar = CFStringGetCharacterAtIndex(password, j); 811 require_quiet(repeating <= identicalCount, fail); 812 if(Char == nextChar){ 813 repeating++; 814 }else{ 815 repeating = 0; 816 break; 817 } 818 } 819 } 820 return true; 821 fail: 822 return false; 823 } 824 825 static bool passwordContainsAtLeastNCharacters(CFStringRef password, CFStringRef characters, CFIndex N) 826 { 827 CFCharacterSetRef characterSet = NULL; 828 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, characters); 829 CFIndex counter = 0; 830 831 for(CFIndex i = 0; i < CFStringGetLength(password); i++){ 832 if(CFStringFindCharacterFromSet(password, characterSet, CFRangeMake(i, 1), 0, NULL)) 833 counter++; 834 } 835 CFReleaseNull(characterSet); 836 if(counter < N) 837 return false; 838 else 839 return true; 840 } 841 842 static bool passwordContainsLessThanNCharacters(CFStringRef password, CFStringRef characters, CFIndex N) 843 { 844 CFCharacterSetRef characterSet = NULL; 845 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, characters); 846 CFIndex counter = 0; 847 848 for(CFIndex i = 0; i < CFStringGetLength(password); i++){ 849 if(CFStringFindCharacterFromSet(password, characterSet, CFRangeMake(i, 1), 0, NULL)) 850 counter++; 851 } 852 CFReleaseNull(characterSet); 853 if(counter > N) 854 return false; 855 else 856 return true; 857 } 858 859 static bool passwordDoesNotContainCharacters(CFStringRef password, CFStringRef prohibitedCharacters) 860 { 861 CFCharacterSetRef characterSet = NULL; 862 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters); 863 CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(password)); 864 865 require_quiet(!CFStringFindCharacterFromSet(password, characterSet, rangeToSearch, 0, NULL), fail); 866 CFReleaseNull(characterSet); 867 return true; 868 fail: 869 CFReleaseNull(characterSet); 870 return false; 871 } 872 873 static OSStatus getPasswordRandomCharacters(CFStringRef *returned, CFDictionaryRef requirements, CFIndex *numberOfRandomCharacters, CFStringRef allowedCharacters) 874 { 875 uint8_t *randomNumbers = malloc(*numberOfRandomCharacters); 876 unsigned char *randomCharacters = malloc(*numberOfRandomCharacters); 877 878 if (randomNumbers == NULL || randomCharacters == NULL) { 879 free(randomNumbers); 880 free(randomCharacters); 881 return errSecMemoryError; 882 } 883 884 getUniformRandomNumbers(randomNumbers, *numberOfRandomCharacters, CFStringGetLength(allowedCharacters)); 885 886 CFTypeRef prohibitedCharacters = NULL; 887 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters)) 888 prohibitedCharacters = NULL; 889 890 //it's faster for long characters to check each character produced for these cases 891 for (CFIndex i = 0; i < *numberOfRandomCharacters; ++i){ 892 //check prohibited characters 893 UniChar randomChar[1]; 894 randomChar[0] = CFStringGetCharacterAtIndex(allowedCharacters, randomNumbers[i]); 895 if (prohibitedCharacters != NULL) 896 { 897 CFStringRef temp = CFStringCreateWithCharacters(kCFAllocatorDefault, randomChar, 1); 898 bool pwdncc = passwordDoesNotContainCharacters(temp, prohibitedCharacters); 899 CFReleaseSafe(temp); 900 if (!pwdncc) { 901 //change up the random numbers so we don't get the same index into allowed 902 getUniformRandomNumbers(randomNumbers, *numberOfRandomCharacters, CFStringGetLength(allowedCharacters)); 903 i--; 904 continue; 905 } 906 } 907 randomCharacters[i] = (unsigned char)randomChar[0]; 908 } 909 910 *returned = CFStringCreateWithBytes(kCFAllocatorDefault, randomCharacters, *numberOfRandomCharacters, kCFStringEncodingUTF8, false); 911 912 free(randomCharacters); 913 free(randomNumbers); 914 915 return errSecSuccess; 916 } 917 918 static bool doesPasswordEndWith(CFStringRef password, CFStringRef prohibitedCharacters) 919 { 920 CFCharacterSetRef characterSet = NULL; 921 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters); 922 923 CFRange rangeToSearch = CFRangeMake(CFStringGetLength(password) - CFStringGetLength(prohibitedCharacters), CFStringGetLength(prohibitedCharacters)); 924 require_quiet(0 == CFStringCompareWithOptions(password, prohibitedCharacters, rangeToSearch, 0), fail); 925 CFReleaseNull(characterSet); 926 return false; 927 fail: 928 CFReleaseNull(characterSet); 929 return true; 930 } 931 932 static bool doesPasswordStartWith(CFStringRef password, CFStringRef prohibitedCharacters) 933 { 934 CFCharacterSetRef characterSet = NULL; 935 characterSet = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, prohibitedCharacters); 936 937 CFRange rangeToSearch = CFRangeMake(0, CFStringGetLength(prohibitedCharacters)); 938 require_quiet(0 == CFStringCompareWithOptions(password, prohibitedCharacters, rangeToSearch, 0), fail); 939 CFReleaseNull(characterSet); 940 return false; //does not start with prohibitedCharacters 941 fail: 942 CFReleaseNull(characterSet); 943 return true; 944 } 945 946 static CFDictionaryRef passwordGenerateCreateDefaultParametersDictionary(SecPasswordType type, CFDictionaryRef requirements){ 947 948 CFMutableArrayRef requiredCharacterSets = NULL; 949 CFNumberRef numReqChars = NULL, checksumChars = NULL; 950 CFStringRef defaultPasswordFormat = NULL; 951 requiredCharacterSets = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 952 defaultPasswordFormat = CFSTR("true"); 953 CFTypeRef groupSizeRef = NULL, numberOfGroupsRef = NULL; 954 CFIndex groupSize, numberOfGroups, checksumSize = 0; 955 CFDictionaryRef returned = NULL; 956 957 switch(type){ 958 case kSecPasswordTypeiCloudRecoveryKey: 959 groupSize = 4; 960 numberOfGroups = 7; 961 checksumSize = 2; 962 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, (groupSize * numberOfGroups) - checksumSize); 963 checksumChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, checksumSize); 964 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize); 965 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups); 966 967 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter); 968 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit); 969 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet); 970 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet); 971 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 972 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat, 973 kSecNumberOfRequiredRandomCharactersKey, numReqChars, 974 kSecNumberOfChecksumCharactersKey, checksumChars, 975 kSecAllowedCharactersKey, defaultiCloudCharacters, 976 kSecRequiredCharacterSetsKey, requiredCharacterSets, 977 kSecPasswordGroupSize, groupSizeRef, 978 kSecPasswordNumberOfGroups, numberOfGroupsRef, 979 NULL); 980 break; 981 case(kSecPasswordTypeiCloudRecovery): 982 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultiCloudPasswordLength); 983 groupSize = 4; 984 numberOfGroups = 6; 985 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize); 986 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups); 987 988 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter); 989 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit); 990 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet); 991 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet); 992 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 993 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat, 994 kSecNumberOfRequiredRandomCharactersKey, numReqChars, 995 kSecAllowedCharactersKey, defaultiCloudCharacters, 996 kSecRequiredCharacterSetsKey, requiredCharacterSets, 997 kSecPasswordGroupSize, groupSizeRef, 998 kSecPasswordNumberOfGroups, numberOfGroupsRef, 999 NULL); 1000 break; 1001 1002 case(kSecPasswordTypePIN): 1003 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultPINLength); 1004 groupSize = 4; 1005 numberOfGroups = 1; 1006 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize); 1007 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups); 1008 1009 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit); 1010 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet); 1011 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 1012 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat, 1013 kSecNumberOfRequiredRandomCharactersKey, numReqChars, 1014 kSecAllowedCharactersKey, defaultPINCharacters, 1015 kSecRequiredCharacterSetsKey, requiredCharacterSets, 1016 kSecPasswordGroupSize, groupSizeRef, 1017 kSecPasswordNumberOfGroups, numberOfGroupsRef, 1018 NULL); 1019 break; 1020 1021 case(kSecPasswordTypeWifi): 1022 groupSize = 4; 1023 numberOfGroups = 3; 1024 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize); 1025 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups); 1026 1027 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter); 1028 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit); 1029 1030 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultWifiPasswordLength); 1031 CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet); 1032 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet); 1033 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 1034 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat, 1035 kSecNumberOfRequiredRandomCharactersKey, numReqChars, 1036 kSecAllowedCharactersKey, defaultWifiCharacters, 1037 kSecRequiredCharacterSetsKey, requiredCharacterSets, 1038 kSecPasswordGroupSize, groupSizeRef, 1039 kSecPasswordNumberOfGroups, numberOfGroupsRef, 1040 NULL); 1041 break; 1042 1043 default: 1044 groupSize = 4; 1045 numberOfGroups = 6; 1046 groupSizeRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &groupSize); 1047 numberOfGroupsRef = CFNumberCreate(NULL, kCFNumberCFIndexType, &numberOfGroups); 1048 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter); 1049 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter); 1050 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit); 1051 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet); 1052 CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet); 1053 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet); 1054 1055 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, defaultNumberOfRandomCharacters); 1056 returned = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 1057 kSecUseDefaultPasswordFormatKey, defaultPasswordFormat, 1058 kSecNumberOfRequiredRandomCharactersKey, numReqChars, 1059 kSecAllowedCharactersKey, defaultCharacters, 1060 kSecRequiredCharacterSetsKey, requiredCharacterSets, 1061 kSecPasswordGroupSize, groupSizeRef, 1062 kSecPasswordNumberOfGroups, numberOfGroupsRef, 1063 NULL); 1064 1065 1066 1067 break; 1068 } 1069 1070 CFReleaseNull(numReqChars); 1071 CFReleaseNull(requiredCharacterSets); 1072 CFReleaseNull(groupSizeRef); 1073 CFReleaseNull(numberOfGroupsRef); 1074 CFReleaseNull(checksumChars); 1075 return returned; 1076 } 1077 static CFDictionaryRef passwordGenerationCreateParametersDictionary(SecPasswordType type, CFDictionaryRef requirements) 1078 { 1079 CFMutableArrayRef requiredCharacterSets = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 1080 CFNumberRef numReqChars = NULL; 1081 CFIndex numberOfRequiredRandomCharacters; 1082 CFStringRef allowedCharacters = NULL, useDefaultPasswordFormat = NULL; 1083 uint64_t valuePtr; 1084 CFTypeRef prohibitedCharacters = NULL, endWith = NULL, startWith = NULL, 1085 groupSizeRef = NULL, numberOfGroupsRef = NULL, separatorRef = NULL, 1086 atMostCharactersRef = NULL,atLeastCharactersRef = NULL, identicalRef = NULL; 1087 1088 CFNumberRef min = (CFNumberRef)CFDictionaryGetValue(requirements, kSecPasswordMinLengthKey); 1089 CFNumberRef max = (CFNumberRef)CFDictionaryGetValue(requirements, kSecPasswordMaxLengthKey); 1090 1091 CFNumberGetValue(min, kCFNumberSInt64Type, &valuePtr); 1092 CFIndex minPasswordLength = (long)valuePtr; 1093 CFNumberGetValue(max, kCFNumberSInt64Type, &valuePtr); 1094 CFIndex maxPasswordLength = (long)valuePtr; 1095 1096 // If requirements allow, we will generate the password in default format. 1097 useDefaultPasswordFormat = CFSTR("true"); 1098 numberOfRequiredRandomCharacters = defaultNumberOfRandomCharacters; 1099 1100 if(type == kSecPasswordTypePIN) 1101 { 1102 if( maxPasswordLength && minPasswordLength ) 1103 numberOfRequiredRandomCharacters = maxPasswordLength; 1104 else if( !maxPasswordLength && minPasswordLength ) 1105 numberOfRequiredRandomCharacters = minPasswordLength; 1106 else if( !minPasswordLength && maxPasswordLength ) 1107 numberOfRequiredRandomCharacters = maxPasswordLength; 1108 else 1109 numberOfRequiredRandomCharacters = defaultPINLength; 1110 1111 allowedCharacters = CFSTR("0123456789"); 1112 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet); 1113 useDefaultPasswordFormat = CFSTR("false"); 1114 } 1115 else{ 1116 CFArrayRef requiredCharactersArray = NULL; 1117 1118 if (minPasswordLength && minPasswordLength > defaultNumberOfRandomCharacters) { 1119 useDefaultPasswordFormat = CFSTR("false"); 1120 numberOfRequiredRandomCharacters = minPasswordLength; 1121 } 1122 if (maxPasswordLength && maxPasswordLength < defaultNumberOfRandomCharacters) { 1123 useDefaultPasswordFormat = CFSTR("false"); 1124 numberOfRequiredRandomCharacters = maxPasswordLength; 1125 } 1126 if (maxPasswordLength && minPasswordLength && maxPasswordLength == minPasswordLength && maxPasswordLength != defaultNumberOfRandomCharacters){ 1127 useDefaultPasswordFormat = CFSTR("false"); 1128 numberOfRequiredRandomCharacters = maxPasswordLength; 1129 } 1130 allowedCharacters = (CFStringRef)CFRetainSafe(CFDictionaryGetValue(requirements, kSecPasswordAllowedCharactersKey)); 1131 requiredCharactersArray = (CFArrayRef)CFDictionaryGetValue(requirements, kSecPasswordRequiredCharactersKey); 1132 1133 if (requiredCharactersArray) { 1134 for (CFIndex i = 0; i < CFArrayGetCount(requiredCharactersArray); i++){ 1135 CFCharacterSetRef stringWithRequiredCharacters = CFArrayGetValueAtIndex(requiredCharactersArray, i); 1136 if(stringWithRequiredCharacters && CFStringFindCharacterFromSet(allowedCharacters, stringWithRequiredCharacters, CFRangeMake(0, CFStringGetLength(allowedCharacters)), 0, NULL)){ 1137 CFArrayAppendValue(requiredCharacterSets, stringWithRequiredCharacters); 1138 } 1139 } 1140 } else{ 1141 uppercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetUppercaseLetter); 1142 lowercaseLetterCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetLowercaseLetter); 1143 decimalDigitCharacterSet = CFCharacterSetGetPredefined(kCFCharacterSetDecimalDigit); 1144 CFArrayAppendValue(requiredCharacterSets, uppercaseLetterCharacterSet); 1145 CFArrayAppendValue(requiredCharacterSets, lowercaseLetterCharacterSet); 1146 CFArrayAppendValue(requiredCharacterSets, decimalDigitCharacterSet); 1147 } 1148 } 1149 1150 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters)) 1151 prohibitedCharacters = NULL; 1152 1153 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantEndWithChars, &endWith)) 1154 endWith = NULL; 1155 1156 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantStartWithChars, &startWith)) 1157 startWith = NULL; 1158 1159 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordGroupSize, &groupSizeRef)) 1160 groupSizeRef = NULL; 1161 1162 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef)) 1163 numberOfGroupsRef = NULL; 1164 1165 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordSeparator, &separatorRef)) 1166 separatorRef = NULL; 1167 1168 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharactersRef)) 1169 atMostCharactersRef = NULL; 1170 1171 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharactersRef)) 1172 atLeastCharactersRef = NULL; 1173 1174 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef)) 1175 identicalRef = NULL; 1176 1177 if (allowedCharacters) { 1178 if( false == CFStringFindWithOptions(allowedCharacters, CFSTR("-"), CFRangeMake(0, CFStringGetLength(allowedCharacters)), kCFCompareCaseInsensitive, NULL)) 1179 useDefaultPasswordFormat = CFSTR("false"); 1180 } else 1181 allowedCharacters = CFRetainSafe(defaultCharacters); 1182 1183 // In default password format, we use dashes only as separators, not as symbols you can encounter at a random position. 1184 if (useDefaultPasswordFormat == CFSTR("false")){ 1185 CFMutableStringRef mutatedAllowedCharacters = CFStringCreateMutableCopy(kCFAllocatorDefault, CFStringGetLength(allowedCharacters), allowedCharacters); 1186 CFStringFindAndReplace (mutatedAllowedCharacters, CFSTR("-"), CFSTR(""), CFRangeMake(0, CFStringGetLength(allowedCharacters)),kCFCompareCaseInsensitive); 1187 CFReleaseSafe(allowedCharacters); 1188 allowedCharacters = mutatedAllowedCharacters; 1189 } 1190 1191 if (CFArrayGetCount(requiredCharacterSets) > numberOfRequiredRandomCharacters) { 1192 CFReleaseNull(requiredCharacterSets); 1193 requiredCharacterSets = NULL; 1194 } 1195 //create new CFDictionary 1196 numReqChars = CFNumberCreateWithCFIndex(kCFAllocatorDefault, numberOfRequiredRandomCharacters); 1197 CFMutableDictionaryRef updatedConstraints = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 1198 CFDictionaryAddValue(updatedConstraints, kSecUseDefaultPasswordFormatKey, useDefaultPasswordFormat); 1199 CFDictionarySetValue(updatedConstraints, kSecNumberOfRequiredRandomCharactersKey, numReqChars); 1200 CFDictionaryAddValue(updatedConstraints, kSecAllowedCharactersKey, allowedCharacters); 1201 if(requiredCharacterSets) 1202 CFDictionaryAddValue(updatedConstraints, kSecRequiredCharacterSetsKey, requiredCharacterSets); 1203 1204 //add the prohibited characters string if it exists to the new dictionary 1205 if(prohibitedCharacters) 1206 CFDictionaryAddValue(updatedConstraints, kSecPasswordDisallowedCharacters, prohibitedCharacters); 1207 1208 //add the characters the password can't end with if it exists to the new dictionary 1209 if(endWith) 1210 CFDictionaryAddValue(updatedConstraints, kSecPasswordCantEndWithChars, endWith); 1211 1212 //add the characters the password can't start with if it exists to the new dictionary 1213 if(startWith) 1214 CFDictionaryAddValue(updatedConstraints, kSecPasswordCantStartWithChars, startWith); 1215 1216 if(groupSizeRef) 1217 CFDictionaryAddValue(updatedConstraints, kSecPasswordGroupSize, groupSizeRef); 1218 1219 if(numberOfGroupsRef) 1220 CFDictionaryAddValue(updatedConstraints, kSecPasswordNumberOfGroups, numberOfGroupsRef); 1221 1222 if(separatorRef) 1223 CFDictionaryAddValue(updatedConstraints, kSecPasswordSeparator, separatorRef); 1224 1225 if(atMostCharactersRef) 1226 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsNoMoreThanNSpecificCharacters, atMostCharactersRef); 1227 1228 if(atLeastCharactersRef) 1229 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsAtLeastNSpecificCharacters, atLeastCharactersRef); 1230 1231 if(identicalRef) 1232 CFDictionaryAddValue(updatedConstraints, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, identicalRef); 1233 1234 CFReleaseNull(useDefaultPasswordFormat); 1235 CFReleaseNull(numReqChars); 1236 CFReleaseNull(allowedCharacters); 1237 CFReleaseNull(requiredCharacterSets); 1238 1239 return updatedConstraints; 1240 } 1241 1242 static bool isDictionaryFormattedProperly(SecPasswordType type, CFDictionaryRef passwordRequirements, CFErrorRef *error){ 1243 1244 CFTypeRef defaults = NULL; 1245 CFErrorRef tempError = NULL; 1246 if(passwordRequirements == NULL){ 1247 return true; 1248 } 1249 1250 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) ){ 1251 if(isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0)){ 1252 return true; 1253 } 1254 } 1255 //only need to check max and min pin length formatting 1256 if(type == kSecPasswordTypePIN){ 1257 CFTypeRef minTest = NULL, maxTest = NULL; 1258 uint64_t valuePtr; 1259 CFIndex minPasswordLength = 0, maxPasswordLength= 0; 1260 1261 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) ){ 1262 if(isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0)){ 1263 return true; 1264 } 1265 } 1266 //check if the values exist! 1267 if( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMaxLengthKey, &maxTest) ){ 1268 require_action_quiet(isNull(maxTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL)); 1269 require_action_quiet(isNumber(maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL)); 1270 1271 } 1272 if (CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMinLengthKey, &minTest) ){ 1273 require_action_quiet(isNull(minTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL)); 1274 require_action_quiet(isNumber(minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL)); 1275 } 1276 //check if the values exist! 1277 if(maxTest){ 1278 CFNumberRef max = (CFNumberRef)maxTest; 1279 CFNumberGetValue(max, kCFNumberSInt64Type, &valuePtr); 1280 maxPasswordLength = (long)valuePtr; 1281 } 1282 if(minTest){ 1283 CFNumberRef min = (CFNumberRef)minTest; 1284 CFNumberGetValue(min, kCFNumberSInt64Type, &valuePtr); 1285 minPasswordLength = (long)valuePtr; 1286 } 1287 //make sure min and max make sense respective to each other and that they aren't less than 4 digits. 1288 require_action_quiet(minPasswordLength && maxPasswordLength && minPasswordLength <= maxPasswordLength, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex)errSecBadReq, NULL)); 1289 require_action_quiet((minPasswordLength && minPasswordLength >= 4) || (maxPasswordLength && maxPasswordLength >= 4), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex)errSecBadReq, NULL)); 1290 } 1291 else{ 1292 CFTypeRef allowedTest, maxTest, minTest, requiredTest, prohibitedCharacters, endWith, startWith, 1293 groupSizeRef, numberOfGroupsRef, separatorRef, atMostCharactersRef, 1294 atLeastCharactersRef, thresholdRef, identicalRef, characters; 1295 uint64_t valuePtr; 1296 1297 //check if the values exist! 1298 require_action_quiet(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordAllowedCharactersKey, &allowedTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need a string of characters; password must only contain characters in this string"), (CFIndex)errSecBadReq, NULL)); 1299 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMaxLengthKey, &maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL)); 1300 require_action_quiet( CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordMinLengthKey, &minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL)); 1301 require_action_quiet(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordRequiredCharactersKey, &requiredTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need an array of character sets, password must have at least 1 character from each set"), (CFIndex)errSecBadReq, NULL)); 1302 1303 //check if values are null? 1304 require_action_quiet(isNull(allowedTest) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need a string of characters; password must only contain characters in this string"), (CFIndex)errSecBadReq, NULL)); 1305 require_action_quiet(isNull(maxTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a max length"), (CFIndex)errSecBadReq, NULL)); 1306 require_action_quiet(isNull(minTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("To generate a password, need a min length"), (CFIndex)errSecBadReq, NULL)); 1307 require_action_quiet(isNull(requiredTest)!= true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need an array of character sets, password must have at least 1 character from each set"), (CFIndex)errSecBadReq, NULL)); 1308 1309 //check if the values are correct 1310 require_action_quiet(isString(allowedTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's allowed characters must be a CFStringRef"), (CFIndex)errSecBadReq, NULL)); 1311 require_action_quiet(isNumber(maxTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's max length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL)); 1312 require_action_quiet(isNumber(minTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's min length must be a CFNumberRef"), (CFIndex)errSecBadReq, NULL)); 1313 require_action_quiet(isArray(requiredTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's required characters must be an array of CFCharacterSetRefs"), (CFIndex)errSecBadReq, NULL)); 1314 1315 CFNumberGetValue(minTest, kCFNumberSInt64Type, &valuePtr); 1316 CFIndex minPasswordLength = (long)valuePtr; 1317 CFNumberGetValue(maxTest, kCFNumberSInt64Type, &valuePtr); 1318 CFIndex maxPasswordLength = (long)valuePtr; 1319 1320 require_action_quiet(minPasswordLength <= maxPasswordLength, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The password's length parameters make no sense ( is max < min ?)"), (CFIndex)errSecBadReq, NULL)); 1321 1322 require_action_quiet(CFStringGetLength((CFStringRef)allowedTest) != 0, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need a string of characters; password must only contain characters in this string"), (CFIndex)errSecBadReq, NULL)); 1323 require_action_quiet(CFArrayGetCount((CFArrayRef)requiredTest), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Need an array of character sets, password must have at least 1 character from each set"), (CFIndex)errSecBadReq, NULL)); 1324 1325 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDisallowedCharacters, &prohibitedCharacters)){ 1326 require_action_quiet(isNull(prohibitedCharacters) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Disallowed Characters dictionary parameter is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1327 require_action_quiet(isString(prohibitedCharacters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("Disallowed Characters dictionary parameter is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1328 } 1329 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordCantEndWithChars, &endWith)){ 1330 require_action_quiet(isNull(endWith) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'EndWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1331 require_action_quiet(isString(endWith), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'EndWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1332 } 1333 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordCantStartWithChars, &startWith)){ 1334 require_action_quiet(isNull(startWith) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'StartWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1335 require_action_quiet(isString(startWith), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'StartWith' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1336 } 1337 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordGroupSize, &groupSizeRef)){ 1338 require_action_quiet(isNull(groupSizeRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'groupsize' is either null or not a number"), (CFIndex)errSecBadReq, NULL)); 1339 require_action_quiet(isNumber(groupSizeRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'groupsize' is either null or not a number"), (CFIndex)errSecBadReq, NULL)); 1340 } 1341 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef)){ 1342 require_action_quiet(isNull(numberOfGroupsRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'number of groupds' is either null or not a number"), (CFIndex)errSecBadReq, NULL)); 1343 require_action_quiet(isNumber(numberOfGroupsRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'number of groupds' is either null or not a number"), (CFIndex)errSecBadReq, NULL)); 1344 } 1345 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordSeparator, &separatorRef)){ 1346 require_action_quiet(isNull(separatorRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'password separator character' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1347 require_action_quiet(isString(separatorRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'password separator character' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1348 } 1349 1350 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharactersRef)){ 1351 require_action_quiet(isNull(atMostCharactersRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Most N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1352 require_action_quiet(isDictionary(atMostCharactersRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Most N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1353 1354 require_action_quiet(CFDictionaryGetValueIfPresent(atMostCharactersRef, kSecPasswordCharacterCount, &thresholdRef) != false, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Most N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1355 require_action_quiet(isNull(thresholdRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL)); 1356 require_action_quiet(isNumber(thresholdRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL)); 1357 1358 require_action_quiet(CFDictionaryGetValueIfPresent(atMostCharactersRef, kSecPasswordCharacters, &characters)!= false, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Most N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1359 require_action_quiet(isNull(characters) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1360 require_action_quiet(isString(characters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1361 } 1362 1363 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharactersRef)){ 1364 require_action_quiet(isNull(atLeastCharactersRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Least N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1365 require_action_quiet(isDictionary(atLeastCharactersRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Least N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1366 1367 require_action_quiet(CFDictionaryGetValueIfPresent(atLeastCharactersRef, kSecPasswordCharacterCount, &thresholdRef) != false, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Least N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1368 1369 require_action_quiet(isNull(thresholdRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL)); 1370 require_action_quiet(isNumber(thresholdRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL)); 1371 1372 require_action_quiet(CFDictionaryGetValueIfPresent(atLeastCharactersRef, kSecPasswordCharacters, &characters) != false, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'At Least N Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1373 require_action_quiet(isNull(characters) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1374 require_action_quiet(isString(characters), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Characters' is either null or not a string"), (CFIndex)errSecBadReq, NULL)); 1375 } 1376 1377 if(CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef)){ 1378 require_action_quiet(isNull(identicalRef) != true, fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Identical Consecutive Characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL)); 1379 require_action_quiet(isNumber(identicalRef), fail, tempError = CFErrorCreate(kCFAllocatorDefault, CFSTR("The dictionary parameter 'Identical Consecutive Characters' is either null or not a number"), (CFIndex)errSecBadReq, NULL)); 1380 } 1381 } 1382 1383 fail: 1384 { 1385 bool result = true; 1386 if (tempError != NULL) { 1387 if (error) 1388 *error = CFRetainSafe(tempError); 1389 result = false; 1390 } 1391 1392 CFReleaseNull(tempError); 1393 return result; 1394 } 1395 } 1396 1397 static bool doesFinalPasswordPass(bool isSimple, CFStringRef password, CFDictionaryRef requirements){ 1398 1399 CFTypeRef characters, identicalRef = NULL, NRef = NULL, endWith= NULL, startWith= NULL, atLeastCharacters= NULL, atMostCharacters = NULL; 1400 uint64_t valuePtr; 1401 CFIndex N, identicalCount = 0; 1402 CFArrayRef requiredCharacterSet = (CFArrayRef)CFDictionaryGetValue(requirements, kSecRequiredCharacterSetsKey); 1403 1404 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantEndWithChars, &endWith)) 1405 endWith = NULL; 1406 1407 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordCantStartWithChars, &startWith)) 1408 startWith = NULL; 1409 1410 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsAtLeastNSpecificCharacters, &atLeastCharacters)) 1411 atLeastCharacters = NULL; 1412 1413 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNSpecificCharacters, &atMostCharacters)) 1414 atMostCharacters = NULL; 1415 1416 if(!CFDictionaryGetValueIfPresent(requirements, kSecPasswordContainsNoMoreThanNConsecutiveIdenticalCharacters, &identicalRef)) 1417 identicalRef = NULL; 1418 else{ 1419 CFNumberGetValue((CFNumberRef)identicalRef, kCFNumberSInt64Type, &valuePtr); 1420 identicalCount = (long)valuePtr; 1421 } 1422 if(endWith != NULL) 1423 { 1424 if(!doesPasswordEndWith(password, endWith)) 1425 return false; 1426 } 1427 if(startWith != NULL){ 1428 if(!doesPasswordStartWith(password, startWith)) 1429 return false; 1430 } 1431 if(atLeastCharacters != NULL){ 1432 NRef = CFDictionaryGetValue(atLeastCharacters, kSecPasswordCharacterCount); 1433 characters = CFDictionaryGetValue(atLeastCharacters, kSecPasswordCharacters); 1434 CFNumberGetValue((CFNumberRef)NRef, kCFNumberSInt64Type, &valuePtr); 1435 N = (long)valuePtr; 1436 if(!passwordContainsAtLeastNCharacters(password, characters, N)) 1437 return false; 1438 } 1439 if(atMostCharacters != NULL){ 1440 NRef = CFDictionaryGetValue(atMostCharacters, kSecPasswordCharacterCount); 1441 characters = CFDictionaryGetValue(atMostCharacters, kSecPasswordCharacters); 1442 CFNumberGetValue((CFNumberRef)NRef, kCFNumberSInt64Type, &valuePtr); 1443 N = (long)valuePtr; 1444 if(!passwordContainsLessThanNCharacters(password, characters, N)) 1445 return false; 1446 } 1447 if(identicalRef != NULL){ 1448 if(!passwordContainsLessThanNIdenticalCharacters(password, identicalCount)) 1449 return false; 1450 } 1451 if (!passwordContainsRequiredCharacters(password, requiredCharacterSet)) 1452 return false; 1453 1454 if(true == SecPasswordIsPasswordWeak2(isSimple, password)) 1455 return false; 1456 1457 return true; 1458 } 1459 1460 static CFStringRef 1461 CreateChecksum(SecPasswordType type, CFStringRef password, CFIndex length, CFStringRef allowedChars) 1462 { 1463 if (type != kSecPasswordTypeiCloudRecoveryKey) 1464 return NULL; 1465 1466 CFMutableStringRef checksum = NULL; 1467 uint8_t digest[CCSHA256_OUTPUT_SIZE]; 1468 if (length > (CFIndex)sizeof(digest)) 1469 return NULL; 1470 1471 CFDataRef data = CFStringCreateExternalRepresentation(SecCFAllocatorZeroize(), password, kCFStringEncodingUTF8, 0); 1472 if (data == NULL) 1473 return NULL; 1474 1475 ccdigest(ccsha256_di(), CFDataGetLength(data), CFDataGetBytePtr(data), digest); 1476 CFReleaseNull(data); 1477 1478 CFIndex allowedCharLength = CFStringGetLength(allowedChars); 1479 1480 checksum = CFStringCreateMutable(SecCFAllocatorZeroize(), 0); 1481 for (CFIndex n = 0; n < length; n++) { 1482 CFIndex selection = digest[n] % allowedCharLength; 1483 UniChar c = CFStringGetCharacterAtIndex(allowedChars, selection); 1484 CFStringAppendCharacters(checksum, &c, 1); 1485 } 1486 1487 return checksum; 1488 } 1489 1490 //entry point into password generation 1491 CF_RETURNS_RETAINED CFStringRef SecPasswordGenerate(SecPasswordType type, CFErrorRef *error, CFDictionaryRef passwordRequirements){ 1492 bool check = false, isSimple = false; 1493 CFTypeRef separator = NULL, defaults = NULL, groupSizeRef = NULL, numberOfGroupsRef = NULL; 1494 CFDictionaryRef properlyFormattedRequirements = NULL; 1495 CFErrorRef localError = NULL; 1496 uint64_t valuePtr, groupSize = 0, numberOfGroups, checksumChars = 0; 1497 CFNumberRef numberOfRequiredRandomCharacters, checksumCharacters; 1498 CFIndex requiredCharactersSize = 0; 1499 CFStringRef randomCharacters = NULL, password = NULL, allowedChars = NULL; 1500 CFMutableStringRef finalPassword = NULL; 1501 1502 if(type == kSecPasswordTypePIN) 1503 isSimple = true; 1504 else 1505 isSimple = false; 1506 check = isDictionaryFormattedProperly(type, passwordRequirements, &localError); 1507 require_quiet(check != false, fail); 1508 1509 //should we generate defaults? 1510 if(passwordRequirements == NULL || (CFDictionaryGetValueIfPresent(passwordRequirements, kSecPasswordDefaultForType, &defaults) && isString(defaults) == true && 0 == CFStringCompare(defaults, CFSTR("true"), 0) )) 1511 properlyFormattedRequirements = passwordGenerateCreateDefaultParametersDictionary(type, passwordRequirements); 1512 else 1513 properlyFormattedRequirements = passwordGenerationCreateParametersDictionary(type, passwordRequirements); 1514 1515 require_quiet(localError == NULL && properlyFormattedRequirements != NULL, fail); 1516 1517 numberOfRequiredRandomCharacters = (CFNumberRef)CFDictionaryGetValue(properlyFormattedRequirements, kSecNumberOfRequiredRandomCharactersKey); 1518 if (isNumber(numberOfRequiredRandomCharacters) && CFNumberGetValue(numberOfRequiredRandomCharacters, kCFNumberSInt64Type, &valuePtr)) 1519 requiredCharactersSize = (long)valuePtr; 1520 1521 checksumCharacters = (CFNumberRef)CFDictionaryGetValue(properlyFormattedRequirements, kSecNumberOfChecksumCharactersKey); 1522 if (isNumber(checksumCharacters) && CFNumberGetValue(checksumCharacters, kCFNumberSInt64Type, &valuePtr)) 1523 checksumChars = (long)valuePtr; 1524 1525 1526 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordGroupSize, &groupSizeRef)){ 1527 groupSizeRef = NULL; 1528 } 1529 else 1530 CFNumberGetValue((CFNumberRef)groupSizeRef, kCFNumberSInt64Type, &groupSize); 1531 1532 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordNumberOfGroups, &numberOfGroupsRef)){ 1533 numberOfGroupsRef = NULL; 1534 } 1535 else 1536 CFNumberGetValue((CFNumberRef)numberOfGroupsRef, kCFNumberSInt64Type, &numberOfGroups); 1537 1538 require(requiredCharactersSize, fail); 1539 1540 while (true) { 1541 allowedChars = CFDictionaryGetValue(properlyFormattedRequirements, kSecAllowedCharactersKey); 1542 require_noerr(getPasswordRandomCharacters(&randomCharacters, properlyFormattedRequirements, &requiredCharactersSize, allowedChars), fail); 1543 1544 if(numberOfGroupsRef && groupSizeRef){ 1545 finalPassword = CFStringCreateMutable(kCFAllocatorDefault, 0); 1546 1547 if(!CFDictionaryGetValueIfPresent(properlyFormattedRequirements, kSecPasswordSeparator, &separator)) 1548 separator = NULL; 1549 1550 if(separator == NULL) 1551 separator = CFSTR("-"); 1552 1553 CFIndex i = 0; 1554 while( i != requiredCharactersSize){ 1555 if((i + (CFIndex)groupSize) < requiredCharactersSize){ 1556 CFStringRef subString = CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, (CFIndex)groupSize)); 1557 CFStringAppend(finalPassword, subString); 1558 CFStringAppend(finalPassword, separator); 1559 CFReleaseSafe(subString); 1560 i+=groupSize; 1561 } 1562 else if((i+(CFIndex)groupSize) == requiredCharactersSize){ 1563 CFStringRef subString = CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, (CFIndex)groupSize)); 1564 CFStringAppend(finalPassword, subString); 1565 CFReleaseSafe(subString); 1566 i+=groupSize; 1567 } 1568 else { 1569 CFStringRef subString = CFStringCreateWithSubstring(kCFAllocatorDefault, randomCharacters, CFRangeMake(i, requiredCharactersSize - i)); 1570 CFStringAppend(finalPassword, subString); 1571 CFReleaseSafe(subString); 1572 i+=(requiredCharactersSize - i); 1573 } 1574 } 1575 if (checksumChars) { 1576 CFStringRef checksum = CreateChecksum(type, randomCharacters, (CFIndex)checksumChars, allowedChars); 1577 CFStringAppend(finalPassword, checksum); 1578 CFReleaseNull(checksum); 1579 } 1580 password = CFStringCreateCopy(kCFAllocatorDefault, finalPassword); 1581 CFReleaseNull(finalPassword); 1582 } 1583 //no fancy formatting 1584 else { 1585 password = CFStringCreateCopy(kCFAllocatorDefault, randomCharacters); 1586 } 1587 1588 CFReleaseNull(randomCharacters); 1589 require_quiet(doesFinalPasswordPass(isSimple, password, properlyFormattedRequirements), no_pass); 1590 CFReleaseNull(properlyFormattedRequirements); 1591 return password; 1592 1593 no_pass: 1594 CFReleaseNull(password); 1595 } 1596 1597 fail: 1598 if (error && localError) { 1599 *error = localError; 1600 localError = NULL; 1601 } 1602 1603 CFReleaseSafe(localError); 1604 CFReleaseNull(properlyFormattedRequirements); 1605 return NULL; 1606 } 1607 1608 const char *in_word_set (const char *str, unsigned int len){ 1609 static const char * wordlist[] = 1610 { 1611 "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 1612 "", "", "0103", "", "", "", "", "0123", "", "", "", "", "0303", "", "", "", 1613 "", "", "", "", "0110", "", "1103", "", "", "", "", "1123", "", "", "0000", 1614 "", "1203", "", "0404", "", "", "", "", "1234", "1110", "2015", "2013", "", 1615 "2014", "1010", "2005", "2003", "", "2004", "1210", "0505", "0111", "", "", 1616 "", "2008", "0101", "", "2007", "", "", "", "", "2006", "2010", "1995", "1993", 1617 "", "1994", "2000", "", "1111", "", "", "", "1998", "1101", "", "1997", "", 1618 "0808", "1211", "", "1996", "0102", "", "1201", "", "", "1990", "", "", "", 1619 "", "0202", "", "2011", "", "", "1112", "1958", "2001", "", "1957", "1102", 1620 "", "3333", "", "1956", "1212", "1985", "1983", "", "1984", "1202", "", "0909", 1621 "", "0606", "", "1988", "1991", "", "1987", "2012", "", "", "", "1986", "2002", 1622 "", "", "", "0707", "1980", "", "2009", "", "", "2222", "1965", "1963", "", 1623 "1964", "", "", "2229", "", "", "1992", "1968", "", "", "1967", "", "", "1999", 1624 "", "1966", "", "1975", "1973", "", "1974", "1960", "", "1981", "", "4444", 1625 "", "1978", "", "7465", "1977", "", "", "", "", "1976", "2580", "", "1959", 1626 "", "", "1970", "", "", "", "", "", "", "", "", "", "1982", "", "1961", "", 1627 "", "5252", "", "1989", "", "", "", "", "", "", "", "", "", "", "", "", "", 1628 "", "1971", "", "", "", "", "", "", "", "1962", "", "5683", "", "6666", "", 1629 "", "1969", "", "", "", "", "", "", "", "", "", "", "", "", "1972", "", "", 1630 "", "", "", "", "1979", "", "", "", "7667" 1631 }; 1632 1633 if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) 1634 { 1635 register int key = pinhash (str, len); 1636 1637 if (key <= MAX_HASH_VALUE && key >= 0) 1638 { 1639 register const char *s = wordlist[key]; 1640 if (*str == *s && !strcmp (str + 1, s + 1)) 1641 return s; 1642 } 1643 } 1644 return 0; 1645 } 1646 CFDictionaryRef SecPasswordCopyDefaultPasswordLength(SecPasswordType type, CFErrorRef *error){ 1647 1648 CFIndex tupleLengthInt = 0, numOfTuplesInt = 0; 1649 CFNumberRef tupleLength = NULL; 1650 CFNumberRef numOfTuples = NULL; 1651 1652 CFMutableDictionaryRef passwordLengthDefaults = NULL; 1653 CFDictionaryRef result = NULL; 1654 1655 switch(type){ 1656 case(kSecPasswordTypeiCloudRecoveryKey): 1657 tupleLengthInt = 4; 1658 numOfTuplesInt = 7; 1659 break; 1660 1661 case(kSecPasswordTypeiCloudRecovery): 1662 tupleLengthInt = 4; 1663 numOfTuplesInt = 6; 1664 break; 1665 1666 case(kSecPasswordTypePIN): 1667 tupleLengthInt = 4; 1668 numOfTuplesInt = 1; 1669 break; 1670 1671 case(kSecPasswordTypeSafari): 1672 tupleLengthInt = 4; 1673 numOfTuplesInt = 5; 1674 break; 1675 1676 case(kSecPasswordTypeWifi): 1677 tupleLengthInt = 4; 1678 numOfTuplesInt = 3; 1679 break; 1680 1681 default: 1682 if(SecError(errSecBadReq, error, CFSTR("Password type does not exist.")) == false) 1683 { 1684 secdebug("secpasswordcopydefaultpasswordlength", "could not create error!"); 1685 } 1686 } 1687 1688 if (tupleLengthInt != 0 && numOfTuplesInt != 0) { 1689 tupleLength = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &tupleLengthInt); 1690 numOfTuples = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &numOfTuplesInt); 1691 passwordLengthDefaults = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 1692 CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordGroupSize, tupleLength); 1693 CFDictionaryAddValue(passwordLengthDefaults, kSecPasswordNumberOfGroups, numOfTuples); 1694 result = CFDictionaryCreateCopy(kCFAllocatorDefault, passwordLengthDefaults); 1695 } 1696 1697 CFReleaseSafe(tupleLength); 1698 CFReleaseSafe(numOfTuples); 1699 CFReleaseSafe(passwordLengthDefaults); 1700 return result; 1701 } 1702 1703 bool 1704 SecPasswordValidatePasswordFormat(SecPasswordType type, CFStringRef password, CFErrorRef *error) 1705 { 1706 CFIndex tupleLengthInt = 0, numOfTuplesInt = 0, checkSumChars = 0; 1707 CFStringRef checksum = NULL, madeChecksum = NULL, passwordNoChecksum = NULL; 1708 CFMutableStringRef randomChars = NULL; 1709 CFStringRef allowedChars = NULL; 1710 bool res = false; 1711 1712 switch (type) { 1713 case kSecPasswordTypeiCloudRecoveryKey: 1714 tupleLengthInt = 4; 1715 numOfTuplesInt = 7; 1716 checkSumChars = 2; 1717 allowedChars = defaultiCloudCharacters; 1718 break; 1719 case kSecPasswordTypeiCloudRecovery: 1720 tupleLengthInt = 4; 1721 numOfTuplesInt = 6; 1722 break; 1723 case(kSecPasswordTypePIN): 1724 tupleLengthInt = 4; 1725 numOfTuplesInt = 1; 1726 break; 1727 default: 1728 SecError(errSecBadReq, error, CFSTR("Password type does not exist.")); 1729 return false; 1730 } 1731 1732 1733 if (numOfTuplesInt < 1) 1734 return false; 1735 if (checkSumChars > tupleLengthInt) 1736 return false; 1737 1738 CFIndex numberOfChars = numOfTuplesInt * tupleLengthInt + (numOfTuplesInt - 1); 1739 1740 /* 1741 * First check expected length 1742 */ 1743 1744 require(CFStringGetLength(password) == numberOfChars, out); /* N groups of M with (N-1) seperator - in-between */ 1745 1746 randomChars = CFStringCreateMutable(SecCFAllocatorZeroize(), 0); 1747 require(randomChars, out); 1748 1749 /* 1750 * make sure dash-es are at the expected spots 1751 */ 1752 1753 for (CFIndex n = 0; n < numOfTuplesInt; n++) { 1754 if (n != 0) { 1755 UniChar c = CFStringGetCharacterAtIndex(password, (n * (tupleLengthInt + 1)) - 1); 1756 require(c == '-', out); 1757 } 1758 CFStringRef substr = CFStringCreateWithSubstring(SecCFAllocatorZeroize(), password, CFRangeMake(n * (tupleLengthInt + 1), tupleLengthInt)); 1759 CFStringAppend(randomChars, substr); 1760 CFReleaseNull(substr); 1761 } 1762 1763 if (checkSumChars) { 1764 /* 1765 * Pull apart and password and checksum 1766 */ 1767 1768 checksum = CFStringCreateWithSubstring(SecCFAllocatorZeroize(), randomChars, CFRangeMake((numOfTuplesInt * tupleLengthInt) - checkSumChars, checkSumChars)); 1769 require(checksum, out); 1770 1771 passwordNoChecksum = CFStringCreateWithSubstring(SecCFAllocatorZeroize(), randomChars, CFRangeMake(0, (numOfTuplesInt * tupleLengthInt) - checkSumChars)); 1772 require(passwordNoChecksum, out); 1773 1774 /* 1775 * Validate checksum 1776 */ 1777 1778 madeChecksum = CreateChecksum(type, passwordNoChecksum, checkSumChars, allowedChars); 1779 require(madeChecksum, out); 1780 1781 require(CFEqual(madeChecksum, checksum), out); 1782 } 1783 1784 res = true; 1785 out: 1786 CFReleaseNull(randomChars); 1787 CFReleaseNull(madeChecksum); 1788 CFReleaseNull(checksum); 1789 CFReleaseNull(passwordNoChecksum); 1790 1791 return res; 1792 }