Subversion Repositories oidplus

Rev

Rev 26 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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