Now in own repository for remote checkouts
[core.git] / inc / classes / main / crypto / class_CryptoHelper.php
1 <?php
2 /**
3  * A helper class for cryptographical things like hashing passwords and so on
4  *
5  * @author              Roland Haeder <webmaster@ship-simu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, this is free software
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.ship-simu.org
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program. If not, see <http://www.gnu.org/licenses/>.
23  */
24 class CryptoHelper extends BaseFrameworkSystem implements Cryptable {
25         // Exception constants
26         const EXCEPTION_ENCRYPT_MISSING = 0x1f0;
27         const EXCEPTION_ENCRYPT_INVALID = 0x1f1;
28
29         /**
30          * An instance of this own clas
31          */
32         private static $selfInstance = null;
33
34         /**
35          * Instance of the random number generator
36          */
37         private $rngInstance = null;
38
39         /**
40          * Salt for hashing operations
41          */
42         private $salt = "";
43
44         /**
45          * Protected constructor
46          *
47          * @return      void
48          */
49         protected function __construct () {
50                 // Call parent constructor
51                 parent::__construct(__CLASS__);
52
53                 // Clean up a little
54                 $this->removeNumberFormaters();
55                 $this->removeSystemArray();
56         }
57
58         /**
59          * Creates an instance of this class
60          *
61          * @return      $cryptoInstance         An instance of this crypto helper class
62          */
63         public final static function createCryptoHelper () {
64                 // Get a new instance
65                 $cryptoInstance = new CryptoHelper();
66
67                 // Initialize the hasher
68                 $cryptoInstance->initHasher();
69
70                 // Return the instance
71                 return $cryptoInstance;
72         }
73
74         /**
75          * Get a singleton instance of this class
76          *
77          * @return      $selfInstance   An instance of this crypto helper class
78          */
79         public final static function getInstance () {
80                 // Is no instance there?
81                 if (is_null(self::$selfInstance)) {
82                         // Then get a new one
83                         self::$selfInstance = self::createCryptoHelper();
84                 }
85
86                 // Return the instance
87                 return self::$selfInstance;
88         }
89
90         /**
91          * Initializes the hasher for different purposes.
92          *
93          * @return      void
94          */
95         protected function initHasher () {
96                 // Initialize the random number generator which is required by some crypto methods
97                 $this->rngInstance = ObjectFactory::createObjectByConfiguredName('rng_class');
98
99                 // Generate a salt for the hasher
100                 $this->generateSalt();
101         }
102
103         /**
104          * Generates the salt based on configured length
105          *
106          * @return      void
107          */
108         private function generateSalt () {
109                 // Get a random string from the RNG
110                 $randomString = $this->rngInstance->randomString();
111
112                 // Get config entry for salt length
113                 $length = $this->getConfigInstance()->readConfig('salt_length');
114
115                 // Keep only defined number of characters
116                 $this->salt = substr(sha1($randomString), -$length, $length);
117         }
118
119         /**
120          * Hashes a string with salt and returns the hash. If an old previous hash
121          * is supplied the method will use the first X chars of that hash for hashing
122          * the password. This is useful if you want to check if password is identical
123          * for authorization purposes.
124          *
125          * @param       $str            Unhashed string
126          * @param       $oldHash        A hash from previous hashed string
127          * @return      $hashed         The hashed and salted string
128          */
129         public function hashString ($str, $oldHash = "") {
130                 // Cast the string
131                 $str = (string) $str;
132
133                 // Default is the default salt ;-)
134                 $salt = $this->salt;
135
136                 // Is the old password set?
137                 if (!empty($oldHash)) {
138                         // Use the salt from hash, first get length
139                         $length = $this->getConfigInstance()->readConfig('salt_length');
140
141                         // Then extract the X first characters from the hash as our salt
142                         $salt = substr($oldHash, 0, $length);
143                 } // END - if
144
145                 // Hash the password with salt
146                 //* DEBUG: */ echo "salt=".$salt."/plain=".$str."<br />\n";
147                 $hashed = $salt . md5(sprintf($this->getConfigInstance()->readConfig('hash_mask'),
148                         $salt,
149                         $this->rngInstance->getFixedSalt(),
150                         $str
151                 ));
152
153                 // And return it
154                 return $hashed;
155         }
156
157         /**
158          * Encrypt the string with fixed salt
159          *
160          * @param       $str            The unencrypted string
161          * @return      $encrypted      Encrypted string
162          */
163         public function encryptString ($str) {
164                 // Init crypto module
165                 $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
166                 $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
167
168                 // Get key
169                 if ($this->getConfigInstance()->readConfig('crypt_fixed_salt') === "Y") {
170                         $key = md5($this->rngInstance->getFixedSalt());
171                 } else {
172                         $key = md5($this->rngInstance->getExtraSalt());
173                 }
174
175                 // Add some "garbage" to the string
176                 switch ($this->rngInstance->randomNumber(0, 8)) {
177                         case 0:
178                                 $garbageString = crc32($this->rngInstance->randomString(10))."|".base64_encode($str)."|".crc32($this->rngInstance->randomString(20));
179                                 break;
180
181                         case 1:
182                                 $garbageString = crc32($this->rngInstance->randomString(10))."|".base64_encode($str)."|".md5($this->rngInstance->randomString(20));
183                                 break;
184
185                         case 2:
186                                 $garbageString = crc32($this->rngInstance->randomString(10))."|".base64_encode($str)."|".sha1($this->rngInstance->randomString(20));
187                                 break;
188
189                         case 3:
190                                 $garbageString = md5($this->rngInstance->randomString(10))."|".base64_encode($str)."|".crc32($this->rngInstance->randomString(20));
191                                 break;
192
193                         case 4:
194                                 $garbageString = md5($this->rngInstance->randomString(10))."|".base64_encode($str)."|".md5($this->rngInstance->randomString(20));
195                                 break;
196
197                         case 5:
198                                 $garbageString = md5($this->rngInstance->randomString(10))."|".base64_encode($str)."|".sha1($this->rngInstance->randomString(20));
199                                 break;
200
201                         case 6:
202                                 $garbageString = sha1($this->rngInstance->randomString(10))."|".base64_encode($str)."|".crc32($this->rngInstance->randomString(20));
203                                 break;
204
205                         case 7:
206                                 $garbageString = sha1($this->rngInstance->randomString(10))."|".base64_encode($str)."|".md5($this->rngInstance->randomString(20));
207                                 break;
208
209                         case 8:
210                                 $garbageString = sha1($this->rngInstance->randomString(10))."|".base64_encode($str)."|".sha1($this->rngInstance->randomString(20));
211                                 break;
212                 }
213
214                 // Encrypt the string
215                 $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $garbageString, MCRYPT_MODE_ECB, $iv);
216
217                 // Return the string
218                 return $encrypted;
219         }
220
221         /**
222          * Decrypt the string with fixed salt
223          *
224          * @param       $encrypted      Encrypted string
225          * @return      $str            The unencrypted string
226          */
227         public function decryptString ($encrypted) {
228                 // Init crypto module
229                 $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
230                 $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
231
232                 // Get key
233                 if ($this->getConfigInstance()->readConfig('crypt_fixed_salt') === "Y") {
234                         $key = md5($this->rngInstance->getFixedSalt());
235                 } else {
236                         $key = md5($this->rngInstance->getExtraSalt());
237                 }
238
239                 // Decrypt the string
240                 $garbageString = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_ECB, $iv);
241
242                 // Get the real string out
243                 $strArray = explode("|", $garbageString);
244
245                 // Does the element count match?
246                 assert(count($strArray) == 3);
247
248                 // Decode the string
249                 $str = base64_decode($strArray[1]);
250
251                 // Trim trailing nulls away
252                 $str = rtrim($str, "\0");
253
254                 // Return the string
255                 return $str;
256         }
257 }
258
259 // [EOF]
260 ?>