1 /**
2 * Hex Encoder/Decoder
3 * 
4 * Copyright:
5 * (C) 1999-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.filters.hex_filt;
12 
13 import botan.filters.filter;
14 import botan.codec.hex;
15 import botan.utils.parsing;
16 import botan.utils.charset;
17 import botan.utils.exceptn;
18 import botan.utils.types;
19 import botan.utils.mem_ops;
20 import std.algorithm;
21 
22 /**
23 * Converts arbitrary binary data to hex strings, optionally with
24 * newlines inserted
25 */
26 final class HexEncoder : Filter, Filterable
27 {
28 public:
29     /**
30     * Whether to use uppercase or lowercase letters for the encoded string.
31     */
32     alias Case = bool;
33     enum : Case { Uppercase, Lowercase }
34 
35     override @property string name() const { return "HexEncoder"; }
36 
37     /*
38     * Convert some data into hex format
39     */
40     override void write(const(ubyte)* input, size_t length)
41     {
42         bufferInsert(m_input, m_position, input, length);
43         if (m_position + length >= m_input.length)
44         {
45             encodeAndSend(m_input.ptr, m_input.length);
46             input += (m_input.length - m_position);
47             length -= (m_input.length - m_position);
48             while (length >= m_input.length)
49             {
50                 encodeAndSend(input, m_input.length);
51                 input += m_input.length;
52                 length -= m_input.length;
53             }
54             copyMem(m_input.ptr, input, length);
55             m_position = 0;
56         }
57         m_position += length;
58     }
59 
60     /*
61     * Flush buffers
62     */
63     override void endMsg()
64     {
65         encodeAndSend(m_input.ptr, m_position);
66         if (m_counter && m_line_length)
67             send('\n');
68         m_counter = m_position = 0;
69     }
70 
71 
72     /**
73     * Create a hex encoder.
74     *
75     * Params:
76     *  the_case = the case to use in the encoded strings.
77     */
78     this(Case the_case)
79     { 
80         m_casing = the_case;
81         m_line_length = 0;
82         m_input.resize(HEX_CODEC_BUFFER_SIZE);
83         m_output.resize(2*m_input.length);
84         m_counter = m_position = 0;
85     }
86 
87 
88     /**
89     * Create a hex encoder.
90     *
91     * Params:
92     *  newlines = should newlines be used
93     *  line_length = if newlines are used, how long are lines
94     *  the_case = the case to use in the encoded strings
95     */
96     this(bool newlines = false, size_t line_length = 72, Case the_case = Uppercase)
97     {
98         m_casing = the_case;
99         m_line_length = newlines ? line_length : 0;
100         m_input.resize(HEX_CODEC_BUFFER_SIZE);
101         m_output.resize(2*HEX_CODEC_BUFFER_SIZE);
102         m_counter = m_position = 0;
103     }
104 
105     // Interface fallthrough
106     override bool attachable() { return super.attachable(); }
107     override void startMsg() { super.startMsg(); }
108     override void setNext(Filter* filters, size_t sz) { super.setNext(filters, sz); }
109 
110 private:
111     /*
112     * Encode and send a block
113     */
114     void encodeAndSend(const(ubyte)* block, size_t length)
115     {
116         hexEncode(cast(char*)(m_output.ptr), block, length, m_casing == Uppercase);
117         
118         if (m_line_length == 0)
119             send(m_output, 2*length);
120         else
121         {
122             size_t remaining = 2*length, offset = 0;
123             while (remaining)
124             {
125                 size_t sent = std.algorithm.min(m_line_length - m_counter, remaining);
126                 send(&m_output[offset], sent);
127                 m_counter += sent;
128                 remaining -= sent;
129                 offset += sent;
130                 if (m_counter == m_line_length)
131                 {
132                     send('\n');
133                     m_counter = 0;
134                 }
135             }
136         }
137     }
138 
139     const Case m_casing;
140     const size_t m_line_length;
141     Vector!ubyte m_input, m_output;
142     size_t m_position, m_counter;
143 
144 }
145 
146 /**
147 * Converts hex strings to bytes
148 */
149 final class HexDecoder : Filter, Filterable
150 {
151 public:
152     override @property string name() const { return "HexDecoder"; }
153 
154     /*
155     * Convert some data from hex format
156     */
157     override void write(const(ubyte)* input, size_t length)
158     {
159         while (length)
160         {
161             size_t to_copy = std.algorithm.min(length, m_input.length - m_position);
162             copyMem(&m_input[m_position], input, to_copy);
163             m_position += to_copy;
164             
165             size_t consumed = 0;
166             size_t written = hexDecode(m_output.ptr,
167                                         cast(const(char)*)(m_input.ptr),
168                                         m_position,
169                                         consumed,
170                                         m_checking != FULL_CHECK);
171             
172             send(m_output, written);
173             
174             if (consumed != m_position)
175             {
176                 copyMem(m_input.ptr, &m_input[consumed], m_position - consumed);
177                 m_position = m_position - consumed;
178             }
179             else
180                 m_position = 0;
181             
182             length -= to_copy;
183             input += to_copy;
184         }
185     }
186 
187     /*
188     * Flush buffers
189     */
190     override void endMsg()
191     {
192         size_t consumed = 0;
193         size_t written = hexDecode(m_output.ptr,
194                                     cast(const(char)*)(m_input.ptr),
195                                     m_position,
196                                     consumed,
197                                     m_checking != FULL_CHECK);
198                                     
199         send(m_output, written);
200         
201         const bool not_full_bytes = consumed != m_position;
202         
203         m_position = 0;
204         
205         if (not_full_bytes)
206             throw new InvalidArgument("HexDecoder: Input not full bytes");
207     }
208 
209 
210     /**
211     * Construct a Hex Decoder using the specified
212     * character checking.
213     *
214     * Params:
215     *  checking = the checking to use during decoding.
216     */
217     this(DecoderChecking checking = NONE)
218     {
219         m_checking = checking;
220         m_input.resize(HEX_CODEC_BUFFER_SIZE);
221         m_output.resize(HEX_CODEC_BUFFER_SIZE / 2);
222         m_position = 0;
223     }
224 
225     // Interface fallthrough
226     override bool attachable() { return super.attachable(); }
227     override void startMsg() { super.startMsg(); }
228     override void setNext(Filter* filters, size_t sz) { super.setNext(filters, sz); }
229 private:
230     const DecoderChecking m_checking;
231     Vector!ubyte m_input, m_output;
232     size_t m_position;
233 }
234 
235 /**
236 * Size used for internal buffer in hex encoder/decoder
237 */
238 immutable size_t HEX_CODEC_BUFFER_SIZE = 256;