Subversion Repositories php_utils

Rev

Rev 30 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
30 daniel-mar 1
<?php
2
 
3
/*
4
 * OpenSSL php functions implemented using phpseclib
5
 * Copyright 2022 Daniel Marschall, ViaThinkSoft
31 daniel-mar 6
 * Version 2022-04-10
30 daniel-mar 7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
 
21
// How to use this supplement:
22
// 1. Include phpseclib using composer and include the autoloader
23
// 2. Then, include this file. The openssl functions are now available.
24
 
25
// ATTENTION: This supplement/polyfill does only implement a few openssl_*() functions,
26
// and only a few algorithms: AES and RSA! Feel free to extend this library!
27
// The sign/verify and encrypt/decrypt functions should be binary compatible with
28
// the actual openssl functions.
29
 
30
if (!function_exists('openssl_pkey_new') && class_exists('\\phpseclib3\\Crypt\\RSA')) {
31
 
32
        define('OPENSSL_SUPPLEMENT', 1);
33
 
34
        $openssl_supplement_last_error = '';
35
 
36
        if (!defined('OPENSSL_KEYTYPE_RSA')) define('OPENSSL_KEYTYPE_RSA', 0);
37
 
38
        if (!defined('OPENSSL_RAW_DATA')) define('OPENSSL_RAW_DATA', 1);
39
        if (!defined('OPENSSL_ZERO_PADDING')) define('OPENSSL_ZERO_PADDING', 2);
40
 
41
        if (!defined('OPENSSL_ALGO_SHA1')) define('OPENSSL_ALGO_SHA1', 1);
42
        if (!defined('OPENSSL_ALGO_SHA224')) define('OPENSSL_ALGO_SHA224', 6);
43
        if (!defined('OPENSSL_ALGO_SHA256')) define('OPENSSL_ALGO_SHA256', 7);
44
        if (!defined('OPENSSL_ALGO_SHA384')) define('OPENSSL_ALGO_SHA384', 8);
45
        if (!defined('OPENSSL_ALGO_SHA512')) define('OPENSSL_ALGO_SHA512', 9);
46
        if (!defined('OPENSSL_ALGO_RMD160')) define('OPENSSL_ALGO_RMD160', 10);
47
        if (!defined('OPENSSL_ALGO_MD5')) define('OPENSSL_ALGO_MD5', 2);
48
        if (!defined('OPENSSL_ALGO_MD4')) define('OPENSSL_ALGO_MD4', 3);
49
 
50
        function openssl_pkey_new($pkey_config=null) {
51
                try {
52
                        $algo = $pkey_config && isset($pkey_config["private_key_type"]) ? $pkey_config["private_key_type"] : OPENSSL_KEYTYPE_RSA;
53
                        $bits = $pkey_config && isset($pkey_config["private_key_bits"]) ? $pkey_config["private_key_bits"] : 2048;
54
 
31 daniel-mar 55
                        // TODO: Also support $pkey_config['encrypt_key'] and $pkey_config['encrypt_key_cipher'] ?
56
 
30 daniel-mar 57
                        if ($algo == OPENSSL_KEYTYPE_RSA) {
58
                                $private = \phpseclib3\Crypt\RSA::createKey($bits);
59
                        } else {
60
                                throw new Exception("Algo not implemented");
61
                        }
62
 
63
                        $private = $private->withPadding(\phpseclib3\Crypt\RSA::ENCRYPTION_PKCS1 | \phpseclib3\Crypt\RSA::SIGNATURE_PKCS1);
64
 
65
                        $public = $private->getPublicKey()->withPadding(\phpseclib3\Crypt\RSA::ENCRYPTION_PKCS1 | \phpseclib3\Crypt\RSA::SIGNATURE_PKCS1);
66
 
67
                        return array($algo, $bits, $private, $public);
68
                } catch (Exception $e) {
69
                        global $openssl_supplement_last_error;
70
                        $openssl_supplement_last_error = $e->getMessage();
71
                        return false;
72
                }
73
        }
74
 
75
        function openssl_pkey_export($res, &$privKey, $passphrase = null, $options = null) {
76
                try {
31 daniel-mar 77
                        if ($res instanceof \phpseclib3\Crypt\Common\PrivateKey /*\phpseclib3\Crypt\RSA\PrivateKey*/ ) {
78
                                $privKey = $res;
79
                                if (!is_null($passphrase)) {
80
                                        $privKey = $res->withPassword($passphrase);
81
                                }
82
                                $privKey = $privKey."";
83
                                return true;
84
                        } else if (is_string($res)) {
85
                                $privKey = $res;
86
                                if (!is_null($passphrase)) {
87
                                        $privKey = \phpseclib3\Crypt\RSA::load($privKey);
88
                                        $privKey = $res->withPassword($passphrase);
89
                                        $privKey = $privKey."";
90
                                }
91
                                return true;
92
                        } else if (is_array($res)) {
93
                                $privKey = $res[2]."";
94
                                if (!is_null($passphrase)) {
95
                                        $privKey = \phpseclib3\Crypt\RSA::load($privKey);
96
                                        $privKey = $res->withPassword($passphrase);
97
                                        $privKey = $privKey."";
98
                                }
99
                                return true;
100
                        } else {
101
                                throw new Exception("Invalid input datatype");
102
                        }
