phpceclib 2.0.21

pull/72/head
Roland Gruber 4 years ago
parent c7c1d030e3
commit d73ffee957

@ -422,7 +422,7 @@ main license and authors:
lib/3rdParty/tcpdf D 2018 Nicola Asuni - Tecnick.com LTD
lib/3rdParty/tcpdf/fonts/DejaVu*.ttf A Public Domain, Bitstream, Inc., Tavmjong Bah
lib/3rdParty/tcpdf/fonts/DejaVu*.z A Public Domain, Bitstream, Inc., Tavmjong Bah
lib/3rdParty/phpseclib B Jim Wigginton
lib/3rdParty/phpseclib B 2019 TerraFrost and other contributors
lib/3rdParty/Monolog B 2011 Jordi Boggiano
lib/3rdParty/Psr B 2012 PHP Framework Interoperability Group
lib/3rdParty/yubico/Yubico.php C 2015 Yubico AB

@ -421,7 +421,7 @@ main license and authors:
lib/3rdParty/tcpdf D 2018 Nicola Asuni - Tecnick.com LTD
lib/3rdParty/tcpdf/fonts/DejaVu*.ttf A Public Domain, Bitstream, Inc., Tavmjong Bah
lib/3rdParty/tcpdf/fonts/DejaVu*.z A Public Domain, Bitstream, Inc., Tavmjong Bah
lib/3rdParty/phpseclib B Jim Wigginton
lib/3rdParty/phpseclib B 2019 TerraFrost and other contributors
lib/3rdParty/Monolog B 2011 Jordi Boggiano
lib/3rdParty/Psr B 2012 PHP Framework Interoperability Group
lib/3rdParty/yubico/Yubico.php C 2015 Yubico AB

