1 /** 2 * Hex Encoding and Decoding 3 * 4 * Copyright: 5 * (C) 2010 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.codec.hex; 12 13 import botan.constants; 14 import memutils.vector; 15 import botan.codec.hex; 16 import botan.utils.mem_ops; 17 import botan.utils.types; 18 import std.exception; 19 import std.conv : to; 20 21 /** 22 * Perform hex encoding 23 * Params: 24 * output = an array of at least input_length*2 bytes 25 * input = is some binary data 26 * input_length = length of input in bytes 27 * uppercase = should output be upper or lower case? 28 */ 29 void hexEncode(char* output, 30 const(ubyte)* input, 31 size_t input_length, 32 bool uppercase = true) 33 { 34 if (input_length == 0) return; 35 __gshared immutable ubyte[16] BIN_TO_HEX_UPPER = [ 36 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 37 'A', 'B', 'C', 'D', 'E', 'F' ]; 38 39 __gshared immutable ubyte[16] BIN_TO_HEX_LOWER = [ 40 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 41 'a', 'b', 'c', 'd', 'e', 'f' ]; 42 43 const(ubyte)* tbl = uppercase ? BIN_TO_HEX_UPPER.ptr : BIN_TO_HEX_LOWER.ptr; 44 45 foreach (size_t i; 0 .. input_length) 46 { 47 ubyte x = input[i]; 48 output[2*i ] = tbl[(x >> 4) & 0x0F]; 49 output[2*i+1] = tbl[(x ) & 0x0F]; 50 } 51 } 52 53 /** 54 * Perform hex encoding 55 * Params: 56 * input = some input 57 * input_length = length of input in bytes 58 * uppercase = should output be upper or lower case? 59 * Returns: hexadecimal representation of input 60 */ 61 string hexEncode(const(ubyte)* input, size_t input_length, bool uppercase = true) 62 { 63 if (input_length == 0) return ""; 64 char[] output; 65 output.length = 2 * input_length; 66 67 if (input_length) 68 hexEncode(output.ptr, input, input_length, uppercase); 69 70 return output.to!string; 71 } 72 73 /** 74 * Perform hex encoding 75 * Params: 76 * input = some input 77 * uppercase = should output be upper or lower case? 78 * Returns: hexadecimal representation of input 79 */ 80 string hexEncode(Alloc)(auto const ref Vector!( ubyte, Alloc ) input, bool uppercase = true) 81 { 82 return hexEncode(input.ptr, input.length, uppercase); 83 } 84 85 /// ditto 86 string hexEncode(Alloc)(auto const ref RefCounted!(Vector!( ubyte, Alloc ), Alloc) input, bool uppercase = true) 87 { 88 return hexEncode(input.ptr, input.length, uppercase); 89 } 90 91 /** 92 * Perform hex decoding 93 * Params: 94 * output = an array of at least input_length/2 bytes 95 * input = some hex input 96 * input_length = length of input in bytes 97 * input_consumed = is an output parameter which says how many 98 * bytes of input were actually consumed. If less than 99 * input_length, then the range input[consumed:length] 100 * should be passed in later along with more input. 101 * ignore_ws = ignore whitespace on input; if false, throw new an 102 exception if whitespace is encountered 103 * Returns: number of bytes written to output 104 */ 105 size_t hexDecode(ubyte* output, 106 const(char)* input, 107 size_t input_length, 108 ref size_t input_consumed, 109 bool ignore_ws = true) 110 { 111 if (input_length == 0) return 0; 112 /* 113 * Mapping of hex characters to either their binary equivalent 114 * or to an error code. 115 * If valid hex (0-9 A-F a-f), the value. 116 * If whitespace, then 0x80 117 * Otherwise 0xFF 118 * Warning: this table assumes ASCII character encodings 119 */ 120 121 __gshared immutable ubyte[256] HEX_TO_BIN = [ 122 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 123 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 124 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 125 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 126 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 127 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 128 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 129 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 130 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 131 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 132 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 133 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 134 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 135 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 136 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 137 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 138 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 139 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 140 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 141 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 142 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 143 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 144 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 145 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 146 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 147 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ]; 148 149 ubyte* out_ptr = output; 150 bool top_nibble = true; 151 152 clearMem(output, input_length / 2); 153 154 foreach (size_t i; 0 .. input_length) 155 { 156 const ubyte bin = HEX_TO_BIN[(cast(ubyte*)input)[i]]; 157 158 if (bin >= 0x10) 159 { 160 if (bin == 0x80 && ignore_ws) 161 continue; 162 163 ubyte bad_char = input[i]; 164 string bad_char_; 165 if (bad_char == '\t') 166 bad_char_ = "\\t"; 167 else if (bad_char == '\n') 168 bad_char_ = "\\n"; 169 170 throw new InvalidArgument("hexDecode: invalid hex character '" ~ bad_char_ ~ "'"); 171 } 172 173 *out_ptr |= bin << ((top_nibble?1:0)*4); 174 175 top_nibble = !top_nibble; 176 if (top_nibble) 177 ++out_ptr; 178 } 179 180 input_consumed = input_length; 181 size_t written = (out_ptr - output); 182 183 /* 184 * We only got half of a ubyte at the end; zap the half-written 185 * output and mark it as unread 186 */ 187 if (!top_nibble) 188 { 189 *out_ptr = 0; 190 input_consumed -= 1; 191 } 192 193 return written; 194 } 195 196 /** 197 * Perform hex decoding 198 * Params: 199 * output = an array of at least input_length/2 bytes 200 * input = some hex input 201 * input_length = length of input in bytes 202 * ignore_ws = ignore whitespace on input; if false, throw new an 203 exception if whitespace is encountered 204 * Returns: number of bytes written to output 205 */ 206 size_t hexDecode(ubyte* output, 207 const(char)* input, 208 size_t input_length, 209 bool ignore_ws = true) 210 { 211 size_t consumed = 0; 212 size_t written = hexDecode(output, input, input_length, consumed, ignore_ws); 213 214 if (consumed != input_length) 215 throw new InvalidArgument("hexDecode: input did not have full bytes"); 216 217 return written; 218 } 219 220 /** 221 * Perform hex decoding 222 * Params: 223 * output = an array of at least input_length/2 bytes 224 * input = some hex input 225 * ignore_ws = ignore whitespace on input; if false, throw new an 226 exception if whitespace is encountered 227 * Returns: number of bytes written to output 228 */ 229 size_t hexDecode(ubyte* output, in string input, bool ignore_ws = true) 230 { 231 return hexDecode(output, input.ptr, input.length, ignore_ws); 232 } 233 234 /** 235 * Perform hex decoding 236 * Params: 237 * input = some hex input 238 * ignore_ws = ignore whitespace on input; if false, throw new an 239 exception if whitespace is encountered 240 * Returns: decoded hex output 241 */ 242 Vector!ubyte hexDecode(string input, bool ignore_ws = true) 243 { 244 if (input.length == 0) return Vector!ubyte(); 245 Vector!ubyte bin; 246 bin.resize(1 + input.length / 2); 247 248 size_t written = hexDecode(bin.ptr, input.ptr, input.length, ignore_ws); 249 bin.resize(written); 250 return bin.move(); 251 } 252 253 /** 254 * Perform hex decoding 255 * Params: 256 * input = some hex input 257 * ignore_ws = ignore whitespace on input; if false, throw new an 258 exception if whitespace is encountered 259 * Returns: decoded hex output 260 */ 261 Vector!ubyte hexDecode(const ref Vector!char input, bool ignore_ws = true) 262 { 263 return hexDecode(cast(string)input[], ignore_ws); 264 } 265 266 /** 267 * Perform hex decoding 268 * Params: 269 * input = some hex input 270 * input_length = the length of input in bytes 271 * ignore_ws = ignore whitespace on input; if false, throw new an 272 exception if whitespace is encountered 273 * Returns: decoded hex output 274 */ 275 SecureVector!ubyte hexDecodeLocked(const(char)* input, size_t input_length, bool ignore_ws = true) 276 { 277 SecureVector!ubyte bin = SecureVector!ubyte(1 + input_length / 2); 278 size_t written = hexDecode(bin.ptr, input, input_length, ignore_ws); 279 bin.resize(written); 280 return bin.move(); 281 } 282 283 /** 284 * Perform hex decoding 285 * Params: 286 * input = some hex input 287 * ignore_ws = ignore whitespace on input; if false, throw new an 288 exception if whitespace is encountered 289 * Returns: decoded hex output 290 */ 291 SecureVector!ubyte hexDecodeLocked(in string input, bool ignore_ws = true) 292 { 293 return hexDecodeLocked(input.ptr, input.length, ignore_ws); 294 }