Subversion Repositories oidplus

Rev

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

Rev Author Line No. Line
2 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
1103 daniel-mar 5
 * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
2 daniel-mar 6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
 
1050 daniel-mar 20
use ViaThinkSoft\OIDplus\OIDplus;
21
 
1130 daniel-mar 22
/**
23
 * @param string $privKey
24
 * @return bool
25
 */
26
function is_privatekey_encrypted(string $privKey): bool {
830 daniel-mar 27
        return strpos($privKey,'BEGIN ENCRYPTED PRIVATE KEY') !== false;
28
}
29
 
1130 daniel-mar 30
/**
31
 * @param string $privKey
32
 * @param string $pubKey
33
 * @return bool
34
 */
35
function verify_private_public_key(string $privKey, string $pubKey): bool {
721 daniel-mar 36
        if (!function_exists('openssl_public_encrypt')) return false;
74 daniel-mar 37
        try {
38
                if (empty($privKey)) return false;
39
                if (empty($pubKey)) return false;
453 daniel-mar 40
                $data = generateRandomString(25);
386 daniel-mar 41
                $encrypted = '';
42
                $decrypted = '';
74 daniel-mar 43
                if (!@openssl_public_encrypt($data, $encrypted, $pubKey)) return false;
44
                if (!@openssl_private_decrypt($encrypted, $decrypted, $privKey)) return false;
45
                return $decrypted == $data;
1050 daniel-mar 46
        } catch (\Exception $e) {
74 daniel-mar 47
                return false;
48
        }
49
}
50
 
1130 daniel-mar 51
/**
52
 * @param string $privKeyOld
53
 * @param string|null $passphrase_old
54
 * @param string|null $passphrase_new
55
 * @return false|string
56
 */
57
function change_private_key_passphrase(string $privKeyOld, string $passphrase_old=null, string $passphrase_new=null) {
830 daniel-mar 58
        $pkey_config = array(
59
            //"digest_alg" => "sha512",
60
            //"private_key_bits" => 2048,
61
            //"private_key_type" => OPENSSL_KEYTYPE_RSA,
1050 daniel-mar 62
            "config" => class_exists("\\ViaThinkSoft\\OIDplus\\OIDplus") ? OIDplus::getOpenSslCnf() : @getenv('OPENSSL_CONF')
830 daniel-mar 63
        );
64
        $privKeyNew = @openssl_pkey_get_private($privKeyOld, $passphrase_old);
65
        if ($privKeyNew === false) return false;
1116 daniel-mar 66
        if (!@openssl_pkey_export($privKeyNew, $privKeyNewExport, $passphrase_new, $pkey_config)) return false;
67
        if ($privKeyNewExport === "") return false;
68
        return "$privKeyNewExport";
830 daniel-mar 69
}
70
 
1130 daniel-mar 71
/**
72
 * @param string $privKey
73
 * @param string $passphrase
74
 * @return false|string
75
 */
76
function decrypt_private_key(string $privKey, string $passphrase) {
830 daniel-mar 77
        return change_private_key_passphrase($privKey, $passphrase, null);
78
}
79
 
1130 daniel-mar 80
/**
81
 * @param string $privKey
82
 * @param string $passphrase
83
 * @return false|string
84
 */
85
function encrypt_private_key(string $privKey, string $passphrase) {
830 daniel-mar 86
        return change_private_key_passphrase($privKey, null, $passphrase);
87
}
88
 
1130 daniel-mar 89
/**
90
 * @param string $data
91
 * @return int
92
 */
93
function smallhash(string $data): int { // get 31 bits from SHA1. Values 0..2147483647
250 daniel-mar 94
        return (hexdec(substr(sha1($data),-4*2)) & 0x7FFFFFFF);
74 daniel-mar 95
}
180 daniel-mar 96
 
1130 daniel-mar 97
/**
98
 * @param string $name
99
 * @return array
100
 */
101
function split_firstname_lastname(string $name): array {
182 daniel-mar 102
        $ary = explode(' ', $name);
103
        $last_name = array_pop($ary);
104
        $first_name = implode(' ', $ary);
105
        return array($first_name, $last_name);
106
}
107
 
