1: <?php
2: /**
3: * Pry Framework
4: *
5: * LICENSE
6: *
7: * This source file is subject to the new BSD license that is bundled
8: * with this package in the file LICENSE.txt.
9: *
10: * @version $Revision: 276 $
11: */
12:
13: /**
14: * Classe implémentant l'algorithm bcrypt pour le cryptage de mot de passe.
15: * <code>
16: * $bcrypt = new Auth_Bcrypt(11);
17: * $hash = $bcrypt->hash('mypass');
18: * var_dump($bcrypt->check('mypass',$hash);
19: * </code>
20: * @package Auth
21: * @version 1.0
22: * @author Olivier ROGER <oroger.fr>
23: * @see http://en.wikipedia.org/wiki/Bcrypt
24: * @see http://stackoverflow.com/questions/4795385/how-do-you-use-bcrypt-for-hashing-passwords-in-php
25: */
26: class Auth_Bcrypt
27: {
28: /** Base du grain de sel blowfish*/
29: const SALTBASE = '$2a$';
30:
31: /** Logarithme base 2 du compteur d'itération*/
32: private $iterations;
33:
34: /** Aléa pour le grain de sel */
35: private $random;
36:
37: /**
38: * Initialise le cryptage
39: * @param int $rounds Nombre d'itération pour l'algo de hashage entre 4 et 31.
40: * Plus ce paramètre est élevé plus le temps de hashage sera long. 12 par défaut
41: * @throws RuntimeException Si le cryptage BlowFish n'est pas supporté sur l'installation
42: * @throws InvalidArgumentException Le paramètres n'est pas compris entre 4 et 31.
43: */
44: public function __construct($rounds = 12)
45: {
46: if(CRYPT_BLOWFISH != 1 )
47: throw new RuntimeException ('Blowfish is not available on your system, it is required for bcrypt');
48:
49: if($rounds < 4 || $rounds > 31)
50: throw new InvalidArgumentException (' The number of rounds have to be between 4 and 31');
51:
52: $this->iterations = $rounds;
53: $this->random = microtime();
54:
55: if(function_exists('getmypid'))
56: $this->random .= getmypid();
57: }
58:
59: /**
60: * Hash la chaine donnée
61: * @param string $str Chaine en clair
62: * @return string Un hash de la chaine de 60 caractères
63: * @throws type
64: */
65: public function hash($str)
66: {
67: $hash = crypt($str,$this->generateSalt());
68: if(strlen($hash) != 60)
69: throw LengthException('Hash is '.strlen($hash).' characters long , somethng went wrong');
70:
71: return $hash;
72: }
73:
74: /**
75: * Vérifie une chaine avec un hash
76: * @param string $str Chaine en claire
77: * @param string $hashedStr Un hash
78: * @return boolean True si les deux correspondent , false sinon
79: */
80: public static function check($str,$hashedStr)
81: {
82: $hash = crypt($str,$hashedStr);
83: return $hash === $hashedStr;
84: }
85:
86: /**
87: * Génération d'un grain de sel pour le hashage
88: * @return string
89: */
90: private function generateSalt()
91: {
92: // Salt se forme aec : $2a$/2 digits/$/22 charactères parmis [./0-9a-Z]
93: $salt = Auth_Bcrypt::SALTBASE.$this->iterations.'$';
94: $bytes = $this->getRandomBytes(16);
95: $salt .= $this->getBlowfishSalt($bytes);
96:
97: return $salt;
98: }
99:
100: /**
101: * Génération de bytes aléatoires
102: * @param int $size Nombre de byte à générer
103: * @return bytes
104: */
105: private function getRandomBytes($size)
106: {
107: $bytes = '';
108:
109: // /dev/urandom est la meilleur source d'aléa , on test sa disponibilité :
110: if(is_readable('/dev/urandom'))
111: {
112: $handle = @fopen('/dev/urandom','rb');
113: if($handle) {
114: $bytes = fread($handle, $size);
115: }
116: }
117:
118: // Si pas de dev/urandom on essai avec openssl
119: if($bytes === '' && function_exists('openssl_random_pseudo_bytes'))
120: $bytes = openssl_random_pseudo_bytes($size);
121:
122: //Si aucun des procédés dispo ou si la génération n'as pas la bonne taille
123: if(strlen($bytes) < $size )
124: {
125: $bytes = '';
126:
127: $this->random = md5(microtime() . $this->random);
128: $bytes .= md5($this->random, true); // Retour en byte et non string
129:
130: $bytes = substr($bytes,0,$size);
131: }
132:
133: return $bytes;
134: }
135:
136: /**
137: * Transformation des bytes générés.
138: * Code original par phpass
139: * @see http://www.openwall.com/phpass/
140: * @param rawbytes $input
141: * @return string
142: */
143: private function getBlowfishSalt($input)
144: {
145: $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
146: $output = '';
147: $i = 0;
148: do {
149: $c1 = ord($input[$i++]);
150: $output .= $itoa64[$c1 >> 2];
151: $c1 = ($c1 & 0x03) << 4;
152: if ($i >= 16) {
153: $output .= $itoa64[$c1];
154: break;
155: }
156:
157: $c2 = ord($input[$i++]);
158: $c1 |= $c2 >> 4;
159: $output .= $itoa64[$c1];
160: $c1 = ($c2 & 0x0f) << 2;
161:
162: $c2 = ord($input[$i++]);
163: $c1 |= $c2 >> 6;
164: $output .= $itoa64[$c1];
165: $output .= $itoa64[$c2 & 0x3f];
166: } while (1);
167:
168: return $output;
169: }
170: }
171:
172: ?>
173: