]> git.mxchange.org Git - core.git/blob - framework/main/classes/crypto/class_CryptoHelper.php
Continued:
[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 - 2023 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                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('CRYPTO-HELPER: CALLED!');
77                 $cryptoInstance = new CryptoHelper();
78
79                 // Initialize the hasher
80                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('CRYPTO-HELPER: Invoking cryptoInstance->initHasher() ...');
81                 $cryptoInstance->initHasher();
82
83                 // Attach a crypto stream
84                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('CRYPTO-HELPER: Invoking cryptoInstance->attachCryptoStream() ...');
85                 $cryptoInstance->attachCryptoStream();
86
87                 // Return the instance
88                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('CRYPTO-HELPER: cryptoInstance=%s - EXIT!', $cryptoInstance->__toString()));
89                 return $cryptoInstance;
90         }
91
92         /**
93          * Get a singleton instance of this class
94          *
95          * @return      $selfInstance   An instance of this crypto helper class
96          */
97         public static final function getSelfInstance () {
98                 // Is no instance there?
99                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('CRYPTO-HELPER: self::selfInstance[]=%s - CALLED!', gettype(self::$selfInstance)));
100                 if (is_null(self::$selfInstance)) {
101                         // Then get a new one
102                         self::$selfInstance = self::createCryptoHelper();
103                 }
104
105                 // Return the instance
106                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('CRYPTO-HELPER: self::selfInstance=%s - EXIT!', self::$selfInstance->__toString()));
107                 return self::$selfInstance;
108         }
109
110         /**
111          * Setter for RNG instance
112          *
113          * @param       $rngInstance    An instance of a random number generator (RNG)
114          * @return      void
115          */
116         protected final function setRngInstance (RandomNumberGenerator $rngInstance) {
117                 $this->rngInstance = $rngInstance;
118         }
119
120         /**
121          * Getter for RNG instance
122          *
123          * @return      $rngInstance    An instance of a random number generator (RNG)
124          */
125         public final function getRngInstance () {
126                 return $this->rngInstance;
127         }
128
129         /**
130          * Attaches a crypto stream to this crypto helper by detecting loaded
131          * modules.
132          *
133          * @return      void
134          */
135         protected function attachCryptoStream () {
136                 // @TODO Maybe rewrite this with DirectoryIterator, similar to Compressor thing?
137                 // Do we have openssl loaded?
138                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('CRYPTO-HELPER: CALLED!');
139                 if ($this->isPhpExtensionLoaded('openssl')) {
140                         // Then use it
141                         /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage('CRYPTO-HELPER: Attaching openssl crypto stream ...');
142                         $this->cryptoStreamInstance = ObjectFactory::createObjectByConfiguredName('crypto_openssl_stream_class', [$this->getRngInstance()]);
143                 } else {
144                         // If nothing works ...
145                         /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage('CRYPTO-HELPER: Attaching NULL crypto stream ...');
146                         $this->cryptoStreamInstance = ObjectFactory::createObjectByConfiguredName('crypto_null_stream_class');
147                 }
148
149                 // Trace message
150                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('CRYPTO-HELPER: EXIT!');
151         }
152
153         /**
154          * Initializes the hasher for different purposes.
155          *
156          * @return      void
157          */
158         protected function initHasher () {
159                 // Initialize the random number generator which is required by some crypto methods
160                 $this->setRngInstance(ObjectFactory::createObjectByConfiguredName('rng_class'));
161
162                 // Generate a salt for the hasher
163                 $this->generateSalt();
164         }
165
166         /**
167          * Generates the salt based on configured length
168          *
169          * @return      void
170          */
171         private function generateSalt () {
172                 // Get a random string from the RNG
173                 $randomString = $this->getRngInstance()->randomString() . $this->createUuid();
174
175                 // Get config entry for salt length
176                 $length = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('salt_length');
177
178                 // Keep only defined number of characters
179                 $this->salt = substr(sha1($randomString), -$length, $length);
180         }
181
182         /**
183          * Returns a UUID (Universal Unique IDentifier) if PECL extension uuid was
184          * found or an empty string it not.
185          *
186          * @return      $uuid   UUID with leading dash or empty string
187          */
188         public function createUuid () {
189                 // Init empty UUID
190                 $uuid = '';
191
192                 // Is the UUID extension loaded and enabled? (see pecl)
193                 if (FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('extension_uuid_loaded') === true) {
194                         // Then add it as well
195                         $uuid = uuid_create();
196                 }
197
198                 // Return it
199                 return $uuid;
200         }
201
202         /**
203          * Hashes a string with salt and returns the hash. If an old previous hash
204          * is supplied the method will use the first X chars of that hash for hashing
205          * the password. This is useful if you want to check if password is identical
206          * for authorization purposes.
207          *
208          * @param       $str            Unhashed string
209          * @param       $oldHash        A hash from previous hashed string
210          * @param       $withFixed      Whether to include a fixed salt (not recommended in p2p applications)
211          * @return      $hashed         The hashed and salted string
212          */
213         public function hashString (string $str, string $oldHash = '', bool $withFixed = true) {
214                 // Default is the default salt ;-)
215                 $salt = $this->salt;
216
217                 // Is the old password set?
218                 if (!empty($oldHash)) {
219                         // Use the salt from hash, first get length
220                         $length = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('salt_length');
221
222                         // Then extract the X first characters from the hash as our salt
223                         $salt = substr($oldHash, 0, $length);
224                 }
225
226                 // Hash the password with salt
227                 //* DEBUG: */ echo "salt=".$salt."/plain=".$str."<br />\n";
228                 if ($withFixed === true) {
229                         // Use additional fixed salt
230                         $hashed = $salt . md5(sprintf(FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('hash_extra_mask'),
231                                 $salt,
232                                 $this->getRngInstance()->getFixedSalt(),
233                                 $str
234                         ));
235                 } else {
236                         // Use salt+string to hash
237                         $hashed = $salt . md5(sprintf(FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('hash_normal_mask'),
238                                 $salt,
239                                 $str
240                         ));
241                 }
242
243                 // And return it
244                 return $hashed;
245         }
246
247         /**
248          * Encrypt the string with fixed salt
249          *
250          * @param       $str            The unencrypted string
251          * @param       $key            Optional key, if none provided, a random key will be generated
252          * @return      $encrypted      Encrypted string
253          */
254         public function encryptString (string $str, string $key = NULL) {
255                 // Encrypt the string through the stream
256                 $encrypted = $this->cryptoStreamInstance->encryptStream($str, $key);
257
258                 // Return the string
259                 return $encrypted;
260         }
261
262         /**
263          * Decrypt the string with fixed salt
264          *
265          * @param       $encrypted      Encrypted string
266          * @return      $str            The unencrypted string
267          */
268         public function decryptString (string $encrypted) {
269                 // Encrypt the string through the stream
270                 $str = $this->cryptoStreamInstance->decryptStream($encrypted);
271
272                 // Return the string
273                 return $str;
274         }
275
276 }