GibberishAES::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 0
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 2
ccs 0
cts 1
cp 0
crap 2
rs 10
1
<?php
2
3
/**
4
 * Gibberish AES, a PHP Implementation
5
 *
6
 * See Gibberish AES javascript encryption library, @link https://github.com/mdp/gibberish-aes
7
 *
8
 * An important note: The complementary JavaScript project Gibberish AES has been
9
 * deprecated, see https://github.com/mdp/gibberish-aes/issues/25
10
 * Consider finding alternative PHP and JavaScript solutions.
11
 *
12
 * This implementation is based on initial code proposed by nbari at dalmp dot com
13
 * @link http://www.php.net/manual/en/function.openssl-decrypt.php#107210
14
 *
15
 * Requirements:
16
 *
17
 * php7.1
18
 *
19
 * modules: openssl, mbstring
20
 *
21
 *
22
 * Usage:
23
 *
24
 * // This is a secret pass-phrase, keep it in a safe place and don't loose it.
25
 * $pass = 'my secret pass-phrase, it should be long';
26
 *
27
 * // The string to be encrypted.
28
 * $string = 'my secret message';
29
 *
30
 * // This is the result after encryption of the given string.
31
 * $encrypted_string = GibberishAES::enc($string, $pass);
32
 *
33
 * // This is the result after decryption of the previously encrypted string.
34
 * // $decrypted_string == $string (should be).
35
 * $decrypted_string = GibberishAES::dec($encrypted_string, $pass);
36
 * echo $decrypted_string;
37
 *
38
 * // The default key-size is 256 bits. 128 and 192 bits are also allowed.
39
 * // Example:
40
 * $old_key_size = GibberishAES::size();
41
 * GibberishAES::size(192);
42
 * // The short way: $old_key_size = GibberishAES::size(192);
43
 * $encrypted_string = GibberishAES::enc($string, $pass);
44
 * $decrypted_string = GibberishAES::dec($encrypted_string, $pass);
45
 * GibberishAES::size($old_key_size);
46
 * echo $decrypted_string;
47
 *
48
 * @author Ivan Tcholakov <[email protected]>, 2012-2016.
49
 * @author Philipp Dittert <[email protected]>, 2019-2020
50
 *
51
 * Code repository: @link https://github.com/dittertp/gibberish-aes-php
52
 *
53
 * @version 2.0.0
54
 *
55
 * @license The MIT License (MIT)
56
 * @link http://opensource.org/licenses/MIT
57
 */
58
59
namespace GibberishAES;
60
61
class GibberishAES
62
{
63
    // The default key size in bits.
64
    protected static $key_size = 256;
65
66
    // The allowed key sizes in bits.
67
    protected static $valid_key_sizes = array(128, 192, 256);
68
69
    protected static $random_bytes_exists = null;
70
    protected static $openssl_encrypt_exists = null;
71
    protected static $openssl_decrypt_exists = null;
72
    protected static $mcrypt_exists = null;
73
    protected static $mbstring_func_overload = null;
74
75
    final private function __construct()
76
    {
77
    }
78
79
    private function __clone()
80
    {
81
    }
82
83
    /**
84
     * Crypt AES (256, 192, 128)
85
     *
86
     * @param   string  $string     The input message to be encrypted.
87
     * @param   string  $pass       The secret pass-phrase, choose a long string
88
     *                              (64 characters for example) for keeping high entropy.
89
     *                              The pass-phrase is converted internaly into
90
     *                              a binary key that is to be used for encryption.
91
     * @return  mixed               base64 encrypted string, FALSE on failure.
92
     * @throws \Exception
93
     */
94 4
    public static function enc(string $string, string $pass): string
95
    {
96 4
        $key_size = self::$key_size;
97
98
        // Set a random salt.
99 4
        $salt = self::randomBytes(8);
100
101 4
        $salted = '';
102 4
        $dx = '';
103
104
        // Lengths in bytes:
105 4
        $key_length = (int) ($key_size / 8);
106 4
        $block_length = 16; // 128 bits, iv has the same length.
107
        // $salted_length = $key_length (32, 24, 16) + $block_length (16) = (48, 40, 32)
108 4
        $salted_length = $key_length + $block_length;
109
110 4
        while (self::strlen($salted) < $salted_length) {
111 4
            $dx = md5($dx . $pass . $salt, true);
112 4
            $salted .= $dx;
113
        }
114
115 4
        $key = self::substr($salted, 0, $key_length);
116 4
        $iv = self::substr($salted, $key_length, $block_length);
117
118 4
        $encrypted = self::aesCbcEncrypt($string, $key, $iv);
119
120 4
        return $encrypted !== false ? base64_encode('Salted__' . $salt . $encrypted) : false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $encrypted !== fa...t . $encrypted) : false could return the type false which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
121
    }
