krb5 commit: Add libkrb5support hex functions and tests

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/720dea558da0062d3cea4385327161e62cf09a5e
commit 720dea558da0062d3cea4385327161e62cf09a5e
Author: Greg Hudson <[email protected]>
Date:   Mon Feb 19 00:51:44 2018 -0500

    Add libkrb5support hex functions and tests

 .gitignore                                    |    1 +
 src/include/k5-hex.h                          |   53 ++++++++
 src/util/support/Makefile.in                  |   15 ++-
 src/util/support/deps                         |    6 +
 src/util/support/hex.c                        |  116 +++++++++++++++++
 src/util/support/libkrb5support-fixed.exports |    2 +
 src/util/support/t_hex.c                      |  169 +++++++++++++++++++++++++
 7 files changed, 359 insertions(+), 3 deletions(-)

diff --git a/.gitignore b/.gitignore
index c13b5e3..91c8b94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -517,6 +517,7 @@ local.properties
 
 /src/util/support/libkrb5support.exports
 /src/util/support/t_base64
+/src/util/support/t_hex
 /src/util/support/t_json
 /src/util/support/t_k5buf
 /src/util/support/t_path
diff --git a/src/include/k5-hex.h b/src/include/k5-hex.h
new file mode 100644
index 0000000..75bd2cb
--- /dev/null
+++ b/src/include/k5-hex.h
@@ -0,0 +1,53 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* include/k5-hex.h - libkrb5support hex encoding/decoding declarations */
+/*
+ * Copyright (C) 2018 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef K5_HEX_H
+#define K5_HEX_H
+
+#include "k5-platform.h"
+
+/*
+ * Encode len bytes in hex, placing the result in allocated storage in
+ * *hex_out.  Use uppercase hex digits if uppercase is non-zero.  Return 0 on
+ * success, ENOMEM on error.
+ */
+int k5_hex_encode(const void *bytes, size_t len, int uppercase,
+                  char **hex_out);
+
+/*
+ * Decode hex bytes, placing the result in allocated storage in *bytes_out and
+ * *len_out.  Null-terminate the result (primarily for decoding passwords in
+ * libkdb_ldap).  Return 0 on success, ENOMEM or EINVAL on error.
+ */
+int k5_hex_decode(const char *hex, uint8_t **bytes_out, size_t *len_out);
+
+#endif /* K5_HEX_H */
diff --git a/src/util/support/Makefile.in b/src/util/support/Makefile.in
index 0bf0b7a..7393826 100644
--- a/src/util/support/Makefile.in
+++ b/src/util/support/Makefile.in
@@ -81,6 +81,7 @@ STLIBOBJS= \
 	path.o \
 	base64.o \
 	json.o \
+	hex.o \
 	bcmp.o \
 	strerror_r.o \
 	$(GETTIMEOFDAY_ST_OBJ) \
@@ -106,6 +107,7 @@ LIBOBJS= \
 	$(OUTPRE)path.$(OBJEXT) \
 	$(OUTPRE)base64.$(OBJEXT) \
 	$(OUTPRE)json.$(OBJEXT) \
+	$(OUTPRE)hex.$(OBJEXT) \
 	$(OUTPRE)bcmp.$(OBJEXT) \
 	$(OUTPRE)strerror_r.$(OBJEXT) \
 	$(GETTIMEOFDAY_OBJ) \
@@ -136,10 +138,12 @@ SRCS=\
 	$(srcdir)/t_unal.c \
 	$(srcdir)/t_path.c \
 	$(srcdir)/t_json.c \
+	$(srcdir)/t_hex.c \
 	$(srcdir)/zap.c \
 	$(srcdir)/path.c \
 	$(srcdir)/base64.c \
 	$(srcdir)/json.c \
+	$(srcdir)/hex.c \
 	$(srcdir)/bcmp.c \
 	$(srcdir)/strerror_r.c \
 	$(srcdir)/t_utf8.c \
@@ -215,6 +219,9 @@ T_JSON_OBJS= t_json.o json.o base64.o k5buf.o $(PRINTF_ST_OBJ)
 t_json: $(T_JSON_OBJS)
 	$(CC_LINK) -o $@ $(T_JSON_OBJS)
 
+t_hex: t_hex.o hex.o
+	$(CC_LINK) -o $@ t_hex.o hex.o
+
 t_unal: t_unal.o
 	$(CC_LINK) -o t_unal t_unal.o
 
@@ -226,7 +233,8 @@ T_UTF16_OBJS= t_utf16.o utf8_conv.o utf8.o k5buf.o $(PRINTF_ST_OBJ)
 t_utf16: $(T_UTF16_OBJS)
 	$(CC_LINK) -o $@ $(T_UTF16_OBJS)
 
