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.dup;
103 
104     e += b;
105     
106     if (results("+", a, b, c, d, e))
107         return 1;
108     
109     d = b + a;
110     e = b.dup;
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.dup;
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.dup;
148     e *= b;
149     
150     if (results("*", a, b, c, d, e))
151         return 1;
152     
153     d = b * a;
154     e = b.dup;
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.dup;
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.dup;
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.dup;
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.dup;
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.dup;
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 }