Merge branch '1.0' into 2.0

This commit is contained in:
terrafrost 2016-06-26 20:34:15 -05:00
commit 17a14eceb2
2 changed files with 120 additions and 38 deletions

View File

@ -224,14 +224,15 @@ class 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;
@ -243,23 +244,24 @@ class 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;
@ -268,11 +270,12 @@ class 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
@ -305,7 +308,7 @@ class 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;
@ -314,7 +317,7 @@ class 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(
@ -338,11 +341,11 @@ class 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 self::TYPE_INTEGER: case self::TYPE_INTEGER:
case self::TYPE_ENUMERATED: case self::TYPE_ENUMERATED:
$current['content'] = new BigInteger($content, -256); $current['content'] = new BigInteger(substr($content, $content_pos), -256);
break; break;
case self::TYPE_REAL: // not currently supported case self::TYPE_REAL: // not currently supported
return false; return false;
@ -351,10 +354,10 @@ class 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
@ -372,13 +375,13 @@ class ASN1
break; break;
case self::TYPE_OCTET_STRING: case self::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'] != self::TYPE_OCTET_STRING) { //if ($temp['type'] != self::TYPE_OCTET_STRING) {
// return false; // return false;
@ -386,7 +389,7 @@ class 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
} }
} }
@ -401,26 +404,28 @@ class ASN1
case self::TYPE_SET: case self::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 self::TYPE_OBJECT_IDENTIFIER: case self::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) {
@ -461,11 +466,11 @@ class ASN1
case self::TYPE_UTF8_STRING: case self::TYPE_UTF8_STRING:
// ???? // ????
case self::TYPE_BMP_STRING: case self::TYPE_BMP_STRING:
$current['content'] = $content; $current['content'] = substr($content, $content_pos);
break; break;
case self::TYPE_UTC_TIME: case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_TIME: case self::TYPE_GENERALIZED_TIME:
$current['content'] = $this->_decodeTime($content, $tag); $current['content'] = $this->_decodeTime(substr($content, $content_pos), $tag);
default: default:
} }

View File

@ -1484,7 +1484,9 @@ class X509
$this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); $this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
$this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1); if ($this->_isSubArrayValid($x509, 'tbsCertificate/extensions')) {
$this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1);
}
$this->_mapInDNs($x509, 'tbsCertificate/issuer/rdnSequence', $asn1); $this->_mapInDNs($x509, 'tbsCertificate/issuer/rdnSequence', $asn1);
$this->_mapInDNs($x509, 'tbsCertificate/subject/rdnSequence', $asn1); $this->_mapInDNs($x509, 'tbsCertificate/subject/rdnSequence', $asn1);
@ -1588,9 +1590,9 @@ class X509
*/ */
function _mapInExtensions(&$root, $path, $asn1) function _mapInExtensions(&$root, $path, $asn1)
{ {
$extensions = &$this->_subArray($root, $path); $extensions = &$this->_subArrayUnchecked($root, $path);
if (is_array($extensions)) { if ($extensions) {
for ($i = 0; $i < count($extensions); $i++) { for ($i = 0; $i < count($extensions); $i++) {
$id = $extensions[$i]['extnId']; $id = $extensions[$i]['extnId'];
$value = &$extensions[$i]['extnValue']; $value = &$extensions[$i]['extnValue'];
@ -1722,7 +1724,7 @@ class X509
if ($mapped !== false) { if ($mapped !== false) {
$values[$j] = $mapped; $values[$j] = $mapped;
} }
if ($id == 'pkcs-9-at-extensionRequest') { if ($id == 'pkcs-9-at-extensionRequest' && $this->_isSubArrayValid($values, $j)) {
$this->_mapInExtensions($values, $j, $asn1); $this->_mapInExtensions($values, $j, $asn1);
} }
} elseif ($map) { } elseif ($map) {
@ -3240,11 +3242,18 @@ class X509
$this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
$this->_mapInDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1); $this->_mapInDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1);
$this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1); if ($this->_isSubArrayValid($crl, 'tbsCertList/crlExtensions')) {
$rclist = &$this->_subArray($crl, 'tbsCertList/revokedCertificates'); $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1);
if (is_array($rclist)) { }
foreach ($rclist as $i => $extension) { if ($this->_isSubArrayValid($crl, 'tbsCertList/revokedCertificates')) {
$this->_mapInExtensions($rclist, "$i/crlEntryExtensions", $asn1); $rclist_ref = &$this->_subArrayUnchecked($crl, 'tbsCertList/revokedCertificates');
if ($rclist_ref) {
$rclist = $crl['tbsCertList']['revokedCertificates'];
foreach ($rclist as $i => $extension) {
if ($this->_isSubArrayValid($rclist, "$i/crlEntryExtensions", $asn1)) {
$this->_mapInExtensions($rclist_ref, "$i/crlEntryExtensions", $asn1);
}
}
} }
} }
@ -3861,6 +3870,74 @@ class X509
$this->caFlag = true; $this->caFlag = true;
} }
/**
* Check for validity of subarray
*
* This is intended for use in conjunction with _subArrayUnchecked(),
* implementing the checks included in _subArray() but without copying
* a potentially large array by passing its reference by-value to is_array().
*
* @param array $root
* @param string $path
* @return boolean
* @access private
*/
function _isSubArrayValid($root, $path)
{
if (!is_array($root)) {
return false;
}
foreach (explode('/', $path) as $i) {
if (!is_array($root)) {
return false;
}
if (!isset($root[$i])) {
return true;
}
$root = $root[$i];
}
return true;
}
/**
* Get a reference to a subarray
*
* This variant of _subArray() does no is_array() checking,
* so $root should be checked with _isSubArrayValid() first.
*
* This is here for performance reasons:
* Passing a reference (i.e. $root) by-value (i.e. to is_array())
* creates a copy. If $root is an especially large array, this is expensive.
*
* @param array $root
* @param string $path absolute path with / as component separator
* @param bool $create optional
* @access private
* @return array|false
*/
function &_subArrayUnchecked(&$root, $path, $create = false)
{
$false = false;
foreach (explode('/', $path) as $i) {
if (!isset($root[$i])) {
if (!$create) {
return $false;
}
$root[$i] = array();
}
$root = &$root[$i];
}
return $root;
}
/** /**
* Get a reference to a subarray * Get a reference to a subarray
* *