1 /** 2 * Filter interface for Transformations 3 * 4 * Copyright: 5 * (C) 2013 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.transform_filter; 12 13 import botan.constants; 14 public import botan.algo_base.transform; 15 public import botan.filters.key_filt; 16 import botan.filters.transform_filter; 17 import botan.stream.stream_cipher; 18 import botan.utils.rounding; 19 import botan.utils.mem_ops; 20 import std.algorithm : min; 21 22 /** 23 * Filter interface for Transformations 24 */ 25 class TransformationFilter : KeyedFilter, Filterable 26 { 27 public: 28 this(Transformation transform) 29 { 30 m_main_block_mod = chooseUpdateSize(transform.updateGranularity()); 31 m_final_minimum = transform.minimumFinalSize(); 32 33 if (m_main_block_mod == 0) 34 throw new InvalidArgument("main_block_mod == 0"); 35 36 if (m_final_minimum > m_main_block_mod) 37 throw new InvalidArgument("final_minimum > main_block_mod"); 38 m_buffer_2.resize(2 * m_main_block_mod); 39 m_buffer_pos = 0; 40 m_nonce = NonceState(transform.defaultNonceLength() == 0); 41 m_transform = transform; 42 m_buffer = m_transform.updateGranularity(); 43 } 44 45 override final void setIv(in InitializationVector iv) 46 { 47 m_nonce.update(*cast(InitializationVector*)&iv); 48 } 49 50 override final void setKey(in SymmetricKey key) 51 { 52 if (KeyedTransform keyed = cast(KeyedTransform)(*m_transform)) 53 keyed.setKey(key); 54 else if (key.length != 0) 55 throw new Exception("Transformation " ~ name ~ " does not accept keys"); 56 } 57 58 override final KeyLengthSpecification keySpec() const 59 { 60 if (KeyedTransform keyed = cast(KeyedTransform)(*m_transform)) 61 return keyed.keySpec(); 62 return KeyLengthSpecification(0); 63 } 64 65 override final bool validIvLength(size_t length) const 66 { 67 return m_transform.validNonceLength(length); 68 } 69 70 override @property string name() const 71 { 72 return m_transform.name; 73 } 74 /** 75 * Write bytes into the buffered filter, which will them emit them 76 * in calls to bufferedBlock in the subclass 77 * Params: 78 * input = the input bytes 79 * input_size = of input in bytes 80 */ 81 override void write(const(ubyte)* input, size_t input_size) 82 { 83 if (!input_size) 84 return; 85 86 if (m_buffer_pos + input_size >= m_main_block_mod + m_final_minimum) 87 { 88 size_t to_copy = min(m_buffer_2.length - m_buffer_pos, input_size); 89 90 // assert(m_buffer_2.length > to_copy); 91 92 copyMem(&m_buffer_2[m_buffer_pos], input, to_copy); 93 94 m_buffer_pos += to_copy; 95 96 input += to_copy; 97 input_size -= to_copy; 98 99 size_t total_to_consume = roundDown(min(m_buffer_pos, 100 m_buffer_pos + input_size - m_final_minimum), 101 m_main_block_mod); 102 103 bufferedBlock(m_buffer_2.ptr, total_to_consume); 104 m_buffer_pos -= total_to_consume; 105 // assert(m_buffer_2.length > total_to_consume); 106 copyMem(m_buffer_2.ptr, m_buffer_2.ptr + total_to_consume, m_buffer_pos); 107 } 108 109 if (input_size >= m_final_minimum) 110 { 111 size_t full_blocks = (input_size - m_final_minimum) / m_main_block_mod; 112 size_t to_copy = full_blocks * m_main_block_mod; 113 114 if (to_copy) 115 { 116 bufferedBlock(input, to_copy); 117 118 input += to_copy; 119 input_size -= to_copy; 120 } 121 } 122 123 // assert(m_buffer_pos + input_size < m_buffer_2.length); 124 copyMem(&m_buffer_2[m_buffer_pos], input, input_size); 125 m_buffer_pos += input_size; 126 } 127 128 void write(Alloc)(const ref Vector!( ubyte, Alloc ) input) 129 { 130 write(input.ptr, input.length); 131 } 132 133 // Interface fallthrough 134 override bool attachable() { return super.attachable(); } 135 override void setNext(Filter* filters, size_t sz) { super.setNext(filters, sz); } 136 137 protected: 138 /** 139 * Returns: block size of inputs 140 */ 141 size_t bufferedBlockSize() const { return m_main_block_mod; } 142 143 /** 144 * Returns: current position in the buffer 145 */ 146 size_t currentPosition() const { return m_buffer_pos; } 147 148 /** 149 * Reset the buffer position 150 */ 151 void bufferReset() { m_buffer_pos = 0; } 152 153 final const(Transformation) getTransform() const { return *m_transform; } 154 155 final Transformation getTransform() { return *m_transform; } 156 157 /** 158 * Finish a message, emitting to bufferedBlock and bufferedFinal 159 * Will throw new an exception if less than final_minimum bytes were 160 * written into the filter. 161 */ 162 override void endMsg() 163 { 164 if (m_buffer_pos < m_final_minimum) 165 throw new Exception("Buffered filter endMsg without enough input"); 166 167 size_t spare_blocks = (m_buffer_pos - m_final_minimum) / m_main_block_mod; 168 169 if (spare_blocks) 170 { 171 size_t spare_bytes = m_main_block_mod * spare_blocks; 172 bufferedBlock(m_buffer_2.ptr, spare_bytes); 173 bufferedFinal(&m_buffer_2[spare_bytes], m_buffer_pos - spare_bytes); 174 } 175 else 176 { 177 bufferedFinal(m_buffer_2.ptr, m_buffer_pos); 178 } 179 m_buffer_pos = 0; 180 181 //scope(exit) logTrace("endMsg(): ", m_buffer[]); 182 } 183 184 override void startMsg() 185 { 186 send(m_transform.start(m_nonce.get())); 187 } 188 189 private: 190 191 /** 192 * The block processor, implemented by subclasses 193 * Params: 194 * input = some input bytes 195 * length = the size of input, guaranteed to be a multiple 196 * of block_size 197 */ 198 final void bufferedBlock(const(ubyte)* input, size_t input_length) 199 { 200 while (input_length) 201 { 202 const size_t take = min(m_transform.updateGranularity(), input_length); 203 m_buffer = SecureVector!ubyte(input[0 .. take]); 204 m_transform.update(m_buffer); 205 206 send(m_buffer); 207 208 input += take; 209 input_length -= take; 210 } 211 } 212 213 /** 214 * The final block, implemented by subclasses 215 * Params: 216 * input = some input bytes 217 * length = the size of input, guaranteed to be at least 218 * final_minimum bytes 219 */ 220 final void bufferedFinal(const(ubyte)* input, size_t input_length) 221 { 222 SecureVector!ubyte buf; 223 buf[] = input[0 .. input_length]; 224 m_transform.finish(buf); 225 send(buf); 226 } 227 228 struct NonceState 229 { 230 public: 231 this(bool allow_null_nonce) 232 { 233 m_fresh_nonce = allow_null_nonce; 234 } 235 236 void update(in InitializationVector iv) 237 { 238 m_nonce = unlock(iv.bitsOf()); 239 m_fresh_nonce = true; 240 } 241 242 ref Vector!ubyte get() 243 { 244 assert(m_fresh_nonce, "The nonce is fresh for this message"); 245 246 if (!m_nonce.empty) 247 m_fresh_nonce = false; 248 return m_nonce; 249 } 250 private: 251 bool m_fresh_nonce; 252 Vector!ubyte m_nonce; 253 } 254 255 private: 256 257 size_t m_main_block_mod, m_final_minimum; 258 NonceState m_nonce; 259 Unique!Transformation m_transform; 260 SecureVector!ubyte m_buffer; 261 SecureVector!ubyte m_buffer_2; 262 size_t m_buffer_pos; 263 } 264 265 private: 266 267 size_t chooseUpdateSize(size_t update_granularity) 268 { 269 const size_t target_size = 1024; 270 271 if (update_granularity >= target_size) 272 return update_granularity; 273 274 return roundUp(target_size, update_granularity); 275 }