1 /** 2 * Unit test helper 3 * 4 * Copyright: 5 * (C) 2014-2015 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.math.bigint.test; 12 13 import botan.constants; 14 static if (BOTAN_TEST && BOTAN_HAS_PUBLIC_KEY_CRYPTO): 15 16 import botan.rng.rng; 17 import botan.rng.auto_rng; 18 import botan.utils.exceptn; 19 import botan.math.numbertheory.numthry; 20 import botan.test; 21 22 string stripComments(string line) 23 { 24 string ret = line; 25 if (ret.canFind('#')) 26 ret = ret[0 .. ret.indexOf('#')]; 27 return ret; 28 } 29 30 /* Strip comments, whitespace, etc */ 31 string strip(string line) 32 { 33 string ret = stripComments(line); 34 35 /* while(line.canFind(' ')) 36 line = line[0 .. line.indexOf(' ')]; 37 */ 38 39 while(ret.canFind('\t')) 40 ret = ret[0 .. ret.indexOf('\t')]; 41 return ret; 42 } 43 44 Vector!string parse(string line) 45 { 46 import std.string : indexOf; 47 const char DELIMITER = ':'; 48 Vector!string substr; 49 size_t end = line.indexOf(DELIMITER); 50 string line_ = line; 51 while(end != -1) 52 { 53 substr.pushBack(line_[0 .. end].idup); 54 if (end + 1 >= line.length) 55 break; 56 line_ = line_[end + 1 .. $]; 57 end = line_.indexOf(DELIMITER); 58 } 59 if (line_.length > 0) 60 substr.pushBack(line_.idup); 61 while(substr.length <= 4) // at least 5 substr, some possibly empty 62 substr.pushBack(""); 63 return substr; 64 } 65 66 // c==expected, d==a op b, e==a op= b 67 size_t results()(string op, auto const ref BigInt a, auto const ref BigInt b, auto const ref BigInt c, 68 auto const ref BigInt d, auto const ref BigInt e) 69 { 70 string op1 = "operator" ~ op; 71 string op2 = op1 ~ "="; 72 73 if (c == d && d == e) 74 return 0; 75 else 76 { 77 logError("ERROR: " ~ op1); 78 79 logDebug("a = ", a.toString()); 80 logDebug("b = ", b.toString()); 81 82 logDebug("c = ", c.toString()); 83 logDebug("d = ", d.toString()); 84 logDebug("e = ", e.toString()); 85 86 if (d != e) 87 { 88 logError("ERROR: " ~ op1 ~ " | " ~ op2 ~ " mismatch"); 89 } 90 assert(false); 91 } 92 } 93 94 size_t checkAdd(const ref Vector!string args) 95 { 96 //logTrace("Add: ", cast(ubyte[])args[0][]); 97 BigInt a = BigInt(args[0]); 98 BigInt b = BigInt(args[1]); 99 BigInt c = BigInt(args[2]); 100 101 BigInt d = a + b; 102 BigInt e = a.clone; 103 104 e += b; 105 106 if (results("+", a, b, c, d, e)) 107 return 1; 108 109 d = b + a; 110 e = b.clone; 111 e += a; 112 113 return results("+", a, b, c, d, e); 114 } 115 116 size_t checkSub(const ref Vector!string args) 117 { 118 BigInt a = BigInt(args[0]); 119 BigInt b = BigInt(args[1]); 120 BigInt c = BigInt(args[2]); 121 122 BigInt d = a - b; 123 BigInt e = a.clone; 124 e -= b; 125 126 return results("-", a, b, c, d, e); 127 } 128 129 size_t checkMul(const ref Vector!string args) 130 { 131 BigInt a = BigInt(args[0]); 132 BigInt b = BigInt(args[1]); 133 BigInt c = BigInt(args[2]); 134 135 /* 136 logTrace("a = " ~ args[0] " ~\n" 137 " ~b = " ~ args[1]); 138 */ 139 /* This makes it more likely the fast multiply algorithms will be usable, 140 which is what we really want to test here (the simple n^2 multiply is 141 pretty well tested at this point). 142 */ 143 a.growTo(64); 144 b.growTo(64); 145 146 BigInt d = a * b; 147 BigInt e = a.clone; 148 e *= b; 149 150 if (results("*", a, b, c, d, e)) 151 return 1; 152 153 d = b * a; 154 e = b.clone; 155 e *= a; 156 157 return results("*", a, b, c, d, e); 158 } 159 160 size_t checkSqr(const ref Vector!string args) 161 { 162 BigInt a = BigInt(args[0]); 163 BigInt b = BigInt(args[1]); 164 165 a.growTo(64); 166 b.growTo(64); 167 168 BigInt c = square(&a); 169 BigInt d = a * &a; 170 171 return results("sqr", a, a, b, c, d); 172 } 173 174 size_t checkDiv(const ref Vector!string args) 175 { 176 BigInt a = BigInt(args[0]); 177 BigInt b = BigInt(args[1]); 178 BigInt c = BigInt(args[2]); 179 180 BigInt d = a / b; 181 BigInt e = a.clone; 182 e /= b; 183 184 return results("/", a, b, c, d, e); 185 } 186 187 size_t checkMod(const ref Vector!string args, RandomNumberGenerator rng) 188 { 189 BigInt a = BigInt(args[0]); 190 BigInt b = BigInt(args[1]); 191 BigInt c = BigInt(args[2]); 192 193 BigInt d = a % b; 194 BigInt e = a.clone; 195 e %= b; 196 197 size_t got = results("%", a, b, c, d, e); 198 199 if (got) return got; 200 201 word b_word = b.wordAt(0); 202 203 /* Won't work for us, just pick one at random */ 204 while(b_word == 0) 205 for(size_t j = 0; j != 2*word.sizeof; j++) 206 b_word = (b_word << 4) ^ rng.nextByte(); 207 208 b = b_word; 209 210 c = a % b; /* we declare the BigInt % BigInt version to be correct here */ 211 212 word d2 = a % b_word; 213 e = a.clone; 214 e %= b_word; 215 216 return results("%(word)", a, b, c, BigInt(d2), e); 217 } 218 219 size_t checkShl(const ref Vector!string args) 220 { 221 BigInt a = BigInt(args[0]); 222 size_t b = args[1].to!size_t; 223 BigInt c = BigInt(args[2]); 224 225 BigInt d = a << b; 226 BigInt e = a.clone; 227 e <<= b; 228 229 return results("<<", a, BigInt(b), c, d, e); 230 } 231 232 size_t checkShr(const ref Vector!string args) 233 { 234 BigInt a = BigInt(args[0]); 235 size_t b = args[1].to!size_t; 236 BigInt c = BigInt(args[2]); 237 238 BigInt d = a >> b; 239 BigInt e = a.clone; 240 e >>= b; 241 242 return results(">>", a, BigInt(b), c, d, e); 243 } 244 245 /* Make sure that (a^b)%m == r */ 246 size_t checkPowmod(const ref Vector!string args) 247 { 248 BigInt a = BigInt(args[0]); 249 BigInt b = BigInt(args[1]); 250 BigInt m = BigInt(args[2]); 251 BigInt c = BigInt(args[3]); 252 253 BigInt r = powerMod(&a, &b, &m); 254 255 if (c != r) 256 { 257 logTrace("ERROR: powerMod"); 258 logTrace("a = ", a.toString()); 259 logTrace("b = ", b.toString()); 260 logTrace("m = ", m.toString()); 261 logTrace("c = ", c.toString()); 262 logTrace("r = ", r.toString()); 263 return 1; 264 } 265 return 0; 266 } 267 268 /* Make sure that n is prime or not prime, according to should_be_prime */ 269 size_t isPrimeTest(const ref Vector!string args, RandomNumberGenerator rng) 270 { 271 BigInt n = BigInt(args[0]); 272 bool should_be_prime = cast(bool)(args[1] == "1"); 273 274 bool isPrime = isPrime(&n, rng); 275 276 if (isPrime != should_be_prime) 277 { 278 logError("ERROR: isPrime"); 279 logDebug("n = ", n.toString()); 280 logDebug(isPrime, " != ", should_be_prime); 281 return 1; 282 } 283 return 0; 284 } 285 286 static if (BOTAN_HAS_TESTS && !SKIP_BIGINT_TEST) unittest 287 { 288 import botan.libstate.global_state; 289 auto state = globalState(); // ensure initialized 290 291 import std.stdio : writeln; 292 logDebug("Testing bigint/test.d ..."); 293 import std.array; 294 const string filename = "test_data/mp_valid.dat"; 295 File test_data = File(filename, "r"); 296 297 if (test_data.error || test_data.eof) 298 throw new StreamIOError("Couldn't open test file " ~ filename); 299 300 size_t total_errors = 0; 301 size_t errors = 0, alg_count = 0; 302 size_t total_alg; 303 string algorithm; 304 bool first = true; 305 size_t counter = 0; 306 307 Unique!AutoSeededRNG rng = new AutoSeededRNG; 308 309 while(!test_data.eof) 310 { 311 if (test_data.error) 312 throw new StreamIOError("File I/O error reading from " ~ filename); 313 string line_data = test_data.readln(); 314 if (!line_data) break; 315 Vector!char line = Vector!char(line_data[0 .. $-1].strip()); 316 if (line.length == 0) continue; 317 318 // Do line continuation 319 while(line[line.length-1] == '\\' && !test_data.eof()) 320 { 321 line.removeBack(); 322 line_data = test_data.readln(); 323 if (!line_data) break; 324 string nextline = line_data[0 .. $-1].strip(); 325 while(nextline.length > 0) { 326 if (nextline[$-1] == '\\') nextline = nextline[0 .. $-1]; 327 line ~= nextline; 328 line_data = test_data.readln(); 329 if (line_data.length == 0) break; 330 nextline = line_data[0 .. $-1].strip(); 331 } 332 } 333 334 if (line[0] == '[' && line[line.length - 1] == ']') 335 { 336 if (!first) 337 testReport("Bigint " ~ algorithm, alg_count, errors); 338 339 algorithm = line[].ptr[1 .. line.length - 1].idup; 340 341 total_errors += errors; 342 total_alg += alg_count; 343 errors = 0; 344 alg_count = 0; 345 counter = 0; 346 347 first = false; 348 continue; 349 } 350 Vector!string substr = parse(line[]); 351 352 logTrace("Testing: " ~ algorithm); 353 354 size_t new_errors = 0; 355 if (algorithm.canFind("Addition")) 356 new_errors = checkAdd(substr); 357 else if (algorithm.canFind("Subtraction")) 358 new_errors = checkSub(substr); 359 else if (algorithm.canFind("Multiplication")) 360 new_errors = checkMul(substr); 361 else if (algorithm.canFind("Square")) 362 new_errors = checkSqr(substr); 363 else if (algorithm.canFind("Division")) 364 new_errors = checkDiv(substr); 365 else if (algorithm.canFind("Modulo")) 366 new_errors = checkMod(substr, *rng); 367 else if (algorithm.canFind("LeftShift")) 368 new_errors = checkShl(substr); 369 else if (algorithm.canFind("RightShift")) 370 new_errors = checkShr(substr); 371 else if (algorithm.canFind("ModExp")) 372 new_errors = checkPowmod(substr); 373 else if (algorithm.canFind("PrimeTest")) 374 new_errors = isPrimeTest(substr, *rng); 375 else 376 logError("Unknown MPI test " ~ algorithm); 377 378 counter++; 379 alg_count++; 380 errors += new_errors; 381 382 if (new_errors) 383 logError("ERROR: BigInt " ~ algorithm ~ " failed test #" ~ alg_count.to!string); 384 } 385 386 testReport("Bigint " ~ algorithm, alg_count, errors); 387 388 total_errors += errors; 389 total_alg += alg_count; 390 391 testReport("BigInt", total_alg, total_errors); 392 }