blob: 3405cbdc55164840a38a916b267d0af5a5f95199 [file] [log] [blame]
David Benjamina5974bf2015-03-25 23:49:37 -04001/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
2 * All rights reserved.
3 *
4 * This package is an SSL implementation written
5 * by Eric Young (eay@cryptsoft.com).
6 * The implementation was written so as to conform with Netscapes SSL.
7 *
8 * This library is free for commercial and non-commercial use as long as
9 * the following conditions are aheared to. The following conditions
10 * apply to all code found in this distribution, be it the RC4, RSA,
11 * lhash, DES, etc., code; not just the SSL code. The SSL documentation
12 * included with this distribution is covered by the same copyright terms
13 * except that the holder is Tim Hudson (tjh@cryptsoft.com).
14 *
15 * Copyright remains Eric Young's, and as such any Copyright notices in
16 * the code are not to be removed.
17 * If this package is used in a product, Eric Young should be given attribution
18 * as the author of the parts of the library used.
19 * This can be in the form of a textual message at program startup or
20 * in documentation (online or textual) provided with the package.
21 *
22 * Redistribution and use in source and binary forms, with or without
23 * modification, are permitted provided that the following conditions
24 * are met:
25 * 1. Redistributions of source code must retain the copyright
26 * notice, this list of conditions and the following disclaimer.
27 * 2. Redistributions in binary form must reproduce the above copyright
28 * notice, this list of conditions and the following disclaimer in the
29 * documentation and/or other materials provided with the distribution.
30 * 3. All advertising materials mentioning features or use of this software
31 * must display the following acknowledgement:
32 * "This product includes cryptographic software written by
33 * Eric Young (eay@cryptsoft.com)"
34 * The word 'cryptographic' can be left out if the rouines from the library
35 * being used are not cryptographic related :-).
36 * 4. If you include any Windows specific code (or a derivative thereof) from
37 * the apps directory (application code) you must include an acknowledgement:
38 * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
39 *
40 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
41 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
44 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50 * SUCH DAMAGE.
51 *
52 * The licence and distribution terms for any publically available version or
53 * derivative of this code cannot be changed. i.e. this code cannot simply be
54 * copied and put under another distribution licence
55 * [including the GNU Public Licence.]
56 */
57/* ====================================================================
58 * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
59 *
60 * Portions of the attached software ("Contribution") are developed by
61 * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
62 *
63 * The Contribution is licensed pursuant to the Eric Young open source
64 * license provided above.
65 *
66 * The binary polynomial arithmetic software is originally written by
67 * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
68 * Laboratories. */
69
David Benjaminc561aa62015-05-09 01:38:06 -040070/* For BIGNUM format macros. */
71#if !defined(__STDC_FORMAT_MACROS)
72#define __STDC_FORMAT_MACROS
73#endif
74
David Benjamin8b66fef2016-06-26 17:58:55 -040075#include <assert.h>
David Benjamin15eaafb2015-05-27 14:51:51 -040076#include <errno.h>
David Benjamin5e9bdc12016-06-26 16:56:29 -040077#include <limits.h>
David Benjamina5974bf2015-03-25 23:49:37 -040078#include <stdio.h>
79#include <string.h>
80
David Benjaminef14b2d2015-11-11 14:01:27 -080081#include <utility>
82
David Benjamina5974bf2015-03-25 23:49:37 -040083#include <openssl/bn.h>
84#include <openssl/crypto.h>
85#include <openssl/err.h>
86#include <openssl/mem.h>
87
David Benjamin80137ba2016-06-26 15:03:41 -040088#include "../crypto/test/file_test.h"
Adam Langleyd2b5af52016-07-12 08:03:59 -070089#include "../crypto/test/scoped_types.h"
Matt Braithwaite64887252015-08-11 17:17:28 -070090#include "../crypto/test/test_util.h"
David Benjamina5974bf2015-03-25 23:49:37 -040091
92
David Benjamine701f162015-12-03 11:04:24 -050093static int HexToBIGNUM(ScopedBIGNUM *out, const char *in) {
94 BIGNUM *raw = NULL;
95 int ret = BN_hex2bn(&raw, in);
96 out->reset(raw);
97 return ret;
98}
99
David Benjamin80137ba2016-06-26 15:03:41 -0400100static ScopedBIGNUM GetBIGNUM(FileTest *t, const char *attribute) {
101 std::string hex;
102 if (!t->GetAttribute(&hex, attribute)) {
103 return nullptr;
104 }
105
106 ScopedBIGNUM ret;
107 if (HexToBIGNUM(&ret, hex.c_str()) != static_cast<int>(hex.size())) {
108 t->PrintLine("Could not decode '%s'.", hex.c_str());
109 return nullptr;
110 }
111 return ret;
112}
113
David Benjamin5e9bdc12016-06-26 16:56:29 -0400114static bool GetInt(FileTest *t, int *out, const char *attribute) {
115 ScopedBIGNUM ret = GetBIGNUM(t, attribute);
116 if (!ret) {
117 return false;
118 }
119
120 BN_ULONG word = BN_get_word(ret.get());
121 if (word > INT_MAX) {
122 return false;
123 }
124
125 *out = static_cast<int>(word);
126 return true;
127}
128
David Benjamin80137ba2016-06-26 15:03:41 -0400129static bool ExpectBIGNUMsEqual(FileTest *t, const char *operation,
130 const BIGNUM *expected, const BIGNUM *actual) {
131 if (BN_cmp(expected, actual) == 0) {
132 return true;
133 }
134
Adam Langley10f97f32016-07-12 08:09:33 -0700135 ScopedOpenSSLString expected_str(BN_bn2hex(expected));
136 ScopedOpenSSLString actual_str(BN_bn2hex(actual));
David Benjamin80137ba2016-06-26 15:03:41 -0400137 if (!expected_str || !actual_str) {
David Benjamina5974bf2015-03-25 23:49:37 -0400138 return false;
139 }
140
David Benjamin80137ba2016-06-26 15:03:41 -0400141 t->PrintLine("Got %s =", operation);
142 t->PrintLine("\t%s", actual_str.get());
143 t->PrintLine("wanted:");
144 t->PrintLine("\t%s", expected_str.get());
145 return false;
146}
147
David Benjamin30581032016-06-26 15:18:28 -0400148static bool TestSum(FileTest *t, BN_CTX *ctx) {
David Benjamin80137ba2016-06-26 15:03:41 -0400149 ScopedBIGNUM a = GetBIGNUM(t, "A");
150 ScopedBIGNUM b = GetBIGNUM(t, "B");
151 ScopedBIGNUM sum = GetBIGNUM(t, "Sum");
152 if (!a || !b || !sum) {
153 return false;
David Benjamina5974bf2015-03-25 23:49:37 -0400154 }
David Benjamin80137ba2016-06-26 15:03:41 -0400155
156 ScopedBIGNUM ret(BN_new());
157 if (!ret ||
158 !BN_add(ret.get(), a.get(), b.get()) ||
159 !ExpectBIGNUMsEqual(t, "A + B", sum.get(), ret.get()) ||
160 !BN_sub(ret.get(), sum.get(), a.get()) ||
161 !ExpectBIGNUMsEqual(t, "Sum - A", b.get(), ret.get()) ||
162 !BN_sub(ret.get(), sum.get(), b.get()) ||
163 !ExpectBIGNUMsEqual(t, "Sum - B", a.get(), ret.get())) {
164 return false;
165 }
166
Brian Smithe4bf8b32016-07-01 13:36:06 -1000167 // Test that the functions work when |r| and |a| point to the same |BIGNUM|,
168 // or when |r| and |b| point to the same |BIGNUM|. TODO: Test the case where
169 // all of |r|, |a|, and |b| point to the same |BIGNUM|.
170 if (!BN_copy(ret.get(), a.get()) ||
171 !BN_add(ret.get(), ret.get(), b.get()) ||
172 !ExpectBIGNUMsEqual(t, "A + B (r is a)", sum.get(), ret.get()) ||
173 !BN_copy(ret.get(), b.get()) ||
174 !BN_add(ret.get(), a.get(), ret.get()) ||
175 !ExpectBIGNUMsEqual(t, "A + B (r is b)", sum.get(), ret.get()) ||
176 !BN_copy(ret.get(), sum.get()) ||
177 !BN_sub(ret.get(), ret.get(), a.get()) ||
178 !ExpectBIGNUMsEqual(t, "Sum - A (r is a)", b.get(), ret.get()) ||
179 !BN_copy(ret.get(), a.get()) ||
180 !BN_sub(ret.get(), sum.get(), ret.get()) ||
181 !ExpectBIGNUMsEqual(t, "Sum - A (r is b)", b.get(), ret.get()) ||
182 !BN_copy(ret.get(), sum.get()) ||
183 !BN_sub(ret.get(), ret.get(), b.get()) ||
184 !ExpectBIGNUMsEqual(t, "Sum - B (r is a)", a.get(), ret.get()) ||
185 !BN_copy(ret.get(), b.get()) ||
186 !BN_sub(ret.get(), sum.get(), ret.get()) ||
187 !ExpectBIGNUMsEqual(t, "Sum - B (r is b)", a.get(), ret.get())) {
188 return false;
189 }
190
Brian Smith3d4030b2016-07-01 14:12:40 -1000191 // Test |BN_uadd| and |BN_usub| with the prerequisites they are documented as
192 // having. Note that these functions are frequently used when the
193 // prerequisites don't hold. In those cases, they are supposed to work as if
194 // the prerequisite hold, but we don't test that yet. TODO: test that.
195 if (!BN_is_negative(a.get()) &&
196 !BN_is_negative(b.get()) && BN_cmp(a.get(), b.get()) >= 0) {
197 if (!BN_uadd(ret.get(), a.get(), b.get()) ||
198 !ExpectBIGNUMsEqual(t, "A +u B", sum.get(), ret.get()) ||
199 !BN_usub(ret.get(), sum.get(), a.get()) ||
200 !ExpectBIGNUMsEqual(t, "Sum -u A", b.get(), ret.get()) ||
201 !BN_usub(ret.get(), sum.get(), b.get()) ||
202 !ExpectBIGNUMsEqual(t, "Sum -u B", a.get(), ret.get())) {
203 return false;
204 }
205
206 // Test that the functions work when |r| and |a| point to the same |BIGNUM|,
207 // or when |r| and |b| point to the same |BIGNUM|. TODO: Test the case where
208 // all of |r|, |a|, and |b| point to the same |BIGNUM|.
209 if (!BN_copy(ret.get(), a.get()) ||
210 !BN_uadd(ret.get(), ret.get(), b.get()) ||
211 !ExpectBIGNUMsEqual(t, "A +u B (r is a)", sum.get(), ret.get()) ||
212 !BN_copy(ret.get(), b.get()) ||
213 !BN_uadd(ret.get(), a.get(), ret.get()) ||
214 !ExpectBIGNUMsEqual(t, "A +u B (r is b)", sum.get(), ret.get()) ||
215 !BN_copy(ret.get(), sum.get()) ||
216 !BN_usub(ret.get(), ret.get(), a.get()) ||
217 !ExpectBIGNUMsEqual(t, "Sum -u A (r is a)", b.get(), ret.get()) ||
218 !BN_copy(ret.get(), a.get()) ||
219 !BN_usub(ret.get(), sum.get(), ret.get()) ||
220 !ExpectBIGNUMsEqual(t, "Sum -u A (r is b)", b.get(), ret.get()) ||
221 !BN_copy(ret.get(), sum.get()) ||
222 !BN_usub(ret.get(), ret.get(), b.get()) ||
223 !ExpectBIGNUMsEqual(t, "Sum -u B (r is a)", a.get(), ret.get()) ||
224 !BN_copy(ret.get(), b.get()) ||
225 !BN_usub(ret.get(), sum.get(), ret.get()) ||
226 !ExpectBIGNUMsEqual(t, "Sum -u B (r is b)", a.get(), ret.get())) {
227 return false;
228 }
229 }
230
Brian Smithfe47ba22016-07-01 11:46:21 -1000231 // Test with |BN_add_word| and |BN_sub_word| if |b| is small enough.
232 BN_ULONG b_word = BN_get_word(b.get());
233 if (!BN_is_negative(b.get()) && b_word != (BN_ULONG)-1) {
234 if (!BN_copy(ret.get(), a.get()) ||
235 !BN_add_word(ret.get(), b_word) ||
236 !ExpectBIGNUMsEqual(t, "A + B (word)", sum.get(), ret.get()) ||
237 !BN_copy(ret.get(), sum.get()) ||
238 !BN_sub_word(ret.get(), b_word) ||
239 !ExpectBIGNUMsEqual(t, "Sum - B (word)", a.get(), ret.get())) {
240 return false;
241 }
242 }
243
David Benjamina5974bf2015-03-25 23:49:37 -0400244 return true;
245}
246
David Benjamin30581032016-06-26 15:18:28 -0400247static bool TestLShift1(FileTest *t, BN_CTX *ctx) {
248 ScopedBIGNUM a = GetBIGNUM(t, "A");
249 ScopedBIGNUM lshift1 = GetBIGNUM(t, "LShift1");
David Benjamine1caf392016-06-26 17:12:23 -0400250 ScopedBIGNUM zero(BN_new());
251 if (!a || !lshift1 || !zero) {
David Benjamin30581032016-06-26 15:18:28 -0400252 return false;
253 }
254
David Benjamine1caf392016-06-26 17:12:23 -0400255 BN_zero(zero.get());
256
257 ScopedBIGNUM ret(BN_new()), two(BN_new()), remainder(BN_new());
258 if (!ret || !two || !remainder ||
David Benjamin30581032016-06-26 15:18:28 -0400259 !BN_set_word(two.get(), 2) ||
260 !BN_add(ret.get(), a.get(), a.get()) ||
261 !ExpectBIGNUMsEqual(t, "A + A", lshift1.get(), ret.get()) ||
262 !BN_mul(ret.get(), a.get(), two.get(), ctx) ||
263 !ExpectBIGNUMsEqual(t, "A * 2", lshift1.get(), ret.get()) ||
David Benjamine1caf392016-06-26 17:12:23 -0400264 !BN_div(ret.get(), remainder.get(), lshift1.get(), two.get(), ctx) ||
David Benjamin30581032016-06-26 15:18:28 -0400265 !ExpectBIGNUMsEqual(t, "LShift1 / 2", a.get(), ret.get()) ||
David Benjamine1caf392016-06-26 17:12:23 -0400266 !ExpectBIGNUMsEqual(t, "LShift1 % 2", zero.get(), remainder.get()) ||
David Benjamin30581032016-06-26 15:18:28 -0400267 !BN_lshift1(ret.get(), a.get()) ||
268 !ExpectBIGNUMsEqual(t, "A << 1", lshift1.get(), ret.get()) ||
269 !BN_rshift1(ret.get(), lshift1.get()) ||
270 !ExpectBIGNUMsEqual(t, "LShift >> 1", a.get(), ret.get()) ||
271 !BN_rshift1(ret.get(), lshift1.get()) ||
272 !ExpectBIGNUMsEqual(t, "LShift >> 1", a.get(), ret.get())) {
273 return false;
274 }
275
276 // Set the LSB to 1 and test rshift1 again.
David Benjamin56cbbe5b2016-07-02 14:24:30 -0400277 if (!BN_set_bit(lshift1.get(), 0) ||
278 !BN_div(ret.get(), nullptr /* rem */, lshift1.get(), two.get(), ctx) ||
David Benjamin30581032016-06-26 15:18:28 -0400279 !ExpectBIGNUMsEqual(t, "(LShift1 | 1) / 2", a.get(), ret.get()) ||
280 !BN_rshift1(ret.get(), lshift1.get()) ||
281 !ExpectBIGNUMsEqual(t, "(LShift | 1) >> 1", a.get(), ret.get())) {
282 return false;
283 }
284
285 return true;
286}
287
David Benjamin5e9bdc12016-06-26 16:56:29 -0400288static bool TestLShift(FileTest *t, BN_CTX *ctx) {
289 ScopedBIGNUM a = GetBIGNUM(t, "A");
290 ScopedBIGNUM lshift = GetBIGNUM(t, "LShift");
Adam Langleyd42e4b22016-06-27 15:57:57 -0700291 int n = 0;
David Benjamin5e9bdc12016-06-26 16:56:29 -0400292 if (!a || !lshift || !GetInt(t, &n, "N")) {
293 return false;
294 }
295
296 ScopedBIGNUM ret(BN_new());
297 if (!ret ||
298 !BN_lshift(ret.get(), a.get(), n) ||
299 !ExpectBIGNUMsEqual(t, "A << N", lshift.get(), ret.get()) ||
300 !BN_rshift(ret.get(), lshift.get(), n) ||
301 !ExpectBIGNUMsEqual(t, "A >> N", a.get(), ret.get())) {
302 return false;
303 }
304
305 return true;
306}
307
308static bool TestRShift(FileTest *t, BN_CTX *ctx) {
309 ScopedBIGNUM a = GetBIGNUM(t, "A");
310 ScopedBIGNUM rshift = GetBIGNUM(t, "RShift");
Adam Langleyd42e4b22016-06-27 15:57:57 -0700311 int n = 0;
David Benjamin5e9bdc12016-06-26 16:56:29 -0400312 if (!a || !rshift || !GetInt(t, &n, "N")) {
313 return false;
314 }
315
316 ScopedBIGNUM ret(BN_new());
317 if (!ret ||
318 !BN_rshift(ret.get(), a.get(), n) ||
319 !ExpectBIGNUMsEqual(t, "A >> N", rshift.get(), ret.get())) {
320 return false;
321 }
322
323 return true;
324}
325
David Benjamine1caf392016-06-26 17:12:23 -0400326static bool TestSquare(FileTest *t, BN_CTX *ctx) {
327 ScopedBIGNUM a = GetBIGNUM(t, "A");
328 ScopedBIGNUM square = GetBIGNUM(t, "Square");
329 ScopedBIGNUM zero(BN_new());
330 if (!a || !square || !zero) {
331 return false;
332 }
333
334 BN_zero(zero.get());
335
336 ScopedBIGNUM ret(BN_new()), remainder(BN_new());
337 if (!ret ||
338 !BN_sqr(ret.get(), a.get(), ctx) ||
339 !ExpectBIGNUMsEqual(t, "A^2", square.get(), ret.get()) ||
340 !BN_mul(ret.get(), a.get(), a.get(), ctx) ||
341 !ExpectBIGNUMsEqual(t, "A * A", square.get(), ret.get()) ||
342 !BN_div(ret.get(), remainder.get(), square.get(), a.get(), ctx) ||
343 !ExpectBIGNUMsEqual(t, "Square / A", a.get(), ret.get()) ||
344 !ExpectBIGNUMsEqual(t, "Square % A", zero.get(), remainder.get())) {
345 return false;
346 }
347
348 BN_set_negative(a.get(), 0);
349 if (!BN_sqrt(ret.get(), square.get(), ctx) ||
350 !ExpectBIGNUMsEqual(t, "sqrt(Square)", a.get(), ret.get())) {
351 return false;
352 }
353
David Benjamin28a8c2f2016-07-02 20:48:45 -0400354 // BN_sqrt should fail on non-squares and negative numbers.
355 if (!BN_is_zero(square.get())) {
356 ScopedBIGNUM tmp(BN_new());
357 if (!tmp || !BN_copy(tmp.get(), square.get())) {
358 return false;
359 }
360 BN_set_negative(tmp.get(), 1);
361
362 if (BN_sqrt(ret.get(), tmp.get(), ctx)) {
363 t->PrintLine("BN_sqrt succeeded on a negative number");
364 return false;
365 }
366 ERR_clear_error();
367
368 BN_set_negative(tmp.get(), 0);
369 if (!BN_add(tmp.get(), tmp.get(), BN_value_one())) {
370 return false;
371 }
372 if (BN_sqrt(ret.get(), tmp.get(), ctx)) {
373 t->PrintLine("BN_sqrt succeeded on a non-square");
374 return false;
375 }
376 ERR_clear_error();
377 }
378
David Benjamine1caf392016-06-26 17:12:23 -0400379 return true;
380}
381
David Benjamincca1c112016-06-26 17:28:55 -0400382static bool TestProduct(FileTest *t, BN_CTX *ctx) {
383 ScopedBIGNUM a = GetBIGNUM(t, "A");
384 ScopedBIGNUM b = GetBIGNUM(t, "B");
385 ScopedBIGNUM product = GetBIGNUM(t, "Product");
386 ScopedBIGNUM zero(BN_new());
387 if (!a || !b || !product || !zero) {
388 return false;
389 }
390
391 BN_zero(zero.get());
392
393 ScopedBIGNUM ret(BN_new()), remainder(BN_new());
394 if (!ret || !remainder ||
395 !BN_mul(ret.get(), a.get(), b.get(), ctx) ||
396 !ExpectBIGNUMsEqual(t, "A * B", product.get(), ret.get()) ||
397 !BN_div(ret.get(), remainder.get(), product.get(), a.get(), ctx) ||
398 !ExpectBIGNUMsEqual(t, "Product / A", b.get(), ret.get()) ||
399 !ExpectBIGNUMsEqual(t, "Product % A", zero.get(), remainder.get()) ||
400 !BN_div(ret.get(), remainder.get(), product.get(), b.get(), ctx) ||
401 !ExpectBIGNUMsEqual(t, "Product / B", a.get(), ret.get()) ||
402 !ExpectBIGNUMsEqual(t, "Product % B", zero.get(), remainder.get())) {
403 return false;
404 }
405
406 return true;
407}
408
David Benjamin8b66fef2016-06-26 17:58:55 -0400409static bool TestQuotient(FileTest *t, BN_CTX *ctx) {
410 ScopedBIGNUM a = GetBIGNUM(t, "A");
411 ScopedBIGNUM b = GetBIGNUM(t, "B");
412 ScopedBIGNUM quotient = GetBIGNUM(t, "Quotient");
413 ScopedBIGNUM remainder = GetBIGNUM(t, "Remainder");
414 if (!a || !b || !quotient || !remainder) {
415 return false;
416 }
417
418 ScopedBIGNUM ret(BN_new()), ret2(BN_new());
419 if (!ret || !ret2 ||
420 !BN_div(ret.get(), ret2.get(), a.get(), b.get(), ctx) ||
421 !ExpectBIGNUMsEqual(t, "A / B", quotient.get(), ret.get()) ||
422 !ExpectBIGNUMsEqual(t, "A % B", remainder.get(), ret2.get()) ||
423 !BN_mul(ret.get(), quotient.get(), b.get(), ctx) ||
424 !BN_add(ret.get(), ret.get(), remainder.get()) ||
425 !ExpectBIGNUMsEqual(t, "Quotient * B + Remainder", a.get(), ret.get())) {
426 return false;
427 }
428
429 // Test with |BN_mod_word| and |BN_div_word| if the divisor is small enough.
430 BN_ULONG b_word = BN_get_word(b.get());
431 if (!BN_is_negative(b.get()) && b_word != (BN_ULONG)-1) {
432 BN_ULONG remainder_word = BN_get_word(remainder.get());
433 assert(remainder_word != (BN_ULONG)-1);
434 if (!BN_copy(ret.get(), a.get())) {
435 return false;
436 }
437 BN_ULONG ret_word = BN_div_word(ret.get(), b_word);
438 if (ret_word != remainder_word) {
439 t->PrintLine("Got A %% B (word) = " BN_HEX_FMT1 ", wanted " BN_HEX_FMT1
440 "\n",
441 ret_word, remainder_word);
442 return false;
443 }
444 if (!ExpectBIGNUMsEqual(t, "A / B (word)", quotient.get(), ret.get())) {
445 return false;
446 }
447
448 ret_word = BN_mod_word(a.get(), b_word);
449 if (ret_word != remainder_word) {
450 t->PrintLine("Got A %% B (word) = " BN_HEX_FMT1 ", wanted " BN_HEX_FMT1
451 "\n",
452 ret_word, remainder_word);
453 return false;
454 }
455 }
456
David Benjamine8317a52016-07-02 20:14:30 -0400457 // Test BN_nnmod.
458 if (!BN_is_negative(b.get())) {
459 ScopedBIGNUM nnmod(BN_new());
460 if (!nnmod ||
461 !BN_copy(nnmod.get(), remainder.get()) ||
462 (BN_is_negative(nnmod.get()) &&
463 !BN_add(nnmod.get(), nnmod.get(), b.get())) ||
464 !BN_nnmod(ret.get(), a.get(), b.get(), ctx) ||
465 !ExpectBIGNUMsEqual(t, "A % B (non-negative)", nnmod.get(),
466 ret.get())) {
467 return false;
468 }
469 }
470
David Benjamin8b66fef2016-06-26 17:58:55 -0400471 return true;
472}
473
David Benjamin5a13e402016-07-02 20:33:07 -0400474static bool TestModMul(FileTest *t, BN_CTX *ctx) {
475 ScopedBIGNUM a = GetBIGNUM(t, "A");
476 ScopedBIGNUM b = GetBIGNUM(t, "B");
477 ScopedBIGNUM m = GetBIGNUM(t, "M");
478 ScopedBIGNUM mod_mul = GetBIGNUM(t, "ModMul");
479 if (!a || !b || !m || !mod_mul) {
480 return false;
481 }
482
483 ScopedBIGNUM ret(BN_new());
484 if (!ret ||
485 !BN_mod_mul(ret.get(), a.get(), b.get(), m.get(), ctx) ||
486 !ExpectBIGNUMsEqual(t, "A * B (mod M)", mod_mul.get(), ret.get())) {
487 return false;
488 }
489
490 if (BN_is_odd(m.get())) {
491 // Reduce |a| and |b| and test the Montgomery version.
492 ScopedBN_MONT_CTX mont(BN_MONT_CTX_new());
493 ScopedBIGNUM a_tmp(BN_new()), b_tmp(BN_new());
494 if (!mont || !a_tmp || !b_tmp ||
495 !BN_MONT_CTX_set(mont.get(), m.get(), ctx) ||
496 !BN_nnmod(a_tmp.get(), a.get(), m.get(), ctx) ||
497 !BN_nnmod(b_tmp.get(), b.get(), m.get(), ctx) ||
498 !BN_to_montgomery(a_tmp.get(), a_tmp.get(), mont.get(), ctx) ||
499 !BN_to_montgomery(b_tmp.get(), b_tmp.get(), mont.get(), ctx) ||
500 !BN_mod_mul_montgomery(ret.get(), a_tmp.get(), b_tmp.get(), mont.get(),
501 ctx) ||
502 !BN_from_montgomery(ret.get(), ret.get(), mont.get(), ctx) ||
503 !ExpectBIGNUMsEqual(t, "A * B (mod M) (Montgomery)",
504 mod_mul.get(), ret.get())) {
505 return false;
506 }
507 }
508
509 return true;
510}
511
David Benjamin45a8c8a2016-07-03 10:05:56 -0400512static bool TestModExp(FileTest *t, BN_CTX *ctx) {
513 ScopedBIGNUM a = GetBIGNUM(t, "A");
514 ScopedBIGNUM e = GetBIGNUM(t, "E");
515 ScopedBIGNUM m = GetBIGNUM(t, "M");
516 ScopedBIGNUM mod_exp = GetBIGNUM(t, "ModExp");
517 if (!a || !e || !m || !mod_exp) {
518 return false;
519 }
520
521 ScopedBIGNUM ret(BN_new());
522 if (!ret ||
523 !BN_mod_exp(ret.get(), a.get(), e.get(), m.get(), ctx) ||
524 !ExpectBIGNUMsEqual(t, "A ^ E (mod M)", mod_exp.get(), ret.get())) {
525 return false;
526 }
527
528 if (BN_is_odd(m.get())) {
529 if (!BN_mod_exp_mont(ret.get(), a.get(), e.get(), m.get(), ctx, NULL) ||
530 !ExpectBIGNUMsEqual(t, "A ^ E (mod M) (Montgomery)", mod_exp.get(),
531 ret.get()) ||
532 !BN_mod_exp_mont_consttime(ret.get(), a.get(), e.get(), m.get(), ctx,
533 NULL) ||
534 !ExpectBIGNUMsEqual(t, "A ^ E (mod M) (constant-time)", mod_exp.get(),
535 ret.get())) {
536 return false;
537 }
538 }
539
540 return true;
541}
542
David Benjamin4cb00ba2016-07-03 11:52:58 -0400543static bool TestExp(FileTest *t, BN_CTX *ctx) {
544 ScopedBIGNUM a = GetBIGNUM(t, "A");
545 ScopedBIGNUM e = GetBIGNUM(t, "E");
546 ScopedBIGNUM exp = GetBIGNUM(t, "Exp");
547 if (!a || !e || !exp) {
548 return false;
549 }
550
551 ScopedBIGNUM ret(BN_new());
552 if (!ret ||
553 !BN_exp(ret.get(), a.get(), e.get(), ctx) ||
554 !ExpectBIGNUMsEqual(t, "A ^ E", exp.get(), ret.get())) {
555 return false;
556 }
557
558 return true;
559}
560
David Benjaminffb7adc2016-07-03 15:19:20 -0400561static bool TestModSqrt(FileTest *t, BN_CTX *ctx) {
562 ScopedBIGNUM a = GetBIGNUM(t, "A");
563 ScopedBIGNUM p = GetBIGNUM(t, "P");
564 ScopedBIGNUM mod_sqrt = GetBIGNUM(t, "ModSqrt");
565 if (!a || !p || !mod_sqrt) {
566 return false;
567 }
568
569 ScopedBIGNUM ret(BN_new());
570 ScopedBIGNUM ret2(BN_new());
571 if (!ret ||
572 !ret2 ||
573 !BN_mod_sqrt(ret.get(), a.get(), p.get(), ctx) ||
574 // There are two possible answers.
575 !BN_sub(ret2.get(), p.get(), ret.get())) {
576 return false;
577 }
578
579 if (BN_cmp(ret2.get(), mod_sqrt.get()) != 0 &&
580 !ExpectBIGNUMsEqual(t, "sqrt(A) (mod P)", mod_sqrt.get(), ret.get())) {
581 return false;
582 }
583
584 return true;
585}
586
David Benjamin80137ba2016-06-26 15:03:41 -0400587struct Test {
588 const char *name;
David Benjamin30581032016-06-26 15:18:28 -0400589 bool (*func)(FileTest *t, BN_CTX *ctx);
David Benjamin80137ba2016-06-26 15:03:41 -0400590};
David Benjamina5974bf2015-03-25 23:49:37 -0400591
David Benjamin80137ba2016-06-26 15:03:41 -0400592static const Test kTests[] = {
593 {"Sum", TestSum},
David Benjamin30581032016-06-26 15:18:28 -0400594 {"LShift1", TestLShift1},
David Benjamin5e9bdc12016-06-26 16:56:29 -0400595 {"LShift", TestLShift},
596 {"RShift", TestRShift},
David Benjamine1caf392016-06-26 17:12:23 -0400597 {"Square", TestSquare},
David Benjamincca1c112016-06-26 17:28:55 -0400598 {"Product", TestProduct},
David Benjamin8b66fef2016-06-26 17:58:55 -0400599 {"Quotient", TestQuotient},
David Benjamin5a13e402016-07-02 20:33:07 -0400600 {"ModMul", TestModMul},
David Benjamin45a8c8a2016-07-03 10:05:56 -0400601 {"ModExp", TestModExp},
David Benjamin4cb00ba2016-07-03 11:52:58 -0400602 {"Exp", TestExp},
David Benjaminffb7adc2016-07-03 15:19:20 -0400603 {"ModSqrt", TestModSqrt},
David Benjamin80137ba2016-06-26 15:03:41 -0400604};
605
606static bool RunTest(FileTest *t, void *arg) {
David Benjamin30581032016-06-26 15:18:28 -0400607 BN_CTX *ctx = reinterpret_cast<BN_CTX *>(arg);
David Benjamin80137ba2016-06-26 15:03:41 -0400608 for (const Test &test : kTests) {
609 if (t->GetType() != test.name) {
610 continue;
David Benjamina5974bf2015-03-25 23:49:37 -0400611 }
David Benjamin30581032016-06-26 15:18:28 -0400612 return test.func(t, ctx);
David Benjamina5974bf2015-03-25 23:49:37 -0400613 }
David Benjamin80137ba2016-06-26 15:03:41 -0400614 t->PrintLine("Unknown test type: %s", t->GetType().c_str());
615 return false;
David Benjamina5974bf2015-03-25 23:49:37 -0400616}
617
David Benjamin96f94472016-06-26 17:31:02 -0400618static bool TestBN2BinPadded(BN_CTX *ctx) {
David Benjamina5974bf2015-03-25 23:49:37 -0400619 uint8_t zeros[256], out[256], reference[128];
620
621 memset(zeros, 0, sizeof(zeros));
622
623 // Test edge case at 0.
624 ScopedBIGNUM n(BN_new());
625 if (!n || !BN_bn2bin_padded(NULL, 0, n.get())) {
626 fprintf(stderr,
627 "BN_bn2bin_padded failed to encode 0 in an empty buffer.\n");
628 return false;
629 }
630 memset(out, -1, sizeof(out));
631 if (!BN_bn2bin_padded(out, sizeof(out), n.get())) {
632 fprintf(stderr,
633 "BN_bn2bin_padded failed to encode 0 in a non-empty buffer.\n");
634 return false;
635 }
636 if (memcmp(zeros, out, sizeof(out))) {
637 fprintf(stderr, "BN_bn2bin_padded did not zero buffer.\n");
638 return false;
639 }
640
641 // Test a random numbers at various byte lengths.
642 for (size_t bytes = 128 - 7; bytes <= 128; bytes++) {
643 if (!BN_rand(n.get(), bytes * 8, 0 /* make sure top bit is 1 */,
644 0 /* don't modify bottom bit */)) {
Brian Smith83a82982015-04-09 16:21:10 -1000645 ERR_print_errors_fp(stderr);
David Benjamina5974bf2015-03-25 23:49:37 -0400646 return false;
647 }
648 if (BN_num_bytes(n.get()) != bytes ||
649 BN_bn2bin(n.get(), reference) != bytes) {
650 fprintf(stderr, "Bad result from BN_rand; bytes.\n");
651 return false;
652 }
653 // Empty buffer should fail.
654 if (BN_bn2bin_padded(NULL, 0, n.get())) {
655 fprintf(stderr,
656 "BN_bn2bin_padded incorrectly succeeded on empty buffer.\n");
657 return false;
658 }
659 // One byte short should fail.
660 if (BN_bn2bin_padded(out, bytes - 1, n.get())) {
661 fprintf(stderr, "BN_bn2bin_padded incorrectly succeeded on short.\n");
662 return false;
663 }
664 // Exactly right size should encode.
665 if (!BN_bn2bin_padded(out, bytes, n.get()) ||
666 memcmp(out, reference, bytes) != 0) {
667 fprintf(stderr, "BN_bn2bin_padded gave a bad result.\n");
668 return false;
669 }
670 // Pad up one byte extra.
671 if (!BN_bn2bin_padded(out, bytes + 1, n.get()) ||
672 memcmp(out + 1, reference, bytes) || memcmp(out, zeros, 1)) {
673 fprintf(stderr, "BN_bn2bin_padded gave a bad result.\n");
674 return false;
675 }
676 // Pad up to 256.
677 if (!BN_bn2bin_padded(out, sizeof(out), n.get()) ||
678 memcmp(out + sizeof(out) - bytes, reference, bytes) ||
679 memcmp(out, zeros, sizeof(out) - bytes)) {
680 fprintf(stderr, "BN_bn2bin_padded gave a bad result.\n");
681 return false;
682 }
683 }
684
685 return true;
686}
David Benjaminc85573c2015-04-20 19:52:31 -0400687
688static int DecimalToBIGNUM(ScopedBIGNUM *out, const char *in) {
689 BIGNUM *raw = NULL;
690 int ret = BN_dec2bn(&raw, in);
691 out->reset(raw);
692 return ret;
693}
694
David Benjamin96f94472016-06-26 17:31:02 -0400695static bool TestDec2BN(BN_CTX *ctx) {
David Benjaminc85573c2015-04-20 19:52:31 -0400696 ScopedBIGNUM bn;
697 int ret = DecimalToBIGNUM(&bn, "0");
698 if (ret != 1 || !BN_is_zero(bn.get()) || BN_is_negative(bn.get())) {
699 fprintf(stderr, "BN_dec2bn gave a bad result.\n");
700 return false;
701 }
702
703 ret = DecimalToBIGNUM(&bn, "256");
704 if (ret != 3 || !BN_is_word(bn.get(), 256) || BN_is_negative(bn.get())) {
705 fprintf(stderr, "BN_dec2bn gave a bad result.\n");
706 return false;
707 }
708
709 ret = DecimalToBIGNUM(&bn, "-42");
710 if (ret != 3 || !BN_abs_is_word(bn.get(), 42) || !BN_is_negative(bn.get())) {
711 fprintf(stderr, "BN_dec2bn gave a bad result.\n");
712 return false;
713 }
714
715 ret = DecimalToBIGNUM(&bn, "-0");
716 if (ret != 2 || !BN_is_zero(bn.get()) || BN_is_negative(bn.get())) {
717 fprintf(stderr, "BN_dec2bn gave a bad result.\n");
718 return false;
719 }
720
721 ret = DecimalToBIGNUM(&bn, "42trailing garbage is ignored");
722 if (ret != 2 || !BN_abs_is_word(bn.get(), 42) || BN_is_negative(bn.get())) {
723 fprintf(stderr, "BN_dec2bn gave a bad result.\n");
724 return false;
725 }
726
727 return true;
728}
729
David Benjamin96f94472016-06-26 17:31:02 -0400730static bool TestHex2BN(BN_CTX *ctx) {
David Benjaminc85573c2015-04-20 19:52:31 -0400731 ScopedBIGNUM bn;
732 int ret = HexToBIGNUM(&bn, "0");
733 if (ret != 1 || !BN_is_zero(bn.get()) || BN_is_negative(bn.get())) {
734 fprintf(stderr, "BN_hex2bn gave a bad result.\n");
735 return false;
736 }
737
738 ret = HexToBIGNUM(&bn, "256");
739 if (ret != 3 || !BN_is_word(bn.get(), 0x256) || BN_is_negative(bn.get())) {
740 fprintf(stderr, "BN_hex2bn gave a bad result.\n");
741 return false;
742 }
743
744 ret = HexToBIGNUM(&bn, "-42");
745 if (ret != 3 || !BN_abs_is_word(bn.get(), 0x42) || !BN_is_negative(bn.get())) {
746 fprintf(stderr, "BN_hex2bn gave a bad result.\n");
747 return false;
748 }
749
750 ret = HexToBIGNUM(&bn, "-0");
751 if (ret != 2 || !BN_is_zero(bn.get()) || BN_is_negative(bn.get())) {
752 fprintf(stderr, "BN_hex2bn gave a bad result.\n");
753 return false;
754 }
755
756 ret = HexToBIGNUM(&bn, "abctrailing garbage is ignored");
757 if (ret != 3 || !BN_is_word(bn.get(), 0xabc) || BN_is_negative(bn.get())) {
758 fprintf(stderr, "BN_hex2bn gave a bad result.\n");
759 return false;
760 }
761
762 return true;
763}
764
765static ScopedBIGNUM ASCIIToBIGNUM(const char *in) {
766 BIGNUM *raw = NULL;
767 if (!BN_asc2bn(&raw, in)) {
768 return nullptr;
769 }
770 return ScopedBIGNUM(raw);
771}
772
David Benjamin96f94472016-06-26 17:31:02 -0400773static bool TestASC2BN(BN_CTX *ctx) {
David Benjaminc85573c2015-04-20 19:52:31 -0400774 ScopedBIGNUM bn = ASCIIToBIGNUM("0");
775 if (!bn || !BN_is_zero(bn.get()) || BN_is_negative(bn.get())) {
776 fprintf(stderr, "BN_asc2bn gave a bad result.\n");
777 return false;
778 }
779
780 bn = ASCIIToBIGNUM("256");
781 if (!bn || !BN_is_word(bn.get(), 256) || BN_is_negative(bn.get())) {
782 fprintf(stderr, "BN_asc2bn gave a bad result.\n");
783 return false;
784 }
785
786 bn = ASCIIToBIGNUM("-42");
787 if (!bn || !BN_abs_is_word(bn.get(), 42) || !BN_is_negative(bn.get())) {
788 fprintf(stderr, "BN_asc2bn gave a bad result.\n");
789 return false;
790 }
791
792 bn = ASCIIToBIGNUM("0x1234");
793 if (!bn || !BN_is_word(bn.get(), 0x1234) || BN_is_negative(bn.get())) {
794 fprintf(stderr, "BN_asc2bn gave a bad result.\n");
795 return false;
796 }
797
798 bn = ASCIIToBIGNUM("0X1234");
799 if (!bn || !BN_is_word(bn.get(), 0x1234) || BN_is_negative(bn.get())) {
800 fprintf(stderr, "BN_asc2bn gave a bad result.\n");
801 return false;
802 }
803
804 bn = ASCIIToBIGNUM("-0xabcd");
805 if (!bn || !BN_abs_is_word(bn.get(), 0xabcd) || !BN_is_negative(bn.get())) {
806 fprintf(stderr, "BN_asc2bn gave a bad result.\n");
807 return false;
808 }
809
810 bn = ASCIIToBIGNUM("-0");
811 if (!bn || !BN_is_zero(bn.get()) || BN_is_negative(bn.get())) {
812 fprintf(stderr, "BN_asc2bn gave a bad result.\n");
813 return false;
814 }
815
816 bn = ASCIIToBIGNUM("123trailing garbage is ignored");
817 if (!bn || !BN_is_word(bn.get(), 123) || BN_is_negative(bn.get())) {
818 fprintf(stderr, "BN_asc2bn gave a bad result.\n");
819 return false;
820 }
821
822 return true;
823}
David Benjaminb3a7b512015-05-26 18:35:28 -0400824
Matt Braithwaite64887252015-08-11 17:17:28 -0700825struct MPITest {
826 const char *base10;
827 const char *mpi;
828 size_t mpi_len;
829};
830
831static const MPITest kMPITests[] = {
832 { "0", "\x00\x00\x00\x00", 4 },
833 { "1", "\x00\x00\x00\x01\x01", 5 },
834 { "-1", "\x00\x00\x00\x01\x81", 5 },
835 { "128", "\x00\x00\x00\x02\x00\x80", 6 },
836 { "256", "\x00\x00\x00\x02\x01\x00", 6 },
837 { "-256", "\x00\x00\x00\x02\x81\x00", 6 },
838};
839
David Benjamin96f94472016-06-26 17:31:02 -0400840static bool TestMPI() {
Matt Braithwaite64887252015-08-11 17:17:28 -0700841 uint8_t scratch[8];
842
843 for (size_t i = 0; i < sizeof(kMPITests) / sizeof(kMPITests[0]); i++) {
844 const MPITest &test = kMPITests[i];
845 ScopedBIGNUM bn(ASCIIToBIGNUM(test.base10));
846 const size_t mpi_len = BN_bn2mpi(bn.get(), NULL);
847 if (mpi_len > sizeof(scratch)) {
848 fprintf(stderr, "MPI test #%u: MPI size is too large to test.\n",
849 (unsigned)i);
850 return false;
851 }
852
853 const size_t mpi_len2 = BN_bn2mpi(bn.get(), scratch);
854 if (mpi_len != mpi_len2) {
855 fprintf(stderr, "MPI test #%u: length changes.\n", (unsigned)i);
856 return false;
857 }
858
859 if (mpi_len != test.mpi_len ||
860 memcmp(test.mpi, scratch, mpi_len) != 0) {
861 fprintf(stderr, "MPI test #%u failed:\n", (unsigned)i);
862 hexdump(stderr, "Expected: ", test.mpi, test.mpi_len);
863 hexdump(stderr, "Got: ", scratch, mpi_len);
864 return false;
865 }
866
867 ScopedBIGNUM bn2(BN_mpi2bn(scratch, mpi_len, NULL));
868 if (bn2.get() == nullptr) {
869 fprintf(stderr, "MPI test #%u: failed to parse\n", (unsigned)i);
870 return false;
871 }
872
873 if (BN_cmp(bn.get(), bn2.get()) != 0) {
874 fprintf(stderr, "MPI test #%u: wrong result\n", (unsigned)i);
875 return false;
876 }
877 }
878
879 return true;
880}
881
David Benjamin96f94472016-06-26 17:31:02 -0400882static bool TestRand() {
David Benjaminb3a7b512015-05-26 18:35:28 -0400883 ScopedBIGNUM bn(BN_new());
884 if (!bn) {
885 return false;
886 }
887
888 // Test BN_rand accounts for degenerate cases with |top| and |bottom|
889 // parameters.
890 if (!BN_rand(bn.get(), 0, 0 /* top */, 0 /* bottom */) ||
891 !BN_is_zero(bn.get())) {
892 fprintf(stderr, "BN_rand gave a bad result.\n");
893 return false;
894 }
895 if (!BN_rand(bn.get(), 0, 1 /* top */, 1 /* bottom */) ||
896 !BN_is_zero(bn.get())) {
897 fprintf(stderr, "BN_rand gave a bad result.\n");
898 return false;
899 }
900
901 if (!BN_rand(bn.get(), 1, 0 /* top */, 0 /* bottom */) ||
902 !BN_is_word(bn.get(), 1)) {
903 fprintf(stderr, "BN_rand gave a bad result.\n");
904 return false;
905 }
906 if (!BN_rand(bn.get(), 1, 1 /* top */, 0 /* bottom */) ||
907 !BN_is_word(bn.get(), 1)) {
908 fprintf(stderr, "BN_rand gave a bad result.\n");
909 return false;
910 }
911 if (!BN_rand(bn.get(), 1, -1 /* top */, 1 /* bottom */) ||
912 !BN_is_word(bn.get(), 1)) {
913 fprintf(stderr, "BN_rand gave a bad result.\n");
914 return false;
915 }
916
917 if (!BN_rand(bn.get(), 2, 1 /* top */, 0 /* bottom */) ||
918 !BN_is_word(bn.get(), 3)) {
919 fprintf(stderr, "BN_rand gave a bad result.\n");
920 return false;
921 }
922
923 return true;
924}
David Benjaminb9c579d2015-06-11 22:52:07 -0400925
926struct ASN1Test {
927 const char *value_ascii;
928 const char *der;
929 size_t der_len;
930};
931
932static const ASN1Test kASN1Tests[] = {
933 {"0", "\x02\x01\x00", 3},
934 {"1", "\x02\x01\x01", 3},
935 {"127", "\x02\x01\x7f", 3},
936 {"128", "\x02\x02\x00\x80", 4},
937 {"0xdeadbeef", "\x02\x05\x00\xde\xad\xbe\xef", 7},
938 {"0x0102030405060708",
939 "\x02\x08\x01\x02\x03\x04\x05\x06\x07\x08", 10},
940 {"0xffffffffffffffff",
941 "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xff", 11},
942};
943
944struct ASN1InvalidTest {
945 const char *der;
946 size_t der_len;
947};
948
949static const ASN1InvalidTest kASN1InvalidTests[] = {
950 // Bad tag.
951 {"\x03\x01\x00", 3},
952 // Empty contents.
953 {"\x02\x00", 2},
David Benjaminb9c579d2015-06-11 22:52:07 -0400954};
955
Adam Langleydd31c4e2016-02-02 08:49:30 -0800956// kASN1BuggyTests contains incorrect encodings and the corresponding, expected
957// results of |BN_parse_asn1_unsigned_buggy| given that input.
David Benjamin4c60d352015-09-23 12:23:01 -0400958static const ASN1Test kASN1BuggyTests[] = {
959 // Negative numbers.
David Benjamin231cb822015-09-15 16:47:35 -0400960 {"128", "\x02\x01\x80", 3},
961 {"255", "\x02\x01\xff", 3},
David Benjamin4c60d352015-09-23 12:23:01 -0400962 // Unnecessary leading zeros.
963 {"1", "\x02\x02\x00\x01", 4},
David Benjamin231cb822015-09-15 16:47:35 -0400964};
965
David Benjamin96f94472016-06-26 17:31:02 -0400966static bool TestASN1() {
David Benjaminb9c579d2015-06-11 22:52:07 -0400967 for (const ASN1Test &test : kASN1Tests) {
968 ScopedBIGNUM bn = ASCIIToBIGNUM(test.value_ascii);
969 if (!bn) {
970 return false;
971 }
972
973 // Test that the input is correctly parsed.
974 ScopedBIGNUM bn2(BN_new());
975 if (!bn2) {
976 return false;
977 }
978 CBS cbs;
979 CBS_init(&cbs, reinterpret_cast<const uint8_t*>(test.der), test.der_len);
David Benjaminacb24512015-12-22 15:02:01 -0500980 if (!BN_parse_asn1_unsigned(&cbs, bn2.get()) || CBS_len(&cbs) != 0) {
David Benjaminb9c579d2015-06-11 22:52:07 -0400981 fprintf(stderr, "Parsing ASN.1 INTEGER failed.\n");
982 return false;
983 }
984 if (BN_cmp(bn.get(), bn2.get()) != 0) {
985 fprintf(stderr, "Bad parse.\n");
986 return false;
987 }
988
989 // Test the value serializes correctly.
990 CBB cbb;
991 uint8_t *der;
992 size_t der_len;
993 CBB_zero(&cbb);
994 if (!CBB_init(&cbb, 0) ||
David Benjaminacb24512015-12-22 15:02:01 -0500995 !BN_marshal_asn1(&cbb, bn.get()) ||
David Benjaminb9c579d2015-06-11 22:52:07 -0400996 !CBB_finish(&cbb, &der, &der_len)) {
997 CBB_cleanup(&cbb);
998 return false;
999 }
Adam Langley10f97f32016-07-12 08:09:33 -07001000 ScopedOpenSSLBytes delete_der(der);
David Benjaminb9c579d2015-06-11 22:52:07 -04001001 if (der_len != test.der_len ||
1002 memcmp(der, reinterpret_cast<const uint8_t*>(test.der), der_len) != 0) {
1003 fprintf(stderr, "Bad serialization.\n");
1004 return false;
1005 }
David Benjamin231cb822015-09-15 16:47:35 -04001006
Adam Langleydd31c4e2016-02-02 08:49:30 -08001007 // |BN_parse_asn1_unsigned_buggy| parses all valid input.
David Benjamin231cb822015-09-15 16:47:35 -04001008 CBS_init(&cbs, reinterpret_cast<const uint8_t*>(test.der), test.der_len);
David Benjaminacb24512015-12-22 15:02:01 -05001009 if (!BN_parse_asn1_unsigned_buggy(&cbs, bn2.get()) || CBS_len(&cbs) != 0) {
David Benjamin231cb822015-09-15 16:47:35 -04001010 fprintf(stderr, "Parsing ASN.1 INTEGER failed.\n");
1011 return false;
1012 }
1013 if (BN_cmp(bn.get(), bn2.get()) != 0) {
1014 fprintf(stderr, "Bad parse.\n");
1015 return false;
1016 }
David Benjaminb9c579d2015-06-11 22:52:07 -04001017 }
1018
1019 for (const ASN1InvalidTest &test : kASN1InvalidTests) {
1020 ScopedBIGNUM bn(BN_new());
1021 if (!bn) {
1022 return false;
1023 }
1024 CBS cbs;
1025 CBS_init(&cbs, reinterpret_cast<const uint8_t*>(test.der), test.der_len);
David Benjaminacb24512015-12-22 15:02:01 -05001026 if (BN_parse_asn1_unsigned(&cbs, bn.get())) {
David Benjaminb9c579d2015-06-11 22:52:07 -04001027 fprintf(stderr, "Parsed invalid input.\n");
1028 return false;
1029 }
1030 ERR_clear_error();
David Benjamin231cb822015-09-15 16:47:35 -04001031
1032 // All tests in kASN1InvalidTests are also rejected by
Adam Langleydd31c4e2016-02-02 08:49:30 -08001033 // |BN_parse_asn1_unsigned_buggy|.
David Benjamin231cb822015-09-15 16:47:35 -04001034 CBS_init(&cbs, reinterpret_cast<const uint8_t*>(test.der), test.der_len);
David Benjaminacb24512015-12-22 15:02:01 -05001035 if (BN_parse_asn1_unsigned_buggy(&cbs, bn.get())) {
David Benjamin231cb822015-09-15 16:47:35 -04001036 fprintf(stderr, "Parsed invalid input.\n");
1037 return false;
1038 }
1039 ERR_clear_error();
1040 }
1041
David Benjamin4c60d352015-09-23 12:23:01 -04001042 for (const ASN1Test &test : kASN1BuggyTests) {
Adam Langleydd31c4e2016-02-02 08:49:30 -08001043 // These broken encodings are rejected by |BN_parse_asn1_unsigned|.
David Benjamin231cb822015-09-15 16:47:35 -04001044 ScopedBIGNUM bn(BN_new());
1045 if (!bn) {
1046 return false;
1047 }
1048
1049 CBS cbs;
1050 CBS_init(&cbs, reinterpret_cast<const uint8_t*>(test.der), test.der_len);
David Benjaminacb24512015-12-22 15:02:01 -05001051 if (BN_parse_asn1_unsigned(&cbs, bn.get())) {
David Benjamin231cb822015-09-15 16:47:35 -04001052 fprintf(stderr, "Parsed invalid input.\n");
1053 return false;
1054 }
1055 ERR_clear_error();
1056
Adam Langleydd31c4e2016-02-02 08:49:30 -08001057 // However |BN_parse_asn1_unsigned_buggy| accepts them.
David Benjamin231cb822015-09-15 16:47:35 -04001058 ScopedBIGNUM bn2 = ASCIIToBIGNUM(test.value_ascii);
1059 if (!bn2) {
1060 return false;
1061 }
1062
1063 CBS_init(&cbs, reinterpret_cast<const uint8_t*>(test.der), test.der_len);
David Benjaminacb24512015-12-22 15:02:01 -05001064 if (!BN_parse_asn1_unsigned_buggy(&cbs, bn.get()) || CBS_len(&cbs) != 0) {
David Benjamin231cb822015-09-15 16:47:35 -04001065 fprintf(stderr, "Parsing (invalid) ASN.1 INTEGER failed.\n");
1066 return false;
1067 }
1068
1069 if (BN_cmp(bn.get(), bn2.get()) != 0) {
1070 fprintf(stderr, "\"Bad\" parse.\n");
1071 return false;
1072 }
David Benjaminb9c579d2015-06-11 22:52:07 -04001073 }
1074
1075 // Serializing negative numbers is not supported.
1076 ScopedBIGNUM bn = ASCIIToBIGNUM("-1");
1077 if (!bn) {
1078 return false;
1079 }
1080 CBB cbb;
1081 CBB_zero(&cbb);
1082 if (!CBB_init(&cbb, 0) ||
David Benjaminacb24512015-12-22 15:02:01 -05001083 BN_marshal_asn1(&cbb, bn.get())) {
David Benjaminb9c579d2015-06-11 22:52:07 -04001084 fprintf(stderr, "Serialized negative number.\n");
1085 CBB_cleanup(&cbb);
1086 return false;
1087 }
David Benjamin8b66fef2016-06-26 17:58:55 -04001088 ERR_clear_error();
David Benjaminb9c579d2015-06-11 22:52:07 -04001089 CBB_cleanup(&cbb);
1090
1091 return true;
1092}
David Benjamincca1c112016-06-26 17:28:55 -04001093
David Benjamin8b66fef2016-06-26 17:58:55 -04001094static bool TestNegativeZero(BN_CTX *ctx) {
David Benjamincca1c112016-06-26 17:28:55 -04001095 ScopedBIGNUM a(BN_new());
1096 ScopedBIGNUM b(BN_new());
1097 ScopedBIGNUM c(BN_new());
David Benjamin8b66fef2016-06-26 17:58:55 -04001098 ScopedBIGNUM d(BN_new());
1099 if (!a || !b || !c || !d) {
David Benjamincca1c112016-06-26 17:28:55 -04001100 return false;
1101 }
1102
1103 // Test that BN_mul never gives negative zero.
1104 if (!BN_set_word(a.get(), 1)) {
1105 return false;
1106 }
1107 BN_set_negative(a.get(), 1);
1108 BN_zero(b.get());
David Benjamin8b66fef2016-06-26 17:58:55 -04001109 if (!BN_mul(c.get(), a.get(), b.get(), ctx)) {
David Benjamincca1c112016-06-26 17:28:55 -04001110 return false;
1111 }
1112 if (!BN_is_zero(c.get()) || BN_is_negative(c.get())) {
1113 fprintf(stderr, "Multiplication test failed!\n");
1114 return false;
1115 }
1116
David Benjamin8b66fef2016-06-26 17:58:55 -04001117 // Test that BN_div never gives negative zero in the quotient.
1118 if (!BN_set_word(a.get(), 1) ||
1119 !BN_set_word(b.get(), 2)) {
1120 return false;
1121 }
1122 BN_set_negative(a.get(), 1);
1123 if (!BN_div(d.get(), c.get(), a.get(), b.get(), ctx)) {
1124 return false;
1125 }
1126 if (!BN_is_zero(d.get()) || BN_is_negative(d.get())) {
1127 fprintf(stderr, "Division test failed!\n");
1128 return false;
1129 }
1130
1131 // Test that BN_div never gives negative zero in the remainder.
1132 if (!BN_set_word(b.get(), 1)) {
1133 return false;
1134 }
1135 if (!BN_div(d.get(), c.get(), a.get(), b.get(), ctx)) {
1136 return false;
1137 }
1138 if (!BN_is_zero(c.get()) || BN_is_negative(c.get())) {
1139 fprintf(stderr, "Division test failed!\n");
1140 return false;
1141 }
1142
1143 return true;
1144}
1145
David Benjamindcc55312016-06-26 18:15:12 -04001146static bool TestBadModulus(BN_CTX *ctx) {
David Benjamin8b66fef2016-06-26 17:58:55 -04001147 ScopedBIGNUM a(BN_new());
1148 ScopedBIGNUM b(BN_new());
1149 ScopedBIGNUM zero(BN_new());
David Benjamindcc55312016-06-26 18:15:12 -04001150 ScopedBN_MONT_CTX mont(BN_MONT_CTX_new());
1151 if (!a || !b || !zero || !mont) {
David Benjamin8b66fef2016-06-26 17:58:55 -04001152 return false;
1153 }
1154
1155 BN_zero(zero.get());
1156
1157 if (BN_div(a.get(), b.get(), BN_value_one(), zero.get(), ctx)) {
1158 fprintf(stderr, "Division by zero succeeded!\n");
1159 return false;
1160 }
1161 ERR_clear_error();
1162
David Benjamindcc55312016-06-26 18:15:12 -04001163 if (BN_mod_mul(a.get(), BN_value_one(), BN_value_one(), zero.get(), ctx)) {
1164 fprintf(stderr, "BN_mod_mul with zero modulus succeeded!\n");
1165 return false;
1166 }
1167 ERR_clear_error();
1168
1169 if (BN_mod_exp(a.get(), BN_value_one(), BN_value_one(), zero.get(), ctx)) {
1170 fprintf(stderr, "BN_mod_exp with zero modulus succeeded!\n");
1171 return 0;
1172 }
1173 ERR_clear_error();
1174
Brian Smithb72f66f2016-07-01 11:34:10 -10001175 if (BN_mod_exp_mont(a.get(), BN_value_one(), BN_value_one(), zero.get(), ctx,
1176 NULL)) {
1177 fprintf(stderr, "BN_mod_exp_mont with zero modulus succeeded!\n");
1178 return 0;
1179 }
1180 ERR_clear_error();
1181
David Benjamindcc55312016-06-26 18:15:12 -04001182 if (BN_mod_exp_mont_consttime(a.get(), BN_value_one(), BN_value_one(),
1183 zero.get(), ctx, nullptr)) {
1184 fprintf(stderr, "BN_mod_exp_mont_consttime with zero modulus succeeded!\n");
1185 return 0;
1186 }
1187 ERR_clear_error();
1188
1189 if (BN_MONT_CTX_set(mont.get(), zero.get(), ctx)) {
1190 fprintf(stderr, "BN_MONT_CTX_set succeeded for zero modulus!\n");
1191 return false;
1192 }
1193 ERR_clear_error();
1194
1195 // Some operations also may not be used with an even modulus.
1196
1197 if (!BN_set_word(b.get(), 16)) {
1198 return false;
1199 }
1200
1201 if (BN_MONT_CTX_set(mont.get(), b.get(), ctx)) {
1202 fprintf(stderr, "BN_MONT_CTX_set succeeded for even modulus!\n");
1203 return false;
1204 }
1205 ERR_clear_error();
1206
Brian Smithb72f66f2016-07-01 11:34:10 -10001207 if (BN_mod_exp_mont(a.get(), BN_value_one(), BN_value_one(), b.get(), ctx,
1208 NULL)) {
1209 fprintf(stderr, "BN_mod_exp_mont with even modulus succeeded!\n");
1210 return 0;
1211 }
1212 ERR_clear_error();
1213
David Benjamindcc55312016-06-26 18:15:12 -04001214 if (BN_mod_exp_mont_consttime(a.get(), BN_value_one(), BN_value_one(),
1215 b.get(), ctx, nullptr)) {
1216 fprintf(stderr, "BN_mod_exp_mont_consttime with even modulus succeeded!\n");
1217 return 0;
1218 }
1219 ERR_clear_error();
1220
David Benjamincca1c112016-06-26 17:28:55 -04001221 return true;
1222}
David Benjamin32a37802016-06-26 18:17:31 -04001223
1224// TestExpModZero tests that 1**0 mod 1 == 0.
1225static bool TestExpModZero() {
1226 ScopedBIGNUM zero(BN_new()), a(BN_new()), r(BN_new());
1227 if (!zero || !a || !r || !BN_rand(a.get(), 1024, 0, 0)) {
1228 return false;
1229 }
1230 BN_zero(zero.get());
1231
1232 if (!BN_mod_exp(r.get(), a.get(), zero.get(), BN_value_one(), nullptr) ||
1233 !BN_is_zero(r.get()) ||
1234 !BN_mod_exp_mont(r.get(), a.get(), zero.get(), BN_value_one(), nullptr,
1235 nullptr) ||
1236 !BN_is_zero(r.get()) ||
1237 !BN_mod_exp_mont_consttime(r.get(), a.get(), zero.get(), BN_value_one(),
1238 nullptr, nullptr) ||
1239 !BN_is_zero(r.get()) ||
1240 !BN_mod_exp_mont_word(r.get(), 42, zero.get(), BN_value_one(), nullptr,
1241 nullptr) ||
1242 !BN_is_zero(r.get())) {
1243 return false;
1244 }
1245
1246 return true;
1247}
1248
1249static bool TestSmallPrime(BN_CTX *ctx) {
1250 static const unsigned kBits = 10;
1251
1252 ScopedBIGNUM r(BN_new());
1253 if (!r || !BN_generate_prime_ex(r.get(), static_cast<int>(kBits), 0, NULL,
1254 NULL, NULL)) {
1255 return false;
1256 }
1257 if (BN_num_bits(r.get()) != kBits) {
1258 fprintf(stderr, "Expected %u bit prime, got %u bit number\n", kBits,
1259 BN_num_bits(r.get()));
1260 return false;
1261 }
1262
1263 return true;
1264}
David Benjamin47489442016-07-09 15:22:50 -07001265
1266int main(int argc, char *argv[]) {
1267 CRYPTO_library_init();
1268
1269 if (argc != 2) {
1270 fprintf(stderr, "%s TEST_FILE\n", argv[0]);
1271 return 1;
1272 }
1273
Adam Langley10f97f32016-07-12 08:09:33 -07001274 ScopedBN_CTX ctx(BN_CTX_new());
David Benjamin47489442016-07-09 15:22:50 -07001275 if (!ctx) {
1276 return 1;
1277 }
1278
Adam Langley10f97f32016-07-12 08:09:33 -07001279 if (!TestBN2BinPadded(ctx.get()) ||
1280 !TestDec2BN(ctx.get()) ||
1281 !TestHex2BN(ctx.get()) ||
1282 !TestASC2BN(ctx.get()) ||
1283 !TestMPI() ||
1284 !TestRand() ||
1285 !TestASN1() ||
1286 !TestNegativeZero(ctx.get()) ||
1287 !TestBadModulus(ctx.get()) ||
1288 !TestExpModZero() ||
1289 !TestSmallPrime(ctx.get())) {
David Benjamin47489442016-07-09 15:22:50 -07001290 return 1;
1291 }
1292
Adam Langley10f97f32016-07-12 08:09:33 -07001293 return FileTestMain(RunTest, ctx.get(), argv[1]);
David Benjamin47489442016-07-09 15:22:50 -07001294}