133cc135683493e887051e0da59423c9dacdd3aa
[core.git] / framework / main / classes / crypto / class_CryptoHelper.php
1 <?php
2 // Own namespace
3 namespace Org\Mxchange\CoreFramework\Helper\Crypto;
4
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap;
7 use Org\Mxchange\CoreFramework\Crypto\Cryptable;
8 use Org\Mxchange\CoreFramework\Crypto\RandomNumber\RandomNumberGenerator;
9 use Org\Mxchange\CoreFramework\Factory\Object\ObjectFactory;
10 use Org\Mxchange\CoreFramework\Object\BaseFrameworkSystem;
11
12 /**
13  * A helper class for cryptographical things like hashing passwords and so on
14  *
15  * @author              Roland Haeder <webmaster@shipsimu.org>
16  * @version             0.0.0
17  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2021 Core Developer Team
18  * @license             GNU GPL 3.0 or any newer version
19  * @link                http://www.shipsimu.org
20  *
21  * This program is free software: you can redistribute it and/or modify
22  * it under the terms of the GNU General Public License as published by
23  * the Free Software Foundation, either version 3 of the License, or
24  * (at your option) any later version.
25  *
26  * This program is distributed in the hope that it will be useful,
27  * but WITHOUT ANY WARRANTY; without even the implied warranty of
28  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29  * GNU General Public License for more details.
30  *
31  * You should have received a copy of the GNU General Public License
32  * along with this program. If not, see <http://www.gnu.org/licenses/>.
33  */
34 class CryptoHelper extends BaseFrameworkSystem implements Cryptable {
35         // Exception constants
36         const EXCEPTION_ENCRYPT_MISSING = 0x1f0;
37         const EXCEPTION_ENCRYPT_INVALID = 0x1f1;
38
39         /**
40          * An instance of this own clas
41          */
42         private static $selfInstance = NULL;
43
44         /**
45          * Instance of the crypto stream
46          */
47         private $cryptoStreamInstance = NULL;
48
49         /**
50          * Salt for hashing operations
51          */
52         private $salt = '';
53
54         /**
55          * Instance of a RNG
56          */
57         private $rngInstance = NULL;
58
59         /**
60          * Protected constructor
61          *
62          * @return      void
63          */
64         private function __construct () {
65                 // Call parent constructor
66                 parent::__construct(__CLASS__);
67         }
68
69         /**
70          * Creates an instance of this class
71          *
72          * @return      $cryptoInstance         An instance of this crypto helper class
73          */
74         public static final function createCryptoHelper () {
75                 // Get a new instance
76                 $cryptoInstance = new CryptoHelper();
77
78                 // Initialize the hasher
79                 $cryptoInstance->initHasher();
80
81                 // Attach a crypto stream
82                 $cryptoInstance->attachCryptoStream();
83
84                 // Return the instance
85                 return $cryptoInstance;
86         }
87
88         /**
89          * Get a singleton instance of this class
90          *
91          * @return      $selfInstance   An instance of this crypto helper class
92          */
93         public static final function getSelfInstance () {
94                 // Is no instance there?
95                 if (is_null(self::$selfInstance)) {
96                         // Then get a new one
97                         self::$selfInstance = self::createCryptoHelper();
98                 } // END - if
99
100                 // Return the instance
101                 return self::$selfInstance;
102         }
103
104         /**
105          * Setter for RNG instance
106          *
107          * @param       $rngInstance    An instance of a random number generator (RNG)
108          * @return      void
109          */
110         protected final function setRngInstance (RandomNumberGenerator $rngInstance) {
111                 $this->rngInstance = $rngInstance;
112         }
113
114         /**
115          * Getter for RNG instance
116          *
117          * @return      $rngInstance    An instance of a random number generator (RNG)
118          */
119         public final function getRngInstance () {
120                 return $this->rngInstance;
121         }
122
123         /**
124          * Attaches a crypto stream to this crypto helper by detecting loaded
125          * modules.
126          *
127          * @return      void
128          */
129         protected function attachCryptoStream () {
130                 // @TODO Maybe rewrite this with DirectoryIterator, similar to Compressor thing?
131                 // Do we have openssl/mcrypt loaded?
132                 if ($this->isPhpExtensionLoaded('mcrypt')) {
133                         // Then use it
134                         $this->cryptoStreamInstance = ObjectFactory::createObjectByConfiguredName('crypto_mcrypt_stream_class', array($this->getRngInstance()));
135                 } elseif ($this->isPhpExtensionLoaded('openssl')) {
136                         // Then use it
137                         $this->cryptoStreamInstance = ObjectFactory::createObjectByConfiguredName('crypto_openssl_stream_class', array($this->getRngInstance()));
138                 } else {
139                         // If nothing works ...
140                         $this->cryptoStreamInstance = ObjectFactory::createObjectByConfiguredName('crypto_null_stream_class');
141                 }
142         }
143
144         /**
145          * Initializes the hasher for different purposes.
146          *
147          * @return      void
148          */
149         protected function initHasher () {
150                 // Initialize the random number generator which is required by some crypto methods
151                 $this->setRngInstance(ObjectFactory::createObjectByConfiguredName('rng_class'));
152
153                 // Generate a salt for the hasher
154                 $this->generateSalt();
155         }
156
157         /**
158          * Generates the salt based on configured length
159          *
160          * @return      void
161          */
162         private function generateSalt () {
163                 // Get a random string from the RNG
164                 $randomString = $this->getRngInstance()->randomString() . $this->createUuid();
165
166                 // Get config entry for salt length
167                 $length = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('salt_length');
168
169                 // Keep only defined number of characters
170                 $this->salt = substr(sha1($randomString), -$length, $length);
171         }
172
173         /**
174          * Returns a UUID (Universal Unique IDentifier) if PECL extension uuid was
175          * found or an empty string it not.
176          *
177          * @return      $uuid   UUID with leading dash or empty string
178          */
179         public function createUuid () {
180                 // Init empty UUID
181                 $uuid = '';
182
183                 // Is the UUID extension loaded and enabled? (see pecl)
184                 if (FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('extension_uuid_loaded') === true) {
185                         // Then add it as well
186                         $uuid = uuid_create();
187                 } // END - if
188
189                 // Return it
190                 return $uuid;
191         }
192
193         /**
194          * Hashes a string with salt and returns the hash. If an old previous hash
195          * is supplied the method will use the first X chars of that hash for hashing
196          * the password. This is useful if you want to check if password is identical
197          * for authorization purposes.
198          *
199          * @param       $str            Unhashed string
200          * @param       $oldHash        A hash from previous hashed string
201          * @param       $withFixed      Whether to include a fixed salt (not recommended in p2p applications)
202          * @return      $hashed         The hashed and salted string
203          */
204         public function hashString (string $str, string $oldHash = '', bool $withFixed = true) {
205                 // Default is the default salt ;-)
206                 $salt = $this->salt;
207
208                 // Is the old password set?
209                 if (!empty($oldHash)) {
210                         // Use the salt from hash, first get length
211                         $length = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('salt_length');
212
213                         // Then extract the X first characters from the hash as our salt
214                         $salt = substr($oldHash, 0, $length);
215                 } // END - if
216
217                 // Hash the password with salt
218                 //* DEBUG: */ echo "salt=".$salt."/plain=".$str."<br />\n";
219                 if ($withFixed === true) {
220                         // Use additional fixed salt
221                         $hashed = $salt . md5(sprintf(FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('hash_extra_mask'),
222                                 $salt,
223                                 $this->getRngInstance()->getFixedSalt(),
224                                 $str
225                         ));
226                 } else {
227                         // Use salt+string to hash
228                         $hashed = $salt . md5(sprintf(FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('hash_normal_mask'),
229                                 $salt,
230                                 $str
231                         ));
232                 }
233
234                 // And return it
235                 return $hashed;
236         }
237
238         /**
239          * Encrypt the string with fixed salt
240          *
241          * @param       $str            The unencrypted string
242          * @param       $key            Optional key, if none provided, a random key will be generated
243          * @return      $encrypted      Encrypted string
244          */
245         public function encryptString (string $str, string $key = NULL) {
246                 // Encrypt the string through the stream
247                 $encrypted = $this->cryptoStreamInstance->encryptStream($str, $key);
248
249                 // Return the string
250                 return $encrypted;
251         }
252
253         /**
254          * Decrypt the string with fixed salt
255          *
256          * @param       $encrypted      Encrypted string
257          * @return      $str            The unencrypted string
258          */
259         public function decryptString (string $encrypted) {
260                 // Encrypt the string through the stream
261                 $str = $this->cryptoStreamInstance->decryptStream($encrypted);
262
263                 // Return the string
264                 return $str;
265         }
266
267 }