30 daniel-mar 103
                } catch (Exception $e) {
104
                        global $openssl_supplement_last_error;
105
                        $openssl_supplement_last_error = $e->getMessage();
106
                        return false;
107
                }
108
        }
109
 
110
        function openssl_pkey_get_details($res) {
111
                return array(
112
                        "bits" => $res[1],
113
                        "key" => $res[3]."",
114
                        "type" => $res[0]
115
                );
116
        }
117
 
118
        function openssl_public_encrypt($data, &$encrypted, $pubKey) {
119
                try {
120
                        if (is_string($pubKey)) $pubKey = openssl_pkey_get_public($pubKey);
31 daniel-mar 121
                        if (!is_object($pubKey) || !method_exists($pubKey,'encrypt'))
122
                                throw new Exception("Invalid input datatype");
30 daniel-mar 123
                        $encrypted = $pubKey->encrypt($data);
124
                        return true;
125
                } catch (Exception $e) {
126
                        global $openssl_supplement_last_error;
127
                        $openssl_supplement_last_error = $e->getMessage();
128
                        return false;
129
                }
130
        }
131
 
132
        function openssl_private_decrypt($encrypted, &$decrypted, $privKey) {
133
                try {
134
                        if (is_string($privKey)) $privKey = openssl_pkey_get_private($privKey);
31 daniel-mar 135
                        if (!is_object($privKey) || !method_exists($privKey,'decrypt'))
136
                                throw new Exception("Invalid input datatype");
30 daniel-mar 137
                        $decrypted = $privKey->decrypt($encrypted);
138
                        return true;
139
                } catch (Exception $e) {
140
                        global $openssl_supplement_last_error;
141
                        $openssl_supplement_last_error = $e->getMessage();
142
                        return false;
143
                }
144
        }
145
 
146
        function openssl_verify($msg, $signature, $public, $algorithm=OPENSSL_ALGO_SHA1) {
147
                try {
148
                        if ($algorithm == OPENSSL_ALGO_SHA1) $algorithm = 'SHA1';
149
                        if ($algorithm == OPENSSL_ALGO_SHA224) $algorithm = 'SHA224';
150
                        if ($algorithm == OPENSSL_ALGO_SHA256) $algorithm = 'SHA256)';
151
                        if ($algorithm == OPENSSL_ALGO_SHA384) $algorithm = 'SHA384';
152
                        if ($algorithm == OPENSSL_ALGO_SHA512) $algorithm = 'SHA512';
153
                        if ($algorithm == OPENSSL_ALGO_RMD160) $algorithm = 'RMD160';
154
                        if ($algorithm == OPENSSL_ALGO_MD5) $algorithm = 'MD5';
155
                        if ($algorithm == OPENSSL_ALGO_MD4) $algorithm = 'MD4';
156
                        if (is_string($public)) $public = openssl_pkey_get_public($public);
31 daniel-mar 157
                        if (!is_object($public) || !method_exists($public,'verify'))
158
                                throw new Exception("Invalid input datatype");
30 daniel-mar 159
                        return $public->withHash($algorithm)->verify($msg, $signature) ? 1 : 0;
160
                } catch (Exception $e) {
161
                        global $openssl_supplement_last_error;
162
                        $openssl_supplement_last_error = $e->getMessage();
163
                        return false;
164
                }
165
        }
