mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #9046 from bergzand/pr/uuid/initial
uuid: Initial import of RFC4122 UUID functions
This commit is contained in:
commit
fce61f91f0
@ -736,6 +736,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
|
||||
|
||||
|
147
sys/include/uuid.h
Normal file
147
sys/include/uuid.h
Normal file
@ -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 */
|
||||
/** @} */
|
1
sys/uuid/Makefile
Normal file
1
sys/uuid/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
112
sys/uuid/uuid.c
Normal file
112
sys/uuid/uuid.c
Normal file
@ -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);
|
||||
}
|
1
tests/unittests/tests-uuid/Makefile
Normal file
1
tests/unittests/tests-uuid/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
1
tests/unittests/tests-uuid/Makefile.include
Normal file
1
tests/unittests/tests-uuid/Makefile.include
Normal file
@ -0,0 +1 @@
|
||||
USEMODULE += uuid
|
113
tests/unittests/tests-uuid/tests-uuid.c
Normal file
113
tests/unittests/tests-uuid/tests-uuid.c
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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 tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Unit tests for uuid module
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "embUnit.h"
|
||||
#include "uuid.h"
|
||||
#include "hashes/md5.h"
|
||||
#include "hashes/sha1.h"
|
||||
|
||||
const char riotos_org[] = "riot-os.org";
|
||||
const char test_str[] = "unittest";
|
||||
|
||||
/*
|
||||
* Generated with python:
|
||||
* import uuid;
|
||||
* riot = uuid.uuid3(uuid.NAMESPACE_DNS, "riot-os.org")
|
||||
* print(riot.hex)
|
||||
* print(uuid.uuid3(riot, "unittest").hex)
|
||||
*/
|
||||
const uint8_t v3_check1[] = {0x23, 0x99, 0x0b, 0xda, 0x1e, 0xe7, 0x34, 0x16,
|
||||
0x90, 0xfe, 0x69, 0x30, 0x7d, 0x90, 0x64, 0x0e};
|
||||
const uint8_t v3_check2[] = {0x20, 0xf5, 0x36, 0x91, 0x91, 0xae, 0x3c, 0xfa,
|
||||
0x99, 0xb5, 0x8e, 0xf9, 0xfa, 0xc2, 0x76, 0x55};
|
||||
|
||||
/*
|
||||
* Generated with python:
|
||||
* import uuid;
|
||||
* riot = uuid.uuid5(uuid.NAMESPACE_DNS, "riot-os.org")
|
||||
* print(riot.hex)
|
||||
* print(uuid.uuid5(riot, "unittest").hex)
|
||||
*/
|
||||
const uint8_t v5_check1[] = {0x54, 0x7d, 0x0d, 0x74, 0x6d, 0x3a, 0x5a, 0x92,
|
||||
0x96, 0x62, 0x48, 0x81, 0xaf, 0xd9, 0x40, 0x7b};
|
||||
const uint8_t v5_check2[] = {0x7a, 0x1b, 0xf5, 0xdb, 0x5e, 0x77, 0x5e, 0x9b,
|
||||
0x80, 0x6f, 0x0f, 0x55, 0x95, 0x58, 0xc9, 0xca};
|
||||
|
||||
/*
|
||||
* Length of the test strings without zero terminator.
|
||||
* Python doesn't feed the zero terminator in the uuid generator, so we test
|
||||
* without the zero terminator here too.
|
||||
*/
|
||||
#define RIOTOS_ORG_LEN (sizeof(riotos_org) -1)
|
||||
#define TEST_STR_LEN (sizeof(test_str) -1)
|
||||
|
||||
|
||||
void test_uuid_v3(void)
|
||||
{
|
||||
uuid_t uuid, uuid_next;
|
||||
uuid_v3(&uuid, &uuid_namespace_dns,
|
||||
(uint8_t*)riotos_org, RIOTOS_ORG_LEN);
|
||||
uuid_v3(&uuid_next, &uuid,
|
||||
(uint8_t*)test_str, TEST_STR_LEN);
|
||||
|
||||
TEST_ASSERT(uuid_equal(&uuid, (uuid_t*)v3_check1));
|
||||
TEST_ASSERT(uuid_equal(&uuid_next, (uuid_t*)v3_check2));
|
||||
TEST_ASSERT_EQUAL_INT(uuid_version(&uuid), UUID_V3);
|
||||
TEST_ASSERT_EQUAL_INT(uuid_version(&uuid_next), UUID_V3);
|
||||
}
|
||||
|
||||
void test_uuid_v4(void)
|
||||
{
|
||||
uuid_t uuid;
|
||||
uuid_v4(&uuid);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(uuid_version(&uuid), 4);
|
||||
}
|
||||
|
||||
void test_uuid_v5(void)
|
||||
{
|
||||
uuid_t uuid, uuid_next;
|
||||
uuid_v5(&uuid, &uuid_namespace_dns,
|
||||
(uint8_t*)riotos_org, RIOTOS_ORG_LEN);
|
||||
uuid_v5(&uuid_next, &uuid,
|
||||
(uint8_t*)test_str, TEST_STR_LEN);
|
||||
|
||||
TEST_ASSERT(uuid_equal(&uuid, (uuid_t*)v5_check1));
|
||||
TEST_ASSERT(uuid_equal(&uuid_next, (uuid_t*)v5_check2));
|
||||
TEST_ASSERT_EQUAL_INT(uuid_version(&uuid), UUID_V5);
|
||||
TEST_ASSERT_EQUAL_INT(uuid_version(&uuid_next), UUID_V5);
|
||||
}
|
||||
|
||||
Test *tests_uuid_all(void)
|
||||
{
|
||||
EMB_UNIT_TESTFIXTURES(fixtures) {
|
||||
new_TestFixture(test_uuid_v3),
|
||||
new_TestFixture(test_uuid_v4),
|
||||
new_TestFixture(test_uuid_v5),
|
||||
};
|
||||
|
||||
EMB_UNIT_TESTCALLER(uuid_tests, NULL, NULL, fixtures);
|
||||
return (Test *)&uuid_tests;
|
||||
}
|
||||
|
||||
void tests_uuid(void)
|
||||
{
|
||||
TESTS_RUN(tests_uuid_all());
|
||||
}
|
37
tests/unittests/tests-uuid/tests-uuid.h
Normal file
37
tests/unittests/tests-uuid/tests-uuid.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup unittests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Unittests for the uuid module
|
||||
*
|
||||
*/
|
||||
#ifndef TESTS_UUID_H
|
||||
#define TESTS_UUID_H
|
||||
|
||||
#include "embUnit/embUnit.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The entry point of this test suite.
|
||||
*/
|
||||
void tests_uuid(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* TESTS_UUID_H */
|
||||
/** @} */
|
Loading…
Reference in New Issue
Block a user