Subversion Repositories uuid_mac_utils

Rev

Rev 13 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  * OidDerConverter.class.php, Version 1.1.1; Based on version 1.3+viathinksoft11 of oid.c
  5.  * Copyright 2014-2022 Daniel Marschall, ViaThinkSoft
  6.  *
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *     http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  */
  19.  
  20.  
  21. // Note: Leading zeros are permitted in dotted notation.
  22.  
  23. // TODO: define output format as parameter; don't force the user to use hexStrToArray
  24.  
  25. class OidDerConverter {
  26.  
  27.         /**
  28.          * @arg $str "\x12\x23" or "12 34"
  29.          * @return array(0x12, 0x23)
  30.          */
  31.         // Doesn't need to be public, but it is a nice handy function, especially in the testcases
  32.         public static function hexStrToArray($str) {
  33.                 $out = array();
  34.                 $str = str_replace('\x', '', $str);
  35.                 $str = trim($str);
  36.                 $str = str_replace(' ', '', $str);
  37.                 $ary = str_split($str, 2);
  38.                 foreach ($ary as &$a) {
  39.                         $a = hexdec($a);
  40.                 }
  41.                 return $ary;
  42.         }
  43.  
  44.         /**
  45.          * @return string|false Outputs .<oid> for an absolute OID and <oid> for a relative OID.
  46.          */
  47.         public static function derToOID($abBinary, $verbose=false) {
  48.                 $output_oid = array();
  49.                 $output_absolute = true;
  50.  
  51.                 if (count($abBinary) < 3) {
  52.                         if ($verbose) fprintf(STDERR, "Encoded OID must have at least three bytes!\n");
  53.                         return false;
  54.                 }
  55.  
  56.                 $nBinary = count($abBinary);
  57.                 $ll = gmp_init(0);
  58.                 $fOK = false;
  59.                 $fSub = 0; // Subtract value from next number output. Used when encoding {2 48} and up
  60.  
  61.                 // 0 = Universal Class Identifier Tag (can be more than 1 byte, but not in our case)
  62.                 // 1 = Length part (may have more than 1 byte!)
  63.                 // 2 = First two arc encoding
  64.                 // 3 = Encoding of arc three and higher
  65.                 $part = 0;
  66.  
  67.                 $lengthbyte_count = 0;
  68.                 $lengthbyte_pos = 0;
  69.                 $lengthfinished = false;
  70.  
  71.                 $arcBeginning = true;
  72.  
  73.                 $isRelative = null; // to avoid that phpstan complains
  74.  
  75.                 foreach ($abBinary as $nn => &$pb) {
  76.                         if ($part == 0) { // Class Tag
  77.                                 // Leading octet
  78.                                 // Bit 7 / Bit 6 = Universal (00), Application (01), Context (10), Private(11)
  79.                                 // Bit 5 = Primitive (0), Constructed (1)
  80.                                 // Bit 4..0 = 00000 .. 11110 => Tag 0..30, 11111 for Tag > 30 (following bytes with the highest bit as "more" bit)
  81.                                 // --> We don't need to respect 11111 (class-tag encodes in more than 1 octet)
  82.                                 //     as we terminate when the tag is not of type OID or RELATEIVE-OID
  83.                                 // See page 396 of "ASN.1 - Communication between Heterogeneous Systems" by Olivier Dubuisson.
  84.  
  85.                                 // Class: 8. - 7. bit
  86.                                 // 0 (00) = Universal
  87.                                 // 1 (01) = Application
  88.                                 // 2 (10) = Context
  89.                                 // 3 (11) = Private
  90.                                 $cl = (($pb & 0xC0) >> 6) & 0x03;
  91.                                 if ($cl != 0) {
  92.                                         if ($verbose) fprintf(STDERR, "Error at type: The OID tags are only defined as UNIVERSAL class tags.\n");
  93.                                         return false;
  94.                                 }
  95.  
  96.                                 // Primitive/Constructed: 6. bit
  97.                                 // 0 = Primitive
  98.                                 // 1 = Constructed
  99.                                 $pc = $pb & 0x20;
  100.                                 if ($pc != 0) {
  101.                                         if ($verbose) fprintf(STDERR, "Error at type: OIDs must be primitive, not constructed.\n");
  102.                                         return false;
  103.                                 }
  104.  
  105.                                 // Tag number: 5. - 1. bit
  106.                                 $tag = $pb & 0x1F;
  107.                                 if ($tag == 0x0D) {
  108.                                         $isRelative = true;
  109.                                 } else if ($tag == 0x06) {
  110.                                         $isRelative = false;
  111.                                 } else {
  112.                                         if ($verbose) fprintf(STDERR, "Error at type: The tag number is neither an absolute OID (0x06) nor a relative OID (0x0D).\n");
  113.                                         return false;
  114.                                 }
  115.  
  116.                                 // Output
  117.                                 $output_absolute = !$isRelative;
  118.  
  119.                                 $part++;
  120.                         } else if ($part == 1) { // Length
  121.                                 // Find out the length and save it into $ll
  122.  
  123.                                 // [length] is encoded as follows:
  124.                                 //  0x00 .. 0x7F = The actual length is in this byte, followed by [data].
  125.                                 //  0x80 + n     = The length of [data] is spread over the following 'n' bytes. (0 < n < 0x7F)
  126.                                 //  0x80         = "indefinite length" (only constructed form) -- Invalid
  127.                                 //  0xFF         = Reserved for further implementations -- Invalid
  128.                                 //  See page 396 of "ASN.1 - Communication between Heterogeneous Systems" by Olivier Dubuisson.
  129.  
  130.                                 if ($nn == 1) { // The first length byte
  131.                                         $lengthbyte_pos = 0;
  132.                                         if (($pb & 0x80) != 0) {
  133.                                                 // 0x80 + n => The length is spread over the following 'n' bytes
  134.                                                 $lengthfinished = false;
  135.                                                 $lengthbyte_count = $pb & 0x7F;
  136.                                                 if ($lengthbyte_count == 0x00) {
  137.                                                         if ($verbose) fprintf(STDERR, "Length value 0x80 is invalid (\"indefinite length\") for primitive types.\n");
  138.                                                         return false;
  139.                                                 } else if ($lengthbyte_count == 0x7F) {
  140.                                                         if ($verbose) fprintf(STDERR, "Length value 0xFF is reserved for further extensions.\n");
  141.                                                         return false;
  142.                                                 }
  143.                                                 $fOK = false;
  144.                                         } else {
  145.                                                 // 0x01 .. 0x7F => The actual length
  146.  
  147.                                                 if ($pb == 0x00) {
  148.                                                         if ($verbose) fprintf(STDERR, "Length value 0x00 is invalid for an OID.\n");
  149.                                                         return false;
  150.                                                 }
  151.  
  152.                                                 $ll = gmp_init($pb);
  153.                                                 $lengthfinished = true;
  154.                                                 $lengthbyte_count = 0;
  155.                                                 $fOK = true;
  156.                                         }
  157.                                 } else {
  158.                                         if ($lengthbyte_count > $lengthbyte_pos) {
  159.                                                 $ll = gmp_mul($ll, 0x100);
  160.                                                 $ll = gmp_add($ll, $pb);
  161.                                                 $lengthbyte_pos++;
  162.                                         }
  163.  
  164.                                         if ($lengthbyte_count == $lengthbyte_pos) {
  165.                                                 $lengthfinished = true;
  166.                                                 $fOK = true;
  167.                                         }
  168.                                 }
  169.  
  170.                                 if ($lengthfinished) { // The length is now in $ll
  171.                                         if (gmp_cmp($ll, $nBinary - 2 - $lengthbyte_count) != 0) {
  172.                                                 if ($verbose) fprintf(STDERR, "Invalid length (%d entered, but %s expected)\n", $nBinary - 2, gmp_strval($ll, 10));
  173.                                                 return false;
  174.                                         }
  175.                                         $ll = gmp_init(0); // reset for later usage
  176.                                         $fOK = true;
  177.                                         $part++;
  178.                                         if ($isRelative) $part++; // Goto step 3!
  179.                                 }
  180.                         } else if ($part == 2) { // First two arcs
  181.                                 $first = floor($pb / 40);
  182.                                 $second = $pb % 40;
  183.                                 if ($first > 2) {
  184.                                         $first = 2;
  185.                                         $output_oid[] = $first;
  186.                                         $arcBeginning = true;
  187.  
  188.                                         if (($pb & 0x80) != 0) {
  189.                                                 // 2.48 and up => 2+ octets
  190.                                                 // Output in "part 3"
  191.  
  192.                                                 if ($pb == 0x80) {
  193.                                                         if ($verbose) fprintf(STDERR, "Encoding error. Illegal 0x80 paddings. (See Rec. ITU-T X.690, clause 8.19.2)\n");
  194.                                                         return false;
  195.                                                 } else {
  196.                                                         $arcBeginning = false;
  197.                                                 }
  198.  
  199.                                                 $ll = gmp_add($ll, ($pb & 0x7F));
  200.                                                 $fSub = 80;
  201.                                                 $fOK = false;
  202.                                         } else {
  203.                                                 // 2.0 till 2.47 => 1 octet
  204.                                                 $second = $pb - 80;
  205.                                                 $output_oid[] = $second;
  206.                                                 $arcBeginning = true;
  207.                                                 $fOK = true;
  208.                                                 $ll = gmp_init(0);
  209.                                         }
  210.                                 } else {
  211.                                         // 0.0 till 0.37 => 1 octet
  212.                                         // 1.0 till 1.37 => 1 octet
  213.                                         $output_oid[] = $first;
  214.                                         $output_oid[] = $second;
  215.                                         $arcBeginning = true;
  216.                                         $fOK = true;
  217.                                         $ll = gmp_init(0);
  218.                                 }
  219.                                 $part++;
  220.                         } else if ($part == 3) { // Arc three and higher
  221.                                 if (($pb & 0x80) != 0) {
  222.                                         if ($arcBeginning && ($pb == 0x80)) {
  223.                                                 if ($verbose) fprintf(STDERR, "Encoding error. Illegal 0x80 paddings. (See Rec. ITU-T X.690, clause 8.19.2)\n");
  224.                                                 return false;
  225.                                         } else {
  226.                                                 $arcBeginning = false;
  227.                                         }
  228.  
  229.                                         $ll = gmp_mul($ll, 0x80);
  230.                                         $ll = gmp_add($ll, ($pb & 0x7F));
  231.                                         $fOK = false;
  232.                                 } else {
  233.                                         $fOK = true;
  234.                                         $ll = gmp_mul($ll, 0x80);
  235.                                         $ll = gmp_add($ll, $pb);
  236.                                         $ll = gmp_sub($ll, $fSub);
  237.                                         $output_oid[] = gmp_strval($ll, 10);
  238.  
  239.                                         // Happens only if 0x80 paddings are allowed
  240.                                         // $fOK = gmp_cmp($ll, 0) >= 0;
  241.                                         $ll = gmp_init(0);
  242.                                         $fSub = 0;
  243.                                         $arcBeginning = true;
  244.                                 }
  245.                         }
  246.                 }
  247.  
  248.                 if (!$fOK) {
  249.                         if ($verbose) fprintf(STDERR, "Encoding error. The OID is not constructed properly.\n");
  250.                         return false;
  251.                 }
  252.  
  253.                 return ($output_absolute ? '.' : '').implode('.', $output_oid);
  254.         }
  255.  
  256.         // Doesn't need to be public, but it is a nice handy function, especially in the testcases
  257.         public static function hexarrayToStr($ary, $nCHex=false) {
  258.                 $str = '';
  259.                 if ($ary === false) return false;
  260.                 foreach ($ary as &$a) {
  261.                         if ($nCHex) {
  262.                                 $str .= sprintf("\"\\x%02X", $a);
  263.                         } else {
  264.                                 $str .= sprintf("%02X ", $a);
  265.                         }
  266.                 }
  267.                 return trim($str);
  268.         }
  269.  
  270.         public static function oidToDER($oid, $isRelative=false, $verbose=false) {
  271.                 if ($oid[0] == '.') { // MIB notation
  272.                         $oid = substr($oid, 1);
  273.                         $isRelative = false;
  274.                 }
  275.  
  276.                 $cl = 0x00; // Class. Always UNIVERSAL (00)
  277.  
  278.                 // Tag for Universal Class
  279.                 if ($isRelative) {
  280.                         $cl |= 0x0D;
  281.                 } else {
  282.                         $cl |= 0x06;
  283.                 }
  284.  
  285.                 $arcs = explode('.', $oid);
  286.  
  287.                 $b = 0;
  288.                 $isjoint = false;
  289.                 $abBinary = array();
  290.  
  291.                 if ((!$isRelative) && (count($arcs) < 2)) {
  292.                         if ($verbose) fprintf(STDERR, "Encoding error. The minimum depth of an encodeable absolute OID is 2. (e.g. 2.999)\n");
  293.                         return false;
  294.                 }
  295.  
  296.                 foreach ($arcs as $n => &$arc) {
  297.                         if (!preg_match('@^\d+$@', $arc)) {
  298.                                 if ($verbose) fprintf(STDERR, "Encoding error. Arc '%s' is invalid.\n", $arc);
  299.                                 return false;
  300.                         }
  301.  
  302.                         $l = gmp_init($arc, 10);
  303.  
  304.                         if ((!$isRelative) && ($n == 0)) {
  305.                                 if (gmp_cmp($l, 2) > 0) {
  306.                                         if ($verbose) fprintf(STDERR, "Encoding error. The top arc is limited to 0, 1 and 2.\n");
  307.                                         return false;
  308.                                 }
  309.                                 $b += 40 * gmp_intval($l);
  310.                                 $isjoint = gmp_cmp($l, 2) == 0;
  311.                         } else if ((!$isRelative) && ($n == 1)) {
  312.                                 if ((!$isjoint) && (gmp_cmp($l, 39) > 0)) {
  313.                                         if ($verbose) fprintf(STDERR, "Encoding error. The second arc is limited to 0..39 for root arcs 0 and 1.\n");
  314.                                         return false;
  315.                                 }
  316.  
  317.                                 if (gmp_cmp($l, 47) > 0) {
  318.                                         $l = gmp_add($l, 80);
  319.                                         self::makeBase128($l, 1, $abBinary);
  320.                                 } else {
  321.                                         $b += gmp_intval($l);
  322.                                         $abBinary[] = $b;
  323.                                 }
  324.                         } else {
  325.                                 self::makeBase128($l, 1, $abBinary);
  326.                         }
  327.                 }
  328.  
  329.                 $output = array();
  330.  
  331.                 // Write class-tag
  332.                 $output[] = $cl;
  333.  
  334.                 // Write length
  335.                 $nBinary = count($abBinary);
  336.                 if ($nBinary <= 0x7F) {
  337.                         $output[] = $nBinary;
  338.                 } else {
  339.                         $lengthCount = 0;
  340.                         $nBinaryWork = $nBinary;
  341.                         do {
  342.                                 $lengthCount++;
  343.                                 $nBinaryWork /= 0x100;
  344.                         } while ($nBinaryWork > 0);
  345.  
  346.                         if ($lengthCount >= 0x7F) {
  347.                                 if ($verbose) fprintf(STDERR, "The length cannot be encoded.\n");
  348.                                 return false;
  349.                         }
  350.  
  351.                         $output[] = 0x80 + $lengthCount;
  352.  
  353.                         $nBinaryWork = $nBinary;
  354.                         do {
  355.                                 $output[] = $nBinaryWork & 0xFF;
  356.                                 $nBinaryWork /= 0x100;
  357.                         } while ($nBinaryWork > 0);
  358.                 }
  359.  
  360.                 foreach ($abBinary as $b) {
  361.                         $output[] = $b;
  362.                 }
  363.  
  364.                 return $output;
  365.         }
  366.  
  367.         protected static function makeBase128($l, $first, &$abBinary) {
  368.                 if (gmp_cmp($l, 127) > 0) {
  369.                         $l2 = gmp_div($l, 128);
  370.                         self::makeBase128($l2 , 0, $abBinary);
  371.                 }
  372.                 $l = gmp_mod($l, 128);
  373.  
  374.                 if ($first) {
  375.                         $abBinary[] = gmp_intval($l);
  376.                 } else {
  377.                         $abBinary[] = 0x80 | gmp_intval($l);
  378.                 }
  379.         }
  380. }
  381.