166
 
167
        function openssl_sign($msg, &$signature, $private, $algorithm=OPENSSL_ALGO_SHA1) {
168
                try {
169
                        if ($algorithm == OPENSSL_ALGO_SHA1) $algorithm = 'SHA1';
170
                        if ($algorithm == OPENSSL_ALGO_SHA224) $algorithm = 'SHA224';
171
                        if ($algorithm == OPENSSL_ALGO_SHA256) $algorithm = 'SHA256)';
172
                        if ($algorithm == OPENSSL_ALGO_SHA384) $algorithm = 'SHA384';
173
                        if ($algorithm == OPENSSL_ALGO_SHA512) $algorithm = 'SHA512';
174
                        if ($algorithm == OPENSSL_ALGO_RMD160) $algorithm = 'RMD160';
175
                        if ($algorithm == OPENSSL_ALGO_MD5) $algorithm = 'MD5';
176
                        if ($algorithm == OPENSSL_ALGO_MD4) $algorithm = 'MD4';
177
                        if (is_string($private)) $private = openssl_pkey_get_private($private);
31 daniel-mar 178
                        if (!is_object($private) || !method_exists($private,'sign'))
179
                                throw new Exception("Invalid input datatype");
30 daniel-mar 180
                        $signature = $private->withHash($algorithm)->sign($msg);
181
                        return true;
182
                } catch (Exception $e) {
183
                        global $openssl_supplement_last_error;
184
                        $openssl_supplement_last_error = $e->getMessage();
185
                        return false;
186
                }
187
        }
188
 
189
        function openssl_error_string() {
190
                global $openssl_supplement_last_error;
191
                return $openssl_supplement_last_error;
192
        }
193
 
194
        function openssl_random_pseudo_bytes($len) {
195
                /*
196
                if (function_exists('openssl_random_pseudo_bytes')) {
197
                        $a = openssl_random_pseudo_bytes($len);
198
                        if ($a) return $a;
199
                }
200
                */
201
 
202
                if (function_exists('mcrypt_create_iv')) {
203
                        $a = bin2hex(mcrypt_create_iv($len, MCRYPT_DEV_URANDOM));
204
                        if ($a) return $a;
205
                }
206
 
207
                if (function_exists('random_bytes')) {
208
                        $a = random_bytes($len);
209
                        if ($a) return $a;
210
                }
211
 
212
                // Fallback to non-secure RNG
213
                $a = '';
214
                while (strlen($a) < $len*2) {
215
                        $a .= sha1(uniqid((string)mt_rand(), true));
216
                }
217
                $a = substr($a, 0, $len*2);
218
                return hex2bin($a);
219
        }
220
 
221
        function openssl_encrypt($data, $cipher_algo, $passphrase, $options=0, $iv="", &$tag=null, $aad="", $tag_length=16) {
222
                try {
223
                        if (!is_null($tag)) throw new Exception("tag not implemented");
224
                        if ($aad != "") throw new Exception("aad not implemented");
225
                        if ($tag_length != 16) throw new Exception("tag_length not implemented");
226
                        if (!preg_match('@AES\\-(.+)\\-(.+)@i', $cipher_algo, $m)) throw new Exception("Algo not implemented");
227
                        if (($options & OPENSSL_ZERO_PADDING) != 0) throw new Exception("OPENSSL_ZERO_PADDING not implemented");
228
                        $aes = new \phpseclib3\Crypt\AES($m[2]);
229
                        $aes->setKeyLength($m[1]);
230
                        $passphrase = substr($passphrase, 0, $m[1]/8);
231
                        $passphrase = str_pad($passphrase, $m[1]/8, "\0", STR_PAD_RIGHT);
232
                        $aes->setKey($passphrase);
233
                        $aes->setIV($iv);
234
                        $res = $aes->encrypt($data);
235
                        if (($options & OPENSSL_RAW_DATA) == 0) $res = base64_encode($res);
236
                        return $res;
237
                } catch (Exception $e) {
238
                        global $openssl_supplement_last_error;
239
                        $openssl_supplement_last_error = $e->getMessage();
240
                        return false;
241
                }
242
        }
