Created
January 2, 2024 16:04
-
-
Save oelna/751d985c16d89a983dca0ba9a5d0e00e to your computer and use it in GitHub Desktop.
Cleaned-up version of the asn1.php script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// ASN.1 parsing library | |
// Attribution: http://www.krisbailey.com | |
// license: unknown | |
// modified: Mike Macgrivin [email protected] 6-oct-2010 to support Salmon auto-discovery | |
// from openssl public keys | |
class ASN_BASE { | |
public $asnData = null; | |
private $cursor = 0; | |
private $parent = null; | |
public static $ASN_MARKERS = array( | |
'ASN_UNIVERSAL' => 0x00, | |
'ASN_APPLICATION' => 0x40, | |
'ASN_CONTEXT' => 0x80, | |
'ASN_PRIVATE' => 0xC0, | |
'ASN_PRIMITIVE' => 0x00, | |
'ASN_CONSTRUCTOR' => 0x20, | |
'ASN_LONG_LEN' => 0x80, | |
'ASN_EXTENSION_ID' => 0x1F, | |
'ASN_BIT' => 0x80, | |
); | |
public static $ASN_TYPES = array( | |
1 => 'ASN_BOOLEAN', | |
2 => 'ASN_INTEGER', | |
3 => 'ASN_BIT_STR', | |
4 => 'ASN_OCTET_STR', | |
5 => 'ASN_NULL', | |
6 => 'ASN_OBJECT_ID', | |
9 => 'ASN_REAL', | |
10 => 'ASN_ENUMERATED', | |
13 => 'ASN_RELATIVE_OID', | |
48 => 'ASN_SEQUENCE', | |
49 => 'ASN_SET', | |
19 => 'ASN_PRINT_STR', | |
22 => 'ASN_IA5_STR', | |
23 => 'ASN_UTC_TIME', | |
24 => 'ASN_GENERAL_TIME', | |
); | |
function __construct($v = false) | |
{ | |
if (false !== $v) { | |
$this->asnData = $v; | |
if (is_array($this->asnData)) { | |
foreach ($this->asnData as $key => $value) { | |
if (is_object($value)) { | |
$this->asnData[$key]->setParent($this); | |
} | |
} | |
} else { | |
if (is_object($this->asnData)) { | |
$this->asnData->setParent($this); | |
} | |
} | |
} | |
} | |
public function setParent($parent) | |
{ | |
if (false !== $parent) { | |
$this->parent = $parent; | |
} | |
} | |
/** | |
* This function will take the markers and types arrays and | |
* dynamically generate classes that extend this class for each one, | |
* and also define constants for them. | |
*/ | |
public static function generateSubclasses() | |
{ | |
define('ASN_BASE', 0); | |
foreach (self::$ASN_MARKERS as $name => $bit) | |
self::makeSubclass($name, $bit); | |
foreach (self::$ASN_TYPES as $bit => $name) | |
self::makeSubclass($name, $bit); | |
} | |
/** | |
* Helper function for generateSubclasses() | |
*/ | |
public static function makeSubclass($name, $bit) | |
{ | |
define($name, $bit); | |
eval("class ".$name." extends ASN_BASE {}"); | |
} | |
/** | |
* This function reset's the internal cursor used for value iteration. | |
*/ | |
public function reset() | |
{ | |
$this->cursor = 0; | |
} | |
/** | |
* This function catches calls to get the value for the type, typeName, value, values, and data | |
* from the object. For type calls we just return the class name or the value of the constant that | |
* is named the same as the class. | |
*/ | |
public function __get($name) | |
{ | |
if ('type' == $name) { | |
// int flag of the data type | |
return constant(get_class($this)); | |
} elseif ('typeName' == $name) { | |
// name of the data type | |
return get_class($this); | |
} elseif ('value' == $name) { | |
// will always return one value and can be iterated over with: | |
// while ($v = $obj->value) { ... | |
// because $this->asnData["invalid key"] will return false | |
return is_array($this->asnData) ? $this->asnData[$this->cursor++] : $this->asnData; | |
} elseif ('values' == $name) { | |
// will always return an array | |
return is_array($this->asnData) ? $this->asnData : array($this->asnData); | |
} elseif ('data' == $name) { | |
// will always return the raw data | |
return $this->asnData; | |
} | |
} | |
/** | |
* Parse an ASN.1 binary string. | |
* | |
* This function takes a binary ASN.1 string and parses it into it's respective | |
* pieces and returns it. It can optionally stop at any depth. | |
* | |
* @param string $string The binary ASN.1 String | |
* @param int $level The current parsing depth level | |
* @param int $maxLevel The max parsing depth level | |
* @return ASN_BASE The array representation of the ASN.1 data contained in $string | |
*/ | |
public static function parseASNString($string=false, $level=1, $maxLevels=false){ | |
if (!class_exists('ASN_UNIVERSAL')) | |
self::generateSubclasses(); | |
if ($level>$maxLevels && $maxLevels) | |
return array(new ASN_BASE($string)); | |
$parsed = array(); | |
$endLength = strlen($string); | |
$bigLength = $length = $type = $dtype = $p = 0; | |
while ($p<$endLength){ | |
$type = ord($string[$p++]); | |
$dtype = ($type & 192) >> 6; | |
if ($type==0){ // if we are type 0, just continue | |
} else { | |
$length = ord($string[$p++]); | |
if (($length & ASN_LONG_LEN)==ASN_LONG_LEN){ | |
$tempLength = 0; | |
for ($x=0; $x<($length & (ASN_LONG_LEN-1)); $x++){ | |
$tempLength = ord($string[$p++]) + ($tempLength * 256); | |
} | |
$length = $tempLength; | |
} | |
$data = substr($string, $p, $length); | |
$parsed[] = self::parseASNData($type, $data, $level, $maxLevels); | |
$p = $p + $length; | |
} | |
} | |
return $parsed; | |
} | |
/** | |
* Parse an ASN.1 field value. | |
* | |
* This function takes a binary ASN.1 value and parses it according to it's specified type | |
* | |
* @param int $type The type of data being provided | |
* @param string $data The raw binary data string | |
* @param int $level The current parsing depth | |
* @param int $maxLevels The max parsing depth | |
* @return mixed The data that was parsed from the raw binary data string | |
*/ | |
public static function parseASNData($type, $data, $level, $maxLevels){ | |
$type = $type%50; // strip out context | |
switch ($type){ | |
default: | |
return new ASN_BASE($data); | |
case ASN_BOOLEAN: | |
return new ASN_BOOLEAN((bool)$data); | |
case ASN_INTEGER: | |
return new ASN_INTEGER(strtr(base64_encode($data),'+/','-_')); | |
// return new ASN_INTEGER(ord($data)); | |
case ASN_BIT_STR: | |
return new ASN_BIT_STR(self::parseASNString($data, $level+1, $maxLevels)); | |
case ASN_OCTET_STR: | |
return new ASN_OCTET_STR($data); | |
case ASN_NULL: | |
return new ASN_NULL(null); | |
case ASN_REAL: | |
return new ASN_REAL($data); | |
case ASN_ENUMERATED: | |
return new ASN_ENUMERATED(self::parseASNString($data, $level+1, $maxLevels)); | |
case ASN_RELATIVE_OID: // I don't really know how this works and don't have an example :-) | |
// so, lets just return it ... | |
return new ASN_RELATIVE_OID($data); | |
case ASN_SEQUENCE: | |
return new ASN_SEQUENCE(self::parseASNString($data, $level+1, $maxLevels)); | |
case ASN_SET: | |
return new ASN_SET(self::parseASNString($data, $level+1, $maxLevels)); | |
case ASN_PRINT_STR: | |
return new ASN_PRINT_STR($data); | |
case ASN_IA5_STR: | |
return new ASN_IA5_STR($data); | |
case ASN_UTC_TIME: | |
return new ASN_UTC_TIME($data); | |
case ASN_GENERAL_TIME: | |
return new ASN_GENERAL_TIME($data); | |
case ASN_OBJECT_ID: | |
return new ASN_OBJECT_ID(self::parseOID($data)); | |
} | |
} | |
/** | |
* Parse an ASN.1 OID value. | |
* | |
* This takes the raw binary string that represents an OID value and parses it into its | |
* dot notation form. example - 1.2.840.113549.1.1.5 | |
* look up OID's here: http://www.oid-info.com/ | |
* (the multi-byte OID section can be done in a more efficient way, I will fix it later) | |
* | |
* @param string $data The raw binary data string | |
* @return string The OID contained in $data | |
*/ | |
public static function parseOID($string){ | |
$ret = floor(ord($string[0])/40)."."; | |
$ret .= (ord($string[0]) % 40); | |
$build = array(); | |
$cs = 0; | |
for ($i=1; $i<strlen($string); $i++){ | |
$v = ord($string[$i]); | |
if ($v>127){ | |
$build[] = ord($string[$i])-ASN_BIT; | |
} elseif ($build){ | |
// do the build here for multibyte values | |
$build[] = ord($string[$i])-ASN_BIT; | |
// you know, it seems there should be a better way to do this... | |
$build = array_reverse($build); | |
$num = 0; | |
for ($x=0; $x<count($build); $x++){ | |
$mult = $x==0?1:pow(256, $x); | |
if ($x+1==count($build)){ | |
$value = ((($build[$x] & (ASN_BIT-1)) >> $x)) * $mult; | |
} else { | |
$value = ((($build[$x] & (ASN_BIT-1)) >> $x) ^ ($build[$x+1] << (7 - $x) & 255)) * $mult; | |
} | |
$num += $value; | |
} | |
$ret .= ".".$num; | |
$build = array(); // start over | |
} else { | |
$ret .= ".".$v; | |
$build = array(); | |
} | |
} | |
return $ret; | |
} | |
public static function printASN($x, $indent=''){ | |
if (is_object($x)) { | |
echo $indent.$x->typeName."\n"; | |
if (ASN_NULL == $x->type) return; | |
if (is_array($x->data)) { | |
while ($d = $x->value) { | |
echo self::printASN($d, $indent.'. '); | |
} | |
$x->reset(); | |
} else { | |
echo self::printASN($x->data, $indent.'. '); | |
} | |
} elseif (is_array($x)) { | |
foreach ($x as $d) { | |
echo self::printASN($d, $indent); | |
} | |
} else { | |
if (preg_match('/[^[:print:]]/', $x)) // if we have non-printable characters that would | |
$x = base64_encode($x); // mess up the console, then print the base64 of them... | |
echo $indent.$x."\n"; | |
} | |
} | |
} | |
function accum($s) { | |
$result = strtr(base64_encode($s),'+/=','-_,'); | |
return $result; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment