ASN1: revamp how OIDs are handled

This commit is contained in:
terrafrost 2019-04-30 22:37:19 -05:00
parent 756b247446
commit e793461543
2 changed files with 126 additions and 39 deletions

View File

@ -515,24 +515,7 @@ class File_ASN1
} }
break; break;
case FILE_ASN1_TYPE_OBJECT_IDENTIFIER: case FILE_ASN1_TYPE_OBJECT_IDENTIFIER:
$temp = ord($content[$content_pos++]); $current['content'] = $this->_decodeOID(substr($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;
//}
break; break;
/* Each character string type shall be encoded as if it had been declared: /* Each character string type shall be encoded as if it had been declared:
[UNIVERSAL x] IMPLICIT OCTET STRING [UNIVERSAL x] IMPLICIT OCTET STRING
@ -1111,27 +1094,7 @@ class File_ASN1
$value = base64_decode($source); $value = base64_decode($source);
break; break;
case FILE_ASN1_TYPE_OBJECT_IDENTIFIER: case FILE_ASN1_TYPE_OBJECT_IDENTIFIER:
$oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids); $value = $this->_encodeOID($source);
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;
}
break; break;
case FILE_ASN1_TYPE_ANY: case FILE_ASN1_TYPE_ANY:
$loc = $this->location; $loc = $this->location;
@ -1230,6 +1193,108 @@ class File_ASN1
return pack('Ca*', 0x80 | strlen($temp), $temp); 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 Math_BigInteger(80);
}
$oid = array();
$pos = 0;
$len = strlen($content);
$n = new Math_BigInteger();
while ($pos < $len) {
$temp = ord($content[$pos++]);
$n = $n->bitwise_leftShift(7);
$n = $n->bitwise_or(new Math_BigInteger($temp & 0x7F));
if (~$temp & 0x80) {
$oid[] = $n;
$n = new Math_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 Math_BigInteger(0x7F);
$zero = new Math_BigInteger();
$forty = new Math_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 Math_BigInteger($part1);
$first = $first->multiply($forty);
$first = $first->add(new Math_BigInteger($part2));
array_unshift($parts, $first->toString());
$value = '';
foreach ($parts as $part) {
if (!$part) {
$temp = "\0";
} else {
$temp = '';
$part = new Math_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 (using UNIX time) * BER-decode the time (using UNIX time)
* *

View File

@ -341,4 +341,26 @@ class Unit_File_ASN1Test extends PhpseclibTestCase
$asn1 = new File_ASN1(); $asn1 = new File_ASN1();
$asn1->decodeBER($data); $asn1->decodeBER($data);
} }
/**
* @group github1367
*/
public function testOIDs()
{
// from the example in 8.19.5 in the following:
// https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=22
$orig = pack('H*', '813403');
$asn1 = new File_ASN1();
$new = $asn1->_decodeOID($orig);
$this->assertSame('2.100.3', $new);
$this->assertSame($orig, $asn1->_encodeOID($new));
// UUID OID from the following:
// https://healthcaresecprivacy.blogspot.com/2011/02/creating-and-using-unique-id-uuid-oid.html
$orig = '2.25.329800735698586629295641978511506172918';
$asn1 = new File_ASN1();
$new = $asn1->_encodeOID($orig);
$this->assertSame(pack('H*', '6983f09da7ebcfdee0c7a1a7b2c0948cc8f9d776'), $new);
$this->assertSame($orig, $asn1->_decodeOID($new));
}
} }