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