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 }