@ -76,6 +76,10 @@ abstract class Base
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
*/
const MODE_CFB = 3;
/**
* Encrypt / decrypt using the Cipher Feedback mode (8bit)
*/
const MODE_CFB8 = 38;
/**
* Encrypt / decrypt using the Output Feedback mode.
*
@ -479,6 +483,7 @@ abstract class Base
break;
case self::MODE_CTR:
case self::MODE_CFB:
case self::MODE_CFB8:
case self::MODE_OFB:
case self::MODE_STREAM:
$this->mode = $mode;
@ -492,8 +497,8 @@ abstract class Base
$this->_setEngine();
// Determining whether inline crypting can be used by the cipher
if ($this->use_inline_crypt !== false && function_exists('create_function')) {
$this->use_inline_crypt = true;
if ($this->use_inline_crypt !== false) {
$this->use_inline_crypt = version_compare(PHP_VERSION, '5.3.0') >= 0 || function_exists('create_function');
}
}
@ -644,10 +649,10 @@ abstract class Base
case !function_exists('hash_algos'):
case !in_array($hash, hash_algos()):
$i = 1;
$hmac = new Hash();
$hmac->setHash($hash);
$hmac->setKey($password);
while (strlen($key) < $dkLen) {
$hmac = new Hash();
$hmac->setHash($hash);
$hmac->setKey($password);
$f = $u = $hmac->hash($salt . pack('N', $i++));
for ($j = 2; $j <= $count; ++$j) {
$u = $hmac->hash($u);
@ -702,7 +707,7 @@ abstract class Base
case self::MODE_STREAM:
return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
case self::MODE_ECB:
$result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
$result = @openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
return !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result;
case self::MODE_CBC:
$result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV);
@ -762,6 +767,16 @@ abstract class Base
$iv = substr($ciphertext, -$this->block_size);
}
return $ciphertext;
case self::MODE_CFB8:
$ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV);
if ($this->continuousBuffer) {
if (($len = strlen($ciphertext)) >= $this->block_size) {
$this->encryptIV = substr($ciphertext, -$this->block_size);
} else {
$this->encryptIV = substr($this->encryptIV, $len - $this->block_size) . substr($ciphertext, -$len);
}
}
return $ciphertext;
case self::MODE_OFB:
return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer);
@ -942,6 +957,24 @@ abstract class Base
$pos = $len;
}
break;
case self::MODE_CFB8:
$ciphertext = '';
$len = strlen($plaintext);
$iv = $this->encryptIV;
for ($i = 0; $i < $len; ++$i) {
$ciphertext .= ($c = $plaintext[$i] ^ $this->_encryptBlock($iv));
$iv = substr($iv, 1) . $c;
}
if ($this->continuousBuffer) {
if ($len >= $block_size) {
$this->encryptIV = substr($ciphertext, -$block_size);
} else {
$this->encryptIV = substr($this->encryptIV, $len - $block_size) . substr($ciphertext, -$len);
}
}
break;
case self::MODE_OFB:
$xor = $this->encryptIV;
if (strlen($buffer['xor'])) {
@ -1007,14 +1040,14 @@ abstract class Base
break;
case self::MODE_ECB:
if (!defined('OPENSSL_RAW_DATA')) {
$ciphertext.= openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true);
$ciphertext.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true);
}
$plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
break;
case self::MODE_CBC:
if (!defined('OPENSSL_RAW_DATA')) {
$padding = str_repeat(chr($this->block_size), $this->block_size) ^ substr($ciphertext, -$this->block_size);
$ciphertext.= substr(openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size);
$ciphertext.= substr(@openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size);
$offset = 2 * $this->block_size;
} else {
$offset = $this->block_size;
@ -1072,6 +1105,16 @@ abstract class Base
$iv = substr($ciphertext, -$this->block_size);
}
break;
case self::MODE_CFB8:
$plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV);
if ($this->continuousBuffer) {
if (($len = strlen($ciphertext)) >= $this->block_size) {
$this->decryptIV = substr($ciphertext, -$this->block_size);
} else {
$this->decryptIV = substr($this->decryptIV, $len - $this->block_size) . substr($ciphertext, -$len);
}
}
break;
case self::MODE_OFB:
$plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer);
}
@ -1235,6 +1278,24 @@ abstract class Base
$pos = $len;
}
break;
case self::MODE_CFB8:
$plaintext = '';
$len = strlen($ciphertext);
$iv = $this->decryptIV;
for ($i = 0; $i < $len; ++$i) {
$plaintext .= $ciphertext[$i] ^ $this->_encryptBlock($iv);
$iv = substr($iv, 1) . $ciphertext[$i];
}
if ($this->continuousBuffer) {
if ($len >= $block_size) {
$this->decryptIV = substr($ciphertext, -$block_size);
} else {
$this->decryptIV = substr($this->decryptIV, $len - $block_size) . substr($ciphertext, -$len);
}
}
break;
case self::MODE_OFB:
$xor = $this->decryptIV;
if (strlen($buffer['xor'])) {
@ -1297,7 +1358,7 @@ abstract class Base
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
if (strlen($block) > strlen($buffer['ciphertext'])) {
$result = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
$result = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
$result = !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result;
$buffer['ciphertext'].= $result;
}
@ -1308,7 +1369,7 @@ abstract class Base
} else {
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
$otp = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
$otp = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
$otp = !defined('OPENSSL_RAW_DATA') ? substr($otp, 0, -$this->block_size) : $otp;
$this->_increment_str($xor);
$ciphertext.= $block ^ $otp;
@ -1352,7 +1413,7 @@ abstract class Base
}
if ($this->continuousBuffer) {
if (!defined('OPENSSL_RAW_DATA')) {
$encryptIV.= openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
$encryptIV.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
}
$encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
if ($overflow) {
@ -1435,6 +1496,8 @@ abstract class Base
return 'ctr';
case self::MODE_CFB:
return 'cfb';
case self::MODE_CFB8:
return 'cfb8';
case self::MODE_OFB:
return 'ofb';
}
@ -1788,6 +1851,7 @@ abstract class Base
self::MODE_ECB => MCRYPT_MODE_ECB,
self::MODE_CBC => MCRYPT_MODE_CBC,
self::MODE_CFB => 'ncfb',
self::MODE_CFB8 => MCRYPT_MODE_CFB,
self::MODE_OFB => MCRYPT_MODE_NOFB,
self::MODE_STREAM => MCRYPT_MODE_STREAM,
);
@ -2359,6 +2423,52 @@ abstract class Base
$_pos = $_len;
}
return $_plaintext;
';
break;
case self::MODE_CFB8:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_len = strlen($_text);
$_iv = $self->encryptIV;
for ($_i = 0; $_i < $_len; ++$_i) {
$in = $_iv;
'.$encrypt_block.'
$_ciphertext .= ($_c = $_text[$_i] ^ $in);
$_iv = substr($_iv, 1) . $_c;
}
if ($self->continuousBuffer) {
if ($_len >= '.$block_size.') {
$self->encryptIV = substr($_ciphertext, -'.$block_size.');
} else {
$self->encryptIV = substr($self->encryptIV, $_len - '.$block_size.') . substr($_ciphertext, -$_len);
}
}
return $_ciphertext;
';
$decrypt = $init_encrypt . '
$_plaintext = "";
$_len = strlen($_text);
$_iv = $self->decryptIV;
for ($_i = 0; $_i < $_len; ++$_i) {
$in = $_iv;
'.$encrypt_block.'
$_plaintext .= $_text[$_i] ^ $in;
$_iv = substr($_iv, 1) . $_text[$_i];
}
if ($self->continuousBuffer) {
if ($_len >= '.$block_size.') {
$self->decryptIV = substr($_text, -'.$block_size.');
} else {
$self->decryptIV = substr($self->decryptIV, $_len - '.$block_size.') . substr($_text, -$_len);
}
}
return $_plaintext;
';
break;
@ -2492,6 +2602,11 @@ abstract class Base
}
// Create the $inline function and return its name as string. Ready to run!
if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
eval('$func = function ($_action, &$self, $_text) { ' . $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' } };');
return $func;
}
return create_function('$_action, &$self, $_text', $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' }');
}
@ -2550,4 +2665,44 @@ abstract class Base
return $result . pack('H*', sha1($hash));
}
}
/**
* Convert float to int
*
* On ARM CPUs converting floats to ints doesn't always work
*
* @access private
* @param string $x
* @return int
*/
function safe_intval($x)
{
switch (true) {
case is_int($x):
// PHP 5.3, per http://php.net/releases/5_3_0.php, introduced "more consistent float rounding"
case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM':
return $x;
}
return (fmod($x, 0x80000000) & 0x7FFFFFFF) |
((fmod(floor($x / 0x80000000), 2) & 1) << 31);
}
/**
* eval()'able string for in-line float to int
*
* @access private
* @return string
*/
function safe_intval_inline()
{
switch (true) {
case defined('PHP_INT_SIZE') && PHP_INT_SIZE == 8:
case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM':
return '%s';
break;
default:
$safeint = '(is_int($temp = %s) ? $temp : (fmod($temp, 0x80000000) & 0x7FFFFFFF) | ';
return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))';
}
}
}

