File indexing completed on 2024-12-22 05:37:18
0001 <?php 0002 0003 /** 0004 * Port of the famous GNU/Linux Password Generator ("pwgen") to PHP. 0005 * This file may be distributed under the terms of the GNU Public License. 0006 * Copyright (C) 2001, 2002 by Theodore Ts'o <tytso@alum.mit.edu> 0007 * Copyright (C) 2009 by Superwayne <superwayne@superwayne.org> 0008 */ 0009 class PWGen 0010 { 0011 // Flags for the pwgen function 0012 const PW_DIGITS = 0x0001; 0013 const PW_UPPERS = 0x0002; // At least one upper letter 0014 const PW_SYMBOLS = 0x0004; 0015 const PW_AMBIGUOUS = 0x0008; 0016 0017 // Flags for the pwgen element 0018 const CONSONANT = 0x0001; 0019 const VOWEL = 0x0002; 0020 const DIPHTHONG = 0x0004; 0021 const NOT_FIRST = 0x0008; 0022 const NO_VOWELS = 0x0010; 0023 0024 private $pwgen; 0025 private $pwgen_flags; 0026 private $password; 0027 private $pw_length; 0028 0029 private static $initialized = false; // static block alread called? 0030 private static $elements; 0031 private static $pw_ambiguous; 0032 private static $pw_symbols; 0033 private static $pw_digits; 0034 private static $pw_uppers; 0035 private static $pw_lowers; 0036 private static $pw_vowels; 0037 0038 /** 0039 * @param int $length Length $length of the generated password. Default: 8 0040 * @param bool $secure Generate $secure completely random, hard-to-memorize passwords. These should only 0041 * be used for machine passwords, since otherwise it's almost guaranteed that 0042 * users will simply write the password on a piece of paper taped to the monitor... 0043 * @param bool $numerals Include $numerals at least one number in the password. This is the default. 0044 * @param bool $capitalize Include $capitalize at least one capital letter in the password. This is the default. 0045 * @param bool $ambiguous Don $ambiguous 't use characters that could be confused by the user when printed, 0046 * such as 'l' and '1', or '0' or 'O'. This reduces the number of possible 0047 * passwords significantly, and as such reduces the quality of the passwords. 0048 * It may be useful for users who have bad vision, but in general use of this 0049 * option is not recommended. 0050 * @param bool $no_vovels $no -vowels Generate random passwords that do not contain vowels or numbers that might be 0051 * mistaken for vowels. It provides less secure passwords to allow system 0052 * administrators to not have to worry with random passwords accidentally contain 0053 * offensive substrings. 0054 * @param bool $symbols Include $symbols at least one special character in the password. 0055 */ 0056 public function __construct($length = 8, $secure = false, $numerals = true, $capitalize = true, 0057 $ambiguous = false, $no_vovels = false, $symbols = false) 0058 { 0059 self::__static(); 0060 0061 $this->pwgen = 'pw_phonemes'; 0062 0063 $this->setLength($length); 0064 $this->setSecure($secure); 0065 $this->setNumerals($numerals); 0066 $this->setCapitalize($capitalize); 0067 $this->setAmbiguous($ambiguous); 0068 $this->setNoVovels($no_vovels); 0069 $this->setSymbols($symbols); 0070 } 0071 0072 /** 0073 * Length of the generated password. Default: 8 0074 */ 0075 public function setLength($length) 0076 { 0077 if (is_numeric($length) && $length > 0) { 0078 $this->pw_length = $length; 0079 if ($this->pw_length < 5) { 0080 $this->pwgen = 'pw_rand'; 0081 } 0082 if ($this->pw_length <= 2) { 0083 $this->setCapitalize(false); 0084 } 0085 if ($this->pw_length <= 1) { 0086 $this->setNumerals(false); 0087 } 0088 } else { 0089 $this->pw_length = 8; 0090 } 0091 return $this; 0092 } 0093 0094 /** 0095 * Generate completely random, hard-to-memorize passwords. These should only used for machine passwords, 0096 * since otherwise it's almost guaranteed that users will simply write the password on a piece of paper 0097 * taped to the monitor... 0098 * Please note that this function implies that you want passwords which include symbols, numerals and 0099 * capital letters. 0100 */ 0101 public function setSecure($secure) 0102 { 0103 if ($secure) { 0104 $this->pwgen = 'pw_rand'; 0105 $this->setNumerals(true); 0106 $this->setCapitalize(true); 0107 } else { 0108 $this->pwgen = 'pw_phonemes'; 0109 } 0110 return $this; 0111 } 0112 0113 /** 0114 * Include at least one number in the password. This is the default. 0115 */ 0116 public function setNumerals($numerals) 0117 { 0118 if ($numerals) { 0119 $this->pwgen_flags |= self::PW_DIGITS; 0120 } else { 0121 $this->pwgen_flags &= ~self::PW_DIGITS; 0122 } 0123 return $this; 0124 } 0125 0126 /** 0127 * Include at least one capital letter in the password. This is the default. 0128 */ 0129 public function setCapitalize($capitalize) 0130 { 0131 if ($capitalize) { 0132 $this->pwgen_flags |= self::PW_UPPERS; 0133 } else { 0134 $this->pwgen_flags &= ~self::PW_UPPERS; 0135 } 0136 return $this; 0137 } 0138 0139 /** 0140 * Don't use characters that could be confused by the user when printed, such as 'l' and '1', or '0' or 0141 * 'O'. This reduces the number of possible passwords significantly, and as such reduces the quality of 0142 * the passwords. It may be useful for users who have bad vision, but in general use of this option is 0143 * not recommended. 0144 */ 0145 public function setAmbiguous($ambiguous) 0146 { 0147 if ($ambiguous) { 0148 $this->pwgen_flags |= self::PW_AMBIGUOUS; 0149 } else { 0150 $this->pwgen_flags &= ~self::PW_AMBIGUOUS; 0151 } 0152 return $this; 0153 } 0154 0155 /** 0156 * Generate random passwords that do not contain vowels or numbers that might be mistaken for vowels. It 0157 * provides less secure passwords to allow system administrators to not have to worry with random 0158 * passwords accidentally contain offensive substrings. 0159 */ 0160 public function setNoVovels($no_vovels) 0161 { 0162 if ($no_vovels) { 0163 $this->pwgen = 'pw_rand'; 0164 $this->pwgen_flags |= self::NO_VOWELS | self::PW_DIGITS | self::PW_UPPERS; 0165 } else { 0166 $this->pwgen = 'pw_phonemes'; 0167 $this->pwgen_flags &= ~self::NO_VOWELS; 0168 } 0169 return $this; 0170 } 0171 0172 public function setSymbols($symbols) 0173 { 0174 if ($symbols) { 0175 $this->pwgen_flags |= self::PW_SYMBOLS; 0176 } else { 0177 $this->pwgen_flags &= ~self::PW_SYMBOLS; 0178 } 0179 return $this; 0180 } 0181 0182 /** 0183 * @return mixed 0184 */ 0185 public function generate() 0186 { 0187 if ($this->pwgen == 'pw_phonemes') { 0188 $this->pw_phonemes(); 0189 } else { // $this->pwgen == 'pw_rand' 0190 $this->pw_rand(); 0191 } 0192 return $this->password; 0193 } 0194 0195 private function pw_phonemes() 0196 { 0197 $this->password = array(); 0198 0199 do { 0200 $feature_flags = $this->pwgen_flags; 0201 $c = 0; 0202 $prev = 0; 0203 $should_be = self::my_rand(0, 1) ? self::VOWEL : self::CONSONANT; 0204 $first = 1; 0205 0206 while ($c < $this->pw_length) { 0207 $i = self::my_rand(0, count(self::$elements) - 1); 0208 $str = self::$elements[$i]->str; 0209 $len = strlen($str); 0210 $flags = self::$elements[$i]->flags; 0211 0212 // Filter on the basic type of the next element 0213 if (($flags & $should_be) == 0) 0214 continue; 0215 // Handle the NOT_FIRST flag 0216 if ($first && ($flags & self::NOT_FIRST)) 0217 continue; 0218 // Don't allow VOWEL followed a Vowel/Dipthong pair 0219 if (($prev & self::VOWEL) && ($flags & self::VOWEL) && 0220 ($flags & self::DIPHTHONG) 0221 ) 0222 continue; 0223 // Don't allow us to overflow the buffer 0224 if ($len > $this->pw_length - $c) 0225 continue; 0226 0227 // Handle the AMBIGUOUS flag 0228 if ($this->pwgen_flags & self::PW_AMBIGUOUS) { 0229 if (strpbrk($str, self::$pw_ambiguous) !== false) 0230 continue; 0231 } 0232 0233 /* 0234 * OK, we found an element which matches our criteria, 0235 * let's do it! 0236 */ 0237 for ($j = 0; $j < $len; $j++) 0238 $this->password[$c + $j] = $str[$j]; 0239 0240 // Handle PW_UPPERS 0241 if ($this->pwgen_flags & self::PW_UPPERS) { 0242 if (($first || $flags & self::CONSONANT) && (self::my_rand(0, 9) < 2)) { 0243 $this->password[$c] = strtoupper($this->password[$c]); 0244 $feature_flags &= ~self::PW_UPPERS; 0245 } 0246 } 0247 0248 $c += $len; 0249 0250 // Time to stop? 0251 if ($c >= $this->pw_length) 0252 break; 0253 0254 // Handle PW_DIGITS 0255 if ($this->pwgen_flags & self::PW_DIGITS) { 0256 if (!$first && (self::my_rand(0, 9) < 3)) { 0257 do { 0258 $ch = strval(self::my_rand(0, 9)); 0259 } while (($this->pwgen_flags & self::PW_AMBIGUOUS) && 0260 strpos(self::$pw_ambiguous, $ch) !== false); 0261 $this->password[$c++] = $ch; 0262 $feature_flags &= ~self::PW_DIGITS; 0263 0264 $first = 1; 0265 $prev = 0; 0266 $should_be = self::my_rand(0, 1) ? self::VOWEL : self::CONSONANT; 0267 continue; 0268 } 0269 } 0270 0271 // Handle PW_SYMBOLS 0272 if ($this->pwgen_flags & self::PW_SYMBOLS) { 0273 if (!$first && (self::my_rand(0, 9) < 2)) { 0274 do { 0275 $ch = self::$pw_symbols[self::my_rand(0, 0276 strlen(self::$pw_symbols) - 1)]; 0277 } while (($this->pwgen_flags & self::PW_AMBIGUOUS) && 0278 strpos(self::$pw_ambiguous, $ch) !== false); 0279 $this->password[$c++] = $ch; 0280 $feature_flags &= ~self::PW_SYMBOLS; 0281 } 0282 } 0283 0284 // OK, figure out what the next element should be 0285 if ($should_be == self::CONSONANT) { 0286 $should_be = self::VOWEL; 0287 } else { // should_be == VOWEL 0288 if (($prev & self::VOWEL) || ($flags & self::DIPHTHONG) || 0289 (self::my_rand(0, 9) > 3) 0290 ) 0291 $should_be = self::CONSONANT; 0292 else 0293 $should_be = self::VOWEL; 0294 } 0295 $prev = $flags; 0296 $first = 0; 0297 } 0298 } while ($feature_flags & (self::PW_UPPERS | self::PW_DIGITS | self::PW_SYMBOLS)); 0299 0300 $this->password = implode('', $this->password); 0301 } 0302 0303 private function pw_rand() 0304 { 0305 $this->password = array(); 0306 0307 $chars = ''; 0308 if ($this->pwgen_flags & self::PW_DIGITS) { 0309 $chars .= self::$pw_digits; 0310 } 0311 if ($this->pwgen_flags & self::PW_UPPERS) { 0312 $chars .= self::$pw_uppers; 0313 } 0314 $chars .= self::$pw_lowers; 0315 if ($this->pwgen_flags & self::PW_SYMBOLS) { 0316 $chars .= self::$pw_symbols; 0317 } 0318 0319 do { 0320 $len = strlen($chars); 0321 $feature_flags = $this->pwgen_flags; 0322 $i = 0; 0323 0324 while ($i < $this->pw_length) { 0325 $ch = $chars[self::my_rand(0, $len - 1)]; 0326 if (($this->pwgen_flags & self::PW_AMBIGUOUS) && 0327 strpos(self::$pw_ambiguous, $ch) !== false 0328 ) 0329 continue; 0330 if (($this->pwgen_flags & self::NO_VOWELS) && 0331 strpos(self::$pw_vowels, $ch) !== false 0332 ) 0333 continue; 0334 $this->password[$i++] = $ch; 0335 if (strpos(self::$pw_digits, $ch) !== false) 0336 $feature_flags &= ~self::PW_DIGITS; 0337 if (strpos(self::$pw_uppers, $ch) !== false) 0338 $feature_flags &= ~self::PW_UPPERS; 0339 if (strchr(self::$pw_symbols, $ch) !== false) 0340 $feature_flags &= ~self::PW_SYMBOLS; 0341 } 0342 } while ($feature_flags & (self::PW_UPPERS | self::PW_DIGITS | self::PW_SYMBOLS)); 0343 0344 $this->password = implode('', $this->password); 0345 } 0346 0347 /** 0348 * Generate a random number n, where $min <= n < $max 0349 * Mersenne Twister is used as an algorithm 0350 */ 0351 public static function my_rand($min = 0, $max = 0) 0352 { 0353 return mt_rand($min, $max); 0354 } 0355 0356 /** 0357 * This method initializes all static vars which contain complex datatypes. 0358 * It acts somewhat like a static block in Java. Since PHP does not support this principle, the method 0359 * is called from the constructor. Because of that you can not access the static vars unless there 0360 * exists at least one object of the class. 0361 */ 0362 private static function __static() 0363 { 0364 if (!self::$initialized) { 0365 self::$initialized = true; 0366 self::$elements = array( 0367 new PWElement('a', self::VOWEL), 0368 new PWElement('ae', self::VOWEL | self::DIPHTHONG), 0369 new PWElement('ah', self::VOWEL | self::DIPHTHONG), 0370 new PWElement('ai', self::VOWEL | self::DIPHTHONG), 0371 new PWElement('b', self::CONSONANT), 0372 new PWElement('c', self::CONSONANT), 0373 new PWElement('ch', self::CONSONANT | self::DIPHTHONG), 0374 new PWElement('d', self::CONSONANT), 0375 new PWElement('e', self::VOWEL), 0376 new PWElement('ee', self::VOWEL | self::DIPHTHONG), 0377 new PWElement('ei', self::VOWEL | self::DIPHTHONG), 0378 new PWElement('f', self::CONSONANT), 0379 new PWElement('g', self::CONSONANT), 0380 new PWElement('gh', self::CONSONANT | self::DIPHTHONG | self::NOT_FIRST), 0381 new PWElement('h', self::CONSONANT), 0382 new PWElement('i', self::VOWEL), 0383 new PWElement('ie', self::VOWEL | self::DIPHTHONG), 0384 new PWElement('j', self::CONSONANT), 0385 new PWElement('k', self::CONSONANT), 0386 new PWElement('l', self::CONSONANT), 0387 new PWElement('m', self::CONSONANT), 0388 new PWElement('n', self::CONSONANT), 0389 new PWElement('ng', self::CONSONANT | self::DIPHTHONG | self::NOT_FIRST), 0390 new PWElement('o', self::VOWEL), 0391 new PWElement('oh', self::VOWEL | self::DIPHTHONG), 0392 new PWElement('oo', self::VOWEL | self::DIPHTHONG), 0393 new PWElement('p', self::CONSONANT), 0394 new PWElement('ph', self::CONSONANT | self::DIPHTHONG), 0395 new PWElement('qu', self::CONSONANT | self::DIPHTHONG), 0396 new PWElement('r', self::CONSONANT), 0397 new PWElement('s', self::CONSONANT), 0398 new PWElement('sh', self::CONSONANT | self::DIPHTHONG), 0399 new PWElement('t', self::CONSONANT), 0400 new PWElement('th', self::CONSONANT | self::DIPHTHONG), 0401 new PWElement('u', self::VOWEL), 0402 new PWElement('v', self::CONSONANT), 0403 new PWElement('w', self::CONSONANT), 0404 new PWElement('x', self::CONSONANT), 0405 new PWElement('y', self::CONSONANT), 0406 new PWElement('z', self::CONSONANT) 0407 ); 0408 self::$pw_ambiguous = 'B8G6I1l0OQDS5Z2'; 0409 self::$pw_symbols = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; 0410 self::$pw_digits = '0123456789'; 0411 self::$pw_uppers = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; 0412 self::$pw_lowers = 'abcdefghijklmnopqrstuvwxyz'; 0413 self::$pw_vowels = '01aeiouyAEIOUY'; 0414 } 0415 } 0416 0417 /** 0418 * Returns the last generated password. If there is none, a new one will be generated. 0419 */ 0420 public function __toString() 0421 { 0422 return (empty($this->password) ? $this->generate() : $this->password); 0423 } 0424 0425 } 0426 0427 class PWElement 0428 { 0429 public $str; 0430 public $flags; 0431 0432 public function __construct($str, $flags) 0433 { 0434 $this->str = $str; 0435 $this->flags = $flags; 0436 } 0437 }