xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/hex_code.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
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 
91 static 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 
hex_encode(VSTRING * result,const char * in,ssize_t len)99 VSTRING *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 
hex_encode_opt(VSTRING * result,const char * in,ssize_t len,int flags)106 VSTRING *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 
hex_decode(VSTRING * result,const char * in,ssize_t len)128 VSTRING *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 
hex_decode_opt(VSTRING * result,const char * in,ssize_t len,int flags)135 VSTRING *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 
main(int unused_argc,char ** unused_argv)191 int     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