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 }