Subversion Repositories php_utils

Rev

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

  1. <?php
  2.  
  3. /*
  4.  * OpenSSL php functions implemented using phpseclib
  5.  * Copyright 2022 Daniel Marschall, ViaThinkSoft
  6.  * Version 2022-04-09
  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.  
  55.                         if ($algo == OPENSSL_KEYTYPE_RSA) {
  56.                                 $private = \phpseclib3\Crypt\RSA::createKey($bits);
  57.                         } else {
  58.                                 throw new Exception("Algo not implemented");
  59.                         }
  60.  
  61.                         $private = $private->withPadding(\phpseclib3\Crypt\RSA::ENCRYPTION_PKCS1 | \phpseclib3\Crypt\RSA::SIGNATURE_PKCS1);
  62.  
  63.                         $public = $private->getPublicKey()->withPadding(\phpseclib3\Crypt\RSA::ENCRYPTION_PKCS1 | \phpseclib3\Crypt\RSA::SIGNATURE_PKCS1);
  64.  
  65.                         return array($algo, $bits, $private, $public);
  66.                 } catch (Exception $e) {
  67.                         global $openssl_supplement_last_error;
  68.                         $openssl_supplement_last_error = $e->getMessage();
  69.                         return false;
  70.                 }
  71.         }
  72.  
  73.         function openssl_pkey_export($res, &$privKey, $passphrase = null, $options = null) {
  74.                 try {
  75.                         if (!is_null($passphrase)) throw new Exception("passphrase not implemented");
  76.                         //if (!is_null($options)) throw new Exception("options not implemented");
  77.                         $privKey = $res[2]."";
  78.                         return true;
  79.                 } catch (Exception $e) {
  80.                         global $openssl_supplement_last_error;
  81.                         $openssl_supplement_last_error = $e->getMessage();
  82.                         return false;
  83.                 }
  84.         }
  85.  
  86.         function openssl_pkey_get_details($res) {
  87.                 return array(
  88.                         "bits" => $res[1],
  89.                         "key" => $res[3]."",
  90.                         "type" => $res[0]
  91.                 );
  92.         }
  93.  
  94.         function openssl_public_encrypt($data, &$encrypted, $pubKey) {
  95.                 try {
  96.                         if (is_string($pubKey)) $pubKey = openssl_pkey_get_public($pubKey);
  97.                         $encrypted = $pubKey->encrypt($data);
  98.                         return true;
  99.                 } catch (Exception $e) {
  100.                         global $openssl_supplement_last_error;
  101.                         $openssl_supplement_last_error = $e->getMessage();
  102.                         return false;
  103.                 }
  104.         }
  105.  
  106.         function openssl_private_decrypt($encrypted, &$decrypted, $privKey) {
  107.                 try {
  108.                         if (is_string($privKey)) $privKey = openssl_pkey_get_private($privKey);
  109.                         $decrypted = $privKey->decrypt($encrypted);
  110.                         return true;
  111.                 } catch (Exception $e) {
  112.                         global $openssl_supplement_last_error;
  113.                         $openssl_supplement_last_error = $e->getMessage();
  114.                         return false;
  115.                 }
  116.         }
  117.  
  118.         function openssl_verify($msg, $signature, $public, $algorithm=OPENSSL_ALGO_SHA1) {
  119.                 try {
  120.                         if ($algorithm == OPENSSL_ALGO_SHA1) $algorithm = 'SHA1';
  121.                         if ($algorithm == OPENSSL_ALGO_SHA224) $algorithm = 'SHA224';
  122.                         if ($algorithm == OPENSSL_ALGO_SHA256) $algorithm = 'SHA256)';
  123.                         if ($algorithm == OPENSSL_ALGO_SHA384) $algorithm = 'SHA384';
  124.                         if ($algorithm == OPENSSL_ALGO_SHA512) $algorithm = 'SHA512';
  125.                         if ($algorithm == OPENSSL_ALGO_RMD160) $algorithm = 'RMD160';
  126.                         if ($algorithm == OPENSSL_ALGO_MD5) $algorithm = 'MD5';
  127.                         if ($algorithm == OPENSSL_ALGO_MD4) $algorithm = 'MD4';
  128.                         if (is_string($public)) $public = openssl_pkey_get_public($public);
  129.                         return $public->withHash($algorithm)->verify($msg, $signature) ? 1 : 0;
  130.                 } catch (Exception $e) {
  131.                         global $openssl_supplement_last_error;
  132.                         $openssl_supplement_last_error = $e->getMessage();
  133.                         return false;
  134.                 }
  135.         }
  136.  
  137.         function openssl_sign($msg, &$signature, $private, $algorithm=OPENSSL_ALGO_SHA1) {
  138.                 try {
  139.                         if ($algorithm == OPENSSL_ALGO_SHA1) $algorithm = 'SHA1';
  140.                         if ($algorithm == OPENSSL_ALGO_SHA224) $algorithm = 'SHA224';
  141.                         if ($algorithm == OPENSSL_ALGO_SHA256) $algorithm = 'SHA256)';
  142.                         if ($algorithm == OPENSSL_ALGO_SHA384) $algorithm = 'SHA384';
  143.                         if ($algorithm == OPENSSL_ALGO_SHA512) $algorithm = 'SHA512';
  144.                         if ($algorithm == OPENSSL_ALGO_RMD160) $algorithm = 'RMD160';
  145.                         if ($algorithm == OPENSSL_ALGO_MD5) $algorithm = 'MD5';
  146.                         if ($algorithm == OPENSSL_ALGO_MD4) $algorithm = 'MD4';
  147.                         if (is_string($private)) $private = openssl_pkey_get_private($private);
  148.                         $signature = $private->withHash($algorithm)->sign($msg);
  149.                         return true;
  150.                 } catch (Exception $e) {
  151.                         global $openssl_supplement_last_error;
  152.                         $openssl_supplement_last_error = $e->getMessage();
  153.                         return false;
  154.                 }
  155.         }
  156.  
  157.         function openssl_error_string() {
  158.                 global $openssl_supplement_last_error;
  159.                 return $openssl_supplement_last_error;
  160.         }
  161.  
  162.         function openssl_random_pseudo_bytes($len) {
  163.                 /*
  164.                 if (function_exists('openssl_random_pseudo_bytes')) {
  165.                         $a = openssl_random_pseudo_bytes($len);
  166.                         if ($a) return $a;
  167.                 }
  168.                 */
  169.  
  170.                 if (function_exists('mcrypt_create_iv')) {
  171.                         $a = bin2hex(mcrypt_create_iv($len, MCRYPT_DEV_URANDOM));
  172.                         if ($a) return $a;
  173.                 }
  174.  
  175.                 if (function_exists('random_bytes')) {
  176.                         $a = random_bytes($len);
  177.                         if ($a) return $a;
  178.                 }
  179.  
  180.                 // Fallback to non-secure RNG
  181.                 $a = '';
  182.                 while (strlen($a) < $len*2) {
  183.                         $a .= sha1(uniqid((string)mt_rand(), true));
  184.                 }
  185.                 $a = substr($a, 0, $len*2);
  186.                 return hex2bin($a);
  187.         }
  188.  
  189.         function openssl_encrypt($data, $cipher_algo, $passphrase, $options=0, $iv="", &$tag=null, $aad="", $tag_length=16) {
  190.                 try {
  191.                         if (!is_null($tag)) throw new Exception("tag not implemented");
  192.                         if ($aad != "") throw new Exception("aad not implemented");
  193.                         if ($tag_length != 16) throw new Exception("tag_length not implemented");
  194.                         if (!preg_match('@AES\\-(.+)\\-(.+)@i', $cipher_algo, $m)) throw new Exception("Algo not implemented");
  195.                         if (($options & OPENSSL_ZERO_PADDING) != 0) throw new Exception("OPENSSL_ZERO_PADDING not implemented");
  196.                         $aes = new \phpseclib3\Crypt\AES($m[2]);
  197.                         $aes->setKeyLength($m[1]);
  198.                         $passphrase = substr($passphrase, 0, $m[1]/8);
  199.                         $passphrase = str_pad($passphrase, $m[1]/8, "\0", STR_PAD_RIGHT);
  200.                         $aes->setKey($passphrase);
  201.                         $aes->setIV($iv);
  202.                         $res = $aes->encrypt($data);
  203.                         if (($options & OPENSSL_RAW_DATA) == 0) $res = base64_encode($res);
  204.                         return $res;
  205.                 } catch (Exception $e) {
  206.                         global $openssl_supplement_last_error;
  207.                         $openssl_supplement_last_error = $e->getMessage();
  208.                         return false;
  209.                 }
  210.         }
  211.  
  212.         function openssl_decrypt($data, $cipher_algo, $passphrase, $options=0, $iv="", $tag=null, $aad="") {
  213.                 try {
  214.                         if (!is_null($tag)) throw new Exception("tag not implemented");
  215.                         if ($aad != "") throw new Exception("aad not implemented");
  216.                         if (!preg_match('@AES\\-(.+)\\-(.+)@i', $cipher_algo, $m)) throw new Exception("Algo not implemented");
  217.                         if (($options & OPENSSL_ZERO_PADDING) != 0) throw new Exception("OPENSSL_ZERO_PADDING not implemented");
  218.                         $aes = new \phpseclib3\Crypt\AES($m[2]);
  219.                         $aes->setKeyLength($m[1]);
  220.                         $passphrase = substr($passphrase, 0, $m[1]/8);
  221.                         $passphrase = str_pad($passphrase, $m[1]/8, "\0", STR_PAD_RIGHT);
  222.                         $aes->setKey($passphrase);
  223.                         $aes->setIV($iv);
  224.                         if (($options & OPENSSL_RAW_DATA) == 0) $data = base64_decode($data);
  225.                         return $aes->decrypt($data);
  226.                 } catch (Exception $e) {
  227.                         global $openssl_supplement_last_error;
  228.                         $openssl_supplement_last_error = $e->getMessage();
  229.                         return false;
  230.                 }
  231.         }
  232.  
  233.         function openssl_free_key($key) {
  234.                 // does nothing
  235.         }
  236.  
  237.         function openssl_pkey_get_private($key, $passphrase=null) {
  238.                 try {
  239.                         if (substr($key,0,7) === 'file://') {
  240.                                 if (!file_exists($file = substr($key, 7))) throw new Exception("file not found");
  241.                                 $key = file_get_contents($file);
  242.                         }
  243.                         if (is_null($passphrase)) $passphrase = false;
  244.                         $privKey = \phpseclib3\Crypt\RSA::load($key, $passphrase);
  245.                         return $privKey->withPadding(\phpseclib3\Crypt\RSA::ENCRYPTION_PKCS1 | \phpseclib3\Crypt\RSA::SIGNATURE_PKCS1); /** @phpstan-ignore-line */ // Call to an undefined method phpseclib3\Crypt\Common\AsymmetricKey::withPadding().
  246.                 } catch (Exception $e) {
  247.                         global $openssl_supplement_last_error;
  248.                         $openssl_supplement_last_error = $e->getMessage();
  249.                         return false;
  250.                 }
  251.         }
  252.  
  253.         function openssl_pkey_get_public($public_key) {
  254.                 try {
  255.                         if (substr($public_key,0,7) === 'file://') {
  256.                                 if (!file_exists($file = substr($public_key, 7))) throw new Exception("file not found");
  257.                                 $public_key = file_get_contents($file);
  258.                         }
  259.                         $pubKey = \phpseclib3\Crypt\RSA::load($public_key);
  260.                         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().
  261.                 } catch (Exception $e) {
  262.                         global $openssl_supplement_last_error;
  263.                         $openssl_supplement_last_error = $e->getMessage();
  264.                         return false;
  265.                 }
  266.         }
  267.  
  268. }
  269.