mirror of
https://github.com/phpseclib/phpseclib.git
synced 2024-11-17 02:35:10 +00:00
Improve performance of File\ASN1->_decode_ber() for large data
This removes the use of _string_shift() which copies the (potentially large) latter part of the input data repeatedly, in favor of maintaining a position var and using string indexing or substr() to only copy the (relatively small) current data as it is parsed.
This commit is contained in:
parent
c788ec574f
commit
a1e16797ca
@ -285,14 +285,15 @@ class File_ASN1
|
|||||||
*
|
*
|
||||||
* @param string $encoded
|
* @param string $encoded
|
||||||
* @param int $start
|
* @param int $start
|
||||||
|
* @param int $encoded_pos
|
||||||
* @return array
|
* @return array
|
||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
function _decode_ber($encoded, $start = 0)
|
function _decode_ber($encoded, $start = 0, $encoded_pos = 0)
|
||||||
{
|
{
|
||||||
$current = array('start' => $start);
|
$current = array('start' => $start);
|
||||||
|
|
||||||
$type = ord($this->_string_shift($encoded));
|
$type = ord($encoded[$encoded_pos++]);
|
||||||
$start++;
|
$start++;
|
||||||
|
|
||||||
$constructed = ($type >> 5) & 1;
|
$constructed = ($type >> 5) & 1;
|
||||||
@ -304,23 +305,24 @@ class File_ASN1
|
|||||||
do {
|
do {
|
||||||
$loop = ord($encoded[0]) >> 7;
|
$loop = ord($encoded[0]) >> 7;
|
||||||
$tag <<= 7;
|
$tag <<= 7;
|
||||||
$tag |= ord($this->_string_shift($encoded)) & 0x7F;
|
$tag |= ord($encoded[$encoded_pos++]) & 0x7F;
|
||||||
$start++;
|
$start++;
|
||||||
} while ($loop);
|
} while ($loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13
|
// Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13
|
||||||
$length = ord($this->_string_shift($encoded));
|
$length = ord($encoded[$encoded_pos++]);
|
||||||
$start++;
|
$start++;
|
||||||
if ($length == 0x80) { // indefinite length
|
if ($length == 0x80) { // indefinite length
|
||||||
// "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all
|
// "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all
|
||||||
// immediately available." -- paragraph 8.1.3.2.c
|
// immediately available." -- paragraph 8.1.3.2.c
|
||||||
$length = strlen($encoded);
|
$length = strlen($encoded) - $encoded_pos;
|
||||||
} elseif ($length & 0x80) { // definite length, long form
|
} elseif ($length & 0x80) { // definite length, long form
|
||||||
// technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only
|
// technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only
|
||||||
// support it up to four.
|
// support it up to four.
|
||||||
$length&= 0x7F;
|
$length&= 0x7F;
|
||||||
$temp = $this->_string_shift($encoded, $length);
|
$temp = substr($encoded, $encoded_pos, $length);
|
||||||
|
$encoded_pos += $length;
|
||||||
// tags of indefinte length don't really have a header length; this length includes the tag
|
// tags of indefinte length don't really have a header length; this length includes the tag
|
||||||
$current+= array('headerlength' => $length + 2);
|
$current+= array('headerlength' => $length + 2);
|
||||||
$start+= $length;
|
$start+= $length;
|
||||||
@ -329,11 +331,12 @@ class File_ASN1
|
|||||||
$current+= array('headerlength' => 2);
|
$current+= array('headerlength' => 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($length > strlen($encoded)) {
|
if ($length > (strlen($encoded) - $encoded_pos)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$content = $this->_string_shift($encoded, $length);
|
$content = substr($encoded, $encoded_pos, $length);
|
||||||
|
$content_pos = 0;
|
||||||
|
|
||||||
// at this point $length can be overwritten. it's only accurate for definite length things as is
|
// at this point $length can be overwritten. it's only accurate for definite length things as is
|
||||||
|
|
||||||
@ -366,7 +369,7 @@ class File_ASN1
|
|||||||
$temp = $this->_decode_ber($content, $start);
|
$temp = $this->_decode_ber($content, $start);
|
||||||
$length = $temp['length'];
|
$length = $temp['length'];
|
||||||
// end-of-content octets - see paragraph 8.1.5
|
// end-of-content octets - see paragraph 8.1.5
|
||||||
if (substr($content, $length, 2) == "\0\0") {
|
if (substr($content, $content_pos + $length, 2) == "\0\0") {
|
||||||
$length+= 2;
|
$length+= 2;
|
||||||
$start+= $length;
|
$start+= $length;
|
||||||
$newcontent[] = $temp;
|
$newcontent[] = $temp;
|
||||||
@ -375,7 +378,7 @@ class File_ASN1
|
|||||||
$start+= $length;
|
$start+= $length;
|
||||||
$remainingLength-= $length;
|
$remainingLength-= $length;
|
||||||
$newcontent[] = $temp;
|
$newcontent[] = $temp;
|
||||||
$this->_string_shift($content, $length);
|
$content_pos += $length;
|
||||||
}
|
}
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
@ -399,11 +402,11 @@ class File_ASN1
|
|||||||
//if (strlen($content) != 1) {
|
//if (strlen($content) != 1) {
|
||||||
// return false;
|
// return false;
|
||||||
//}
|
//}
|
||||||
$current['content'] = (bool) ord($content[0]);
|
$current['content'] = (bool) ord($content[$content_pos]);
|
||||||
break;
|
break;
|
||||||
case FILE_ASN1_TYPE_INTEGER:
|
case FILE_ASN1_TYPE_INTEGER:
|
||||||
case FILE_ASN1_TYPE_ENUMERATED:
|
case FILE_ASN1_TYPE_ENUMERATED:
|
||||||
$current['content'] = new Math_BigInteger($content, -256);
|
$current['content'] = new Math_BigInteger(substr($content, $content_pos), -256);
|
||||||
break;
|
break;
|
||||||
case FILE_ASN1_TYPE_REAL: // not currently supported
|
case FILE_ASN1_TYPE_REAL: // not currently supported
|
||||||
return false;
|
return false;
|
||||||
@ -412,10 +415,10 @@ class File_ASN1
|
|||||||
// the number of unused bits in the final subsequent octet. The number shall be in the range zero to
|
// the number of unused bits in the final subsequent octet. The number shall be in the range zero to
|
||||||
// seven.
|
// seven.
|
||||||
if (!$constructed) {
|
if (!$constructed) {
|
||||||
$current['content'] = $content;
|
$current['content'] = substr($content, $content_pos);
|
||||||
} else {
|
} else {
|
||||||
$temp = $this->_decode_ber($content, $start);
|
$temp = $this->_decode_ber($content, $start, $content_pos);
|
||||||
$length-= strlen($content);
|
$length-= (strlen($content) - $content_pos);
|
||||||
$last = count($temp) - 1;
|
$last = count($temp) - 1;
|
||||||
for ($i = 0; $i < $last; $i++) {
|
for ($i = 0; $i < $last; $i++) {
|
||||||
// all subtags should be bit strings
|
// all subtags should be bit strings
|
||||||
@ -433,13 +436,13 @@ class File_ASN1
|
|||||||
break;
|
break;
|
||||||
case FILE_ASN1_TYPE_OCTET_STRING:
|
case FILE_ASN1_TYPE_OCTET_STRING:
|
||||||
if (!$constructed) {
|
if (!$constructed) {
|
||||||
$current['content'] = $content;
|
$current['content'] = substr($content, $content_pos);
|
||||||
} else {
|
} else {
|
||||||
$current['content'] = '';
|
$current['content'] = '';
|
||||||
$length = 0;
|
$length = 0;
|
||||||
while (substr($content, 0, 2) != "\0\0") {
|
while (substr($content, $content_pos, 2) != "\0\0") {
|
||||||
$temp = $this->_decode_ber($content, $length + $start);
|
$temp = $this->_decode_ber($content, $length + $start, $content_pos);
|
||||||
$this->_string_shift($content, $temp['length']);
|
$content_pos += $temp['length'];
|
||||||
// all subtags should be octet strings
|
// all subtags should be octet strings
|
||||||
//if ($temp['type'] != FILE_ASN1_TYPE_OCTET_STRING) {
|
//if ($temp['type'] != FILE_ASN1_TYPE_OCTET_STRING) {
|
||||||
// return false;
|
// return false;
|
||||||
@ -447,7 +450,7 @@ class File_ASN1
|
|||||||
$current['content'].= $temp['content'];
|
$current['content'].= $temp['content'];
|
||||||
$length+= $temp['length'];
|
$length+= $temp['length'];
|
||||||
}
|
}
|
||||||
if (substr($content, 0, 2) == "\0\0") {
|
if (substr($content, $content_pos, 2) == "\0\0") {
|
||||||
$length+= 2; // +2 for the EOC
|
$length+= 2; // +2 for the EOC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -462,26 +465,28 @@ class File_ASN1
|
|||||||
case FILE_ASN1_TYPE_SET:
|
case FILE_ASN1_TYPE_SET:
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
$current['content'] = array();
|
$current['content'] = array();
|
||||||
while (strlen($content)) {
|
$content_len = strlen($content);
|
||||||
|
while ($content_pos < $content_len) {
|
||||||
// if indefinite length construction was used and we have an end-of-content string next
|
// if indefinite length construction was used and we have an end-of-content string next
|
||||||
// see paragraphs 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2
|
// see paragraphs 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2
|
||||||
if (!isset($current['headerlength']) && substr($content, 0, 2) == "\0\0") {
|
if (!isset($current['headerlength']) && substr($content, $content_pos, 2) == "\0\0") {
|
||||||
$length = $offset + 2; // +2 for the EOC
|
$length = $offset + 2; // +2 for the EOC
|
||||||
break 2;
|
break 2;
|
||||||
}
|
}
|
||||||
$temp = $this->_decode_ber($content, $start + $offset);
|
$temp = $this->_decode_ber($content, $start + $offset, $content_pos);
|
||||||
$this->_string_shift($content, $temp['length']);
|
$content_pos += $temp['length'];
|
||||||
$current['content'][] = $temp;
|
$current['content'][] = $temp;
|
||||||
$offset+= $temp['length'];
|
$offset+= $temp['length'];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FILE_ASN1_TYPE_OBJECT_IDENTIFIER:
|
case FILE_ASN1_TYPE_OBJECT_IDENTIFIER:
|
||||||
$temp = ord($this->_string_shift($content));
|
$temp = ord($content[$content_pos++]);
|
||||||
$current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40);
|
$current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40);
|
||||||
$valuen = 0;
|
$valuen = 0;
|
||||||
// process septets
|
// process septets
|
||||||
while (strlen($content)) {
|
$content_len = strlen($content);
|
||||||
$temp = ord($this->_string_shift($content));
|
while ($content_pos < $content_len) {
|
||||||
|
$temp = ord($content[$content_pos++]);
|
||||||
$valuen <<= 7;
|
$valuen <<= 7;
|
||||||
$valuen |= $temp & 0x7F;
|
$valuen |= $temp & 0x7F;
|
||||||
if (~$temp & 0x80) {
|
if (~$temp & 0x80) {
|
||||||
@ -522,11 +527,11 @@ class File_ASN1
|
|||||||
case FILE_ASN1_TYPE_UTF8_STRING:
|
case FILE_ASN1_TYPE_UTF8_STRING:
|
||||||
// ????
|
// ????
|
||||||
case FILE_ASN1_TYPE_BMP_STRING:
|
case FILE_ASN1_TYPE_BMP_STRING:
|
||||||
$current['content'] = $content;
|
$current['content'] = substr($content, $content_pos);
|
||||||
break;
|
break;
|
||||||
case FILE_ASN1_TYPE_UTC_TIME:
|
case FILE_ASN1_TYPE_UTC_TIME:
|
||||||
case FILE_ASN1_TYPE_GENERALIZED_TIME:
|
case FILE_ASN1_TYPE_GENERALIZED_TIME:
|
||||||
$current['content'] = $this->_decodeTime($content, $tag);
|
$current['content'] = $this->_decodeTime(substr($content, $content_pos), $tag);
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user