]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
uuidgen: add support for hash-based UUIDs
authorPhilip Prindeville <philipp@redfish-solutions.com>
Mon, 28 Aug 2017 19:36:30 +0000 (13:36 -0600)
committerKarel Zak <kzak@redhat.com>
Tue, 5 Sep 2017 09:56:24 +0000 (11:56 +0200)
Signed-off-by: Philip Prindeville <philipp@redfish-solutions.com>
misc-utils/uuidgen.1
misc-utils/uuidgen.c
tests/expected/uuid/oids [new file with mode: 0644]
tests/ts/uuid/oids [new file with mode: 0755]

index 56c2bc7bd27920959c86b312defe310949d538c7..500e3124c509a58be35f6a3c8402fb5c5c0867e8 100644 (file)
@@ -18,18 +18,41 @@ all UUIDs created on the local system,
 and among UUIDs created on other systems in the past
 and in the future.
 .PP
-There are two types of UUIDs which
+There are three types of UUIDs which
 .B uuidgen
-can generate: time-based UUIDs and random-based UUIDs.  By default
+can generate: time-based UUIDs, random-based UUIDs, and hash-based UUIDs.
+By default
 .B uuidgen
 will generate a random-based UUID if a high-quality random number
 generator is present.  Otherwise, it will choose a time-based UUID.
-It is possible to force the generation of one of these two
+It is possible to force the generation of one of these first two
 UUID types by using the
 .B \-r
 or
 .B \-t
 options.
+.PP
+The third type of UUID is generated with the
+.B \-m
+or
+.B \-s
+options (MD5 or SHA1, respectively), followed by
+.BR "\-n " \fInamespace
+and
+.BR "\-N " \fIname\fR.
+The \fInamespace\fR may either be a well-known UUID, or else
+an alias to one of the well-known UUIDs defined in RFC 4122, that is
+.BR @dns ,
+.BR @url ,
+.BR @oid ,
+or
+.BR @x500 .
+The \fIname\fR is an arbitrary string value.  The generated UUID is the
+digest of the concatentation of the namespace UUID and the name value, hashed
+with the MD5 or SHA1 algorithms.  It is, therefore, a predictable value
+which may be useful when UUIDs are being used as handles or nonces for
+more complex values or values which shouldn't be disclosed directly.
+See the RFC for more information.
 .SH OPTIONS
 .TP
 .BR \-r , " \-\-random"
@@ -47,13 +70,29 @@ Display help text and exit.
 .TP
 .BR \-V , " \-\-version"
 Display version information and exit.
+.TP
+.BR \-m , " \-\-md5"
+Use MD5 as the hash algorithm.
+.TP
+.BR \-s , " \-\-sha1"
+Use SHA1 as the hash algorith.
+.TP
+.BR \-n , " \-\-namespace " \fInamespace\fP
+Generate the hash with the \fInamespace\fP prefix.
+.TP
+.BR \-N , " \-\-name " \fIname\fR
+Generate the hash of the \fIname\fR.
+.TP
+.BR \-x , " \-\-hex"
+Interpret name \fIname\fR as a hexidecimal string.
 .SH "CONFORMING TO"
 OSF DCE 1.1
 .SH AUTHOR
 .B uuidgen
 was written by Andreas Dilger for libuuid.
 .SH SEE ALSO
-.BR libuuid (3)
+.BR libuuid (3),
+.B "RFC 4122"
 .SH AVAILABILITY
 The uuidgen command is part of the util-linux package and is available from
 https://www.kernel.org/pub/linux/utils/util-linux/.
index ac950747d7de6345cd802da8f1fcc0acdada156e..c1970695389cdf6c17eba99e57891c80b7069b4c 100644 (file)
@@ -29,27 +29,75 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_("Create a new UUID value.\n"), out);
 
        fputs(USAGE_OPTIONS, out);
-       fputs(_(" -r, --random     generate random-based uuid\n"), out);
-       fputs(_(" -t, --time       generate time-based uuid\n"), out);
+       fputs(_(" -r, --random        generate random-based uuid\n"), out);
+       fputs(_(" -t, --time          generate time-based uuid\n"), out);
+       fputs(_(" -n, --namespace ns  generate hash-based uuid in this namespace\n"), out);
+       fputs(_(" -N, --name name     generate hash-based uuid from this name\n"), out);
+       fputs(_(" -m, --md5           generate md5 hash\n"), out);
+       fputs(_(" -s, --sha1          generate sha1 hash\n"), out);
+       fputs(_(" -x, --hex           interpret name as hex string\n"), out);
        fputs(USAGE_SEPARATOR, out);
        printf(USAGE_HELP_OPTIONS(18));
        printf(USAGE_MAN_TAIL("uuidgen(1)"));
        exit(EXIT_SUCCESS);
 }
 
+static char *unhex(const char *value, size_t *valuelen)
+{
+       char c, *value2;
+       unsigned n, x;
+
+       if (*valuelen % 2 != 0) {
+badstring:
+               fprintf(stderr, "%s: not a valid hex string\n", program_invocation_short_name);
+               errtryhelp(EXIT_FAILURE);
+       }
+
+       value2 = malloc(*valuelen / 2 + 1);
+
+       for (x = n = 0; n < *valuelen; n++) {
+               c = value[n];
+               if ('0' <= c && c <= '9')
+                       x += c - '0';
+               else if (('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'))
+                       x += (c - 'A' + 10) & 0xf;
+               else
+                       goto badstring;
+
+               if (n % 2 == 0)
+                       x *= 16;
+               else {
+                       value2[n / 2] = x;
+                       x = 0;
+               }
+       }
+       value2[n / 2] = '\0';
+
+       *valuelen = (n / 2);
+
+       return value2;
+}
+
 int
 main (int argc, char *argv[])
 {
        int    c;
-       int    do_type = 0;
+       int    do_type = 0, is_hex = 0;
        char   str[UUID_STR_LEN];
-       uuid_t uu;
+       char   *namespace = NULL, *name = NULL;
+       size_t namelen = 0;
+       uuid_t ns, uu;
 
        static const struct option longopts[] = {
                {"random", no_argument, NULL, 'r'},
                {"time", no_argument, NULL, 't'},
                {"version", no_argument, NULL, 'V'},
                {"help", no_argument, NULL, 'h'},
+               {"namespace", required_argument, NULL, 'n'},
+               {"name", required_argument, NULL, 'N'},
+               {"md5", no_argument, NULL, 'm'},
+               {"sha1", no_argument, NULL, 's'},
+               {"hex", no_argument, NULL, 'x'},
                {NULL, 0, NULL, 0}
        };
 
@@ -58,7 +106,7 @@ main (int argc, char *argv[])
        textdomain(PACKAGE);
        atexit(close_stdout);
 
-       while ((c = getopt_long(argc, argv, "rtVh", longopts, NULL)) != -1)
+       while ((c = getopt_long(argc, argv, "rtVhn:N:msx", longopts, NULL)) != -1)
                switch (c) {
                case 't':
                        do_type = UUID_TYPE_DCE_TIME;
@@ -69,12 +117,53 @@ main (int argc, char *argv[])
                case 'V':
                        printf(UTIL_LINUX_VERSION);
                        return EXIT_SUCCESS;
+               case 'n':
+                       namespace = optarg;
+                       break;
+               case 'N':
+                       name = optarg;
+                       break;
+               case 'm':
+                       do_type = UUID_TYPE_DCE_MD5;
+                       break;
+               case 's':
+                       do_type = UUID_TYPE_DCE_SHA1;
+                       break;
+               case 'x':
+                       is_hex = 1;
+                       break;
                case 'h':
                        usage();
                default:
                        errtryhelp(EXIT_FAILURE);
                }
 
+       if (namespace) {
+               if (!name) {
+                       fprintf(stderr, "%s: --namespace requires --name argument\n", program_invocation_short_name);
+                       errtryhelp(EXIT_FAILURE);
+               }
+               if (do_type != UUID_TYPE_DCE_MD5 && do_type != UUID_TYPE_DCE_SHA1) {
+                       fprintf(stderr, "%s: --namespace requires --md5 or --sha1\n", program_invocation_short_name);
+                       errtryhelp(EXIT_FAILURE);
+               }
+       } else {
+               if (name) {
+                       fprintf(stderr, "%s: --name requires --namespace argument\n", program_invocation_short_name);
+                       errtryhelp(EXIT_FAILURE);
+               }
+               if (do_type == UUID_TYPE_DCE_MD5 || do_type == UUID_TYPE_DCE_SHA1) {
+                       fprintf(stderr, "%s: --md5 or --sha1 require --namespace\n", program_invocation_short_name);
+                       errtryhelp(EXIT_FAILURE);
+               }
+       }
+
+       if (name) {
+               namelen = strlen(name);
+               if (is_hex)
+                       name = unhex(name, &namelen);
+       }
+
        switch (do_type) {
        case UUID_TYPE_DCE_TIME:
                uuid_generate_time(uu);
@@ -82,6 +171,28 @@ main (int argc, char *argv[])
        case UUID_TYPE_DCE_RANDOM:
                uuid_generate_random(uu);
                break;
+       case UUID_TYPE_DCE_MD5:
+       case UUID_TYPE_DCE_SHA1:
+               if (namespace[0] == '@' && namespace[1] != '\0') {
+                       const uuid_t *uuidptr;
+
+                       uuidptr = uuid_get_template(&namespace[1]);
+                       if (uuidptr == NULL) {
+                               fprintf(stderr, "%s: unknown namespace alias '%s'\n", program_invocation_short_name, namespace);
+                               errtryhelp(EXIT_FAILURE);
+                       }
+                       memcpy(ns, *uuidptr, sizeof(ns));
+               } else {
+                       if (uuid_parse(namespace, ns) != 0) {
+                               fprintf(stderr, "%s: invalid uuid for namespace '%s'\n", program_invocation_short_name, namespace);
+                               errtryhelp(EXIT_FAILURE);
+                       }
+               }
+               if (do_type == UUID_TYPE_DCE_MD5)
+                       uuid_generate_md5(uu, ns, name, namelen);
+               else
+                       uuid_generate_sha1(uu, ns, name, namelen);
+               break;
        default:
                uuid_generate(uu);
                break;
@@ -91,5 +202,8 @@ main (int argc, char *argv[])
 
        printf("%s\n", str);
 
+       if (is_hex)
+               free(name);
+
        return EXIT_SUCCESS;
 }
diff --git a/tests/expected/uuid/oids b/tests/expected/uuid/oids
new file mode 100644 (file)
index 0000000..4644848
--- /dev/null
@@ -0,0 +1,4 @@
+3d813cbb-47fb-32ba-91df-831e1593ac29
+5df41881-3aed-3515-88a7-2f4a814cf09e
+2ed6657d-e927-568b-95e1-2665a8aea6a2
+fcdc2122-78d2-59f7-91ed-041a561ef652
diff --git a/tests/ts/uuid/oids b/tests/ts/uuid/oids
new file mode 100755 (executable)
index 0000000..3b005c5
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# This file is part of util-linux.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This file is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="oids"
+
+. $TS_TOPDIR/functions.sh
+ts_init "$*"
+
+: . > $TS_OUTPUT
+
+uuidgen --md5 --namespace @dns --name "www.widgets.com" >> $TS_OUTPUT
+
+uuidgen --md5 --namespace @dns --name "www.example.com" >> $TS_OUTPUT
+
+uuidgen --sha1 --namespace @dns --name "www.example.com" >> $TS_OUTPUT
+
+uuidgen --sha1 --namespace @oid --hex --name "525400fc0f5e" >> $TS_OUTPUT
+
+ts_finalize