122
123
    /**
124
     * Decrypt AES (256, 192, 128)
125
     *
126
     * @param   string  $string     The input message to be decrypted.
127
     * @param   string  $pass       The secret pass-phrase that has been used for encryption.
128
     * @return  mixed               base64 decrypted string, FALSE on failure.
129
     */
130 4
    public static function dec(string $string, string $pass): string
131
    {
132
133 4
        $key_size = self::$key_size;
134
135
        // Lengths in bytes:
136 4
        $key_length = (int) ($key_size / 8);
137 4
        $block_length = 16;
138
139 4
        $data = base64_decode($string);
140 4
        $salt = self::substr($data, 8, 8);
141 4
        $encrypted = self::substr($data, 16);
142
143 4
        $rounds = 3;
144 4
        if ($key_size == 128) {
145 1
            $rounds = 2;
146
        }
147
148 4
        $data00 = $pass . $salt;
149 4
        $md5_hash = array();
150 4
        $md5_hash[0] = md5($data00, true);
151 4
        $result = $md5_hash[0];
152
153 4
        for ($i = 1; $i < $rounds; $i++) {
154 4
            $md5_hash[$i] = md5($md5_hash[$i - 1] . $data00, true);
155 4
            $result .= $md5_hash[$i];
156
        }
157
158 4
        $key = self::substr($result, 0, $key_length);
159 4
        $iv = self::substr($result, $key_length, $block_length);
160
161 4
        return self::aesCbcDecrypt($encrypted, $key, $iv);
162
    }
163
164
    /**
165
     * Sets the key-size for encryption/decryption in number of bits
166
     *
167
     * @param   int|null $newSize The new key size. The valid integer values are: 128, 192, 256 (default)
168
     *                            $newsize may be NULL or may be omited - in this case
169
     *                            this method is just a getter of the current key size value.
170
     *
171
     * @return int Returns the old key size value.
172
     *
173
     * @throws \Exception
174
     */
175 3
    public static function size(?int $newSize = null): int
176
    {
177 3
        $result = self::$key_size;
178
179 3
        if (is_null($newSize)) {
180
            return $result;
181
        }
182
183 3
        if (!in_array($newSize, self::$valid_key_sizes)) {
184
            throw new \Exception(
185
                'GibberishAES: Invalid key size value was to be set. Allowed values: '
186
                . implode(', ', self::$valid_key_sizes)
187
            );
188
        }
189
190 3
        self::$key_size = $newSize;
191
192 3
        return $newSize;
193
    }
194
195
    /**
196
     * returns the multibyte string length
197
     *
198
     * @param string $str
199
     *
200
     * @return int
201
     */
202 4
    protected static function strlen(string $str): int
203
    {
204 4
        return mb_strlen($str, '8bit');
205
    }
206
207
    /**
208
     * returns a substring of given string
209
     *
210
     * @param string $str
211
     * @param int $start
212
     * @param int|null $length
213
     *
214
     * @return string
215
     */
216 4
    protected static function substr(string $str, int $start, ?int $length = null): string
217
    {
218 4
        isset($length) or $length = ($start >= 0 ? self::strlen($str) - $start : -$start);
219
220 4
        return mb_substr($str, $start, $length, '8bit');
221
    }
222
223
    /**
224
     * return random bytes
225
     *
226
     * @param int $length
227
     *
228
     * @return string
229
     * @throws \Exception
230
     */
231 4
    protected static function randomBytes(int $length): string
232
    {
233 4
        return random_bytes($length);
234
    }
235
236
    /**
237
     * encrypt given string with given key
238
     *
239
     * @param string $string
240
     * @param string $key
241
     * @param string $iv
242
     *
243
     * @return string
244
     */
245 4
    protected static function aesCbcEncrypt(string $string, string $key, string $iv): string
246
    {
247 4
        $key_size = self::$key_size;
248
249 4
        return openssl_encrypt($string, "aes-$key_size-cbc", $key, true, $iv);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type integer expected by parameter $options of openssl_encrypt(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

249
        return openssl_encrypt($string, "aes-$key_size-cbc", $key, /** @scrutinizer ignore-type */ true, $iv);
Loading history...
250
    }
251
252
    /**
253
     * decrypts given string with given key
254
     *
255
     * @param string $crypted
256
     * @param string $key
257
     * @param string $iv
258
     *
259
     * @return string
260
     */
261 4
    protected static function aesCbcDecrypt(string $crypted, string $key, string $iv): string
262
    {
263 4
        $key_size = self::$key_size;
264
265 4
        return openssl_decrypt($crypted, "aes-$key_size-cbc", $key, 1, $iv);
266
    }
267
}
268