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() return
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 }