From 8f7abc69e3ff8176d2c678fb81fe6a4b2267a875 Mon Sep 17 00:00:00 2001 From: Adam Tulloss Date: Sat, 7 Oct 2023 15:40:19 -0400 Subject: [PATCH 01/13] Added pure PHP implementation of RC4, and parameter to use to fall back on it. This resolves the issue in #14. --- .gitignore | 1 + src/FpdiProtection.php | 84 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index ca3b886..48f40a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ vendor .idea composer.phar +.DS_Store \ No newline at end of file diff --git a/src/FpdiProtection.php b/src/FpdiProtection.php index 961dc8e..74cca21 100644 --- a/src/FpdiProtection.php +++ b/src/FpdiProtection.php @@ -193,6 +193,11 @@ class FpdiProtection extends \setasign\Fpdi\Fpdi */ protected $fileIdentifier; + /** + * @var bool + */ + protected $bad_openssl; + /** * FpdiProtection constructor. * @@ -200,17 +205,19 @@ class FpdiProtection extends \setasign\Fpdi\Fpdi * @param string $unit * @param string $size */ - public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4') + public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4', $bad_openssl = false) { parent::__construct($orientation, $unit, $size); + $this->bad_openssl = $bad_openssl; $randomBytes = function_exists('random_bytes') ? \random_bytes(32) : \mt_rand(); $this->fileIdentifier = md5(__FILE__ . PHP_SAPI . PHP_VERSION . $randomBytes, true); - if (!function_exists('openssl_encrypt') || !in_array('rc4-40', openssl_get_cipher_methods(), true)) { + if (!$bad_openssl && !function_exists('openssl_encrypt') || !in_array('rc4-40', openssl_get_cipher_methods(), true)) { throw new \RuntimeException( 'OpenSSL with RC4 supported is required. In case you use OpenSSL 3 make sure that ' . - 'legacy providers are loaded (see https://wiki.openssl.org/index.php/OpenSSL_3.0#Providers).' + 'legacy providers are loaded (see https://wiki.openssl.org/index.php/OpenSSL_3.0#Providers).' . + 'Pass $bad_openssl as true to fallback on a pure PHP implementation.' ); } } @@ -509,6 +516,9 @@ protected function computeUValue($encryptionKey) */ protected function arcfour($key, $data) { + if($this->bad_openssl) { + return $this->rc4($key, $data); + } return openssl_encrypt($data, 'RC4-40', $key, OPENSSL_RAW_DATA, ''); } @@ -646,4 +656,72 @@ protected function _puttrailer() $this->_put('/ID [<' . $fileIdentifier . '><' . $fileIdentifier . '>]'); } } + + /* + * Copyright 2011 Michael Cutler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + /** + * A PHP implementation of RC4 based on the original C code from + * the 1994 usenet post: + * + * http://groups.google.com/groups?selm=sternCvKL4B.Hyy@netcom.com + * + * @param string $key_str the key as a binary string + * @param string $data_str the data to decrypt/encrypt as a binary string + * @return string the result of the RC4 as a binary string + * @author Michael Cutler + */ + private function rc4($key_str, $data_str) + { + // convert input string(s) to array(s) + $key = array(); + $data = array(); + for ($i = 0; $i < strlen($key_str); $i++) { + $key[] = ord($key_str[$i]); + } + for ($i = 0; $i < strlen($data_str); $i++) { + $data[] = ord($data_str[$i]); + } + // prepare key + $state = range(0, 255); + $len = count($key); + $index1 = $index2 = 0; + for ($counter = 0; $counter < 256; $counter++) { + $index2 = ($key[$index1] + $state[$counter] + $index2) % 256; + $tmp = $state[$counter]; + $state[$counter] = $state[$index2]; + $state[$index2] = $tmp; + $index1 = ($index1 + 1) % $len; + } + // rc4 + $len = count($data); + $x = $y = 0; + for ($counter = 0; $counter < $len; $counter++) { + $x = ($x + 1) % 256; + $y = ($state[$x] + $y) % 256; + $tmp = $state[$x]; + $state[$x] = $state[$y]; + $state[$y] = $tmp; + $data[$counter] ^= $state[($state[$x] + $state[$y]) % 256]; + } + // convert output back to a string + $data_str = ""; + for ($i = 0; $i < $len; $i++) { + $data_str .= chr($data[$i]); + } + return $data_str; + } } From 008ca1691e86e4c0a433824e42f0abfbbbfbec08 Mon Sep 17 00:00:00 2001 From: Adam Tulloss Date: Mon, 9 Oct 2023 13:06:35 -0400 Subject: [PATCH 02/13] Adjusted to only allow use of fallback if PHP version is incompatible with OpenSSL, or OpenSSL does not support RC4. Uses setasign implementation of RC4 --- src/FpdiProtection.php | 118 +++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 76 deletions(-) diff --git a/src/FpdiProtection.php b/src/FpdiProtection.php index 74cca21..fff8f40 100644 --- a/src/FpdiProtection.php +++ b/src/FpdiProtection.php @@ -196,7 +196,7 @@ class FpdiProtection extends \setasign\Fpdi\Fpdi /** * @var bool */ - protected $bad_openssl; + protected $useArcfourFallback = false; /** * FpdiProtection constructor. @@ -205,19 +205,24 @@ class FpdiProtection extends \setasign\Fpdi\Fpdi * @param string $unit * @param string $size */ - public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4', $bad_openssl = false) + public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4', $useArcfourFallback = false) { parent::__construct($orientation, $unit, $size); - $this->bad_openssl = $bad_openssl; $randomBytes = function_exists('random_bytes') ? \random_bytes(32) : \mt_rand(); $this->fileIdentifier = md5(__FILE__ . PHP_SAPI . PHP_VERSION . $randomBytes, true); - if (!$bad_openssl && !function_exists('openssl_encrypt') || !in_array('rc4-40', openssl_get_cipher_methods(), true)) { + if ($useArcfourFallback && extension_loaded('openssl')) { + $supportsRc4 = in_array('rc4-40', openssl_get_cipher_methods(), true); + $this->useArcfourFallback = ((OPENSSL_VERSION_NUMBER >= 0x10100000) && PHP_VERSION_ID < 80000) || !$supportsRc4; + if($this->useArcfourFallback) return; + } + + if (!function_exists('openssl_encrypt') || !in_array('rc4-40', openssl_get_cipher_methods(), true)) { throw new \RuntimeException( 'OpenSSL with RC4 supported is required. In case you use OpenSSL 3 make sure that ' . 'legacy providers are loaded (see https://wiki.openssl.org/index.php/OpenSSL_3.0#Providers).' . - 'Pass $bad_openssl as true to fallback on a pure PHP implementation.' + 'If ' ); } } @@ -516,10 +521,39 @@ protected function computeUValue($encryptionKey) */ protected function arcfour($key, $data) { - if($this->bad_openssl) { - return $this->rc4($key, $data); + if (!$this->useArcfourFallback) { + return openssl_encrypt($data, 'rc4-40', $key, OPENSSL_RAW_DATA, ''); + } + + static $_lastRc4Key = null, $_lastRc4KeyValue = null; + + if ($_lastRc4Key !== $key) { + $k = str_repeat($key, (int)(256 / strlen($key) + 1)); + $rc4 = range(0, 255); + $j = 0; + for ($i = 0; $i < 256; $i++) { + $rc4[$i] = $rc4[$j = ($j + ($t = $rc4[$i]) + ord($k[$i])) % 256]; + $rc4[$j] = $t; + } + $_lastRc4Key = $key; + $_lastRc4KeyValue = $rc4; + + } else { + $rc4 = $_lastRc4KeyValue; } - return openssl_encrypt($data, 'RC4-40', $key, OPENSSL_RAW_DATA, ''); + + $len = strlen($data); + $newData = ''; + $a = 0; + $b = 0; + for ($i = 0; $i < $len; $i++) { + $b = ($b + ($t = $rc4[$a = ($a + 1) % 256])) % 256; + $rc4[$a] = $rc4[$b]; + $rc4[$b] = $t; + $newData .= chr(ord($data[$i]) ^ $rc4[($rc4[$a] + $rc4[$b]) % 256]); + } + + return $newData; } /** @@ -656,72 +690,4 @@ protected function _puttrailer() $this->_put('/ID [<' . $fileIdentifier . '><' . $fileIdentifier . '>]'); } } - - /* - * Copyright 2011 Michael Cutler - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - /** - * A PHP implementation of RC4 based on the original C code from - * the 1994 usenet post: - * - * http://groups.google.com/groups?selm=sternCvKL4B.Hyy@netcom.com - * - * @param string $key_str the key as a binary string - * @param string $data_str the data to decrypt/encrypt as a binary string - * @return string the result of the RC4 as a binary string - * @author Michael Cutler - */ - private function rc4($key_str, $data_str) - { - // convert input string(s) to array(s) - $key = array(); - $data = array(); - for ($i = 0; $i < strlen($key_str); $i++) { - $key[] = ord($key_str[$i]); - } - for ($i = 0; $i < strlen($data_str); $i++) { - $data[] = ord($data_str[$i]); - } - // prepare key - $state = range(0, 255); - $len = count($key); - $index1 = $index2 = 0; - for ($counter = 0; $counter < 256; $counter++) { - $index2 = ($key[$index1] + $state[$counter] + $index2) % 256; - $tmp = $state[$counter]; - $state[$counter] = $state[$index2]; - $state[$index2] = $tmp; - $index1 = ($index1 + 1) % $len; - } - // rc4 - $len = count($data); - $x = $y = 0; - for ($counter = 0; $counter < $len; $counter++) { - $x = ($x + 1) % 256; - $y = ($state[$x] + $y) % 256; - $tmp = $state[$x]; - $state[$x] = $state[$y]; - $state[$y] = $tmp; - $data[$counter] ^= $state[($state[$x] + $state[$y]) % 256]; - } - // convert output back to a string - $data_str = ""; - for ($i = 0; $i < $len; $i++) { - $data_str .= chr($data[$i]); - } - return $data_str; - } } From 5005ff1696e35356c907ad8a89a043e0d0f3828f Mon Sep 17 00:00:00 2001 From: Adam Tulloss <35859316+artulloss@users.noreply.github.com> Date: Tue, 10 Oct 2023 09:50:17 -0400 Subject: [PATCH 03/13] Change OpenSSL version checking, and allow fallback to be forcefully enabled regardless of extension status. --- src/FpdiProtection.php | 65 +++++++++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/src/FpdiProtection.php b/src/FpdiProtection.php index fff8f40..d18a53d 100644 --- a/src/FpdiProtection.php +++ b/src/FpdiProtection.php @@ -196,7 +196,7 @@ class FpdiProtection extends \setasign\Fpdi\Fpdi /** * @var bool */ - protected $useArcfourFallback = false; + protected $useArcfourFallback; /** * FpdiProtection constructor. @@ -211,18 +211,13 @@ public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4', $use $randomBytes = function_exists('random_bytes') ? \random_bytes(32) : \mt_rand(); $this->fileIdentifier = md5(__FILE__ . PHP_SAPI . PHP_VERSION . $randomBytes, true); + $this->useArcfourFallback = $useArcfourFallback; - if ($useArcfourFallback && extension_loaded('openssl')) { - $supportsRc4 = in_array('rc4-40', openssl_get_cipher_methods(), true); - $this->useArcfourFallback = ((OPENSSL_VERSION_NUMBER >= 0x10100000) && PHP_VERSION_ID < 80000) || !$supportsRc4; - if($this->useArcfourFallback) return; - } - - if (!function_exists('openssl_encrypt') || !in_array('rc4-40', openssl_get_cipher_methods(), true)) { + if (!$useArcfourFallback && (!function_exists('openssl_encrypt') || !in_array('rc4-40', openssl_get_cipher_methods(), true) || !$this->isOpensslCompatible())) { throw new \RuntimeException( - 'OpenSSL with RC4 supported is required. In case you use OpenSSL 3 make sure that ' . - 'legacy providers are loaded (see https://wiki.openssl.org/index.php/OpenSSL_3.0#Providers).' . - 'If ' + 'OpenSSL with RC4 supported is required if $useArcfourFallback is false.' . + 'If using OpenSSL 3 make sure that legacy providers are loaded ' . + '(see https://wiki.openssl.org/index.php/OpenSSL_3.0#Providers).' . ); } } @@ -690,4 +685,52 @@ protected function _puttrailer() $this->_put('/ID [<' . $fileIdentifier . '><' . $fileIdentifier . '>]'); } } + + protected function isOpensslCompatible() { + $compatible = false; + if (PHP_VERSION_ID < 70100) { + // PHP 7.0 requires OpenSSL >= 0.9.8, < 1.2 + if (version_compare(get_openssl_version_number(), '0.9.8', '>=') && version_compare(get_openssl_version_number(), '1.2.0', '<')) { + $compatible = true; + } + } elseif (PHP_VERSION_ID < 80000) { + // PHP 7.1-8.0 requires OpenSSL >= 1.0.1, < 3.0 + if (version_compare(get_openssl_version_number(), '1.0.1', '>=') && version_compare(get_openssl_version_number(), '3.0.0', '<')) { + $compatible = true; + } + } elseif (PHP_VERSION_ID >= 80100) { + // PHP >= 8.1 requires OpenSSL >= 1.0.2, < 4.0 + if (version_compare(get_openssl_version_number(), '1.0.2', '>=') && version_compare(get_openssl_version_number(), '4.0.0', '<')) { + $compatible = true; + } + } + return $compatible; + } + + protected function getOpensslVersionNumber($patchAsNumber = false, $opensslVersionNumber = null) { + if (is_null($opensslVersionNumber)) $opensslVersionNumber = opensslVersionNumber; + $opensslVersionNumber = str_pad((string)dechex($opensslVersionNumber), 8, '0', STR_PAD_LEFT); + + $opensslVersionNumber = array(); + $preg = '/(?[[:xdigit:]])(?[[:xdigit:]][[:xdigit:]])(?[[:xdigit:]][[:xdigit:]])'; + $preg.= '(?[[:xdigit:]][[:xdigit:]])(?[[:xdigit:]])/'; + preg_match_all($preg, $opensslVersionNumber, $opensslVersionNumber); + $opensslVersion = false; + if (!empty($opensslVersionNumber)) { + $alphabet = array(1=>'a',2=>'b',3=>'c',4=>'d',5=>'e',6=>'f',7=>'g',8=>'h',9=>'i',10=>'j',11=>'k', + 12=>'l',13=>'m',14=>'n',15=>'o',16=>'p',17=>'q',18=>'r',19=>'s',20=>'t',21=>'u', + 22=>'v',23=>'w',24=>'x',25=>'y',26=>'z'); + $opensslVersion = intval($opensslVersionNumber['major'][0]).'.'; + $opensslVersion.= intval($opensslVersionNumber['minor'][0]).'.'; + $opensslVersion.= intval($opensslVersionNumber['fix'][0]); + $patchlevelDec = hexdec($opensslVersionNumber['patch'][0]); + if (!$patchAsNumber && array_key_exists($patchlevelDec, $alphabet)) { + $opensslVersion .= $alphabet[$patchlevelDec]; // ideal for text comparison + } + else { + $opensslVersion .= '.' . $patchlevelDec; // ideal for version_compare + } + } + return $opensslVersion; + } } From b25275326780de07728349f7b6e377bd97fe8976 Mon Sep 17 00:00:00 2001 From: Adam Tulloss <35859316+artulloss@users.noreply.github.com> Date: Tue, 10 Oct 2023 09:52:39 -0400 Subject: [PATCH 04/13] Fix syntax in message. --- src/FpdiProtection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FpdiProtection.php b/src/FpdiProtection.php index d18a53d..d9cd32c 100644 --- a/src/FpdiProtection.php +++ b/src/FpdiProtection.php @@ -217,7 +217,7 @@ public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4', $use throw new \RuntimeException( 'OpenSSL with RC4 supported is required if $useArcfourFallback is false.' . 'If using OpenSSL 3 make sure that legacy providers are loaded ' . - '(see https://wiki.openssl.org/index.php/OpenSSL_3.0#Providers).' . + '(see https://wiki.openssl.org/index.php/OpenSSL_3.0#Providers).' ); } } From 7f2ac9d605f2617b258b990b4c8334c0a6ecd4bd Mon Sep 17 00:00:00 2001 From: Adam Tulloss <35859316+artulloss@users.noreply.github.com> Date: Tue, 10 Oct 2023 09:56:37 -0400 Subject: [PATCH 05/13] Fix casing issue. --- src/FpdiProtection.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/FpdiProtection.php b/src/FpdiProtection.php index d9cd32c..1486d55 100644 --- a/src/FpdiProtection.php +++ b/src/FpdiProtection.php @@ -688,19 +688,20 @@ protected function _puttrailer() protected function isOpensslCompatible() { $compatible = false; + $opensslVersion = $this->getOpensslVersionNumber(); if (PHP_VERSION_ID < 70100) { // PHP 7.0 requires OpenSSL >= 0.9.8, < 1.2 - if (version_compare(get_openssl_version_number(), '0.9.8', '>=') && version_compare(get_openssl_version_number(), '1.2.0', '<')) { + if (version_compare($opensslVersion, '0.9.8', '>=') && version_compare($opensslVersion, '1.2.0', '<')) { $compatible = true; } } elseif (PHP_VERSION_ID < 80000) { // PHP 7.1-8.0 requires OpenSSL >= 1.0.1, < 3.0 - if (version_compare(get_openssl_version_number(), '1.0.1', '>=') && version_compare(get_openssl_version_number(), '3.0.0', '<')) { + if (version_compare($opensslVersion, '1.0.1', '>=') && version_compare($opensslVersion, '3.0.0', '<')) { $compatible = true; } } elseif (PHP_VERSION_ID >= 80100) { // PHP >= 8.1 requires OpenSSL >= 1.0.2, < 4.0 - if (version_compare(get_openssl_version_number(), '1.0.2', '>=') && version_compare(get_openssl_version_number(), '4.0.0', '<')) { + if (version_compare($opensslVersion, '1.0.2', '>=') && version_compare($opensslVersion, '4.0.0', '<')) { $compatible = true; } } From 88f9d65d183f7d60cb85002639d9c2c589a86277 Mon Sep 17 00:00:00 2001 From: Adam Tulloss <35859316+artulloss@users.noreply.github.com> Date: Tue, 10 Oct 2023 09:59:02 -0400 Subject: [PATCH 06/13] Fix const casing problem --- src/FpdiProtection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FpdiProtection.php b/src/FpdiProtection.php index 1486d55..f4a049a 100644 --- a/src/FpdiProtection.php +++ b/src/FpdiProtection.php @@ -709,7 +709,7 @@ protected function isOpensslCompatible() { } protected function getOpensslVersionNumber($patchAsNumber = false, $opensslVersionNumber = null) { - if (is_null($opensslVersionNumber)) $opensslVersionNumber = opensslVersionNumber; + if (is_null($opensslVersionNumber)) $opensslVersionNumber = OPENSSL_VERSION_NUMBER; $opensslVersionNumber = str_pad((string)dechex($opensslVersionNumber), 8, '0', STR_PAD_LEFT); $opensslVersionNumber = array(); From b8d60fbbcb2e22109c0517fd473fcaca697e72a5 Mon Sep 17 00:00:00 2001 From: Adam Tulloss Date: Tue, 10 Oct 2023 10:21:14 -0400 Subject: [PATCH 07/13] Fix function, got broken when changing casing to match project --- src/FpdiProtection.php | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/FpdiProtection.php b/src/FpdiProtection.php index f4a049a..8aa8484 100644 --- a/src/FpdiProtection.php +++ b/src/FpdiProtection.php @@ -519,9 +519,9 @@ protected function arcfour($key, $data) if (!$this->useArcfourFallback) { return openssl_encrypt($data, 'rc4-40', $key, OPENSSL_RAW_DATA, ''); } - + static $_lastRc4Key = null, $_lastRc4KeyValue = null; - + if ($_lastRc4Key !== $key) { $k = str_repeat($key, (int)(256 / strlen($key) + 1)); $rc4 = range(0, 255); @@ -532,11 +532,11 @@ protected function arcfour($key, $data) } $_lastRc4Key = $key; $_lastRc4KeyValue = $rc4; - + } else { $rc4 = $_lastRc4KeyValue; } - + $len = strlen($data); $newData = ''; $a = 0; @@ -547,7 +547,7 @@ protected function arcfour($key, $data) $rc4[$b] = $t; $newData .= chr(ord($data[$i]) ^ $rc4[($rc4[$a] + $rc4[$b]) % 256]); } - + return $newData; } @@ -710,26 +710,26 @@ protected function isOpensslCompatible() { protected function getOpensslVersionNumber($patchAsNumber = false, $opensslVersionNumber = null) { if (is_null($opensslVersionNumber)) $opensslVersionNumber = OPENSSL_VERSION_NUMBER; - $opensslVersionNumber = str_pad((string)dechex($opensslVersionNumber), 8, '0', STR_PAD_LEFT); - - $opensslVersionNumber = array(); + $opensslNumericIdentifier = str_pad((string)dechex($opensslVersionNumber),8,'0',STR_PAD_LEFT); + + $opensslVersionParsed = array(); $preg = '/(?[[:xdigit:]])(?[[:xdigit:]][[:xdigit:]])(?[[:xdigit:]][[:xdigit:]])'; $preg.= '(?[[:xdigit:]][[:xdigit:]])(?[[:xdigit:]])/'; - preg_match_all($preg, $opensslVersionNumber, $opensslVersionNumber); + preg_match_all($preg, $opensslNumericIdentifier, $opensslVersionParsed); $opensslVersion = false; - if (!empty($opensslVersionNumber)) { + if (!empty($opensslVersionParsed)) { $alphabet = array(1=>'a',2=>'b',3=>'c',4=>'d',5=>'e',6=>'f',7=>'g',8=>'h',9=>'i',10=>'j',11=>'k', 12=>'l',13=>'m',14=>'n',15=>'o',16=>'p',17=>'q',18=>'r',19=>'s',20=>'t',21=>'u', 22=>'v',23=>'w',24=>'x',25=>'y',26=>'z'); - $opensslVersion = intval($opensslVersionNumber['major'][0]).'.'; - $opensslVersion.= intval($opensslVersionNumber['minor'][0]).'.'; - $opensslVersion.= intval($opensslVersionNumber['fix'][0]); - $patchlevelDec = hexdec($opensslVersionNumber['patch'][0]); + $opensslVersion = intval($opensslVersionParsed['major'][0]).'.'; + $opensslVersion.= intval($opensslVersionParsed['minor'][0]).'.'; + $opensslVersion.= intval($opensslVersionParsed['fix'][0]); + $patchlevelDec = hexdec($opensslVersionParsed['patch'][0]); if (!$patchAsNumber && array_key_exists($patchlevelDec, $alphabet)) { - $opensslVersion .= $alphabet[$patchlevelDec]; // ideal for text comparison + $opensslVersion.= $alphabet[$patchlevelDec]; // ideal for text comparison } else { - $opensslVersion .= '.' . $patchlevelDec; // ideal for version_compare + $opensslVersion.= '.'.$patchlevelDec; // ideal for version_compare } } return $opensslVersion; From 03c78ffe9409d57fdf6f2804107e1fcdff7a6892 Mon Sep 17 00:00:00 2001 From: Adam Tulloss Date: Tue, 10 Oct 2023 11:05:40 -0400 Subject: [PATCH 08/13] Simpler version check, only for 7.1-8 --- src/FpdiProtection.php | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/src/FpdiProtection.php b/src/FpdiProtection.php index 8aa8484..3b22c29 100644 --- a/src/FpdiProtection.php +++ b/src/FpdiProtection.php @@ -209,11 +209,13 @@ public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4', $use { parent::__construct($orientation, $unit, $size); + // PHP 7.1-8.0 requires OpenSSL >= 1.0.1, < 3.0 + $isOpensslCompatibleWithPhp7 = PHP_VERSION_ID < 70100 || PHP_VERSION_ID > 80100 || OPENSSL_VERSION_NUMBER >= 0x10001010 && OPENSSL_VERSION_NUMBER < 0x30000000; $randomBytes = function_exists('random_bytes') ? \random_bytes(32) : \mt_rand(); $this->fileIdentifier = md5(__FILE__ . PHP_SAPI . PHP_VERSION . $randomBytes, true); $this->useArcfourFallback = $useArcfourFallback; - if (!$useArcfourFallback && (!function_exists('openssl_encrypt') || !in_array('rc4-40', openssl_get_cipher_methods(), true) || !$this->isOpensslCompatible())) { + if (!$useArcfourFallback && (!function_exists('openssl_encrypt') || !in_array('rc4-40', openssl_get_cipher_methods(), true) || !$isOpensslCompatibleWithPhp7)) { throw new \RuntimeException( 'OpenSSL with RC4 supported is required if $useArcfourFallback is false.' . 'If using OpenSSL 3 make sure that legacy providers are loaded ' . @@ -686,28 +688,6 @@ protected function _puttrailer() } } - protected function isOpensslCompatible() { - $compatible = false; - $opensslVersion = $this->getOpensslVersionNumber(); - if (PHP_VERSION_ID < 70100) { - // PHP 7.0 requires OpenSSL >= 0.9.8, < 1.2 - if (version_compare($opensslVersion, '0.9.8', '>=') && version_compare($opensslVersion, '1.2.0', '<')) { - $compatible = true; - } - } elseif (PHP_VERSION_ID < 80000) { - // PHP 7.1-8.0 requires OpenSSL >= 1.0.1, < 3.0 - if (version_compare($opensslVersion, '1.0.1', '>=') && version_compare($opensslVersion, '3.0.0', '<')) { - $compatible = true; - } - } elseif (PHP_VERSION_ID >= 80100) { - // PHP >= 8.1 requires OpenSSL >= 1.0.2, < 4.0 - if (version_compare($opensslVersion, '1.0.2', '>=') && version_compare($opensslVersion, '4.0.0', '<')) { - $compatible = true; - } - } - return $compatible; - } - protected function getOpensslVersionNumber($patchAsNumber = false, $opensslVersionNumber = null) { if (is_null($opensslVersionNumber)) $opensslVersionNumber = OPENSSL_VERSION_NUMBER; $opensslNumericIdentifier = str_pad((string)dechex($opensslVersionNumber),8,'0',STR_PAD_LEFT); From 474d3fac5e64dc255ab44685ecf5f343ca8f3506 Mon Sep 17 00:00:00 2001 From: Adam Tulloss Date: Thu, 12 Oct 2023 12:11:40 -0400 Subject: [PATCH 09/13] Update docblock, remove unused utility --- src/FpdiProtection.php | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/src/FpdiProtection.php b/src/FpdiProtection.php index 3b22c29..23ef967 100644 --- a/src/FpdiProtection.php +++ b/src/FpdiProtection.php @@ -204,12 +204,12 @@ class FpdiProtection extends \setasign\Fpdi\Fpdi * @param string $orientation * @param string $unit * @param string $size + * @param bool $useArcfourFallback */ public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4', $useArcfourFallback = false) { parent::__construct($orientation, $unit, $size); - // PHP 7.1-8.0 requires OpenSSL >= 1.0.1, < 3.0 $isOpensslCompatibleWithPhp7 = PHP_VERSION_ID < 70100 || PHP_VERSION_ID > 80100 || OPENSSL_VERSION_NUMBER >= 0x10001010 && OPENSSL_VERSION_NUMBER < 0x30000000; $randomBytes = function_exists('random_bytes') ? \random_bytes(32) : \mt_rand(); $this->fileIdentifier = md5(__FILE__ . PHP_SAPI . PHP_VERSION . $randomBytes, true); @@ -687,31 +687,4 @@ protected function _puttrailer() $this->_put('/ID [<' . $fileIdentifier . '><' . $fileIdentifier . '>]'); } } - - protected function getOpensslVersionNumber($patchAsNumber = false, $opensslVersionNumber = null) { - if (is_null($opensslVersionNumber)) $opensslVersionNumber = OPENSSL_VERSION_NUMBER; - $opensslNumericIdentifier = str_pad((string)dechex($opensslVersionNumber),8,'0',STR_PAD_LEFT); - - $opensslVersionParsed = array(); - $preg = '/(?[[:xdigit:]])(?[[:xdigit:]][[:xdigit:]])(?[[:xdigit:]][[:xdigit:]])'; - $preg.= '(?[[:xdigit:]][[:xdigit:]])(?[[:xdigit:]])/'; - preg_match_all($preg, $opensslNumericIdentifier, $opensslVersionParsed); - $opensslVersion = false; - if (!empty($opensslVersionParsed)) { - $alphabet = array(1=>'a',2=>'b',3=>'c',4=>'d',5=>'e',6=>'f',7=>'g',8=>'h',9=>'i',10=>'j',11=>'k', - 12=>'l',13=>'m',14=>'n',15=>'o',16=>'p',17=>'q',18=>'r',19=>'s',20=>'t',21=>'u', - 22=>'v',23=>'w',24=>'x',25=>'y',26=>'z'); - $opensslVersion = intval($opensslVersionParsed['major'][0]).'.'; - $opensslVersion.= intval($opensslVersionParsed['minor'][0]).'.'; - $opensslVersion.= intval($opensslVersionParsed['fix'][0]); - $patchlevelDec = hexdec($opensslVersionParsed['patch'][0]); - if (!$patchAsNumber && array_key_exists($patchlevelDec, $alphabet)) { - $opensslVersion.= $alphabet[$patchlevelDec]; // ideal for text comparison - } - else { - $opensslVersion.= '.'.$patchlevelDec; // ideal for version_compare - } - } - return $opensslVersion; - } } From 992c9f50d1f87155211c9f94a381c6e027bda35d Mon Sep 17 00:00:00 2001 From: Adam Tulloss Date: Thu, 12 Oct 2023 12:30:24 -0400 Subject: [PATCH 10/13] Simplified conditions for checking openssl incompatibility --- src/FpdiProtection.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/FpdiProtection.php b/src/FpdiProtection.php index 23ef967..016bb9e 100644 --- a/src/FpdiProtection.php +++ b/src/FpdiProtection.php @@ -210,18 +210,19 @@ public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4', $use { parent::__construct($orientation, $unit, $size); - $isOpensslCompatibleWithPhp7 = PHP_VERSION_ID < 70100 || PHP_VERSION_ID > 80100 || OPENSSL_VERSION_NUMBER >= 0x10001010 && OPENSSL_VERSION_NUMBER < 0x30000000; + $incompatibleOpenssl = OPENSSL_VERSION_NUMBER >= 0x30000000 && (PHP_VERSION_ID < 80200 || PHP_VERSION_ID >= 70100); $randomBytes = function_exists('random_bytes') ? \random_bytes(32) : \mt_rand(); $this->fileIdentifier = md5(__FILE__ . PHP_SAPI . PHP_VERSION . $randomBytes, true); $this->useArcfourFallback = $useArcfourFallback; - if (!$useArcfourFallback && (!function_exists('openssl_encrypt') || !in_array('rc4-40', openssl_get_cipher_methods(), true) || !$isOpensslCompatibleWithPhp7)) { - throw new \RuntimeException( - 'OpenSSL with RC4 supported is required if $useArcfourFallback is false.' . - 'If using OpenSSL 3 make sure that legacy providers are loaded ' . - '(see https://wiki.openssl.org/index.php/OpenSSL_3.0#Providers).' - ); - } + if($useArcfourFallback) return; + if(function_exists('openssl_encrypt') && in_array('rc4-40', openssl_get_cipher_methods(), true) && !$incompatibleOpenssl) return; + + throw new \RuntimeException( + 'OpenSSL with RC4 supported is required if $useArcfourFallback is false.' . + 'If using OpenSSL 3 make sure that legacy providers are loaded ' . + '(see https://wiki.openssl.org/index.php/OpenSSL_3.0#Providers).' + ); } /** From 4ff1b7973e2651858859e89cb6aeea42c9b5dbd1 Mon Sep 17 00:00:00 2001 From: Adam Tulloss Date: Fri, 13 Oct 2023 08:40:26 -0400 Subject: [PATCH 11/13] Seperate error message if PHP version is incompatible --- src/FpdiProtection.php | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/FpdiProtection.php b/src/FpdiProtection.php index 016bb9e..142b87e 100644 --- a/src/FpdiProtection.php +++ b/src/FpdiProtection.php @@ -210,19 +210,28 @@ public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4', $use { parent::__construct($orientation, $unit, $size); - $incompatibleOpenssl = OPENSSL_VERSION_NUMBER >= 0x30000000 && (PHP_VERSION_ID < 80200 || PHP_VERSION_ID >= 70100); $randomBytes = function_exists('random_bytes') ? \random_bytes(32) : \mt_rand(); $this->fileIdentifier = md5(__FILE__ . PHP_SAPI . PHP_VERSION . $randomBytes, true); $this->useArcfourFallback = $useArcfourFallback; if($useArcfourFallback) return; - if(function_exists('openssl_encrypt') && in_array('rc4-40', openssl_get_cipher_methods(), true) && !$incompatibleOpenssl) return; - throw new \RuntimeException( - 'OpenSSL with RC4 supported is required if $useArcfourFallback is false.' . - 'If using OpenSSL 3 make sure that legacy providers are loaded ' . - '(see https://wiki.openssl.org/index.php/OpenSSL_3.0#Providers).' - ); + if(OPENSSL_VERSION_NUMBER >= 0x30000000 && (PHP_VERSION_ID < 80200 || PHP_VERSION_ID >= 70100)) { + throw new \RuntimeException( + 'OpenSSL 3 is not supported with PHP versions < 8.2.0 and >= 7.1.0. ' . + 'You\'re using PHP ' . PHP_VERSION . ' with ' . OPENSSL_VERSION_TEXT . '. ' . + 'Please fix your PHP installation or set $useArcfourFallback to true to use a slower fallback implementation.' + ); + } + + if(function_exists('openssl_encrypt') && in_array('rc4-40', openssl_get_cipher_methods(), true)) { + throw new \RuntimeException( + 'OpenSSL with RC4 supported is required if $useArcfourFallback is false.' . + 'If using OpenSSL 3 make sure that legacy providers are loaded ' . + '(see https://wiki.openssl.org/index.php/OpenSSL_3.0#Providers).' + ); + } + } /** From ece8d62734427a6ea9a12c5164423135d092b888 Mon Sep 17 00:00:00 2001 From: Adam Tulloss Date: Fri, 13 Oct 2023 08:43:23 -0400 Subject: [PATCH 12/13] Flip condition, add space in error message. --- src/FpdiProtection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FpdiProtection.php b/src/FpdiProtection.php index 142b87e..fc95c6a 100644 --- a/src/FpdiProtection.php +++ b/src/FpdiProtection.php @@ -224,9 +224,9 @@ public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4', $use ); } - if(function_exists('openssl_encrypt') && in_array('rc4-40', openssl_get_cipher_methods(), true)) { + if(!(function_exists('openssl_encrypt') && in_array('rc4-40', openssl_get_cipher_methods(), true))) { throw new \RuntimeException( - 'OpenSSL with RC4 supported is required if $useArcfourFallback is false.' . + 'OpenSSL with RC4 supported is required if $useArcfourFallback is false. ' . 'If using OpenSSL 3 make sure that legacy providers are loaded ' . '(see https://wiki.openssl.org/index.php/OpenSSL_3.0#Providers).' ); From bd17ce7809e38ec96a6567a7b97051ab628a99df Mon Sep 17 00:00:00 2001 From: Adam Tulloss Date: Wed, 18 Oct 2023 08:59:23 -0400 Subject: [PATCH 13/13] Spacing adjustments, simplify and fix wrong version check --- src/FpdiProtection.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/FpdiProtection.php b/src/FpdiProtection.php index fc95c6a..656d747 100644 --- a/src/FpdiProtection.php +++ b/src/FpdiProtection.php @@ -214,17 +214,19 @@ public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4', $use $this->fileIdentifier = md5(__FILE__ . PHP_SAPI . PHP_VERSION . $randomBytes, true); $this->useArcfourFallback = $useArcfourFallback; - if($useArcfourFallback) return; + if ($useArcfourFallback) { + return; + } - if(OPENSSL_VERSION_NUMBER >= 0x30000000 && (PHP_VERSION_ID < 80200 || PHP_VERSION_ID >= 70100)) { + if (OPENSSL_VERSION_NUMBER >= 0x30000000 && PHP_VERSION_ID < 80100) { throw new \RuntimeException( - 'OpenSSL 3 is not supported with PHP versions < 8.2.0 and >= 7.1.0. ' . + 'OpenSSL 3 is not supported with PHP versions < 8.1.0. ' . 'You\'re using PHP ' . PHP_VERSION . ' with ' . OPENSSL_VERSION_TEXT . '. ' . 'Please fix your PHP installation or set $useArcfourFallback to true to use a slower fallback implementation.' ); } - if(!(function_exists('openssl_encrypt') && in_array('rc4-40', openssl_get_cipher_methods(), true))) { + if (!(function_exists('openssl_encrypt') && in_array('rc4-40', openssl_get_cipher_methods(), true))) { throw new \RuntimeException( 'OpenSSL with RC4 supported is required if $useArcfourFallback is false. ' . 'If using OpenSSL 3 make sure that legacy providers are loaded ' .