krot

asn1

Mar 20th, 2020
638
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 8.62 KB | None | 0 0
  1. <?php
  2.  
  3. // ASN.1 parsing library
  4. // Attribution: http://www.krisbailey.com
  5. // license: unknown
  6. // modified: Mike Macgrivin hide@address.com 6-oct-2010 to support Salmon auto-discovery
  7. // from openssl public keys
  8.  
  9.  
  10. class ASN_BASE {
  11.     public $asnData = null;
  12.     private $cursor = 0;
  13.     private $parent = null;
  14.    
  15.     public static $ASN_MARKERS = array(
  16.         'ASN_UNIVERSAL'     => 0x00,
  17.         'ASN_APPLICATION'   => 0x40,
  18.         'ASN_CONTEXT'       => 0x80,
  19.         'ASN_PRIVATE'       => 0xC0,
  20.  
  21.         'ASN_PRIMITIVE'     => 0x00,
  22.         'ASN_CONSTRUCTOR'   => 0x20,
  23.  
  24.         'ASN_LONG_LEN'      => 0x80,
  25.         'ASN_EXTENSION_ID'  => 0x1F,
  26.         'ASN_BIT'       => 0x80,
  27.     );
  28.    
  29.     public static $ASN_TYPES = array(
  30.         1   => 'ASN_BOOLEAN',
  31.         2   => 'ASN_INTEGER',
  32.         3   => 'ASN_BIT_STR',
  33.         4   => 'ASN_OCTET_STR',
  34.         5   => 'ASN_NULL',
  35.         6   => 'ASN_OBJECT_ID',
  36.         9   => 'ASN_REAL',
  37.         10  => 'ASN_ENUMERATED',
  38.         13  => 'ASN_RELATIVE_OID',
  39.         48  => 'ASN_SEQUENCE',
  40.         49  => 'ASN_SET',
  41.         19  => 'ASN_PRINT_STR',
  42.         22  => 'ASN_IA5_STR',
  43.         23  => 'ASN_UTC_TIME',
  44.         24  => 'ASN_GENERAL_TIME',
  45.     );
  46.  
  47.     function __construct($v = false)
  48.     {
  49.         if (false !== $v) {
  50.             $this->asnData = $v;
  51.             if (is_array($this->asnData)) {
  52.                 foreach ($this->asnData as $key => $value) {
  53.                     if (is_object($value)) {
  54.                         $this->asnData[$key]->setParent($this);
  55.                     }
  56.                 }
  57.             } else {
  58.                 if (is_object($this->asnData)) {
  59.                     $this->asnData->setParent($this);
  60.                 }
  61.             }
  62.         }
  63.     }
  64.    
  65.     public function setParent($parent)
  66.     {
  67.         if (false !== $parent) {
  68.             $this->parent = $parent;
  69.         }
  70.     }
  71.    
  72.     /**
  73.      * This function will take the markers and types arrays and
  74.      * dynamically generate classes that extend this class for each one,
  75.      * and also define constants for them.
  76.      */
  77.     public static function generateSubclasses()
  78.     {
  79.         define('ASN_BASE', 0);
  80.         foreach (self::$ASN_MARKERS as $name => $bit)
  81.             self::makeSubclass($name, $bit);
  82.         foreach (self::$ASN_TYPES as $bit => $name)
  83.             self::makeSubclass($name, $bit);
  84.     }
  85.    
  86.     /**
  87.      * Helper function for generateSubclasses()
  88.      */
  89.     public static function makeSubclass($name, $bit)
  90.     {
  91.         define($name, $bit);
  92.         eval("class ".$name." extends ASN_BASE {}");
  93.     }
  94.    
  95.     /**
  96.      * This function reset's the internal cursor used for value iteration.
  97.      */
  98.     public function reset()
  99.     {
  100.         $this->cursor = 0;
  101.     }
  102.    
  103.     /**
  104.      * This function catches calls to get the value for the type, typeName, value, values, and data
  105.      * from the object.  For type calls we just return the class name or the value of the constant that
  106.      * is named the same as the class.
  107.      */
  108.     public function __get($name)
  109.     {
  110.         if ('type' == $name) {
  111.             // int flag of the data type
  112.             return constant(get_class($this));
  113.         } elseif ('typeName' == $name) {
  114.             // name of the data type
  115.             return get_class($this);
  116.         } elseif ('value' == $name) {
  117.             // will always return one value and can be iterated over with:
  118.             // while ($v = $obj->value) { ...
  119.             // because $this->asnData["invalid key"] will return false
  120.             return is_array($this->asnData) ? $this->asnData[$this->cursor++] : $this->asnData;
  121.         } elseif ('values' == $name) {
  122.             // will always return an array
  123.             return is_array($this->asnData) ? $this->asnData : array($this->asnData);
  124.         } elseif ('data' == $name) {
  125.             // will always return the raw data
  126.             return $this->asnData;
  127.         }
  128.     }
  129.  
  130.     /**
  131.      * Parse an ASN.1 binary string.
  132.      *
  133.      * This function takes a binary ASN.1 string and parses it into it's respective
  134.      * pieces and returns it.  It can optionally stop at any depth.
  135.      *
  136.      * @param   string  $string     The binary ASN.1 String
  137.      * @param   int $level      The current parsing depth level
  138.      * @param   int $maxLevel   The max parsing depth level
  139.      * @return  ASN_BASE    The array representation of the ASN.1 data contained in $string
  140.      */
  141.     public static function parseASNString($string=false, $level=1, $maxLevels=false){
  142.         if (!class_exists('ASN_UNIVERSAL'))
  143.             self::generateSubclasses();
  144.         if ($level>$maxLevels && $maxLevels)
  145.             return array(new ASN_BASE($string));
  146.         $parsed = array();
  147.         $endLength = strlen($string);
  148.         $bigLength = $length = $type = $dtype = $p = 0;
  149.         while ($p<$endLength){
  150.             $type = ord($string[$p++]);
  151.             $dtype = ($type & 192) >> 6;
  152.             if ($type==0){ // if we are type 0, just continue
  153.             } else {
  154.                 $length = ord($string[$p++]);
  155.                 if (($length & ASN_LONG_LEN)==ASN_LONG_LEN){
  156.                     $tempLength = 0;
  157.                     for ($x=0; $x<($length & (ASN_LONG_LEN-1)); $x++){
  158.                         $tempLength = ord($string[$p++]) + ($tempLength * 256);
  159.                     }
  160.                     $length = $tempLength;
  161.                 }
  162.                 $data = substr($string, $p, $length);
  163.                 $parsed[] = self::parseASNData($type, $data, $level, $maxLevels);
  164.                 $p = $p + $length;
  165.             }
  166.         }
  167.         return $parsed;
  168.     }
  169.  
  170.     /**
  171.      * Parse an ASN.1 field value.
  172.      *
  173.      * This function takes a binary ASN.1 value and parses it according to it's specified type
  174.      *
  175.      * @param   int $type       The type of data being provided
  176.      * @param   string  $data       The raw binary data string
  177.      * @param   int $level      The current parsing depth
  178.      * @param   int $maxLevels  The max parsing depth
  179.      * @return  mixed   The data that was parsed from the raw binary data string
  180.      */
  181.     public static function parseASNData($type, $data, $level, $maxLevels){
  182.         $type = $type%50; // strip out context
  183.         switch ($type){
  184.             default:
  185.                 return new ASN_BASE($data);
  186.             case ASN_BOOLEAN:
  187.                 return new ASN_BOOLEAN((bool)$data);
  188.             case ASN_INTEGER:
  189.                 return new ASN_INTEGER(strtr(base64_encode($data),'+/','-_'));
  190. //              return new ASN_INTEGER(ord($data));
  191.             case ASN_BIT_STR:
  192.                 return new ASN_BIT_STR(self::parseASNString($data, $level+1, $maxLevels));
  193.             case ASN_OCTET_STR:
  194.                 return new ASN_OCTET_STR($data);
  195.             case ASN_NULL:
  196.                 return new ASN_NULL(null);
  197.             case ASN_REAL:
  198.                 return new ASN_REAL($data);
  199.             case ASN_ENUMERATED:
  200.                 return new ASN_ENUMERATED(self::parseASNString($data, $level+1, $maxLevels));
  201.             case ASN_RELATIVE_OID: // I don't really know how this works and don't have an example :-)
  202.                         // so, lets just return it ...
  203.                 return new ASN_RELATIVE_OID($data);
  204.             case ASN_SEQUENCE:
  205.                 return new ASN_SEQUENCE(self::parseASNString($data, $level+1, $maxLevels));
  206.             case ASN_SET:
  207.                 return new ASN_SET(self::parseASNString($data, $level+1, $maxLevels));
  208.             case ASN_PRINT_STR:
  209.                 return new ASN_PRINT_STR($data);
  210.             case ASN_IA5_STR:
  211.                 return new ASN_IA5_STR($data);
  212.             case ASN_UTC_TIME:
  213.                 return new ASN_UTC_TIME($data);
  214.             case ASN_GENERAL_TIME:
  215.                 return new ASN_GENERAL_TIME($data);
  216.             case ASN_OBJECT_ID:
  217.                 return new ASN_OBJECT_ID(self::parseOID($data));
  218.         }
  219.     }
  220.  
  221.     /**
  222.      * Parse an ASN.1 OID value.
  223.      *
  224.      * This takes the raw binary string that represents an OID value and parses it into its
  225.      * dot notation form.  example - 1.2.840.113549.1.1.5
  226.      * look up OID's here: http://www.oid-info.com/
  227.      * (the multi-byte OID section can be done in a more efficient way, I will fix it later)
  228.      *
  229.      * @param   string  $data       The raw binary data string
  230.      * @return  string  The OID contained in $data
  231.      */
  232.     public static function parseOID($string){
  233.         $ret = floor(ord($string[0])/40).".";
  234.         $ret .= (ord($string[0]) % 40);
  235.         $build = array();
  236.         $cs = 0;   
  237.        
  238.         for ($i=1; $i<strlen($string); $i++){
  239.             $v = ord($string[$i]);
  240.             if ($v>127){
  241.                 $build[] = ord($string[$i])-ASN_BIT;
  242.             } elseif ($build){
  243.                 // do the build here for multibyte values
  244.                 $build[] = ord($string[$i])-ASN_BIT;
  245.                 // you know, it seems there should be a better way to do this...
  246.                 $build = array_reverse($build);
  247.                 $num = 0;
  248.                 for ($x=0; $x<count($build); $x++){
  249.                     $mult = $x==0?1:pow(256, $x);
  250.                     if ($x+1==count($build)){
  251.                         $value = ((($build[$x] & (ASN_BIT-1)) >> $x)) * $mult;
  252.                     } else {
  253.                         $value = ((($build[$x] & (ASN_BIT-1)) >> $x) ^ ($build[$x+1] << (7 - $x) & 255)) * $mult;
  254.                     }
  255.                     $num += $value;
  256.                 }
  257.                 $ret .= ".".$num;
  258.                 $build = array(); // start over
  259.             } else {
  260.                 $ret .= ".".$v;
  261.                 $build = array();
  262.             }
  263.         }
  264.         return $ret;
  265.     }
  266.    
  267.     public static function printASN($x, $indent=''){
  268.         if (is_object($x)) {
  269.             echo $indent.$x->typeName."\n";
  270.             if (ASN_NULL == $x->type) return;
  271.             if (is_array($x->data)) {
  272.                 while ($d = $x->value) {
  273.                     echo self::printASN($d, $indent.'.  ');
  274.                 }
  275.                 $x->reset();
  276.             } else {
  277.                 echo self::printASN($x->data, $indent.'.  ');
  278.             }
  279.         } elseif (is_array($x)) {
  280.             foreach ($x as $d) {
  281.                 echo self::printASN($d, $indent);
  282.             }
  283.         } else {
  284.             if (preg_match('/[^[:print:]]/', $x))   // if we have non-printable characters that would
  285.                 $x = base64_encode($x);     // mess up the console, then print the base64 of them...
  286.             echo $indent.$x."\n";
  287.         }
  288.     }
  289.  
  290.    
  291. }
  292.  
  293.  
  294. function accum($s) {
  295.     $result = strtr(base64_encode($s),'+/=','-_,');
  296.     return $result;
  297. }
Add Comment
Please, Sign In to add comment