/ OSX / sec / Security / SecPasswordGenerate.c
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  }