summaryrefslogtreecommitdiffstats
path: root/src/util/hex_code.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/hex_code.c')
-rw-r--r--src/util/hex_code.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/src/util/hex_code.c b/src/util/hex_code.c
new file mode 100644
index 0000000..3dfcb98
--- /dev/null
+++ b/src/util/hex_code.c
@@ -0,0 +1,243 @@
+/*++
+/* NAME
+/* hex_code 3
+/* SUMMARY
+/* encode/decode data, hexadecimal style
+/* SYNOPSIS
+/* #include <hex_code.h>
+/*
+/* VSTRING *hex_encode(result, in, len)
+/* VSTRING *result;
+/* const char *in;
+/* ssize_t len;
+/*
+/* VSTRING *hex_decode(result, in, len)
+/* VSTRING *result;
+/* const char *in;
+/* ssize_t len;
+/*
+/* VSTRING *hex_encode_opt(result, in, len, flags)
+/* VSTRING *result;
+/* const char *in;
+/* ssize_t len;
+/* int flags;
+/*
+/* VSTRING *hex_decode_opt(result, in, len, flags)
+/* VSTRING *result;
+/* const char *in;
+/* ssize_t len;
+/* int flags;
+/* DESCRIPTION
+/* hex_encode() takes a block of len bytes and encodes it as one
+/* upper-case null-terminated string. The result value is
+/* the result argument.
+/*
+/* hex_decode() performs the opposite transformation on
+/* lower-case, upper-case or mixed-case input. The result
+/* value is the result argument. The result is null terminated,
+/* whether or not that makes sense.
+/*
+/* hex_encode_opt() enables extended functionality as controlled
+/* with \fIflags\fR.
+/* .IP HEX_ENCODE_FLAG_NONE
+/* The default: a self-documenting flag that enables no
+/* functionality.
+/* .IP HEX_ENCODE_FLAG_USE_COLON
+/* Inserts one ":" between bytes.
+/* .PP
+/* hex_decode_opt() enables extended functionality as controlled
+/* with \fIflags\fR.
+/* .IP HEX_DECODE_FLAG_NONE
+/* The default: a self-documenting flag that enables no
+/* functionality.
+/* .IP HEX_DECODE_FLAG_ALLOW_COLON
+/* Allows, but does not require, one ":" between bytes.
+/* DIAGNOSTICS
+/* hex_decode() returns a null pointer when the input contains
+/* characters not in the hexadecimal alphabet.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <ctype.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <hex_code.h>
+
+/* Application-specific. */
+
+static const unsigned char hex_chars[] = "0123456789ABCDEF";
+
+#define UCHAR_PTR(x) ((const unsigned char *)(x))
+
+/* hex_encode - ABI compatibility */
+
+#undef hex_encode
+
+VSTRING *hex_encode(VSTRING *result, const char *in, ssize_t len)
+{
+ return (hex_encode_opt(result, in, len, HEX_ENCODE_FLAG_NONE));
+}
+
+/* hex_encode_opt - raw data to encoded */
+
+VSTRING *hex_encode_opt(VSTRING *result, const char *in, ssize_t len, int flags)
+{
+ const unsigned char *cp;
+ int ch;
+ ssize_t count;
+
+ VSTRING_RESET(result);
+ for (cp = UCHAR_PTR(in), count = len; count > 0; count--, cp++) {
+ ch = *cp;
+ VSTRING_ADDCH(result, hex_chars[(ch >> 4) & 0xf]);
+ VSTRING_ADDCH(result, hex_chars[ch & 0xf]);
+ if ((flags & HEX_ENCODE_FLAG_USE_COLON) && count > 1)
+ VSTRING_ADDCH(result, ':');
+ }
+ VSTRING_TERMINATE(result);
+ return (result);
+}
+
+/* hex_decode - ABI compatibility wrapper */
+
+#undef hex_decode
+
+VSTRING *hex_decode(VSTRING *result, const char *in, ssize_t len)
+{
+ return (hex_decode_opt(result, in, len, HEX_DECODE_FLAG_NONE));
+}
+
+/* hex_decode_opt - encoded data to raw */
+
+VSTRING *hex_decode_opt(VSTRING *result, const char *in, ssize_t len, int flags)
+{
+ const unsigned char *cp;
+ ssize_t count;
+ unsigned int hex;
+ unsigned int bin;
+
+ VSTRING_RESET(result);
+ for (cp = UCHAR_PTR(in), count = len; count > 0; cp += 2, count -= 2) {
+ if (count < 2)
+ return (0);
+ hex = cp[0];
+ if (hex >= '0' && hex <= '9')
+ bin = (hex - '0') << 4;
+ else if (hex >= 'A' && hex <= 'F')
+ bin = (hex - 'A' + 10) << 4;
+ else if (hex >= 'a' && hex <= 'f')
+ bin = (hex - 'a' + 10) << 4;
+ else
+ return (0);
+ hex = cp[1];
+ if (hex >= '0' && hex <= '9')
+ bin |= (hex - '0');
+ else if (hex >= 'A' && hex <= 'F')
+ bin |= (hex - 'A' + 10);
+ else if (hex >= 'a' && hex <= 'f')
+ bin |= (hex - 'a' + 10);
+ else
+ return (0);
+ VSTRING_ADDCH(result, bin);
+
+ /*
+ * Support *colon-separated* input (no leading or trailing colons).
+ * After decoding "xx", skip a possible ':' preceding "yy" in
+ * "xx:yy".
+ */
+ if ((flags & HEX_DECODE_FLAG_ALLOW_COLON)
+ && count > 4 && cp[2] == ':') {
+ ++cp;
+ --count;
+ }
+ }
+ VSTRING_TERMINATE(result);
+ return (result);
+}
+
+#ifdef TEST
+#include <argv.h>
+
+ /*
+ * Proof-of-concept test program: convert to hexadecimal and back.
+ */
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+int main(int unused_argc, char **unused_argv)
+{
+ VSTRING *b1 = vstring_alloc(1);
+ VSTRING *b2 = vstring_alloc(1);
+ char *test = "this is a test";
+ ARGV *argv;
+
+#define DECODE(b,x,l) { \
+ if (hex_decode((b),(x),(l)) == 0) \
+ msg_panic("bad hex: %s", (x)); \
+ }
+#define VERIFY(b,t) { \
+ if (strcmp((b), (t)) != 0) \
+ msg_panic("bad test: %s", (b)); \
+ }
+
+ hex_encode(b1, test, strlen(test));
+ DECODE(b2, STR(b1), LEN(b1));
+ VERIFY(STR(b2), test);
+
+ hex_encode(b1, test, strlen(test));
+ hex_encode(b2, STR(b1), LEN(b1));
+ hex_encode(b1, STR(b2), LEN(b2));
+ DECODE(b2, STR(b1), LEN(b1));
+ DECODE(b1, STR(b2), LEN(b2));
+ DECODE(b2, STR(b1), LEN(b1));
+ VERIFY(STR(b2), test);
+
+ hex_encode(b1, test, strlen(test));
+ hex_encode(b2, STR(b1), LEN(b1));
+ hex_encode(b1, STR(b2), LEN(b2));
+ hex_encode(b2, STR(b1), LEN(b1));
+ hex_encode(b1, STR(b2), LEN(b2));
+ DECODE(b2, STR(b1), LEN(b1));
+ DECODE(b1, STR(b2), LEN(b2));
+ DECODE(b2, STR(b1), LEN(b1));
+ DECODE(b1, STR(b2), LEN(b2));
+ DECODE(b2, STR(b1), LEN(b1));
+ VERIFY(STR(b2), test);
+
+ hex_encode_opt(b1, test, strlen(test), HEX_ENCODE_FLAG_USE_COLON);
+ argv = argv_split(STR(b1), ":");
+ if (argv->argc != strlen(test))
+ msg_panic("HEX_ENCODE_FLAG_USE_COLON");
+ if (hex_decode_opt(b2, STR(b1), LEN(b1), HEX_DECODE_FLAG_ALLOW_COLON) == 0)
+ msg_panic("HEX_DECODE_FLAG_ALLOW_COLON");
+ VERIFY(STR(b2), test);
+ argv_free(argv);
+
+ vstring_free(b1);
+ vstring_free(b2);
+ return (0);
+}
+
+#endif