1130 daniel-mar 108
/**
109
 * @return void
110
 */
180 daniel-mar 111
function originHeaders() {
112
        // CORS
113
        // Author: Till Wehowski
426 daniel-mar 114
        // TODO: add to class OIDplus
182 daniel-mar 115
 
180 daniel-mar 116
        header("Access-Control-Allow-Credentials: true");
117
        header("Access-Control-Allow-Origin: ".strip_tags(((isset($_SERVER['HTTP_ORIGIN'])) ? $_SERVER['HTTP_ORIGIN'] : "*")));
118
 
119
        header("Access-Control-Allow-Headers: If-None-Match, X-Requested-With, Origin, X-Frdlweb-Bugs, Etag, X-Forgery-Protection-Token, X-CSRF-Token");
120
 
121
        if (isset($_SERVER['HTTP_ORIGIN'])) {
122
                header('X-Frame-Options: ALLOW-FROM '.$_SERVER['HTTP_ORIGIN']);
123
        } else {
124
                header_remove("X-Frame-Options");
125
        }
126
 
127
        $expose = array('Etag', 'X-CSRF-Token');
128
        foreach (headers_list() as $num => $header) {
129
                $h = explode(':', $header);
130
                $expose[] = trim($h[0]);
131
        }
132
        header("Access-Control-Expose-Headers: ".implode(',',$expose));
133
 
134
        header("Vary: Origin");
135
}
236 daniel-mar 136
 
346 daniel-mar 137
if (!function_exists('mb_wordwrap')) {
1130 daniel-mar 138
        /**
139
         * @param string $str
140
         * @param int $width
141
         * @param string $break
142
         * @param bool $cut
143
         * @return string
144
         */
145
        function mb_wordwrap(string $str, int $width = 75, string $break = "\n", bool $cut = false): string {
346 daniel-mar 146
                // https://stackoverflow.com/a/4988494/488539
147
                $lines = explode($break, $str);
148
                foreach ($lines as &$line) {
149
                        $line = rtrim($line);
150
                        if (mb_strlen($line) <= $width) {
151
                                continue;
152
                        }
153
                        $words = explode(' ', $line);
154
                        $line = '';
155
                        $actual = '';
156
                        foreach ($words as $word) {
157
                                if (mb_strlen($actual.$word) <= $width) {
158
                                        $actual .= $word.' ';
159
                                } else {
160
                                        if ($actual != '') {
161
                                                $line .= rtrim($actual).$break;
162
                                        }
163
                                        $actual = $word;
164
                                        if ($cut) {
165
                                                while (mb_strlen($actual) > $width) {
166
                                                        $line .= mb_substr($actual, 0, $width).$break;
167
                                                        $actual = mb_substr($actual, $width);
168
                                                }
169
                                        }
170
                                        $actual .= ' ';
171
                                }
172
                        }
173
                        $line .= trim($actual);
174
                }
175
                return implode($break, $lines);
176
        }
177
}
355 daniel-mar 178
 
1130 daniel-mar 179
/**
180
 * @param string $out
181
 * @param string $contentType
182
 * @param string $filename
183
 * @return void
184
 */
185
function httpOutWithETag(string $out, string $contentType, string $filename='') {
379 daniel-mar 186
        $etag = md5($out);
187
        header("Etag: $etag");
188
        header("Content-MD5: $etag"); // RFC 2616 clause 14.15
189
        if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && (trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag)) {
1068 daniel-mar 190
                if (PHP_SAPI != 'cli') @http_response_code(304); // 304 Not Modified
379 daniel-mar 191
        } else {
192
                header("Content-Type: $contentType");
193
                if (!empty($filename)) {
194
                        header('Content-Disposition:inline; filename="'.$filename.'"');
195
                }
196
                echo $out;
197
        }
198
        die();
199
}
200
 
1130 daniel-mar 201
/**
202
 * @param string $str
203
 * @param array $args
204
 * @return string
205
 */
