hex string UPDATE canonize yang-types hex-strings
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8ed9695..762d259 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -103,6 +103,7 @@
     src/plugins_types/ipv4_prefix.c
     src/plugins_types/ipv6_prefix.c
     src/plugins_types/date_and_time.c
+    src/plugins_types/hex_string.c
     src/plugins_types/xpath1.0.c
     src/plugins_types/node_instanceid.c)
 
diff --git a/src/plugins.c b/src/plugins.c
index d62da1c..011e464 100644
--- a/src/plugins.c
+++ b/src/plugins.c
@@ -70,6 +70,7 @@
  * ietf-yang-types
  */
 extern const struct lyplg_type_record plugins_date_and_time[];
+extern const struct lyplg_type_record plugins_hex_string[];
 extern const struct lyplg_type_record plugins_xpath10[];
 
 /*
@@ -475,6 +476,7 @@
 
     /* ietf-yang-types */
     LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_date_and_time), error);
+    LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_hex_string), error);
     LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_xpath10), error);
 
     /* ietf-netconf-acm */
diff --git a/src/plugins_types/hex_string.c b/src/plugins_types/hex_string.c
new file mode 100644
index 0000000..f72ce66
--- /dev/null
+++ b/src/plugins_types/hex_string.c
@@ -0,0 +1,171 @@
+/**
+ * @file hex_string.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Built-in hex-string and associated types plugin.
+ *
+ * Copyright (c) 2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "plugins_types.h"
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesHexString phys-address, mac-address, hex-string, uuid (ietf-yang-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string itself |
+ */
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_hex_string(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+        uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+        struct ly_err_item **err)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+    uint32_t i;
+
+    /* init storage */
+    memset(storage, 0, sizeof *storage);
+    storage->realtype = type;
+
+    /* check hints */
+    ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* length restriction of the string */
+    if (type_str->length) {
+        /* value_len is in bytes, but we need number of characters here */
+        ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    /* pattern restrictions */
+    ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* make a copy, it is needed for canonization */
+    if ((format != LY_VALUE_CANON) && !(options & LYPLG_TYPE_STORE_DYNAMIC)) {
+        value = strndup(value, value_len);
+        LY_CHECK_ERR_GOTO(!value, ret = LY_EMEM, cleanup);
+        options |= LYPLG_TYPE_STORE_DYNAMIC;
+    }
+
+    /* store canonical value */
+    if (format != LY_VALUE_CANON) {
+        /* make lowercase and store, the value must be dynamic */
+        for (i = 0; i < value_len; ++i) {
+            ((char *)value)[i] = tolower(((char *)value)[i]);
+        }
+
+        ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+        options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+        LY_CHECK_GOTO(ret, cleanup);
+    } else {
+        /* store directly */
+        ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+cleanup:
+    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+        free((void *)value);
+    }
+
+    if (ret) {
+        lyplg_type_free_simple(ctx, storage);
+    }
+    return ret;
+}
+
+/**
+ * @brief Plugin information for string type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ *   LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_hex_string[] = {
+    {
+        .module = "ietf-yang-types",
+        .revision = "2013-07-15",
+        .name = "phys-address",
+
+        .plugin.id = "libyang 2 - hex-string, version 1",
+        .plugin.store = lyplg_type_store_hex_string,
+        .plugin.validate = NULL,
+        .plugin.compare = lyplg_type_compare_simple,
+        .plugin.sort = NULL,
+        .plugin.print = lyplg_type_print_simple,
+        .plugin.duplicate = lyplg_type_dup_simple,
+        .plugin.free = lyplg_type_free_simple,
+        .plugin.lyb_data_len = -1,
+    },
+    {
+        .module = "ietf-yang-types",
+        .revision = "2013-07-15",
+        .name = "mac-address",
+
+        .plugin.id = "libyang 2 - hex-string, version 1",
+        .plugin.store = lyplg_type_store_hex_string,
+        .plugin.validate = NULL,
+        .plugin.compare = lyplg_type_compare_simple,
+        .plugin.sort = NULL,
+        .plugin.print = lyplg_type_print_simple,
+        .plugin.duplicate = lyplg_type_dup_simple,
+        .plugin.free = lyplg_type_free_simple,
+        .plugin.lyb_data_len = -1,
+    },
+    {
+        .module = "ietf-yang-types",
+        .revision = "2013-07-15",
+        .name = "hex-string",
+
+        .plugin.id = "libyang 2 - hex-string, version 1",
+        .plugin.store = lyplg_type_store_hex_string,
+        .plugin.validate = NULL,
+        .plugin.compare = lyplg_type_compare_simple,
+        .plugin.sort = NULL,
+        .plugin.print = lyplg_type_print_simple,
+        .plugin.duplicate = lyplg_type_dup_simple,
+        .plugin.free = lyplg_type_free_simple,
+        .plugin.lyb_data_len = -1,
+    },
+    {
+        .module = "ietf-yang-types",
+        .revision = "2013-07-15",
+        .name = "uuid",
+
+        .plugin.id = "libyang 2 - hex-string, version 1",
+        .plugin.store = lyplg_type_store_hex_string,
+        .plugin.validate = NULL,
+        .plugin.compare = lyplg_type_compare_simple,
+        .plugin.sort = NULL,
+        .plugin.print = lyplg_type_print_simple,
+        .plugin.duplicate = lyplg_type_dup_simple,
+        .plugin.free = lyplg_type_free_simple,
+        .plugin.lyb_data_len = -1,
+    },
+    {0}
+};
diff --git a/tests/utests/types/yang_types.c b/tests/utests/types/yang_types.c
index f0fe252..60649ef 100644
--- a/tests/utests/types/yang_types.c
+++ b/tests/utests/types/yang_types.c
@@ -84,7 +84,9 @@
     /* xml test */
     schema = MODULE_CREATE_YANG("a",
             "leaf l {type yang:date-and-time;}"
-            "leaf l2 {type yang:xpath1.0;}");
+            "leaf l21 {type yang:hex-string;}"
+            "leaf l22 {type yang:uuid;}"
+            "leaf l3 {type yang:xpath1.0;}");
     UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
     schema = MODULE_CREATE_YANG("b",
             "");
