From fc03d6f694e8793a2e2d007002581bcc7fb8a5c3 Mon Sep 17 00:00:00 2001
From: Koen Zandberg <koen@bergzand.net>
Date: Sat, 28 Apr 2018 11:33:06 +0200
Subject: [PATCH] uuid: Initial import of RFC4122 UUID functions

Provides functions for type 3, 4 and 5 UUID generations.

UUID type 1 is timestamp based and requires an accurate time source. For
this reason it is left out of this implementation. UUID type 2 is not
defined in RFC 4122 and thus also not included here
---
 Makefile.dep       |   5 ++
 sys/include/uuid.h | 147 +++++++++++++++++++++++++++++++++++++++++++++
 sys/uuid/Makefile  |   1 +
 sys/uuid/uuid.c    | 112 ++++++++++++++++++++++++++++++++++
 4 files changed, 265 insertions(+)
 create mode 100644 sys/include/uuid.h
 create mode 100644 sys/uuid/Makefile
 create mode 100644 sys/uuid/uuid.c

diff --git a/Makefile.dep b/Makefile.dep
index 6fbd8a1e6f..0eb348aaa4 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -728,6 +728,11 @@ ifneq (,$(filter tlsf-malloc,$(USEMODULE)))
   USEPKG += tlsf
 endif
 
+ifneq (,$(filter uuid,$(USEMODULE)))
+  USEMODULE += hashes
+  USEMODULE += random
+endif
+
 # always select gpio (until explicit dependencies are sorted out)
 FEATURES_OPTIONAL += periph_gpio
 
diff --git a/sys/include/uuid.h b/sys/include/uuid.h
new file mode 100644
index 0000000000..2616ebd3b4
--- /dev/null
+++ b/sys/include/uuid.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ * Copyright (C) 2018 Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @defgroup    sys_uuid RFC 4122 compliant UUID's
+ * @ingroup     sys
+ * @brief       Provides RFC 4122 compliant UUID's
+ *
+ * This module provides RFC 4122 compliant UUID generation. The UUID stored in
+ * @ref uuid_t struct is stored in network byte order.
+ *
+ * @{
+ *
+ * @file
+ * @brief       [RFC 4122](https://tools.ietf.org/html/rfc4122) UUID functions
+ *
+ * @author      Koen Zandberg <koen@bergzand.net>
+ */
+
+#ifndef UUID_H
+#define UUID_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "byteorder.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UUID_NODE_LEN    (6U)   /**< Size of the node identifier in bytes */
+
+/**
+ * @name UUID version identifiers
+ * @{
+ */
+#define UUID_V1         (0x01)  /**< Type 1 UUID - timestamp based      */
+#define UUID_V2         (0x02)  /**< Type 2 UUID - DCE Security version */
+#define UUID_V3         (0x03)  /**< Type 3 UUID - Name based with MD5  */
+#define UUID_V4         (0x04)  /**< Type 4 UUID - Random generated     */
+#define UUID_V5         (0x05)  /**< Type 5 UUID - Name based with SHA1 */
+/** @} */
+
+/**
+ * @name Version part of the time_hi field
+ */
+#define UUID_VERSION_MASK   (0xF000)
+
+/**
+ * @brief UUID layout
+ *
+ * Directly from [rfc4122](https://tools.ietf.org/html/rfc4122#section-4.1.2)
+ */
+typedef struct __attribute__((packed)) {
+    network_uint32_t time_low;      /**< The low field of the timestamp       */
+    network_uint16_t time_mid;      /**< The middle field of the timestamp    */
+    network_uint16_t time_hi;       /**< The high field of the timestamp
+                                     *  multiplexed with the version  number */
+    uint8_t clk_seq_hi_res;         /**< The high field of the clock sequence
+                                     *  Multiplexed with the variant         */
+    uint8_t clk_seq_low;            /**< The low field of the clock sequence  */
+    uint8_t node[UUID_NODE_LEN];    /**< The spatially unique node identifier */
+} uuid_t;
+
+
+/**
+ * @name Namespace IDs from RFC4122
+ *
+ * Copied from [rfc4122 Appendix
+ * C](https://tools.ietf.org/html/rfc4122#appendix-C)
+ *
+ * @{
+ */
+extern const uuid_t uuid_namespace_dns;     /**< DNS namespace UUID */
+extern const uuid_t uuid_namespace_url;     /**< URL namespace UUID */
+extern const uuid_t uuid_namespace_iso;     /**< ISO OID namespace UUID */
+extern const uuid_t uuid_namespace_x500;    /**< X.500 DN namespace UUID */
+/** @} */
+
+/**
+ * Generate a version 3(md5 based) UUID from a namespace and a byte array
+ *
+ * @param[out]  uuid    UUID struct to fill
+ * @param[in]   ns      Namespace UUID
+ * @param[in]   name    Ptr to byte array to use as name part
+ * @param[in]   len     Length of the byte array
+ */
+void uuid_v3(uuid_t *uuid, const uuid_t *ns, const uint8_t *name, size_t len);
+
+
+/**
+ * Generate a version 4(Full random) UUID
+ *
+ * @param[out]  uuid    UUID struct to fill
+ */
+void uuid_v4(uuid_t *uuid);
+
+/**
+ * Generate a version 5(sha1 based) UUID from a namespace and a byte array
+ *
+ * @param[out]  uuid    UUID struct to fill
+ * @param[in]   ns      Namespace UUID
+ * @param[in]   name    Ptr to byte array to use as name part
+ * @param[in]   len     Length of the byte array
+ */
+void uuid_v5(uuid_t *uuid, const uuid_t *ns, const uint8_t *name, size_t len);
+
+/**
+ * Retrieve the type number of a UUID
+ *
+ * @param[in]   uuid    UUID to retrieve version number from
+ *
+ * @return              Version number
+ */
+static inline unsigned uuid_version(uuid_t *uuid)
+{
+    uint16_t time_hi_vers = byteorder_ntohs(uuid->time_hi);
+
+    return (time_hi_vers & 0xF000) >> 12;
+}
+
+/**
+ * Compare two UUID's
+ *
+ * @param[in]   uuid1   First uuid to compare
+ * @param[in]   uuid2   Second uuid to compare
+ *
+ * @return              True when equal
+ */
+static inline bool uuid_equal(uuid_t *uuid1, uuid_t *uuid2)
+{
+    return (memcmp(uuid1, uuid2, sizeof(uuid_t)) == 0);
+}
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* UUID_H */
+/** @} */
diff --git a/sys/uuid/Makefile b/sys/uuid/Makefile
new file mode 100644
index 0000000000..48422e909a
--- /dev/null
+++ b/sys/uuid/Makefile
@@ -0,0 +1 @@
+include $(RIOTBASE)/Makefile.base
diff --git a/sys/uuid/uuid.c b/sys/uuid/uuid.c
new file mode 100644
index 0000000000..a7c9c95e40
--- /dev/null
+++ b/sys/uuid/uuid.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ * Copyright (C) 2018 Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup sys_uuid
+ * @{
+ * @file
+ * @brief   Function implementations to create RFC 4122 UUID objects
+ *
+ * @author  Koen Zandberg <koen@bergzand.net>
+ * @}
+ */
+
+#include <string.h>
+#include "byteorder.h"
+#include "hashes/md5.h"
+#include "hashes/sha1.h"
+#include "random.h"
+#include "uuid.h"
+
+const uuid_t uuid_namespace_dns = { /* 6ba7b810-9dad-11d1-80b4-00c04fd430c8 */
+    .time_low.u8 =      { 0x6b, 0xa7, 0xb8, 0x10 },
+    .time_mid.u8 =      { 0x9d, 0xad },
+    .time_hi.u8 =       { 0x11, 0xd1 },
+    .clk_seq_hi_res =   0x80,
+    .clk_seq_low =      0xb4,
+    .node =             { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }
+};
+
+const uuid_t uuid_namespace_url = { /* 6ba7b811-9dad-11d1-80b4-00c04fd430c8 */
+    .time_low.u8 =      { 0x6b, 0xa7, 0xb8, 0x11 },
+    .time_mid.u8 =      { 0x9d, 0xad },
+    .time_hi.u8 =       { 0x11, 0xd1 },
+    .clk_seq_hi_res =   0x80,
+    .clk_seq_low =      0xb4,
+    .node =             { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }
+};
+
+/* Name string is an ISO OID */
+const uuid_t uuid_namespace_iso = { /* 6ba7b812-9dad-11d1-80b4-00c04fd430c8 */
+    .time_low.u8 =      { 0x6b, 0xa7, 0xb8, 0x12 },
+    .time_mid.u8 =      { 0x9d, 0xad },
+    .time_hi.u8 =       { 0x11, 0xd1 },
+    .clk_seq_hi_res =   0x80,
+    .clk_seq_low =      0xb4,
+    .node =             { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }
+};
+
+const uuid_t uuid_namespace_x500 = { /* 6ba7b814-9dad-11d1-80b4-00c04fd430c8 */
+    .time_low.u8 =      { 0x6b, 0xa7, 0xb8, 0x14 },
+    .time_mid.u8 =      { 0x9d, 0xad },
+    .time_hi.u8 =       { 0x11, 0xd1 },
+    .clk_seq_hi_res =   0x80,
+    .clk_seq_low =      0xb4,
+    .node =             { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }
+};
+
+static inline void _set_version(uuid_t *uuid, unsigned version)
+{
+    uint16_t time_hi = byteorder_ntohs(uuid->time_hi) & 0x0fff;
+
+    time_hi |= (version << 12);
+    uuid->time_hi = byteorder_htons(time_hi);
+}
+
+static inline void _set_reserved(uuid_t *uuid)
+{
+    uuid->clk_seq_hi_res = (uuid->clk_seq_hi_res & 0x3f) | 0x80;
+}
+
+void uuid_v3(uuid_t *uuid, const uuid_t *ns, const uint8_t *name, size_t len)
+{
+    /* Digest calculation */
+    md5_ctx_t ctx;
+
+    md5_init(&ctx);
+    md5_update(&ctx, ns, sizeof(uuid_t));
+    md5_update(&ctx, name, len);
+    md5_final(&ctx, uuid);
+
+    _set_version(uuid, UUID_V3);
+    _set_reserved(uuid);
+}
+
+void uuid_v4(uuid_t *uuid)
+{
+    random_bytes((uint8_t *)uuid, sizeof(uuid_t));
+    _set_version(uuid, UUID_V4);
+    _set_reserved(uuid);
+}
+
+void uuid_v5(uuid_t *uuid, const uuid_t *ns, const uint8_t *name, size_t len)
+{
+    uint8_t digest[20];
+    sha1_context ctx;
+
+    sha1_init(&ctx);
+    sha1_update(&ctx, ns, sizeof(uuid_t));
+    sha1_update(&ctx, name, len);
+    sha1_final(&ctx, digest);
+
+    memcpy(uuid, digest, sizeof(uuid_t));
+
+    _set_version(uuid, UUID_V5);
+    _set_reserved(uuid);
+}
-- 
GitLab