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;