From c6aa3ffd1fbaf5b1064470d109d7e832f2eb965b Mon Sep 17 00:00:00 2001 From: Paragon Initiative Enterprises Date: Tue, 23 Apr 2024 15:40:05 -0400 Subject: [PATCH 1/3] Ensure no leading zeroes in content length Also fixes a PHP 8 deprecation --- lib/ASN1/ASNObject.php | 6 +++++- lib/ASN1/AbstractTime.php | 3 +++ lib/ASN1/Universal/OctetString.php | 14 +++++++++++++- tests/ASN1/ObjectTest.php | 8 ++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/lib/ASN1/ASNObject.php b/lib/ASN1/ASNObject.php index 3b7f162..f07e2cd 100644 --- a/lib/ASN1/ASNObject.php +++ b/lib/ASN1/ASNObject.php @@ -330,7 +330,11 @@ protected static function parseContentLength(&$binaryData, &$offsetIndex, $minim if (strlen($binaryData) <= $offsetIndex) { throw new ParserException('Can not parse content length (long form) from data: Offset index larger than input size', $offsetIndex); } - $contentLength = $contentLength->shiftLeft(8)->add(ord($binaryData[$offsetIndex++])); + $octet = ord($binaryData[$offsetIndex++]); + if ($i === 0 && $octet === 0) { + throw new ParserException('Content length cannot have leading zero bytes', $offsetIndex); + } + $contentLength = $contentLength->shiftLeft(8)->add($octet); } if ($contentLength->compare(PHP_INT_MAX) > 0) { diff --git a/lib/ASN1/AbstractTime.php b/lib/ASN1/AbstractTime.php index 8e721ae..f67ef26 100644 --- a/lib/ASN1/AbstractTime.php +++ b/lib/ASN1/AbstractTime.php @@ -23,6 +23,9 @@ abstract class AbstractTime extends ASNObject public function __construct($dateTime = null, $dateTimeZone = 'UTC') { if ($dateTime == null || is_string($dateTime)) { + if (is_null($dateTime)) { + $dateTime = 'NOW'; + } $timeZone = new DateTimeZone($dateTimeZone); $dateTimeObject = new DateTime($dateTime, $timeZone); if ($dateTimeObject == false) { diff --git a/lib/ASN1/Universal/OctetString.php b/lib/ASN1/Universal/OctetString.php index 5d69ae7..b07c0d8 100644 --- a/lib/ASN1/Universal/OctetString.php +++ b/lib/ASN1/Universal/OctetString.php @@ -47,18 +47,27 @@ public function getType() protected function calculateContentLength() { + if (is_null($this->value)) { + return 0; + } return strlen($this->value) / 2; } protected function getEncodedValue() { $value = $this->value; + if (is_null($value)) { + return ''; + } + if (strlen($value) & 1) { + $value = '0' . $value; + } $result = ''; //Actual content while (strlen($value) >= 2) { // get the hex value byte by byte from the string and and add it to binary result - $result .= chr(hexdec(substr($value, 0, 2))); + $result .= @chr(hexdec(substr($value, 0, 2))); $value = substr($value, 2); } @@ -67,6 +76,9 @@ protected function getEncodedValue() public function getContent() { + if (is_null($this->value)) { + return ''; + } return strtoupper($this->value); } diff --git a/tests/ASN1/ObjectTest.php b/tests/ASN1/ObjectTest.php index 9649948..7f27746 100644 --- a/tests/ASN1/ObjectTest.php +++ b/tests/ASN1/ObjectTest.php @@ -321,6 +321,14 @@ public function testFromBinaryExceedsMaxInt() ASNObject::fromBinary($bin); } + public function testWithLeadingZeroes() + { + $this->expectException(ParserException::class); + $this->expectExceptionMessage("ASN.1 Parser Exception at offset 3: Content length cannot have leading zero bytes"); + $bin = hex2bin('30820066023100814cc9a70febda342d4ada87fc39426f403d5e89808428460c1eca60c897bfd6728da14673854673d7d297ea944a15e202310084f5ef11d22f22d0548af6a50dbf2f6a1bb9054585af5e600c49cf35b1e69b712754dd781c837355ddd41c752193a7cd'); + ASNObject::fromBinary($bin); + } + /** * @depends testFromBinary */ From 346ac238ce40d36468709a37ff82dd7a29633e29 Mon Sep 17 00:00:00 2001 From: Paragon Initiative Enterprises Date: Tue, 23 Apr 2024 16:06:28 -0400 Subject: [PATCH 2/3] Do not tolerate extended lengths for short messages This is an invalid state --- lib/ASN1/ASNObject.php | 3 +++ tests/ASN1/ObjectTest.php | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/lib/ASN1/ASNObject.php b/lib/ASN1/ASNObject.php index f07e2cd..d338ad9 100644 --- a/lib/ASN1/ASNObject.php +++ b/lib/ASN1/ASNObject.php @@ -336,6 +336,9 @@ protected static function parseContentLength(&$binaryData, &$offsetIndex, $minim } $contentLength = $contentLength->shiftLeft(8)->add($octet); } + if ($nrOfLengthOctets < 2 && $contentLength->compare(0x80) < 0) { + throw new ParserException('Extended length used for short message', $offsetIndex); + } if ($contentLength->compare(PHP_INT_MAX) > 0) { throw new ParserException("Can not parse content length from data: length > maximum integer", $offsetIndex); diff --git a/tests/ASN1/ObjectTest.php b/tests/ASN1/ObjectTest.php index 7f27746..114d3e2 100644 --- a/tests/ASN1/ObjectTest.php +++ b/tests/ASN1/ObjectTest.php @@ -329,6 +329,14 @@ public function testWithLeadingZeroes() ASNObject::fromBinary($bin); } + public function testExtendedFormShortLength() + { + $this->expectException(ParserException::class); + $this->expectExceptionMessage('ASN.1 Parser Exception at offset 3: Extended length used for short message'); + $bin = hex2bin('30814502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db'); + ASNObject::fromBinary($bin); + } + /** * @depends testFromBinary */ From c6382e2231d21d1ecd75bac2797ce2110ea9bcee Mon Sep 17 00:00:00 2001 From: Paragon Initiative Enterprises Date: Tue, 23 Apr 2024 16:15:45 -0400 Subject: [PATCH 3/3] Update comments --- lib/ASN1/Universal/OctetString.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/ASN1/Universal/OctetString.php b/lib/ASN1/Universal/OctetString.php index b07c0d8..e12e722 100644 --- a/lib/ASN1/Universal/OctetString.php +++ b/lib/ASN1/Universal/OctetString.php @@ -59,14 +59,12 @@ protected function getEncodedValue() if (is_null($value)) { return ''; } - if (strlen($value) & 1) { - $value = '0' . $value; - } + // This appears to expect hex strings but sometimes is populated by binary data $result = ''; - //Actual content + // Actual content while (strlen($value) >= 2) { - // get the hex value byte by byte from the string and and add it to binary result + // get the hex value byte by byte from the string and add it to binary result $result .= @chr(hexdec(substr($value, 0, 2))); $value = substr($value, 2); }