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 }