David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 1 | /* 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 Benjamin | c561aa6 | 2015-05-09 01:38:06 -0400 | [diff] [blame] | 70 | /* For BIGNUM format macros. */ |
| 71 | #if !defined(__STDC_FORMAT_MACROS) |
| 72 | #define __STDC_FORMAT_MACROS |
| 73 | #endif |
| 74 | |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 75 | #include <assert.h> |
David Benjamin | 15eaafb | 2015-05-27 14:51:51 -0400 | [diff] [blame] | 76 | #include <errno.h> |
David Benjamin | 5e9bdc1 | 2016-06-26 16:56:29 -0400 | [diff] [blame] | 77 | #include <limits.h> |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 78 | #include <stdio.h> |
| 79 | #include <string.h> |
| 80 | |
David Benjamin | ef14b2d | 2015-11-11 14:01:27 -0800 | [diff] [blame] | 81 | #include <utility> |
| 82 | |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 83 | #include <openssl/bn.h> |
| 84 | #include <openssl/crypto.h> |
| 85 | #include <openssl/err.h> |
| 86 | #include <openssl/mem.h> |
| 87 | |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 88 | #include "../crypto/test/file_test.h" |
Adam Langley | d2b5af5 | 2016-07-12 08:03:59 -0700 | [diff] [blame] | 89 | #include "../crypto/test/scoped_types.h" |
Matt Braithwaite | 6488725 | 2015-08-11 17:17:28 -0700 | [diff] [blame] | 90 | #include "../crypto/test/test_util.h" |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 91 | |
| 92 | |
David Benjamin | e701f16 | 2015-12-03 11:04:24 -0500 | [diff] [blame] | 93 | static 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 Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 100 | static 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 Benjamin | 5e9bdc1 | 2016-06-26 16:56:29 -0400 | [diff] [blame] | 114 | static 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 Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 129 | static 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 Langley | 10f97f3 | 2016-07-12 08:09:33 -0700 | [diff] [blame^] | 135 | ScopedOpenSSLString expected_str(BN_bn2hex(expected)); |
| 136 | ScopedOpenSSLString actual_str(BN_bn2hex(actual)); |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 137 | if (!expected_str || !actual_str) { |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 138 | return false; |
| 139 | } |
| 140 | |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 141 | 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 Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 148 | static bool TestSum(FileTest *t, BN_CTX *ctx) { |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 149 | 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 Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 154 | } |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 155 | |
| 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 Smith | e4bf8b3 | 2016-07-01 13:36:06 -1000 | [diff] [blame] | 167 | // 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 Smith | 3d4030b | 2016-07-01 14:12:40 -1000 | [diff] [blame] | 191 | // 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 Smith | fe47ba2 | 2016-07-01 11:46:21 -1000 | [diff] [blame] | 231 | // 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 Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 244 | return true; |
| 245 | } |
| 246 | |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 247 | static bool TestLShift1(FileTest *t, BN_CTX *ctx) { |
| 248 | ScopedBIGNUM a = GetBIGNUM(t, "A"); |
| 249 | ScopedBIGNUM lshift1 = GetBIGNUM(t, "LShift1"); |
David Benjamin | e1caf39 | 2016-06-26 17:12:23 -0400 | [diff] [blame] | 250 | ScopedBIGNUM zero(BN_new()); |
| 251 | if (!a || !lshift1 || !zero) { |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 252 | return false; |
| 253 | } |
| 254 | |
David Benjamin | e1caf39 | 2016-06-26 17:12:23 -0400 | [diff] [blame] | 255 | BN_zero(zero.get()); |
| 256 | |
| 257 | ScopedBIGNUM ret(BN_new()), two(BN_new()), remainder(BN_new()); |
| 258 | if (!ret || !two || !remainder || |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 259 | !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 Benjamin | e1caf39 | 2016-06-26 17:12:23 -0400 | [diff] [blame] | 264 | !BN_div(ret.get(), remainder.get(), lshift1.get(), two.get(), ctx) || |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 265 | !ExpectBIGNUMsEqual(t, "LShift1 / 2", a.get(), ret.get()) || |
David Benjamin | e1caf39 | 2016-06-26 17:12:23 -0400 | [diff] [blame] | 266 | !ExpectBIGNUMsEqual(t, "LShift1 % 2", zero.get(), remainder.get()) || |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 267 | !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 Benjamin | 56cbbe5b | 2016-07-02 14:24:30 -0400 | [diff] [blame] | 277 | if (!BN_set_bit(lshift1.get(), 0) || |
| 278 | !BN_div(ret.get(), nullptr /* rem */, lshift1.get(), two.get(), ctx) || |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 279 | !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 Benjamin | 5e9bdc1 | 2016-06-26 16:56:29 -0400 | [diff] [blame] | 288 | static bool TestLShift(FileTest *t, BN_CTX *ctx) { |
| 289 | ScopedBIGNUM a = GetBIGNUM(t, "A"); |
| 290 | ScopedBIGNUM lshift = GetBIGNUM(t, "LShift"); |
Adam Langley | d42e4b2 | 2016-06-27 15:57:57 -0700 | [diff] [blame] | 291 | int n = 0; |
David Benjamin | 5e9bdc1 | 2016-06-26 16:56:29 -0400 | [diff] [blame] | 292 | 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 | |
| 308 | static bool TestRShift(FileTest *t, BN_CTX *ctx) { |
| 309 | ScopedBIGNUM a = GetBIGNUM(t, "A"); |
| 310 | ScopedBIGNUM rshift = GetBIGNUM(t, "RShift"); |
Adam Langley | d42e4b2 | 2016-06-27 15:57:57 -0700 | [diff] [blame] | 311 | int n = 0; |
David Benjamin | 5e9bdc1 | 2016-06-26 16:56:29 -0400 | [diff] [blame] | 312 | 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 Benjamin | e1caf39 | 2016-06-26 17:12:23 -0400 | [diff] [blame] | 326 | static 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 Benjamin | 28a8c2f | 2016-07-02 20:48:45 -0400 | [diff] [blame] | 354 | // 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 Benjamin | e1caf39 | 2016-06-26 17:12:23 -0400 | [diff] [blame] | 379 | return true; |
| 380 | } |
| 381 | |
David Benjamin | cca1c11 | 2016-06-26 17:28:55 -0400 | [diff] [blame] | 382 | static 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 Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 409 | static 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 Benjamin | e8317a5 | 2016-07-02 20:14:30 -0400 | [diff] [blame] | 457 | // 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 Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 471 | return true; |
| 472 | } |
| 473 | |
David Benjamin | 5a13e40 | 2016-07-02 20:33:07 -0400 | [diff] [blame] | 474 | static 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 Benjamin | 45a8c8a | 2016-07-03 10:05:56 -0400 | [diff] [blame] | 512 | static 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 Benjamin | 4cb00ba | 2016-07-03 11:52:58 -0400 | [diff] [blame] | 543 | static 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 Benjamin | ffb7adc | 2016-07-03 15:19:20 -0400 | [diff] [blame] | 561 | static 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 Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 587 | struct Test { |
| 588 | const char *name; |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 589 | bool (*func)(FileTest *t, BN_CTX *ctx); |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 590 | }; |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 591 | |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 592 | static const Test kTests[] = { |
| 593 | {"Sum", TestSum}, |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 594 | {"LShift1", TestLShift1}, |
David Benjamin | 5e9bdc1 | 2016-06-26 16:56:29 -0400 | [diff] [blame] | 595 | {"LShift", TestLShift}, |
| 596 | {"RShift", TestRShift}, |
David Benjamin | e1caf39 | 2016-06-26 17:12:23 -0400 | [diff] [blame] | 597 | {"Square", TestSquare}, |
David Benjamin | cca1c11 | 2016-06-26 17:28:55 -0400 | [diff] [blame] | 598 | {"Product", TestProduct}, |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 599 | {"Quotient", TestQuotient}, |
David Benjamin | 5a13e40 | 2016-07-02 20:33:07 -0400 | [diff] [blame] | 600 | {"ModMul", TestModMul}, |
David Benjamin | 45a8c8a | 2016-07-03 10:05:56 -0400 | [diff] [blame] | 601 | {"ModExp", TestModExp}, |
David Benjamin | 4cb00ba | 2016-07-03 11:52:58 -0400 | [diff] [blame] | 602 | {"Exp", TestExp}, |
David Benjamin | ffb7adc | 2016-07-03 15:19:20 -0400 | [diff] [blame] | 603 | {"ModSqrt", TestModSqrt}, |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 604 | }; |
| 605 | |
| 606 | static bool RunTest(FileTest *t, void *arg) { |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 607 | BN_CTX *ctx = reinterpret_cast<BN_CTX *>(arg); |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 608 | for (const Test &test : kTests) { |
| 609 | if (t->GetType() != test.name) { |
| 610 | continue; |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 611 | } |
David Benjamin | 3058103 | 2016-06-26 15:18:28 -0400 | [diff] [blame] | 612 | return test.func(t, ctx); |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 613 | } |
David Benjamin | 80137ba | 2016-06-26 15:03:41 -0400 | [diff] [blame] | 614 | t->PrintLine("Unknown test type: %s", t->GetType().c_str()); |
| 615 | return false; |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 616 | } |
| 617 | |
David Benjamin | 96f9447 | 2016-06-26 17:31:02 -0400 | [diff] [blame] | 618 | static bool TestBN2BinPadded(BN_CTX *ctx) { |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 619 | 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 Smith | 83a8298 | 2015-04-09 16:21:10 -1000 | [diff] [blame] | 645 | ERR_print_errors_fp(stderr); |
David Benjamin | a5974bf | 2015-03-25 23:49:37 -0400 | [diff] [blame] | 646 | 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 Benjamin | c85573c | 2015-04-20 19:52:31 -0400 | [diff] [blame] | 687 | |
| 688 | static 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 Benjamin | 96f9447 | 2016-06-26 17:31:02 -0400 | [diff] [blame] | 695 | static bool TestDec2BN(BN_CTX *ctx) { |
David Benjamin | c85573c | 2015-04-20 19:52:31 -0400 | [diff] [blame] | 696 | 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 Benjamin | 96f9447 | 2016-06-26 17:31:02 -0400 | [diff] [blame] | 730 | static bool TestHex2BN(BN_CTX *ctx) { |
David Benjamin | c85573c | 2015-04-20 19:52:31 -0400 | [diff] [blame] | 731 | 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 | |
| 765 | static 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 Benjamin | 96f9447 | 2016-06-26 17:31:02 -0400 | [diff] [blame] | 773 | static bool TestASC2BN(BN_CTX *ctx) { |
David Benjamin | c85573c | 2015-04-20 19:52:31 -0400 | [diff] [blame] | 774 | 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 Benjamin | b3a7b51 | 2015-05-26 18:35:28 -0400 | [diff] [blame] | 824 | |
Matt Braithwaite | 6488725 | 2015-08-11 17:17:28 -0700 | [diff] [blame] | 825 | struct MPITest { |
| 826 | const char *base10; |
| 827 | const char *mpi; |
| 828 | size_t mpi_len; |
| 829 | }; |
| 830 | |
| 831 | static 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 Benjamin | 96f9447 | 2016-06-26 17:31:02 -0400 | [diff] [blame] | 840 | static bool TestMPI() { |
Matt Braithwaite | 6488725 | 2015-08-11 17:17:28 -0700 | [diff] [blame] | 841 | 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 Benjamin | 96f9447 | 2016-06-26 17:31:02 -0400 | [diff] [blame] | 882 | static bool TestRand() { |
David Benjamin | b3a7b51 | 2015-05-26 18:35:28 -0400 | [diff] [blame] | 883 | 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 Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 925 | |
| 926 | struct ASN1Test { |
| 927 | const char *value_ascii; |
| 928 | const char *der; |
| 929 | size_t der_len; |
| 930 | }; |
| 931 | |
| 932 | static 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 | |
| 944 | struct ASN1InvalidTest { |
| 945 | const char *der; |
| 946 | size_t der_len; |
| 947 | }; |
| 948 | |
| 949 | static const ASN1InvalidTest kASN1InvalidTests[] = { |
| 950 | // Bad tag. |
| 951 | {"\x03\x01\x00", 3}, |
| 952 | // Empty contents. |
| 953 | {"\x02\x00", 2}, |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 954 | }; |
| 955 | |
Adam Langley | dd31c4e | 2016-02-02 08:49:30 -0800 | [diff] [blame] | 956 | // kASN1BuggyTests contains incorrect encodings and the corresponding, expected |
| 957 | // results of |BN_parse_asn1_unsigned_buggy| given that input. |
David Benjamin | 4c60d35 | 2015-09-23 12:23:01 -0400 | [diff] [blame] | 958 | static const ASN1Test kASN1BuggyTests[] = { |
| 959 | // Negative numbers. |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 960 | {"128", "\x02\x01\x80", 3}, |
| 961 | {"255", "\x02\x01\xff", 3}, |
David Benjamin | 4c60d35 | 2015-09-23 12:23:01 -0400 | [diff] [blame] | 962 | // Unnecessary leading zeros. |
| 963 | {"1", "\x02\x02\x00\x01", 4}, |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 964 | }; |
| 965 | |
David Benjamin | 96f9447 | 2016-06-26 17:31:02 -0400 | [diff] [blame] | 966 | static bool TestASN1() { |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 967 | 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 Benjamin | acb2451 | 2015-12-22 15:02:01 -0500 | [diff] [blame] | 980 | if (!BN_parse_asn1_unsigned(&cbs, bn2.get()) || CBS_len(&cbs) != 0) { |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 981 | 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 Benjamin | acb2451 | 2015-12-22 15:02:01 -0500 | [diff] [blame] | 995 | !BN_marshal_asn1(&cbb, bn.get()) || |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 996 | !CBB_finish(&cbb, &der, &der_len)) { |
| 997 | CBB_cleanup(&cbb); |
| 998 | return false; |
| 999 | } |
Adam Langley | 10f97f3 | 2016-07-12 08:09:33 -0700 | [diff] [blame^] | 1000 | ScopedOpenSSLBytes delete_der(der); |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 1001 | 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 Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1006 | |
Adam Langley | dd31c4e | 2016-02-02 08:49:30 -0800 | [diff] [blame] | 1007 | // |BN_parse_asn1_unsigned_buggy| parses all valid input. |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1008 | CBS_init(&cbs, reinterpret_cast<const uint8_t*>(test.der), test.der_len); |
David Benjamin | acb2451 | 2015-12-22 15:02:01 -0500 | [diff] [blame] | 1009 | if (!BN_parse_asn1_unsigned_buggy(&cbs, bn2.get()) || CBS_len(&cbs) != 0) { |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1010 | 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 Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 1017 | } |
| 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 Benjamin | acb2451 | 2015-12-22 15:02:01 -0500 | [diff] [blame] | 1026 | if (BN_parse_asn1_unsigned(&cbs, bn.get())) { |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 1027 | fprintf(stderr, "Parsed invalid input.\n"); |
| 1028 | return false; |
| 1029 | } |
| 1030 | ERR_clear_error(); |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1031 | |
| 1032 | // All tests in kASN1InvalidTests are also rejected by |
Adam Langley | dd31c4e | 2016-02-02 08:49:30 -0800 | [diff] [blame] | 1033 | // |BN_parse_asn1_unsigned_buggy|. |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1034 | CBS_init(&cbs, reinterpret_cast<const uint8_t*>(test.der), test.der_len); |
David Benjamin | acb2451 | 2015-12-22 15:02:01 -0500 | [diff] [blame] | 1035 | if (BN_parse_asn1_unsigned_buggy(&cbs, bn.get())) { |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1036 | fprintf(stderr, "Parsed invalid input.\n"); |
| 1037 | return false; |
| 1038 | } |
| 1039 | ERR_clear_error(); |
| 1040 | } |
| 1041 | |
David Benjamin | 4c60d35 | 2015-09-23 12:23:01 -0400 | [diff] [blame] | 1042 | for (const ASN1Test &test : kASN1BuggyTests) { |
Adam Langley | dd31c4e | 2016-02-02 08:49:30 -0800 | [diff] [blame] | 1043 | // These broken encodings are rejected by |BN_parse_asn1_unsigned|. |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1044 | 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 Benjamin | acb2451 | 2015-12-22 15:02:01 -0500 | [diff] [blame] | 1051 | if (BN_parse_asn1_unsigned(&cbs, bn.get())) { |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1052 | fprintf(stderr, "Parsed invalid input.\n"); |
| 1053 | return false; |
| 1054 | } |
| 1055 | ERR_clear_error(); |
| 1056 | |
Adam Langley | dd31c4e | 2016-02-02 08:49:30 -0800 | [diff] [blame] | 1057 | // However |BN_parse_asn1_unsigned_buggy| accepts them. |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1058 | 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 Benjamin | acb2451 | 2015-12-22 15:02:01 -0500 | [diff] [blame] | 1064 | if (!BN_parse_asn1_unsigned_buggy(&cbs, bn.get()) || CBS_len(&cbs) != 0) { |
David Benjamin | 231cb82 | 2015-09-15 16:47:35 -0400 | [diff] [blame] | 1065 | 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 Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 1073 | } |
| 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 Benjamin | acb2451 | 2015-12-22 15:02:01 -0500 | [diff] [blame] | 1083 | BN_marshal_asn1(&cbb, bn.get())) { |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 1084 | fprintf(stderr, "Serialized negative number.\n"); |
| 1085 | CBB_cleanup(&cbb); |
| 1086 | return false; |
| 1087 | } |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 1088 | ERR_clear_error(); |
David Benjamin | b9c579d | 2015-06-11 22:52:07 -0400 | [diff] [blame] | 1089 | CBB_cleanup(&cbb); |
| 1090 | |
| 1091 | return true; |
| 1092 | } |
David Benjamin | cca1c11 | 2016-06-26 17:28:55 -0400 | [diff] [blame] | 1093 | |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 1094 | static bool TestNegativeZero(BN_CTX *ctx) { |
David Benjamin | cca1c11 | 2016-06-26 17:28:55 -0400 | [diff] [blame] | 1095 | ScopedBIGNUM a(BN_new()); |
| 1096 | ScopedBIGNUM b(BN_new()); |
| 1097 | ScopedBIGNUM c(BN_new()); |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 1098 | ScopedBIGNUM d(BN_new()); |
| 1099 | if (!a || !b || !c || !d) { |
David Benjamin | cca1c11 | 2016-06-26 17:28:55 -0400 | [diff] [blame] | 1100 | 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 Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 1109 | if (!BN_mul(c.get(), a.get(), b.get(), ctx)) { |
David Benjamin | cca1c11 | 2016-06-26 17:28:55 -0400 | [diff] [blame] | 1110 | 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 Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 1117 | // 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 Benjamin | dcc5531 | 2016-06-26 18:15:12 -0400 | [diff] [blame] | 1146 | static bool TestBadModulus(BN_CTX *ctx) { |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 1147 | ScopedBIGNUM a(BN_new()); |
| 1148 | ScopedBIGNUM b(BN_new()); |
| 1149 | ScopedBIGNUM zero(BN_new()); |
David Benjamin | dcc5531 | 2016-06-26 18:15:12 -0400 | [diff] [blame] | 1150 | ScopedBN_MONT_CTX mont(BN_MONT_CTX_new()); |
| 1151 | if (!a || !b || !zero || !mont) { |
David Benjamin | 8b66fef | 2016-06-26 17:58:55 -0400 | [diff] [blame] | 1152 | 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 Benjamin | dcc5531 | 2016-06-26 18:15:12 -0400 | [diff] [blame] | 1163 | 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 Smith | b72f66f | 2016-07-01 11:34:10 -1000 | [diff] [blame] | 1175 | 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 Benjamin | dcc5531 | 2016-06-26 18:15:12 -0400 | [diff] [blame] | 1182 | 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 Smith | b72f66f | 2016-07-01 11:34:10 -1000 | [diff] [blame] | 1207 | 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 Benjamin | dcc5531 | 2016-06-26 18:15:12 -0400 | [diff] [blame] | 1214 | 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 Benjamin | cca1c11 | 2016-06-26 17:28:55 -0400 | [diff] [blame] | 1221 | return true; |
| 1222 | } |
David Benjamin | 32a3780 | 2016-06-26 18:17:31 -0400 | [diff] [blame] | 1223 | |
| 1224 | // TestExpModZero tests that 1**0 mod 1 == 0. |
| 1225 | static 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 | |
| 1249 | static 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 Benjamin | 4748944 | 2016-07-09 15:22:50 -0700 | [diff] [blame] | 1265 | |
| 1266 | int 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 Langley | 10f97f3 | 2016-07-12 08:09:33 -0700 | [diff] [blame^] | 1274 | ScopedBN_CTX ctx(BN_CTX_new()); |
David Benjamin | 4748944 | 2016-07-09 15:22:50 -0700 | [diff] [blame] | 1275 | if (!ctx) { |
| 1276 | return 1; |
| 1277 | } |
| 1278 | |
Adam Langley | 10f97f3 | 2016-07-12 08:09:33 -0700 | [diff] [blame^] | 1279 | 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 Benjamin | 4748944 | 2016-07-09 15:22:50 -0700 | [diff] [blame] | 1290 | return 1; |
| 1291 | } |
| 1292 | |
Adam Langley | 10f97f3 | 2016-07-12 08:09:33 -0700 | [diff] [blame^] | 1293 | return FileTestMain(RunTest, ctx.get(), argv[1]); |
David Benjamin | 4748944 | 2016-07-09 15:22:50 -0700 | [diff] [blame] | 1294 | } |