243
 
244
        function openssl_decrypt($data, $cipher_algo, $passphrase, $options=0, $iv="", $tag=null, $aad="") {
245
                try {
246
                        if (!is_null($tag)) throw new Exception("tag not implemented");
247
                        if ($aad != "") throw new Exception("aad not implemented");
248
                        if (!preg_match('@AES\\-(.+)\\-(.+)@i', $cipher_algo, $m)) throw new Exception("Algo not implemented");
249
                        if (($options & OPENSSL_ZERO_PADDING) != 0) throw new Exception("OPENSSL_ZERO_PADDING not implemented");
250
                        $aes = new \phpseclib3\Crypt\AES($m[2]);
251
                        $aes->setKeyLength($m[1]);
252
                        $passphrase = substr($passphrase, 0, $m[1]/8);
253
                        $passphrase = str_pad($passphrase, $m[1]/8, "\0", STR_PAD_RIGHT);
254
                        $aes->setKey($passphrase);
255
                        $aes->setIV($iv);
256
                        if (($options & OPENSSL_RAW_DATA) == 0) $data = base64_decode($data);
257
                        return $aes->decrypt($data);
258
                } catch (Exception $e) {
259
                        global $openssl_supplement_last_error;
260
                        $openssl_supplement_last_error = $e->getMessage();
261
                        return false;
262
                }
263
        }
264
 
265
        function openssl_free_key($key) {
266
                // does nothing
267
        }
268
 
269
        function openssl_pkey_get_private($key, $passphrase=null) {
270
                try {
271
                        if (substr($key,0,7) === 'file://') {
272
                                if (!file_exists($file = substr($key, 7))) throw new Exception("file not found");
273
                                $key = file_get_contents($file);
274
                        }
275
                        if (is_null($passphrase)) $passphrase = false;
276
                        $privKey = \phpseclib3\Crypt\RSA::load($key, $passphrase);
31 daniel-mar 277
                        return $privKey->withPassword(false)->withPadding(\phpseclib3\Crypt\RSA::ENCRYPTION_PKCS1 | \phpseclib3\Crypt\RSA::SIGNATURE_PKCS1); /** @phpstan-ignore-line */ // Call to an undefined method phpseclib3\Crypt\Common\AsymmetricKey::withPadding().
30 daniel-mar 278
                } catch (Exception $e) {
279
                        global $openssl_supplement_last_error;
280
                        $openssl_supplement_last_error = $e->getMessage();
281
                        return false;
282
                }
283
        }
284
 
285
        function openssl_pkey_get_public($public_key) {
286
                try {
287
                        if (substr($public_key,0,7) === 'file://') {
288
                                if (!file_exists($file = substr($public_key, 7))) throw new Exception("file not found");
289
                                $public_key = file_get_contents($file);
290
                        }
291
                        $pubKey = \phpseclib3\Crypt\RSA::load($public_key);
292
                        return $pubKey->withPadding(\phpseclib3\Crypt\RSA::ENCRYPTION_PKCS1 | \phpseclib3\Crypt\RSA::SIGNATURE_PKCS1); /** @phpstan-ignore-line */ // Call to an undefined method phpseclib3\Crypt\Common\AsymmetricKey::withPadding().
293
                } catch (Exception $e) {
294
                        global $openssl_supplement_last_error;
295
                        $openssl_supplement_last_error = $e->getMessage();
296
                        return false;
297
                }
298
        }
299
 
300
}