Login | ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/oidinfo_api/trunk/uuid_utils.inc.phps
Revision: 9
Committed: Wed Nov 6 23:26:33 2019 UTC (4 months, 3 weeks ago) by daniel-marschall
File size: 16983 byte(s)

File Contents

# Content
1 <?php
2
3 /*
4 * UUID utils for PHP
5 * Copyright 2011-2019 Daniel Marschall, ViaThinkSoft
6 * Version 2019-11-04
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 if (file_exists(__DIR__ . '/mac_utils.inc.phps')) include_once __DIR__ . '/mac_utils.inc.phps';
22
23 define('UUID_NAMEBASED_NS_DNS', '6ba7b810-9dad-11d1-80b4-00c04fd430c8');
24 define('UUID_NAMEBASED_NS_URL', '6ba7b811-9dad-11d1-80b4-00c04fd430c8');
25 define('UUID_NAMEBASED_NS_OID', '6ba7b812-9dad-11d1-80b4-00c04fd430c8');
26 define('UUID_NAMEBASED_NS_X500_DN', '6ba7b814-9dad-11d1-80b4-00c04fd430c8');
27
28 function uuid_valid($uuid) {
29 $uuid = str_replace(array('-', '{', '}'), '', $uuid);
30 $uuid = strtoupper($uuid);
31
32 if (strlen($uuid) != 32) return false;
33
34 $uuid = preg_replace('@[0-9A-F]@', '', $uuid);
35
36 return ($uuid == '');
37 }
38
39 # TODO: Don't echo
40 function uuid_info($uuid) {
41 if (!uuid_valid($uuid)) return false;
42
43 # $uuid = str_replace(array('-', '{', '}'), '', $uuid);
44 $uuid = strtoupper($uuid);
45 $uuid = preg_replace('@[^0-9A-F]@', '', $uuid);
46
47 $x = hexdec(substr($uuid, 16, 1));
48 if ($x >= 14 /* 1110 */) $variant = 3;
49 else if ($x >= 12 /* 1100 */) $variant = 2;
50 else if ($x >= 8 /* 1000 */) $variant = 1;
51 else if ($x >= 0 /* 0000 */) $variant = 0;
52
53
54 switch ($variant) {
55 case 0:
56 echo sprintf("%-24s %s\n", "Variant:", "[0xx] NCS (reserved for backward compatibility)");
57
58 /*
59 * Internal structure of variant #0 UUIDs
60 *
61 * The first 6 octets are the number of 4 usec units of time that have
62 * passed since 1/1/80 0000 GMT. The next 2 octets are reserved for
63 * future use. The next octet is an address family. The next 7 octets
64 * are a host ID in the form allowed by the specified address family.
65 *
66 * Note that while the family field (octet 8) was originally conceived
67 * of as being able to hold values in the range [0..255], only [0..13]
68 * were ever used. Thus, the 2 MSB of this field are always 0 and are
69 * used to distinguish old and current UUID forms.
70 *
71 * +--------------------------------------------------------------+
72 * | high 32 bits of time | 0-3 .time_high
73 * +-------------------------------+-------------------------------
74 * | low 16 bits of time | 4-5 .time_low
75 * +-------+-----------------------+
76 * | reserved | 6-7 .reserved
77 * +---------------+---------------+
78 * | family | 8 .family
79 * +---------------+----------...-----+
80 * | node ID | 9-16 .node
81 * +--------------------------...-----+
82 *
83 */
84
85 // Example of an UUID: 333a2276-0000-0000-0d00-00809c000000
86
87 # TODO: See https://github.com/cjsv/uuid/blob/master/Doc
88
89 # Timestamp: Count of 4us intervals since 01 Jan 1980 00:00:00 GMT
90 # 1/0,000004 = 250000
91 # Seconds between 1970 and 1980 : 315532800
92 # 250000*315532800=78883200000000
93 $timestamp = substr($uuid, 0, 12);
94 $ts = gmp_init($timestamp, 16);
95 $ts = gmp_add($ts, gmp_init("78883200000000"));
96 $ms = gmp_mod($ts, gmp_init("250000"));
97 $ts = gmp_div($ts, gmp_init("250000"));
98 $ts = gmp_strval($ts);
99 $ms = gmp_strval($ms);
100 $ts = gmdate('Y-m-d H:i:s', $ts)."'".str_pad($ms, 7, '0', STR_PAD_LEFT).' GMT';
101 echo sprintf("%-24s %s\n", "Timestamp:", "[0x$timestamp] $ts");
102
103 $reserved = substr($uuid, 12, 4);
104 echo sprintf("%-24s %s\n", "Reserved:", "0x$reserved");
105
106 # Family 13 (dds) looks like node is 00 | nnnnnn 000000.
107 # Family 2 is presumably (ip).
108 # Not sure if anything else was used.
109 $family_hex = substr($uuid, 16, 2);
110 $family_dec = hexdec($family_hex);
111 if ($family_dec == 2) {
112 $family_ = 'IP';
113 } else if ($family_dec == 13) {
114 $family_ = 'DDS (Data Link)';
115 } else {
116 $family_ = "Unknown ($family_dec)"; # There are probably no more families
117 }
118 echo sprintf("%-24s %s\n", "Family:", "[0x$family_hex = $family_dec] $family_");
119
120 $nodeid = substr($uuid, 18, 14);
121 echo sprintf("%-24s %s\n", "Node ID:", "0x$nodeid");
122 # TODO: interprete node id (the family specifies it)
123
124 break;
125 case 1:
126 echo sprintf("%-24s %s\n", "Variant:", "[10x] RFC 4122 (Leach-Mealling-Salz)");
127
128 $version = hexdec(substr($uuid, 12, 1));
129 switch ($version) {
130 case 1:
131 echo sprintf("%-24s %s\n", "Version:", "[1] Time-based with unique random host identifier");
132
133 # Timestamp: Count of 100ns intervals since 15 Oct 1582 00:00:00
134 # 1/0,0000001 = 10000000
135 $timestamp = substr($uuid, 13, 3).substr($uuid, 8, 4).substr($uuid, 0, 8);
136 $ts = gmp_init($timestamp, 16);
137 $ts = gmp_sub($ts, gmp_init("122192928000000000"));
138 $ms = gmp_mod($ts, gmp_init("10000000"));
139 $ts = gmp_div($ts, gmp_init("10000000"));
140 $ts = gmp_strval($ts);
141 $ms = gmp_strval($ms);
142 $ts = gmdate('Y-m-d H:i:s', $ts)."'".str_pad($ms, 7, '0', STR_PAD_LEFT).' GMT';
143 echo sprintf("%-24s %s\n", "Timestamp:", "[0x$timestamp] $ts");
144
145 $x = hexdec(substr($uuid, 16, 4));
146 $dec = $x & 0x3FFF; // The highest 2 bits are used by "variant" (10x)
147 $hex = substr($uuid, 16, 4);
148 echo sprintf("%-24s %s\n", "Clock ID:", "[0x$hex] $dec");
149
150 $x = substr($uuid, 20, 12);
151 $nodeid = '';
152 for ($i=0; $i<6; $i++) {
153 $nodeid .= substr($x, $i*2, 2);
154 if ($i != 5) $nodeid .= ':';
155 }
156 echo sprintf("%-24s %s\n", "Node ID:", "$nodeid");
157
158 if (function_exists('decode_mac')) {
159 echo "\nIn case that this Node ID is a MAC address, here is the interpretation of that MAC address:\n";
160 echo decode_mac($nodeid);
161 }
162
163 break;
164 case 2:
165 echo sprintf("%-24s %s\n", "Version:", "[2] DCE Security version");
166
167 # 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.
168 $x = substr($uuid, 0, 8);
169 echo sprintf("%-24s %s\n", "Local ID:", "0x$x");
170
171 # 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".
172 $x = substr($uuid, 18, 2);
173 if ($x == '00') $domain_info = 'POSIX: User-ID / Non-POSIX: site-defined';
174 else if ($x == '01') $domain_info = 'POSIX: Group-ID / Non-POSIX: site-defined';
175 else $domain_info = 'site-defined';
176 echo sprintf("%-24s %s\n", "Local Domain:", "0x$x ($domain_info)");
177
178 # Timestamp: Count of 100ns intervals since 15 Oct 1582 00:00:00
179 # 1/0,0000001 = 10000000
180 $timestamp = substr($uuid, 13, 3).substr($uuid, 8, 4).'00000000';
181 $ts = gmp_init($timestamp, 16);
182 $ts = gmp_sub($ts, gmp_init("122192928000000000"));
183 $ms = gmp_mod($ts, gmp_init("10000000"));
184 $ts = gmp_div($ts, gmp_init("10000000"));
185 $ts = gmp_strval($ts);
186 $ms = gmp_strval($ms);
187 $ts_min = gmdate('Y-m-d H:i:s', $ts)."'".str_pad($ms, 7, '0', STR_PAD_LEFT).' GMT';
188
189 $timestamp = substr($uuid, 13, 3).substr($uuid, 8, 4).'FFFFFFFF';
190 $ts = gmp_init($timestamp, 16);
191 $ts = gmp_sub($ts, gmp_init("122192928000000000"));
192 $ms = gmp_mod($ts, gmp_init("10000000"));
193 $ts = gmp_div($ts, gmp_init("10000000"));
194 $ts = gmp_strval($ts);
195 $ms = gmp_strval($ms);
196 $ts_max = gmdate('Y-m-d H:i:s', $ts)."'".str_pad($ms, 7, '0', STR_PAD_LEFT).' GMT';
197
198 $timestamp = substr($uuid, 13, 3).substr($uuid, 8, 4).'xxxxxxxx';
199 echo sprintf("%-24s %s\n", "Timestamp:", "[0x$timestamp] $ts_min - $ts_max");
200
201 $x = hexdec(substr($uuid, 16, 2).'00');
202 $dec_min = $x & 0x3FFF; // The highest 2 bits are used by "variant" (10x)
203 $x = hexdec(substr($uuid, 16, 2).'FF');
204 $dec_max = $x & 0x3FFF; // The highest 2 bits are used by "variant" (10x)
205 $hex = substr($uuid, 16, 2).'xx';
206 echo sprintf("%-24s %s\n", "Clock ID:", "[0x$hex] $dec_min - $dec_max");
207
208 $x = substr($uuid, 20, 12);
209 $nodeid = '';
210 for ($i=0; $i<6; $i++) {
211 $nodeid .= substr($x, $i*2, 2);
212 if ($i != 5) $nodeid .= ':';
213 }
214 echo sprintf("%-24s %s\n", "Node ID:", "$nodeid");
215
216 if (function_exists('decode_mac')) {
217 echo "\nIn case that this Node ID is a MAC address, here is the interpretation of that MAC address:\n";
218 echo decode_mac($nodeid);
219 }
220
221 break;
222 case 3:
223 echo sprintf("%-24s %s\n", "Version:", "[3] Name-based (MD5 hash)");
224
225 $hash = str_replace('-', '', strtolower($uuid));
226 $hash[12] = '?'; // was overwritten by version
227 $hash[16] = '?'; // was partially overwritten by variant
228
229 echo sprintf("%-24s %s\n", "MD5(Namespace+Subject):", "$hash");
230
231 break;
232 case 4:
233 echo sprintf("%-24s %s\n", "Version:", "[4] Random");
234
235 $rand = '';
236 for ($i=0; $i<16; $i++) {
237 $bin = base_convert(substr($uuid, $i*2, 2), 16, 2);
238 $bin = str_pad($bin, 8, "0", STR_PAD_LEFT);
239
240 if ($i == 6) {
241 $bin[0] = 'x';
242 $bin[1] = 'x';
243 } else if ($i == 8) {
244 $bin[0] = 'x';
245 $bin[1] = 'x';
246 $bin[2] = 'x';
247 $bin[3] = 'x';
248 }
249
250 $rand .= "$bin ";
251 }
252
253 echo sprintf("%-24s %s\n", "Random bits:", trim($rand));
254
255 break;
256 case 5:
257 echo sprintf("%-24s %s\n", "Version:", "[5] Name-based (SHA-1 hash)");
258
259 $hash = str_replace('-', '', strtolower($uuid));
260 $hash[12] = '?'; // was overwritten by version
261 $hash[16] = '?'; // was partially overwritten by variant
262 $hash .= '????????'; // was cut off
263
264 echo sprintf("%-24s %s\n", "SHA1(Namespace+Subject):", "$hash");
265
266
267 break;
268 default:
269 echo sprintf("%-24s %s\n", "Version:", "[$version] Unknown");
270 break;
271 }
272
273 break;
274 case 2:
275 echo sprintf("%-24s %s\n", "Variant:", "[110] Reserved for Microsoft Corporation");
276 break;
277 case 3:
278 echo sprintf("%-24s %s\n", "Variant:", "[111] Reserved for future use");
279 break;
280 }
281 }
282
283 function uuid_canonize($uuid) {
284 if (!uuid_valid($uuid)) return false;
285 return oid_to_uuid(uuid_to_oid($uuid));
286 }
287
288 function oid_to_uuid($oid) {
289 if (!is_uuid_oid($oid)) return false;
290
291 if ($oid[0] == '.') {
292 $oid = substr($oid, 1);
293 }
294 $ary = explode('.', $oid);
295 $val = $ary[2];
296
297 $x = gmp_init($val, 10);
298 $y = gmp_strval($x, 16);
299 $y = str_pad($y, 32, "0", STR_PAD_LEFT);
300 return substr($y, 0, 8).'-'.
301 substr($y, 8, 4).'-'.
302 substr($y, 12, 4).'-'.
303 substr($y, 16, 4).'-'.
304 substr($y, 20, 12);
305 }
306
307 function is_uuid_oid($oid, $only_allow_root=false) {
308 if ($oid[0] == '.') $oid = substr($oid, 1); // remove leading dot
309
310 $ary = explode('.', $oid);
311
312 if ($only_allow_root) {
313 if (count($ary) != 3) return false;
314 }
315
316 if ($ary[0] != '2') return false;
317 if ($ary[1] != '25') return false;
318 for ($i=2; $i<count($ary); $i++) {
319 $v = $ary[$i];
320 if (!is_numeric($v)) return false;
321 if ($i == 2) {
322 // Must be in the range of 128 bit UUID
323 $test = gmp_init($v, 10);
324 if (strlen(gmp_strval($test, 16)) > 32) return false;
325 }
326 if ($v < 0) return false;
327 }
328
329 return true;
330 }
331
332 function uuid_to_oid($uuid) {
333 if (!uuid_valid($uuid)) return false;
334
335 $uuid = str_replace(array('-', '{', '}'), '', $uuid);
336 $x = gmp_init($uuid, 16);
337 return '2.25.'.gmp_strval($x, 10); # TODO: parameter with or without leading dot
338 }
339
340 function gen_uuid($prefer_timebased = true) {
341 if ($prefer_timebased) $uuid = gen_uuid_timebased();
342 if ($uuid === false) $uuid = gen_uuid_random();
343 return $uuid;
344 }
345
346 // Version 1 (Time based) UUID
347 function gen_uuid_timebased() {
348 # On Debian: aptitude install php-uuid
349 # extension_loaded('uuid')
350 if (function_exists('uuid_create')) {
351 # OSSP uuid extension like seen in php5-uuid at Debian 8
352 /*
353 $x = uuid_create($context);
354 uuid_make($context, UUID_MAKE_V1);
355 uuid_export($context, UUID_FMT_STR, $uuid);
356 return trim($uuid);
357 */
358
359 # PECL uuid extension like seen in php-uuid at Debian 9
360 return trim(uuid_create(UUID_TYPE_TIME));
361 }
362
363 # On Debian: aptitude install uuid-runtime
364 $out = array();
365 exec('uuidgen -t', $out, $ec);
366 if ($ec == 0) return $out[0];
367
368 # TODO: Implement the time based generation routine ourselves!
369 # At the moment we cannot determine the time based UUID
370 return false;
371 }
372
373 // Version 2 (DCE Security) UUID
374 function gen_uuid_dce($domain, $id) {
375 # Start with a version 1 UUID
376 $uuid = gen_uuid_timebased();
377
378 # Add ID
379 $uuid = str_pad(dechex($id), 8, '0', STR_PAD_LEFT) . substr($uuid, 8);
380
381 # Add domain
382 $uuid = substr($uuid,0,21) . str_pad(dechex($domain), 2, '0', STR_PAD_LEFT) . substr($uuid, 23);
383
384 # Change version to 2
385 $uuid[14] = '2';
386
387 return $uuid;
388 }
389
390 // Version 3 (MD5 name based) UUID
391 function gen_uuid_md5_namebased($namespace_uuid, $name) {
392 if (!uuid_valid($namespace_uuid)) return false;
393 $namespace_uuid = uuid_canonize($namespace_uuid);
394 $namespace_uuid = str_replace('-', '', $namespace_uuid);
395 $namespace_uuid = hex2bin($namespace_uuid);
396
397 $hash = md5($namespace_uuid.$name);
398 $hash[12] = '3'; // Set version: 3 = MD5
399 $hash[16] = dechex(hexdec($hash[16]) & 0x3 | 0x8); // Set variant to "10xx" (RFC4122)
400
401 return substr($hash, 0, 8).'-'.
402 substr($hash, 8, 4).'-'.
403 substr($hash, 12, 4).'-'.
404 substr($hash, 16, 4).'-'.
405 substr($hash, 20, 12);
406 }
407
408 // Version 4 (Random) UUID
409 function gen_uuid_random() {
410 # On Debian: aptitude install php-uuid
411 # extension_loaded('uuid')
412 if (function_exists('uuid_create')) {
413 # OSSP uuid extension like seen in php5-uuid at Debian 8
414 /*
415 $x = uuid_create($context);
416 uuid_make($context, UUID_MAKE_V4);
417 uuid_export($context, UUID_FMT_STR, $uuid);
418 return trim($uuid);
419 */
420
421 # PECL uuid extension like seen in php-uuid at Debian 9
422 return trim(uuid_create(UUID_TYPE_RANDOM));
423 }
424
425 # On Debian: aptitude install uuid-runtime
426 $out = array();
427 exec('uuidgen -r', $out, $ec);
428 if ($ec == 0) return $out[0];
429
430 # On Debian Jessie: UUID V4 (Random)
431 if (file_exists('/proc/sys/kernel/random/uuid')) {
432 return file_get_contents('/proc/sys/kernel/random/uuid');
433 }
434
435 # Make the UUID by ourselves
436 # Source: http://rogerstringer.com/2013/11/15/generate-uuids-php
437 return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
438 mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
439 mt_rand( 0, 0xffff ),
440 mt_rand( 0, 0x0fff ) | 0x4000,
441 mt_rand( 0, 0x3fff ) | 0x8000,
442 mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
443 );
444 }
445
446 // Version 5 (SHA1 name based) UUID
447 function gen_uuid_sha1_namebased($namespace_uuid, $name) {
448 $namespace_uuid = str_replace('-', '', $namespace_uuid);
449 $namespace_uuid = hex2bin($namespace_uuid);
450
451 $hash = sha1($namespace_uuid.$name);
452 $hash[12] = '5'; // Set version: 5 = SHA1
453 $hash[16] = dechex(hexdec($hash[16]) & 0x3 | 0x8); // Set variant to "10xx" (RFC4122)
454
455 return substr($hash, 0, 8).'-'.
456 substr($hash, 8, 4).'-'.
457 substr($hash, 12, 4).'-'.
458 substr($hash, 16, 4).'-'.
459 substr($hash, 20, 12);
460 }
461
462 function uuid_numeric_value($uuid) {
463 $oid = uuid_to_oid($uuid);
464 if (!$oid) return false;
465 return substr($oid, strlen('2.25.'));
466 }
467
468 function uuid_c_syntax($uuid) {
469 $uuid = str_replace('{', '', $uuid);
470 return '{ 0x' . substr($uuid, 0, 8) .
471 ', 0x' . substr($uuid, 9, 4) .
472 ', 0x' . substr($uuid, 14, 4) .
473 ', { 0x' . substr($uuid, 19, 2).
474 ', 0x' . substr($uuid, 21, 2) .
475 ', 0x' . substr($uuid, 24, 2) .
476 ', 0x' . substr($uuid, 26, 2) .
477 ', 0x' . substr($uuid, 28, 2) .
478 ', 0x' . substr($uuid, 30, 2) .
479 ', 0x' . substr($uuid, 32, 2) .
480 ', 0x' . substr($uuid, 34, 2) . ' } }';
481 }
482
483 # ---
484
485 // http://php.net/manual/de/function.hex2bin.php#113057
486 if ( !function_exists( 'hex2bin' ) ) {
487 function hex2bin( $str ) {
488 $sbin = "";
489 $len = strlen( $str );
490 for ( $i = 0; $i < $len; $i += 2 ) {
491 $sbin .= pack( "H*", substr( $str, $i, 2 ) );
492 }
493
494 return $sbin;
495 }
496 }