Subversion Repositories php_utils

Rev

Rev 30 | 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-10
  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.                         // TODO: Also support $pkey_config['encrypt_key'] and $pkey_config['encrypt_key_cipher'] ?
  56.  
  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 {
  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.                         }
  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);
  121.                         if (!is_object($pubKey) || !method_exists($pubKey,'encrypt'))
  122.                                 throw new Exception("Invalid input datatype");
  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);
  135.                         if (!is_object($privKey) || !method_exists($privKey,'decrypt'))
  136.                                 throw new Exception("Invalid input datatype");
  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);
  157.                         if (!is_object($public) || !method_exists($public,'verify'))
  158.                                 throw new Exception("Invalid input datatype");
  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);
  178.                         if (!is_object($private) || !method_exists($private,'sign'))
  179.                                 throw new Exception("Invalid input datatype");
  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);
  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().
  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. }
  301.