@@ -114,24 +116,28 @@
             "\"\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[\\+\\-]\\d{2}:\\d{2})\".",
             "Schema location \"/a:l\", line number 1.");
 
+    /* hex-string */
+    TEST_SUCCESS_XML("a", "l21", "DB:BA:12:54:fa", STRING, "db:ba:12:54:fa");
+    TEST_SUCCESS_XML("a", "l22", "f81D4fAE-7dec-11d0-A765-00a0c91E6BF6", STRING, "f81d4fae-7dec-11d0-a765-00a0c91e6bf6");
+
     /* xpath1.0 */
-    TEST_SUCCESS_XML("a\" xmlns:aa=\"urn:tests:a", "l2", "/aa:l2[. = '4']", STRING, "/a:l2[.='4']");
+    TEST_SUCCESS_XML("a\" xmlns:aa=\"urn:tests:a", "l3", "/aa:l3[. = '4']", STRING, "/a:l3[.='4']");
     TEST_SUCCESS_XML("a\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
-            "xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores", "l2",
+            "xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores", "l3",
             "/yl:yang-library/yl:datastore/yl:name = 'ds:running'", STRING,
             "/ietf-yang-library:yang-library/datastore/name='ietf-datastores:running'");
-    TEST_SUCCESS_XML("a\" xmlns:a1=\"urn:tests:a\" xmlns:a2=\"urn:tests:a\" xmlns:bb=\"urn:tests:b", "l2",
+    TEST_SUCCESS_XML("a\" xmlns:a1=\"urn:tests:a\" xmlns:a2=\"urn:tests:a\" xmlns:bb=\"urn:tests:b", "l3",
             "/a1:node1/a2:node2[a1:node3/bb:node4]/bb:node5 | bb:node6 and (bb:node7)", STRING,
             "/a:node1/node2[node3/b:node4]/b:node5 | b:node6 and (b:node7)");
-    TEST_SUCCESS_XML("a", "l2", "/l2[. = '4']", STRING, "/l2[.='4']");
+    TEST_SUCCESS_XML("a", "l3", "/l3[. = '4']", STRING, "/l3[.='4']");
 
-    TEST_ERROR_XML("a", "l2", "/a:l2[. = '4']");
-    CHECK_LOG_CTX("Failed to resolve prefix \"a\".", "Schema location \"/a:l2\", line number 1.");
-    TEST_ERROR_XML("a\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library", "l2",
+    TEST_ERROR_XML("a", "l3", "/a:l3[. = '4']");
+    CHECK_LOG_CTX("Failed to resolve prefix \"a\".", "Schema location \"/a:l3\", line number 1.");
+    TEST_ERROR_XML("a\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library", "l3",
             "/yl:yang-library/yl:datastore/yl::name");
-    CHECK_LOG_CTX("Storing value failed.", "Schema location \"/a:l2\", line number 1.");
+    CHECK_LOG_CTX("Storing value failed.", "Schema location \"/a:l3\", line number 1.");
     CHECK_LOG_CTX("Invalid character 'y'[31] of expression '/yl:yang-library/yl:datastore/yl::name'.",
-            "Schema location \"/a:l2\", line number 1.");
+            "Schema location \"/a:l3\", line number 1.");
 }
 
 static void