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 + */ + +#ifndef UUID_H +#define UUID_H + +#include +#include +#include +#include +#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 + * @} + */ + +#include +#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); +}