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