-TEST_PROGS= t_k5buf t_path t_path_win t_base64 t_json t_unal t_utf8 t_utf16
+TEST_PROGS= t_k5buf t_path t_path_win t_base64 t_json t_hex t_unal t_utf8 \
+	t_utf16
 
 check-unix: $(TEST_PROGS)
 	./t_k5buf
@@ -234,6 +242,7 @@ check-unix: $(TEST_PROGS)
 	./t_path_win
 	./t_base64
 	./t_json
+	./t_hex
 	./t_unal
 	./t_utf8
 	./t_utf16
@@ -241,8 +250,8 @@ check-unix: $(TEST_PROGS)
 clean:
 	$(RM) t_k5buf.o t_k5buf t_unal.o t_unal path_win.o path_win
 	$(RM) t_path_win.o t_path_win t_path.o t_path t_base64.o t_base64
-	$(RM) t_json.o t_json libkrb5support.exports t_utf8.o t_utf8
-	$(RM) t_utf16.o t_utf16
+	$(RM) t_json.o t_json t_hex.o t_hex libkrb5support.exports
+	$(RM) t_utf8.o t_utf8 t_utf16.o t_utf16
 
 @lib_frag@
 @libobj_frag@
diff --git a/src/util/support/deps b/src/util/support/deps
index 34d8a88..80e9a1c 100644
--- a/src/util/support/deps
+++ b/src/util/support/deps
@@ -63,6 +63,9 @@ t_path.so t_path.po $(OUTPRE)t_path.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   t_path.c
 t_json.so t_json.po $(OUTPRE)t_json.$(OBJEXT): $(top_srcdir)/include/k5-json.h \
   t_json.c
+t_hex.so t_hex.po $(OUTPRE)t_hex.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(top_srcdir)/include/k5-hex.h $(top_srcdir)/include/k5-platform.h \
+  $(top_srcdir)/include/k5-thread.h t_hex.c
 zap.so zap.po $(OUTPRE)zap.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
   zap.c
@@ -76,6 +79,9 @@ json.so json.po $(OUTPRE)json.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(top_srcdir)/include/k5-base64.h $(top_srcdir)/include/k5-buf.h \
   $(top_srcdir)/include/k5-json.h $(top_srcdir)/include/k5-platform.h \
   $(top_srcdir)/include/k5-thread.h json.c
+hex.so hex.po $(OUTPRE)hex.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(top_srcdir)/include/k5-hex.h $(top_srcdir)/include/k5-platform.h \
+  $(top_srcdir)/include/k5-thread.h hex.c
 bcmp.so bcmp.po $(OUTPRE)bcmp.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
   bcmp.c
diff --git a/src/util/support/hex.c b/src/util/support/hex.c
new file mode 100644
index 0000000..4407ff9
--- /dev/null
+++ b/src/util/support/hex.c
@@ -0,0 +1,116 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/hex.c - hex encoding/decoding implementation */
+/*
+ * Copyright (C) 2018 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <k5-platform.h>
+#include <k5-hex.h>
+#include <ctype.h>
+
+static inline char
+hex_digit(uint8_t bval, int uppercase)
+{
+    assert(bval >= 0 && bval <= 0xF);
+    if (bval < 10)
+        return '0' + bval;
+    else if (uppercase)
+        return 'A' + (bval - 10);
+    else
+        return 'a' + (bval - 10);
+}
+
+int
+k5_hex_encode(const void *bytes, size_t len, int uppercase, char **hex_out)
+{
+    size_t i;
+    const uint8_t *p = bytes;
+    char *hex;
+
+    *hex_out = NULL;
+
+    hex = malloc(len * 2 + 1);
+    if (hex == NULL)
+        return ENOMEM;
+
+    for (i = 0; i < len; i++) {
+        hex[i * 2] = hex_digit(p[i] >> 4, uppercase);
+        hex[i * 2 + 1] = hex_digit(p[i] & 0xF, uppercase);
+    }
+    hex[len * 2] = '\0';
+
+    *hex_out = hex;
+    return 0;
+}
+
+/* Decode a hex digit.  Return 0-15 on success, -1 on invalid input. */
+static inline int
+decode_hexchar(unsigned char c)
+{
+    if (isdigit(c))
+        return c - '0';
+    if (c >= 'A' && c <= 'F')
+        return c - 'A' + 10;
+    if (c >= 'a' && c <= 'f')
+        return c - 'a' + 10;
+    return -1;
+}
+
+int
+k5_hex_decode(const char *hex, uint8_t **bytes_out, size_t *len_out)
+{
+    size_t hexlen, i;
+    int h1, h2;
+    uint8_t *bytes;
+
+    *bytes_out = NULL;
+    *len_out = 0;
+
+    hexlen = strlen(hex);
+    if (hexlen % 2 != 0)
+        return EINVAL;
+    bytes = malloc(hexlen / 2 + 1);
+    if (bytes == NULL)
+        return ENOMEM;
+
+    for (i = 0; i < hexlen / 2; i++) {
+        h1 = decode_hexchar(hex[i * 2]);
+        h2 = decode_hexchar(hex[i * 2 + 1]);
+        if (h1 == -1 || h2 == -1) {
+            free(bytes);
+            return EINVAL;
+        }
+        bytes[i] = h1 * 16 + h2;
+    }
+    bytes[i] = 0;
+
+    *bytes_out = bytes;
+    *len_out = hexlen / 2;
+    return 0;
+}
diff --git a/src/util/support/libkrb5support-fixed.exports b/src/util/support/libkrb5support-fixed.exports
index 12dab38..cb9bf08 100644
--- a/src/util/support/libkrb5support-fixed.exports
+++ b/src/util/support/libkrb5support-fixed.exports
@@ -17,6 +17,8 @@ k5_get_error
 k5_free_error
 k5_clear_error
 k5_set_error_info_callout_fn
