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 }