1 /**
2 * Transformations of data
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.algo_base.transform;
12 
13 import memutils.vector;
14 import botan.algo_base.key_spec;
15 import botan.utils.exceptn;
16 import botan.algo_base.symkey;
17 import botan.utils.types;
18 import botan.constants;
19 
20 /**
21 * Interface for general transformations on data
22 */
23 interface Transformation
24 {
25 public:
26     /**
27     * Begin processing a message.
28     * 
29     * Params:
30     *  nonce = the per message nonce
31     */    
32     final SecureVector!ubyte start(Alloc)(auto const ref RefCounted!(Vector!( ubyte, Alloc ), Alloc) nonce)
33     {
34         return startRaw(nonce.ptr, nonce.length);
35     }
36 
37     /**
38     * Begin processing a message.
39     * 
40     * Params:
41     *  nonce = the per message nonce
42     */    
43     final SecureVector!ubyte start(Alloc)(auto const ref Vector!( ubyte, Alloc ) nonce)
44     {
45         return startRaw(nonce.ptr, nonce.length);
46     }
47 
48     /**
49     * Begin processing a message.
50     * 
51     * Params:
52     *  nonce = a pointer to the per message nonce
53     *  nonce_len = the length of the message
54     */    
55     final SecureVector!ubyte start(const(ubyte)* nonce, size_t nonce_len)
56     {
57         return startRaw(nonce, nonce_len);
58     }
59 
60     /**
61      * Begin processing a message.
62      */
63     final SecureVector!ubyte start()
64     {
65         return startRaw(null, 0);
66     }
67 
68     /**
69     * Begin processing a message.
70     * 
71     * Params:
72     *  nonce = the per message nonce
73     *  nonce_len = length of nonce
74     */
75     SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len);
76 
77     /**
78     * Process some data. Input must be in size $(D updateGranularity()) ubyte blocks.
79     * 
80     * Params:
81     *  blocks = in/out paramter which will possibly be resized
82     *  offset = an offset into blocks to begin processing
83     */
84     void update(ref SecureVector!ubyte blocks, size_t offset = 0);
85 
86     /**
87     * Complete processing of a message.
88     *
89     * Params:
90     *  final_block = in/out parameter which must be at least
91     *          $(D minimumFinalSize()) bytes, and will be set to any final output
92     *  offset = an offset into final_block to begin processing
93     */
94     void finish(ref SecureVector!ubyte final_block, size_t offset = 0);
95 
96     /**
97     * Returns: The size of the output if this transform is used to process a
98     * message with input_length bytes. Will throw if unable to give a precise
99     * answer.
100     */
101     size_t outputLength(size_t input_length) const;
102 
103     /**
104     * Returns: size of required blocks to update
105     */
106     size_t updateGranularity() const;
107 
108     /**
109     * Returns: required minimium size to $(D finalize()) - may be any length larger than this.
110     */
111     size_t minimumFinalSize() const;
112 
113     /**
114     * Returns: the default size for a nonce
115     */
116     size_t defaultNonceLength() const;
117 
118     /**
119     * Returns: true iff nonce_len is a valid length for the nonce
120     */
121     bool validNonceLength(size_t nonce_len) const;
122 
123     /**
124     * Short name describing the provider of this tranformation.
125     * 
126     * Useful in cases where multiple implementations are available (eg,
127     * different implementations of AES). Default "core" is used for the 
128     * 'standard' implementation included in the library.
129     */
130     string provider() const;
131 
132     @property string name() const;
133 
134     void clear();
135 }
136 
137 class KeyedTransform : Transformation
138 {
139 public:
140     /**
141     * Returns: object describing limits on key size
142     */
143     abstract KeyLengthSpecification keySpec() const;
144 
145     /**
146     * Check whether a given key length is valid for this algorithm.
147     * 
148     * Params:
149     *  length = the key length to be checked.
150     * 
151     * Returns: true if the key length is valid.
152     */
153     final bool validKeylength(size_t length) const
154     {
155         return keySpec().validKeylength(length);
156     }
157 
158 
159 
160     /**
161     * Set the symmetric key of this transform
162     * 
163     * Params:
164     *  key = contains the key material
165     *  length = size in bytes of key param
166     */
167     final void setKey(const(ubyte)* key, size_t length)
168     {
169         if (!validKeylength(length))
170             throw new InvalidKeyLength(name, length);
171         keySchedule(key, length);
172     }
173 
174     /// ditto
175     final void setKey(Alloc)(in RefCounted!(Vector!( ubyte, Alloc ), Alloc) key)
176     {
177         setKey(key.ptr, key.length);
178     }
179 
180     /// ditto    
181     final void setKey(Alloc)(const ref Vector!( ubyte, Alloc ) key)
182     {
183         setKey(key.ptr, key.length);
184     }
185 
186     /// ditto
187     final void setKey(in SymmetricKey key)
188     {
189         setKey(key.ptr, key.length);
190     }
191 
192 
193 protected:
194 
195     abstract void keySchedule(const(ubyte)* key, size_t length);
196 }
197 
198 static if (BOTAN_TEST):
199 
200 import botan.test;
201 import botan.codec.hex;
202 import core.atomic;
203 import memutils.hashmap;
204 
205 shared size_t total_tests;
206 
207 Transformation getTransform(string algo)
208 {
209     throw new Exception("Unknown transform " ~ algo);
210 }
211 
212 SecureVector!ubyte transformTest(string algo,
213                                  in SecureVector!ubyte nonce,
214                                  in SecureVector!ubyte /*key*/,
215                                  in SecureVector!ubyte input)
216 {
217     Unique!Transformation transform = getTransform(algo);
218 
219     //transform.setKey(key);
220     transform.start(nonce);
221     
222     SecureVector!ubyte output = input.dup;
223     transform.update(output, 0);
224     
225     return output;
226 }
227 
228 static if (BOTAN_HAS_TESTS && !SKIP_TRANSFORM_TEST) unittest
229 {
230     logDebug("Testing transform.d ...");
231     File vec = File("../test_data/transform.vec", "r");
232     size_t fails = runTests(vec, "Transform", "Output", true,
233          (ref HashMap!(string, string) m) {
234             atomicOp!"+="(total_tests, 1);
235             return hexEncode(transformTest(m["Transform"],
236                                 hexDecodeLocked(m["Nonce"]),
237                                 hexDecodeLocked(m["Key"]),
238                                 hexDecodeLocked(m["Input"])));
239         });
240 
241     testReport("transform", total_tests, fails);
242 }