+k5_hex_decode
+k5_hex_encode
 k5_json_array_add
 k5_json_array_create
 k5_json_array_fmt
diff --git a/src/util/support/t_hex.c b/src/util/support/t_hex.c
new file mode 100644
index 0000000..a586a1b
--- /dev/null
+++ b/src/util/support/t_hex.c
@@ -0,0 +1,169 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/t_hex.c - Test hex encoding and decoding */
+/*
+ * Copyright (C) 2018 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <k5-platform.h>
+#include <k5-hex.h>
+
+struct {
+    const char *hex;
+    const char *binary;
+    size_t binary_len;
+    int uppercase;
+} tests[] = {
+    /* Invalid hex strings */
+    { "1" },
+    { "123" },
+    { "0/" },
+    { "/0" },
+    { "0:" },
+    { ":0" },
+    { "0@" },
+    { "@0" },
+    { "0G" },
+    { "G0" },
+    { "0`" },
+    { "`0" },
+    { "0g" },
+    { "g0" },
+    { " 00 " },
+    { "0\x01" },
+
+    { "", "", 0 },
+    { "00", "\x00", 1 },
+    { "01", "\x01", 1 },
+    { "10", "\x10", 1 },
+    { "01ff", "\x01\xFF", 2 },
+    { "A0B0C0", "\xA0\xB0\xC0", 3, 1 },
+    { "1a2b3c4d5e6f", "\x1A\x2B\x3C\x4D\x5E\x6F", 6 },
+    { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+      "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+      "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 32 },
+
+    /* All byte values, lowercase */
+    { "0001020304050607", "\x00\x01\x02\x03\x04\x05\x06\x07", 8 },
+    { "08090a0b0c0d0e0f", "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 8 },
+    { "1011121314151617", "\x10\x11\x12\x13\x14\x15\x16\x17", 8 },
+    { "18191a1b1c1d1e1f", "\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", 8 },
+    { "2021222324252627", "\x20\x21\x22\x23\x24\x25\x26\x27", 8 },
+    { "28292a2b2c2d2e2f", "\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F", 8 },
+    { "3031323334353637", "\x30\x31\x32\x33\x34\x35\x36\x37", 8 },
+    { "38393a3b3c3d3e3f", "\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F", 8 },
+    { "4041424344454647", "\x40\x41\x42\x43\x44\x45\x46\x47", 8 },
+    { "48494a4b4c4d4e4f", "\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F", 8 },
+    { "5051525354555657", "\x50\x51\x52\x53\x54\x55\x56\x57", 8 },
+    { "58595a5b5c5d5e5f", "\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F", 8 },
+    { "6061626364656667", "\x60\x61\x62\x63\x64\x65\x66\x67", 8 },
+    { "68696a6b6c6d6e6f", "\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F", 8 },
+    { "7071727374757677", "\x70\x71\x72\x73\x74\x75\x76\x77", 8 },
+    { "78797a7b7c7d7e7f", "\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F", 8 },
+    { "8081828384858687", "\x80\x81\x82\x83\x84\x85\x86\x87", 8 },
+    { "88898a8b8c8d8e8f", "\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F", 8 },
+    { "9091929394959697", "\x90\x91\x92\x93\x94\x95\x96\x97", 8 },
+    { "98999a9b9c9d9e9f", "\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F", 8 },
+    { "a0a1a2a3a4a5a6a7", "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7", 8 },
+    { "a8a9aaabacadaeaf", "\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF", 8 },
+    { "b0b1b2b3b4b5b6b7", "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7", 8 },
+    { "b8b9babbbcbdbebf", "\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF", 8 },
+    { "c0c1c2c3c4c5c6c7", "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7", 8 },
+    { "c8c9cacbcccdcecf", "\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF", 8 },
+    { "d0d1d2d3d4d5d6d7", "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7", 8 },
+    { "d8d9dadbdcdddedf", "\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF", 8 },
+    { "e0e1e2e3e4e5e6e7", "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7", 8 },
+    { "e8e9eaebecedeeef", "\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF", 8 },
+    { "f0f1f2f3f4f5f6f7", "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7", 8 },
+    { "f8f9fafbfcfdfeff", "\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF", 8 },
+
+    /* All byte values, uppercase */
+    { "0001020304050607", "\x00\x01\x02\x03\x04\x05\x06\x07", 8, 1 },
+    { "08090A0B0C0D0E0F", "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 8, 1 },
+    { "1011121314151617", "\x10\x11\x12\x13\x14\x15\x16\x17", 8, 1 },
+    { "18191A1B1C1D1E1F", "\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", 8, 1 },
+    { "2021222324252627", "\x20\x21\x22\x23\x24\x25\x26\x27", 8, 1 },
+    { "28292A2B2C2D2E2F", "\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F", 8, 1 },
+    { "3031323334353637", "\x30\x31\x32\x33\x34\x35\x36\x37", 8, 1 },
+    { "38393A3B3C3D3E3F", "\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F", 8, 1 },
+    { "4041424344454647", "\x40\x41\x42\x43\x44\x45\x46\x47", 8, 1 },
+    { "48494A4B4C4D4E4F", "\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F", 8, 1 },
+    { "5051525354555657", "\x50\x51\x52\x53\x54\x55\x56\x57", 8, 1 },
+    { "58595A5B5C5D5E5F", "\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F", 8, 1 },
+    { "6061626364656667", "\x60\x61\x62\x63\x64\x65\x66\x67", 8, 1 },
+    { "68696A6B6C6D6E6F", "\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F", 8, 1 },
+    { "7071727374757677", "\x70\x71\x72\x73\x74\x75\x76\x77", 8, 1 },
+    { "78797A7B7C7D7E7F", "\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F", 8, 1 },
+    { "8081828384858687", "\x80\x81\x82\x83\x84\x85\x86\x87", 8, 1 },
+    { "88898A8B8C8D8E8F", "\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F", 8, 1 },
+    { "9091929394959697", "\x90\x91\x92\x93\x94\x95\x96\x97", 8, 1 },
+    { "98999A9B9C9D9E9F", "\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F", 8, 1 },
+    { "A0A1A2A3A4A5A6A7", "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7", 8, 1 },
+    { "A8A9AAABACADAEAF", "\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF", 8, 1 },
+    { "B0B1B2B3B4B5B6B7", "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7", 8, 1 },
+    { "B8B9BABBBCBDBEBF", "\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF", 8, 1 },
+    { "C0C1C2C3C4C5C6C7", "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7", 8, 1 },
+    { "C8C9CACBCCCDCECF", "\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF", 8, 1 },
+    { "D0D1D2D3D4D5D6D7", "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7", 8, 1 },
+    { "D8D9DADBDCDDDEDF", "\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF", 8, 1 },
+    { "E0E1E2E3E4E5E6E7", "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7", 8, 1 },
+    { "E8E9EAEBECEDEEEF", "\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF", 8, 1 },
+    { "F0F1F2F3F4F5F6F7", "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7", 8, 1 },
+    { "F8F9FAFBFCFDFEFF", "\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF", 8, 1 },
+};
+
+int main()
+{
+    size_t i;
+    char *hex;
+    int ret;
+    uint8_t *bytes;
+    size_t len;
+
+    for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) {
+        if (tests[i].binary == NULL) {
+            ret = k5_hex_decode(tests[i].hex, &bytes, &len);
+            assert(ret == EINVAL && bytes == NULL && len == 0);
+            continue;
+        }
+
+        ret = k5_hex_decode(tests[i].hex, &bytes, &len);
+        assert(ret == 0);
+        assert(len == tests[i].binary_len);
+        assert(memcmp(bytes, tests[i].binary, len) == 0);
+        assert(bytes[len] == 0);
+        free(bytes);
+
+        ret = k5_hex_encode((uint8_t *)tests[i].binary, tests[i].binary_len,
+                            tests[i].uppercase, &hex);
+        assert(ret == 0);
+        assert(strcmp(tests[i].hex, hex) == 0);
+        free(hex);
+    }
+    return 0;
+}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.