Subversion Repositories uuid_mac_utils

Rev

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

  1. <?php
  2.  
  3. /*
  4.  * UUID utils for PHP
  5.  * Copyright 2011 - 2023 Daniel Marschall, ViaThinkSoft
  6.  * Version 2023-07-18
  7.  *
  8.  * Licensed under the Apache License, Version 2.0 (the "License");
  9.  * you may not use this file except in compliance with the License.
  10.  * You may obtain a copy of the License at
  11.  *
  12.  *     http://www.apache.org/licenses/LICENSE-2.0
  13.  *
  14.  * Unless required by applicable law or agreed to in writing, software
  15.  * distributed under the License is distributed on an "AS IS" BASIS,
  16.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17.  * See the License for the specific language governing permissions and
  18.  * limitations under the License.
  19.  */
  20.  
  21. # This library requires either the GMP extension (or BCMath if gmp_supplement.inc.php is present)
  22. // TODO: If we are on 64 bit PHP (PHP_INT_SIZE > 4), then replace GMP with normal PHP operations
  23.  
  24. if (file_exists($f = __DIR__ . '/mac_utils.inc.php')) include_once $f;
  25. else if (file_exists($f = __DIR__ . '/mac_utils.inc.phps')) include_once $f;
  26.  
  27. if (file_exists($f = __DIR__ . '/gmp_supplement.inc.php')) include_once $f;
  28. else if (file_exists($f = __DIR__ . '/gmp_supplement.inc.phps')) include_once $f;
  29.  
  30. if (file_exists($f = __DIR__ . '/OidDerConverter.class.php')) include_once $f;
  31. else if (file_exists($f = __DIR__ . '/OidDerConverter.class.phps')) include_once $f;
  32.  
  33. const UUID_NAMEBASED_NS_DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; // FQDN
  34. const UUID_NAMEBASED_NS_URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
  35. const UUID_NAMEBASED_NS_OID = '6ba7b812-9dad-11d1-80b4-00c04fd430c8';
  36. const UUID_NAMEBASED_NS_X500_DN = '6ba7b814-9dad-11d1-80b4-00c04fd430c8'; // "DER or text encoding" according to RFC4122bis
  37.  
  38. if (!function_exists('_random_int')) {
  39.         function _random_int($min, $max) {
  40.                 // This function tries a CSRNG and falls back to a RNG if no CSRNG is available
  41.                 try {
  42.                         return random_int($min, $max);
  43.                 } catch (Exception $e) {
  44.                         return mt_rand($min, $max);
  45.                 }
  46.         }
  47. }
  48.  
  49. function uuid_valid($uuid) {
  50.         $uuid = str_replace(array('-', '{', '}'), '', $uuid);
  51.         $uuid = strtoupper($uuid);
  52.         #$uuid = trim($uuid);
  53.  
  54.         if (strlen($uuid) != 32) return false;
  55.  
  56.         $uuid = preg_replace('@[0-9A-F]@i', '', $uuid);
  57.  
  58.         return ($uuid == '');
  59. }
  60.  
  61. function uuid_version($uuid) {
  62.         $uuid = uuid_canonize($uuid);
  63.         if (!$uuid) return false;
  64.         return substr($uuid, 19, 1);
  65. }
  66.  
  67. function uuid_info($uuid, $echo=true) {
  68.         if (!uuid_valid($uuid)) return false;
  69.  
  70.         $oid = uuid_to_oid($uuid);
  71.  
  72.         echo sprintf("%-32s %s\n", "Your input:", $uuid);
  73.         echo "\n";
  74.         echo "<u>Various notations:</u>\n";
  75.         echo "\n";
  76.         echo sprintf("%-32s %s\n", "URN:", 'urn:uuid:' . strtolower(oid_to_uuid(uuid_to_oid($uuid))));
  77.         echo sprintf("%-32s %s\n", "URI:", 'uuid:' . strtolower(oid_to_uuid(uuid_to_oid($uuid))));
  78.         echo sprintf("%-32s %s\n", "Microsoft GUID syntax:", '{' . strtoupper(oid_to_uuid(uuid_to_oid($uuid))) . '}');
  79.         echo sprintf("%-32s %s\n", "C++ struct syntax:", uuid_c_syntax($uuid));
  80.         echo "\n";
  81.         echo sprintf("%-32s %s\n", "As OID:", $oid);
  82.         if (class_exists('OidDerConverter')) {
  83.                 echo sprintf("%-32s %s\n", "DER encoding of OID:", OidDerConverter::hexarrayToStr(OidDerConverter::oidToDER($oid)));
  84.         }
  85.         echo "\n";
  86.         echo "<u>Interpretation of the UUID:</u>\n\n";
  87.  
  88.         if (!$echo) ob_start();
  89.  
  90.         #$uuid = trim($uuid);
  91.         # $uuid = str_replace(array('-', '{', '}'), '', $uuid);
  92.         $uuid = strtolower($uuid);
  93.         $uuid = preg_replace('@[^0-9A-F]@i', '', $uuid);
  94.  
  95.         $x = hexdec(substr($uuid, 16, 1));
  96.              if ($x >= 14 /* 0b1110 */) $variant = 3;
  97.         else if ($x >= 12 /* 0b110_ */) $variant = 2;
  98.         else if ($x >=  8 /* 0b10__ */) $variant = 1;
  99.         else if ($x >=  0 /* 0b0___ */) $variant = 0;
  100.         else $variant = -1; // should not happen
  101.  
  102.         if ($uuid == '00000000000000000000000000000000') {
  103.                 echo sprintf("%-32s %s\n", "Special Use:", "Nil UUID");
  104.                 echo "\n";
  105.         }
  106.         else if ($uuid == 'ffffffffffffffffffffffffffffffff') {
  107.                 echo sprintf("%-32s %s\n", "Special Use:", "Max UUID");
  108.                 echo "\n";
  109.         }
  110.  
  111.         switch ($variant) {
  112.                 case 0:
  113.                         echo sprintf("%-32s %s\n", "Variant:", "[0b0__] Network Computing System (NCS)");
  114.  
  115.                         /*
  116.                          * Internal structure of variant #0 UUIDs
  117.                          *
  118.                          * The first 6 octets are the number of 4 usec units of time that have
  119.                          * passed since 1/1/80 0000 GMT.  The next 2 octets are reserved for
  120.                          * future use.  The next octet is an address family.  The next 7 octets
  121.                          * are a host ID in the form allowed by the specified address family.
  122.                          *
  123.                          * Note that while the family field (octet 8) was originally conceived
  124.                          * of as being able to hold values in the range [0..255], only [0..13]
  125.                          * were ever used.  Thus, the 2 MSB of this field are always 0 and are
  126.                          * used to distinguish old and current UUID forms.
  127.                          */
  128.  
  129.                         /*
  130.                         Variant 0 UUID
  131.                         - 32 bit High Time
  132.                         - 16 bit Low Time
  133.                         - 16 bit Reserved
  134.                         -  1 bit Variant (fix 0b0)
  135.                         -  7 bit Family
  136.                         - 56 bit Node
  137.                         */
  138.  
  139.                         // Example of an UUID: 333a2276-0000-0000-0d00-00809c000000
  140.  
  141.                         // TODO: also show legacy format, e.g. 458487b55160.02.c0.64.02.03.00.00.00
  142.  
  143.                         # see also some notes at See https://github.com/cjsv/uuid/blob/master/Doc
  144.  
  145.                         /*
  146.                         NOTE: A generator is not possible, because there are no timestamps left!
  147.                         The last possible timestamp was:
  148.                             [0xFFFFFFFFFFFF] 2015-09-05 05:58:26'210655 GMT
  149.                         That is in the following UUID:
  150.                             ffffffff-ffff-0000-027f-000001000000
  151.                         Current timestamp generator:
  152.                             echo dechex(round((microtime(true)+315532800)*250000));
  153.                         */
  154.  
  155.                         # Timestamp: Count of 4us intervals since 01 Jan 1980 00:00:00 GMT
  156.                         # 1/0,000004 = 250000
  157.                         # Seconds between 1970 and 1980 : 315532800
  158.                         # 250000*315532800=78883200000000
  159.                         $timestamp = substr($uuid, 0, 12);
  160.                         $ts = gmp_init($timestamp, 16);
  161.                         $ts = gmp_add($ts, gmp_init("78883200000000", 10));
  162.                         $ms = gmp_mod($ts, gmp_init("250000", 10));
  163.                         $ts = gmp_div($ts, gmp_init("250000", 10));
  164.                         $ts = gmp_strval($ts, 10);
  165.                         $ms = gmp_strval($ms, 10);
  166.                         $ts = gmdate('Y-m-d H:i:s', intval($ts))."'".str_pad($ms, 6/*us*/, '0', STR_PAD_LEFT).' GMT';
  167.                         echo sprintf("%-32s %s\n", "Timestamp:", "[0x$timestamp] $ts");
  168.  
  169.                         $reserved = substr($uuid, 12, 4);
  170.                         echo sprintf("%-32s %s\n", "Reserved:", "[0x$reserved]");
  171.  
  172.                         $family_hex = substr($uuid, 16, 2);
  173.                         $family_dec = hexdec($family_hex);
  174.                         $nodeid_hex = substr($uuid, 18, 14);
  175.                         $nodeid_dec = hexdec($nodeid_hex);
  176.  
  177.                         // Sources:
  178.                         // - https://bitsavers.org/pdf/ibm/rs6000/aix_3.0/SC23-2206-0_AIX_Version_3_for_RS6000_Communications_Programming_Concepts_199003.pdf
  179.                         // - (For comparison) https://github.com/uuid6/uuid6-ietf-draft/issues/26#issuecomment-1062164457
  180.                         // - (For comparison) https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.addressfamily?view=net-7.0 [numbers 0..13 are mostly identical]
  181.  
  182.                         if ($family_dec == 0) {
  183.                                 # Microsoft's AdressFamily: Unspecified 0       Unspecified address family.
  184.                                 # AIX 3.0 Manual:  0   unspec = Unspecified
  185.                                 $family_name = 'socket_$unspec (Unspecified)';
  186.                                 $nodeid_desc = ''; // TODO: how to interprete the Node-ID of that family?
  187.                         }
  188.                         else if ($family_dec == 1) {
  189.                                 # Microsoft's AdressFamily: Unix        1       Unix local to host address.
  190.                                 # AIX 3.0 Manual:  1   unix = Local to host (pipes, portals)
  191.                                 $family_name = 'socket_$unix (Local to host, e.g. pipes, portals)';
  192.                                 $nodeid_desc = ''; // TODO: how to interprete the Node-ID of that family?
  193.                         }
  194.                         else if ($family_dec == 2) {
  195.                                 # Microsoft's AdressFamily: InterNetwork        2       Address for IP version 4.
  196.                                 # AIX 3.0 Manual:  2   ip = Internet Protocols
  197.                                 $family_name = 'socket_$internet (Internet Protocols, e.g. IPv4)';
  198.                                 // https://www.ibm.com/docs/en/aix/7.1?topic=u-uuid-gen-command-ncs (AIX 7.1) shows the following example output for /etc/ncs/uuid_gen -P
  199.                                 // := [
  200.                                 //    time_high := 16#458487df,
  201.                                 //    time_low := 16#9fb2,
  202.                                 //    reserved := 16#000,
  203.                                 //    family := chr(16#02),
  204.                                 //    host := [chr(16#c0), chr(16#64), chr(16#02), chr(16#03),
  205.                                 //             chr(16#00), chr(16#00), chr(16#00)]
  206.                                 //    ]
  207.                                 // This means that the IP address is 32 bits hex, and 32 bits are unused
  208.                                 $nodeid_desc = hexdec(substr($nodeid_hex,0,2)).'.'.
  209.                                                hexdec(substr($nodeid_hex,2,2)).'.'.
  210.                                                hexdec(substr($nodeid_hex,4,2)).'.'.
  211.                                                hexdec(substr($nodeid_hex,6,2));
  212.                                 $rest = substr($nodeid_hex,8,6);
  213.                                 if ($rest != '000000') $nodeid_desc .= " + unexpected rest 0x$rest";
  214.                         }
  215.                         else if ($family_dec == 3) {
  216.                                 # Microsoft's AdressFamily: ImpLink     3       ARPANET IMP address.
  217.                                 # AIX 3.0 Manual:  3   implink = ARPANET imp addresses
  218.                                 $family_name = 'socket_$implink (ARPANET imp addresses)';
  219.                                 $nodeid_desc = ''; // TODO: how to interprete the Node-ID of that family?
  220.                         }
  221.                         else if ($family_dec == 4) {
  222.                                 # Microsoft's AdressFamily: Pup 4       Address for PUP protocols.
  223.                                 # AIX 3.0 Manual:  4   pup = Pup protocols (for example, BSP)
  224.                                 $family_name = 'socket_$pup (Pup protocols, e.g. BSP)';
  225.                                 $nodeid_desc = ''; // TODO: how to interprete the Node-ID of that family?
  226.                         }
  227.                         else if ($family_dec == 5) {
  228.                                 # Microsoft's AdressFamily: Chaos       5       Address for MIT CHAOS protocols.
  229.                                 # AIX 3.0 Manual:  5   chaos = MIT CHAOS protocols
  230.                                 $family_name = 'socket_$chaos (MIT CHAOS protocols)';
  231.                                 $nodeid_desc = ''; // TODO: how to interprete the Node-ID of that family?
  232.                         }
  233.                         else if ($family_dec == 6) {
  234.                                 # Microsoft's AdressFamily: NS  6       Address for Xerox NS protocols.
  235.                                 # Microsoft's AdressFamily: Ipx 6       IPX or SPX address.
  236.                                 # AIX 3.0 Manual:  6   ns = XEROX NS protocols
  237.                                 $family_name = 'socket_$ns (XEROX NS protocols)';
  238.                                 $nodeid_desc = ''; // TODO: how to interprete the Node-ID of that family?
  239.                         }
  240.                         else if ($family_dec == 7) {
  241.                                 # Microsoft's AdressFamily: Osi 7       Address for OSI protocols.
  242.                                 # Microsoft's AdressFamily: Iso 7       Address for ISO protocols.
  243.                                 # AIX 3.0 Manual:  7   nbs = NBS protocols
  244.                                 $family_name = 'socket_$nbs (NBS protocols)';
  245.                                 $nodeid_desc = ''; // TODO: how to interprete the Node-ID of that family?
  246.                         }
  247.                         else if ($family_dec == 8) {
  248.                                 # Microsoft's AdressFamily: Ecma        8       European Computer Manufacturers Association (ECMA) address.
  249.                                 # AIX 3.0 Manual:  8   ecma = European computer manufacturers
  250.                                 $family_name = 'socket_$ecma (European computer manufacturers protocols)';
  251.                                 $nodeid_desc = ''; // TODO: how to interprete the Node-ID of that family?
  252.                         }
  253.                         else if ($family_dec == 9) {
  254.                                 # Microsoft's AdressFamily: DataKit     9       Address for Datakit protocols.
  255.                                 # AIX 3.0 Manual:  9   datakit = Datakit protocols
  256.                                 $family_name = 'socket_$datakit (Datakit protocols)';
  257.                                 $nodeid_desc = ''; // TODO: how to interprete the Node-ID of that family?
  258.                         }
  259.                         else if ($family_dec == 10) {
  260.                                 # Microsoft's AdressFamily: Ccitt       10      Addresses for CCITT protocols, such as X.25.
  261.                                 # AIX 3.0 Manual:  A   ccitt = CCITT protocols (for example, X.25)
  262.                                 $family_name = 'socket_$ccitt (CCITT protocols, e.g. X.25)';
  263.                                 $nodeid_desc = ''; // TODO: how to interprete the Node-ID of that family?
  264.                         }
  265.                         else if ($family_dec == 11) {
  266.                                 # Microsoft's AdressFamily: Sna 11      IBM SNA address.
  267.                                 # AIX 3.0 Manual:  B   sna = IBM SNA
  268.                                 $family_name = 'socket_$sna (IBM SNA)';
  269.                                 $nodeid_desc = ''; // TODO: how to interprete the Node-ID of that family?
  270.                         }
  271.                         else if ($family_dec == 12) {
  272.                                 # Microsoft's AdressFamily: DecNet      12      DECnet address.
  273.                                 # AIX 3.0 Manual:  C   unspec2 = Unspecified
  274.                                 $family_name = 'socket_$unspec2 (Unspecified)';
  275.                                 $nodeid_desc = ''; // TODO: how to interprete the Node-ID of that family?
  276.                         }
  277.                         else if ($family_dec == 13) {
  278.                                 # Microsoft's AdressFamily: DataLink    13      Direct data-link interface address.
  279.                                 # AIX 3.0 Manual:  D   dds = Domain DDS protocol
  280.                                 # Some also call this "Data Link" ... Is that correct?
  281.                                 $family_name = 'socket_$dds (Domain DDS protocol)';
  282.                                 // https://www.ibm.com/docs/en/aix/7.1?topic=u-uuid-gen-command-ncs (AIX 7.1) shows the following example output for /etc/ncs/uuid_gen -C
  283.                                 // = { 0x34dc23af,
  284.                                 //    0xf000,
  285.                                 //    0x0000,
  286.                                 //    0x0d,
  287.                                 //    {0x00, 0x00, 0x7c, 0x5f, 0x00, 0x00, 0x00} };
  288.                                 // https://github.com/cjsv/uuid/blob/master/Doc writes:
  289.                                 //    "Family 13 (dds) looks like node is 00 | nnnnnn 000000."
  290.  
  291.                                 $nodeid_desc = '';
  292.  
  293.                                 $start = substr($nodeid_hex,0,2);
  294.                                 if ($start != '00') $nodeid_desc .= "unexpected start 0x$start + ";
  295.  
  296.                                 $nodeid_desc .= ($nodeid_dec >> 24) & 0xFFFFFF;
  297.  
  298.                                 $rest = substr($nodeid_hex,8,6);
  299.                                 if ($rest != '000000') $nodeid_desc .= " + unexpected rest 0x$rest";
  300.                         } else {
  301.                                 $family_name = "Unknown (Family $family_dec)"; # There are probably no more families
  302.                                 $nodeid_desc = "Unknown";
  303.                         }
  304.                         echo sprintf("%-32s %s\n", "Family:", "[0x$family_hex] $family_name");
  305.  
  306.                         echo sprintf("%-32s %s\n", "Node ID:", "[0x$nodeid_hex] $nodeid_desc");
  307.  
  308.                         break;
  309.                 case 1:
  310.                         // TODO: Show byte order: 00112233-4455-6677-8899-aabbccddeeff => 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff
  311.  
  312.                         $version = hexdec(substr($uuid, 12, 1));
  313.  
  314.                         if ($version <= 2) {
  315.                                 echo sprintf("%-32s %s\n", "Variant:", "[0b10_] RFC 4122 (Leach-Mealling-Salz) / DCE 1.1");
  316.                         } else if (($version >= 3) && ($version <= 5)) {
  317.                                 echo sprintf("%-32s %s\n", "Variant:", "[0b10_] RFC 4122 (Leach-Mealling-Salz)");
  318.                         } else if (($version >= 6) && ($version <= 8)) {
  319.                                 echo sprintf("%-32s %s\n", "Variant:", "[0b10_] RFC 4122bis (Leach-Mealling-Peabody-Davis)");
  320.                         } else {
  321.                                 echo sprintf("%-32s %s\n", "Variant:", "[0b10_] RFC 4122?");
  322.                         }
  323.  
  324.                         switch ($version) {
  325.                                 case 6:
  326.                                         /*
  327.                                         Variant 1, Version 6 UUID
  328.                                         - 48 bit High Time
  329.                                         -  4 bit Version (fix 0x6)
  330.                                         - 12 bit Low Time
  331.                                         -  2 bit Variant (fix 0b10)
  332.                                         -  6 bit Clock Sequence High
  333.                                         -  8 bit Clock Sequence Low
  334.                                         - 48 bit MAC Address
  335.                                         */
  336.                                         echo sprintf("%-32s %s\n", "Version:", "[0x6] Reordered Time-Based");
  337.                                         $uuid = substr($uuid,  0, 8).'-'.
  338.                                                 substr($uuid,  8, 4).'-'.
  339.                                                 substr($uuid, 12, 4).'-'.
  340.                                                 substr($uuid, 16, 4).'-'.
  341.                                                 substr($uuid, 20, 12);
  342.                                         $uuid = uuid6_to_uuid1($uuid);
  343.                                         $uuid = str_replace('-', '', $uuid);
  344.  
  345.                                 /* fallthrough */
  346.                                 case 1:
  347.                                         /*
  348.                                         Variant 1, Version 1 UUID
  349.                                         - 32 bit Low Time
  350.                                         - 16 bit Mid Time
  351.                                         -  4 bit Version (fix 0x1)
  352.                                         - 12 bit High Time
  353.                                         -  2 bit Variant (fix 0b10)
  354.                                         -  6 bit Clock Sequence High
  355.                                         -  8 bit Clock Sequence Low
  356.                                         - 48 bit MAC Address
  357.                                         */
  358.  
  359.                                         if ($version == 1) echo sprintf("%-32s %s\n", "Version:", "[0x1] Time-based with unique host identifier");
  360.  
  361.                                         # Timestamp: Count of 100ns intervals since 15 Oct 1582 00:00:00
  362.                                         # 1/0,0000001 = 10000000
  363.                                         $timestamp = substr($uuid, 13, 3).substr($uuid, 8, 4).substr($uuid, 0, 8);
  364.                                         $ts = gmp_init($timestamp, 16);
  365.                                         $ts = gmp_sub($ts, gmp_init("122192928000000000", 10));
  366.                                         $ms = gmp_mod($ts, gmp_init("10000000", 10));
  367.                                         $ts = gmp_div($ts, gmp_init("10000000", 10));
  368.                                         $ts = gmp_strval($ts, 10);
  369.                                         $ms = gmp_strval($ms, 10);
  370.                                         $ts = gmdate('Y-m-d H:i:s', intval($ts))."'".str_pad($ms, 7/*0.1us*/, '0', STR_PAD_LEFT).' GMT';
  371.                                         echo sprintf("%-32s %s\n", "Timestamp:", "[0x$timestamp] $ts");
  372.  
  373.                                         $x = hexdec(substr($uuid, 16, 4));
  374.                                         $dec = $x & 0x3FFF; // The highest 2 bits are used by "variant" (10x)
  375.                                         $hex = substr($uuid, 16, 4);
  376.                                         $hex = '<abbr title="The highest 2 bits are used by the UUID variant (10xx)">'.$hex[0].'</abbr>'.substr($hex,1);
  377.                                         echo sprintf("%-32s %s\n", "Clock ID:", "[0x$hex] $dec");
  378.  
  379.                                         $x = substr($uuid, 20, 12);
  380.                                         $nodeid = '';
  381.                                         for ($i=0; $i<6; $i++) {
  382.                                                 $nodeid .= substr($x, $i*2, 2);
  383.                                                 if ($i != 5) $nodeid .= '-';
  384.                                         }
  385.                                         $nodeid = strtoupper($nodeid);
  386.                                         echo sprintf("%-32s %s\n", "Node ID:", "[0x$x] $nodeid");
  387.  
  388.                                         echo "\n<u>In case that this Node ID is a MAC address, here is the interpretation of that MAC address:</u>\n\n";
  389.                                         decode_mac(strtoupper($nodeid));
  390.  
  391.                                         break;
  392.                                 case 2:
  393.                                         /*
  394.                                         Variant 1, Version 2 UUID
  395.                                         - 32 bit Local Domain Number
  396.                                         - 16 bit Mid Time
  397.                                         -  4 bit Version (fix 0x2)
  398.                                         - 12 bit High Time
  399.                                         -  2 bit Variant (fix 0b10)
  400.                                         -  6 bit Clock Sequence
  401.                                         -  8 bit Local Domain
  402.                                         - 48 bit MAC Address
  403.                                         */
  404.  
  405.                                         // see also https://unicorn-utterances.com/posts/what-happened-to-uuid-v2
  406.  
  407.                                         echo sprintf("%-32s %s\n", "Version:", "[0x2] DCE Security version");
  408.  
  409.                                         # The clock_seq_low field (which represents an integer in the range [0, 28-1]) is interpreted as a local domain (as represented by sec_rgy_domain_t; see sec_rgy_domain_t ); that is, an identifier domain meaningful to the local host. (Note that the data type sec_rgy_domain_t can potentially hold values outside the range [0, 28-1]; however, the only values currently registered are in the range [0, 2], so this type mismatch is not significant.) In the particular case of a POSIX host, the value sec_rgy_domain_person is to be interpreted as the "POSIX UID domain", and the value sec_rgy_domain_group is to be interpreted as the "POSIX GID domain".
  410.                                         $x = substr($uuid, 18, 2);
  411.                                         if ($x == '00') $domain_info = 'Person (e.g. POSIX UID)';
  412.                                         else if ($x == '01') $domain_info = 'Group (e.g. POSIX GID)';
  413.                                         else if ($x == '02') $domain_info = 'Organization';
  414.                                         else $domain_info = 'site-defined (Domain '.hexdec($x).')';
  415.                                         echo sprintf("%-32s %s\n", "Local Domain:", "[0x$x] $domain_info");
  416.  
  417.                                         # The time_low field (which represents an integer in the range [0, 232-1]) is interpreted as a local-ID; that is, an identifier (within the domain specified by clock_seq_low) meaningful to the local host. In the particular case of a POSIX host, when combined with a POSIX UID or POSIX GID domain in the clock_seq_low field (above), the time_low field represents a POSIX UID or POSIX GID, respectively.
  418.                                         $x = substr($uuid, 0, 8);
  419.                                         $dec = hexdec($x);
  420.                                         echo sprintf("%-32s %s\n", "Local Domain Number:", "[0x$x] $dec");
  421.  
  422.                                         # Timestamp: Count of 100ns intervals since 15 Oct 1582 00:00:00
  423.                                         # 1/0,0000001 = 10000000
  424.                                         $timestamp = substr($uuid, 13, 3).substr($uuid, 8, 4).'00000000';
  425.                                         $ts = gmp_init($timestamp, 16);
  426.                                         $ts = gmp_sub($ts, gmp_init("122192928000000000", 10));
  427.                                         $ms = gmp_mod($ts, gmp_init("10000000", 10));
  428.                                         $ts = gmp_div($ts, gmp_init("10000000", 10));
  429.                                         $ts = gmp_strval($ts, 10);
  430.                                         $ms = gmp_strval($ms, 10);
  431.                                         $ts_min = gmdate('Y-m-d H:i:s', intval($ts))."'".str_pad($ms, 7/*0.1us*/, '0', STR_PAD_LEFT).' GMT';
  432.  
  433.                                         $timestamp = substr($uuid, 13, 3).substr($uuid, 8, 4).'FFFFFFFF';
  434.                                         $ts = gmp_init($timestamp, 16);
  435.                                         $ts = gmp_sub($ts, gmp_init("122192928000000000", 10));
  436.                                         $ms = gmp_mod($ts, gmp_init("10000000", 10));
  437.                                         $ts = gmp_div($ts, gmp_init("10000000", 10));
  438.                                         $ts = gmp_strval($ts, 10);
  439.                                         $ms = gmp_strval($ms, 10);
  440.                                         $ts_max = gmdate('Y-m-d H:i:s', intval($ts))."'".str_pad($ms, 7/*0.1us*/, '0', STR_PAD_LEFT).' GMT';
  441.  
  442.                                         $timestamp = substr($uuid, 13, 3).substr($uuid, 8, 4)/*.'xxxxxxxx'*/;
  443.                                         echo sprintf("%-32s %s\n", "Timestamp:", "[0x$timestamp] $ts_min - $ts_max");
  444.  
  445.                                         $x = hexdec(substr($uuid, 16, 2));
  446.                                         $dec = $x & 0x3F; // The highest 2 bits are used by "variant" (10xx)
  447.                                         $hex = substr($uuid, 16, 2);
  448.                                         $hex = '<abbr title="The highest 2 bits are used by the UUID variant (10xx)">'.$hex[0].'</abbr>'.substr($hex,1);
  449.                                         echo sprintf("%-32s %s\n", "Clock ID:", "[0x$hex] $dec");
  450.  
  451.                                         $x = substr($uuid, 20, 12);
  452.                                         $nodeid = '';
  453.                                         for ($i=0; $i<6; $i++) {
  454.                                                 $nodeid .= substr($x, $i*2, 2);
  455.                                                 if ($i != 5) $nodeid .= '-';
  456.                                         }
  457.                                         $nodeid = strtoupper($nodeid);
  458.                                         echo sprintf("%-32s %s\n", "Node ID:", "[0x$x] $nodeid");
  459.  
  460.                                         echo "\n<u>In case that this Node ID is a MAC address, here is the interpretation of that MAC address:</u>\n\n";
  461.                                         decode_mac(strtoupper($nodeid));
  462.  
  463.                                         break;
  464.                                 case 3:
  465.                                         /*
  466.                                         Variant 1, Version 3 UUID
  467.                                         - 48 bit Hash High
  468.                                         -  4 bit Version (fix 0x3)
  469.                                         - 12 bit Hash Mid
  470.                                         -  2 bit Variant (fix 0b10)
  471.                                         - 62 bit Hash Low
  472.                                         */
  473.  
  474.                                         echo sprintf("%-32s %s\n", "Version:", "[0x3] Name-based (MD5 hash)");
  475.  
  476.                                         $hash = str_replace('-', '', strtolower($uuid));
  477.  
  478.                                         $hash[12] = '?'; // was overwritten by version
  479.  
  480.                                         $var16a = strtoupper(dechex(hexdec($hash[16]) & 0b0011 | 0b0000));
  481.                                         $var16b = strtoupper(dechex(hexdec($hash[16]) & 0b0011 | 0b0100));
  482.                                         $var16c = strtoupper(dechex(hexdec($hash[16]) & 0b0011 | 0b1000));
  483.                                         $var16d = strtoupper(dechex(hexdec($hash[16]) & 0b0011 | 0b1100));
  484.                                         $hash[16] = '?'; // was partially overwritten by variant
  485.  
  486.                                         $p = 16;
  487.                                         $hash = substr($hash,0,$p)."<abbr title=\"$var16a, $var16b, $var16c, or $var16d\">".substr($hash,$p,1).'</abbr>'.substr($hash,$p+1);
  488.                                         echo sprintf("%-32s %s\n", "MD5(Namespace+Subject):", "[0x$hash]");
  489.  
  490.                                         break;
  491.                                 case 4:
  492.                                         /*
  493.                                         Variant 1, Version 4 UUID
  494.                                         - 48 bit Random High
  495.                                         -  4 bit Version (fix 0x4)
  496.                                         - 12 bit Random Mid
  497.                                         -  2 bit Variant (fix 0b10)
  498.                                         - 62 bit Random Low
  499.                                         */
  500.  
  501.                                         echo sprintf("%-32s %s\n", "Version:", "[0x4] Random");
  502.  
  503.                                         $rand_line1 = '';
  504.                                         $rand_line2 = '';
  505.                                         for ($i=0; $i<16; $i++) {
  506.                                                 $bin = base_convert(substr($uuid, $i*2, 2), 16, 2);
  507.                                                 $bin = str_pad($bin, 8, "0", STR_PAD_LEFT);
  508.  
  509.                                                 if ($i == 6) {
  510.                                                         // was overwritten by version
  511.                                                         $bin[0] = '?';
  512.                                                         $bin[1] = '?';
  513.                                                         $bin[2] = '?';
  514.                                                         $bin[3] = '?';
  515.                                                 } else if ($i == 8) {
  516.                                                         // was partially overwritten by variant
  517.                                                         $bin[0] = '?';
  518.                                                         $bin[1] = '?';
  519.                                                 }
  520.  
  521.                                                 if ($i<8) $rand_line1 .= "$bin ";
  522.                                                 if ($i>=8) $rand_line2 .= "$bin ";
  523.                                         }
  524.                                         echo sprintf("%-32s %s\n", "Random bits:", trim($rand_line1));
  525.                                         echo sprintf("%-32s %s\n", "",             trim($rand_line2));
  526.  
  527.                                         $rand_bytes = str_replace('-', '', strtolower($uuid));
  528.                                         $rand_bytes[12] = '?'; // was overwritten by version
  529.                                         $var16a = strtoupper(dechex(hexdec($rand_bytes[16]) & 0b0011 | 0b0000));
  530.                                         $var16b = strtoupper(dechex(hexdec($rand_bytes[16]) & 0b0011 | 0b0100));
  531.                                         $var16c = strtoupper(dechex(hexdec($rand_bytes[16]) & 0b0011 | 0b1000));
  532.                                         $var16d = strtoupper(dechex(hexdec($rand_bytes[16]) & 0b0011 | 0b1100));
  533.                                         $rand_bytes[16] = '?'; // was partially overwritten by variant
  534.  
  535.                                         $p = 16;
  536.                                         $rand_bytes = substr($rand_bytes,0,$p)."<abbr title=\"$var16a, $var16b, $var16c, or $var16d\">".substr($rand_bytes,$p,1).'</abbr>'.substr($rand_bytes,$p+1);
  537.                                         echo sprintf("%-32s %s\n", "Random bytes:", "[0x$rand_bytes]");
  538.  
  539.                                         break;
  540.                                 case 5:
  541.                                         /*
  542.                                         Variant 1, Version 5 UUID
  543.                                         - 48 bit Hash High
  544.                                         -  4 bit Version (fix 0x5)
  545.                                         - 12 bit Hash Mid
  546.                                         -  2 bit Variant (fix 0b10)
  547.                                         - 62 bit Hash Low
  548.                                         */
  549.  
  550.                                         echo sprintf("%-32s %s\n", "Version:", "[0x5] Name-based (SHA-1 hash)");
  551.  
  552.                                         $hash = str_replace('-', '', strtolower($uuid));
  553.  
  554.                                         $hash[12] = '?'; // was overwritten by version
  555.  
  556.                                         $var16a = strtoupper(dechex(hexdec($hash[16]) & 0b0011 | 0b0000));
  557.                                         $var16b = strtoupper(dechex(hexdec($hash[16]) & 0b0011 | 0b0100));
  558.                                         $var16c = strtoupper(dechex(hexdec($hash[16]) & 0b0011 | 0b1000));
  559.                                         $var16d = strtoupper(dechex(hexdec($hash[16]) & 0b0011 | 0b1100));
  560.                                         $hash[16] = '?'; // was partially overwritten by variant
  561.  
  562.                                         $hash .= '????????'; // was cut off
  563.  
  564.                                         $p = 16;
  565.                                         $hash = substr($hash,0,$p)."<abbr title=\"$var16a, $var16b, $var16c, or $var16d\">".substr($hash,$p,1).'</abbr>'.substr($hash,$p+1);
  566.                                         echo sprintf("%-32s %s\n", "SHA1(Namespace+Subject):", "[0x$hash]");
  567.  
  568.                                         break;
  569.                                 case 7:
  570.                                         /*
  571.                                         Variant 1, Version 7 UUID
  572.                                         - 48 bit Unix Time in milliseconds
  573.                                         -  4 bit Version (fix 0x7)
  574.                                         - 12 bit Random
  575.                                         -  2 bit Variant (fix 0b10)
  576.                                         - 62 bit Random
  577.                                         */
  578.  
  579.                                         echo sprintf("%-32s %s\n", "Version:", "[0x7] Unix Epoch Time");
  580.  
  581.                                         $timestamp = substr($uuid, 0, 12);
  582.  
  583.                                         // Timestamp: Split into seconds and milliseconds
  584.                                         $ts = gmp_init($timestamp, 16);
  585.                                         $ms = gmp_mod($ts, gmp_init("1000", 10));
  586.                                         $ts = gmp_div($ts, gmp_init("1000", 10));
  587.                                         $ts = gmp_strval($ts, 10);
  588.                                         $ms = gmp_strval($ms, 10);
  589.                                         $ts = gmdate('Y-m-d H:i:s', intval($ts))."'".str_pad($ms, 3/*ms*/, '0', STR_PAD_LEFT).' GMT';
  590.                                         echo sprintf("%-32s %s\n", "Timestamp:", "[0x$timestamp] $ts");
  591.  
  592.                                         $rand = '';
  593.                                         for ($i=6; $i<16; $i++) {
  594.                                                 $bin = base_convert(substr($uuid, $i*2, 2), 16, 2);
  595.                                                 $bin = str_pad($bin, 8, "0", STR_PAD_LEFT);
  596.  
  597.                                                 if ($i == 6) {
  598.                                                         // was overwritten by version
  599.                                                         $bin[0] = '?';
  600.                                                         $bin[1] = '?';
  601.                                                         $bin[2] = '?';
  602.                                                         $bin[3] = '?';
  603.                                                 } else if ($i == 8) {
  604.                                                         // was partially overwritten by variant
  605.                                                         $bin[0] = '?';
  606.                                                         $bin[1] = '?';
  607.                                                 }
  608.  
  609.                                                 $rand .= "$bin ";
  610.                                         }
  611.                                         echo sprintf("%-32s %s\n", "Random bits:", trim($rand));
  612.  
  613.                                         $rand_bytes = substr(str_replace('-', '', strtolower($uuid)),13);
  614.                                         $var16a = strtoupper(dechex(hexdec($rand_bytes[3]) & 0b0011 | 0b0000));
  615.                                         $var16b = strtoupper(dechex(hexdec($rand_bytes[3]) & 0b0011 | 0b0100));
  616.                                         $var16c = strtoupper(dechex(hexdec($rand_bytes[3]) & 0b0011 | 0b1000));
  617.                                         $var16d = strtoupper(dechex(hexdec($rand_bytes[3]) & 0b0011 | 0b1100));
  618.                                         $rand_bytes[3] = '?'; // was partially overwritten by variant
  619.  
  620.                                         $p = 3;
  621.                                         $rand_bytes = substr($rand_bytes,0,$p)."<abbr title=\"$var16a, $var16b, $var16c, or $var16d\">".substr($rand_bytes,$p,1).'</abbr>'.substr($rand_bytes,$p+1);
  622.                                         echo sprintf("%-32s %s\n", "Random bytes:", "[0x$rand_bytes]");
  623.  
  624.                                         // TODO: convert to and from Base32 CROCKFORD ULID (make 2 methods in uuid_utils.inc.php)
  625.                                         // e.g. ULID: 01GCZ05N3JFRKBRWKNGCQZGP44
  626.                                         // "Be aware that all version 7 UUIDs may be converted to ULIDs but not all ULIDs may be converted to UUIDs."
  627.  
  628.                                         break;
  629.                                 case 8:
  630.                                         /*
  631.                                         Variant 1, Version 8 UUID
  632.                                         - 48 bit Custom data
  633.                                         -  4 bit Version (fix 0x8)
  634.                                         - 12 bit Custom data
  635.                                         -  2 bit Variant (fix 0b10)
  636.                                         - 62 bit Custom data
  637.                                         */
  638.  
  639.                                         echo sprintf("%-32s %s\n", "Version:", "[0x8] Custom implementation");
  640.  
  641.                                         $custom_data = substr($uuid,0,12).substr($uuid,13); // exclude version nibble
  642.                                         $custom_data[15] = dechex(hexdec($custom_data[15]) & 0b0011); // nibble was partially overwritten by variant
  643.                                         $custom_data = strtolower($custom_data);
  644.  
  645.                                         $custom_block1 = substr($uuid,  0, 8);
  646.                                         $custom_block2 = substr($uuid,  8, 4);
  647.                                         $custom_block3 = substr($uuid, 12, 4);
  648.                                         $custom_block4 = substr($uuid, 16, 4);
  649.                                         $custom_block5 = substr($uuid, 20);
  650.  
  651.                                         $custom_block3 = substr($custom_block3, 1); // remove version
  652.                                         $custom_block4[0] = dechex(hexdec($custom_block4[0]) & 0b0011); // remove variant
  653.  
  654.                                         echo sprintf("%-32s %s\n", "Custom data:", "[0x$custom_data]");
  655.                                         echo sprintf("%-32s %s\n", "Custom data block1 (32 bit):", "[0x$custom_block1]");
  656.                                         echo sprintf("%-32s %s\n", "Custom data block2 (16 bit):", "[0x$custom_block2]");
  657.                                         echo sprintf("%-32s %s\n", "Custom data block3 (12 bit):", "[0x$custom_block3]");
  658.                                         echo sprintf("%-32s %s\n", "Custom data block4 (14 bit):", "[0x$custom_block4]");
  659.                                         echo sprintf("%-32s %s\n", "Custom data block5 (48 bit):", "[0x$custom_block5]");
  660.  
  661.                                         // START: Check if Custom UUIDv8 is likely an OIDplus 2.0 Custom UUID
  662.  
  663.                                         $oidplus_systemid_hex = $custom_block1;
  664.                                         $oidplus_systemid_int = hexdec($oidplus_systemid_hex); // 31 bit hash of public key
  665.                                         $oidplus_systemid_valid = hexdec($custom_block1) < 0x80000000;
  666.  
  667.                                         $oidplus_creation_hex = $custom_block2;
  668.                                         $oidplus_creation_int = hexdec($oidplus_creation_hex); // days since 1 January 1970, or 0 if unknown
  669.                                         //$oidplus_creation_valid = ($oidplus_creation_int >= 14610/*1 Jan 2010*/) && ($oidplus_creation_int <= floor(time()/24/60/60)/*Today*/);
  670.                                         $oidplus_creation_unknown = $oidplus_creation_int == 0;
  671.  
  672.                                         $oidplus_reserved_hex = $custom_block3;
  673.                                         $oidplus_reserved_int = hexdec($oidplus_reserved_hex);
  674.  
  675.                                         $oidplus_namespace_hex = $custom_block4;
  676.                                         $oidplus_namespace_int = hexdec($oidplus_namespace_hex);
  677.  
  678.                                         $oidplus_data_hex = $custom_block5;
  679.                                         $oidplus_data_int = (PHP_INT_SIZE == 4) ? gmp_strval(gmp_init($oidplus_data_hex,16),10) : hexdec($custom_block5);
  680.  
  681.                                         if ($oidplus_systemid_valid && ($oidplus_reserved_int == 0)) {
  682.                                                 if (($oidplus_namespace_int == 0) && $oidplus_creation_unknown && (strtolower($oidplus_data_hex) == '1890afd80709')) {
  683.                                                         // System GUID, e.g. 6e932dd7-0000-8000-8000-1890afd80709
  684.                                                         echo "\n<u>Interpretation of <a href=\"https://github.com/danielmarschall/oidplus/blob/master/doc/oidplus_custom_guid.md\">OIDplus 2.0 Custom UUID</a></u>\n\n";
  685.                                                         echo sprintf("%-32s %s\n", "System ID:", "[0x$oidplus_systemid_hex] ".$oidplus_systemid_int);
  686.                                                         echo sprintf("%-32s %s\n", "Creation time:", "[0x$oidplus_creation_hex] ".($oidplus_creation_unknown ? 'Unknown' : date('Y-m-d', $oidplus_creation_int*24*60*60))); /**@phpstan-ignore-line*/
  687.                                                         echo sprintf("%-32s %s\n", "Reserved:", "[0x$oidplus_reserved_hex]");
  688.                                                         echo sprintf("%-32s %s\n", "Namespace:", "[0x$oidplus_namespace_hex] $oidplus_namespace_int=System");
  689.                                                         echo sprintf("%-32s %s\n", "Data (empty string hash):", "[0x$oidplus_data_hex] SHA1('') = ????????????????????????????$oidplus_data_hex");
  690.                                                 }
  691.                                                 else if (($oidplus_namespace_int == 1) && $oidplus_creation_unknown) {
  692.                                                         // User GUID, e.g. 6e932dd7-0000-8000-8001-2938f50e857e (User), 6e932dd7-0000-8000-8001-000000000000 (Admin)
  693.                                                         echo "\n<u>Interpretation of <a href=\"https://github.com/danielmarschall/oidplus/blob/master/doc/oidplus_custom_guid.md\">OIDplus 2.0 Custom UUID</a></u>\n\n";
  694.                                                         echo sprintf("%-32s %s\n", "System ID:", "[0x$oidplus_systemid_hex] ".$oidplus_systemid_int);
  695.                                                         echo sprintf("%-32s %s\n", "Creation time:", "[0x$oidplus_creation_hex] ".($oidplus_creation_unknown ? 'Unknown' : date('Y-m-d', $oidplus_creation_int*24*60*60)));  /**@phpstan-ignore-line*/
  696.                                                         echo sprintf("%-32s %s\n", "Reserved:", "[0x$oidplus_reserved_hex]");
  697.                                                         echo sprintf("%-32s %s\n", "Namespace:", "[0x$oidplus_namespace_hex] $oidplus_namespace_int=User");
  698.                                                         if ($oidplus_data_int == 0) {
  699.                                                                 echo sprintf("%-32s %s\n", "Data (Username):", "[0x$oidplus_data_hex] 0=Admin");
  700.                                                         } else {
  701.                                                                 echo sprintf("%-32s %s\n", "Data (Username):", "[0x$oidplus_data_hex] SHA1(UserName) = ????????????????????????????$oidplus_data_hex");
  702.                                                         }
  703.                                                 }
  704.                                                 else if (($oidplus_namespace_int == 2)/* && $oidplus_creation_valid*/) {
  705.                                                         // Log entry GUID, e.g. 6e932dd7-458c-8000-8002-0000000004d2
  706.                                                         echo "\n<u>Interpretation of <a href=\"https://github.com/danielmarschall/oidplus/blob/master/doc/oidplus_custom_guid.md\">OIDplus 2.0 Custom UUID</a></u>\n\n";
  707.                                                         echo sprintf("%-32s %s\n", "System ID:", "[0x$oidplus_systemid_hex] ".$oidplus_systemid_int);
  708.                                                         echo sprintf("%-32s %s\n", "Creation time:", "[0x$oidplus_creation_hex] ".($oidplus_creation_unknown ? 'Unknown' : date('Y-m-d', $oidplus_creation_int*24*60*60)));
  709.                                                         echo sprintf("%-32s %s\n", "Reserved:", "[0x$oidplus_reserved_hex]");
  710.                                                         echo sprintf("%-32s %s\n", "Namespace:", "[0x$oidplus_namespace_hex] $oidplus_namespace_int=Log Entry");
  711.                                                         echo sprintf("%-32s %s\n", "Data (Sequence number):", "[0x$oidplus_data_hex] $oidplus_data_int");
  712.                                                 }
  713.                                                 else if (($oidplus_namespace_int == 3) && $oidplus_creation_unknown) {
  714.                                                         // Configuration entry GUID, e.g. 6e932dd7-0000-8000-8003-f14dda42862a
  715.                                                         echo "\n<u>Interpretation of <a href=\"https://github.com/danielmarschall/oidplus/blob/master/doc/oidplus_custom_guid.md\">OIDplus 2.0 Custom UUID</a></u>\n\n";
  716.                                                         echo sprintf("%-32s %s\n", "System ID:", "[0x$oidplus_systemid_hex] ".$oidplus_systemid_int);
  717.                                                         echo sprintf("%-32s %s\n", "Creation time:", "[0x$oidplus_creation_hex] ".($oidplus_creation_unknown ? 'Unknown' : date('Y-m-d', $oidplus_creation_int*24*60*60))); /**@phpstan-ignore-line*/
  718.                                                         echo sprintf("%-32s %s\n", "Reserved:", "[0x$oidplus_reserved_hex]");
  719.                                                         echo sprintf("%-32s %s\n", "Namespace:", "[0x$oidplus_namespace_hex] $oidplus_namespace_int=Configuration Entry");
  720.                                                         echo sprintf("%-32s %s\n", "Data (Setting name hash):", "[0x$oidplus_data_hex] SHA1(SettingName) = ????????????????????????????$oidplus_data_hex");
  721.                                                 }
  722.                                                 else if ($oidplus_namespace_int == 4) {
  723.                                                         // ASN.1 Alpahnumeric identifier GUID, e.g. 6e932dd7-0000-8000-8004-208ded8a3f8f
  724.                                                         echo "\n<u>Interpretation of <a href=\"https://github.com/danielmarschall/oidplus/blob/master/doc/oidplus_custom_guid.md\">OIDplus 2.0 Custom UUID</a></u>\n\n";
  725.                                                         echo sprintf("%-32s %s\n", "System ID:", "[0x$oidplus_systemid_hex] ".$oidplus_systemid_int);
  726.                                                         echo sprintf("%-32s %s\n", "Creation time:", "[0x$oidplus_creation_hex] ".($oidplus_creation_unknown ? 'Unknown' : date('Y-m-d', $oidplus_creation_int*24*60*60)));
  727.                                                         echo sprintf("%-32s %s\n", "Reserved:", "[0x$oidplus_reserved_hex]");
  728.                                                         echo sprintf("%-32s %s\n", "Namespace:", "[0x$oidplus_namespace_hex] $oidplus_namespace_int=ASN.1 Alphanumeric ID");
  729.                                                         $oidplus_data_24hi_hex = substr($oidplus_data_hex, 0, 6);
  730.                                                         $oidplus_data_24lo_hex = substr($oidplus_data_hex, 6, 6);
  731.                                                         echo sprintf("%-32s %s\n", "Data (OID hash):", "[0x$oidplus_data_24hi_hex] SHA1(OID) = ????????????????????????????$oidplus_data_24hi_hex");
  732.                                                         echo sprintf("%-32s %s\n", "Data (Name hash):", "[0x$oidplus_data_24lo_hex] SHA1(AlphaNumId) = ????????????????????????????$oidplus_data_24lo_hex");
  733.                                                 }
  734.                                                 else if ($oidplus_namespace_int == 5) {
  735.                                                         // Unicode Label entry GUID, e.g. 6e932dd7-0000-8000-8005-208dedaf9a96
  736.                                                         echo "\n<u>Interpretation of <a href=\"https://github.com/danielmarschall/oidplus/blob/master/doc/oidplus_custom_guid.md\">OIDplus 2.0 Custom UUID</a></u>\n\n";
  737.                                                         echo sprintf("%-32s %s\n", "System ID:", "[0x$oidplus_systemid_hex] ".$oidplus_systemid_int);
  738.                                                         echo sprintf("%-32s %s\n", "Creation time:", "[0x$oidplus_creation_hex] ".($oidplus_creation_unknown ? 'Unknown' : date('Y-m-d', $oidplus_creation_int*24*60*60)));
  739.                                                         echo sprintf("%-32s %s\n", "Reserved:", "[0x$oidplus_reserved_hex]");
  740.                                                         echo sprintf("%-32s %s\n", "Namespace:", "[0x$oidplus_namespace_hex] $oidplus_namespace_int=Unicode Label");
  741.                                                         $oidplus_data_24hi_hex = substr($oidplus_data_hex, 0, 6);
  742.                                                         $oidplus_data_24lo_hex = substr($oidplus_data_hex, 6, 6);
  743.                                                         echo sprintf("%-32s %s\n", "Data (OID hash):", "[0x$oidplus_data_24hi_hex] SHA1(OID) = ????????????????????????????$oidplus_data_24hi_hex");
  744.                                                         echo sprintf("%-32s %s\n", "Data (Name hash):", "[0x$oidplus_data_24lo_hex] SHA1(UnicodeLabel) = ????????????????????????????$oidplus_data_24lo_hex");
  745.                                                 }
  746.                                                 else if (($oidplus_namespace_int >= 6) && ($oidplus_namespace_int <= 0xF)) {
  747.                                                         // System reserved
  748.                                                         echo "\n<u>Interpretation of <a href=\"https://github.com/danielmarschall/oidplus/blob/master/doc/oidplus_custom_guid.md\">OIDplus 2.0 Custom UUID</a></u>\n\n";
  749.                                                         echo sprintf("%-32s %s\n", "System ID:", "[0x$oidplus_systemid_hex] ".$oidplus_systemid_int);
  750.                                                         echo sprintf("%-32s %s\n", "Creation time:", "[0x$oidplus_creation_hex] ".($oidplus_creation_unknown ? 'Unknown' : date('Y-m-d', $oidplus_creation_int*24*60*60)));
  751.                                                         echo sprintf("%-32s %s\n", "Reserved:", "[0x$oidplus_reserved_hex]");
  752.                                                         echo sprintf("%-32s %s\n", "Namespace:", "[0x$oidplus_namespace_hex] $oidplus_namespace_int=Unknown (System Reserved)");
  753.                                                         echo sprintf("%-32s %s\n", "Data (Setting name hash):", "[0x$oidplus_data_hex] Unknown");
  754.                                                 }
  755.                                                 else if ($oidplus_namespace_int > 0xF) {
  756.                                                         // Information Object GUID, e.g. 6e932dd7-458c-8000-b9e9-c1e3894d1105
  757.                                                         $known_objecttype_plugins = array(
  758.                                                                 // Latest list here: https://github.com/danielmarschall/oidplus/blob/master/doc/oidplus_custom_guid.md
  759.                                                                 '1.3.6.1.4.1.37476.2.5.2.4.8.1' => 'doi (ViaThinkSoft plugin)',
  760.                                                                 '1.3.6.1.4.1.37476.2.5.2.4.8.2' => 'gs1 (ViaThinkSoft plugin)',
  761.                                                                 '1.3.6.1.4.1.37476.2.5.2.4.8.3' => 'guid (ViaThinkSoft plugin)',
  762.                                                                 '1.3.6.1.4.1.37476.2.5.2.4.8.4' => 'ipv4 (ViaThinkSoft plugin)',
  763.                                                                 '1.3.6.1.4.1.37476.2.5.2.4.8.5' => 'ipv6 (ViaThinkSoft plugin)',
  764.                                                                 '1.3.6.1.4.1.37476.2.5.2.4.8.6' => 'java (ViaThinkSoft plugin)',
  765.                                                                 '1.3.6.1.4.1.37476.2.5.2.4.8.7' => 'oid (ViaThinkSoft plugin)',
  766.                                                                 '1.3.6.1.4.1.37476.2.5.2.4.8.8' => 'other (ViaThinkSoft plugin)',
  767.                                                                 '1.3.6.1.4.1.37476.2.5.2.4.8.9' => 'domain (ViaThinkSoft plugin)',
  768.                                                                 '1.3.6.1.4.1.37476.2.5.2.4.8.10' => 'fourcc (ViaThinkSoft plugin)',
  769.                                                                 '1.3.6.1.4.1.37476.2.5.2.4.8.11' => 'aid (ViaThinkSoft plugin)',
  770.                                                                 '1.3.6.1.4.1.37476.2.5.2.4.8.12' => 'php (ViaThinkSoft plugin)',
  771.                                                                 '1.3.6.1.4.1.37476.2.5.2.4.8.13' => 'mac (ViaThinkSoft plugin)',
  772.                                                                 '1.3.6.1.4.1.37553.8.1.8.8.53354196964.27255728261' => 'circuit (Frdlweb plugin)',
  773.                                                                 '1.3.6.1.4.1.37476.9000.108.19361.856' => 'ns (Frdlweb plugin)',
  774.                                                                 '1.3.6.1.4.1.37553.8.1.8.8.53354196964.32927' => 'pen (Frdlweb plugin)',
  775.                                                                 '1.3.6.1.4.1.37553.8.1.8.8.53354196964.39870' => 'uri (Frdlweb plugin)',
  776.                                                                 '1.3.6.1.4.1.37553.8.1.8.8.53354196964.1958965295' => 'web+fan (Frdlweb plugin)'
  777.                                                         );
  778.                                                         $namespace_desc = 'Unknown object type';
  779.                                                         foreach ($known_objecttype_plugins as $oid => $name) {
  780.                                                                 if ((hexdec(substr(sha1($oid), -4)) & 0x3fff) == $oidplus_namespace_int) $namespace_desc = "$oid = $name";
  781.                                                         }
  782.                                                         echo "\n<u>Interpretation of <a href=\"https://github.com/danielmarschall/oidplus/blob/master/doc/oidplus_custom_guid.md\">OIDplus 2.0 Custom UUID</a></u>\n\n";
  783.                                                         echo sprintf("%-32s %s\n", "System ID:", "[0x$oidplus_systemid_hex] ".$oidplus_systemid_int);
  784.                                                         echo sprintf("%-32s %s\n", "Creation time:", "[0x$oidplus_creation_hex] ".($oidplus_creation_unknown ? 'Unknown' : date('Y-m-d', $oidplus_creation_int*24*60*60)));
  785.                                                         echo sprintf("%-32s %s\n", "Reserved:", "[0x$oidplus_reserved_hex]");
  786.                                                         echo sprintf("%-32s %s\n", "Namespace (Obj.type OID hash):", "[0x$oidplus_namespace_hex] $namespace_desc");
  787.                                                         echo sprintf("%-32s %s\n", "Data (Object name hash):", "[0x$oidplus_data_hex] SHA1(ObjectName) = ????????????????????????????$oidplus_data_hex");
  788.                                                 }
  789.                                         }
  790.  
  791.                                         // END: OIDplus 2.0 Custom UUID Interpretation
  792.  
  793.                                         break;
  794.                                 default:
  795.                                         echo sprintf("%-32s %s\n", "Version:", "[0x".dechex($version)."] Unknown");
  796.                                         break;
  797.                         }
  798.  
  799.                         break;
  800.                 case 2:
  801.                         // TODO: Show byte order: 00112233-4455-6677-8899-aabbccddeeff => 33 22 11 00 55 44 77 66 88 99 aa bb cc dd ee ff
  802.  
  803.                         // TODO: Is there any scheme in that legacy Microsoft GUIDs?
  804.                         echo sprintf("%-32s %s\n", "Variant:", "[0b110] Reserved for Microsoft Corporation");
  805.                         break;
  806.                 case 3:
  807.                         echo sprintf("%-32s %s\n", "Variant:", "[0b111] Reserved for future use");
  808.                         break;
  809.         }
  810.  
  811.         if (!$echo) {
  812.                 $out = ob_get_contents();
  813.                 ob_end_clean();
  814.                 return $out;
  815.         } else {
  816.                 return true;
  817.         }
  818. }
  819.  
  820. function uuid_canonize($uuid) {
  821.         if (!uuid_valid($uuid)) return false;
  822.         return oid_to_uuid(uuid_to_oid($uuid));
  823. }
  824.  
  825. function oid_to_uuid($oid) {
  826.         if (!is_uuid_oid($oid,true)) return false;
  827.  
  828.         if (substr($oid,0,1) == '.') {
  829.                 $oid = substr($oid, 1);
  830.         }
  831.         $ary = explode('.', $oid);
  832.  
  833.         if (!isset($ary[2])) return false;
  834.  
  835.         $val = $ary[2];
  836.  
  837.         $x = gmp_init($val, 10);
  838.         $y = gmp_strval($x, 16);
  839.         $y = str_pad($y, 32, "0", STR_PAD_LEFT);
  840.         return substr($y,  0, 8).'-'.
  841.                substr($y,  8, 4).'-'.
  842.                substr($y, 12, 4).'-'.
  843.                substr($y, 16, 4).'-'.
  844.                substr($y, 20, 12);
  845. }
  846.  
  847. function is_uuid_oid($oid, $only_allow_root=false) {
  848.         if (substr($oid,0,1) == '.') $oid = substr($oid, 1); // remove leading dot
  849.  
  850.         $ary = explode('.', $oid);
  851.  
  852.         if ($only_allow_root) {
  853.                 if (count($ary) != 3) return false;
  854.         } else {
  855.                 if (count($ary) < 3) return false;
  856.         }
  857.  
  858.         if ($ary[0] != '2') return false;
  859.         if ($ary[1] != '25') return false;
  860.         for ($i=2; $i<count($ary); $i++) {
  861.                 $v = $ary[$i];
  862.                 if (!is_numeric($v)) return false;
  863.                 if ($i == 2) {
  864.                         // Must be in the range of 128 bit UUID
  865.                         $test = gmp_init($v, 10);
  866.                         if (strlen(gmp_strval($test, 16)) > 32) return false;
  867.                 }
  868.                 if ($v < 0) return false;
  869.         }
  870.  
  871.         return true;
  872. }
  873.  
  874. function uuid_to_oid($uuid) {
  875.         if (!uuid_valid($uuid)) return false;
  876.  
  877.         $uuid = str_replace(array('-', '{', '}'), '', $uuid);
  878.         $x = gmp_init($uuid, 16);
  879.         return '2.25.'.gmp_strval($x, 10);
  880. }
  881.  
  882. function uuid_numeric_value($uuid) {
  883.         $oid = uuid_to_oid($uuid);
  884.         if (!$oid) return false;
  885.         return substr($oid, strlen('2.25.'));
  886. }
  887.  
  888. function uuid_c_syntax($uuid) {
  889.         $uuid = str_replace('{', '', $uuid);
  890.         return '{ 0x' . substr($uuid, 0, 8) .
  891.                 ', 0x' . substr($uuid, 9, 4) .
  892.                 ', 0x' . substr($uuid, 14, 4) .
  893.                 ', { 0x' . substr($uuid, 19, 2).
  894.                 ', 0x' . substr($uuid, 21, 2) .
  895.                 ', 0x' . substr($uuid, 24, 2) .
  896.                 ', 0x' . substr($uuid, 26, 2) .
  897.                 ', 0x' . substr($uuid, 28, 2) .
  898.                 ', 0x' . substr($uuid, 30, 2) .
  899.                 ', 0x' . substr($uuid, 32, 2) .
  900.                 ', 0x' . substr($uuid, 34, 2) . ' } }';
  901. }
  902.  
  903. function gen_uuid($prefer_mac_address_based = true) {
  904.         $uuid = $prefer_mac_address_based ? gen_uuid_reordered()/*UUIDv6*/ : false;
  905.         if ($uuid === false) $uuid = gen_uuid_unix_epoch()/*UUIDv7*/;
  906.         return $uuid;
  907. }
  908.  
  909. # --------------------------------------
  910. // Variant 1, Version 1 (Time based) UUID
  911. # --------------------------------------
  912.  
  913. function gen_uuid_v1() {
  914.         return gen_uuid_timebased();
  915. }
  916. function gen_uuid_timebased($force_php_implementation=false) {
  917.         # On Debian: apt-get install php-uuid
  918.         # extension_loaded('uuid')
  919.         if (!$force_php_implementation && function_exists('uuid_create')) {
  920.                 # OSSP uuid extension like seen in php5-uuid at Debian 8
  921.                 /*
  922.                 $x = uuid_create($context);
  923.                 uuid_make($context, UUID_MAKE_V1);
  924.                 uuid_export($context, UUID_FMT_STR, $uuid);
  925.                 return trim($uuid);
  926.                 */
  927.  
  928.                 # PECL uuid extension like seen in php-uuid at Debian 9
  929.                 return trim(uuid_create(UUID_TYPE_TIME));
  930.         }
  931.  
  932.         # On Debian: apt-get install uuid-runtime
  933.         if (!$force_php_implementation && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
  934.                 $out = array();
  935.                 $ec = -1;
  936.                 exec('uuidgen -t 2>/dev/null', $out, $ec);
  937.                 if ($ec == 0) return trim($out[0]);
  938.         }
  939.  
  940.         # If we hadn't any success yet, then implement the time based generation routine ourselves!
  941.         # Based on https://github.com/fredriklindberg/class.uuid.php/blob/master/class.uuid.php
  942.  
  943.         $uuid = array(
  944.                 'time_low' => 0,                /* 32-bit */
  945.                 'time_mid' => 0,                /* 16-bit */
  946.                 'time_hi' => 0,                 /* 16-bit */
  947.                 'clock_seq_hi' => 0,            /*  8-bit */
  948.                 'clock_seq_low' => 0,           /*  8-bit */
  949.                 'node' => array()               /* 48-bit */
  950.         );
  951.  
  952.         /*
  953.          * Get current time in 100 ns intervals. The magic value
  954.          * is the offset between UNIX epoch and the UUID UTC
  955.          * time base October 15, 1582.
  956.          */
  957.         if (time_nanosleep(0,100) !== true) usleep(1); // Wait 100ns, to make sure that the time part changes if multiple UUIDs are generated
  958.         $tp = gettimeofday();
  959.         if (PHP_INT_SIZE == 4) {
  960.                 $tp['sec'] = gmp_init($tp['sec'],10);
  961.                 $tp['usec'] = gmp_init($tp['usec'],10);
  962.                 $time = gmp_add(gmp_add(gmp_mul($tp['sec'], gmp_init('10000000',10)),gmp_mul($tp['usec'], gmp_init('10',10))),gmp_init('01B21DD213814000',16));
  963.                 $uuid['time_low'] = gmp_and($time, gmp_init('ffffffff',16));
  964.                 $high = gmp_shiftr($time,32);
  965.                 $uuid['time_mid'] = gmp_and($high, gmp_init('ffff',16));
  966.                 $uuid['time_hi'] = intval(gmp_and(gmp_shiftr($high,16),gmp_init('fff',16)),10) | (1/*TimeBased*/ << 12);
  967.         } else {
  968.                 $time = ($tp['sec'] * 10000000) + ($tp['usec'] * 10) + 0x01B21DD213814000;
  969.                 $uuid['time_low'] = $time & 0xffffffff;
  970.                 /* Work around PHP 32-bit bit-operation limits */
  971.                 $high = intval($time / 0xffffffff);
  972.                 $uuid['time_mid'] = $high & 0xffff;
  973.                 $uuid['time_hi'] = (($high >> 16) & 0xfff) | (1/*TimeBased*/ << 12);
  974.         }
  975.  
  976.         /*
  977.          * We don't support saved state information and generate
  978.          * a random clock sequence each time.
  979.          */
  980.         $uuid['clock_seq_hi'] = _random_int(0, 255) & 0b00111111 | 0b10000000; // set variant to 0b10__ (RFC 4122)
  981.         $uuid['clock_seq_low'] = _random_int(0, 255);
  982.  
  983.         /*
  984.          * Node should be set to the 48-bit IEEE node identifier
  985.          */
  986.         $mac = get_mac_address();
  987.         if ($mac) {
  988.                 $node = str_replace('-','',str_replace(':','',$mac));
  989.                 for ($i = 0; $i < 6; $i++) {
  990.                         $uuid['node'][$i] = hexdec(substr($node, $i*2, 2));
  991.                 }
  992.         } else {
  993.                 // If we cannot get a MAC address, then generate a random AAI
  994.                 // RFC 4122 requires the multicast bit to be set, to make sure
  995.                 // that a UUID from a system with network card never conflicts
  996.                 // with a UUID from a system without network ard.
  997.                 // We are additionally defining the other 3 bits as AAI,
  998.                 // to avoid that we are misusing the CID or OUI from other vendors
  999.                 // if we would create multicast ELI (based on CID) or EUI (based on OUI).
  1000.                 $uuid['node'] = explode('-', gen_aai(48, true/*Multicast*/));
  1001.                 $uuid['node'] = array_map('hexdec', $uuid['node']);
  1002.         }
  1003.  
  1004.         /*
  1005.          * Now output the UUID
  1006.          */
  1007.         return sprintf(
  1008.                 '%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x',
  1009.                 ($uuid['time_low']), ($uuid['time_mid']), ($uuid['time_hi']),
  1010.                 $uuid['clock_seq_hi'], $uuid['clock_seq_low'],
  1011.                 $uuid['node'][0], $uuid['node'][1], $uuid['node'][2],
  1012.                 $uuid['node'][3], $uuid['node'][4], $uuid['node'][5]);
  1013. }
  1014.  
  1015. # --------------------------------------
  1016. // Variant 1, Version 2 (DCE Security) UUID
  1017. # --------------------------------------
  1018.  
  1019. define('DCE_DOMAIN_PERSON', 0);
  1020. define('DCE_DOMAIN_GROUP', 1);
  1021. define('DCE_DOMAIN_ORG', 2);
  1022. function gen_uuid_v2($domain, $id) {
  1023.         return gen_uuid_dce($domain, $id);
  1024. }
  1025. function gen_uuid_dce($domain, $id) {
  1026.         if (($domain ?? '') === '') throw new Exception("Domain ID missing");
  1027.         if (!is_numeric($domain)) throw new Exception("Invalid Domain ID");
  1028.         if (($domain < 0) || ($domain > 255)) throw new Exception("Domain ID must be in range 0..255");
  1029.  
  1030.         if (($id ?? '') === '') throw new Exception("ID value missing");
  1031.         if (!is_numeric($id)) throw new Exception("Invalid ID value");
  1032.         if (($id < 0) || ($id > 4294967295)) throw new Exception("ID value must be in range 0..4294967295");
  1033.  
  1034.         # Start with a version 1 UUID
  1035.         $uuid = gen_uuid_timebased();
  1036.  
  1037.         # Add Domain Number
  1038.         $uuid = str_pad(dechex($id), 8, '0', STR_PAD_LEFT) . substr($uuid, 8);
  1039.  
  1040.         # Add Domain (this overwrites part of the clock sequence)
  1041.         $uuid = substr($uuid,0,21) . str_pad(dechex($domain), 2, '0', STR_PAD_LEFT) . substr($uuid, 23);
  1042.  
  1043.         # Change version to 2
  1044.         $uuid[14] = '2';
  1045.  
  1046.         return $uuid;
  1047. }
  1048.  
  1049. # --------------------------------------
  1050. // Variant 1, Version 3 (MD5 name based) UUID
  1051. # --------------------------------------
  1052.  
  1053. function gen_uuid_v3($namespace_uuid, $name) {
  1054.         return gen_uuid_md5_namebased($namespace_uuid, $name);
  1055. }
  1056. function gen_uuid_md5_namebased($namespace_uuid, $name) {
  1057.         if (($namespace_uuid ?? '') === '') throw new Exception("Namespace UUID missing");
  1058.         if (!uuid_valid($namespace_uuid)) throw new Exception("Invalid namespace UUID '$namespace_uuid'");
  1059.  
  1060.         $namespace_uuid = uuid_canonize($namespace_uuid);
  1061.         $namespace_uuid = str_replace('-', '', $namespace_uuid);
  1062.         $namespace_uuid = hex2bin($namespace_uuid);
  1063.  
  1064.         $hash = md5($namespace_uuid.$name);
  1065.         $hash[12] = '3'; // Set version: 3 = MD5
  1066.         $hash[16] = dechex(hexdec($hash[16]) & 0b0011 | 0b1000); // Set variant to "10xx" (RFC4122)
  1067.  
  1068.         return substr($hash,  0, 8).'-'.
  1069.                substr($hash,  8, 4).'-'.
  1070.                substr($hash, 12, 4).'-'.
  1071.                substr($hash, 16, 4).'-'.
  1072.                substr($hash, 20, 12);
  1073. }
  1074.  
  1075. # --------------------------------------
  1076. // Variant 1, Version 4 (Random) UUID
  1077. # --------------------------------------
  1078.  
  1079. function gen_uuid_v4() {
  1080.         return gen_uuid_random();
  1081. }
  1082. function gen_uuid_random() {
  1083.         # On Windows: Requires
  1084.         #    extension_dir = "C:\php-8.0.3-nts-Win32-vs16-x64\ext"
  1085.         #    extension=com_dotnet
  1086.         if (function_exists('com_create_guid')) {
  1087.                 $uuid = trim(com_create_guid(), '{}');
  1088.                 if (uuid_version($uuid) === '4') { // <-- just to make 100% sure that Windows's CoCreateGuid() did output UUIDv4
  1089.                         return strtolower($uuid);
  1090.                 }
  1091.         }
  1092.  
  1093.         # On Debian: apt-get install php-uuid
  1094.         # extension_loaded('uuid')
  1095.         if (function_exists('uuid_create')) {
  1096.                 # OSSP uuid extension like seen in php5-uuid at Debian 8
  1097.                 /*
  1098.                 $x = uuid_create($context);
  1099.                 uuid_make($context, UUID_MAKE_V4);
  1100.                 uuid_export($context, UUID_FMT_STR, $uuid);
  1101.                 return trim($uuid);
  1102.                 */
  1103.  
  1104.                 # PECL uuid extension like seen in php-uuid at Debian 9
  1105.                 return trim(uuid_create(UUID_TYPE_RANDOM));
  1106.         }
  1107.  
  1108.         if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
  1109.                 # On Debian: apt-get install uuid-runtime
  1110.                 $out = array();
  1111.                 $ec = -1;
  1112.                 exec('uuidgen -r 2>/dev/null', $out, $ec);
  1113.                 if ($ec == 0) return trim($out[0]);
  1114.  
  1115.                 # On Debian Jessie: UUID V4 (Random)
  1116.                 if (file_exists('/proc/sys/kernel/random/uuid')) {
  1117.                         return trim(file_get_contents('/proc/sys/kernel/random/uuid'));
  1118.                 }
  1119.         }
  1120.  
  1121.         # Make the UUID by ourselves
  1122.  
  1123.         if (function_exists('openssl_random_pseudo_bytes')) {
  1124.                 // Source: https://www.php.net/manual/en/function.com-create-guid.php#119168
  1125.                 $data = openssl_random_pseudo_bytes(16);
  1126.                 $data[6] = chr(ord($data[6]) & 0x0f | 0x40);    // set version to 0100
  1127.                 $data[8] = chr(ord($data[8]) & 0x3f | 0x80);    // set bits 6-7 to 10
  1128.                 return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
  1129.         } else {
  1130.                 // Source: http://rogerstringer.com/2013/11/15/generate-uuids-php
  1131.                 return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
  1132.                         _random_int( 0, 0xffff ), _random_int( 0, 0xffff ),
  1133.                         _random_int( 0, 0xffff ),
  1134.                         _random_int( 0, 0x0fff ) | 0x4000,
  1135.                         _random_int( 0, 0x3fff ) | 0x8000,
  1136.                         _random_int( 0, 0xffff ), _random_int( 0, 0xffff ), _random_int( 0, 0xffff )
  1137.                 );
  1138.         }
  1139. }
  1140.  
  1141. # --------------------------------------
  1142. // Variant 1, Version 5 (SHA1 name based) UUID
  1143. # --------------------------------------
  1144.  
  1145. function gen_uuid_v5($namespace_uuid, $name) {
  1146.         return gen_uuid_sha1_namebased($namespace_uuid, $name);
  1147. }
  1148. function gen_uuid_sha1_namebased($namespace_uuid, $name) {
  1149.         if (($namespace_uuid ?? '') === '') throw new Exception("Namespace UUID missing");
  1150.         if (!uuid_valid($namespace_uuid)) throw new Exception("Invalid namespace UUID '$namespace_uuid'");
  1151.  
  1152.         $namespace_uuid = str_replace('-', '', $namespace_uuid);
  1153.         $namespace_uuid = hex2bin($namespace_uuid);
  1154.  
  1155.         $hash = sha1($namespace_uuid.$name);
  1156.         $hash[12] = '5'; // Set version: 5 = SHA1
  1157.         $hash[16] = dechex(hexdec($hash[16]) & 0b0011 | 0b1000); // Set variant to "0b10__" (RFC4122/DCE1.1)
  1158.  
  1159.         return substr($hash,  0, 8).'-'.
  1160.                substr($hash,  8, 4).'-'.
  1161.                substr($hash, 12, 4).'-'.
  1162.                substr($hash, 16, 4).'-'.
  1163.                substr($hash, 20, 12);
  1164. }
  1165.  
  1166. # --------------------------------------
  1167. // Variant 1, Version 6 (Reordered) UUID
  1168. # --------------------------------------
  1169.  
  1170. function gen_uuid_v6() {
  1171.         return gen_uuid_reordered();
  1172. }
  1173. function gen_uuid_reordered() {
  1174.         // Start with a UUIDv1
  1175.         $uuid = gen_uuid_timebased();
  1176.  
  1177.         // Convert to UUIDv6
  1178.         return uuid1_to_uuid6($uuid);
  1179. }
  1180. function uuid6_to_uuid1($hex) {
  1181.         $hex = uuid_canonize($hex);
  1182.         if ($hex === false) return false;
  1183.         $hex = preg_replace('@[^0-9A-F]@i', '', $hex);
  1184.         $hex = substr($hex, 7, 5).
  1185.                substr($hex, 13, 3).
  1186.                substr($hex, 3, 4).
  1187.                '1' . substr($hex, 0, 3).
  1188.                substr($hex, 16);
  1189.         return substr($hex,  0, 8).'-'.
  1190.                substr($hex,  8, 4).'-'.
  1191.                substr($hex, 12, 4).'-'.
  1192.                substr($hex, 16, 4).'-'.
  1193.                substr($hex, 20, 12);
  1194. }
  1195. function uuid1_to_uuid6($hex) {
  1196.         $hex = uuid_canonize($hex);
  1197.         if ($hex === false) return false;
  1198.         $hex = preg_replace('@[^0-9A-F]@i', '', $hex);
  1199.         $hex = substr($hex, 13, 3).
  1200.                substr($hex, 8, 4).
  1201.                substr($hex, 0, 5).
  1202.                '6' . substr($hex, 5, 3).
  1203.                substr($hex, 16);
  1204.         return substr($hex,  0, 8).'-'.
  1205.                substr($hex,  8, 4).'-'.
  1206.                substr($hex, 12, 4).'-'.
  1207.                substr($hex, 16, 4).'-'.
  1208.                substr($hex, 20, 12);
  1209. }
  1210.  
  1211. # --------------------------------------
  1212. // Variant 1, Version 7 (Unix Epoch) UUID
  1213. # --------------------------------------
  1214.  
  1215. function gen_uuid_v7() {
  1216.         return gen_uuid_unix_epoch();
  1217. }
  1218. function gen_uuid_unix_epoch() {
  1219.         // Start with an UUIDv4
  1220.         $uuid = gen_uuid_random();
  1221.  
  1222.         // Add the timestamp
  1223.         usleep(1000); // Wait 1ms, to make sure that the time part changes if multiple UUIDs are generated
  1224.         if (function_exists('gmp_init')) {
  1225.                 list($ms,$sec) = explode(' ', microtime(false));
  1226.                 $sec = gmp_init($sec, 10);
  1227.                 $ms = gmp_init(substr($ms,2,3), 10);
  1228.                 $unix_ts = gmp_strval(gmp_add(gmp_mul($sec, '1000'), $ms),16);
  1229.         } else {
  1230.                 $unix_ts = dechex((int)round(microtime(true)*1000));
  1231.         }
  1232.         $unix_ts = str_pad($unix_ts, 12, '0', STR_PAD_LEFT);
  1233.         for ($i=0;$i<8;$i++) $uuid[$i] = substr($unix_ts, $i, 1);
  1234.         for ($i=0;$i<4;$i++) $uuid[9+$i] = substr($unix_ts, 8+$i, 1);
  1235.  
  1236.         // set version
  1237.         $uuid[14] = '7';
  1238.  
  1239.         return $uuid;
  1240. }
  1241.  
  1242. # --------------------------------------
  1243. // Variant 1, Version 8 (Custom) UUID
  1244. # --------------------------------------
  1245.  
  1246. function gen_uuid_v8($block1_32bit, $block2_16bit, $block3_12bit, $block4_14bit, $block5_48bit) {
  1247.         return gen_uuid_custom($block1_32bit, $block2_16bit, $block3_12bit, $block4_14bit, $block5_48bit);
  1248. }
  1249. function gen_uuid_custom($block1_32bit, $block2_16bit, $block3_12bit, $block4_14bit, $block5_48bit) {
  1250.         if (preg_replace('@[0-9A-F]@i', '', $block1_32bit) != '') throw new Exception("Invalid data for block 1. Must be hex input");
  1251.         if (preg_replace('@[0-9A-F]@i', '', $block2_16bit) != '') throw new Exception("Invalid data for block 2. Must be hex input");
  1252.         if (preg_replace('@[0-9A-F]@i', '', $block3_12bit) != '') throw new Exception("Invalid data for block 3. Must be hex input");
  1253.         if (preg_replace('@[0-9A-F]@i', '', $block4_14bit) != '') throw new Exception("Invalid data for block 4. Must be hex input");
  1254.         if (preg_replace('@[0-9A-F]@i', '', $block5_48bit) != '') throw new Exception("Invalid data for block 5. Must be hex input");
  1255.  
  1256.         $block1 = str_pad(substr($block1_32bit, -8),  8, '0', STR_PAD_LEFT);
  1257.         $block2 = str_pad(substr($block2_16bit, -4),  4, '0', STR_PAD_LEFT);
  1258.         $block3 = str_pad(substr($block3_12bit, -4),  4, '0', STR_PAD_LEFT);
  1259.         $block4 = str_pad(substr($block4_14bit, -4),  4, '0', STR_PAD_LEFT);
  1260.         $block5 = str_pad(substr($block5_48bit,-12), 12, '0', STR_PAD_LEFT);
  1261.  
  1262.         $block3[0] = '8'; // Version 8 = Custom
  1263.         $block4[0] = dechex(hexdec($block4[0]) & 0b0011 | 0b1000); // Variant 0b10__ = RFC4122
  1264.  
  1265.         return strtolower($block1.'-'.$block2.'-'.$block3.'-'.$block4.'-'.$block5);
  1266. }
  1267.  
  1268. # --------------------------------------
  1269.  
  1270. // http://php.net/manual/de/function.hex2bin.php#113057
  1271. if (!function_exists('hex2bin')) {
  1272.     function hex2bin($str) {
  1273.         $sbin = "";
  1274.         $len = strlen($str);
  1275.         for ( $i = 0; $i < $len; $i += 2 ) {
  1276.             $sbin .= pack("H*", substr($str, $i, 2));
  1277.         }
  1278.         return $sbin;
  1279.     }
  1280. }
  1281.  
  1282. // https://stackoverflow.com/questions/72127764/shift-right-left-bitwise-operators-in-php7-gmp-extension
  1283. if (!function_exists('gmp_shiftl')) {
  1284.     function gmp_shiftl($x,$n) { // shift left
  1285.         return(gmp_mul($x,gmp_pow(2,$n)));
  1286.     }
  1287. }
  1288.  
  1289. if (!function_exists('gmp_shiftr')) {
  1290.     function gmp_shiftr($x,$n) { // shift right
  1291.         return(gmp_div_q($x,gmp_pow(2,$n)));
  1292.     }
  1293. }
  1294.