Subversion Repositories uuid_mac_utils

Rev

Rev 5 | Rev 11 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

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