Skip to content

Instantly share code, notes, and snippets.

@alexwright
Created May 5, 2012 19:18
Show Gist options
  • Save alexwright/2604853 to your computer and use it in GitHub Desktop.
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.
<?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