206
function my_vsprintf(string $str, array $args): string {
207
        $n = 1;
208
        foreach ($args as $val) {
209
                $str = str_replace("%$n", $val, $str);
210
                $n++;
211
        }
212
        return str_replace("%%", "%", $str);
360 daniel-mar 213
}
214
 
1130 daniel-mar 215
/**
216
 * @param string $str
217
 * @param mixed ...$sprintfArgs
218
 * @return string
219
 * @throws \ViaThinkSoft\OIDplus\OIDplusConfigInitializationException
220
 * @throws \ViaThinkSoft\OIDplus\OIDplusException
221
 */
222
function _L(string $str, ...$sprintfArgs): string {
401 daniel-mar 223
        static $translation_array = array();
224
        static $translation_loaded = null;
225
 
506 daniel-mar 226
        $str = trim($str);
227
 
1050 daniel-mar 228
        if (!class_exists(OIDplus::class)) {
438 daniel-mar 229
                return my_vsprintf($str, $sprintfArgs);
230
        }
231
 
360 daniel-mar 232
        $lang = OIDplus::getCurrentLang();
468 daniel-mar 233
        $ta = OIDplus::getTranslationArray($lang);
234
        $res = (isset($ta[$lang]) && isset($ta[$lang][$str])) ? $ta[$lang][$str] : $str;
360 daniel-mar 235
 
236
        $res = str_replace('###', OIDplus::baseConfig()->getValue('TABLENAME_PREFIX', ''), $res);
237
 
1130 daniel-mar 238
        return my_vsprintf($res, $sprintfArgs);
370 daniel-mar 239
}
386 daniel-mar 240
 
1130 daniel-mar 241
/**
242
 * @param array $params
243
 * @param string $key
244
 * @return void
245
 * @throws \ViaThinkSoft\OIDplus\OIDplusConfigInitializationException
246
 * @throws \ViaThinkSoft\OIDplus\OIDplusException
247
 */
248
function _CheckParamExists(array $params, string $key) {
1050 daniel-mar 249
        if (class_exists(OIDplusException::class)) {
698 daniel-mar 250
                if (!isset($params[$key])) throw new OIDplusException(_L('Parameter %1 is missing', $key));
251
        } else {
252
                if (!isset($params[$key])) throw new Exception(_L('Parameter %1 is missing', $key));
253
        }
552 daniel-mar 254
}
255
 
1130 daniel-mar 256
/**
257
 * @param string $cont
258
 * @return array
259
 */
260
function extractHtmlContents(string $cont): array {
721 daniel-mar 261
        // make sure the program works even if the user provided HTML is not UTF-8
262
        $cont = convert_to_utf8_no_bom($cont);
263
 
386 daniel-mar 264
        $out_js = '';
265
        $m = array();
266
        preg_match_all('@<script[^>]*>(.+)</script>@ismU', $cont, $m);
267
        foreach ($m[1] as $x) {
268
                $out_js = $x . "\n\n";
269
        }
270
 
271
        $out_css = '';
272
        $m = array();
273
        preg_match_all('@<style[^>]*>(.+)</style>@ismU', $cont, $m);
274
        foreach ($m[1] as $x) {
275
                $out_css = $x . "\n\n";
276
        }
277
 
278
        $out_html = $cont;
279
        $out_html = preg_replace('@^(.+)<body[^>]*>@isU', '', $out_html);
280
        $out_html = preg_replace('@</body>.+$@isU', '', $out_html);
281
        $out_html = preg_replace('@<title>.+</title>@isU', '', $out_html);
282
        $out_html = preg_replace('@<h1>.+</h1>@isU', '', $out_html, 1);
283
        $out_html = preg_replace('@<script[^>]*>(.+)</script>@ismU', '', $out_html);
284
        $out_html = preg_replace('@<style[^>]*>(.+)</style>@ismU', '', $out_html);
285
 
286
        return array($out_html, $out_js, $out_css);
392 daniel-mar 287
}
288
 
1130 daniel-mar 289
/**
290
 * @param string $password
291
 * @param bool $raw_output
292
 * @return string
293
 * @throws Exception
294
 */
295
function sha3_512(string $password, bool $raw_output=false): string {
1103 daniel-mar 296
        if (hash_supported_natively('sha3-512')) {
400 daniel-mar 297
                return hash('sha3-512', $password, $raw_output);
392 daniel-mar 298
        } else {
1021 daniel-mar 299
                return \bb\Sha3\Sha3::hash($password, 512, $raw_output);
392 daniel-mar 300
        }
301
}
486 daniel-mar 302
 
1130 daniel-mar 303
/**
304
 * @param string $message
305
 * @param string $key
306
 * @param bool $raw_output
307
 * @return string
308
 */
309
function sha3_512_hmac(string $message, string $key, bool $raw_output=false): string {
710 daniel-mar 310
        // RFC 2104 HMAC
1103 daniel-mar 311
        if (hash_hmac_supported_natively('sha3-512')) {
847 daniel-mar 312
                return hash_hmac('sha3-512', $message, $key, $raw_output);
313
        } else {
1021 daniel-mar 314
                return \bb\Sha3\Sha3::hash_hmac($message, $key, 512, $raw_output);
847 daniel-mar 315
        }
710 daniel-mar 316
}
317
 
1130 daniel-mar 318
/**
319
 * @param string $password
320
 * @param string $salt
321
 * @param int $iterations
322
 * @param int $length
323
 * @param bool $binary
324
 * @return string
325
 */
326
function sha3_512_pbkdf2(string $password, string $salt, int $iterations, int $length=0, bool $binary=false): string {
1103 daniel-mar 327
        if (hash_pbkdf2_supported_natively('sha3-512')) {
328
                return hash_pbkdf2('sha3-512', $password, $salt, $iterations, $length, $binary);
329
        } else {
330
                return \bb\Sha3\Sha3::hash_pbkdf2($password, $salt, $iterations, 512, $length, $binary);
331
        }
332
}
333
 
1130 daniel-mar 334
/**
335
 * @param string $url
336
 * @param string $userAgent
337
 * @return string|false
338
 */
339
function url_get_contents(string $url, string $userAgent='ViaThinkSoft-OIDplus/2.0') {
653 daniel-mar 340
        if (function_exists('curl_init')) {
341
                $ch = curl_init();
1050 daniel-mar 342
                if (class_exists(OIDplus::class)) {
698 daniel-mar 343
                        if (ini_get('curl.cainfo') == '') curl_setopt($ch, CURLOPT_CAINFO, OIDplus::localpath() . 'vendor/cacert.pem');
344
                }
653 daniel-mar 345
                curl_setopt($ch, CURLOPT_URL, $url);
963 daniel-mar 346
                curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
653 daniel-mar 347
                curl_setopt($ch, CURLOPT_POST, 0);
348
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
349
                curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
350
                curl_setopt($ch, CURLOPT_AUTOREFERER, true);
1006 daniel-mar 351
                $res = @curl_exec($ch);
963 daniel-mar 352
                $error_code = @curl_getinfo($ch, CURLINFO_HTTP_CODE);
1006 daniel-mar 353
                @curl_close($ch);
963 daniel-mar 354
                if ($error_code >= 400) return false;
1006 daniel-mar 355
                if ($res === false) return false;
653 daniel-mar 356
        } else {
963 daniel-mar 357
                // Attention: HTTPS only works if OpenSSL extension is enabled.
358
                // Our supplement does not help...
716 daniel-mar 359
                $opts = [
360
                        "http" => [
361
                                "method" => "GET",
963 daniel-mar 362
                                "header" => "User-Agent: $userAgent\r\n"
716 daniel-mar 363
                        ]
364
                ];
365
                $context = stream_context_create($opts);
366
                $res = @file_get_contents($url, false, $context);
653 daniel-mar 367
                if ($res === false) return false;
368
        }
369
        return $res;
660 daniel-mar 370
}
778 daniel-mar 371
 
1130 daniel-mar 372
/**
373
 * @return string
374
 */
375
function getSortedQuery(): string {
778 daniel-mar 376
        // https://stackoverflow.com/a/51777249/488539
377
        $url = [];
378
        parse_str($_SERVER['QUERY_STRING'], $url);
379
        ksort($url);
380
        return http_build_query($url);
381
}