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 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 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 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 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 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