1/*	$NetBSD: hex_code.c,v 1.3 2022/10/08 16:12:50 christos Exp $	*/
2
3/*++
4/* NAME
5/*	hex_code 3
6/* SUMMARY
7/*	encode/decode data, hexadecimal style
8/* SYNOPSIS
9/*	#include <hex_code.h>
10/*
11/*	VSTRING	*hex_encode(result, in, len)
12/*	VSTRING	*result;
13/*	const char *in;
14/*	ssize_t	len;
15/*
16/*	VSTRING	*hex_decode(result, in, len)
17/*	VSTRING	*result;
18/*	const char *in;
19/*	ssize_t	len;
20/*
21/*	VSTRING	*hex_encode_opt(result, in, len, flags)
22/*	VSTRING	*result;
23/*	const char *in;
24/*	ssize_t	len;
25/*	int	flags;
26/*
27/*	VSTRING	*hex_decode_opt(result, in, len, flags)
28/*	VSTRING	*result;
29/*	const char *in;
30/*	ssize_t	len;
31/*	int	flags;
32/* DESCRIPTION
33/*	hex_encode() takes a block of len bytes and encodes it as one
34/*	upper-case null-terminated string.  The result value is
35/*	the result argument.
36/*
37/*	hex_decode() performs the opposite transformation on
38/*	lower-case, upper-case or mixed-case input. The result
39/*	value is the result argument. The result is null terminated,
40/*	whether or not that makes sense.
41/*
42/*	hex_encode_opt() enables extended functionality as controlled
43/*	with \fIflags\fR.
44/* .IP HEX_ENCODE_FLAG_NONE
45/*	The default: a self-documenting flag that enables no
46/*	functionality.
47/* .IP HEX_ENCODE_FLAG_USE_COLON
48/*	Inserts one ":" between bytes.
49/* .PP
50/*	hex_decode_opt() enables extended functionality as controlled
51/*	with \fIflags\fR.
52/* .IP HEX_DECODE_FLAG_NONE
53/*	The default: a self-documenting flag that enables no
54/*	functionality.
55/* .IP HEX_DECODE_FLAG_ALLOW_COLON
56/*	Allows, but does not require, one ":" between bytes.
57/* DIAGNOSTICS
58/*	hex_decode() returns a null pointer when the input contains
59/*	characters not in the hexadecimal alphabet.
60/* LICENSE
61/* .ad
62/* .fi
63/*	The Secure Mailer license must be distributed with this software.
64/* AUTHOR(S)
65/*	Wietse Venema
66/*	IBM T.J. Watson Research
67/*	P.O. Box 704
68/*	Yorktown Heights, NY 10598, USA
69/*
70/*	Wietse Venema
71/*	Google, Inc.
72/*	111 8th Avenue
73/*	New York, NY 10011, USA
74/*--*/
75
76/* System library. */
77
78#include <sys_defs.h>
79#include <ctype.h>
80#include <string.h>
81
82/* Utility library. */
83
84#include <msg.h>
85#include <mymalloc.h>
86#include <vstring.h>
87#include <hex_code.h>
88
89/* Application-specific. */
90
91static const unsigned char hex_chars[] = "0123456789ABCDEF";
92
93#define UCHAR_PTR(x) ((const unsigned char *)(x))
94
95/* hex_encode - ABI compatibility */
96
97#undef hex_encode
98
99VSTRING *hex_encode(VSTRING *result, const char *in, ssize_t len)
100{
101    return (hex_encode_opt(result, in, len, HEX_ENCODE_FLAG_NONE));
102}
103
104/* hex_encode_opt - raw data to encoded */
105
106VSTRING *hex_encode_opt(VSTRING *result, const char *in, ssize_t len, int flags)
107{
108    const unsigned char *cp;
109    int     ch;
110    ssize_t count;
111
112    VSTRING_RESET(result);
113    for (cp = UCHAR_PTR(in), count = len; count > 0; count--, cp++) {
114	ch = *cp;
115	VSTRING_ADDCH(result, hex_chars[(ch >> 4) & 0xf]);
116	VSTRING_ADDCH(result, hex_chars[ch & 0xf]);
117	if ((flags & HEX_ENCODE_FLAG_USE_COLON) && count > 1)
118	    VSTRING_ADDCH(result, ':');
119    }
120    VSTRING_TERMINATE(result);
121    return (result);
122}
123
124/* hex_decode - ABI compatibility wrapper */
125
126#undef hex_decode
127
128VSTRING *hex_decode(VSTRING *result, const char *in, ssize_t len)
129{
130    return (hex_decode_opt(result, in, len, HEX_DECODE_FLAG_NONE));
131}
132
133/* hex_decode_opt - encoded data to raw */
134
135VSTRING *hex_decode_opt(VSTRING *result, const char *in, ssize_t len, int flags)
136{
137    const unsigned char *cp;
138    ssize_t count;
139    unsigned int hex;
140    unsigned int bin;
141
142    VSTRING_RESET(result);
143    for (cp = UCHAR_PTR(in), count = len; count > 0; cp += 2, count -= 2) {
144	if (count < 2)
145	    return (0);
146	hex = cp[0];
147	if (hex >= '0' && hex <= '9')
148	    bin = (hex - '0') << 4;
149	else if (hex >= 'A' && hex <= 'F')
150	    bin = (hex - 'A' + 10) << 4;
151	else if (hex >= 'a' && hex <= 'f')
152	    bin = (hex - 'a' + 10) << 4;
153	else
154	    return (0);
155	hex = cp[1];
156	if (hex >= '0' && hex <= '9')
157	    bin |= (hex - '0');
158	else if (hex >= 'A' && hex <= 'F')
159	    bin |= (hex - 'A' + 10);
160	else if (hex >= 'a' && hex <= 'f')
161	    bin |= (hex - 'a' + 10);
162	else
163	    return (0);
164	VSTRING_ADDCH(result, bin);
165
166	/*
167	 * Support *colon-separated* input (no leading or trailing colons).
168	 * After decoding "xx", skip a possible ':' preceding "yy" in
169	 * "xx:yy".
170	 */
171	if ((flags & HEX_DECODE_FLAG_ALLOW_COLON)
172	    && count > 4 && cp[2] == ':') {
173	    ++cp;
174	    --count;
175	}
176    }
177    VSTRING_TERMINATE(result);
178    return (result);
179}
180
181#ifdef TEST
182#include <argv.h>
183
184 /*
185  * Proof-of-concept test program: convert to hexadecimal and back.
186  */
187
188#define STR(x)	vstring_str(x)
189#define LEN(x)	VSTRING_LEN(x)
190
191int     main(int unused_argc, char **unused_argv)
192{
193    VSTRING *b1 = vstring_alloc(1);
194    VSTRING *b2 = vstring_alloc(1);
195    char   *test = "this is a test";
196    ARGV   *argv;
197
198#define DECODE(b,x,l) { \
199	if (hex_decode((b),(x),(l)) == 0) \
200	    msg_panic("bad hex: %s", (x)); \
201    }
202#define VERIFY(b,t) { \
203	if (strcmp((b), (t)) != 0) \
204	    msg_panic("bad test: %s", (b)); \
205    }
206
207    hex_encode(b1, test, strlen(test));
208    DECODE(b2, STR(b1), LEN(b1));
209    VERIFY(STR(b2), test);
210
211    hex_encode(b1, test, strlen(test));
212    hex_encode(b2, STR(b1), LEN(b1));
213    hex_encode(b1, STR(b2), LEN(b2));
214    DECODE(b2, STR(b1), LEN(b1));
215    DECODE(b1, STR(b2), LEN(b2));
216    DECODE(b2, STR(b1), LEN(b1));
217    VERIFY(STR(b2), test);
218
219    hex_encode(b1, test, strlen(test));
220    hex_encode(b2, STR(b1), LEN(b1));
221    hex_encode(b1, STR(b2), LEN(b2));
222    hex_encode(b2, STR(b1), LEN(b1));
223    hex_encode(b1, STR(b2), LEN(b2));
224    DECODE(b2, STR(b1), LEN(b1));
225    DECODE(b1, STR(b2), LEN(b2));
226    DECODE(b2, STR(b1), LEN(b1));
227    DECODE(b1, STR(b2), LEN(b2));
228    DECODE(b2, STR(b1), LEN(b1));
229    VERIFY(STR(b2), test);
230
231    hex_encode_opt(b1, test, strlen(test), HEX_ENCODE_FLAG_USE_COLON);
232    argv = argv_split(STR(b1), ":");
233    if (argv->argc != strlen(test))
234	msg_panic("HEX_ENCODE_FLAG_USE_COLON");
235    if (hex_decode_opt(b2, STR(b1), LEN(b1), HEX_DECODE_FLAG_ALLOW_COLON) == 0)
236	msg_panic("HEX_DECODE_FLAG_ALLOW_COLON");
237    VERIFY(STR(b2), test);
238    argv_free(argv);
239
240    vstring_free(b1);
241    vstring_free(b2);
242    return (0);
243}
244
245#endif
246