@ -294,7 +294,7 @@ class Blowfish extends Base
function setKeyLength($length)
{
if ($length < 32) {
$this->key_length = 7;
$this->key_length = 4;
} elseif ($length > 448) {
$this->key_length = 56;
} else {
@ -408,16 +408,14 @@ class Blowfish extends Base
for ($i = 0; $i < 16; $i+= 2) {
$l^= $p[$i];
$r^= ($sb_0[$l >> 24 & 0xff] +
$sb_1[$l >> 16 & 0xff] ^
$r^= $this->safe_intval(($this->safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^
$sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff];
$sb_3[$l & 0xff]);
$r^= $p[$i + 1];
$l^= ($sb_0[$r >> 24 & 0xff] +
$sb_1[$r >> 16 & 0xff] ^
$l^= $this->safe_intval(($this->safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^
$sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff];
$sb_3[$r & 0xff]);
}
return pack("N*", $r ^ $p[17], $l ^ $p[16]);
}
@ -443,16 +441,14 @@ class Blowfish extends Base
for ($i = 17; $i > 2; $i-= 2) {
$l^= $p[$i];
$r^= ($sb_0[$l >> 24 & 0xff] +
$sb_1[$l >> 16 & 0xff] ^
$r^= $this->safe_intval(($this->safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^
$sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff];
$sb_3[$l & 0xff]);
$r^= $p[$i - 1];
$l^= ($sb_0[$r >> 24 & 0xff] +
$sb_1[$r >> 16 & 0xff] ^
$l^= $this->safe_intval(($this->safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^
$sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff];
$sb_3[$r & 0xff]);
}
return pack("N*", $r ^ $p[0], $l ^ $p[1]);
}
@ -478,6 +474,8 @@ class Blowfish extends Base
$code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
}
$safeint = $this->safe_intval_inline();
if (!isset($lambda_functions[$code_hash])) {
switch (true) {
case $gen_hi_opt_code:
@ -513,16 +511,14 @@ class Blowfish extends Base
for ($i = 0; $i < 16; $i+= 2) {
$encrypt_block.= '
$l^= ' . $p[$i] . ';
$r^= ($sb_0[$l >> 24 & 0xff] +
$sb_1[$l >> 16 & 0xff] ^
$r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^
$sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff];
$sb_3[$l & 0xff]') . ';
$r^= ' . $p[$i + 1] . ';
$l^= ($sb_0[$r >> 24 & 0xff] +
$sb_1[$r >> 16 & 0xff] ^
$l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^
$sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff];
$sb_3[$r & 0xff]') . ';
';
}
$encrypt_block.= '
@ -542,16 +538,14 @@ class Blowfish extends Base
for ($i = 17; $i > 2; $i-= 2) {
$decrypt_block.= '
$l^= ' . $p[$i] . ';
$r^= ($sb_0[$l >> 24 & 0xff] +
$sb_1[$l >> 16 & 0xff] ^
$r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^
$sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff];
$sb_3[$l & 0xff]') . ';
$r^= ' . $p[$i - 1] . ';
$l^= ($sb_0[$r >> 24 & 0xff] +
$sb_1[$r >> 16 & 0xff] ^
$l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^
$sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff];
$sb_3[$r & 0xff]') . ';
';
}

@ -1357,8 +1357,8 @@ class DES extends Base
$k[self::ENCRYPT][$i] = '$ke[' . $i . ']';
$k[self::DECRYPT][$i] = '$kd[' . $i . ']';
}
$init_encrypt = '$ke = $self->keys[self::ENCRYPT];';
$init_decrypt = '$kd = $self->keys[self::DECRYPT];';
$init_encrypt = '$ke = $self->keys[$self::ENCRYPT];';
$init_decrypt = '$kd = $self->keys[$self::DECRYPT];';
break;
}

@ -112,6 +112,15 @@ class Hash
*/
var $key = false;
/**
* Computed Key
*
* @see self::_computeKey()
* @var string
* @access private
*/
var $computedKey = false;
/**
* Outer XOR (Internal HMAC)
*
@ -130,6 +139,15 @@ class Hash
*/
var $ipad;
/**
* Engine
*
* @see self::setHash()
* @var string
* @access private
*/
var $engine;
/**
* Default Constructor.
*
@ -166,6 +184,43 @@ class Hash
function setKey($key = false)
{
$this->key = $key;
$this->_computeKey();
}
/**
* Pre-compute the key used by the HMAC
*
* Quoting http://tools.ietf.org/html/rfc2104#section-2, "Applications that use keys longer than B bytes
* will first hash the key using H and then use the resultant L byte string as the actual key to HMAC."
*
* As documented in https://www.reddit.com/r/PHP/comments/9nct2l/symfonypolyfill_hash_pbkdf2_correct_fix_for/
* when doing an HMAC multiple times it's faster to compute the hash once instead of computing it during
* every call
*
* @access private
*/
function _computeKey()
{
if ($this->key === false) {
$this->computedKey = false;
return;
}
if (strlen($this->key) <= $this->b) {
$this->computedKey = $this->key;
return;
}
switch ($this->engine) {
case self::MODE_MHASH:
$this->computedKey = mhash($this->hash, $this->key);
break;
case self::MODE_HASH:
$this->computedKey = hash($this->hash, $this->key, true);
break;
case self::MODE_INTERNAL:
$this->computedKey = call_user_func($this->hash, $this->key);
}
}
/**
@ -215,20 +270,39 @@ class Hash
$this->l = 64;
}
switch ($hash) {
case 'md2-96':
case 'md2':
$this->b = 16;
case 'md5-96':
case 'sha1-96':
case 'sha224-96':
case 'sha256-96':
case 'md2':
case 'md5':
case 'sha1':
case 'sha224':
case 'sha256':
$this->b = 64;
break;
default:
$this->b = 128;
}
switch ($hash) {
case 'md2':
$mode = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ?
$this->engine = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ?
self::MODE_HASH : self::MODE_INTERNAL;
break;
case 'sha384':
case 'sha512':
$mode = CRYPT_HASH_MODE == self::MODE_MHASH ? self::MODE_INTERNAL : CRYPT_HASH_MODE;
$this->engine = CRYPT_HASH_MODE == self::MODE_MHASH ? self::MODE_INTERNAL : CRYPT_HASH_MODE;
break;
default:
$mode = CRYPT_HASH_MODE;
$this->engine = CRYPT_HASH_MODE;
}
switch ($mode) {
switch ($this->engine) {
case self::MODE_MHASH:
switch ($hash) {
case 'md5':
@ -241,6 +315,7 @@ class Hash
default:
$this->hash = MHASH_SHA1;
}
$this->_computeKey(self::MODE_MHASH);
return;
case self::MODE_HASH:
switch ($hash) {
@ -257,35 +332,33 @@ class Hash
default:
$this->hash = 'sha1';
}
$this->_computeKey(self::MODE_HASH);
return;
}
switch ($hash) {
case 'md2':
$this->b = 16;
$this->hash = array($this, '_md2');
break;
case 'md5':
$this->b = 64;
$this->hash = array($this, '_md5');
break;
case 'sha256':
$this->b = 64;
$this->hash = array($this, '_sha256');
break;
case 'sha384':
case 'sha512':
$this->b = 128;
$this->hash = array($this, '_sha512');
break;
case 'sha1':
default:
$this->b = 64;
$this->hash = array($this, '_sha1');
}
$this->ipad = str_repeat(chr(0x36), $this->b);
$this->opad = str_repeat(chr(0x5C), $this->b);
$this->_computeKey(self::MODE_INTERNAL);
}
/**
@ -297,33 +370,25 @@ class Hash
*/
function hash($text)
{
$mode = is_array($this->hash) ? self::MODE_INTERNAL : CRYPT_HASH_MODE;
if (!empty($this->key) || is_string($this->key)) {
switch ($mode) {
switch ($this->engine) {
case self::MODE_MHASH:
$output = mhash($this->hash, $text, $this->key);
$output = mhash($this->hash, $text, $this->computedKey);
break;
case self::MODE_HASH:
$output = hash_hmac($this->hash, $text, $this->key, true);
$output = hash_hmac($this->hash, $text, $this->computedKey, true);
break;
case self::MODE_INTERNAL:
/* "Applications that use keys longer than B bytes will first hash the key using H and then use the
resultant L byte string as the actual key to HMAC."
-- http://tools.ietf.org/html/rfc2104#section-2 */
$key = strlen($this->key) > $this->b ? call_user_func($this->hash, $this->key) : $this->key;
$key = str_pad($key, $this->b, chr(0)); // step 1
$temp = $this->ipad ^ $key; // step 2
$temp .= $text; // step 3
$temp = call_user_func($this->hash, $temp); // step 4
$output = $this->opad ^ $key; // step 5
$output.= $temp; // step 6
$output = call_user_func($this->hash, $output); // step 7
$key = str_pad($this->computedKey, $this->b, chr(0)); // step 1
$temp = $this->ipad ^ $key; // step 2
$temp .= $text; // step 3
$temp = call_user_func($this->hash, $temp); // step 4
$output = $this->opad ^ $key; // step 5
$output.= $temp; // step 6
$output = call_user_func($this->hash, $output); // step 7
}
} else {
switch ($mode) {
switch ($this->engine) {
case self::MODE_MHASH:
$output = mhash($this->hash, $text);
break;
@ -802,7 +867,12 @@ class Hash
$result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument;
}
return fmod($result, $mod);
if ((php_uname('m') & "\xDF\xDF\xDF") != 'ARM') {
return fmod($result, $mod);
}
return (fmod($result, 0x80000000) & 0x7FFFFFFF) |
((fmod(floor($result / 0x80000000), 2) & 1) << 31);
}
/**

@ -296,7 +296,7 @@ class RC2 extends Base
function setKeyLength($length)
{
if ($length < 8) {
$this->default_key_length = 8;
$this->default_key_length = 1;
} elseif ($length > 1024) {
$this->default_key_length = 128;
} else {

@ -107,7 +107,7 @@ class RC4 extends Base
* @var string
* @access private
*/
var $key = "\0";
var $key;
/**
* The Key Stream for decryption and encryption

@ -182,6 +182,10 @@ class RSA
* PKCS#8 formatted private key
*/
const PRIVATE_FORMAT_PKCS8 = 8;
/**
* OpenSSH formatted private key
*/
const PRIVATE_FORMAT_OPENSSH = 9;
/**#@-*/
/**#@+
@ -468,23 +472,27 @@ class RSA
break;
case extension_loaded('openssl') && file_exists($this->configFile):
// some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
ob_start();
@phpinfo();
$content = ob_get_contents();
ob_end_clean();
preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
$versions = array();
if (!empty($matches[1])) {
for ($i = 0; $i < count($matches[1]); $i++) {
$fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
// Remove letter part in OpenSSL version
if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
$versions[$matches[1][$i]] = $fullVersion;
} else {
$versions[$matches[1][$i]] = $m[0];
// avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems)
if (strpos(ini_get('disable_functions'), 'phpinfo') === false) {
ob_start();
@phpinfo();
$content = ob_get_contents();
ob_end_clean();
preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
if (!empty($matches[1])) {
for ($i = 0; $i < count($matches[1]); $i++) {
$fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
// Remove letter part in OpenSSL version
if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
$versions[$matches[1][$i]] = $fullVersion;
} else {
$versions[$matches[1][$i]] = $m[0];
}
}
}
}
@ -816,6 +824,58 @@ class RSA
$key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";
return $key;
case self::PRIVATE_FORMAT_OPENSSH:
if ($num_primes != 2) {
return false;
}
$publicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']);
$privateKey = pack(
'Na*Na*Na*Na*Na*Na*Na*',
strlen('ssh-rsa'),
'ssh-rsa',
strlen($raw['modulus']),
$raw['modulus'],
strlen($raw['publicExponent']),
$raw['publicExponent'],
strlen($raw['privateExponent']),
$raw['privateExponent'],
strlen($raw['coefficient']),
$raw['coefficient'],
strlen($raw['prime1']),
$raw['prime1'],
strlen($raw['prime2']),
$raw['prime2']
);
$checkint = Random::string(4);
$paddedKey = pack(
'a*Na*',
$checkint . $checkint . $privateKey,
strlen($this->comment),
$this->comment
);
$paddingLength = (7 * strlen($paddedKey)) % 8;
for ($i = 1; $i <= $paddingLength; $i++) {
$paddedKey.= chr($i);
}
$key = pack(
'Na*Na*Na*NNa*Na*',
strlen('none'),
'none',
strlen('none'),
'none',
0,
'',
1,
strlen($publicKey),
$publicKey,
strlen($paddedKey),
$paddedKey
);
$key = "openssh-key-v1\0$key";
return "-----BEGIN OPENSSH PRIVATE KEY-----\r\n" .
chunk_split(base64_encode($key), 70) .
"-----END OPENSSH PRIVATE KEY-----";
default: // eg. self::PRIVATE_FORMAT_PKCS1
$components = array();
foreach ($raw as $name => $value) {
@ -1016,9 +1076,9 @@ class RSA
* @access private
* @see self::_convertPublicKey()
* @see self::_convertPrivateKey()
* @param string $key
* @param string|array $key
* @param int $type
* @return array
* @return array|bool
*/
function _parseKey($key, $type)
{
@ -1329,9 +1389,14 @@ class RSA
xml_set_character_data_handler($xml, '_data_handler');
// add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
xml_parser_free($xml);
unset($xml);
return false;
}
xml_parser_free($xml);
unset($xml);
return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
// from PuTTY's SSHPUBK.C
case self::PRIVATE_FORMAT_PUTTY:
@ -1403,6 +1468,75 @@ class RSA
}
$components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256));
return $components;
case self::PRIVATE_FORMAT_OPENSSH:
$components = array();
$decoded = $this->_extractBER($key);
$magic = $this->_string_shift($decoded, 15);
if ($magic !== "openssh-key-v1\0") {
return false;
}
$options = $this->_string_shift($decoded, 24);
// \0\0\0\4none = ciphername
// \0\0\0\4none = kdfname
// \0\0\0\0 = kdfoptions
// \0\0\0\1 = numkeys
if ($options != "\0\0\0\4none\0\0\0\4none\0\0\0\0\0\0\0\1") {
return false;
}
extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
if (strlen($decoded) < $length) {
return false;
}
$publicKey = $this->_string_shift($decoded, $length);
extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
if (strlen($decoded) < $length) {
return false;
}
$paddedKey = $this->_string_shift($decoded, $length);
if ($this->_string_shift($publicKey, 11) !== "\0\0\0\7ssh-rsa") {
return false;
}
$checkint1 = $this->_string_shift($paddedKey, 4);
$checkint2 = $this->_string_shift($paddedKey, 4);
if (strlen($checkint1) != 4 || $checkint1 !== $checkint2) {
return false;
}
if ($this->_string_shift($paddedKey, 11) !== "\0\0\0\7ssh-rsa") {
return false;
}
$values = array(
&$components['modulus'],
&$components['publicExponent'],
&$components['privateExponent'],
&$components['coefficients'][2],
&$components['primes'][1],
&$components['primes'][2]
);
foreach ($values as &$value) {
extract(unpack('Nlength', $this->_string_shift($paddedKey, 4)));
if (strlen($paddedKey) < $length) {
return false;
}
$value = new BigInteger($this->_string_shift($paddedKey, $length), -256);
}
extract(unpack('Nlength', $this->_string_shift($paddedKey, 4)));
if (strlen($paddedKey) < $length) {
return false;
}
$components['comment'] = $this->_string_shift($decoded, $length);
$temp = $components['primes'][1]->subtract($this->one);
$components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
$temp = $components['primes'][2]->subtract($this->one);
$components['exponents'][] = $components['publicExponent']->modInverse($temp);
return $components;
}
}
@ -1501,8 +1635,9 @@ class RSA
* Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
*
* @access public
* @param string $key
* @param int $type optional
* @param string|RSA|array $key
* @param bool|int $type optional
* @return bool
*/
function loadKey($key, $type = false)
{
@ -1559,7 +1694,8 @@ class RSA
self::PRIVATE_FORMAT_PKCS1,
self::PRIVATE_FORMAT_XML,
self::PRIVATE_FORMAT_PUTTY,
self::PUBLIC_FORMAT_OPENSSH
self::PUBLIC_FORMAT_OPENSSH,
self::PRIVATE_FORMAT_OPENSSH
);
foreach ($types as $type) {
$components = $this->_parseKey($key, $type);
@ -2207,16 +2343,21 @@ class RSA
*/
function _equals($x, $y)
{
if (function_exists('hash_equals')) {
return hash_equals($x, $y);
}
if (strlen($x) != strlen($y)) {
return false;
}
$result = 0;
$result = "\0";
$x^= $y;
for ($i = 0; $i < strlen($x); $i++) {
$result |= ord($x[$i]) ^ ord($y[$i]);
$result|= $x[$i];
}
return $result == 0;
return $result === "\0";
}
/**
@ -2423,19 +2564,26 @@ class RSA
$db = $maskedDB ^ $dbMask;
$lHash2 = substr($db, 0, $this->hLen);
$m = substr($db, $this->hLen);
if ($lHash != $lHash2) {
user_error('Decryption error');
return false;
}
$m = ltrim($m, chr(0));
if (ord($m[0]) != 1) {
$hashesMatch = $this->_equals($lHash, $lHash2);
$leadingZeros = 1;
$patternMatch = 0;
$offset = 0;
for ($i = 0; $i < strlen($m); $i++) {
$patternMatch|= $leadingZeros & ($m[$i] === "\1");
$leadingZeros&= $m[$i] === "\0";
$offset+= $patternMatch ? 0 : 1;
}
// we do & instead of && to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation
// to protect against timing attacks
if (!$hashesMatch & !$patternMatch) {
user_error('Decryption error');
return false;
}
// Output the message M
return substr($m, 1);
return substr($m, $offset + 1);
}
/**

@ -45,6 +45,10 @@ class Random
*/
static function string($length)
{
if (!$length) {
return '';
}
if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
try {
return \random_bytes($length);

@ -432,8 +432,10 @@ class Twofish extends Base
$m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff);
$K[] = $A+= $B;
$K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff);
$A = $this->safe_intval($A + $B);
$K[] = $A;
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
}
for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0];
@ -456,8 +458,10 @@ class Twofish extends Base
$m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff);
$K[] = $A+= $B;
$K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff);
$A = $this->safe_intval($A + $B);
$K[] = $A;
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
}
for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0];
@ -481,8 +485,10 @@ class Twofish extends Base
$m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff);
$K[] = $A+= $B;
$K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff);
$A = $this->safe_intval($A + $B);
$K[] = $A;
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
}
for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0];
@ -578,9 +584,9 @@ class Twofish extends Base
$S1[ $R1 & 0xff] ^
$S2[($R1 >> 8) & 0xff] ^
$S3[($R1 >> 16) & 0xff];
$R2^= $t0 + $t1 + $K[++$ki];
$R2^= $this->safe_intval($t0 + $t1 + $K[++$ki]);
$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
$R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ($t0 + ($t1 << 1) + $K[++$ki]);
$R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ $this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]);
$t0 = $S0[ $R2 & 0xff] ^
$S1[($R2 >> 8) & 0xff] ^
@ -590,9 +596,9 @@ class Twofish extends Base
$S1[ $R3 & 0xff] ^
$S2[($R3 >> 8) & 0xff] ^
$S3[($R3 >> 16) & 0xff];
$R0^= ($t0 + $t1 + $K[++$ki]);
$R0^= $this->safe_intval($t0 + $t1 + $K[++$ki]);
$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
$R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ($t0 + ($t1 << 1) + $K[++$ki]);
$R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ $this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]);
}
// @codingStandardsIgnoreStart
@ -634,9 +640,9 @@ class Twofish extends Base
$S1[$R1 & 0xff] ^
$S2[$R1 >> 8 & 0xff] ^
$S3[$R1 >> 16 & 0xff];
$R3^= $t0 + ($t1 << 1) + $K[--$ki];
$R3^= $this->safe_intval($t0 + ($t1 << 1) + $K[--$ki]);
$R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
$R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ($t0 + $t1 + $K[--$ki]);
$R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ $this->safe_intval($t0 + $t1 + $K[--$ki]);
$t0 = $S0[$R2 & 0xff] ^
$S1[$R2 >> 8 & 0xff] ^
@ -646,9 +652,9 @@ class Twofish extends Base
$S1[$R3 & 0xff] ^
$S2[$R3 >> 8 & 0xff] ^
$S3[$R3 >> 16 & 0xff];
$R1^= $t0 + ($t1 << 1) + $K[--$ki];
$R1^= $this->safe_intval($t0 + ($t1 << 1) + $K[--$ki]);
$R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
$R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ($t0 + $t1 + $K[--$ki]);
$R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ $this->safe_intval($t0 + $t1 + $K[--$ki]);
}
// @codingStandardsIgnoreStart
@ -679,6 +685,8 @@ class Twofish extends Base
$code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
}
$safeint = $this->safe_intval_inline();
if (!isset($lambda_functions[$code_hash])) {
switch (true) {
case $gen_hi_opt_code:
@ -727,9 +735,9 @@ class Twofish extends Base
$S1[ $R1 & 0xff] ^
$S2[($R1 >> 8) & 0xff] ^
$S3[($R1 >> 16) & 0xff];
$R2^= ($t0 + $t1 + '.$K[++$ki].');
$R2^= ' . sprintf($safeint, '$t0 + $t1 + ' . $K[++$ki]) . ';
$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
$R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ($t0 + ($t1 << 1) + '.$K[++$ki].');
$R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . ';
$t0 = $S0[ $R2 & 0xff] ^
$S1[($R2 >> 8) & 0xff] ^
@ -739,16 +747,16 @@ class Twofish extends Base
$S1[ $R3 & 0xff] ^
$S2[($R3 >> 8) & 0xff] ^
$S3[($R3 >> 16) & 0xff];
$R0^= ($t0 + $t1 + '.$K[++$ki].');
$R0^= ' . sprintf($safeint, '($t0 + $t1 + ' . $K[++$ki] . ')') . ';
$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
$R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ($t0 + ($t1 << 1) + '.$K[++$ki].');
$R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . ';
';
}
$encrypt_block.= '
$in = pack("V4", '.$K[4].' ^ $R2,
'.$K[5].' ^ $R3,
'.$K[6].' ^ $R0,
'.$K[7].' ^ $R1);
$in = pack("V4", ' . $K[4] . ' ^ $R2,
' . $K[5] . ' ^ $R3,
' . $K[6] . ' ^ $R0,
' . $K[7] . ' ^ $R1);
';
// Generating decrypt code:
@ -769,9 +777,9 @@ class Twofish extends Base
$S1[$R1 & 0xff] ^
$S2[$R1 >> 8 & 0xff] ^
$S3[$R1 >> 16 & 0xff];
$R3^= $t0 + ($t1 << 1) + '.$K[--$ki].';
$R3^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . ';
$R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
$R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ($t0 + $t1 + '.$K[--$ki].');
$R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] . ')') . ';
$t0 = $S0[$R2 & 0xff] ^
$S1[$R2 >> 8 & 0xff] ^
@ -781,16 +789,16 @@ class Twofish extends Base
$S1[$R3 & 0xff] ^
$S2[$R3 >> 8 & 0xff] ^
$S3[$R3 >> 16 & 0xff];
$R1^= $t0 + ($t1 << 1) + '.$K[--$ki].';
$R1^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . ';
$R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
$R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ($t0 + $t1 + '.$K[--$ki].');
$R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] . ')') . ';
';
}
$decrypt_block.= '
$in = pack("V4", '.$K[0].' ^ $R2,
'.$K[1].' ^ $R3,
'.$K[2].' ^ $R0,
'.$K[3].' ^ $R1);
$in = pack("V4", ' . $K[0] . ' ^ $R2,
' . $K[1] . ' ^ $R3,
' . $K[2] . ' ^ $R0,
' . $K[3] . ' ^ $R1);
';
$lambda_functions[$code_hash] = $this->_createInlineCryptFunction(

@ -305,6 +305,9 @@ class ANSI
case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): // Move cursor left n lines
$this->old_x = $this->x;
$this->x-= $match[1];
if ($this->x < 0) {
$this->x = 0;
}
break;
case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window
break;
@ -416,7 +419,7 @@ class ANSI
if ($this->x > $this->max_x) {
$this->x = 0;
$this->y++;
$this->_newLine();
} else {
$this->x++;
}

@ -25,6 +25,8 @@ namespace phpseclib\File;
use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger;
use DateTime;
use DateTimeZone;
/**
* Pure-PHP ASN.1 Parser
@ -242,9 +244,10 @@ class ASN1
$tag = 0;
// process septets (since the eighth bit is ignored, it's not an octet)
do {
$loop = ord($encoded[0]) >> 7;
$temp = ord($encoded[$encoded_pos++]);
$loop = $temp >> 7;
$tag <<= 7;
$tag |= ord($encoded[$encoded_pos++]) & 0x7F;
$tag |= $temp & 0x7F;
$start++;
} while ($loop);
}
@ -306,6 +309,9 @@ class ASN1
$remainingLength = $length;
while ($remainingLength > 0) {
$temp = $this->_decode_ber($content, $start, $content_pos);
if ($temp === false) {
break;
}
$length = $temp['length'];
// end-of-content octets - see paragraph 8.1.5
if (substr($content, $content_pos + $length, 2) == "\0\0") {
@ -357,6 +363,9 @@ class ASN1
$current['content'] = substr($content, $content_pos);
} else {
$temp = $this->_decode_ber($content, $start, $content_pos);
if ($temp === false) {
return false;
}
$length-= (strlen($content) - $content_pos);
$last = count($temp) - 1;
for ($i = 0; $i < $last; $i++) {
@ -381,6 +390,9 @@ class ASN1
$length = 0;
while (substr($content, $content_pos, 2) != "\0\0") {
$temp = $this->_decode_ber($content, $length + $start, $content_pos);
if ($temp === false) {
return false;
}
$content_pos += $temp['length'];
// all subtags should be octet strings
//if ($temp['type'] != self::TYPE_OCTET_STRING) {
@ -413,30 +425,16 @@ class ASN1
break 2;
}
$temp = $this->_decode_ber($content, $start + $offset, $content_pos);
if ($temp === false) {
return false;
}
$content_pos += $temp['length'];
$current['content'][] = $temp;
$offset+= $temp['length'];
}
break;
case self::TYPE_OBJECT_IDENTIFIER:
$temp = ord($content[$content_pos++]);
$current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40);
$valuen = 0;
// process septets
$content_len = strlen($content);
while ($content_pos < $content_len) {
$temp = ord($content[$content_pos++]);
$valuen <<= 7;
$valuen |= $temp & 0x7F;
if (~$temp & 0x80) {
$current['content'].= ".$valuen";
$valuen = 0;
}
}
// the eighth bit of the last byte should not be 1
//if ($temp >> 7) {
// return false;
//}
$current['content'] = $this->_decodeOID(substr($content, $content_pos));
break;
/* Each character string type shall be encoded as if it had been declared:
[UNIVERSAL x] IMPLICIT OCTET STRING
@ -580,7 +578,7 @@ class ASN1
$childClass = $tempClass = self::CLASS_UNIVERSAL;
$constant = null;
if (isset($temp['constant'])) {
$tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC;
$tempClass = $temp['type'];
}
if (isset($child['class'])) {
$childClass = $child['class'];
@ -643,7 +641,7 @@ class ASN1
$temp = $decoded['content'][$i];
$tempClass = self::CLASS_UNIVERSAL;
if (isset($temp['constant'])) {
$tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC;
$tempClass = $temp['type'];
}
foreach ($mapping['children'] as $key => $child) {
@ -707,7 +705,7 @@ class ASN1
if (isset($mapping['implicit'])) {
$decoded['content'] = $this->_decodeTime($decoded['content'], $decoded['type']);
}
return @date($this->format, $decoded['content']);
return $decoded['content'] ? $decoded['content']->format($this->format) : false;
case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) {
$offset = ord($decoded['content'][0]);
@ -956,7 +954,8 @@ class ASN1
case self::TYPE_GENERALIZED_TIME:
$format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y';
$format.= 'mdHis';
$value = @gmdate($format, strtotime($source)) . 'Z';
$date = new DateTime($source, new DateTimeZone('GMT'));
$value = $date->format($format) . 'Z';
break;
case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) {
@ -998,27 +997,7 @@ class ASN1
$value = base64_decode($source);
break;
case self::TYPE_OBJECT_IDENTIFIER:
$oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids);
if ($oid === false) {
user_error('Invalid OID');
return false;
}
$value = '';
$parts = explode('.', $oid);
$value = chr(40 * $parts[0] + $parts[1]);
for ($i = 2; $i < count($parts); $i++) {
$temp = '';
if (!$parts[$i]) {
$temp = "\0";
} else {
while ($parts[$i]) {
$temp = chr(0x80 | ($parts[$i] & 0x7F)) . $temp;
$parts[$i] >>= 7;
}
$temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F);
}
$value.= $temp;
}
$value = $this->_encodeOID($source);
break;
case self::TYPE_ANY:
$loc = $this->location;
@ -1117,6 +1096,108 @@ class ASN1
return pack('Ca*', 0x80 | strlen($temp), $temp);
}
/**
* BER-decode the OID
*
* Called by _decode_ber()
*
* @access private
* @param string $content
* @return string
*/
function _decodeOID($content)
{
static $eighty;
if (!$eighty) {
$eighty = new BigInteger(80);
}
$oid = array();
$pos = 0;
$len = strlen($content);
$n = new BigInteger();
while ($pos < $len) {
$temp = ord($content[$pos++]);
$n = $n->bitwise_leftShift(7);
$n = $n->bitwise_or(new BigInteger($temp & 0x7F));
if (~$temp & 0x80) {
$oid[] = $n;
$n = new BigInteger();
}
}
$part1 = array_shift($oid);
$first = floor(ord($content[0]) / 40);
/*
"This packing of the first two object identifier components recognizes that only three values are allocated from the root
node, and at most 39 subsequent values from nodes reached by X = 0 and X = 1."
-- https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=22
*/
if ($first <= 2) { // ie. 0 <= ord($content[0]) < 120 (0x78)
array_unshift($oid, ord($content[0]) % 40);
array_unshift($oid, $first);
} else {
array_unshift($oid, $part1->subtract($eighty));
array_unshift($oid, 2);
}
return implode('.', $oid);
}
/**
* DER-encode the OID
*
* Called by _encode_der()
*
* @access private
* @param string $content
* @return string
*/
function _encodeOID($source)
{
static $mask, $zero, $forty;
if (!$mask) {
$mask = new BigInteger(0x7F);
$zero = new BigInteger();
$forty = new BigInteger(40);
}
$oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids);
if ($oid === false) {
user_error('Invalid OID');
return false;
}
$parts = explode('.', $oid);
$part1 = array_shift($parts);
$part2 = array_shift($parts);
$first = new BigInteger($part1);
$first = $first->multiply($forty);
$first = $first->add(new BigInteger($part2));
array_unshift($parts, $first->toString());
$value = '';
foreach ($parts as $part) {
if (!$part) {
$temp = "\0";
} else {
$temp = '';
$part = new BigInteger($part);
while (!$part->equals($zero)) {
$submask = $part->bitwise_and($mask);
$submask->setPrecision(8);
$temp = (chr(0x80) | $submask->toBytes()) . $temp;
$part = $part->bitwise_rightShift(7);
}
$temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F);
}
$value.= $temp;
}
return $value;
}
/**
* BER-decode the time
*
@ -1137,33 +1218,32 @@ class ASN1
http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
http://www.obj-sys.com/asn1tutorial/node14.html */
$pattern = $tag == self::TYPE_UTC_TIME ?
'#^(..)(..)(..)(..)(..)(..)?(.*)$#' :
'#(....)(..)(..)(..)(..)(..).*([Z+-].*)$#';
preg_match($pattern, $content, $matches);
list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches;
$format = 'YmdHis';
if ($tag == self::TYPE_UTC_TIME) {
$year = $year >= 50 ? "19$year" : "20$year";
// https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=28 says "the seconds
// element shall always be present" but none-the-less I've seen X509 certs where it isn't and if the
// browsers parse it phpseclib ought to too
if (preg_match('#^(\d{10})(Z|[+-]\d{4})$#', $content, $matches)) {
$content = $matches[1] . '00' . $matches[2];