1 /**
2 * Startup Self Test
3 * 
4 * Copyright:
5 * (C) 1999-2007 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.selftest.selftest;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_SELFTESTS && BOTAN_HAS_PUBLIC_KEY_CRYPTO):
15 
16 import botan.algo_factory.algo_factory;
17 import botan.algo_base.scan_token;
18 
19 import botan.filters.filters;
20 import botan.filters.hex_filt;
21 import botan.filters.aead_filt;
22 import botan.codec.hex;
23 import botan.hash.hash;
24 import botan.mac.mac;
25 import botan.block.block_cipher;
26 import botan.stream.stream_cipher;
27 import botan.engine.core_engine;
28 import botan.algo_base.symkey;
29 import memutils.dictionarylist;
30 import memutils.hashmap;
31 import botan.utils.exceptn;
32 import botan.utils.types;
33 
34 /**
35 * Run a set of self tests on some basic algorithms like AES and SHA-1
36 * Params:
37 *  af = an algorithm factory
38 * Throws: $(D SelfTestFailure) if a failure occured
39 */
40 /*
41 * Perform Self Tests
42 */
43 void confirmStartupSelfTests(AlgorithmFactory af)
44 {
45     logInfo("************************");
46     logInfo("*** START SELF TESTS ***");
47     logInfo("************************");
48     cipherKat(af, "DES",
49                "0123456789ABCDEF", "1234567890ABCDEF",
50                "4E6F77206973207468652074696D6520666F7220616C6C20",
51                "3FA40E8A984D48156A271787AB8883F9893D51EC4B563B53",
52                "E5C7CDDE872BF27C43E934008C389C0F683788499A7C05F6",
53                "F3096249C7F46E51A69E839B1A92F78403467133898EA622",
54                "F3096249C7F46E5135F24A242EEB3D3F3D6D5BE3255AF8C3",
55                "F3096249C7F46E51163A8CA0FFC94C27FA2F80F480B86F75");
56     
57     cipherKat(af, "TripleDES",
58                "385D7189A5C3D485E1370AA5D408082B5CCCCB5E19F2D90E",
59                "C141B5FCCD28DC8A",
60                "6E1BD7C6120947A464A6AAB293A0F89A563D8D40D3461B68",
61                "64EAAD4ACBB9CEAD6C7615E7C7E4792FE587D91F20C7D2F4",
62                "6235A461AFD312973E3B4F7AA7D23E34E03371F8E8C376C9",
63                "E26BA806A59B0330DE40CA38E77A3E494BE2B212F6DD624B",
64                "E26BA806A59B03307DE2BCC25A08BA40A8BA335F5D604C62",
65                "E26BA806A59B03303C62C2EFF32D3ACDD5D5F35EBCC53371");
66     
67     cipherKat(af, "AES-128",
68                "2B7E151628AED2A6ABF7158809CF4F3C",
69                "000102030405060708090A0B0C0D0E0F",
70                "6BC1BEE22E409F96E93D7E117393172A"
71                ~ "AE2D8A571E03AC9C9EB76FAC45AF8E51",
72                "3AD77BB40D7A3660A89ECAF32466EF97"
73                ~ "F5D3D58503B9699DE785895A96FDBAAF",
74                "7649ABAC8119B246CEE98E9B12E9197D"
75                ~ "5086CB9B507219EE95DB113A917678B2",
76                "3B3FD92EB72DAD20333449F8E83CFB4A"
77                ~ "C8A64537A0B3A93FCDE3CDAD9F1CE58B",
78                "3B3FD92EB72DAD20333449F8E83CFB4A"
79                ~ "7789508D16918F03F53C52DAC54ED825",
80                "3B3FD92EB72DAD20333449F8E83CFB4A"
81                ~ "010C041999E03F36448624483E582D0E");
82     
83     hashTest(af, "SHA-1",
84               "", "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709");
85     
86     hashTest(af, "SHA-1",
87               "616263", "A9993E364706816ABA3E25717850C26C9CD0D89D");
88     
89     hashTest(af, "SHA-1",
90               "6162636462636465636465666465666765666768666768696768696A"
91               ~ "68696A6B696A6B6C6A6B6C6D6B6C6D6E6C6D6E6F6D6E6F706E6F7071",
92               "84983E441C3BD26EBAAE4AA1F95129E5E54670F1");
93     
94     macTest(af, "HMAC(SHA-1)",
95              "4869205468657265",
96              "B617318655057264E28BC0B6FB378C8EF146BE00",
97              "0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B");
98     
99     hashTest(af, "SHA-256",
100               "",
101               "E3B0C44298FC1C149AFBF4C8996FB924"
102               ~ "27AE41E4649B934CA495991B7852B855");
103     
104     hashTest(af, "SHA-256",
105               "616263",
106               "BA7816BF8F01CFEA414140DE5DAE2223"
107               ~ "B00361A396177A9CB410FF61F20015AD");
108     
109     hashTest(af, "SHA-256",
110               "6162636462636465636465666465666765666768666768696768696A"
111               ~ "68696A6B696A6B6C6A6B6C6D6B6C6D6E6C6D6E6F6D6E6F706E6F7071",
112               "248D6A61D20638B8E5C026930C3E6039"
113               ~ "A33CE45964FF2167F6ECEDD419DB06C1");
114     
115     macTest(af, "HMAC(SHA-256)",
116              "4869205468657265",
117              "198A607EB44BFBC69903A0F1CF2BBDC5"
118              ~ "BA0AA3F3D9AE3C1C7A3B1696A0B68CF7",
119              "0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B"
120              ~ "0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B");
121     logInfo("**********************");
122     logInfo("*** END SELF TESTS ***");
123     logInfo("**********************");
124 }
125 
126 /**
127 * Run a set of self tests on some basic algorithms like AES and SHA-1
128 * Params:
129 *  af = an algorithm factory
130 * Returns:s false if a failure occured, otherwise true
131 */
132 bool passesSelfTests(AlgorithmFactory af)
133 {
134     try
135     {
136         confirmStartupSelfTests(af);
137     }
138     catch(SelfTestFailure)
139     {
140         return false;
141     }
142     
143     return true;
144 }
145 
146 
147 /**
148 * Run a set of algorithm KATs (known answer tests)
149 * Params:
150 *  algo_name = the algorithm we are testing
151 *  vars = a set of input variables for this test, all
152             hex encoded. Keys used: "input", "output", "key", and "iv"
153 *  af = an algorithm factory
154 * Returns: map from provider name to test result for that provider
155 */
156 HashMapRef!(string, bool)
157     algorithmKat(in SCANToken algo_name,
158                   in HashMapRef!(string, string) vars,
159                   AlgorithmFactory af)
160 {
161     const auto result = algorithmKatDetailed(algo_name, vars, af);
162     
163     HashMapRef!(string, bool) pass_or_fail;
164     
165     foreach (const ref string key, const ref string val; result)
166         pass_or_fail[key] = (val == "PASSED");
167     
168     return pass_or_fail;
169 }
170 
171 /**
172 * Run a set of algorithm KATs (known answer tests)
173 * Params:
174 *  algo_name = the algorithm we are testing
175 *  vars = a set of input variables for this test, all
176             hex encoded. Keys used: "input", "output", "key", and "iv"
177 *  af = an algorithm factory
178 * Returns:s map from provider name to test result for that provider
179 */
180 HashMapRef!(string, string)
181     algorithmKatDetailed(in SCANToken algo_name,
182                          const ref HashMapRef!(string, string) vars,
183                          AlgorithmFactory af)
184 {
185     logTrace("Testing ", algo_name);
186     const string algo = algo_name.algoNameAndArgs();
187     
188     logTrace("algoNameAndArgs: ", algo);
189     Vector!string providers = af.providersOf(algo);
190     logTrace("Providers: ", providers[]);
191     HashMapRef!(string, string) all_results;
192     
193     if (providers.empty) { // no providers, nothing to do
194         logTrace("Warning: ", algo_name.toString(), " has no providers");
195         return all_results;
196     }
197     const string input = vars.get("input");
198     const string output = vars.get("output");
199     
200     SymmetricKey key = SymmetricKey(vars.get("key"));
201     InitializationVector iv = InitializationVector(vars.get("iv"));
202     
203     for (size_t i = 0; i != providers.length; ++i)
204     {
205         const string provider = providers[i];
206         
207         if (const HashFunction proto = af.prototypeHashFunction(algo, provider))
208         {
209             logTrace("Found ", proto.name);
210             Filter filt = new HashFilter(proto.clone());
211             all_results[provider] = testFilterKat(filt, input, output);
212             logInfo(proto.name, " (", provider, ") ... ", all_results[provider]);
213         }
214         else if (const MessageAuthenticationCode proto = af.prototypeMac(algo, provider))
215         {
216             logTrace("Found ", proto.name);
217             KeyedFilter filt = new MACFilter(proto.clone(), key);
218             all_results[provider] = testFilterKat(filt, input, output);
219             logInfo(proto.name, " (", provider, ") ... ", all_results[provider]);
220         }
221         else if (const StreamCipher proto = af.prototypeStreamCipher(algo, provider))
222         {
223             logTrace("Found ", proto.name);
224             KeyedFilter filt = new StreamCipherFilter(proto.clone());
225             filt.setKey(key);
226             filt.setIv(iv);
227             
228             all_results[provider] = testFilterKat(filt, input, output);
229 
230             
231             logInfo(proto.name, " (", provider, ") ... ", all_results[provider]);
232         }
233         else if (const BlockCipher proto = af.prototypeBlockCipher(algo, provider))
234         {
235             logTrace("Found ", proto.name);
236             KeyedFilter enc = getCipherMode(proto, ENCRYPTION,
237                                             algo_name.cipherMode(),
238                                             algo_name.cipherModePad());
239             
240             KeyedFilter dec = getCipherMode(proto, DECRYPTION,
241                                             algo_name.cipherMode(),
242                                             algo_name.cipherModePad());
243             
244             if (!enc || !dec)
245             {
246                 logTrace("Enc/dec failure");
247                 destroy(enc);
248                 destroy(dec);
249                 continue;
250             }
251             
252             enc.setKey(key);
253             
254             if (enc.validIvLength(iv.length))
255                 enc.setIv(iv);
256             else if (!enc.validIvLength(0))
257                 throw new InvalidIVLength(algo, iv.length);
258             
259             dec.setKey(key);
260             
261             if (dec.validIvLength(iv.length))
262                 dec.setIv(iv);
263             else if (!dec.validIvLength(0))
264                 throw new InvalidIVLength(algo, iv.length);
265             
266             const Vector!ubyte ad = hexDecode(vars.get("ad"));
267             
268             if (!ad.empty)
269             {
270                 static if (BOTAN_HAS_AEAD_FILTER) {
271                     if (AEADFilter enc_aead = cast(AEADFilter)(enc))
272                     {
273                         enc_aead.setAssociatedData(ad.ptr, ad.length);
274                         
275                         if (AEADFilter dec_aead = cast(AEADFilter)(dec))
276                             dec_aead.setAssociatedData(ad.ptr, ad.length);
277                     }
278                 }
279             }
280             
281             all_results[provider ~ " (encrypt)"] = testFilterKat(enc, input, output);
282             all_results[provider ~ " (decrypt)"] = testFilterKat(dec, output, input);
283 
284             logInfo(proto.name, " (", provider, " encrypt) ... ", all_results[provider ~ " (encrypt)"]);
285             logInfo(proto.name, " (", provider, " decrypt) ... ", all_results[provider ~ " (decrypt)"]);
286 
287         }
288     }    
289 
290     return all_results;
291 }
292 
293 private:
294 
295 void verifyResults(in string algo, const HashMapRef!(string, string) results)
296 {
297     foreach (const ref string key, const ref string value; *results)
298     {
299         if (value != "PASSED")
300             throw new SelfTestFailure(algo ~ " self-test failed (" ~ value ~ ")" ~
301                                         " with provider " ~ key);
302     }
303 }
304 
305 void hashTest(AlgorithmFactory af, in string name, in string input, in string output)
306 {
307     HashMapRef!(string, string) vars;
308     vars["input"] = input;
309     vars["output"] = output;
310     
311     verifyResults(name, algorithmKatDetailed(SCANToken(name), vars, af));
312 }
313 
314 void macTest(AlgorithmFactory af,
315               in string name,
316               in string input,
317               in string output,
318               in string key)
319 {
320     HashMapRef!(string, string) vars;
321     vars["input"] = input;
322     vars["output"] = output;
323     vars["key"] = key;
324     
325     verifyResults(name, algorithmKatDetailed(SCANToken(name), vars, af));
326 }
327 
328 /*
329 * Perform a KAT for a cipher
330 */
331 void cipherKat(AlgorithmFactory af,
332                in string algo,
333                in string key_str,
334                in string iv_str,
335                in string input,
336                in string ecb_out,
337                in string cbc_out,
338                in string cfb_out,
339                in string ofb_out,
340                in string ctr_out)
341 {
342     SymmetricKey key = SymmetricKey(key_str);
343     InitializationVector iv = InitializationVector(iv_str);
344     
345     HashMapRef!(string, string) vars;
346     vars["key"] = key_str;
347     vars["iv"] = iv_str;
348     vars["input"] = input;
349     
350     HashMapRef!(string, bool) results;
351     
352     vars["output"] = ecb_out;
353     verifyResults(algo ~ "/ECB", algorithmKatDetailed(SCANToken(algo ~ "/ECB"), vars, af));
354     
355     vars["output"] = cbc_out;
356     verifyResults(algo ~ "/CBC", algorithmKatDetailed(SCANToken(algo ~ "/CBC/NoPadding"), vars, af));
357     
358     vars["output"] = cfb_out;
359     verifyResults(algo ~ "/CFB", algorithmKatDetailed(SCANToken(algo ~ "/CFB"), vars, af));
360     
361     vars["output"] = ofb_out;
362     verifyResults(algo ~ "/OFB", algorithmKatDetailed(SCANToken(algo ~ "/OFB"), vars, af));
363     
364     vars["output"] = ctr_out;
365     verifyResults(algo ~ "/CTR", algorithmKatDetailed(SCANToken(algo ~ "/CTR-BE"), vars, af));
366 }
367 
368 
369 /*
370 * Perform a Known Answer Test
371 */
372 string testFilterKat(Filter filter,
373                      in string input,
374                      in string expected)
375 {
376     try
377     {
378         Pipe pipe = Pipe(new HexDecoder, filter, new HexEncoder);
379         pipe.processMsg(input);
380         const string got = pipe.toString();
381         const bool same = (got == expected);
382         
383         if (same) {
384             return "PASSED";
385 
386         } else {
387             return "************** FAILED **************** => got " ~ got ~ " expected " ~ expected;
388         }
389     }
390     catch(Exception e)
391     {
392         return "exception " ~ e.msg;
393     }
394 }