Created
May 5, 2012 19:18
-
-
Save alexwright/2604853 to your computer and use it in GitHub Desktop.
PHP class(es) to parse and generate some pretty basic ASN.1 stuff.
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 | |
class ASN { | |
public static function decode ($binary_string) | |
{ | |
$parser = new ASN_Parser($binary_string); | |
return $parser->parse(); | |
} | |
public static function encode ($asn_data) | |
{ | |
$generator = new ASN_Generator(); | |
return $generator->generate($asn_data); | |
} | |
} | |
class ASN_Generator { | |
public function generate ($asn_data) | |
{ | |
$bin = ''; | |
foreach ($asn_data AS $d) | |
{ | |
switch ($d->type) | |
{ | |
case 'sequence': | |
echo "sequence\n"; | |
$bin .= chr(0x30); | |
$payload = $this->generate($d->value); | |
$bin .= $this->encode_length(strlen($payload)); | |
$bin .= $payload; | |
break; | |
case 'integer': | |
echo "int\n"; | |
$bin .= chr(0x02); | |
$bin .= $this->encode_length(count($d->value)); | |
$bin .= $this->array_to_bin($d->value); | |
break; | |
case 'object': | |
echo "object\n"; | |
$bin .= chr(0x06); | |
$bin .= $this->encode_length(count($d->value)); | |
$bin .= $this->array_to_bin($d->value); | |
break; | |
case 'null': | |
echo "null\n"; | |
$bin .= chr(0x05); | |
$bin .= $this->encode_length(0); | |
break; | |
case 'bitstring': | |
$bin .= chr(0x03); | |
$payload = chr(0x00); | |
$payload .= $this->generate($d->value); | |
$bin .= $this->encode_length(strlen($payload)); | |
$bin .= $payload; | |
break; | |
default: | |
throw new Exception('Unknow tag type: ' . $d->type); | |
} | |
} | |
return $bin; | |
} | |
private function array_to_bin ($array) | |
{ | |
$bin = ''; | |
foreach ($array AS $i => $b) | |
{ | |
$bin .= chr($b); | |
} | |
return $bin; | |
} | |
private function encode_length ($length) | |
{ | |
// 513 = 0x82, 0x02, 0x01 | |
echo "length: {$length}\n"; | |
if ($length <= 127) | |
{ | |
return chr($length); | |
} | |
$bytes = str_split(sprintf('%08x', $length), 2); | |
while ($bytes[0] == '00') | |
{ | |
array_shift($bytes); | |
} | |
$first_byte = count($bytes); | |
$first_byte |= 128; | |
array_unshift($bytes, sprintf('%02x', $first_byte)); | |
$bin = ''; | |
foreach ($bytes AS $i => $b) | |
{ | |
$bin .= chr(hexdec($b)); | |
} | |
return $bin; | |
} | |
} | |
class ASN_Parser { | |
private $bytes; | |
private $index = 0; | |
public function __construct ($bytes) | |
{ | |
$this->bytes = $bytes; | |
$this->return = array(); | |
} | |
private function push ($type, $value) | |
{ | |
array_push($this->return, (object)array( | |
'type' => $type, | |
'value' => $value, | |
)); | |
} | |
public function parse () | |
{ | |
$v = array(); | |
$i = 0; | |
do | |
{ | |
$byte = $this->next(); | |
if ($byte === FALSE) | |
{ | |
break; | |
} | |
switch ($byte) | |
{ | |
case 0x30: | |
$length = $this->get_length(); | |
$buffer = array(); | |
printf("Sequence %d bytes\n", $length); | |
//break; | |
while ($length > 0) | |
{ | |
$buffer[] = chr($this->next(FALSE)); | |
$length--; | |
} | |
$buffer = implode('', $buffer); | |
/* | |
var_dump(array( | |
'sequence' => strlen($buffer), | |
)); | |
*/ | |
$parse = new ASN_Parser($buffer); | |
$d = $parse->parse(); | |
$this->push('sequence', $d); | |
break; | |
case 0x06: | |
// objects | |
$length = $this->get_length(); | |
printf("Object %d bytes\n", $length); | |
$object = $this->get_bytes($length, TRUE); | |
$this->push('object', $object); | |
break; | |
case 0x05: | |
// null | |
$length = $this->get_length(); | |
printf("Null %d bytes\n", $length); | |
if ($length == 0) | |
{ | |
$this->push('null', NULL); | |
} | |
else | |
{ | |
$this->get_bytes($length); | |
throw new Exception("I don't think this can happen."); | |
} | |
break; | |
case 0x03: | |
// bit string | |
$length = $this->get_length(); | |
$null_byte = $this->next(); | |
printf("BitString %d bytes\n", $length); | |
$length--; | |
$buffer = array(); | |
while ($length > 0) | |
{ | |
$buffer[] = chr($this->next(FALSE)); | |
$length--; | |
} | |
$buffer = implode('', $buffer); | |
/* | |
$bit_string = $this->get_bytes($length); | |
*/ | |
$parse = new ASN_Parser($buffer); | |
$d = $parse->parse(); | |
$this->push('bitstring', $d); | |
break; | |
case 0x02: | |
// integer | |
$length = $this->get_length(); | |
$int = $this->get_bytes($length, TRUE); | |
$this->push('integer', $int); | |
/* | |
array( | |
sprintf("%d bytes", $length), | |
count($int), | |
)); | |
*/ | |
break; | |
default: | |
printf("Unknow tag: %02x\n", $byte); | |
exit; | |
break; | |
} | |
$i++; | |
} | |
while ($byte !== FALSE); | |
return $this->return; | |
} | |
private function get_length () | |
{ | |
$b = $this->next(); | |
if ($b === 128) | |
throw new Exception('Variable length tokens not implemented'); | |
if ($b & 128) | |
{ | |
$bytes = $b & 0x7f; | |
$length = 0; | |
for ($o=0; $o<$bytes; $o++) | |
{ | |
$length <<= 8; | |
$length += $this->next(); | |
} | |
return $length; | |
} | |
else | |
{ | |
return $b; | |
} | |
} | |
private function next($debug = FALSE) | |
{ | |
if ($this->index >= strlen($this->bytes)) | |
{ | |
return FALSE; | |
} | |
if ($debug) | |
printf("next: %02x, %02x\n", ord($this->bytes[$this->index]), $this->bytes[$this->index]); | |
return ord($this->bytes[$this->index++]); | |
} | |
private function get_bytes ($length, $array = FALSE) | |
{ | |
if ($array) | |
$return = array(); | |
else | |
$return = ''; | |
while ($length > 0) | |
{ | |
if ($array) | |
$return[] = $this->next(FALSE); | |
else | |
$return .= $this->next(FALSE); | |
$length--; | |
} | |
return $return; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment